summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/camera3_hal.cpp2
-rw-r--r--src/android/camera_buffer.h2
-rw-r--r--src/android/camera_capabilities.cpp48
-rw-r--r--src/android/camera_capabilities.h2
-rw-r--r--src/android/camera_device.cpp76
-rw-r--r--src/android/camera_device.h2
-rw-r--r--src/android/camera_hal_config.cpp2
-rw-r--r--src/android/camera_hal_config.h2
-rw-r--r--src/android/camera_hal_manager.cpp2
-rw-r--r--src/android/camera_hal_manager.h2
-rw-r--r--src/android/camera_metadata.cpp2
-rw-r--r--src/android/camera_metadata.h2
-rw-r--r--src/android/camera_ops.cpp2
-rw-r--r--src/android/camera_ops.h2
-rw-r--r--src/android/camera_request.cpp2
-rw-r--r--src/android/camera_request.h2
-rw-r--r--src/android/camera_stream.cpp2
-rw-r--r--src/android/camera_stream.h2
-rw-r--r--src/android/cros/camera3_hal.cpp2
-rw-r--r--src/android/cros_mojo_token.h2
-rw-r--r--src/android/frame_buffer_allocator.h2
-rw-r--r--src/android/hal_framebuffer.cpp2
-rw-r--r--src/android/hal_framebuffer.h2
-rw-r--r--src/android/jpeg/encoder.h2
-rw-r--r--src/android/jpeg/encoder_jea.cpp2
-rw-r--r--src/android/jpeg/encoder_jea.h2
-rw-r--r--src/android/jpeg/encoder_libjpeg.cpp8
-rw-r--r--src/android/jpeg/encoder_libjpeg.h2
-rw-r--r--src/android/jpeg/exif.cpp2
-rw-r--r--src/android/jpeg/exif.h2
-rw-r--r--src/android/jpeg/post_processor_jpeg.cpp2
-rw-r--r--src/android/jpeg/post_processor_jpeg.h2
-rw-r--r--src/android/jpeg/thumbnailer.cpp2
-rw-r--r--src/android/jpeg/thumbnailer.h2
-rw-r--r--src/android/meson.build22
-rw-r--r--src/android/mm/cros_camera_buffer.cpp2
-rw-r--r--src/android/mm/cros_frame_buffer_allocator.cpp3
-rw-r--r--src/android/mm/generic_camera_buffer.cpp2
-rw-r--r--src/android/mm/generic_frame_buffer_allocator.cpp2
-rw-r--r--src/android/mm/libhardware_stub.c2
-rw-r--r--src/android/post_processor.h2
-rw-r--r--src/android/yuv/post_processor_yuv.cpp2
-rw-r--r--src/android/yuv/post_processor_yuv.h2
-rw-r--r--src/apps/cam/camera_session.cpp110
-rw-r--r--src/apps/cam/camera_session.h2
-rw-r--r--src/apps/cam/capture_script.cpp47
-rw-r--r--src/apps/cam/capture_script.h2
-rw-r--r--src/apps/cam/drm.cpp2
-rw-r--r--src/apps/cam/drm.h2
-rw-r--r--src/apps/cam/file_sink.cpp73
-rw-r--r--src/apps/cam/file_sink.h21
-rw-r--r--src/apps/cam/frame_sink.cpp2
-rw-r--r--src/apps/cam/frame_sink.h2
-rw-r--r--src/apps/cam/kms_sink.cpp2
-rw-r--r--src/apps/cam/kms_sink.h2
-rw-r--r--src/apps/cam/main.cpp11
-rw-r--r--src/apps/cam/main.h2
-rw-r--r--src/apps/cam/meson.build4
-rw-r--r--src/apps/cam/sdl_sink.cpp63
-rw-r--r--src/apps/cam/sdl_sink.h2
-rw-r--r--src/apps/cam/sdl_texture.cpp2
-rw-r--r--src/apps/cam/sdl_texture.h6
-rw-r--r--src/apps/cam/sdl_texture_1plane.cpp17
-rw-r--r--src/apps/cam/sdl_texture_1plane.h18
-rw-r--r--src/apps/cam/sdl_texture_mjpg.cpp4
-rw-r--r--src/apps/cam/sdl_texture_mjpg.h4
-rw-r--r--src/apps/cam/sdl_texture_yuv.cpp16
-rw-r--r--src/apps/cam/sdl_texture_yuv.h11
-rw-r--r--src/apps/common/dng_writer.cpp226
-rw-r--r--src/apps/common/dng_writer.h3
-rw-r--r--src/apps/common/event_loop.cpp66
-rw-r--r--src/apps/common/event_loop.h25
-rw-r--r--src/apps/common/image.cpp2
-rw-r--r--src/apps/common/image.h2
-rw-r--r--src/apps/common/meson.build1
-rw-r--r--src/apps/common/options.cpp12
-rw-r--r--src/apps/common/options.h2
-rw-r--r--src/apps/common/ppm_writer.cpp54
-rw-r--r--src/apps/common/ppm_writer.h20
-rw-r--r--src/apps/common/stream_options.cpp5
-rw-r--r--src/apps/common/stream_options.h2
-rw-r--r--src/apps/ipa-verify/main.cpp2
-rw-r--r--src/apps/lc-compliance/capture_test.cpp135
-rw-r--r--src/apps/lc-compliance/environment.cpp2
-rw-r--r--src/apps/lc-compliance/environment.h4
-rw-r--r--src/apps/lc-compliance/helpers/capture.cpp176
-rw-r--r--src/apps/lc-compliance/helpers/capture.h45
-rw-r--r--src/apps/lc-compliance/main.cpp44
-rw-r--r--src/apps/lc-compliance/meson.build17
-rw-r--r--src/apps/lc-compliance/simple_capture.cpp190
-rw-r--r--src/apps/lc-compliance/simple_capture.h66
-rw-r--r--src/apps/lc-compliance/test_base.cpp28
-rw-r--r--src/apps/lc-compliance/test_base.h24
-rw-r--r--src/apps/lc-compliance/tests/capture_test.cpp145
-rw-r--r--src/apps/qcam/assets/shader/bayer_8.vert4
-rw-r--r--src/apps/qcam/assets/shader/identity.vert3
-rw-r--r--src/apps/qcam/cam_select_dialog.cpp12
-rw-r--r--src/apps/qcam/cam_select_dialog.h2
-rw-r--r--src/apps/qcam/format_converter.cpp4
-rw-r--r--src/apps/qcam/format_converter.h2
-rw-r--r--src/apps/qcam/main.cpp6
-rw-r--r--src/apps/qcam/main_window.cpp77
-rw-r--r--src/apps/qcam/main_window.h4
-rw-r--r--src/apps/qcam/meson.build50
-rw-r--r--src/apps/qcam/message_handler.cpp2
-rw-r--r--src/apps/qcam/message_handler.h2
-rw-r--r--src/apps/qcam/viewfinder.h2
-rw-r--r--src/apps/qcam/viewfinder_gl.cpp54
-rw-r--r--src/apps/qcam/viewfinder_gl.h5
-rw-r--r--src/apps/qcam/viewfinder_qt.cpp53
-rw-r--r--src/apps/qcam/viewfinder_qt.h4
-rw-r--r--src/gstreamer/gstlibcamera-controls.cpp.in327
-rw-r--r--src/gstreamer/gstlibcamera-controls.h43
-rw-r--r--src/gstreamer/gstlibcamera-utils.cpp160
-rw-r--r--src/gstreamer/gstlibcamera-utils.h19
-rw-r--r--src/gstreamer/gstlibcamera.cpp2
-rw-r--r--src/gstreamer/gstlibcameraallocator.cpp26
-rw-r--r--src/gstreamer/gstlibcameraallocator.h2
-rw-r--r--src/gstreamer/gstlibcamerapad.cpp33
-rw-r--r--src/gstreamer/gstlibcamerapad.h10
-rw-r--r--src/gstreamer/gstlibcamerapool.cpp58
-rw-r--r--src/gstreamer/gstlibcamerapool.h5
-rw-r--r--src/gstreamer/gstlibcameraprovider.cpp17
-rw-r--r--src/gstreamer/gstlibcameraprovider.h2
-rw-r--r--src/gstreamer/gstlibcamerasrc.cpp557
-rw-r--r--src/gstreamer/gstlibcamerasrc.h33
-rw-r--r--src/gstreamer/meson.build23
-rwxr-xr-xsrc/ipa/ipa-sign-install.sh2
-rwxr-xr-xsrc/ipa/ipa-sign.sh2
-rw-r--r--src/ipa/ipu3/algorithms/af.cpp5
-rw-r--r--src/ipa/ipu3/algorithms/af.h2
-rw-r--r--src/ipa/ipu3/algorithms/agc.cpp310
-rw-r--r--src/ipa/ipu3/algorithms/agc.h33
-rw-r--r--src/ipa/ipu3/algorithms/algorithm.h2
-rw-r--r--src/ipa/ipu3/algorithms/awb.cpp71
-rw-r--r--src/ipa/ipu3/algorithms/awb.h21
-rw-r--r--src/ipa/ipu3/algorithms/blc.cpp8
-rw-r--r--src/ipa/ipu3/algorithms/blc.h2
-rw-r--r--src/ipa/ipu3/algorithms/tone_mapping.cpp2
-rw-r--r--src/ipa/ipu3/algorithms/tone_mapping.h2
-rw-r--r--src/ipa/ipu3/data/meson.build3
-rw-r--r--src/ipa/ipu3/ipa_context.cpp17
-rw-r--r--src/ipa/ipu3/ipa_context.h16
-rw-r--r--src/ipa/ipu3/ipu3-ipa-design-guide.rst14
-rw-r--r--src/ipa/ipu3/ipu3.cpp61
-rw-r--r--src/ipa/ipu3/meson.build8
-rw-r--r--src/ipa/ipu3/module.h2
-rw-r--r--src/ipa/libipa/agc_mean_luminance.cpp589
-rw-r--r--src/ipa/libipa/agc_mean_luminance.h98
-rw-r--r--src/ipa/libipa/algorithm.cpp2
-rw-r--r--src/ipa/libipa/algorithm.h2
-rw-r--r--src/ipa/libipa/awb.cpp265
-rw-r--r--src/ipa/libipa/awb.h67
-rw-r--r--src/ipa/libipa/awb_bayes.cpp505
-rw-r--r--src/ipa/libipa/awb_bayes.h59
-rw-r--r--src/ipa/libipa/awb_grey.cpp114
-rw-r--r--src/ipa/libipa/awb_grey.h36
-rw-r--r--src/ipa/libipa/camera_sensor_helper.cpp420
-rw-r--r--src/ipa/libipa/camera_sensor_helper.h28
-rw-r--r--src/ipa/libipa/colours.cpp81
-rw-r--r--src/ipa/libipa/colours.h23
-rw-r--r--src/ipa/libipa/exposure_mode_helper.cpp242
-rw-r--r--src/ipa/libipa/exposure_mode_helper.h53
-rw-r--r--src/ipa/libipa/fc_queue.cpp2
-rw-r--r--src/ipa/libipa/fc_queue.h23
-rw-r--r--src/ipa/libipa/fixedpoint.cpp42
-rw-r--r--src/ipa/libipa/fixedpoint.h65
-rw-r--r--src/ipa/libipa/histogram.cpp76
-rw-r--r--src/ipa/libipa/histogram.h19
-rw-r--r--src/ipa/libipa/interpolator.cpp163
-rw-r--r--src/ipa/libipa/interpolator.h136
-rw-r--r--src/ipa/libipa/lsc_polynomial.cpp81
-rw-r--r--src/ipa/libipa/lsc_polynomial.h105
-rw-r--r--src/ipa/libipa/lux.cpp173
-rw-r--r--src/ipa/libipa/lux.h41
-rw-r--r--src/ipa/libipa/meson.build26
-rw-r--r--src/ipa/libipa/module.cpp2
-rw-r--r--src/ipa/libipa/module.h2
-rw-r--r--src/ipa/libipa/pwl.cpp462
-rw-r--r--src/ipa/libipa/pwl.h86
-rw-r--r--src/ipa/mali-c55/algorithms/agc.cpp410
-rw-r--r--src/ipa/mali-c55/algorithms/agc.h81
-rw-r--r--src/ipa/mali-c55/algorithms/algorithm.h39
-rw-r--r--src/ipa/mali-c55/algorithms/awb.cpp230
-rw-r--r--src/ipa/mali-c55/algorithms/awb.h40
-rw-r--r--src/ipa/mali-c55/algorithms/blc.cpp140
-rw-r--r--src/ipa/mali-c55/algorithms/blc.h42
-rw-r--r--src/ipa/mali-c55/algorithms/lsc.cpp216
-rw-r--r--src/ipa/mali-c55/algorithms/lsc.h45
-rw-r--r--src/ipa/mali-c55/algorithms/meson.build8
-rw-r--r--src/ipa/mali-c55/data/imx415.yaml325
-rw-r--r--src/ipa/mali-c55/data/meson.build9
-rw-r--r--src/ipa/mali-c55/data/uncalibrated.yaml7
-rw-r--r--src/ipa/mali-c55/ipa_context.cpp101
-rw-r--r--src/ipa/mali-c55/ipa_context.h90
-rw-r--r--src/ipa/mali-c55/mali-c55.cpp399
-rw-r--r--src/ipa/mali-c55/meson.build33
-rw-r--r--src/ipa/mali-c55/module.h27
-rw-r--r--src/ipa/meson.build8
-rw-r--r--src/ipa/rkisp1/algorithms/agc.cpp630
-rw-r--r--src/ipa/rkisp1/algorithms/agc.h31
-rw-r--r--src/ipa/rkisp1/algorithms/algorithm.h2
-rw-r--r--src/ipa/rkisp1/algorithms/awb.cpp404
-rw-r--r--src/ipa/rkisp1/algorithms/awb.h15
-rw-r--r--src/ipa/rkisp1/algorithms/blc.cpp137
-rw-r--r--src/ipa/rkisp1/algorithms/blc.h13
-rw-r--r--src/ipa/rkisp1/algorithms/ccm.cpp178
-rw-r--r--src/ipa/rkisp1/algorithms/ccm.h56
-rw-r--r--src/ipa/rkisp1/algorithms/cproc.cpp75
-rw-r--r--src/ipa/rkisp1/algorithms/cproc.h7
-rw-r--r--src/ipa/rkisp1/algorithms/dpcc.cpp12
-rw-r--r--src/ipa/rkisp1/algorithms/dpcc.h4
-rw-r--r--src/ipa/rkisp1/algorithms/dpf.cpp33
-rw-r--r--src/ipa/rkisp1/algorithms/dpf.h4
-rw-r--r--src/ipa/rkisp1/algorithms/filter.cpp58
-rw-r--r--src/ipa/rkisp1/algorithms/filter.h4
-rw-r--r--src/ipa/rkisp1/algorithms/goc.cpp149
-rw-r--r--src/ipa/rkisp1/algorithms/goc.h42
-rw-r--r--src/ipa/rkisp1/algorithms/gsl.cpp22
-rw-r--r--src/ipa/rkisp1/algorithms/gsl.h4
-rw-r--r--src/ipa/rkisp1/algorithms/lsc.cpp442
-rw-r--r--src/ipa/rkisp1/algorithms/lsc.h19
-rw-r--r--src/ipa/rkisp1/algorithms/lux.cpp76
-rw-r--r--src/ipa/rkisp1/algorithms/lux.h36
-rw-r--r--src/ipa/rkisp1/algorithms/meson.build3
-rw-r--r--src/ipa/rkisp1/data/imx219.yaml4
-rw-r--r--src/ipa/rkisp1/data/imx258.yaml1
-rw-r--r--src/ipa/rkisp1/data/meson.build7
-rw-r--r--src/ipa/rkisp1/data/ov4689.yaml4
-rw-r--r--src/ipa/rkisp1/data/ov5640.yaml4
-rw-r--r--src/ipa/rkisp1/data/uncalibrated.yaml1
-rw-r--r--src/ipa/rkisp1/ipa_context.cpp230
-rw-r--r--src/ipa/rkisp1/ipa_context.h116
-rw-r--r--src/ipa/rkisp1/meson.build9
-rw-r--r--src/ipa/rkisp1/module.h5
-rw-r--r--src/ipa/rkisp1/params.cpp222
-rw-r--r--src/ipa/rkisp1/params.h163
-rw-r--r--src/ipa/rkisp1/rkisp1.cpp149
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper.cpp16
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper.h11
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx219.cpp4
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx283.cpp61
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx290.cpp21
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx296.cpp13
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx415.cpp64
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx477.cpp17
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx519.cpp17
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_imx708.cpp21
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp17
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_ov64a40.cpp62
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_ov7251.cpp54
-rw-r--r--src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp16
-rw-r--r--src/ipa/rpi/cam_helper/md_parser.h2
-rw-r--r--src/ipa/rpi/cam_helper/md_parser_smia.cpp13
-rw-r--r--src/ipa/rpi/cam_helper/meson.build4
-rw-r--r--src/ipa/rpi/common/ipa_base.cpp430
-rw-r--r--src/ipa/rpi/common/ipa_base.h17
-rw-r--r--src/ipa/rpi/controller/af_status.h2
-rw-r--r--src/ipa/rpi/controller/agc_algorithm.h23
-rw-r--r--src/ipa/rpi/controller/agc_status.h6
-rw-r--r--src/ipa/rpi/controller/algorithm.cpp2
-rw-r--r--src/ipa/rpi/controller/algorithm.h2
-rw-r--r--src/ipa/rpi/controller/alsc_status.h2
-rw-r--r--src/ipa/rpi/controller/awb_algorithm.h4
-rw-r--r--src/ipa/rpi/controller/awb_status.h2
-rw-r--r--src/ipa/rpi/controller/black_level_algorithm.h23
-rw-r--r--src/ipa/rpi/controller/black_level_status.h2
-rw-r--r--src/ipa/rpi/controller/cac_status.h2
-rw-r--r--src/ipa/rpi/controller/camera_mode.h8
-rw-r--r--src/ipa/rpi/controller/ccm_algorithm.h2
-rw-r--r--src/ipa/rpi/controller/ccm_status.h2
-rw-r--r--src/ipa/rpi/controller/contrast_algorithm.h2
-rw-r--r--src/ipa/rpi/controller/contrast_status.h6
-rw-r--r--src/ipa/rpi/controller/controller.cpp24
-rw-r--r--src/ipa/rpi/controller/controller.h5
-rw-r--r--src/ipa/rpi/controller/denoise_algorithm.h2
-rw-r--r--src/ipa/rpi/controller/denoise_status.h2
-rw-r--r--src/ipa/rpi/controller/device_status.cpp4
-rw-r--r--src/ipa/rpi/controller/device_status.h10
-rw-r--r--src/ipa/rpi/controller/dpc_status.h2
-rw-r--r--src/ipa/rpi/controller/geq_status.h2
-rw-r--r--src/ipa/rpi/controller/hdr_algorithm.h2
-rw-r--r--src/ipa/rpi/controller/hdr_status.h2
-rw-r--r--src/ipa/rpi/controller/histogram.cpp18
-rw-r--r--src/ipa/rpi/controller/histogram.h2
-rw-r--r--src/ipa/rpi/controller/lux_status.h2
-rw-r--r--src/ipa/rpi/controller/meson.build2
-rw-r--r--src/ipa/rpi/controller/metadata.h25
-rw-r--r--src/ipa/rpi/controller/noise_status.h2
-rw-r--r--src/ipa/rpi/controller/pdaf_data.h2
-rw-r--r--src/ipa/rpi/controller/pwl.cpp269
-rw-r--r--src/ipa/rpi/controller/pwl.h127
-rw-r--r--src/ipa/rpi/controller/region_stats.h2
-rw-r--r--src/ipa/rpi/controller/rpi/af.cpp8
-rw-r--r--src/ipa/rpi/controller/rpi/af.h7
-rw-r--r--src/ipa/rpi/controller/rpi/agc.cpp103
-rw-r--r--src/ipa/rpi/controller/rpi/agc.h25
-rw-r--r--src/ipa/rpi/controller/rpi/agc_channel.cpp280
-rw-r--r--src/ipa/rpi/controller/rpi/agc_channel.h32
-rw-r--r--src/ipa/rpi/controller/rpi/alsc.cpp30
-rw-r--r--src/ipa/rpi/controller/rpi/alsc.h2
-rw-r--r--src/ipa/rpi/controller/rpi/awb.cpp151
-rw-r--r--src/ipa/rpi/controller/rpi/awb.h32
-rw-r--r--src/ipa/rpi/controller/rpi/black_level.cpp13
-rw-r--r--src/ipa/rpi/controller/rpi/black_level.h8
-rw-r--r--src/ipa/rpi/controller/rpi/cac.cpp84
-rw-r--r--src/ipa/rpi/controller/rpi/cac.h5
-rw-r--r--src/ipa/rpi/controller/rpi/ccm.cpp81
-rw-r--r--src/ipa/rpi/controller/rpi/ccm.h42
-rw-r--r--src/ipa/rpi/controller/rpi/contrast.cpp22
-rw-r--r--src/ipa/rpi/controller/rpi/contrast.h7
-rw-r--r--src/ipa/rpi/controller/rpi/denoise.cpp2
-rw-r--r--src/ipa/rpi/controller/rpi/dpc.cpp2
-rw-r--r--src/ipa/rpi/controller/rpi/dpc.h2
-rw-r--r--src/ipa/rpi/controller/rpi/focus.h2
-rw-r--r--src/ipa/rpi/controller/rpi/geq.cpp11
-rw-r--r--src/ipa/rpi/controller/rpi/geq.h6
-rw-r--r--src/ipa/rpi/controller/rpi/hdr.cpp118
-rw-r--r--src/ipa/rpi/controller/rpi/hdr.h19
-rw-r--r--src/ipa/rpi/controller/rpi/lux.cpp11
-rw-r--r--src/ipa/rpi/controller/rpi/lux.h4
-rw-r--r--src/ipa/rpi/controller/rpi/noise.cpp6
-rw-r--r--src/ipa/rpi/controller/rpi/noise.h2
-rw-r--r--src/ipa/rpi/controller/rpi/saturation.cpp2
-rw-r--r--src/ipa/rpi/controller/rpi/sdn.cpp5
-rw-r--r--src/ipa/rpi/controller/rpi/sdn.h2
-rw-r--r--src/ipa/rpi/controller/rpi/sharpen.cpp6
-rw-r--r--src/ipa/rpi/controller/rpi/sharpen.h2
-rw-r--r--src/ipa/rpi/controller/rpi/tonemap.cpp4
-rw-r--r--src/ipa/rpi/controller/rpi/tonemap.h5
-rw-r--r--src/ipa/rpi/controller/saturation_status.h2
-rw-r--r--src/ipa/rpi/controller/sharpen_algorithm.h2
-rw-r--r--src/ipa/rpi/controller/sharpen_status.h2
-rw-r--r--src/ipa/rpi/controller/statistics.h2
-rw-r--r--src/ipa/rpi/controller/stitch_status.h2
-rw-r--r--src/ipa/rpi/controller/tonemap_status.h6
-rw-r--r--src/ipa/rpi/pisp/data/imx219.json1187
-rw-r--r--src/ipa/rpi/pisp/data/imx219_noir.json1112
-rw-r--r--src/ipa/rpi/pisp/data/imx283.json1334
-rw-r--r--src/ipa/rpi/pisp/data/imx290.json341
-rw-r--r--src/ipa/rpi/pisp/data/imx296.json1194
-rw-r--r--src/ipa/rpi/pisp/data/imx296_16mm.json1247
-rw-r--r--src/ipa/rpi/pisp/data/imx296_6mm.json1247
-rw-r--r--src/ipa/rpi/pisp/data/imx296_mono.json960
-rw-r--r--src/ipa/rpi/pisp/data/imx378.json634
-rwxr-xr-xsrc/ipa/rpi/pisp/data/imx415.json1159
-rw-r--r--src/ipa/rpi/pisp/data/imx462.json342
-rw-r--r--src/ipa/rpi/pisp/data/imx477.json1186
-rw-r--r--src/ipa/rpi/pisp/data/imx477_16mm.json1240
-rw-r--r--src/ipa/rpi/pisp/data/imx477_6mm.json1240
-rw-r--r--src/ipa/rpi/pisp/data/imx477_noir.json1148
-rw-r--r--src/ipa/rpi/pisp/data/imx477_scientific.json546
-rw-r--r--src/ipa/rpi/pisp/data/imx519.json634
-rw-r--r--src/ipa/rpi/pisp/data/imx708.json1270
-rw-r--r--src/ipa/rpi/pisp/data/imx708_noir.json1233
-rw-r--r--src/ipa/rpi/pisp/data/imx708_wide.json1293
-rw-r--r--src/ipa/rpi/pisp/data/imx708_wide_noir.json1148
-rw-r--r--src/ipa/rpi/pisp/data/meson.build30
-rw-r--r--src/ipa/rpi/pisp/data/ov5647.json1186
-rw-r--r--src/ipa/rpi/pisp/data/ov5647_noir.json1121
-rwxr-xr-xsrc/ipa/rpi/pisp/data/ov64a40.json1133
-rw-r--r--src/ipa/rpi/pisp/data/ov9281_mono.json215
-rw-r--r--src/ipa/rpi/pisp/data/se327m12.json639
-rw-r--r--src/ipa/rpi/pisp/data/uncalibrated.json135
-rw-r--r--src/ipa/rpi/pisp/meson.build49
-rw-r--r--src/ipa/rpi/pisp/pisp.cpp1068
-rw-r--r--src/ipa/rpi/vc4/data/imx219.json383
-rw-r--r--src/ipa/rpi/vc4/data/imx219_noir.json383
-rw-r--r--src/ipa/rpi/vc4/data/imx283.json548
-rw-r--r--src/ipa/rpi/vc4/data/imx290.json15
-rw-r--r--src/ipa/rpi/vc4/data/imx296.json17
-rw-r--r--src/ipa/rpi/vc4/data/imx296_mono.json17
-rw-r--r--src/ipa/rpi/vc4/data/imx327.json215
-rw-r--r--src/ipa/rpi/vc4/data/imx378.json15
-rwxr-xr-xsrc/ipa/rpi/vc4/data/imx415.json413
-rw-r--r--src/ipa/rpi/vc4/data/imx462.json215
-rw-r--r--src/ipa/rpi/vc4/data/imx477.json395
-rw-r--r--src/ipa/rpi/vc4/data/imx477_noir.json383
-rw-r--r--src/ipa/rpi/vc4/data/imx477_scientific.json15
-rw-r--r--src/ipa/rpi/vc4/data/imx477_v1.json15
-rw-r--r--src/ipa/rpi/vc4/data/imx519.json15
-rw-r--r--src/ipa/rpi/vc4/data/imx708.json349
-rw-r--r--src/ipa/rpi/vc4/data/imx708_noir.json349
-rw-r--r--src/ipa/rpi/vc4/data/imx708_wide.json349
-rw-r--r--src/ipa/rpi/vc4/data/imx708_wide_noir.json349
-rw-r--r--src/ipa/rpi/vc4/data/meson.build9
-rw-r--r--src/ipa/rpi/vc4/data/ov5647.json387
-rw-r--r--src/ipa/rpi/vc4/data/ov5647_noir.json15
-rw-r--r--src/ipa/rpi/vc4/data/ov64a40.json422
-rw-r--r--src/ipa/rpi/vc4/data/ov7251_mono.json136
-rw-r--r--src/ipa/rpi/vc4/data/ov9281_mono.json5
-rw-r--r--src/ipa/rpi/vc4/data/se327m12.json15
-rw-r--r--src/ipa/rpi/vc4/data/uncalibrated.json5
-rw-r--r--src/ipa/rpi/vc4/meson.build7
-rw-r--r--src/ipa/rpi/vc4/vc4.cpp46
-rw-r--r--src/ipa/simple/algorithms/agc.cpp146
-rw-r--r--src/ipa/simple/algorithms/agc.h33
-rw-r--r--src/ipa/simple/algorithms/algorithm.h22
-rw-r--r--src/ipa/simple/algorithms/awb.cpp100
-rw-r--r--src/ipa/simple/algorithms/awb.h36
-rw-r--r--src/ipa/simple/algorithms/blc.cpp107
-rw-r--r--src/ipa/simple/algorithms/blc.h38
-rw-r--r--src/ipa/simple/algorithms/ccm.cpp76
-rw-r--r--src/ipa/simple/algorithms/ccm.h43
-rw-r--r--src/ipa/simple/algorithms/lut.cpp159
-rw-r--r--src/ipa/simple/algorithms/lut.h46
-rw-r--r--src/ipa/simple/algorithms/meson.build9
-rw-r--r--src/ipa/simple/data/meson.build10
-rw-r--r--src/ipa/simple/data/uncalibrated.yaml19
-rw-r--r--src/ipa/simple/ipa_context.cpp102
-rw-r--r--src/ipa/simple/ipa_context.h101
-rw-r--r--src/ipa/simple/meson.build31
-rw-r--r--src/ipa/simple/module.h30
-rw-r--r--src/ipa/simple/soft_simple.cpp358
-rw-r--r--src/ipa/vimc/data/meson.build3
-rw-r--r--src/ipa/vimc/meson.build8
-rw-r--r--src/ipa/vimc/vimc.cpp11
-rw-r--r--src/libcamera/base/backtrace.cpp2
-rw-r--r--src/libcamera/base/bound_method.cpp3
-rw-r--r--src/libcamera/base/class.cpp2
-rw-r--r--src/libcamera/base/event_dispatcher.cpp2
-rw-r--r--src/libcamera/base/event_dispatcher_poll.cpp5
-rw-r--r--src/libcamera/base/event_notifier.cpp8
-rw-r--r--src/libcamera/base/file.cpp2
-rw-r--r--src/libcamera/base/flags.cpp2
-rw-r--r--src/libcamera/base/log.cpp157
-rw-r--r--src/libcamera/base/memfd.cpp123
-rw-r--r--src/libcamera/base/meson.build26
-rw-r--r--src/libcamera/base/message.cpp2
-rw-r--r--src/libcamera/base/mutex.cpp2
-rw-r--r--src/libcamera/base/object.cpp59
-rw-r--r--src/libcamera/base/semaphore.cpp2
-rw-r--r--src/libcamera/base/shared_fd.cpp2
-rw-r--r--src/libcamera/base/signal.cpp7
-rw-r--r--src/libcamera/base/thread.cpp115
-rw-r--r--src/libcamera/base/timer.cpp12
-rw-r--r--src/libcamera/base/unique_fd.cpp2
-rw-r--r--src/libcamera/base/utils.cpp161
-rw-r--r--src/libcamera/bayer_format.cpp31
-rw-r--r--src/libcamera/byte_stream_buffer.cpp2
-rw-r--r--src/libcamera/camera.cpp61
-rw-r--r--src/libcamera/camera_controls.cpp2
-rw-r--r--src/libcamera/camera_lens.cpp2
-rw-r--r--src/libcamera/camera_manager.cpp116
-rw-r--r--src/libcamera/color_space.cpp2
-rw-r--r--src/libcamera/control_ids.cpp.in103
-rw-r--r--src/libcamera/control_ids.yaml1009
-rw-r--r--src/libcamera/control_ids_core.yaml1271
-rw-r--r--src/libcamera/control_ids_debug.yaml6
-rw-r--r--src/libcamera/control_ids_draft.yaml297
-rw-r--r--src/libcamera/control_ids_rpi.yaml74
-rw-r--r--src/libcamera/control_ranges.yaml20
-rw-r--r--src/libcamera/control_serializer.cpp10
-rw-r--r--src/libcamera/control_validator.cpp2
-rw-r--r--src/libcamera/controls.cpp155
-rw-r--r--src/libcamera/converter.cpp139
-rw-r--r--src/libcamera/converter/converter_v4l2_m2m.cpp378
-rw-r--r--src/libcamera/converter/meson.build2
-rw-r--r--src/libcamera/debug_controls.cpp164
-rw-r--r--src/libcamera/delayed_controls.cpp2
-rw-r--r--src/libcamera/device_enumerator.cpp14
-rw-r--r--src/libcamera/device_enumerator_sysfs.cpp4
-rw-r--r--src/libcamera/device_enumerator_udev.cpp10
-rw-r--r--src/libcamera/dma_buf_allocator.cpp356
-rw-r--r--src/libcamera/fence.cpp4
-rw-r--r--src/libcamera/formats.c0
-rw-r--r--src/libcamera/formats.cpp126
-rw-r--r--src/libcamera/formats.yaml26
-rw-r--r--src/libcamera/framebuffer.cpp9
-rw-r--r--src/libcamera/framebuffer_allocator.cpp15
-rw-r--r--src/libcamera/geometry.cpp79
-rw-r--r--src/libcamera/ipa/meson.build7
-rw-r--r--src/libcamera/ipa_controls.cpp6
-rw-r--r--src/libcamera/ipa_data_serializer.cpp8
-rw-r--r--src/libcamera/ipa_interface.cpp2
-rw-r--r--src/libcamera/ipa_manager.cpp30
-rw-r--r--src/libcamera/ipa_module.cpp9
-rw-r--r--src/libcamera/ipa_proxy.cpp52
-rw-r--r--src/libcamera/ipa_pub_key.cpp.in2
-rw-r--r--src/libcamera/ipc_pipe.cpp2
-rw-r--r--src/libcamera/ipc_pipe_unixsocket.cpp2
-rw-r--r--src/libcamera/ipc_unixsocket.cpp13
-rw-r--r--src/libcamera/mapped_framebuffer.cpp6
-rw-r--r--src/libcamera/matrix.cpp333
-rw-r--r--src/libcamera/media_device.cpp41
-rw-r--r--src/libcamera/media_object.cpp52
-rw-r--r--src/libcamera/media_pipeline.cpp304
-rw-r--r--src/libcamera/meson.build96
-rw-r--r--src/libcamera/orientation.cpp17
-rw-r--r--src/libcamera/pipeline/imx8-isi/imx8-isi.cpp38
-rw-r--r--src/libcamera/pipeline/imx8-isi/meson.build2
-rw-r--r--src/libcamera/pipeline/ipu3/cio2.cpp21
-rw-r--r--src/libcamera/pipeline/ipu3/cio2.h2
-rw-r--r--src/libcamera/pipeline/ipu3/frames.cpp5
-rw-r--r--src/libcamera/pipeline/ipu3/frames.h2
-rw-r--r--src/libcamera/pipeline/ipu3/imgu.cpp6
-rw-r--r--src/libcamera/pipeline/ipu3/imgu.h2
-rw-r--r--src/libcamera/pipeline/ipu3/ipu3.cpp52
-rw-r--r--src/libcamera/pipeline/ipu3/meson.build2
-rw-r--r--src/libcamera/pipeline/mali-c55/mali-c55.cpp1755
-rw-r--r--src/libcamera/pipeline/mali-c55/meson.build5
-rw-r--r--src/libcamera/pipeline/rkisp1/meson.build2
-rw-r--r--src/libcamera/pipeline/rkisp1/rkisp1.cpp628
-rw-r--r--src/libcamera/pipeline/rkisp1/rkisp1_path.cpp119
-rw-r--r--src/libcamera/pipeline/rkisp1/rkisp1_path.h13
-rw-r--r--src/libcamera/pipeline/rpi/common/delayed_controls.cpp2
-rw-r--r--src/libcamera/pipeline/rpi/common/delayed_controls.h2
-rw-r--r--src/libcamera/pipeline/rpi/common/meson.build2
-rw-r--r--src/libcamera/pipeline/rpi/common/pipeline_base.cpp223
-rw-r--r--src/libcamera/pipeline/rpi/common/pipeline_base.h30
-rw-r--r--src/libcamera/pipeline/rpi/common/rpi_stream.cpp2
-rw-r--r--src/libcamera/pipeline/rpi/common/rpi_stream.h22
-rw-r--r--src/libcamera/pipeline/rpi/common/shared_mem_object.h128
-rw-r--r--src/libcamera/pipeline/rpi/pisp/data/example.yaml45
-rw-r--r--src/libcamera/pipeline/rpi/pisp/data/meson.build8
-rw-r--r--src/libcamera/pipeline/rpi/pisp/meson.build12
-rw-r--r--src/libcamera/pipeline/rpi/pisp/pisp.cpp2372
-rw-r--r--src/libcamera/pipeline/rpi/vc4/data/meson.build3
-rw-r--r--src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp90
-rw-r--r--src/libcamera/pipeline/rpi/vc4/dma_heaps.h32
-rw-r--r--src/libcamera/pipeline/rpi/vc4/meson.build3
-rw-r--r--src/libcamera/pipeline/rpi/vc4/vc4.cpp51
-rw-r--r--src/libcamera/pipeline/simple/meson.build2
-rw-r--r--src/libcamera/pipeline/simple/simple.cpp497
-rw-r--r--src/libcamera/pipeline/uvcvideo/meson.build2
-rw-r--r--src/libcamera/pipeline/uvcvideo/uvcvideo.cpp235
-rw-r--r--src/libcamera/pipeline/vimc/meson.build2
-rw-r--r--src/libcamera/pipeline/vimc/vimc.cpp85
-rw-r--r--src/libcamera/pipeline/virtual/README.md65
-rw-r--r--src/libcamera/pipeline/virtual/config_parser.cpp264
-rw-r--r--src/libcamera/pipeline/virtual/config_parser.h39
-rw-r--r--src/libcamera/pipeline/virtual/data/meson.build4
-rw-r--r--src/libcamera/pipeline/virtual/data/virtual.yaml36
-rw-r--r--src/libcamera/pipeline/virtual/frame_generator.h29
-rw-r--r--src/libcamera/pipeline/virtual/image_frame_generator.cpp172
-rw-r--r--src/libcamera/pipeline/virtual/image_frame_generator.h49
-rw-r--r--src/libcamera/pipeline/virtual/meson.build15
-rw-r--r--src/libcamera/pipeline/virtual/test_pattern_generator.cpp125
-rw-r--r--src/libcamera/pipeline/virtual/test_pattern_generator.h48
-rw-r--r--src/libcamera/pipeline/virtual/virtual.cpp419
-rw-r--r--src/libcamera/pipeline/virtual/virtual.h62
-rw-r--r--src/libcamera/pipeline_handler.cpp149
-rw-r--r--src/libcamera/pixel_format.cpp2
-rw-r--r--src/libcamera/process.cpp10
-rw-r--r--src/libcamera/property_ids.cpp.in58
-rw-r--r--src/libcamera/property_ids_core.yaml (renamed from src/libcamera/property_ids.yaml)34
-rw-r--r--src/libcamera/property_ids_draft.yaml39
-rw-r--r--src/libcamera/proxy/meson.build5
-rw-r--r--src/libcamera/proxy/worker/meson.build6
-rw-r--r--src/libcamera/pub_key.cpp2
-rw-r--r--src/libcamera/request.cpp29
-rw-r--r--src/libcamera/sensor/camera_sensor.cpp583
-rw-r--r--src/libcamera/sensor/camera_sensor_legacy.cpp (renamed from src/libcamera/camera_sensor.cpp)727
-rw-r--r--src/libcamera/sensor/camera_sensor_properties.cpp (renamed from src/libcamera/camera_sensor_properties.cpp)216
-rw-r--r--src/libcamera/sensor/camera_sensor_raw.cpp1157
-rw-r--r--src/libcamera/sensor/meson.build8
-rw-r--r--src/libcamera/shared_mem_object.cpp231
-rw-r--r--src/libcamera/software_isp/TODO208
-rw-r--r--src/libcamera/software_isp/debayer.cpp179
-rw-r--r--src/libcamera/software_isp/debayer.h55
-rw-r--r--src/libcamera/software_isp/debayer_cpu.cpp875
-rw-r--r--src/libcamera/software_isp/debayer_cpu.h170
-rw-r--r--src/libcamera/software_isp/meson.build15
-rw-r--r--src/libcamera/software_isp/software_isp.cpp422
-rw-r--r--src/libcamera/software_isp/swstats_cpu.cpp434
-rw-r--r--src/libcamera/software_isp/swstats_cpu.h97
-rw-r--r--src/libcamera/source_paths.cpp2
-rw-r--r--src/libcamera/stream.cpp31
-rw-r--r--src/libcamera/sysfs.cpp2
-rw-r--r--src/libcamera/tracepoints.cpp2
-rw-r--r--src/libcamera/transform.cpp2
-rw-r--r--src/libcamera/v4l2_device.cpp103
-rw-r--r--src/libcamera/v4l2_pixelformat.cpp54
-rw-r--r--src/libcamera/v4l2_subdevice.cpp1400
-rw-r--r--src/libcamera/v4l2_videodevice.cpp166
-rw-r--r--src/libcamera/vector.cpp356
-rw-r--r--src/libcamera/version.cpp.in2
-rw-r--r--src/libcamera/yaml_parser.cpp261
-rw-r--r--src/meson.build31
-rw-r--r--src/py/cam/cam_qt.py6
-rw-r--r--src/py/cam/cam_qtgl.py12
-rwxr-xr-xsrc/py/libcamera/gen-py-controls.py119
-rw-r--r--src/py/libcamera/meson.build50
-rw-r--r--src/py/libcamera/py_camera_manager.h2
-rw-r--r--src/py/libcamera/py_color_space.cpp2
-rw-r--r--src/py/libcamera/py_controls_generated.cpp.in33
-rw-r--r--src/py/libcamera/py_enums.cpp5
-rw-r--r--src/py/libcamera/py_formats_generated.cpp.in2
-rw-r--r--src/py/libcamera/py_geometry.cpp2
-rw-r--r--src/py/libcamera/py_helpers.cpp16
-rw-r--r--src/py/libcamera/py_main.cpp29
-rw-r--r--src/py/libcamera/py_main.h10
-rw-r--r--src/py/libcamera/py_properties_generated.cpp.in30
-rw-r--r--src/py/libcamera/py_transform.cpp2
-rw-r--r--src/py/meson.build12
-rw-r--r--src/v4l2/meson.build11
-rw-r--r--src/v4l2/v4l2_camera.cpp14
-rw-r--r--src/v4l2/v4l2_camera.h11
-rw-r--r--src/v4l2/v4l2_camera_file.cpp2
-rw-r--r--src/v4l2/v4l2_camera_file.h2
-rw-r--r--src/v4l2/v4l2_camera_proxy.cpp88
-rw-r--r--src/v4l2/v4l2_camera_proxy.h5
-rw-r--r--src/v4l2/v4l2_compat.cpp80
-rw-r--r--src/v4l2/v4l2_compat_manager.cpp3
-rw-r--r--src/v4l2/v4l2_compat_manager.h2
605 files changed, 65378 insertions, 8009 deletions
diff --git a/src/android/camera3_hal.cpp b/src/android/camera3_hal.cpp
index da836bae..a5ad2374 100644
--- a/src/android/camera3_hal.cpp
+++ b/src/android/camera3_hal.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera3_hal.cpp - Android Camera HALv3 module
+ * Android Camera HALv3 module
*/
#include <hardware/camera_common.h>
diff --git a/src/android/camera_buffer.h b/src/android/camera_buffer.h
index b4531c80..96669962 100644
--- a/src/android/camera_buffer.h
+++ b/src/android/camera_buffer.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_buffer.h - Frame buffer handling interface definition
+ * Frame buffer handling interface definition
*/
#pragma once
diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index 1bfeaea4..b161bc6b 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_capabilities.cpp - Camera static properties manager
+ * Camera static properties manager
*/
#include "camera_capabilities.h"
@@ -11,6 +11,7 @@
#include <array>
#include <cmath>
#include <map>
+#include <stdint.h>
#include <type_traits>
#include <hardware/camera3.h>
@@ -1081,7 +1082,7 @@ int CameraCapabilities::initializeStaticMetadata()
}
{
- const Span<const Rectangle> &rects =
+ const Span<const Rectangle> rects =
properties.get(properties::PixelArrayActiveAreas).value_or(Span<const Rectangle>{});
std::vector<int32_t> data{
static_cast<int32_t>(rects[0].x),
@@ -1176,11 +1177,46 @@ int CameraCapabilities::initializeStaticMetadata()
maxFrameDuration_);
/* Statistics static metadata. */
- uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
- staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
- faceDetectMode);
-
int32_t maxFaceCount = 0;
+ auto iter = camera_->controls().find(controls::draft::FaceDetectMode.id());
+ if (iter != camera_->controls().end()) {
+ const ControlInfo &faceDetectCtrlInfo = iter->second;
+ std::vector<uint8_t> faceDetectModes;
+ bool hasFaceDetection = false;
+
+ for (const auto &value : faceDetectCtrlInfo.values()) {
+ int32_t mode = value.get<int32_t>();
+ uint8_t androidMode = 0;
+
+ switch (mode) {
+ case controls::draft::FaceDetectModeOff:
+ androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ break;
+ case controls::draft::FaceDetectModeSimple:
+ androidMode = ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE;
+ hasFaceDetection = true;
+ break;
+ default:
+ LOG(HAL, Fatal) << "Received invalid face detect mode: " << mode;
+ }
+ faceDetectModes.push_back(androidMode);
+ }
+ if (hasFaceDetection) {
+ /*
+ * \todo Create new libcamera controls to query max
+ * possible faces detected.
+ */
+ maxFaceCount = 10;
+ staticMetadata_->addEntry(
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ faceDetectModes.data(), faceDetectModes.size());
+ }
+ } else {
+ uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ faceDetectMode);
+ }
+
staticMetadata_->addEntry(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
maxFaceCount);
diff --git a/src/android/camera_capabilities.h b/src/android/camera_capabilities.h
index 6f66f221..56ac1efe 100644
--- a/src/android/camera_capabilities.h
+++ b/src/android/camera_capabilities.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_capabilities.h - Camera static properties manager
+ * Camera static properties manager
*/
#pragma once
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index 25cedd44..a038131a 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_device.cpp - libcamera Android Camera Device
+ * libcamera Android Camera Device
*/
#include "camera_device.h"
@@ -22,6 +22,7 @@
#include <libcamera/controls.h>
#include <libcamera/fence.h>
#include <libcamera/formats.h>
+#include <libcamera/geometry.h>
#include <libcamera/property_ids.h>
#include "system/graphics.h"
@@ -433,8 +434,6 @@ void CameraDevice::flush()
void CameraDevice::stop()
{
MutexLocker stateLock(stateMutex_);
- if (state_ == State::Stopped)
- return;
camera_->stop();
@@ -815,6 +814,11 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
controls.set(controls::ScalerCrop, cropRegion);
}
+ if (settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry)) {
+ const uint8_t *data = entry.data.u8;
+ controls.set(controls::draft::FaceDetectMode, data[0]);
+ }
+
if (settings.getEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, &entry)) {
const int32_t data = *entry.data.i32;
int32_t testPatternMode = controls::draft::TestPatternModeOff;
@@ -952,8 +956,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
*/
if (camera3Request->settings)
lastSettings_ = camera3Request->settings;
- else
- descriptor->settings_ = lastSettings_;
+
+ descriptor->settings_ = lastSettings_;
LOG(HAL, Debug) << "Queueing request " << descriptor->request_->cookie()
<< " with " << descriptor->buffers_.size() << " streams";
@@ -1108,6 +1112,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
}
if (state_ == State::Stopped) {
+ lastSettings_ = {};
+
ret = camera_->start();
if (ret) {
LOG(HAL, Error) << "Failed to start camera";
@@ -1540,8 +1546,9 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
value32 = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
resultMetadata->addEntry(ANDROID_SENSOR_TEST_PATTERN_MODE, value32);
- value = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
- resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, value);
+ if (settings.getEntry(ANDROID_STATISTICS_FACE_DETECT_MODE, &entry))
+ resultMetadata->addEntry(ANDROID_STATISTICS_FACE_DETECT_MODE,
+ entry.data.u8, 1);
value = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
resultMetadata->addEntry(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
@@ -1580,6 +1587,61 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
resultMetadata->addEntry(ANDROID_SENSOR_FRAME_DURATION,
*frameDuration * 1000);
+ const auto &faceDetectRectangles =
+ metadata.get(controls::draft::FaceDetectFaceRectangles);
+ if (faceDetectRectangles) {
+ std::vector<int32_t> flatRectangles;
+ for (const Rectangle &rect : *faceDetectRectangles) {
+ flatRectangles.push_back(rect.x);
+ flatRectangles.push_back(rect.y);
+ flatRectangles.push_back(rect.x + rect.width);
+ flatRectangles.push_back(rect.y + rect.height);
+ }
+ resultMetadata->addEntry(
+ ANDROID_STATISTICS_FACE_RECTANGLES, flatRectangles);
+ }
+
+ const auto &faceDetectFaceScores =
+ metadata.get(controls::draft::FaceDetectFaceScores);
+ if (faceDetectRectangles && faceDetectFaceScores) {
+ if (faceDetectFaceScores->size() != faceDetectRectangles->size()) {
+ LOG(HAL, Error) << "Pipeline returned wrong number of face scores; "
+ << "Expected: " << faceDetectRectangles->size()
+ << ", got: " << faceDetectFaceScores->size();
+ }
+ resultMetadata->addEntry(ANDROID_STATISTICS_FACE_SCORES,
+ *faceDetectFaceScores);
+ }
+
+ const auto &faceDetectFaceLandmarks =
+ metadata.get(controls::draft::FaceDetectFaceLandmarks);
+ if (faceDetectRectangles && faceDetectFaceLandmarks) {
+ size_t expectedLandmarks = faceDetectRectangles->size() * 3;
+ if (faceDetectFaceLandmarks->size() != expectedLandmarks) {
+ LOG(HAL, Error) << "Pipeline returned wrong number of face landmarks; "
+ << "Expected: " << expectedLandmarks
+ << ", got: " << faceDetectFaceLandmarks->size();
+ }
+
+ std::vector<int32_t> androidLandmarks;
+ for (const Point &landmark : *faceDetectFaceLandmarks) {
+ androidLandmarks.push_back(landmark.x);
+ androidLandmarks.push_back(landmark.y);
+ }
+ resultMetadata->addEntry(
+ ANDROID_STATISTICS_FACE_LANDMARKS, androidLandmarks);
+ }
+
+ const auto &faceDetectFaceIds = metadata.get(controls::draft::FaceDetectFaceIds);
+ if (faceDetectRectangles && faceDetectFaceIds) {
+ if (faceDetectFaceIds->size() != faceDetectRectangles->size()) {
+ LOG(HAL, Error) << "Pipeline returned wrong number of face ids; "
+ << "Expected: " << faceDetectRectangles->size()
+ << ", got: " << faceDetectFaceIds->size();
+ }
+ resultMetadata->addEntry(ANDROID_STATISTICS_FACE_IDS, *faceDetectFaceIds);
+ }
+
const auto &scalerCrop = metadata.get(controls::ScalerCrop);
if (scalerCrop) {
const Rectangle &crop = *scalerCrop;
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 43ee0159..194ca303 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_device.h - libcamera Android Camera Device
+ * libcamera Android Camera Device
*/
#pragma once
diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp
index 0e7cde63..7ef451ef 100644
--- a/src/android/camera_hal_config.cpp
+++ b/src/android/camera_hal_config.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_hal_config.cpp - Camera HAL configuration file manager
+ * Camera HAL configuration file manager
*/
#include "camera_hal_config.h"
diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h
index 9df554f9..a4bedb6e 100644
--- a/src/android/camera_hal_config.h
+++ b/src/android/camera_hal_config.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_hal_config.h - Camera HAL configuration file manager
+ * Camera HAL configuration file manager
*/
#pragma once
diff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp
index a86e23d4..7500c749 100644
--- a/src/android/camera_hal_manager.cpp
+++ b/src/android/camera_hal_manager.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_hal_manager.cpp - libcamera Android Camera Manager
+ * libcamera Android Camera Manager
*/
#include "camera_hal_manager.h"
diff --git a/src/android/camera_hal_manager.h b/src/android/camera_hal_manager.h
index a5f8b933..836a8daf 100644
--- a/src/android/camera_hal_manager.h
+++ b/src/android/camera_hal_manager.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_hal_manager.h - libcamera Android Camera Manager
+ * libcamera Android Camera Manager
*/
#pragma once
diff --git a/src/android/camera_metadata.cpp b/src/android/camera_metadata.cpp
index b3e515d2..99f033f9 100644
--- a/src/android/camera_metadata.cpp
+++ b/src/android/camera_metadata.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_metadata.cpp - libcamera Android Camera Metadata Helper
+ * libcamera Android Camera Metadata Helper
*/
#include "camera_metadata.h"
diff --git a/src/android/camera_metadata.h b/src/android/camera_metadata.h
index 0c31ec6b..474f280c 100644
--- a/src/android/camera_metadata.h
+++ b/src/android/camera_metadata.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_metadata.h - libcamera Android Camera Metadata Helper
+ * libcamera Android Camera Metadata Helper
*/
#pragma once
diff --git a/src/android/camera_ops.cpp b/src/android/camera_ops.cpp
index 8a3cfa17..ecaac5a3 100644
--- a/src/android/camera_ops.cpp
+++ b/src/android/camera_ops.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_ops.h - Android Camera HAL Operations
+ * Android Camera HAL Operations
*/
#include "camera_ops.h"
diff --git a/src/android/camera_ops.h b/src/android/camera_ops.h
index b501bb7e..750dc945 100644
--- a/src/android/camera_ops.h
+++ b/src/android/camera_ops.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_ops.h - Android Camera HAL Operations
+ * Android Camera HAL Operations
*/
#pragma once
diff --git a/src/android/camera_request.cpp b/src/android/camera_request.cpp
index 6c87adba..0d45960d 100644
--- a/src/android/camera_request.cpp
+++ b/src/android/camera_request.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Google Inc.
*
- * camera_request.cpp - libcamera Android Camera Request Descriptor
+ * libcamera Android Camera Request Descriptor
*/
#include "camera_request.h"
diff --git a/src/android/camera_request.h b/src/android/camera_request.h
index 20aba79d..5b479180 100644
--- a/src/android/camera_request.h
+++ b/src/android/camera_request.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Google Inc.
*
- * camera_request.h - libcamera Android Camera Request Descriptor
+ * libcamera Android Camera Request Descriptor
*/
#pragma once
diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp
index 045e6006..1d68540d 100644
--- a/src/android/camera_stream.cpp
+++ b/src/android/camera_stream.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * camera_stream.cpp - Camera HAL stream
+ * Camera HAL stream
*/
#include "camera_stream.h"
diff --git a/src/android/camera_stream.h b/src/android/camera_stream.h
index 4c5078b2..395552da 100644
--- a/src/android/camera_stream.h
+++ b/src/android/camera_stream.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * camera_stream.h - Camera HAL stream
+ * Camera HAL stream
*/
#pragma once
diff --git a/src/android/cros/camera3_hal.cpp b/src/android/cros/camera3_hal.cpp
index 71acb441..6010a5ad 100644
--- a/src/android/cros/camera3_hal.cpp
+++ b/src/android/cros/camera3_hal.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera3_hal.cpp - cros-specific components of Android Camera HALv3 module
+ * cros-specific components of Android Camera HALv3 module
*/
#include <cros-camera/cros_camera_hal.h>
diff --git a/src/android/cros_mojo_token.h b/src/android/cros_mojo_token.h
index 043c752a..d0baa80f 100644
--- a/src/android/cros_mojo_token.h
+++ b/src/android/cros_mojo_token.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * cros_mojo_token.h - cros-specific mojo token
+ * cros-specific mojo token
*/
#pragma once
diff --git a/src/android/frame_buffer_allocator.h b/src/android/frame_buffer_allocator.h
index e5c94922..3e68641c 100644
--- a/src/android/frame_buffer_allocator.h
+++ b/src/android/frame_buffer_allocator.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * frame_buffer_allocator.h - Interface definition to allocate Frame buffer in
+ * Interface definition to allocate Frame buffer in
* platform dependent way.
*/
#ifndef __ANDROID_FRAME_BUFFER_ALLOCATOR_H__
diff --git a/src/android/hal_framebuffer.cpp b/src/android/hal_framebuffer.cpp
index 3f3d1ed1..d4899f45 100644
--- a/src/android/hal_framebuffer.cpp
+++ b/src/android/hal_framebuffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * hal_framebuffer.cpp - HAL Frame Buffer Handling
+ * HAL Frame Buffer Handling
*/
#include "hal_framebuffer.h"
diff --git a/src/android/hal_framebuffer.h b/src/android/hal_framebuffer.h
index dc96a7e1..cea49e2d 100644
--- a/src/android/hal_framebuffer.h
+++ b/src/android/hal_framebuffer.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * hal_framebuffer.h - HAL Frame Buffer Handling
+ * HAL Frame Buffer Handling
*/
#pragma once
diff --git a/src/android/jpeg/encoder.h b/src/android/jpeg/encoder.h
index 31f26895..ed033c19 100644
--- a/src/android/jpeg/encoder.h
+++ b/src/android/jpeg/encoder.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * encoder.h - Image encoding interface
+ * Image encoding interface
*/
#pragma once
diff --git a/src/android/jpeg/encoder_jea.cpp b/src/android/jpeg/encoder_jea.cpp
index 7880a6bd..25dc4317 100644
--- a/src/android/jpeg/encoder_jea.cpp
+++ b/src/android/jpeg/encoder_jea.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * encoder_jea.cpp - JPEG encoding using CrOS JEA
+ * JPEG encoding using CrOS JEA
*/
#include "encoder_jea.h"
diff --git a/src/android/jpeg/encoder_jea.h b/src/android/jpeg/encoder_jea.h
index ffe9df27..91115d2e 100644
--- a/src/android/jpeg/encoder_jea.h
+++ b/src/android/jpeg/encoder_jea.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * encoder_jea.h - JPEG encoding using CrOS JEA
+ * JPEG encoding using CrOS JEA
*/
#pragma once
diff --git a/src/android/jpeg/encoder_libjpeg.cpp b/src/android/jpeg/encoder_libjpeg.cpp
index f4e8dfad..cb242b5e 100644
--- a/src/android/jpeg/encoder_libjpeg.cpp
+++ b/src/android/jpeg/encoder_libjpeg.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * encoder_libjpeg.cpp - JPEG encoding using libjpeg native API
+ * JPEG encoding using libjpeg native API
*/
#include "encoder_libjpeg.h"
@@ -125,7 +125,7 @@ void EncoderLibJpeg::compressRGB(const std::vector<Span<uint8_t>> &planes)
*/
void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)
{
- uint8_t tmprowbuf[compress_.image_width * 3];
+ std::vector<uint8_t> tmprowbuf(compress_.image_width * 3);
/*
* \todo Use the raw api, and only unpack the cb/cr samples to new line
@@ -149,10 +149,10 @@ void EncoderLibJpeg::compressNV(const std::vector<Span<uint8_t>> &planes)
const unsigned char *src_c = planes[1].data();
JSAMPROW row_pointer[1];
- row_pointer[0] = &tmprowbuf[0];
+ row_pointer[0] = tmprowbuf.data();
for (unsigned int y = 0; y < compress_.image_height; y++) {
- unsigned char *dst = &tmprowbuf[0];
+ unsigned char *dst = tmprowbuf.data();
const unsigned char *src_y = src + y * y_stride;
const unsigned char *src_cb = src_c + (y / vertSubSample) * c_stride + cb_pos;
diff --git a/src/android/jpeg/encoder_libjpeg.h b/src/android/jpeg/encoder_libjpeg.h
index 146a6a72..4ac85c22 100644
--- a/src/android/jpeg/encoder_libjpeg.h
+++ b/src/android/jpeg/encoder_libjpeg.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * encoder_libjpeg.h - JPEG encoding using libjpeg
+ * JPEG encoding using libjpeg
*/
#pragma once
diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp
index 6b1d0f1f..b8c871df 100644
--- a/src/android/jpeg/exif.cpp
+++ b/src/android/jpeg/exif.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * exif.cpp - EXIF tag creation using libexif
+ * EXIF tag creation using libexif
*/
#include "exif.h"
diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h
index e68716f3..446d53f3 100644
--- a/src/android/jpeg/exif.h
+++ b/src/android/jpeg/exif.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * exif.h - EXIF tag creator using libexif
+ * EXIF tag creator using libexif
*/
#pragma once
diff --git a/src/android/jpeg/post_processor_jpeg.cpp b/src/android/jpeg/post_processor_jpeg.cpp
index 40261652..89b8a401 100644
--- a/src/android/jpeg/post_processor_jpeg.cpp
+++ b/src/android/jpeg/post_processor_jpeg.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * post_processor_jpeg.cpp - JPEG Post Processor
+ * JPEG Post Processor
*/
#include "post_processor_jpeg.h"
diff --git a/src/android/jpeg/post_processor_jpeg.h b/src/android/jpeg/post_processor_jpeg.h
index 98309b01..6fe21457 100644
--- a/src/android/jpeg/post_processor_jpeg.h
+++ b/src/android/jpeg/post_processor_jpeg.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * post_processor_jpeg.h - JPEG Post Processor
+ * JPEG Post Processor
*/
#pragma once
diff --git a/src/android/jpeg/thumbnailer.cpp b/src/android/jpeg/thumbnailer.cpp
index 41c71c76..adafc468 100644
--- a/src/android/jpeg/thumbnailer.cpp
+++ b/src/android/jpeg/thumbnailer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * thumbnailer.cpp - Simple image thumbnailer
+ * Simple image thumbnailer
*/
#include "thumbnailer.h"
diff --git a/src/android/jpeg/thumbnailer.h b/src/android/jpeg/thumbnailer.h
index d933cf0e..1b836e59 100644
--- a/src/android/jpeg/thumbnailer.h
+++ b/src/android/jpeg/thumbnailer.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * thumbnailer.h - Simple image thumbnailer
+ * Simple image thumbnailer
*/
#pragma once
diff --git a/src/android/meson.build b/src/android/meson.build
index 68646120..7b226a4b 100644
--- a/src/android/meson.build
+++ b/src/android/meson.build
@@ -4,6 +4,7 @@ android_deps = [
dependency('libexif', required : get_option('android')),
dependency('libjpeg', required : get_option('android')),
libcamera_private,
+ libyuv_dep,
]
android_enabled = true
@@ -15,27 +16,6 @@ foreach dep : android_deps
endif
endforeach
-libyuv_dep = dependency('libyuv', required : false)
-
-# Fallback to a subproject if libyuv isn't found, as it's typically not
-# provided by distributions.
-if not libyuv_dep.found()
- cmake = import('cmake')
-
- libyuv_vars = cmake.subproject_options()
- libyuv_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'})
- libyuv_vars.set_override_option('cpp_std', 'c++17')
- libyuv_vars.append_compile_args('cpp',
- '-Wno-sign-compare',
- '-Wno-unused-variable',
- '-Wno-unused-parameter')
- libyuv_vars.append_link_args('-ljpeg')
- libyuv = cmake.subproject('libyuv', options : libyuv_vars)
- libyuv_dep = libyuv.dependency('yuv')
-endif
-
-android_deps += [libyuv_dep]
-
android_hal_sources = files([
'camera3_hal.cpp',
'camera_capabilities.cpp',
diff --git a/src/android/mm/cros_camera_buffer.cpp b/src/android/mm/cros_camera_buffer.cpp
index 2ac3dc4a..e2a44a2a 100644
--- a/src/android/mm/cros_camera_buffer.cpp
+++ b/src/android/mm/cros_camera_buffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * cros_camera_buffer.cpp - Chromium OS buffer backend using CameraBufferManager
+ * Chromium OS buffer backend using CameraBufferManager
*/
#include "../camera_buffer.h"
diff --git a/src/android/mm/cros_frame_buffer_allocator.cpp b/src/android/mm/cros_frame_buffer_allocator.cpp
index 0a5c59f2..264c0d48 100644
--- a/src/android/mm/cros_frame_buffer_allocator.cpp
+++ b/src/android/mm/cros_frame_buffer_allocator.cpp
@@ -2,8 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * cros_frame_buffer.cpp - Allocate FrameBuffer for Chromium OS using
- * CameraBufferManager
+ * Allocate FrameBuffer for Chromium OS using CameraBufferManager
*/
#include <memory>
diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp
index 1bd7090d..0ffcb445 100644
--- a/src/android/mm/generic_camera_buffer.cpp
+++ b/src/android/mm/generic_camera_buffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * generic_camera_buffer.cpp - Generic Android frame buffer backend
+ * Generic Android frame buffer backend
*/
#include "../camera_buffer.h"
diff --git a/src/android/mm/generic_frame_buffer_allocator.cpp b/src/android/mm/generic_frame_buffer_allocator.cpp
index 7ecef2c6..79625a9a 100644
--- a/src/android/mm/generic_frame_buffer_allocator.cpp
+++ b/src/android/mm/generic_frame_buffer_allocator.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * generic_camera_buffer.cpp - Allocate FrameBuffer using gralloc API
+ * Allocate FrameBuffer using gralloc API
*/
#include <dlfcn.h>
diff --git a/src/android/mm/libhardware_stub.c b/src/android/mm/libhardware_stub.c
index 00f15cd9..28faa638 100644
--- a/src/android/mm/libhardware_stub.c
+++ b/src/android/mm/libhardware_stub.c
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Ideas on Board
*
- * libhardware_stub.c - Android libhardware stub for test compilation
+ * Android libhardware stub for test compilation
*/
#include <errno.h>
diff --git a/src/android/post_processor.h b/src/android/post_processor.h
index 1a205b05..b504a379 100644
--- a/src/android/post_processor.h
+++ b/src/android/post_processor.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * post_processor.h - CameraStream Post Processing Interface
+ * CameraStream Post Processing Interface
*/
#pragma once
diff --git a/src/android/yuv/post_processor_yuv.cpp b/src/android/yuv/post_processor_yuv.cpp
index ed44e6fe..c998807b 100644
--- a/src/android/yuv/post_processor_yuv.cpp
+++ b/src/android/yuv/post_processor_yuv.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * post_processor_yuv.cpp - Post Processor using libyuv
+ * Post Processor using libyuv
*/
#include "post_processor_yuv.h"
diff --git a/src/android/yuv/post_processor_yuv.h b/src/android/yuv/post_processor_yuv.h
index a7ac17c5..ed7bb1fb 100644
--- a/src/android/yuv/post_processor_yuv.h
+++ b/src/android/yuv/post_processor_yuv.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * post_processor_yuv.h - Post Processor using libyuv
+ * Post Processor using libyuv
*/
#pragma once
diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
index 8447f932..f63fcb22 100644
--- a/src/apps/cam/camera_session.cpp
+++ b/src/apps/cam/camera_session.cpp
@@ -2,12 +2,15 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_session.cpp - Camera capture session
+ * Camera capture session
*/
+#include "camera_session.h"
+
#include <iomanip>
#include <iostream>
#include <limits.h>
+#include <optional>
#include <sstream>
#include <libcamera/control_ids.h>
@@ -16,7 +19,6 @@
#include "../common/event_loop.h"
#include "../common/stream_options.h"
-#include "camera_session.h"
#include "capture_script.h"
#include "file_sink.h"
#ifdef HAVE_KMS
@@ -39,9 +41,14 @@ CameraSession::CameraSession(CameraManager *cm,
{
char *endptr;
unsigned long index = strtoul(cameraId.c_str(), &endptr, 10);
- if (*endptr == '\0' && index > 0 && index <= cm->cameras().size())
- camera_ = cm->cameras()[index - 1];
- else
+
+ if (*endptr == '\0' && index > 0) {
+ auto cameras = cm->cameras();
+ if (index <= cameras.size())
+ camera_ = cameras[index - 1];
+ }
+
+ if (!camera_)
camera_ = cm->get(cameraId);
if (!camera_) {
@@ -55,11 +62,32 @@ CameraSession::CameraSession(CameraManager *cm,
return;
}
- std::vector<StreamRole> roles = StreamKeyValueParser::roles(options_[OptStream]);
+ std::vector<StreamRole> roles =
+ StreamKeyValueParser::roles(options_[OptStream]);
+ std::vector<std::vector<StreamRole>> tryRoles;
+ if (!roles.empty()) {
+ /*
+ * If the roles are explicitly specified then there's no need
+ * to try other roles
+ */
+ tryRoles.push_back(roles);
+ } else {
+ tryRoles.push_back({ StreamRole::Viewfinder });
+ tryRoles.push_back({ StreamRole::Raw });
+ }
+
+ std::unique_ptr<CameraConfiguration> config;
+ bool valid = false;
+ for (std::vector<StreamRole> &rolesIt : tryRoles) {
+ config = camera_->generateConfiguration(rolesIt);
+ if (config && config->size() == rolesIt.size()) {
+ roles = rolesIt;
+ valid = true;
+ break;
+ }
+ }
- std::unique_ptr<CameraConfiguration> config =
- camera_->generateConfiguration(roles);
- if (!config || config->size() != roles.size()) {
+ if (!valid) {
std::cerr << "Failed to get default stream configuration"
<< std::endl;
return;
@@ -154,8 +182,51 @@ CameraSession::~CameraSession()
void CameraSession::listControls() const
{
for (const auto &[id, info] : camera_->controls()) {
- std::cout << "Control: " << id->name() << ": "
- << info.toString() << std::endl;
+ std::stringstream io;
+ io << "["
+ << (id->isInput() ? "in" : " ")
+ << (id->isOutput() ? "out" : " ")
+ << "] ";
+
+ if (info.values().empty()) {
+ std::cout << "Control: " << io.str()
+ << id->vendor() << "::" << id->name() << ": "
+ << info.toString() << std::endl;
+ } else {
+ std::cout << "Control: " << io.str()
+ << id->vendor() << "::" << id->name() << ":"
+ << std::endl;
+
+ std::optional<int32_t> def;
+ if (!info.def().isNone())
+ def = info.def().get<int32_t>();
+
+ for (const auto &value : info.values()) {
+ int32_t val = value.get<int32_t>();
+ const auto &it = id->enumerators().find(val);
+
+ std::cout << " - ";
+ if (it == id->enumerators().end())
+ std::cout << "UNKNOWN";
+ else
+ std::cout << it->second;
+
+ std::cout << " (" << val << ")"
+ << (val == def ? " [default]" : "")
+ << std::endl;
+ }
+ }
+
+ if (id->isArray()) {
+ std::size_t size = id->size();
+
+ std::cout << " Size: ";
+ if (size == std::numeric_limits<std::size_t>::max())
+ std::cout << "n";
+ else
+ std::cout << std::to_string(size);
+ std::cout << std::endl;
+ }
}
}
@@ -225,11 +296,16 @@ int CameraSession::start()
#endif
if (options_.isSet(OptFile)) {
- if (!options_[OptFile].toString().empty())
- sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_,
- options_[OptFile]);
- else
- sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_);
+ std::unique_ptr<FileSink> sink =
+ std::make_unique<FileSink>(camera_.get(), streamNames_);
+
+ if (!options_[OptFile].toString().empty()) {
+ ret = sink->setFilePattern(options_[OptFile]);
+ if (ret)
+ return ret;
+ }
+
+ sink_ = std::move(sink);
}
if (sink_) {
@@ -377,7 +453,7 @@ void CameraSession::requestComplete(Request *request)
* Defer processing of the completed request to the event loop, to avoid
* blocking the camera manager thread.
*/
- EventLoop::instance()->callLater([=]() { processRequest(request); });
+ EventLoop::instance()->callLater([this, request]() { processRequest(request); });
}
void CameraSession::processRequest(Request *request)
diff --git a/src/apps/cam/camera_session.h b/src/apps/cam/camera_session.h
index 0bab519f..4442fd9b 100644
--- a/src/apps/cam/camera_session.h
+++ b/src/apps/cam/camera_session.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_session.h - Camera capture session
+ * Camera capture session
*/
#pragma once
diff --git a/src/apps/cam/capture_script.cpp b/src/apps/cam/capture_script.cpp
index 062a7258..fdf82efc 100644
--- a/src/apps/cam/capture_script.cpp
+++ b/src/apps/cam/capture_script.cpp
@@ -2,12 +2,13 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * capture_script.cpp - Capture session configuration script
+ * Capture session configuration script
*/
#include "capture_script.h"
#include <iostream>
+#include <memory>
#include <stdio.h>
#include <stdlib.h>
@@ -351,7 +352,10 @@ ControlValue CaptureScript::parseRectangles()
}
ControlValue controlValue;
- controlValue.set(Span<const Rectangle>(rectangles));
+ if (rectangles.size() == 1)
+ controlValue.set(rectangles.at(0));
+ else
+ controlValue.set(Span<const Rectangle>(rectangles));
return controlValue;
}
@@ -518,45 +522,22 @@ ControlValue CaptureScript::parseArrayControl(const ControlId *id,
case ControlTypeNone:
break;
case ControlTypeBool: {
- /*
- * This is unpleasant, but we cannot use an std::vector<> as its
- * boolean type overload does not allow to access the raw data,
- * as boolean values are stored in a bitmask for efficiency.
- *
- * As we need a contiguous memory region to wrap in a Span<>,
- * use an array instead but be strict about not overflowing it
- * by limiting the number of controls we can store.
- *
- * Be loud but do not fail, as the issue would present at
- * runtime and it's not fatal.
- */
- static constexpr unsigned int kMaxNumBooleanControls = 1024;
- std::array<bool, kMaxNumBooleanControls> values;
- unsigned int idx = 0;
+ auto values = std::make_unique<bool[]>(repr.size());
- for (const std::string &s : repr) {
- bool val;
+ for (std::size_t i = 0; i < repr.size(); i++) {
+ const auto &s = repr[i];
if (s == "true") {
- val = true;
+ values[i] = true;
} else if (s == "false") {
- val = false;
+ values[i] = false;
} else {
unpackFailure(id, s);
return value;
}
-
- if (idx == kMaxNumBooleanControls) {
- std::cerr << "Cannot parse more than "
- << kMaxNumBooleanControls
- << " boolean controls" << std::endl;
- break;
- }
-
- values[idx++] = val;
}
- value = Span<bool>(values.data(), idx);
+ value = Span<bool>(values.get(), repr.size());
break;
}
case ControlTypeByte: {
@@ -597,10 +578,6 @@ ControlValue CaptureScript::parseArrayControl(const ControlId *id,
value = Span<const float>(values.data(), values.size());
break;
}
- case ControlTypeString: {
- value = Span<const std::string>(repr.data(), repr.size());
- break;
- }
default:
std::cerr << "Unsupported control type" << std::endl;
break;
diff --git a/src/apps/cam/capture_script.h b/src/apps/cam/capture_script.h
index 40042c03..294b9203 100644
--- a/src/apps/cam/capture_script.h
+++ b/src/apps/cam/capture_script.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * capture_script.h - Capture session configuration script
+ * Capture session configuration script
*/
#pragma once
diff --git a/src/apps/cam/drm.cpp b/src/apps/cam/drm.cpp
index 8779a713..47bbb6b0 100644
--- a/src/apps/cam/drm.cpp
+++ b/src/apps/cam/drm.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * drm.cpp - DRM/KMS Helpers
+ * DRM/KMS Helpers
*/
#include "drm.h"
diff --git a/src/apps/cam/drm.h b/src/apps/cam/drm.h
index ebaea04d..1ba83b6e 100644
--- a/src/apps/cam/drm.h
+++ b/src/apps/cam/drm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * drm.h - DRM/KMS Helpers
+ * DRM/KMS Helpers
*/
#pragma once
diff --git a/src/apps/cam/file_sink.cpp b/src/apps/cam/file_sink.cpp
index dca350c4..65794a2f 100644
--- a/src/apps/cam/file_sink.cpp
+++ b/src/apps/cam/file_sink.cpp
@@ -2,9 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * file_sink.cpp - File Sink
+ * File Sink
*/
+#include "file_sink.h"
+
+#include <array>
#include <assert.h>
#include <fcntl.h>
#include <iomanip>
@@ -12,24 +15,24 @@
#include <sstream>
#include <string.h>
#include <unistd.h>
+#include <utility>
#include <libcamera/camera.h>
#include "../common/dng_writer.h"
#include "../common/image.h"
-
-#include "file_sink.h"
+#include "../common/ppm_writer.h"
using namespace libcamera;
FileSink::FileSink([[maybe_unused]] const libcamera::Camera *camera,
- const std::map<const libcamera::Stream *, std::string> &streamNames,
- const std::string &pattern)
+ const std::map<const libcamera::Stream *, std::string> &streamNames)
:
#ifdef HAVE_TIFF
camera_(camera),
#endif
- streamNames_(streamNames), pattern_(pattern)
+ pattern_(kDefaultFilePattern), fileType_(FileType::Binary),
+ streamNames_(streamNames)
{
}
@@ -37,6 +40,41 @@ FileSink::~FileSink()
{
}
+int FileSink::setFilePattern(const std::string &pattern)
+{
+ static const std::array<std::pair<std::string, FileType>, 2> types{{
+ { ".dng", FileType::Dng },
+ { ".ppm", FileType::Ppm },
+ }};
+
+ pattern_ = pattern;
+
+ if (pattern_.empty() || pattern_.back() == '/')
+ pattern_ += kDefaultFilePattern;
+
+ fileType_ = FileType::Binary;
+
+ for (const auto &type : types) {
+ if (pattern_.size() < type.first.size())
+ continue;
+
+ if (pattern_.find(type.first, pattern_.size() - type.first.size()) !=
+ std::string::npos) {
+ fileType_ = type.second;
+ break;
+ }
+ }
+
+#ifndef HAVE_TIFF
+ if (fileType_ == FileType::Dng) {
+ std::cerr << "DNG support not available" << std::endl;
+ return -EINVAL;
+ }
+#endif /* HAVE_TIFF */
+
+ return 0;
+}
+
int FileSink::configure(const libcamera::CameraConfiguration &config)
{
int ret = FrameSink::configure(config);
@@ -66,20 +104,10 @@ bool FileSink::processRequest(Request *request)
void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
[[maybe_unused]] const ControlList &metadata)
{
- std::string filename;
+ std::string filename = pattern_;
size_t pos;
int fd, ret = 0;
- if (!pattern_.empty())
- filename = pattern_;
-
-#ifdef HAVE_TIFF
- bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos;
-#endif /* HAVE_TIFF */
-
- if (filename.empty() || filename.back() == '/')
- filename += "frame-#.bin";
-
pos = filename.find_first_of('#');
if (pos != std::string::npos) {
std::stringstream ss;
@@ -91,7 +119,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
Image *image = mappedBuffers_[buffer].get();
#ifdef HAVE_TIFF
- if (dng) {
+ if (fileType_ == FileType::Dng) {
ret = DNGWriter::write(filename.c_str(), camera_,
stream->configuration(), metadata,
buffer, image->data(0).data());
@@ -102,6 +130,15 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer,
return;
}
#endif /* HAVE_TIFF */
+ if (fileType_ == FileType::Ppm) {
+ ret = PPMWriter::write(filename.c_str(), stream->configuration(),
+ image->data(0));
+ if (ret < 0)
+ std::cerr << "failed to write PPM file `" << filename
+ << "'" << std::endl;
+
+ return;
+ }
fd = open(filename.c_str(), O_CREAT | O_WRONLY |
(pos == std::string::npos ? O_APPEND : O_TRUNC),
diff --git a/src/apps/cam/file_sink.h b/src/apps/cam/file_sink.h
index 300edf8d..26cd61b3 100644
--- a/src/apps/cam/file_sink.h
+++ b/src/apps/cam/file_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * file_sink.h - File Sink
+ * File Sink
*/
#pragma once
@@ -11,6 +11,7 @@
#include <memory>
#include <string>
+#include <libcamera/controls.h>
#include <libcamera/stream.h>
#include "frame_sink.h"
@@ -21,10 +22,11 @@ class FileSink : public FrameSink
{
public:
FileSink(const libcamera::Camera *camera,
- const std::map<const libcamera::Stream *, std::string> &streamNames,
- const std::string &pattern = "");
+ const std::map<const libcamera::Stream *, std::string> &streamNames);
~FileSink();
+ int setFilePattern(const std::string &pattern);
+
int configure(const libcamera::CameraConfiguration &config) override;
void mapBuffer(libcamera::FrameBuffer *buffer) override;
@@ -32,6 +34,14 @@ public:
bool processRequest(libcamera::Request *request) override;
private:
+ static constexpr const char *kDefaultFilePattern = "frame-#.bin";
+
+ enum class FileType {
+ Binary,
+ Dng,
+ Ppm,
+ };
+
void writeBuffer(const libcamera::Stream *stream,
libcamera::FrameBuffer *buffer,
const libcamera::ControlList &metadata);
@@ -39,7 +49,10 @@ private:
#ifdef HAVE_TIFF
const libcamera::Camera *camera_;
#endif
- std::map<const libcamera::Stream *, std::string> streamNames_;
+
std::string pattern_;
+ FileType fileType_;
+
+ std::map<const libcamera::Stream *, std::string> streamNames_;
std::map<libcamera::FrameBuffer *, std::unique_ptr<Image>> mappedBuffers_;
};
diff --git a/src/apps/cam/frame_sink.cpp b/src/apps/cam/frame_sink.cpp
index af21d575..68d6f2c1 100644
--- a/src/apps/cam/frame_sink.cpp
+++ b/src/apps/cam/frame_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * frame_sink.cpp - Base Frame Sink Class
+ * Base Frame Sink Class
*/
#include "frame_sink.h"
diff --git a/src/apps/cam/frame_sink.h b/src/apps/cam/frame_sink.h
index ca4347cb..11105c6c 100644
--- a/src/apps/cam/frame_sink.h
+++ b/src/apps/cam/frame_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * frame_sink.h - Base Frame Sink Class
+ * Base Frame Sink Class
*/
#pragma once
diff --git a/src/apps/cam/kms_sink.cpp b/src/apps/cam/kms_sink.cpp
index 6991c3fa..672c985a 100644
--- a/src/apps/cam/kms_sink.cpp
+++ b/src/apps/cam/kms_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * kms_sink.cpp - KMS Sink
+ * KMS Sink
*/
#include "kms_sink.h"
diff --git a/src/apps/cam/kms_sink.h b/src/apps/cam/kms_sink.h
index e2c618a1..4b7b4c26 100644
--- a/src/apps/cam/kms_sink.h
+++ b/src/apps/cam/kms_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * kms_sink.h - KMS Sink
+ * KMS Sink
*/
#pragma once
diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
index 179cc376..fa266eca 100644
--- a/src/apps/cam/main.cpp
+++ b/src/apps/cam/main.cpp
@@ -2,9 +2,11 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.cpp - cam - The libcamera swiss army knife
+ * cam - The libcamera swiss army knife
*/
+#include "main.h"
+
#include <atomic>
#include <iomanip>
#include <iostream>
@@ -19,7 +21,6 @@
#include "../common/stream_options.h"
#include "camera_session.h"
-#include "main.h"
using namespace libcamera;
@@ -154,6 +155,8 @@ int CamApp::parseOptions(int argc, char *argv[])
"If the file name ends with '.dng', then the frame will be written to\n"
"the output file(s) in DNG format.\n"
#endif
+ "If the file name ends with '.ppm', then the frame will be written to\n"
+ "the output file(s) in PPM format.\n"
"The default file name is 'frame-#.bin'.",
"file", ArgumentOptional, "filename", false,
OptCamera);
@@ -342,12 +345,16 @@ std::string CamApp::cameraName(const Camera *camera)
return name;
}
+namespace {
+
void signalHandler([[maybe_unused]] int signal)
{
std::cout << "Exiting" << std::endl;
CamApp::instance()->quit();
}
+} /* namespace */
+
int main(int argc, char **argv)
{
CamApp app;
diff --git a/src/apps/cam/main.h b/src/apps/cam/main.h
index 4aa959b3..64e6a20e 100644
--- a/src/apps/cam/main.h
+++ b/src/apps/cam/main.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.h - Cam application
+ * Cam application
*/
#pragma once
diff --git a/src/apps/cam/meson.build b/src/apps/cam/meson.build
index 48c834ac..2833c86e 100644
--- a/src/apps/cam/meson.build
+++ b/src/apps/cam/meson.build
@@ -34,6 +34,7 @@ if libsdl2.found()
cam_sources += files([
'sdl_sink.cpp',
'sdl_texture.cpp',
+ 'sdl_texture_1plane.cpp',
'sdl_texture_yuv.cpp',
])
@@ -58,4 +59,5 @@ cam = executable('cam', cam_sources,
libyaml,
],
cpp_args : cam_cpp_args,
- install : true)
+ install : true,
+ install_tag : 'bin')
diff --git a/src/apps/cam/sdl_sink.cpp b/src/apps/cam/sdl_sink.cpp
index a2f4abc1..30cfdf0a 100644
--- a/src/apps/cam/sdl_sink.cpp
+++ b/src/apps/cam/sdl_sink.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_sink.h - SDL Sink
+ * SDL Sink
*/
#include "sdl_sink.h"
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include <iomanip>
#include <iostream>
+#include <optional>
#include <signal.h>
#include <sstream>
#include <string.h>
@@ -22,6 +23,7 @@
#include "../common/event_loop.h"
#include "../common/image.h"
+#include "sdl_texture_1plane.h"
#ifdef HAVE_LIBJPEG
#include "sdl_texture_mjpg.h"
#endif
@@ -31,6 +33,46 @@ using namespace libcamera;
using namespace std::chrono_literals;
+namespace {
+
+std::optional<SDL_PixelFormatEnum> singlePlaneFormatToSDL(const libcamera::PixelFormat &f)
+{
+ switch (f) {
+ case libcamera::formats::RGB888:
+ return SDL_PIXELFORMAT_BGR24;
+ case libcamera::formats::BGR888:
+ return SDL_PIXELFORMAT_RGB24;
+ case libcamera::formats::RGBA8888:
+ return SDL_PIXELFORMAT_ABGR32;
+ case libcamera::formats::ARGB8888:
+ return SDL_PIXELFORMAT_BGRA32;
+ case libcamera::formats::BGRA8888:
+ return SDL_PIXELFORMAT_ARGB32;
+ case libcamera::formats::ABGR8888:
+ return SDL_PIXELFORMAT_RGBA32;
+#if SDL_VERSION_ATLEAST(2, 29, 1)
+ case libcamera::formats::RGBX8888:
+ return SDL_PIXELFORMAT_XBGR32;
+ case libcamera::formats::XRGB8888:
+ return SDL_PIXELFORMAT_BGRX32;
+ case libcamera::formats::BGRX8888:
+ return SDL_PIXELFORMAT_XRGB32;
+ case libcamera::formats::XBGR8888:
+ return SDL_PIXELFORMAT_RGBX32;
+#endif
+ case libcamera::formats::YUYV:
+ return SDL_PIXELFORMAT_YUY2;
+ case libcamera::formats::UYVY:
+ return SDL_PIXELFORMAT_UYVY;
+ case libcamera::formats::YVYU:
+ return SDL_PIXELFORMAT_YVYU;
+ }
+
+ return {};
+}
+
+} /* namespace */
+
SDLSink::SDLSink()
: window_(nullptr), renderer_(nullptr), rect_({}),
init_(false)
@@ -62,25 +104,20 @@ int SDLSink::configure(const libcamera::CameraConfiguration &config)
rect_.w = cfg.size.width;
rect_.h = cfg.size.height;
- switch (cfg.pixelFormat) {
+ if (auto sdlFormat = singlePlaneFormatToSDL(cfg.pixelFormat))
+ texture_ = std::make_unique<SDLTexture1Plane>(rect_, *sdlFormat, cfg.stride);
#ifdef HAVE_LIBJPEG
- case libcamera::formats::MJPEG:
+ else if (cfg.pixelFormat == libcamera::formats::MJPEG)
texture_ = std::make_unique<SDLTextureMJPG>(rect_);
- break;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 16)
- case libcamera::formats::NV12:
+ else if (cfg.pixelFormat == libcamera::formats::NV12)
texture_ = std::make_unique<SDLTextureNV12>(rect_, cfg.stride);
- break;
#endif
- case libcamera::formats::YUYV:
- texture_ = std::make_unique<SDLTextureYUYV>(rect_, cfg.stride);
- break;
- default:
- std::cerr << "Unsupported pixel format "
- << cfg.pixelFormat.toString() << std::endl;
+ else {
+ std::cerr << "Unsupported pixel format " << cfg.pixelFormat << std::endl;
return -EINVAL;
- };
+ }
return 0;
}
diff --git a/src/apps/cam/sdl_sink.h b/src/apps/cam/sdl_sink.h
index 6c19c663..18ec7fbe 100644
--- a/src/apps/cam/sdl_sink.h
+++ b/src/apps/cam/sdl_sink.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_sink.h - SDL Sink
+ * SDL Sink
*/
#pragma once
diff --git a/src/apps/cam/sdl_texture.cpp b/src/apps/cam/sdl_texture.cpp
index e9040bc5..e52c4a3a 100644
--- a/src/apps/cam/sdl_texture.cpp
+++ b/src/apps/cam/sdl_texture.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture.cpp - SDL Texture
+ * SDL Texture
*/
#include "sdl_texture.h"
diff --git a/src/apps/cam/sdl_texture.h b/src/apps/cam/sdl_texture.h
index 3993dd46..39e1c7b3 100644
--- a/src/apps/cam/sdl_texture.h
+++ b/src/apps/cam/sdl_texture.h
@@ -2,12 +2,12 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture.h - SDL Texture
+ * SDL Texture
*/
#pragma once
-#include <vector>
+#include <libcamera/base/span.h>
#include <SDL2/SDL.h>
@@ -19,7 +19,7 @@ public:
SDLTexture(const SDL_Rect &rect, uint32_t pixelFormat, const int stride);
virtual ~SDLTexture();
int create(SDL_Renderer *renderer);
- virtual void update(const std::vector<libcamera::Span<const uint8_t>> &data) = 0;
+ virtual void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) = 0;
SDL_Texture *get() const { return ptr_; }
protected:
diff --git a/src/apps/cam/sdl_texture_1plane.cpp b/src/apps/cam/sdl_texture_1plane.cpp
new file mode 100644
index 00000000..b97015bc
--- /dev/null
+++ b/src/apps/cam/sdl_texture_1plane.cpp
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * SDL single plane textures
+ */
+
+#include "sdl_texture_1plane.h"
+
+#include <assert.h>
+
+void SDLTexture1Plane::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
+{
+ assert(data.size() == 1);
+ assert(data[0].size_bytes() == std::size_t(rect_.h) * std::size_t(stride_));
+ SDL_UpdateTexture(ptr_, nullptr, data[0].data(), stride_);
+}
diff --git a/src/apps/cam/sdl_texture_1plane.h b/src/apps/cam/sdl_texture_1plane.h
new file mode 100644
index 00000000..795e1fa4
--- /dev/null
+++ b/src/apps/cam/sdl_texture_1plane.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * SDL single plane textures
+ */
+
+#pragma once
+
+#include "sdl_texture.h"
+
+class SDLTexture1Plane final : public SDLTexture
+{
+public:
+ using SDLTexture::SDLTexture;
+
+ void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
+};
diff --git a/src/apps/cam/sdl_texture_mjpg.cpp b/src/apps/cam/sdl_texture_mjpg.cpp
index da958e03..ca49a114 100644
--- a/src/apps/cam/sdl_texture_mjpg.cpp
+++ b/src/apps/cam/sdl_texture_mjpg.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_mjpg.cpp - SDL Texture MJPG
+ * SDL Texture MJPG
*/
#include "sdl_texture_mjpg.h"
@@ -76,7 +76,7 @@ int SDLTextureMJPG::decompress(Span<const uint8_t> data)
return 0;
}
-void SDLTextureMJPG::update(const std::vector<libcamera::Span<const uint8_t>> &data)
+void SDLTextureMJPG::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
{
decompress(data[0]);
SDL_UpdateTexture(ptr_, nullptr, rgb_.get(), stride_);
diff --git a/src/apps/cam/sdl_texture_mjpg.h b/src/apps/cam/sdl_texture_mjpg.h
index 814ca79a..be8a55fe 100644
--- a/src/apps/cam/sdl_texture_mjpg.h
+++ b/src/apps/cam/sdl_texture_mjpg.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_mjpg.h - SDL Texture MJPG
+ * SDL Texture MJPG
*/
#pragma once
@@ -14,7 +14,7 @@ class SDLTextureMJPG : public SDLTexture
public:
SDLTextureMJPG(const SDL_Rect &rect);
- void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
+ void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
private:
int decompress(libcamera::Span<const uint8_t> data);
diff --git a/src/apps/cam/sdl_texture_yuv.cpp b/src/apps/cam/sdl_texture_yuv.cpp
index b29c3b93..bed297d2 100644
--- a/src/apps/cam/sdl_texture_yuv.cpp
+++ b/src/apps/cam/sdl_texture_yuv.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_yuv.cpp - SDL YUV Textures
+ * SDL YUV Textures
*/
#include "sdl_texture_yuv.h"
@@ -15,19 +15,9 @@ SDLTextureNV12::SDLTextureNV12(const SDL_Rect &rect, unsigned int stride)
{
}
-void SDLTextureNV12::update(const std::vector<libcamera::Span<const uint8_t>> &data)
+void SDLTextureNV12::update(libcamera::Span<const libcamera::Span<const uint8_t>> data)
{
- SDL_UpdateNVTexture(ptr_, &rect_, data[0].data(), stride_,
+ SDL_UpdateNVTexture(ptr_, nullptr, data[0].data(), stride_,
data[1].data(), stride_);
}
#endif
-
-SDLTextureYUYV::SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride)
- : SDLTexture(rect, SDL_PIXELFORMAT_YUY2, stride)
-{
-}
-
-void SDLTextureYUYV::update(const std::vector<libcamera::Span<const uint8_t>> &data)
-{
- SDL_UpdateTexture(ptr_, &rect_, data[0].data(), stride_);
-}
diff --git a/src/apps/cam/sdl_texture_yuv.h b/src/apps/cam/sdl_texture_yuv.h
index 310e4e50..c271f901 100644
--- a/src/apps/cam/sdl_texture_yuv.h
+++ b/src/apps/cam/sdl_texture_yuv.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas on Board Oy
*
- * sdl_texture_yuv.h - SDL YUV Textures
+ * SDL YUV Textures
*/
#pragma once
@@ -14,13 +14,6 @@ class SDLTextureNV12 : public SDLTexture
{
public:
SDLTextureNV12(const SDL_Rect &rect, unsigned int stride);
- void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
+ void update(libcamera::Span<const libcamera::Span<const uint8_t>> data) override;
};
#endif
-
-class SDLTextureYUYV : public SDLTexture
-{
-public:
- SDLTextureYUYV(const SDL_Rect &rect, unsigned int stride);
- void update(const std::vector<libcamera::Span<const uint8_t>> &data) override;
-};
diff --git a/src/apps/common/dng_writer.cpp b/src/apps/common/dng_writer.cpp
index c945edce..ac461951 100644
--- a/src/apps/common/dng_writer.cpp
+++ b/src/apps/common/dng_writer.cpp
@@ -2,14 +2,16 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * dng_writer.cpp - DNG writer
+ * DNG writer
*/
#include "dng_writer.h"
#include <algorithm>
+#include <endian.h>
#include <iostream>
#include <map>
+#include <vector>
#include <tiffio.h>
@@ -126,7 +128,9 @@ struct Matrix3d {
float m[9];
};
-void packScanlineSBGGR8(void *output, const void *input, unsigned int width)
+namespace {
+
+void packScanlineRaw8(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -134,7 +138,67 @@ void packScanlineSBGGR8(void *output, const void *input, unsigned int width)
std::copy(in, in + width, out);
}
-void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)
+void packScanlineRaw10(void *output, const void *input, unsigned int width)
+{
+ const uint8_t *in = static_cast<const uint8_t *>(input);
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ for (unsigned int i = 0; i < width; i += 4) {
+ *out++ = in[1] << 6 | in[0] >> 2;
+ *out++ = in[0] << 6 | (in[3] & 0x03) << 4 | in[2] >> 4;
+ *out++ = in[2] << 4 | (in[5] & 0x03) << 2 | in[4] >> 6;
+ *out++ = in[4] << 2 | (in[7] & 0x03) << 0;
+ *out++ = in[6];
+ in += 8;
+ }
+}
+
+void packScanlineRaw12(void *output, const void *input, unsigned int width)
+{
+ const uint8_t *in = static_cast<const uint8_t *>(input);
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ for (unsigned int i = 0; i < width; i += 2) {
+ *out++ = in[1] << 4 | in[0] >> 4;
+ *out++ = in[0] << 4 | (in[3] & 0x0f);
+ *out++ = in[2];
+ in += 4;
+ }
+}
+
+void packScanlineRaw16(void *output, const void *input, unsigned int width)
+{
+ const uint16_t *in = static_cast<const uint16_t *>(input);
+ uint16_t *out = static_cast<uint16_t *>(output);
+
+ std::copy(in, in + width, out);
+}
+
+/* Thumbnail function for raw data with each pixel aligned to 16bit. */
+void thumbScanlineRaw(const FormatInfo &info, void *output, const void *input,
+ unsigned int width, unsigned int stride)
+{
+ const uint16_t *in = static_cast<const uint16_t *>(input);
+ const uint16_t *in2 = static_cast<const uint16_t *>(input) + stride / 2;
+ uint8_t *out = static_cast<uint8_t *>(output);
+
+ /* Shift down to 8. */
+ unsigned int shift = info.bitsPerSample - 8;
+
+ /* Simple averaging that produces greyscale RGB values. */
+ for (unsigned int x = 0; x < width; x++) {
+ uint16_t value = (le16toh(in[0]) + le16toh(in[1]) +
+ le16toh(in2[0]) + le16toh(in2[1])) >> 2;
+ value = value >> shift;
+ *out++ = value;
+ *out++ = value;
+ *out++ = value;
+ in += 16;
+ in2 += 16;
+ }
+}
+
+void packScanlineRaw10_CSI2P(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -150,7 +214,7 @@ void packScanlineSBGGR10P(void *output, const void *input, unsigned int width)
}
}
-void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)
+void packScanlineRaw12_CSI2P(void *output, const void *input, unsigned int width)
{
const uint8_t *in = static_cast<const uint8_t *>(input);
uint8_t *out = static_cast<uint8_t *>(output);
@@ -164,7 +228,7 @@ void packScanlineSBGGR12P(void *output, const void *input, unsigned int width)
}
}
-void thumbScanlineSBGGRxxP(const FormatInfo &info, void *output,
+void thumbScanlineRaw_CSI2P(const FormatInfo &info, void *output,
const void *input, unsigned int width,
unsigned int stride)
{
@@ -248,6 +312,7 @@ void thumbScanlineIPU3([[maybe_unused]] const FormatInfo &info, void *output,
uint16_t val1, val2, val3, val4;
switch (pixelInBlock % 4) {
+ default:
case 0:
val1 = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6;
val2 = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4;
@@ -281,78 +346,150 @@ void thumbScanlineIPU3([[maybe_unused]] const FormatInfo &info, void *output,
}
}
-static const std::map<PixelFormat, FormatInfo> formatInfo = {
+const std::map<PixelFormat, FormatInfo> formatInfo = {
{ formats::SBGGR8, {
.bitsPerSample = 8,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG8, {
.bitsPerSample = 8,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG8, {
.bitsPerSample = 8,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB8, {
.bitsPerSample = 8,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR8,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw8,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
+ } },
+ { formats::SBGGR10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB10, {
+ .bitsPerSample = 10,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw10,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SBGGR12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB12, {
+ .bitsPerSample = 12,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw12,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SBGGR16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGBRG16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SGRBG16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
+ } },
+ { formats::SRGGB16, {
+ .bitsPerSample = 16,
+ .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
+ .packScanline = packScanlineRaw16,
+ .thumbScanline = thumbScanlineRaw,
} },
{ formats::SBGGR10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB10_CSI2P, {
.bitsPerSample = 10,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR10P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw10_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SBGGR12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGBRG12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SGRBG12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SRGGB12_CSI2P, {
.bitsPerSample = 12,
.pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue },
- .packScanline = packScanlineSBGGR12P,
- .thumbScanline = thumbScanlineSBGGRxxP,
+ .packScanline = packScanlineRaw12_CSI2P,
+ .thumbScanline = thumbScanlineRaw_CSI2P,
} },
{ formats::SBGGR10_IPU3, {
.bitsPerSample = 16,
@@ -380,6 +517,8 @@ static const std::map<PixelFormat, FormatInfo> formatInfo = {
} },
};
+} /* namespace */
+
int DNGWriter::write(const char *filename, const Camera *camera,
const StreamConfiguration &config,
const ControlList &metadata,
@@ -406,7 +545,7 @@ int DNGWriter::write(const char *filename, const Camera *camera,
* or a thumbnail scanline. The latter will always be much smaller than
* the former as we downscale by 16 in both directions.
*/
- uint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];
+ std::vector<uint8_t> scanline((config.size.width * info->bitsPerSample + 7) / 8);
toff_t rawIFDOffset = 0;
toff_t exifIFDOffset = 0;
@@ -506,10 +645,10 @@ int DNGWriter::write(const char *filename, const Camera *camera,
/* Write the thumbnail. */
const uint8_t *row = static_cast<const uint8_t *>(data);
for (unsigned int y = 0; y < config.size.height / 16; y++) {
- info->thumbScanline(*info, &scanline, row,
+ info->thumbScanline(*info, scanline.data(), row,
config.size.width / 16, config.stride);
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
+ if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) {
std::cerr << "Failed to write thumbnail scanline"
<< std::endl;
TIFFClose(tif);
@@ -521,6 +660,23 @@ int DNGWriter::write(const char *filename, const Camera *camera,
TIFFWriteDirectory(tif);
+ /*
+ * Workaround for a bug introduced in libtiff version 4.5.1 and no fix
+ * released. In these versions the CFA* tags were missing in the field
+ * info.
+ * Introduced by: https://gitlab.com/libtiff/libtiff/-/commit/738e04099b13192bb1f654e74e9b5829313f3161
+ * Fixed by: https://gitlab.com/libtiff/libtiff/-/commit/49856998c3d82e65444b47bb4fb11b7830a0c2be
+ */
+ if (!TIFFFindField(tif, TIFFTAG_CFAREPEATPATTERNDIM, TIFF_ANY)) {
+ static const TIFFFieldInfo infos[] = {
+ { TIFFTAG_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, FIELD_CUSTOM,
+ 1, 0, const_cast<char *>("CFARepeatPatternDim") },
+ { TIFFTAG_CFAPATTERN, -1, -1, TIFF_BYTE, FIELD_CUSTOM,
+ 1, 1, const_cast<char *>("CFAPattern") },
+ };
+ TIFFMergeFieldInfo(tif, infos, 2);
+ }
+
/* Create a new IFD for the RAW image. */
const uint16_t cfaRepeatPatternDim[] = { 2, 2 };
const uint8_t cfaPlaneColor[] = {
@@ -592,9 +748,9 @@ int DNGWriter::write(const char *filename, const Camera *camera,
/* Write RAW content. */
row = static_cast<const uint8_t *>(data);
for (unsigned int y = 0; y < config.size.height; y++) {
- info->packScanline(&scanline, row, config.size.width);
+ info->packScanline(scanline.data(), row, config.size.width);
- if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) {
+ if (TIFFWriteScanline(tif, scanline.data(), y, 0) != 1) {
std::cerr << "Failed to write RAW scanline"
<< std::endl;
TIFFClose(tif);
diff --git a/src/apps/common/dng_writer.h b/src/apps/common/dng_writer.h
index 38f38f62..aaa8a852 100644
--- a/src/apps/common/dng_writer.h
+++ b/src/apps/common/dng_writer.h
@@ -2,13 +2,12 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * dng_writer.h - DNG writer
+ * DNG writer
*/
#pragma once
#ifdef HAVE_TIFF
-#define HAVE_DNG
#include <libcamera/camera.h>
#include <libcamera/controls.h>
diff --git a/src/apps/common/event_loop.cpp b/src/apps/common/event_loop.cpp
index cb83845c..b6230f4b 100644
--- a/src/apps/common/event_loop.cpp
+++ b/src/apps/common/event_loop.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_loop.cpp - cam - Event loop
+ * cam - Event loop
*/
#include "event_loop.h"
@@ -21,12 +21,35 @@ EventLoop::EventLoop()
evthread_use_pthreads();
base_ = event_base_new();
instance_ = this;
+
+ callsTrigger_ = event_new(base_, -1, EV_PERSIST, [](evutil_socket_t, short, void *closure) {
+ auto *self = static_cast<EventLoop *>(closure);
+
+ for (;;) {
+ std::function<void()> call;
+
+ {
+ std::lock_guard locker(self->lock_);
+ if (self->calls_.empty())
+ break;
+
+ call = std::move(self->calls_.front());
+ self->calls_.pop_front();
+ }
+
+ call();
+ }
+ }, this);
+ assert(callsTrigger_);
+ event_add(callsTrigger_, nullptr);
}
EventLoop::~EventLoop()
{
instance_ = nullptr;
+ event_free(callsTrigger_);
+
events_.clear();
event_base_free(base_);
libevent_global_shutdown();
@@ -50,20 +73,20 @@ void EventLoop::exit(int code)
event_base_loopbreak(base_);
}
-void EventLoop::callLater(const std::function<void()> &func)
+void EventLoop::callLater(std::function<void()> &&func)
{
{
std::unique_lock<std::mutex> locker(lock_);
- calls_.push_back(func);
+ calls_.push_back(std::move(func));
}
- event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr);
+ event_active(callsTrigger_, 0, 0);
}
void EventLoop::addFdEvent(int fd, EventType type,
- const std::function<void()> &callback)
+ std::function<void()> &&callback)
{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
+ std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
short events = (type & Read ? EV_READ : 0)
| (type & Write ? EV_WRITE : 0)
| EV_PERSIST;
@@ -85,9 +108,9 @@ void EventLoop::addFdEvent(int fd, EventType type,
}
void EventLoop::addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &callback)
+ std::function<void()> &&callback)
{
- std::unique_ptr<Event> event = std::make_unique<Event>(callback);
+ std::unique_ptr<Event> event = std::make_unique<Event>(std::move(callback));
event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch,
event.get());
if (!event->event_) {
@@ -108,31 +131,8 @@ void EventLoop::addTimerEvent(const std::chrono::microseconds period,
events_.push_back(std::move(event));
}
-void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd,
- [[maybe_unused]] short flags, void *param)
-{
- EventLoop *loop = static_cast<EventLoop *>(param);
- loop->dispatchCall();
-}
-
-void EventLoop::dispatchCall()
-{
- std::function<void()> call;
-
- {
- std::unique_lock<std::mutex> locker(lock_);
- if (calls_.empty())
- return;
-
- call = calls_.front();
- calls_.pop_front();
- }
-
- call();
-}
-
-EventLoop::Event::Event(const std::function<void()> &callback)
- : callback_(callback), event_(nullptr)
+EventLoop::Event::Event(std::function<void()> &&callback)
+ : callback_(std::move(callback)), event_(nullptr)
{
}
diff --git a/src/apps/common/event_loop.h b/src/apps/common/event_loop.h
index ef79e8e5..d8b6df2f 100644
--- a/src/apps/common/event_loop.h
+++ b/src/apps/common/event_loop.h
@@ -2,17 +2,20 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_loop.h - cam - Event loop
+ * cam - Event loop
*/
#pragma once
#include <chrono>
+#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
+#include <libcamera/base/class.h>
+
#include <event2/util.h>
struct event_base;
@@ -33,18 +36,20 @@ public:
int exec();
void exit(int code = 0);
- void callLater(const std::function<void()> &func);
+ void callLater(std::function<void()> &&func);
void addFdEvent(int fd, EventType type,
- const std::function<void()> &handler);
+ std::function<void()> &&handler);
- using duration = std::chrono::steady_clock::duration;
void addTimerEvent(const std::chrono::microseconds period,
- const std::function<void()> &handler);
+ std::function<void()> &&handler);
private:
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(EventLoop)
+
struct Event {
- Event(const std::function<void()> &callback);
+ Event(std::function<void()> &&callback);
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(Event)
~Event();
static void dispatch(int fd, short events, void *arg);
@@ -58,11 +63,9 @@ private:
struct event_base *base_;
int exitCode_;
- std::list<std::function<void()>> calls_;
+ std::deque<std::function<void()>> calls_;
+ struct event *callsTrigger_ = nullptr;
+
std::list<std::unique_ptr<Event>> events_;
std::mutex lock_;
-
- static void dispatchCallback(evutil_socket_t fd, short flags,
- void *param);
- void dispatchCall();
};
diff --git a/src/apps/common/image.cpp b/src/apps/common/image.cpp
index fe2cc6da..a2a0f58f 100644
--- a/src/apps/common/image.cpp
+++ b/src/apps/common/image.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * image.cpp - Multi-planar image with access to pixel data
+ * Multi-planar image with access to pixel data
*/
#include "image.h"
diff --git a/src/apps/common/image.h b/src/apps/common/image.h
index 7953b177..e47e446b 100644
--- a/src/apps/common/image.h
+++ b/src/apps/common/image.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * image.h - Multi-planar image with access to pixel data
+ * Multi-planar image with access to pixel data
*/
#pragma once
diff --git a/src/apps/common/meson.build b/src/apps/common/meson.build
index 479326cd..5b683390 100644
--- a/src/apps/common/meson.build
+++ b/src/apps/common/meson.build
@@ -3,6 +3,7 @@
apps_sources = files([
'image.cpp',
'options.cpp',
+ 'ppm_writer.cpp',
'stream_options.cpp',
])
diff --git a/src/apps/common/options.cpp b/src/apps/common/options.cpp
index 4f7e8691..cae193cc 100644
--- a/src/apps/common/options.cpp
+++ b/src/apps/common/options.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * options.cpp - cam - Options parsing
+ * cam - Options parsing
*/
#include <assert.h>
@@ -10,6 +10,7 @@
#include <iomanip>
#include <iostream>
#include <string.h>
+#include <vector>
#include "options.h"
@@ -879,8 +880,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
* Allocate short and long options arrays large enough to contain all
* options.
*/
- char shortOptions[optionsMap_.size() * 3 + 2];
- struct option longOptions[optionsMap_.size() + 1];
+ std::vector<char> shortOptions(optionsMap_.size() * 3 + 2);
+ std::vector<struct option> longOptions(optionsMap_.size() + 1);
unsigned int ids = 0;
unsigned int idl = 0;
@@ -922,7 +923,8 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv)
opterr = 0;
while (true) {
- int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr);
+ int c = getopt_long(argc, argv, shortOptions.data(),
+ longOptions.data(), nullptr);
if (c == -1)
break;
@@ -1038,7 +1040,7 @@ void OptionsParser::usageOptions(const std::list<Option> &options,
std::cerr << std::setw(indent) << argument;
- for (const char *help = option.help, *end = help; end; ) {
+ for (const char *help = option.help, *end = help; end;) {
end = strchr(help, '\n');
if (end) {
std::cerr << std::string(help, end - help + 1);
diff --git a/src/apps/common/options.h b/src/apps/common/options.h
index 4ddd4987..9771aa7a 100644
--- a/src/apps/common/options.h
+++ b/src/apps/common/options.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * options.h - cam - Options parsing
+ * cam - Options parsing
*/
#pragma once
diff --git a/src/apps/common/ppm_writer.cpp b/src/apps/common/ppm_writer.cpp
new file mode 100644
index 00000000..368de8bf
--- /dev/null
+++ b/src/apps/common/ppm_writer.cpp
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * PPM writer
+ */
+
+#include "ppm_writer.h"
+
+#include <errno.h>
+#include <fstream>
+#include <iostream>
+
+#include <libcamera/formats.h>
+#include <libcamera/pixel_format.h>
+
+using namespace libcamera;
+
+int PPMWriter::write(const char *filename,
+ const StreamConfiguration &config,
+ const Span<uint8_t> &data)
+{
+ if (config.pixelFormat != formats::BGR888) {
+ std::cerr << "Only BGR888 output pixel format is supported ("
+ << config.pixelFormat << " requested)" << std::endl;
+ return -EINVAL;
+ }
+
+ std::ofstream output(filename, std::ios::binary);
+ if (!output) {
+ std::cerr << "Failed to open ppm file: " << filename << std::endl;
+ return -EIO;
+ }
+
+ output << "P6" << std::endl
+ << config.size.width << " " << config.size.height << std::endl
+ << "255" << std::endl;
+ if (!output) {
+ std::cerr << "Failed to write the file header" << std::endl;
+ return -EIO;
+ }
+
+ const unsigned int rowLength = config.size.width * 3;
+ const char *row = reinterpret_cast<const char *>(data.data());
+ for (unsigned int y = 0; y < config.size.height; y++, row += config.stride) {
+ output.write(row, rowLength);
+ if (!output) {
+ std::cerr << "Failed to write image data at row " << y << std::endl;
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/apps/common/ppm_writer.h b/src/apps/common/ppm_writer.h
new file mode 100644
index 00000000..8c8d2e15
--- /dev/null
+++ b/src/apps/common/ppm_writer.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat, Inc.
+ *
+ * PPM writer
+ */
+
+#pragma once
+
+#include <libcamera/base/span.h>
+
+#include <libcamera/stream.h>
+
+class PPMWriter
+{
+public:
+ static int write(const char *filename,
+ const libcamera::StreamConfiguration &config,
+ const libcamera::Span<uint8_t> &data);
+};
diff --git a/src/apps/common/stream_options.cpp b/src/apps/common/stream_options.cpp
index 663b979a..288f8653 100644
--- a/src/apps/common/stream_options.cpp
+++ b/src/apps/common/stream_options.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * stream_options.cpp - Helper to parse options for streams
+ * Helper to parse options for streams
*/
#include "stream_options.h"
@@ -42,9 +42,8 @@ KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments)
std::vector<StreamRole> StreamKeyValueParser::roles(const OptionValue &values)
{
- /* If no configuration values to examine default to viewfinder. */
if (values.empty())
- return { StreamRole::Viewfinder };
+ return {};
const std::vector<OptionValue> &streamParameters = values.toArray();
diff --git a/src/apps/common/stream_options.h b/src/apps/common/stream_options.h
index a5f3bde0..a93f104c 100644
--- a/src/apps/common/stream_options.h
+++ b/src/apps/common/stream_options.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * stream_options.h - Helper to parse options for streams
+ * Helper to parse options for streams
*/
#pragma once
diff --git a/src/apps/ipa-verify/main.cpp b/src/apps/ipa-verify/main.cpp
index 76ba5073..0903cd85 100644
--- a/src/apps/ipa-verify/main.cpp
+++ b/src/apps/ipa-verify/main.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Ideas on Board Oy
*
- * ipa_verify.cpp - Verify signature on an IPA module
+ * Verify signature on an IPA module
*/
#include <iostream>
diff --git a/src/apps/lc-compliance/capture_test.cpp b/src/apps/lc-compliance/capture_test.cpp
deleted file mode 100644
index 1dcfcf92..00000000
--- a/src/apps/lc-compliance/capture_test.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020, Google Inc.
- * Copyright (C) 2021, Collabora Ltd.
- *
- * capture_test.cpp - Test camera capture
- */
-
-#include <iostream>
-
-#include <gtest/gtest.h>
-
-#include "environment.h"
-#include "simple_capture.h"
-
-using namespace libcamera;
-
-const std::vector<int> NUMREQUESTS = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
-const std::vector<StreamRole> ROLES = {
- StreamRole::Raw,
- StreamRole::StillCapture,
- StreamRole::VideoRecording,
- StreamRole::Viewfinder
-};
-
-class SingleStream : public testing::TestWithParam<std::tuple<StreamRole, int>>
-{
-public:
- static std::string nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info);
-
-protected:
- void SetUp() override;
- void TearDown() override;
-
- std::shared_ptr<Camera> camera_;
-};
-
-/*
- * We use gtest's SetUp() and TearDown() instead of constructor and destructor
- * in order to be able to assert on them.
- */
-void SingleStream::SetUp()
-{
- Environment *env = Environment::get();
-
- camera_ = env->cm()->get(env->cameraId());
-
- ASSERT_EQ(camera_->acquire(), 0);
-}
-
-void SingleStream::TearDown()
-{
- if (!camera_)
- return;
-
- camera_->release();
- camera_.reset();
-}
-
-std::string SingleStream::nameParameters(const testing::TestParamInfo<SingleStream::ParamType> &info)
-{
- std::map<StreamRole, std::string> rolesMap = {
- { StreamRole::Raw, "Raw" },
- { StreamRole::StillCapture, "StillCapture" },
- { StreamRole::VideoRecording, "VideoRecording" },
- { StreamRole::Viewfinder, "Viewfinder" }
- };
-
- std::string roleName = rolesMap[std::get<0>(info.param)];
- std::string numRequestsName = std::to_string(std::get<1>(info.param));
-
- return roleName + "_" + numRequestsName;
-}
-
-/*
- * Test single capture cycles
- *
- * Makes sure the camera completes the exact number of requests queued. Example
- * failure is a camera that completes less requests than the number of requests
- * queued.
- */
-TEST_P(SingleStream, Capture)
-{
- auto [role, numRequests] = GetParam();
-
- SimpleCaptureBalanced capture(camera_);
-
- capture.configure(role);
-
- capture.capture(numRequests);
-}
-
-/*
- * Test multiple start/stop cycles
- *
- * Makes sure the camera supports multiple start/stop cycles. Example failure is
- * a camera that does not clean up correctly in its error path but is only
- * tested by single-capture applications.
- */
-TEST_P(SingleStream, CaptureStartStop)
-{
- auto [role, numRequests] = GetParam();
- unsigned int numRepeats = 3;
-
- SimpleCaptureBalanced capture(camera_);
-
- capture.configure(role);
-
- for (unsigned int starts = 0; starts < numRepeats; starts++)
- capture.capture(numRequests);
-}
-
-/*
- * Test unbalanced stop
- *
- * Makes sure the camera supports a stop with requests queued. Example failure
- * is a camera that does not handle cancelation of buffers coming back from the
- * video device while stopping.
- */
-TEST_P(SingleStream, UnbalancedStop)
-{
- auto [role, numRequests] = GetParam();
-
- SimpleCaptureUnbalanced capture(camera_);
-
- capture.configure(role);
-
- capture.capture(numRequests);
-}
-
-INSTANTIATE_TEST_SUITE_P(CaptureTests,
- SingleStream,
- testing::Combine(testing::ValuesIn(ROLES),
- testing::ValuesIn(NUMREQUESTS)),
- SingleStream::nameParameters);
diff --git a/src/apps/lc-compliance/environment.cpp b/src/apps/lc-compliance/environment.cpp
index 5eb3775f..987264f1 100644
--- a/src/apps/lc-compliance/environment.cpp
+++ b/src/apps/lc-compliance/environment.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Collabora Ltd.
*
- * environment.cpp - Common environment for tests
+ * Common environment for tests
*/
#include "environment.h"
diff --git a/src/apps/lc-compliance/environment.h b/src/apps/lc-compliance/environment.h
index 0debbcce..834c722e 100644
--- a/src/apps/lc-compliance/environment.h
+++ b/src/apps/lc-compliance/environment.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Collabora Ltd.
*
- * environment.h - Common environment for tests
+ * Common environment for tests
*/
#pragma once
@@ -23,5 +23,5 @@ private:
Environment() = default;
std::string cameraId_;
- libcamera::CameraManager *cm_;
+ libcamera::CameraManager *cm_ = nullptr;
};
diff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp
new file mode 100644
index 00000000..2a3fa3b6
--- /dev/null
+++ b/src/apps/lc-compliance/helpers/capture.cpp
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020-2021, Google Inc.
+ *
+ * Simple capture helper
+ */
+
+#include "capture.h"
+
+#include <assert.h>
+
+#include <gtest/gtest.h>
+
+using namespace libcamera;
+
+Capture::Capture(std::shared_ptr<Camera> camera)
+ : camera_(std::move(camera)), allocator_(camera_)
+{
+}
+
+Capture::~Capture()
+{
+ stop();
+}
+
+void Capture::configure(libcamera::Span<const libcamera::StreamRole> roles)
+{
+ assert(!roles.empty());
+
+ config_ = camera_->generateConfiguration(roles);
+ if (!config_)
+ GTEST_SKIP() << "Roles not supported by camera";
+
+ ASSERT_EQ(config_->size(), roles.size()) << "Unexpected number of streams in configuration";
+
+ /*
+ * Set the buffers count to the largest value across all streams.
+ * \todo: Should all streams from a Camera have the same buffer count ?
+ */
+ auto largest =
+ std::max_element(config_->begin(), config_->end(),
+ [](const StreamConfiguration &l, const StreamConfiguration &r)
+ { return l.bufferCount < r.bufferCount; });
+
+ assert(largest != config_->end());
+
+ for (auto &cfg : *config_)
+ cfg.bufferCount = largest->bufferCount;
+
+ if (config_->validate() != CameraConfiguration::Valid) {
+ config_.reset();
+ FAIL() << "Configuration not valid";
+ }
+
+ if (camera_->configure(config_.get())) {
+ config_.reset();
+ FAIL() << "Failed to configure camera";
+ }
+}
+
+void Capture::run(unsigned int captureLimit, std::optional<unsigned int> queueLimit)
+{
+ assert(!queueLimit || captureLimit <= *queueLimit);
+
+ captureLimit_ = captureLimit;
+ queueLimit_ = queueLimit;
+
+ captureCount_ = queueCount_ = 0;
+
+ EventLoop loop;
+ loop_ = &loop;
+
+ start();
+
+ for (const auto &request : requests_)
+ queueRequest(request.get());
+
+ EXPECT_EQ(loop_->exec(), 0);
+
+ stop();
+
+ EXPECT_LE(captureLimit_, captureCount_);
+ EXPECT_LE(captureCount_, queueCount_);
+ EXPECT_TRUE(!queueLimit_ || queueCount_ <= *queueLimit_);
+}
+
+int Capture::queueRequest(libcamera::Request *request)
+{
+ if (queueLimit_ && queueCount_ >= *queueLimit_)
+ return 0;
+
+ int ret = camera_->queueRequest(request);
+ if (ret < 0)
+ return ret;
+
+ queueCount_ += 1;
+ return 0;
+}
+
+void Capture::requestComplete(Request *request)
+{
+ captureCount_++;
+ if (captureCount_ >= captureLimit_) {
+ loop_->exit(0);
+ return;
+ }
+
+ EXPECT_EQ(request->status(), Request::Status::RequestComplete)
+ << "Request didn't complete successfully";
+
+ request->reuse(Request::ReuseBuffers);
+ if (queueRequest(request))
+ loop_->exit(-EINVAL);
+}
+
+void Capture::start()
+{
+ assert(config_);
+ assert(!config_->empty());
+ assert(!allocator_.allocated());
+ assert(requests_.empty());
+
+ const auto bufferCount = config_->at(0).bufferCount;
+
+ /* No point in testing less requests then the camera depth. */
+ if (queueLimit_ && *queueLimit_ < bufferCount) {
+ GTEST_SKIP() << "Camera needs " << bufferCount
+ << " requests, can't test only " << *queueLimit_;
+ }
+
+ for (std::size_t i = 0; i < bufferCount; i++) {
+ std::unique_ptr<Request> request = camera_->createRequest();
+ ASSERT_TRUE(request) << "Can't create request";
+ requests_.push_back(std::move(request));
+ }
+
+ for (const auto &cfg : *config_) {
+ Stream *stream = cfg.stream();
+
+ int count = allocator_.allocate(stream);
+ ASSERT_GE(count, 0) << "Failed to allocate buffers";
+
+ const auto &buffers = allocator_.buffers(stream);
+ ASSERT_EQ(buffers.size(), bufferCount) << "Mismatching buffer count";
+
+ for (std::size_t i = 0; i < bufferCount; i++) {
+ ASSERT_EQ(requests_[i]->addBuffer(stream, buffers[i].get()), 0)
+ << "Failed to add buffer to request";
+ }
+ }
+
+ ASSERT_TRUE(allocator_.allocated());
+
+ camera_->requestCompleted.connect(this, &Capture::requestComplete);
+
+ ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
+}
+
+void Capture::stop()
+{
+ if (!config_ || !allocator_.allocated())
+ return;
+
+ camera_->stop();
+
+ camera_->requestCompleted.disconnect(this);
+
+ requests_.clear();
+
+ for (const auto &cfg : *config_) {
+ EXPECT_EQ(allocator_.free(cfg.stream()), 0)
+ << "Failed to free buffers associated with stream";
+ }
+
+ EXPECT_FALSE(allocator_.allocated());
+}
diff --git a/src/apps/lc-compliance/helpers/capture.h b/src/apps/lc-compliance/helpers/capture.h
new file mode 100644
index 00000000..ea01c11c
--- /dev/null
+++ b/src/apps/lc-compliance/helpers/capture.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020-2021, Google Inc.
+ *
+ * Simple capture helper
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include <libcamera/libcamera.h>
+
+#include "../common/event_loop.h"
+
+class Capture
+{
+public:
+ Capture(std::shared_ptr<libcamera::Camera> camera);
+ ~Capture();
+
+ void configure(libcamera::Span<const libcamera::StreamRole> roles);
+ void run(unsigned int captureLimit, std::optional<unsigned int> queueLimit = {});
+
+private:
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(Capture)
+
+ void start();
+ void stop();
+
+ int queueRequest(libcamera::Request *request);
+ void requestComplete(libcamera::Request *request);
+
+ std::shared_ptr<libcamera::Camera> camera_;
+ libcamera::FrameBufferAllocator allocator_;
+ std::unique_ptr<libcamera::CameraConfiguration> config_;
+ std::vector<std::unique_ptr<libcamera::Request>> requests_;
+
+ EventLoop *loop_ = nullptr;
+ unsigned int captureLimit_ = 0;
+ std::optional<unsigned int> queueLimit_;
+ unsigned int captureCount_ = 0;
+ unsigned int queueCount_ = 0;
+};
diff --git a/src/apps/lc-compliance/main.cpp b/src/apps/lc-compliance/main.cpp
index 74e0d4df..e9f0ffbb 100644
--- a/src/apps/lc-compliance/main.cpp
+++ b/src/apps/lc-compliance/main.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Google Inc.
* Copyright (C) 2021, Collabora Ltd.
*
- * main.cpp - lc-compliance - The libcamera compliance tool
+ * lc-compliance - The libcamera compliance tool
*/
#include <iomanip>
@@ -45,13 +45,11 @@ class ThrowListener : public testing::EmptyTestEventListener
static void listCameras(CameraManager *cm)
{
for (const std::shared_ptr<Camera> &cam : cm->cameras())
- std::cout << "- " << cam.get()->id() << std::endl;
+ std::cout << "- " << cam->id() << std::endl;
}
static int initCamera(CameraManager *cm, OptionsParser::Options options)
{
- std::shared_ptr<Camera> camera;
-
int ret = cm->start();
if (ret) {
std::cout << "Failed to start camera manager: "
@@ -66,7 +64,7 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
}
const std::string &cameraId = options[OptCamera];
- camera = cm->get(cameraId);
+ std::shared_ptr<Camera> camera = cm->get(cameraId);
if (!camera) {
std::cout << "Camera " << cameraId << " not found, available cameras:" << std::endl;
listCameras(cm);
@@ -82,45 +80,27 @@ static int initCamera(CameraManager *cm, OptionsParser::Options options)
static int initGtestParameters(char *arg0, OptionsParser::Options options)
{
- const std::map<std::string, std::string> gtestFlags = { { "list", "--gtest_list_tests" },
- { "filter", "--gtest_filter" } };
-
- int argc = 0;
+ std::vector<const char *> argv;
std::string filterParam;
- /*
- * +2 to have space for both the 0th argument that is needed but not
- * used and the null at the end.
- */
- char **argv = new char *[(gtestFlags.size() + 2)];
- if (!argv)
- return -ENOMEM;
-
- argv[0] = arg0;
- argc++;
+ argv.push_back(arg0);
- if (options.isSet(OptList)) {
- argv[argc] = const_cast<char *>(gtestFlags.at("list").c_str());
- argc++;
- }
+ if (options.isSet(OptList))
+ argv.push_back("--gtest_list_tests");
if (options.isSet(OptFilter)) {
/*
* The filter flag needs to be passed as a single parameter, in
* the format --gtest_filter=filterStr
*/
- filterParam = gtestFlags.at("filter") + "=" +
- static_cast<const std::string &>(options[OptFilter]);
-
- argv[argc] = const_cast<char *>(filterParam.c_str());
- argc++;
+ filterParam = "--gtest_filter=" + options[OptFilter].toString();
+ argv.push_back(filterParam.c_str());
}
- argv[argc] = nullptr;
-
- ::testing::InitGoogleTest(&argc, argv);
+ argv.push_back(nullptr);
- delete[] argv;
+ int argc = argv.size();
+ ::testing::InitGoogleTest(&argc, const_cast<char **>(argv.data()));
return 0;
}
diff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build
index 51d9075a..80b9a160 100644
--- a/src/apps/lc-compliance/meson.build
+++ b/src/apps/lc-compliance/meson.build
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: CC0-1.0
-libgtest = dependency('gtest', required : get_option('lc-compliance'),
+libgtest = dependency('gtest', version : '>=1.10.0',
+ required : get_option('lc-compliance'),
fallback : ['gtest', 'gtest_dep'])
if opt_lc_compliance.disabled() or not libevent.found() or not libgtest.found()
@@ -12,9 +13,15 @@ lc_compliance_enabled = true
lc_compliance_sources = files([
'environment.cpp',
+ 'helpers/capture.cpp',
'main.cpp',
- 'simple_capture.cpp',
- 'capture_test.cpp',
+ 'test_base.cpp',
+ 'tests/capture_test.cpp',
+])
+
+lc_compliance_includes = ([
+ include_directories('.'),
+ include_directories('helpers/')
])
lc_compliance = executable('lc-compliance', lc_compliance_sources,
@@ -26,4 +33,6 @@ lc_compliance = executable('lc-compliance', lc_compliance_sources,
libevent,
libgtest,
],
- install : true)
+ include_directories : lc_compliance_includes,
+ install : true,
+ install_tag : 'bin-devel')
diff --git a/src/apps/lc-compliance/simple_capture.cpp b/src/apps/lc-compliance/simple_capture.cpp
deleted file mode 100644
index cf4d7cf3..00000000
--- a/src/apps/lc-compliance/simple_capture.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020-2021, Google Inc.
- *
- * simple_capture.cpp - Simple capture helper
- */
-
-#include <gtest/gtest.h>
-
-#include "simple_capture.h"
-
-using namespace libcamera;
-
-SimpleCapture::SimpleCapture(std::shared_ptr<Camera> camera)
- : loop_(nullptr), camera_(camera),
- allocator_(std::make_unique<FrameBufferAllocator>(camera))
-{
-}
-
-SimpleCapture::~SimpleCapture()
-{
- stop();
-}
-
-void SimpleCapture::configure(StreamRole role)
-{
- config_ = camera_->generateConfiguration({ role });
-
- if (!config_) {
- std::cout << "Role not supported by camera" << std::endl;
- GTEST_SKIP();
- }
-
- if (config_->validate() != CameraConfiguration::Valid) {
- config_.reset();
- FAIL() << "Configuration not valid";
- }
-
- if (camera_->configure(config_.get())) {
- config_.reset();
- FAIL() << "Failed to configure camera";
- }
-}
-
-void SimpleCapture::start()
-{
- Stream *stream = config_->at(0).stream();
- int count = allocator_->allocate(stream);
-
- ASSERT_GE(count, 0) << "Failed to allocate buffers";
- EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
-
- camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);
-
- ASSERT_EQ(camera_->start(), 0) << "Failed to start camera";
-}
-
-void SimpleCapture::stop()
-{
- if (!config_ || !allocator_->allocated())
- return;
-
- camera_->stop();
-
- camera_->requestCompleted.disconnect(this);
-
- Stream *stream = config_->at(0).stream();
- requests_.clear();
- allocator_->free(stream);
-}
-
-/* SimpleCaptureBalanced */
-
-SimpleCaptureBalanced::SimpleCaptureBalanced(std::shared_ptr<Camera> camera)
- : SimpleCapture(camera)
-{
-}
-
-void SimpleCaptureBalanced::capture(unsigned int numRequests)
-{
- start();
-
- Stream *stream = config_->at(0).stream();
- const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
-
- /* No point in testing less requests then the camera depth. */
- if (buffers.size() > numRequests) {
- std::cout << "Camera needs " + std::to_string(buffers.size())
- + " requests, can't test only "
- + std::to_string(numRequests) << std::endl;
- GTEST_SKIP();
- }
-
- queueCount_ = 0;
- captureCount_ = 0;
- captureLimit_ = numRequests;
-
- /* Queue the recommended number of reqeuests. */
- for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
- std::unique_ptr<Request> request = camera_->createRequest();
- ASSERT_TRUE(request) << "Can't create request";
-
- ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
-
- ASSERT_EQ(queueRequest(request.get()), 0) << "Failed to queue request";
-
- requests_.push_back(std::move(request));
- }
-
- /* Run capture session. */
- loop_ = new EventLoop();
- loop_->exec();
- stop();
- delete loop_;
-
- ASSERT_EQ(captureCount_, captureLimit_);
-}
-
-int SimpleCaptureBalanced::queueRequest(Request *request)
-{
- queueCount_++;
- if (queueCount_ > captureLimit_)
- return 0;
-
- return camera_->queueRequest(request);
-}
-
-void SimpleCaptureBalanced::requestComplete(Request *request)
-{
- captureCount_++;
- if (captureCount_ >= captureLimit_) {
- loop_->exit(0);
- return;
- }
-
- request->reuse(Request::ReuseBuffers);
- if (queueRequest(request))
- loop_->exit(-EINVAL);
-}
-
-/* SimpleCaptureUnbalanced */
-
-SimpleCaptureUnbalanced::SimpleCaptureUnbalanced(std::shared_ptr<Camera> camera)
- : SimpleCapture(camera)
-{
-}
-
-void SimpleCaptureUnbalanced::capture(unsigned int numRequests)
-{
- start();
-
- Stream *stream = config_->at(0).stream();
- const std::vector<std::unique_ptr<FrameBuffer>> &buffers = allocator_->buffers(stream);
-
- captureCount_ = 0;
- captureLimit_ = numRequests;
-
- /* Queue the recommended number of reqeuests. */
- for (const std::unique_ptr<FrameBuffer> &buffer : buffers) {
- std::unique_ptr<Request> request = camera_->createRequest();
- ASSERT_TRUE(request) << "Can't create request";
-
- ASSERT_EQ(request->addBuffer(stream, buffer.get()), 0) << "Can't set buffer for request";
-
- ASSERT_EQ(camera_->queueRequest(request.get()), 0) << "Failed to queue request";
-
- requests_.push_back(std::move(request));
- }
-
- /* Run capture session. */
- loop_ = new EventLoop();
- int status = loop_->exec();
- stop();
- delete loop_;
-
- ASSERT_EQ(status, 0);
-}
-
-void SimpleCaptureUnbalanced::requestComplete(Request *request)
-{
- captureCount_++;
- if (captureCount_ >= captureLimit_) {
- loop_->exit(0);
- return;
- }
-
- request->reuse(Request::ReuseBuffers);
- if (camera_->queueRequest(request))
- loop_->exit(-EINVAL);
-}
diff --git a/src/apps/lc-compliance/simple_capture.h b/src/apps/lc-compliance/simple_capture.h
deleted file mode 100644
index 2911d601..00000000
--- a/src/apps/lc-compliance/simple_capture.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2020-2021, Google Inc.
- *
- * simple_capture.h - Simple capture helper
- */
-
-#pragma once
-
-#include <memory>
-
-#include <libcamera/libcamera.h>
-
-#include "../common/event_loop.h"
-
-class SimpleCapture
-{
-public:
- void configure(libcamera::StreamRole role);
-
-protected:
- SimpleCapture(std::shared_ptr<libcamera::Camera> camera);
- virtual ~SimpleCapture();
-
- void start();
- void stop();
-
- virtual void requestComplete(libcamera::Request *request) = 0;
-
- EventLoop *loop_;
-
- std::shared_ptr<libcamera::Camera> camera_;
- std::unique_ptr<libcamera::FrameBufferAllocator> allocator_;
- std::unique_ptr<libcamera::CameraConfiguration> config_;
- std::vector<std::unique_ptr<libcamera::Request>> requests_;
-};
-
-class SimpleCaptureBalanced : public SimpleCapture
-{
-public:
- SimpleCaptureBalanced(std::shared_ptr<libcamera::Camera> camera);
-
- void capture(unsigned int numRequests);
-
-private:
- int queueRequest(libcamera::Request *request);
- void requestComplete(libcamera::Request *request) override;
-
- unsigned int queueCount_;
- unsigned int captureCount_;
- unsigned int captureLimit_;
-};
-
-class SimpleCaptureUnbalanced : public SimpleCapture
-{
-public:
- SimpleCaptureUnbalanced(std::shared_ptr<libcamera::Camera> camera);
-
- void capture(unsigned int numRequests);
-
-private:
- void requestComplete(libcamera::Request *request) override;
-
- unsigned int captureCount_;
- unsigned int captureLimit_;
-};
diff --git a/src/apps/lc-compliance/test_base.cpp b/src/apps/lc-compliance/test_base.cpp
new file mode 100644
index 00000000..c9957b9e
--- /dev/null
+++ b/src/apps/lc-compliance/test_base.cpp
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, Collabora Ltd.
+ *
+ * test_base.cpp - Base definitions for tests
+ */
+
+#include "test_base.h"
+
+#include "environment.h"
+
+void CameraHolder::acquireCamera()
+{
+ Environment *env = Environment::get();
+
+ camera_ = env->cm()->get(env->cameraId());
+
+ ASSERT_EQ(camera_->acquire(), 0);
+}
+
+void CameraHolder::releaseCamera()
+{
+ if (!camera_)
+ return;
+
+ camera_->release();
+ camera_.reset();
+}
diff --git a/src/apps/lc-compliance/test_base.h b/src/apps/lc-compliance/test_base.h
new file mode 100644
index 00000000..52347749
--- /dev/null
+++ b/src/apps/lc-compliance/test_base.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, Collabora Ltd.
+ *
+ * test_base.h - Base definitions for tests
+ */
+
+#ifndef __LC_COMPLIANCE_TEST_BASE_H__
+#define __LC_COMPLIANCE_TEST_BASE_H__
+
+#include <libcamera/libcamera.h>
+
+#include <gtest/gtest.h>
+
+class CameraHolder
+{
+protected:
+ void acquireCamera();
+ void releaseCamera();
+
+ std::shared_ptr<libcamera::Camera> camera_;
+};
+
+#endif /* __LC_COMPLIANCE_TEST_BASE_H__ */
diff --git a/src/apps/lc-compliance/tests/capture_test.cpp b/src/apps/lc-compliance/tests/capture_test.cpp
new file mode 100644
index 00000000..29d8b7f8
--- /dev/null
+++ b/src/apps/lc-compliance/tests/capture_test.cpp
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ * Copyright (C) 2021, Collabora Ltd.
+ *
+ * Test camera capture
+ */
+
+#include "capture.h"
+
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "test_base.h"
+
+namespace {
+
+using namespace libcamera;
+
+class SimpleCapture : public testing::TestWithParam<std::tuple<std::vector<StreamRole>, int>>, public CameraHolder
+{
+public:
+ static std::string nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info);
+
+protected:
+ void SetUp() override;
+ void TearDown() override;
+};
+
+/*
+ * We use gtest's SetUp() and TearDown() instead of constructor and destructor
+ * in order to be able to assert on them.
+ */
+void SimpleCapture::SetUp()
+{
+ acquireCamera();
+}
+
+void SimpleCapture::TearDown()
+{
+ releaseCamera();
+}
+
+std::string SimpleCapture::nameParameters(const testing::TestParamInfo<SimpleCapture::ParamType> &info)
+{
+ const auto &[roles, numRequests] = info.param;
+ std::ostringstream ss;
+
+ for (StreamRole r : roles)
+ ss << r << '_';
+
+ ss << '_' << numRequests;
+
+ return ss.str();
+}
+
+/*
+ * Test single capture cycles
+ *
+ * Makes sure the camera completes the exact number of requests queued. Example
+ * failure is a camera that completes less requests than the number of requests
+ * queued.
+ */
+TEST_P(SimpleCapture, Capture)
+{
+ const auto &[roles, numRequests] = GetParam();
+
+ Capture capture(camera_);
+
+ capture.configure(roles);
+
+ capture.run(numRequests, numRequests);
+}
+
+/*
+ * Test multiple start/stop cycles
+ *
+ * Makes sure the camera supports multiple start/stop cycles. Example failure is
+ * a camera that does not clean up correctly in its error path but is only
+ * tested by single-capture applications.
+ */
+TEST_P(SimpleCapture, CaptureStartStop)
+{
+ const auto &[roles, numRequests] = GetParam();
+ unsigned int numRepeats = 3;
+
+ Capture capture(camera_);
+
+ capture.configure(roles);
+
+ for (unsigned int starts = 0; starts < numRepeats; starts++)
+ capture.run(numRequests, numRequests);
+}
+
+/*
+ * Test unbalanced stop
+ *
+ * Makes sure the camera supports a stop with requests queued. Example failure
+ * is a camera that does not handle cancelation of buffers coming back from the
+ * video device while stopping.
+ */
+TEST_P(SimpleCapture, UnbalancedStop)
+{
+ const auto &[roles, numRequests] = GetParam();
+
+ Capture capture(camera_);
+
+ capture.configure(roles);
+
+ capture.run(numRequests);
+}
+
+const int NUMREQUESTS[] = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
+
+const std::vector<StreamRole> SINGLEROLES[] = {
+ { StreamRole::Raw, },
+ { StreamRole::StillCapture, },
+ { StreamRole::VideoRecording, },
+ { StreamRole::Viewfinder, },
+};
+
+const std::vector<StreamRole> MULTIROLES[] = {
+ { StreamRole::Raw, StreamRole::StillCapture },
+ { StreamRole::Raw, StreamRole::VideoRecording },
+ { StreamRole::StillCapture, StreamRole::VideoRecording },
+ { StreamRole::VideoRecording, StreamRole::VideoRecording },
+};
+
+INSTANTIATE_TEST_SUITE_P(SingleStream,
+ SimpleCapture,
+ testing::Combine(testing::ValuesIn(SINGLEROLES),
+ testing::ValuesIn(NUMREQUESTS)),
+ SimpleCapture::nameParameters);
+
+INSTANTIATE_TEST_SUITE_P(MultiStream,
+ SimpleCapture,
+ testing::Combine(testing::ValuesIn(MULTIROLES),
+ testing::ValuesIn(NUMREQUESTS)),
+ SimpleCapture::nameParameters);
+
+} /* namespace */
diff --git a/src/apps/qcam/assets/shader/bayer_8.vert b/src/apps/qcam/assets/shader/bayer_8.vert
index 3695a5e9..fb5109ee 100644
--- a/src/apps/qcam/assets/shader/bayer_8.vert
+++ b/src/apps/qcam/assets/shader/bayer_8.vert
@@ -19,6 +19,8 @@ Copyright (C) 2021, Linaro
attribute vec4 vertexIn;
attribute vec2 textureIn;
+uniform mat4 proj_matrix;
+
uniform vec2 tex_size; /* The texture size in pixels */
uniform vec2 tex_step;
@@ -47,5 +49,5 @@ void main(void) {
yCoord = center.y + vec4(-2.0 * tex_step.y,
-tex_step.y, tex_step.y, 2.0 * tex_step.y);
- gl_Position = vertexIn;
+ gl_Position = proj_matrix * vertexIn;
}
diff --git a/src/apps/qcam/assets/shader/identity.vert b/src/apps/qcam/assets/shader/identity.vert
index 12c41377..907e8741 100644
--- a/src/apps/qcam/assets/shader/identity.vert
+++ b/src/apps/qcam/assets/shader/identity.vert
@@ -9,10 +9,11 @@ attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
+uniform mat4 proj_matrix;
uniform float stride_factor;
void main(void)
{
- gl_Position = vertexIn;
+ gl_Position = proj_matrix * vertexIn;
textureOut = vec2(textureIn.x * stride_factor, textureIn.y);
}
diff --git a/src/apps/qcam/cam_select_dialog.cpp b/src/apps/qcam/cam_select_dialog.cpp
index 3c8b12a9..6b6d0713 100644
--- a/src/apps/qcam/cam_select_dialog.cpp
+++ b/src/apps/qcam/cam_select_dialog.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Utkarsh Tiwari <utkarsh02t@gmail.com>
*
- * cam_select_dialog.cpp - qcam - Camera Selection dialog
+ * qcam - Camera Selection dialog
*/
#include "cam_select_dialog.h"
@@ -15,7 +15,9 @@
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
+#include <QGuiApplication>
#include <QLabel>
+#include <QScreen>
#include <QString>
CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager,
@@ -53,6 +55,14 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag
layout->addRow("Location:", cameraLocation_);
layout->addRow("Model:", cameraModel_);
layout->addWidget(buttonBox);
+
+ /*
+ * Decrease the minimum width of dialog to fit on narrow screens, with a
+ * 20 pixels margin.
+ */
+ QRect screenGeometry = qGuiApp->primaryScreen()->availableGeometry();
+ if (screenGeometry.width() < minimumWidth())
+ setMinimumWidth(screenGeometry.width() - 20);
}
CameraSelectorDialog::~CameraSelectorDialog() = default;
diff --git a/src/apps/qcam/cam_select_dialog.h b/src/apps/qcam/cam_select_dialog.h
index 0b7709ed..4bec9ea9 100644
--- a/src/apps/qcam/cam_select_dialog.h
+++ b/src/apps/qcam/cam_select_dialog.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Utkarsh Tiwari <utkarsh02t@gmail.com>
*
- * cam_select_dialog.h - qcam - Camera Selection dialog
+ * qcam - Camera Selection dialog
*/
#pragma once
diff --git a/src/apps/qcam/format_converter.cpp b/src/apps/qcam/format_converter.cpp
index de76b32c..b025a3c7 100644
--- a/src/apps/qcam/format_converter.cpp
+++ b/src/apps/qcam/format_converter.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * format_convert.cpp - qcam - Convert buffer to RGB
+ * Convert buffer to RGB
*/
#include "format_converter.h"
@@ -249,7 +249,7 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
dst_stride = width_ * 4;
for (src_y = 0, dst_y = 0; dst_y < height_; src_y++, dst_y++) {
- for (src_x = 0, dst_x = 0; dst_x < width_; ) {
+ for (src_x = 0, dst_x = 0; dst_x < width_;) {
cb = src[src_y * src_stride + src_x * 4 + cb_pos_];
cr = src[src_y * src_stride + src_x * 4 + cr_pos];
diff --git a/src/apps/qcam/format_converter.h b/src/apps/qcam/format_converter.h
index 37dbfae2..819a87a5 100644
--- a/src/apps/qcam/format_converter.h
+++ b/src/apps/qcam/format_converter.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * format_convert.h - qcam - Convert buffer to RGB
+ * Convert buffer to RGB
*/
#pragma once
diff --git a/src/apps/qcam/main.cpp b/src/apps/qcam/main.cpp
index 36cb93a5..d0bde141 100644
--- a/src/apps/qcam/main.cpp
+++ b/src/apps/qcam/main.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main.cpp - qcam - The libcamera GUI test application
+ * qcam - The libcamera GUI test application
*/
#include <signal.h>
@@ -21,6 +21,8 @@
using namespace libcamera;
+namespace {
+
void signalHandler([[maybe_unused]] int signal)
{
qInfo() << "Exiting";
@@ -52,6 +54,8 @@ OptionsParser::Options parseOptions(int argc, char *argv[])
return options;
}
+} /* namespace */
+
int main(int argc, char **argv)
{
QApplication app(argc, argv);
diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
index 0f16c038..7e3f3da6 100644
--- a/src/apps/qcam/main_window.cpp
+++ b/src/apps/qcam/main_window.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main_window.cpp - qcam - Main application window
+ * qcam - Main application window
*/
#include "main_window.h"
@@ -37,16 +37,6 @@
using namespace libcamera;
-#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
-/*
- * Qt::fixed was introduced in v5.14, and ::fixed deprecated in v5.15. Allow
- * usage of Qt::fixed unconditionally.
- */
-namespace Qt {
-constexpr auto fixed = ::fixed;
-} /* namespace Qt */
-#endif
-
/**
* \brief Custom QEvent to signal capture completion
*/
@@ -190,7 +180,7 @@ int MainWindow::createToolbars()
action = toolbar_->addAction(QIcon::fromTheme("application-exit",
QIcon(":x-circle.svg")),
"Quit");
- action->setShortcut(Qt::CTRL | Qt::Key_Q);
+ action->setShortcut(QKeySequence::Quit);
connect(action, &QAction::triggered, this, &MainWindow::quit);
/* Camera selector. */
@@ -221,7 +211,7 @@ int MainWindow::createToolbars()
action->setShortcut(QKeySequence::SaveAs);
connect(action, &QAction::triggered, this, &MainWindow::saveImageAs);
-#ifdef HAVE_DNG
+#ifdef HAVE_TIFF
/* Save Raw action. */
action = toolbar_->addAction(QIcon::fromTheme("camera-photo",
QIcon(":aperture.svg")),
@@ -261,16 +251,14 @@ void MainWindow::updateTitle()
void MainWindow::switchCamera()
{
/* Get and acquire the new camera. */
- std::string newCameraId = chooseCamera();
+ std::shared_ptr<Camera> cam = chooseCamera();
- if (newCameraId.empty())
+ if (!cam)
return;
- if (camera_ && newCameraId == camera_->id())
+ if (camera_ && cam == camera_)
return;
- const std::shared_ptr<Camera> &cam = cm_->get(newCameraId);
-
if (cam->acquire()) {
qInfo() << "Failed to acquire camera" << cam->id().c_str();
return;
@@ -292,40 +280,41 @@ void MainWindow::switchCamera()
startStopAction_->setChecked(true);
/* Display the current cameraId in the toolbar .*/
- cameraSelectButton_->setText(QString::fromStdString(newCameraId));
+ cameraSelectButton_->setText(QString::fromStdString(cam->id()));
}
-std::string MainWindow::chooseCamera()
+std::shared_ptr<Camera> MainWindow::chooseCamera()
{
if (cameraSelectorDialog_->exec() != QDialog::Accepted)
- return std::string();
+ return {};
- return cameraSelectorDialog_->getCameraId();
+ std::string id = cameraSelectorDialog_->getCameraId();
+ return cm_->get(id);
}
int MainWindow::openCamera()
{
- std::string cameraName;
-
/*
- * Use the camera specified on the command line, if any, or display the
- * camera selection dialog box otherwise.
+ * If a camera is specified on the command line, get it. Otherwise, if
+ * only one camera is available, pick it automatically, else, display
+ * the selector dialog box.
*/
- if (options_.isSet(OptCamera))
- cameraName = static_cast<std::string>(options_[OptCamera]);
- else
- cameraName = chooseCamera();
-
- if (cameraName == "")
- return -EINVAL;
+ if (options_.isSet(OptCamera)) {
+ std::string cameraName = static_cast<std::string>(options_[OptCamera]);
+ camera_ = cm_->get(cameraName);
+ if (!camera_)
+ qInfo() << "Camera" << cameraName.c_str() << "not found";
+ } else {
+ std::vector<std::shared_ptr<Camera>> cameras = cm_->cameras();
+ camera_ = (cameras.size() == 1) ? cameras[0] : chooseCamera();
+ if (!camera_)
+ qInfo() << "No camera detected";
+ }
- /* Get and acquire the camera. */
- camera_ = cm_->get(cameraName);
- if (!camera_) {
- qInfo() << "Camera" << cameraName.c_str() << "not found";
+ if (!camera_)
return -ENODEV;
- }
+ /* Acquire the camera. */
if (camera_->acquire()) {
qInfo() << "Failed to acquire camera";
camera_.reset();
@@ -333,7 +322,7 @@ int MainWindow::openCamera()
}
/* Set the camera switch button with the currently selected Camera id. */
- cameraSelectButton_->setText(QString::fromStdString(cameraName));
+ cameraSelectButton_->setText(QString::fromStdString(camera_->id()));
return 0;
}
@@ -367,6 +356,9 @@ int MainWindow::startCapture()
/* Verify roles are supported. */
switch (roles.size()) {
+ case 0:
+ roles.push_back(StreamRole::Viewfinder);
+ break;
case 1:
if (roles[0] != StreamRole::Viewfinder) {
qWarning() << "Only viewfinder supported for single stream";
@@ -397,10 +389,7 @@ int MainWindow::startCapture()
/* Use a format supported by the viewfinder if available. */
std::vector<PixelFormat> formats = vfConfig.formats().pixelformats();
for (const PixelFormat &format : viewfinder_->nativeFormats()) {
- auto match = std::find_if(formats.begin(), formats.end(),
- [&](const PixelFormat &f) {
- return f == format;
- });
+ auto match = std::find(formats.begin(), formats.end(), format);
if (match != formats.end()) {
vfConfig.pixelFormat = format;
break;
@@ -656,7 +645,7 @@ void MainWindow::captureRaw()
void MainWindow::processRaw(FrameBuffer *buffer,
[[maybe_unused]] const ControlList &metadata)
{
-#ifdef HAVE_DNG
+#ifdef HAVE_TIFF
QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
QString filename = QFileDialog::getSaveFileName(this, "Save DNG", defaultPath,
"DNG Files (*.dng)");
diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h
index 2e3e1b5c..81fcf915 100644
--- a/src/apps/qcam/main_window.h
+++ b/src/apps/qcam/main_window.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * main_window.h - qcam - Main application window
+ * qcam - Main application window
*/
#pragma once
@@ -73,7 +73,7 @@ private Q_SLOTS:
private:
int createToolbars();
- std::string chooseCamera();
+ std::shared_ptr<libcamera::Camera> chooseCamera();
int openCamera();
int startCapture();
diff --git a/src/apps/qcam/meson.build b/src/apps/qcam/meson.build
index 2e77146c..f7c14064 100644
--- a/src/apps/qcam/meson.build
+++ b/src/apps/qcam/meson.build
@@ -1,13 +1,13 @@
# SPDX-License-Identifier: CC0-1.0
-qt5 = import('qt5')
-qt5_dep = dependency('qt5',
+qt6 = import('qt6')
+qt6_dep = dependency('qt6',
method : 'pkg-config',
- modules : ['Core', 'Gui', 'Widgets'],
+ modules : ['Core', 'Gui', 'OpenGL', 'OpenGLWidgets', 'Widgets'],
required : get_option('qcam'),
- version : '>=5.4')
+ version : '>=6.2')
-if not qt5_dep.found()
+if not qt6_dep.found()
qcam_enabled = false
subdir_done()
endif
@@ -20,54 +20,40 @@ qcam_sources = files([
'main.cpp',
'main_window.cpp',
'message_handler.cpp',
+ 'viewfinder_gl.cpp',
'viewfinder_qt.cpp',
])
qcam_moc_headers = files([
'cam_select_dialog.h',
'main_window.h',
+ 'viewfinder_gl.h',
'viewfinder_qt.h',
])
qcam_resources = files([
'assets/feathericons/feathericons.qrc',
+ 'assets/shader/shaders.qrc',
])
-qt5_cpp_args = [apps_cpp_args, '-DQT_NO_KEYWORDS']
+qt6_cpp_args = [
+ apps_cpp_args,
+ '-DQT_NO_KEYWORDS',
+ '-Wno-extra-semi',
+]
-if cxx.has_header_symbol('QOpenGLWidget', 'QOpenGLWidget',
- dependencies : qt5_dep, args : '-fPIC')
- qcam_sources += files([
- 'viewfinder_gl.cpp',
- ])
- qcam_moc_headers += files([
- 'viewfinder_gl.h',
- ])
- qcam_resources += files([
- 'assets/shader/shaders.qrc'
- ])
-endif
-
-# gcc 9 introduced a deprecated-copy warning that is triggered by Qt until
-# Qt 5.13. clang 10 introduced the same warning, but detects more issues
-# that are not fixed in Qt yet. Disable the warning manually in both cases.
-if ((cc.get_id() == 'gcc' and cc.version().version_compare('>=9.0') and
- qt5_dep.version().version_compare('<5.13')) or
- (cc.get_id() == 'clang' and cc.version().version_compare('>=10.0')))
- qt5_cpp_args += ['-Wno-deprecated-copy']
-endif
-
-resources = qt5.preprocess(moc_headers : qcam_moc_headers,
+resources = qt6.preprocess(moc_headers : qcam_moc_headers,
qresources : qcam_resources,
- dependencies : qt5_dep)
+ dependencies : qt6_dep)
qcam = executable('qcam', qcam_sources, resources,
install : true,
+ install_tag : 'bin',
link_with : apps_lib,
dependencies : [
libatomic,
libcamera_public,
libtiff,
- qt5_dep,
+ qt6_dep,
],
- cpp_args : qt5_cpp_args)
+ cpp_args : qt6_cpp_args)
diff --git a/src/apps/qcam/message_handler.cpp b/src/apps/qcam/message_handler.cpp
index 261623e1..c89714a9 100644
--- a/src/apps/qcam/message_handler.cpp
+++ b/src/apps/qcam/message_handler.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
- * message_handler.cpp - qcam - Log message handling
+ * qcam - Log message handling
*/
#include "message_handler.h"
diff --git a/src/apps/qcam/message_handler.h b/src/apps/qcam/message_handler.h
index 56294d37..92ef74d1 100644
--- a/src/apps/qcam/message_handler.h
+++ b/src/apps/qcam/message_handler.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
- * message_handler.cpp - qcam - Log message handling
+ * Log message handling
*/
#pragma once
diff --git a/src/apps/qcam/viewfinder.h b/src/apps/qcam/viewfinder.h
index a57446e8..914f88ec 100644
--- a/src/apps/qcam/viewfinder.h
+++ b/src/apps/qcam/viewfinder.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder.h - qcam - Viewfinder base class
+ * qcam - Viewfinder base class
*/
#pragma once
diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp
index f83b99ad..f31956ff 100644
--- a/src/apps/qcam/viewfinder_gl.cpp
+++ b/src/apps/qcam/viewfinder_gl.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Linaro
*
- * viewfinderGL.cpp - OpenGL Viewfinder for rendering by OpenGL shader
+ * OpenGL Viewfinder for rendering by OpenGL shader
*/
#include "viewfinder_gl.h"
@@ -12,6 +12,7 @@
#include <QByteArray>
#include <QFile>
#include <QImage>
+#include <QMatrix4x4>
#include <QStringList>
#include <libcamera/formats.h>
@@ -443,15 +444,11 @@ bool ViewFinderGL::createFragmentShader()
close();
}
- /* Bind shader pipeline for use */
- if (!shaderProgram_.bind()) {
- qWarning() << "[ViewFinderGL]:" << shaderProgram_.log();
- close();
- }
-
attributeVertex = shaderProgram_.attributeLocation("vertexIn");
attributeTexture = shaderProgram_.attributeLocation("textureIn");
+ vertexBuffer_.bind();
+
shaderProgram_.enableAttributeArray(attributeVertex);
shaderProgram_.setAttributeBuffer(attributeVertex,
GL_FLOAT,
@@ -466,6 +463,9 @@ bool ViewFinderGL::createFragmentShader()
2,
2 * sizeof(GLfloat));
+ vertexBuffer_.release();
+
+ projMatrixUniform_ = shaderProgram_.uniformLocation("proj_matrix");
textureUniformY_ = shaderProgram_.uniformLocation("tex_y");
textureUniformU_ = shaderProgram_.uniformLocation("tex_u");
textureUniformV_ = shaderProgram_.uniformLocation("tex_v");
@@ -511,7 +511,7 @@ void ViewFinderGL::initializeGL()
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
- static const GLfloat coordinates[2][4][2]{
+ const GLfloat coordinates[2][4][2]{
{
/* Vertex coordinates */
{ -1.0f, -1.0f },
@@ -535,8 +535,6 @@ void ViewFinderGL::initializeGL()
/* Create Vertex Shader */
if (!createVertexShader())
qWarning() << "[ViewFinderGL]: create vertex shader failed.";
-
- glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
void ViewFinderGL::doRender()
@@ -805,28 +803,42 @@ void ViewFinderGL::doRender()
shaderProgram_.setUniformValue(textureUniformStrideFactor_,
static_cast<float>(size_.width() - 1) /
(stridePixels - 1));
+
+ /*
+ * Place the viewfinder in the centre of the widget, preserving the
+ * aspect ratio of the image.
+ */
+ QMatrix4x4 projMatrix;
+ QSizeF scaledSize = size_.scaled(size(), Qt::KeepAspectRatio);
+ projMatrix.scale(scaledSize.width() / size().width(),
+ scaledSize.height() / size().height());
+
+ shaderProgram_.setUniformValue(projMatrixUniform_, projMatrix);
}
void ViewFinderGL::paintGL()
{
- if (!fragmentShader_)
+ if (!fragmentShader_) {
if (!createFragmentShader()) {
qWarning() << "[ViewFinderGL]:"
<< "create fragment shader failed.";
}
+ }
- if (image_) {
- glClearColor(0.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- doRender();
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ /* Bind shader pipeline for use. */
+ if (!shaderProgram_.bind()) {
+ qWarning() << "[ViewFinderGL]:" << shaderProgram_.log();
+ close();
}
-}
-void ViewFinderGL::resizeGL(int w, int h)
-{
- glViewport(0, 0, w, h);
+ if (!image_)
+ return;
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ doRender();
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
QSize ViewFinderGL::sizeHint() const
diff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h
index 68c2912d..23c657bc 100644
--- a/src/apps/qcam/viewfinder_gl.h
+++ b/src/apps/qcam/viewfinder_gl.h
@@ -2,8 +2,7 @@
/*
* Copyright (C) 2020, Linaro
*
- * viewfinder_GL.h - OpenGL Viewfinder for rendering by OpenGL shader
- *
+ * OpenGL Viewfinder for rendering by OpenGL shader
*/
#pragma once
@@ -52,7 +51,6 @@ Q_SIGNALS:
protected:
void initializeGL() override;
void paintGL() override;
- void resizeGL(int w, int h) override;
QSize sizeHint() const override;
private:
@@ -89,6 +87,7 @@ private:
/* Common texture parameters */
GLuint textureMinMagFilters_;
+ GLuint projMatrixUniform_;
/* YUV texture parameters */
GLuint textureUniformU_;
diff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp
index 62ed5e7c..1a238922 100644
--- a/src/apps/qcam/viewfinder_qt.cpp
+++ b/src/apps/qcam/viewfinder_qt.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder_qt.cpp - qcam - QPainter-based ViewFinder
+ * qcam - QPainter-based ViewFinder
*/
#include "viewfinder_qt.h"
@@ -18,6 +18,7 @@
#include <QMap>
#include <QMutexLocker>
#include <QPainter>
+#include <QResizeEvent>
#include <QtDebug>
#include "../common/image.h"
@@ -26,23 +27,23 @@
static const QMap<libcamera::PixelFormat, QImage::Format> nativeFormats
{
-#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
{ libcamera::formats::ABGR8888, QImage::Format_RGBX8888 },
{ libcamera::formats::XBGR8888, QImage::Format_RGBX8888 },
-#endif
{ libcamera::formats::ARGB8888, QImage::Format_RGB32 },
{ libcamera::formats::XRGB8888, QImage::Format_RGB32 },
-#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
{ libcamera::formats::RGB888, QImage::Format_BGR888 },
-#endif
{ libcamera::formats::BGR888, QImage::Format_RGB888 },
{ libcamera::formats::RGB565, QImage::Format_RGB16 },
};
ViewFinderQt::ViewFinderQt(QWidget *parent)
- : QWidget(parent), buffer_(nullptr)
+ : QWidget(parent), place_(rect()), buffer_(nullptr)
{
icon_ = QIcon(":camera-off.svg");
+
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, Qt::black);
+ setPalette(pal);
}
ViewFinderQt::~ViewFinderQt()
@@ -117,6 +118,11 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer, Image *image)
}
}
+ /*
+ * Indicate the widget paints all its pixels, to optimize rendering by
+ * skipping erasing the widget before painting.
+ */
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
update();
if (buffer)
@@ -132,6 +138,11 @@ void ViewFinderQt::stop()
buffer_ = nullptr;
}
+ /*
+ * The logo has a transparent background, reenable erasing the widget
+ * before painting.
+ */
+ setAttribute(Qt::WA_OpaquePaintEvent, false);
update();
}
@@ -146,9 +157,23 @@ void ViewFinderQt::paintEvent(QPaintEvent *)
{
QPainter painter(this);
- /* If we have an image, draw it. */
+ painter.setBrush(palette().window());
+
+ /* If we have an image, draw it, with black letterbox rectangles. */
if (!image_.isNull()) {
- painter.drawImage(rect(), image_, image_.rect());
+ if (place_.width() < width()) {
+ QRect rect{ 0, 0, (width() - place_.width()) / 2, height() };
+ painter.drawRect(rect);
+ rect.moveLeft(place_.right());
+ painter.drawRect(rect);
+ } else {
+ QRect rect{ 0, 0, width(), (height() - place_.height()) / 2 };
+ painter.drawRect(rect);
+ rect.moveTop(place_.bottom());
+ painter.drawRect(rect);
+ }
+
+ painter.drawImage(place_, image_, image_.rect());
return;
}
@@ -173,7 +198,6 @@ void ViewFinderQt::paintEvent(QPaintEvent *)
else
point.setY((height() - pixmap_.height()) / 2);
- painter.setBackgroundMode(Qt::OpaqueMode);
painter.drawPixmap(point, pixmap_);
}
@@ -181,3 +205,14 @@ QSize ViewFinderQt::sizeHint() const
{
return size_.isValid() ? size_ : QSize(640, 480);
}
+
+void ViewFinderQt::resizeEvent(QResizeEvent *event)
+{
+ if (!size_.isValid())
+ return;
+
+ place_.setSize(size_.scaled(event->size(), Qt::KeepAspectRatio));
+ place_.moveCenter(rect().center());
+
+ QWidget::resizeEvent(event);
+}
diff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h
index eb3a9988..50fde88e 100644
--- a/src/apps/qcam/viewfinder_qt.h
+++ b/src/apps/qcam/viewfinder_qt.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * viewfinder_qt.h - qcam - QPainter-based ViewFinder
+ * qcam - QPainter-based ViewFinder
*/
#pragma once
@@ -44,6 +44,7 @@ Q_SIGNALS:
protected:
void paintEvent(QPaintEvent *) override;
+ void resizeEvent(QResizeEvent *) override;
QSize sizeHint() const override;
private:
@@ -51,6 +52,7 @@ private:
libcamera::PixelFormat format_;
QSize size_;
+ QRect place_;
/* Camera stopped icon */
QSize vfSize_;
diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in b/src/gstreamer/gstlibcamera-controls.cpp.in
new file mode 100644
index 00000000..89c530da
--- /dev/null
+++ b/src/gstreamer/gstlibcamera-controls.cpp.in
@@ -0,0 +1,327 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Jaslo Ziska
+ *
+ * GStreamer Camera Controls
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#include <vector>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+
+#include "gstlibcamera-controls.h"
+
+using namespace libcamera;
+
+static void value_set_rectangle(GValue *value, const Rectangle &rect)
+{
+ Point top_left = rect.topLeft();
+ Size size = rect.size();
+
+ GValue x = G_VALUE_INIT;
+ g_value_init(&x, G_TYPE_INT);
+ g_value_set_int(&x, top_left.x);
+ gst_value_array_append_and_take_value(value, &x);
+
+ GValue y = G_VALUE_INIT;
+ g_value_init(&y, G_TYPE_INT);
+ g_value_set_int(&y, top_left.y);
+ gst_value_array_append_and_take_value(value, &y);
+
+ GValue width = G_VALUE_INIT;
+ g_value_init(&width, G_TYPE_INT);
+ g_value_set_int(&width, size.width);
+ gst_value_array_append_and_take_value(value, &width);
+
+ GValue height = G_VALUE_INIT;
+ g_value_init(&height, G_TYPE_INT);
+ g_value_set_int(&height, size.height);
+ gst_value_array_append_and_take_value(value, &height);
+}
+
+static Rectangle value_get_rectangle(const GValue *value)
+{
+ const GValue *r;
+ r = gst_value_array_get_value(value, 0);
+ int x = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 1);
+ int y = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 2);
+ int w = g_value_get_int(r);
+ r = gst_value_array_get_value(value, 3);
+ int h = g_value_get_int(r);
+
+ return Rectangle(x, y, w, h);
+}
+
+{% for vendor, ctrls in controls %}
+{%- for ctrl in ctrls if ctrl.is_enum %}
+static const GEnumValue {{ ctrl.name|snake_case }}_types[] = {
+{%- for enum in ctrl.enum_values %}
+ {
+ controls::{{ ctrl.namespace }}{{ enum.name }},
+ {{ enum.description|format_description|indent_str('\t\t') }},
+ "{{ enum.gst_name }}"
+ },
+{%- endfor %}
+ {0, NULL, NULL}
+};
+
+#define TYPE_{{ ctrl.name|snake_case|upper }} \
+ ({{ ctrl.name|snake_case }}_get_type())
+static GType {{ ctrl.name|snake_case }}_get_type()
+{
+ static GType {{ ctrl.name|snake_case }}_type = 0;
+
+ if (!{{ ctrl.name|snake_case }}_type)
+ {{ ctrl.name|snake_case }}_type =
+ g_enum_register_static("{{ ctrl.name }}",
+ {{ ctrl.name|snake_case }}_types);
+
+ return {{ ctrl.name|snake_case }}_type;
+}
+{% endfor %}
+{%- endfor %}
+
+void GstCameraControls::installProperties(GObjectClass *klass, int lastPropId)
+{
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+{%- set spec %}
+{%- if ctrl.is_rectangle -%}
+gst_param_spec_array(
+{%- else -%}
+g_param_spec_{{ ctrl.gtype }}(
+{%- endif -%}
+{%- if ctrl.is_array %}
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}-value",
+ "{{ ctrl.name }} Value",
+ "One {{ ctrl.name }} element value",
+{%- else %}
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}",
+ "{{ ctrl.name }}",
+ {{ ctrl.description|format_description|indent_str('\t') }},
+{%- endif %}
+{%- if ctrl.is_enum %}
+ TYPE_{{ ctrl.name|snake_case|upper }},
+ {{ ctrl.default }},
+{%- elif ctrl.is_rectangle %}
+ g_param_spec_int(
+ "rectangle-value",
+ "Rectangle Value",
+ "One rectangle value, either x, y, width or height.",
+ {{ ctrl.min }}, {{ ctrl.max }}, {{ ctrl.default }},
+ (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+ ),
+{%- elif ctrl.gtype == 'boolean' %}
+ {{ ctrl.default }},
+{%- elif ctrl.gtype in ['float', 'int', 'int64', 'uchar'] %}
+ {{ ctrl.min }}, {{ ctrl.max }}, {{ ctrl.default }},
+{%- endif %}
+ (GParamFlags) (GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+)
+{%- endset %}
+
+ g_object_class_install_property(
+ klass,
+ lastPropId + controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }},
+{%- if ctrl.is_array %}
+ gst_param_spec_array(
+ "{{ ctrl.vendor_prefix }}{{ ctrl.name|kebab_case }}",
+ "{{ ctrl.name }}",
+ {{ ctrl.description|format_description|indent_str('\t\t\t') }},
+ {{ spec|indent_str('\t\t\t') }},
+ (GParamFlags) (GST_PARAM_CONTROLLABLE |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS)
+ )
+{%- else %}
+ {{ spec|indent_str('\t\t') }}
+{%- endif %}
+ );
+{%- endfor %}
+{%- endfor %}
+}
+
+bool GstCameraControls::getProperty(guint propId, GValue *value,
+ [[maybe_unused]] GParamSpec *pspec)
+{
+ if (!controls_acc_.contains(propId)) {
+ GST_WARNING("Control '%s' is not available, default value will "
+ "be returned",
+ controls::controls.at(propId)->name().c_str());
+ return true;
+ }
+ const ControlValue &cv = controls_acc_.get(propId);
+
+ switch (propId) {
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+ case controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }}: {
+ auto control = cv.get<{{ ctrl.type }}>();
+
+{%- if ctrl.is_array %}
+ for (size_t i = 0; i < control.size(); ++i) {
+ GValue element = G_VALUE_INIT;
+{%- if ctrl.is_rectangle %}
+ g_value_init(&element, GST_TYPE_PARAM_ARRAY_LIST);
+ value_set_rectangle(&element, control[i]);
+{%- else %}
+ g_value_init(&element, G_TYPE_{{ ctrl.gtype|upper }});
+ g_value_set_{{ ctrl.gtype }}(&element, control[i]);
+{%- endif %}
+ gst_value_array_append_and_take_value(value, &element);
+ }
+{%- else %}
+{%- if ctrl.is_rectangle %}
+ value_set_rectangle(value, control);
+{%- else %}
+ g_value_set_{{ ctrl.gtype }}(value, control);
+{%- endif %}
+{%- endif %}
+
+ return true;
+ }
+{%- endfor %}
+{%- endfor %}
+
+ default:
+ return false;
+ }
+}
+
+bool GstCameraControls::setProperty(guint propId, const GValue *value,
+ [[maybe_unused]] GParamSpec *pspec)
+{
+ /*
+ * Check whether the camera capabilities are already available.
+ * They might not be available if the pipeline has not started yet.
+ */
+ if (!capabilities_.empty()) {
+ /* If so, check that the control is supported by the camera. */
+ const ControlId *cid = capabilities_.idmap().at(propId);
+ auto info = capabilities_.find(cid);
+
+ if (info == capabilities_.end()) {
+ GST_WARNING("Control '%s' is not supported by the "
+ "camera and will be ignored",
+ cid->name().c_str());
+ return true;
+ }
+ }
+
+ switch (propId) {
+{%- for vendor, ctrls in controls %}
+{%- for ctrl in ctrls %}
+
+ case controls::{{ ctrl.namespace }}{{ ctrl.name|snake_case|upper }}: {
+{%- if ctrl.is_array %}
+ size_t size = gst_value_array_get_size(value);
+{%- if ctrl.size != 0 %}
+ if (size != {{ ctrl.size }}) {
+ GST_ERROR("Incorrect array size for control "
+ "'{{ ctrl.name|kebab_case }}', must be of "
+ "size {{ ctrl.size }}");
+ return true;
+ }
+{%- endif %}
+
+ std::vector<{{ ctrl.element_type }}> values(size);
+ for (size_t i = 0; i < size; ++i) {
+ const GValue *element =
+ gst_value_array_get_value(value, i);
+{%- if ctrl.is_rectangle %}
+ if (gst_value_array_get_size(element) != 4) {
+ GST_ERROR("Rectangle in control "
+ "'{{ ctrl.name|kebab_case }}' at"
+ "index %zu must be an array of size 4",
+ i);
+ return true;
+ }
+ values[i] = value_get_rectangle(element);
+{%- else %}
+ values[i] = g_value_get_{{ ctrl.gtype }}(element);
+{%- endif %}
+ }
+
+{%- if ctrl.size == 0 %}
+ Span<const {{ ctrl.element_type }}> val(values.data(), size);
+{%- else %}
+ Span<const {{ ctrl.element_type }}, {{ ctrl.size }}> val(values.data(), size);
+{%- endif %}
+{%- else %}
+{%- if ctrl.is_rectangle %}
+ if (gst_value_array_get_size(value) != 4) {
+ GST_ERROR("Rectangle in control "
+ "'{{ ctrl.name|kebab_case }}' must be an "
+ "array of size 4");
+ return true;
+ }
+ Rectangle val = value_get_rectangle(value);
+{%- else %}
+ auto val = g_value_get_{{ ctrl.gtype }}(value);
+{%- endif %}
+{%- endif %}
+ controls_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
+ controls_acc_.set(controls::{{ ctrl.namespace }}{{ ctrl.name }}, val);
+ return true;
+ }
+{%- endfor %}
+{%- endfor %}
+
+ default:
+ return false;
+ }
+}
+
+void GstCameraControls::setCamera(const std::shared_ptr<libcamera::Camera> &cam)
+{
+ capabilities_ = cam->controls();
+
+ /*
+ * Check the controls which were set before the camera capabilities were
+ * known. This is required because GStreamer may set properties before
+ * the pipeline has started and thus before the camera was known.
+ */
+ ControlList new_controls;
+ for (auto control = controls_acc_.begin();
+ control != controls_acc_.end();
+ ++control) {
+ unsigned int id = control->first;
+ ControlValue value = control->second;
+
+ const ControlId *cid = capabilities_.idmap().at(id);
+ auto info = capabilities_.find(cid);
+
+ /* Only add controls which are supported. */
+ if (info != capabilities_.end())
+ new_controls.set(id, value);
+ else
+ GST_WARNING("Control '%s' is not supported by the "
+ "camera and will be ignored",
+ cid->name().c_str());
+ }
+
+ controls_acc_ = new_controls;
+ controls_ = new_controls;
+}
+
+void GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> &request)
+{
+ request->controls().merge(controls_);
+ controls_.clear();
+}
+
+void GstCameraControls::readMetadata(libcamera::Request *request)
+{
+ controls_acc_.merge(request->metadata(),
+ ControlList::MergePolicy::OverwriteExisting);
+}
diff --git a/src/gstreamer/gstlibcamera-controls.h b/src/gstreamer/gstlibcamera-controls.h
new file mode 100644
index 00000000..749220b5
--- /dev/null
+++ b/src/gstreamer/gstlibcamera-controls.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Collabora Ltd.
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+ *
+ * GStreamer Camera Controls
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <libcamera/camera.h>
+#include <libcamera/controls.h>
+#include <libcamera/request.h>
+
+#include "gstlibcamerasrc.h"
+
+namespace libcamera {
+
+class GstCameraControls
+{
+public:
+ static void installProperties(GObjectClass *klass, int lastProp);
+
+ bool getProperty(guint propId, GValue *value, GParamSpec *pspec);
+ bool setProperty(guint propId, const GValue *value, GParamSpec *pspec);
+
+ void setCamera(const std::shared_ptr<libcamera::Camera> &cam);
+
+ void applyControls(std::unique_ptr<libcamera::Request> &request);
+ void readMetadata(libcamera::Request *request);
+
+private:
+ /* Supported controls and limits of camera. */
+ ControlInfoMap capabilities_;
+ /* Set of user modified controls. */
+ ControlList controls_;
+ /* Accumulator of all controls ever set and metadata returned by camera */
+ ControlList controls_acc_;
+};
+
+} /* namespace libcamera */
diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index 750ec351..a548b0c1 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera-utils.c - GStreamer libcamera Utility Function
+ * GStreamer libcamera Utility Function
*/
#include "gstlibcamera-utils.h"
@@ -25,6 +25,26 @@ static struct {
{ GST_VIDEO_FORMAT_ENCODED, formats::SGBRG8 },
{ GST_VIDEO_FORMAT_ENCODED, formats::SGRBG8 },
{ GST_VIDEO_FORMAT_ENCODED, formats::SRGGB8 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SBGGR10 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGBRG10 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGRBG10 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SRGGB10 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SBGGR12 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGBRG12 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGRBG12 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SRGGB12 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SBGGR14 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGBRG14 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGRBG14 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SRGGB14 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SBGGR16 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGBRG16 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SGRBG16 },
+ { GST_VIDEO_FORMAT_ENCODED, formats::SRGGB16 },
+
+ /* Monochrome */
+ { GST_VIDEO_FORMAT_GRAY8, formats::R8 },
+ { GST_VIDEO_FORMAT_GRAY16_LE, formats::R16 },
/* RGB16 */
{ GST_VIDEO_FORMAT_RGB16, formats::RGB565 },
@@ -54,6 +74,7 @@ static struct {
{ GST_VIDEO_FORMAT_I420, formats::YUV420 },
{ GST_VIDEO_FORMAT_YV12, formats::YVU420 },
{ GST_VIDEO_FORMAT_Y42B, formats::YUV422 },
+ { GST_VIDEO_FORMAT_Y444, formats::YUV444 },
/* YUV Packed */
{ GST_VIDEO_FORMAT_UYVY, formats::UYVY },
@@ -65,7 +86,7 @@ static struct {
};
static GstVideoColorimetry
-colorimetry_from_colorspace(const ColorSpace &colorSpace)
+colorimetry_from_colorspace(const ColorSpace &colorSpace, GstVideoTransferFunction transfer)
{
GstVideoColorimetry colorimetry;
@@ -93,6 +114,8 @@ colorimetry_from_colorspace(const ColorSpace &colorSpace)
break;
case ColorSpace::TransferFunction::Rec709:
colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
+ if (transfer != GST_VIDEO_TRANSFER_UNKNOWN)
+ colorimetry.transfer = transfer;
break;
}
@@ -124,7 +147,8 @@ colorimetry_from_colorspace(const ColorSpace &colorSpace)
}
static std::optional<ColorSpace>
-colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
+colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry,
+ GstVideoTransferFunction *transfer)
{
std::optional<ColorSpace> colorspace = ColorSpace::Raw;
@@ -168,6 +192,7 @@ colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
case GST_VIDEO_TRANSFER_BT2020_12:
case GST_VIDEO_TRANSFER_BT709:
colorspace->transferFunction = ColorSpace::TransferFunction::Rec709;
+ *transfer = colorimetry.transfer;
break;
default:
GST_WARNING("Colorimetry transfer function %d not mapped in gstlibcamera",
@@ -234,20 +259,50 @@ gst_format_to_pixel_format(GstVideoFormat gst_format)
return PixelFormat{};
}
+static const struct {
+ PixelFormat format;
+ const gchar *name;
+} bayer_map[]{
+ { formats::SBGGR8, "bggr" },
+ { formats::SGBRG8, "gbrg" },
+ { formats::SGRBG8, "grbg" },
+ { formats::SRGGB8, "rggb" },
+ { formats::SBGGR10, "bggr10le" },
+ { formats::SGBRG10, "gbrg10le" },
+ { formats::SGRBG10, "grbg10le" },
+ { formats::SRGGB10, "rggb10le" },
+ { formats::SBGGR12, "bggr12le" },
+ { formats::SGBRG12, "gbrg12le" },
+ { formats::SGRBG12, "grbg12le" },
+ { formats::SRGGB12, "rggb12le" },
+ { formats::SBGGR14, "bggr14le" },
+ { formats::SGBRG14, "gbrg14le" },
+ { formats::SGRBG14, "grbg14le" },
+ { formats::SRGGB14, "rggb14le" },
+ { formats::SBGGR16, "bggr16le" },
+ { formats::SGBRG16, "gbrg16le" },
+ { formats::SGRBG16, "grbg16le" },
+ { formats::SRGGB16, "rggb16le" },
+};
+
static const gchar *
-bayer_format_to_string(int format)
+bayer_format_to_string(PixelFormat format)
{
- switch (format) {
- case formats::SBGGR8:
- return "bggr";
- case formats::SGBRG8:
- return "gbrg";
- case formats::SGRBG8:
- return "grbg";
- case formats::SRGGB8:
- return "rggb";
+ for (auto &b : bayer_map) {
+ if (b.format == format)
+ return b.name;
}
- return NULL;
+ return nullptr;
+}
+
+static PixelFormat
+bayer_format_from_string(const gchar *name)
+{
+ for (auto &b : bayer_map) {
+ if (strcmp(b.name, name) == 0)
+ return b.format;
+ }
+ return PixelFormat{};
}
static GstStructure *
@@ -307,13 +362,21 @@ gst_libcamera_stream_formats_to_caps(const StreamFormats &formats)
GValue val = G_VALUE_INIT;
g_value_init(&val, GST_TYPE_INT_RANGE);
- gst_value_set_int_range_step(&val, range.min.width, range.max.width, range.hStep);
- gst_structure_set_value(s, "width", &val);
- gst_value_set_int_range_step(&val, range.min.height, range.max.height, range.vStep);
- gst_structure_set_value(s, "height", &val);
+ if (range.min.width == range.max.width) {
+ gst_structure_set(s, "width", G_TYPE_INT, range.min.width, nullptr);
+ } else {
+ gst_value_set_int_range_step(&val, range.min.width, range.max.width, range.hStep);
+ gst_structure_set_value(s, "width", &val);
+ }
+ if (range.min.height == range.max.height) {
+ gst_structure_set(s, "height", G_TYPE_INT, range.min.height, nullptr);
+ } else {
+ gst_value_set_int_range_step(&val, range.min.height, range.max.height, range.vStep);
+ gst_structure_set_value(s, "height", &val);
+ }
g_value_unset(&val);
- gst_caps_append_structure(caps, s);
+ caps = gst_caps_merge_structure(caps, s);
}
}
@@ -321,7 +384,8 @@ gst_libcamera_stream_formats_to_caps(const StreamFormats &formats)
}
GstCaps *
-gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg)
+gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg,
+ GstVideoTransferFunction transfer)
{
GstCaps *caps = gst_caps_new_empty();
GstStructure *s = bare_structure_from_format(stream_cfg.pixelFormat);
@@ -332,8 +396,8 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
nullptr);
if (stream_cfg.colorSpace) {
- GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value());
- gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
+ GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value(), transfer);
+ g_autofree gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
if (colorimetry_str)
gst_structure_set(s, "colorimetry", G_TYPE_STRING, colorimetry_str, nullptr);
@@ -347,9 +411,8 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
return caps;
}
-void
-gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
- GstCaps *caps)
+void gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
+ GstCaps *caps, GstVideoTransferFunction *transfer)
{
GstVideoFormat gst_format = pixel_format_to_gst_format(stream_cfg.pixelFormat);
guint i;
@@ -414,6 +477,9 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
const gchar *format = gst_structure_get_string(s, "format");
gst_format = gst_video_format_from_string(format);
stream_cfg.pixelFormat = gst_format_to_pixel_format(gst_format);
+ } else if (gst_structure_has_name(s, "video/x-bayer")) {
+ const gchar *format = gst_structure_get_string(s, "format");
+ stream_cfg.pixelFormat = bayer_format_from_string(format);
} else if (gst_structure_has_name(s, "image/jpeg")) {
stream_cfg.pixelFormat = formats::MJPEG;
} else {
@@ -428,13 +494,16 @@ gst_libcamera_configure_stream_from_caps(StreamConfiguration &stream_cfg,
/* Configure colorimetry */
if (gst_structure_has_field(s, "colorimetry")) {
- const gchar *colorimetry_str = gst_structure_get_string(s, "colorimetry");
+ const gchar *colorimetry_str;
GstVideoColorimetry colorimetry;
+ gst_structure_fixate_field(s, "colorimetry");
+ colorimetry_str = gst_structure_get_string(s, "colorimetry");
+
if (!gst_video_colorimetry_from_string(&colorimetry, colorimetry_str))
g_critical("Invalid colorimetry %s", colorimetry_str);
- stream_cfg.colorSpace = colorspace_from_colorimetry(colorimetry);
+ stream_cfg.colorSpace = colorspace_from_colorimetry(colorimetry, transfer);
}
}
@@ -530,6 +599,43 @@ gst_task_resume(GstTask *task)
}
#endif
+#if !GST_CHECK_VERSION(1, 22, 0)
+/*
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) <2007> David A. Schleef <ds@schleef.org>
+ */
+/*
+ * This function has been imported directly from the gstreamer project to
+ * support backwards compatibility and should be removed when the older version
+ * is no longer supported.
+ */
+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride)
+{
+ gint estride;
+ gint comp[GST_VIDEO_MAX_COMPONENTS];
+ gint i;
+
+ /* There is nothing to extrapolate on first plane. */
+ if (plane == 0)
+ return stride;
+
+ gst_video_format_info_component(finfo, plane, comp);
+
+ /*
+ * For now, all planar formats have a single component on first plane, but
+ * if there was a planar format with more, we'd have to make a ratio of the
+ * number of component on the first plane against the number of component on
+ * the current plane.
+ */
+ estride = 0;
+ for (i = 0; i < GST_VIDEO_MAX_COMPONENTS && comp[i] >= 0; i++)
+ estride += GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(finfo, comp[i], stride);
+
+ return estride;
+}
+#endif
+
G_LOCK_DEFINE_STATIC(cm_singleton_lock);
static std::weak_ptr<CameraManager> cm_singleton_ptr;
diff --git a/src/gstreamer/gstlibcamera-utils.h b/src/gstreamer/gstlibcamera-utils.h
index fd304a8b..5f4e8a0f 100644
--- a/src/gstreamer/gstlibcamera-utils.h
+++ b/src/gstreamer/gstlibcamera-utils.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera-utils.h - GStreamer libcamera Utility Functions
+ * GStreamer libcamera Utility Functions
*/
#pragma once
@@ -16,18 +16,31 @@
#include <gst/video/video.h>
GstCaps *gst_libcamera_stream_formats_to_caps(const libcamera::StreamFormats &formats);
-GstCaps *gst_libcamera_stream_configuration_to_caps(const libcamera::StreamConfiguration &stream_cfg);
+GstCaps *gst_libcamera_stream_configuration_to_caps(const libcamera::StreamConfiguration &stream_cfg,
+ GstVideoTransferFunction transfer);
void gst_libcamera_configure_stream_from_caps(libcamera::StreamConfiguration &stream_cfg,
- GstCaps *caps);
+ GstCaps *caps, GstVideoTransferFunction *transfer);
void gst_libcamera_get_framerate_from_caps(GstCaps *caps, GstStructure *element_caps);
void gst_libcamera_clamp_and_set_frameduration(libcamera::ControlList &controls,
const libcamera::ControlInfoMap &camera_controls,
GstStructure *element_caps);
void gst_libcamera_framerate_to_caps(GstCaps *caps, const GstStructure *element_caps);
+#if !GST_CHECK_VERSION(1, 16, 0)
+static inline void gst_clear_event(GstEvent **event_ptr)
+{
+ g_clear_pointer(event_ptr, gst_mini_object_unref);
+}
+#endif
+
#if !GST_CHECK_VERSION(1, 17, 1)
gboolean gst_task_resume(GstTask *task);
#endif
+
+#if !GST_CHECK_VERSION(1, 22, 0)
+gint gst_video_format_info_extrapolate_stride(const GstVideoFormatInfo *finfo, gint plane, gint stride);
+#endif
+
std::shared_ptr<libcamera::CameraManager> gst_libcamera_get_camera_manager(int &ret);
/**
diff --git a/src/gstreamer/gstlibcamera.cpp b/src/gstreamer/gstlibcamera.cpp
index 52388b5e..bff98979 100644
--- a/src/gstreamer/gstlibcamera.cpp
+++ b/src/gstreamer/gstlibcamera.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamera.c - GStreamer plugin
+ * GStreamer plugin
*/
#include "gstlibcameraprovider.h"
diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp
index c740b8fc..d4492d99 100644
--- a/src/gstreamer/gstlibcameraallocator.cpp
+++ b/src/gstreamer/gstlibcameraallocator.cpp
@@ -3,11 +3,13 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraallocator.cpp - GStreamer Custom Allocator
+ * GStreamer Custom Allocator
*/
#include "gstlibcameraallocator.h"
+#include <utility>
+
#include <libcamera/camera.h>
#include <libcamera/framebuffer_allocator.h>
#include <libcamera/stream.h>
@@ -100,6 +102,11 @@ struct _GstLibcameraAllocator {
* FrameWrap.
*/
GHashTable *pools;
+ /*
+ * The camera manager represents the library, which needs to be kept
+ * alive until all the memory has been released.
+ */
+ std::shared_ptr<CameraManager> *cm_ptr;
};
G_DEFINE_TYPE(GstLibcameraAllocator, gst_libcamera_allocator,
@@ -173,6 +180,9 @@ gst_libcamera_allocator_finalize(GObject *object)
delete self->fb_allocator;
+ /* Keep last. */
+ delete self->cm_ptr;
+
G_OBJECT_CLASS(gst_libcamera_allocator_parent_class)->finalize(object);
}
@@ -191,16 +201,20 @@ GstLibcameraAllocator *
gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
CameraConfiguration *config_)
{
- auto *self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
- nullptr));
+ g_autoptr(GstLibcameraAllocator) self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
+ nullptr));
+ gint ret;
+
+ self->cm_ptr = new std::shared_ptr<CameraManager>(gst_libcamera_get_camera_manager(ret));
+ if (ret)
+ return nullptr;
self->fb_allocator = new FrameBufferAllocator(camera);
for (StreamConfiguration &streamCfg : *config_) {
Stream *stream = streamCfg.stream();
- gint ret;
ret = self->fb_allocator->allocate(stream);
- if (ret == 0)
+ if (ret <= 0)
return nullptr;
GQueue *pool = g_queue_new();
@@ -214,7 +228,7 @@ gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
g_hash_table_insert(self->pools, stream, pool);
}
- return self;
+ return std::exchange(self, nullptr);
}
bool
diff --git a/src/gstreamer/gstlibcameraallocator.h b/src/gstreamer/gstlibcameraallocator.h
index 0a08c3bb..1a6ba346 100644
--- a/src/gstreamer/gstlibcameraallocator.h
+++ b/src/gstreamer/gstlibcameraallocator.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraallocator.h - GStreamer Custom Allocator
+ * GStreamer Custom Allocator
*/
#pragma once
diff --git a/src/gstreamer/gstlibcamerapad.cpp b/src/gstreamer/gstlibcamerapad.cpp
index 9e710a47..3bc2bc87 100644
--- a/src/gstreamer/gstlibcamerapad.cpp
+++ b/src/gstreamer/gstlibcamerapad.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapad.cpp - GStreamer Capture Pad
+ * GStreamer Capture Pad
*/
#include "gstlibcamerapad.h"
@@ -18,6 +18,8 @@ struct _GstLibcameraPad {
GstPad parent;
StreamRole role;
GstLibcameraPool *pool;
+ GstBufferPool *video_pool;
+ GstVideoInfo info;
GstClockTime latency;
};
@@ -153,6 +155,35 @@ gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool)
self->pool = pool;
}
+GstBufferPool *
+gst_libcamera_pad_get_video_pool(GstPad *pad)
+{
+ auto *self = GST_LIBCAMERA_PAD(pad);
+ return self->video_pool;
+}
+
+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool)
+{
+ auto *self = GST_LIBCAMERA_PAD(pad);
+
+ if (self->video_pool)
+ g_object_unref(self->video_pool);
+ self->video_pool = video_pool;
+}
+
+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad)
+{
+ auto *self = GST_LIBCAMERA_PAD(pad);
+ return self->info;
+}
+
+void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info)
+{
+ auto *self = GST_LIBCAMERA_PAD(pad);
+
+ self->info = *info;
+}
+
Stream *
gst_libcamera_pad_get_stream(GstPad *pad)
{
diff --git a/src/gstreamer/gstlibcamerapad.h b/src/gstreamer/gstlibcamerapad.h
index 103ee57a..f98b8a7f 100644
--- a/src/gstreamer/gstlibcamerapad.h
+++ b/src/gstreamer/gstlibcamerapad.h
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapad.h - GStreamer Capture Element
+ * GStreamer Capture Element
*/
#pragma once
@@ -23,6 +23,14 @@ GstLibcameraPool *gst_libcamera_pad_get_pool(GstPad *pad);
void gst_libcamera_pad_set_pool(GstPad *pad, GstLibcameraPool *pool);
+GstBufferPool *gst_libcamera_pad_get_video_pool(GstPad *pad);
+
+void gst_libcamera_pad_set_video_pool(GstPad *pad, GstBufferPool *video_pool);
+
+GstVideoInfo gst_libcamera_pad_get_video_info(GstPad *pad);
+
+void gst_libcamera_pad_set_video_info(GstPad *pad, const GstVideoInfo *info);
+
libcamera::Stream *gst_libcamera_pad_get_stream(GstPad *pad);
void gst_libcamera_pad_set_latency(GstPad *pad, GstClockTime latency);
diff --git a/src/gstreamer/gstlibcamerapool.cpp b/src/gstreamer/gstlibcamerapool.cpp
index 0c2be43c..8278144f 100644
--- a/src/gstreamer/gstlibcamerapool.cpp
+++ b/src/gstreamer/gstlibcamerapool.cpp
@@ -3,11 +3,13 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapool.cpp - GStreamer Buffer Pool
+ * GStreamer Buffer Pool
*/
#include "gstlibcamerapool.h"
+#include <deque>
+
#include <libcamera/stream.h>
#include "gstlibcamera-utils.h"
@@ -24,24 +26,41 @@ static guint signals[N_SIGNALS];
struct _GstLibcameraPool {
GstBufferPool parent;
- GstAtomicQueue *queue;
+ std::deque<GstBuffer *> *queue;
GstLibcameraAllocator *allocator;
Stream *stream;
};
G_DEFINE_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_TYPE_BUFFER_POOL)
+static GstBuffer *
+gst_libcamera_pool_pop_buffer(GstLibcameraPool *self)
+{
+ GLibLocker lock(GST_OBJECT(self));
+ GstBuffer *buf;
+
+ if (self->queue->empty())
+ return nullptr;
+
+ buf = self->queue->front();
+ self->queue->pop_front();
+
+ return buf;
+}
+
static GstFlowReturn
gst_libcamera_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer,
[[maybe_unused]] GstBufferPoolAcquireParams *params)
{
GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool);
- GstBuffer *buf = GST_BUFFER(gst_atomic_queue_pop(self->queue));
+ GstBuffer *buf = gst_libcamera_pool_pop_buffer(self);
+
if (!buf)
return GST_FLOW_ERROR;
if (!gst_libcamera_allocator_prepare_buffer(self->allocator, self->stream, buf)) {
- gst_atomic_queue_push(self->queue, buf);
+ GLibLocker lock(GST_OBJECT(self));
+ self->queue->push_back(buf);
return GST_FLOW_ERROR;
}
@@ -64,9 +83,13 @@ static void
gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
{
GstLibcameraPool *self = GST_LIBCAMERA_POOL(pool);
- bool do_notify = gst_atomic_queue_length(self->queue) == 0;
+ bool do_notify;
- gst_atomic_queue_push(self->queue, buffer);
+ {
+ GLibLocker lock(GST_OBJECT(self));
+ do_notify = self->queue->empty();
+ self->queue->push_back(buffer);
+ }
if (do_notify)
g_signal_emit(self, signals[SIGNAL_BUFFER_NOTIFY], 0);
@@ -75,7 +98,7 @@ gst_libcamera_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
static void
gst_libcamera_pool_init(GstLibcameraPool *self)
{
- self->queue = gst_atomic_queue_new(4);
+ self->queue = new std::deque<GstBuffer *>();
}
static void
@@ -84,10 +107,10 @@ gst_libcamera_pool_finalize(GObject *object)
GstLibcameraPool *self = GST_LIBCAMERA_POOL(object);
GstBuffer *buf;
- while ((buf = GST_BUFFER(gst_atomic_queue_pop(self->queue))))
+ while ((buf = gst_libcamera_pool_pop_buffer(self)))
gst_buffer_unref(buf);
- gst_atomic_queue_unref(self->queue);
+ delete self->queue;
g_object_unref(self->allocator);
G_OBJECT_CLASS(gst_libcamera_pool_parent_class)->finalize(object);
@@ -111,8 +134,20 @@ gst_libcamera_pool_class_init(GstLibcameraPoolClass *klass)
G_TYPE_NONE, 0);
}
+static void
+gst_libcamera_buffer_add_video_meta(GstBuffer *buffer, GstVideoInfo *info)
+{
+ GstVideoMeta *vmeta;
+ vmeta = gst_buffer_add_video_meta_full(buffer, GST_VIDEO_FRAME_FLAG_NONE,
+ GST_VIDEO_INFO_FORMAT(info), GST_VIDEO_INFO_WIDTH(info),
+ GST_VIDEO_INFO_HEIGHT(info), GST_VIDEO_INFO_N_PLANES(info),
+ info->offset, info->stride);
+ GST_META_FLAGS(vmeta) = (GstMetaFlags)(GST_META_FLAGS(vmeta) | GST_META_FLAG_POOLED);
+}
+
GstLibcameraPool *
-gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
+gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream,
+ GstVideoInfo *info)
{
auto *pool = GST_LIBCAMERA_POOL(g_object_new(GST_TYPE_LIBCAMERA_POOL, nullptr));
@@ -122,7 +157,8 @@ gst_libcamera_pool_new(GstLibcameraAllocator *allocator, Stream *stream)
gsize pool_size = gst_libcamera_allocator_get_pool_size(allocator, stream);
for (gsize i = 0; i < pool_size; i++) {
GstBuffer *buffer = gst_buffer_new();
- gst_atomic_queue_push(pool->queue, buffer);
+ gst_libcamera_buffer_add_video_meta(buffer, info);
+ pool->queue->push_back(buffer);
}
return pool;
diff --git a/src/gstreamer/gstlibcamerapool.h b/src/gstreamer/gstlibcamerapool.h
index ce3bf60b..02ee4dd4 100644
--- a/src/gstreamer/gstlibcamerapool.h
+++ b/src/gstreamer/gstlibcamerapool.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerapool.h - GStreamer Buffer Pool
+ * GStreamer Buffer Pool
*
* This is a partial implementation of GstBufferPool intended for internal use
* only. This pool cannot be configured or activated.
@@ -14,6 +14,7 @@
#include "gstlibcameraallocator.h"
#include <gst/gst.h>
+#include <gst/video/video.h>
#include <libcamera/stream.h>
@@ -21,7 +22,7 @@
G_DECLARE_FINAL_TYPE(GstLibcameraPool, gst_libcamera_pool, GST_LIBCAMERA, POOL, GstBufferPool)
GstLibcameraPool *gst_libcamera_pool_new(GstLibcameraAllocator *allocator,
- libcamera::Stream *stream);
+ libcamera::Stream *stream, GstVideoInfo *info);
libcamera::Stream *gst_libcamera_pool_get_stream(GstLibcameraPool *self);
diff --git a/src/gstreamer/gstlibcameraprovider.cpp b/src/gstreamer/gstlibcameraprovider.cpp
index ce3e0a08..5da96ea3 100644
--- a/src/gstreamer/gstlibcameraprovider.cpp
+++ b/src/gstreamer/gstlibcameraprovider.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraprovider.c - GStreamer Device Provider
+ * GStreamer Device Provider
*/
#include <array>
@@ -33,7 +33,6 @@ GST_DEBUG_CATEGORY_STATIC(provider_debug);
enum {
PROP_DEVICE_NAME = 1,
- PROP_AUTO_FOCUS_MODE = 2,
};
#define GST_TYPE_LIBCAMERA_DEVICE gst_libcamera_device_get_type()
@@ -43,7 +42,6 @@ G_DECLARE_FINAL_TYPE(GstLibcameraDevice, gst_libcamera_device,
struct _GstLibcameraDevice {
GstDevice parent;
gchar *name;
- controls::AfModeEnum auto_focus_mode = controls::AfModeManual;
};
G_DEFINE_TYPE(GstLibcameraDevice, gst_libcamera_device, GST_TYPE_DEVICE)
@@ -60,7 +58,6 @@ gst_libcamera_device_create_element(GstDevice *device, const gchar *name)
g_assert(source);
g_object_set(source, "camera-name", GST_LIBCAMERA_DEVICE(device)->name, nullptr);
- g_object_set(source, "auto-focus-mode", GST_LIBCAMERA_DEVICE(device)->auto_focus_mode, nullptr);
return source;
}
@@ -87,9 +84,6 @@ gst_libcamera_device_set_property(GObject *object, guint prop_id,
case PROP_DEVICE_NAME:
device->name = g_value_dup_string(value);
break;
- case PROP_AUTO_FOCUS_MODE:
- device->auto_focus_mode = static_cast<controls::AfModeEnum>(g_value_get_enum(value));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -129,15 +123,6 @@ gst_libcamera_device_class_init(GstLibcameraDeviceClass *klass)
(GParamFlags)(G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(object_class, PROP_DEVICE_NAME, pspec);
-
- pspec = g_param_spec_enum("auto-focus-mode",
- "Set auto-focus mode",
- "Available options: AfModeManual, "
- "AfModeAuto or AfModeContinuous.",
- gst_libcamera_auto_focus_get_type(),
- static_cast<gint>(controls::AfModeManual),
- G_PARAM_WRITABLE);
- g_object_class_install_property(object_class, PROP_AUTO_FOCUS_MODE, pspec);
}
static GstDevice *
diff --git a/src/gstreamer/gstlibcameraprovider.h b/src/gstreamer/gstlibcameraprovider.h
index aaceabf5..19708b9d 100644
--- a/src/gstreamer/gstlibcameraprovider.h
+++ b/src/gstreamer/gstlibcameraprovider.h
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcameraprovider.h - GStreamer Device Provider
+ * GStreamer Device Provider
*/
#pragma once
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 63d99571..b34f0897 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -3,16 +3,14 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerasrc.cpp - GStreamer Capture Element
+ * GStreamer Capture Element
*/
/**
* \todo The following is a list of items that needs implementation in the GStreamer plugin
* - Implement GstElement::send_event
- * + Allowing application to send EOS
* + Allowing application to use FLUSH/FLUSH_STOP
* + Prevent the main thread from accessing streaming thread
- * - Implement renegotiation (even if slow)
* - Implement GstElement::request-new-pad (multi stream)
* + Evaluate if a single streaming thread is fine
* - Add application driven request (snapshot)
@@ -29,6 +27,7 @@
#include "gstlibcamerasrc.h"
+#include <atomic>
#include <queue>
#include <vector>
@@ -38,10 +37,11 @@
#include <gst/base/base.h>
+#include "gstlibcamera-controls.h"
+#include "gstlibcamera-utils.h"
#include "gstlibcameraallocator.h"
#include "gstlibcamerapad.h"
#include "gstlibcamerapool.h"
-#include "gstlibcamera-utils.h"
using namespace libcamera;
@@ -129,10 +129,12 @@ struct GstLibcameraSrcState {
ControlList initControls_;
guint group_id_;
+ GstCameraControls controls_;
int queueRequest();
void requestCompleted(Request *request);
int processRequest();
+ void clearRequests();
};
struct _GstLibcameraSrc {
@@ -142,7 +144,8 @@ struct _GstLibcameraSrc {
GstTask *task;
gchar *camera_name;
- controls::AfModeEnum auto_focus_mode = controls::AfModeManual;
+
+ std::atomic<GstEvent *> pending_eos;
GstLibcameraSrcState *state;
GstLibcameraAllocator *allocator;
@@ -152,10 +155,15 @@ struct _GstLibcameraSrc {
enum {
PROP_0,
PROP_CAMERA_NAME,
- PROP_AUTO_FOCUS_MODE,
+ PROP_LAST
};
+static void gst_libcamera_src_child_proxy_init(gpointer g_iface,
+ gpointer iface_data);
+
G_DEFINE_TYPE_WITH_CODE(GstLibcameraSrc, gst_libcamera_src, GST_TYPE_ELEMENT,
+ G_IMPLEMENT_INTERFACE(GST_TYPE_CHILD_PROXY,
+ gst_libcamera_src_child_proxy_init)
GST_DEBUG_CATEGORY_INIT(source_debug, "libcamerasrc", 0,
"libcamera Source"))
@@ -178,6 +186,9 @@ int GstLibcameraSrcState::queueRequest()
if (!request)
return -ENOMEM;
+ /* Apply controls */
+ controls_.applyControls(request);
+
std::unique_ptr<RequestWrap> wrap =
std::make_unique<RequestWrap>(std::move(request));
@@ -221,6 +232,9 @@ GstLibcameraSrcState::requestCompleted(Request *request)
{
GLibLocker locker(&lock_);
+
+ controls_.readMetadata(request);
+
wrap = std::move(queuedRequests_.front());
queuedRequests_.pop();
}
@@ -254,6 +268,55 @@ GstLibcameraSrcState::requestCompleted(Request *request)
gst_task_resume(src_->task);
}
+static void
+gst_libcamera_extrapolate_info(GstVideoInfo *info, guint32 stride)
+{
+ guint i, estride;
+ gsize offset = 0;
+
+ /* This should be updated if tiled formats get added in the future. */
+ for (i = 0; i < GST_VIDEO_INFO_N_PLANES(info); i++) {
+ estride = gst_video_format_info_extrapolate_stride(info->finfo, i, stride);
+ info->stride[i] = estride;
+ info->offset[i] = offset;
+ offset += estride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info->finfo, i,
+ GST_VIDEO_INFO_HEIGHT(info));
+ }
+}
+
+static GstFlowReturn
+gst_libcamera_video_frame_copy(GstBuffer *src, GstBuffer *dest, const GstVideoInfo *dest_info, guint32 stride)
+{
+ GstVideoInfo src_info = *dest_info;
+ GstVideoFrame src_frame, dest_frame;
+
+ gst_libcamera_extrapolate_info(&src_info, stride);
+ src_info.size = gst_buffer_get_size(src);
+
+ if (!gst_video_frame_map(&src_frame, &src_info, src, GST_MAP_READ)) {
+ GST_ERROR("Could not map src buffer");
+ return GST_FLOW_ERROR;
+ }
+
+ if (!gst_video_frame_map(&dest_frame, const_cast<GstVideoInfo *>(dest_info), dest, GST_MAP_WRITE)) {
+ GST_ERROR("Could not map dest buffer");
+ gst_video_frame_unmap(&src_frame);
+ return GST_FLOW_ERROR;
+ }
+
+ if (!gst_video_frame_copy(&dest_frame, &src_frame)) {
+ GST_ERROR("Could not copy frame");
+ gst_video_frame_unmap(&src_frame);
+ gst_video_frame_unmap(&dest_frame);
+ return GST_FLOW_ERROR;
+ }
+
+ gst_video_frame_unmap(&src_frame);
+ gst_video_frame_unmap(&dest_frame);
+
+ return GST_FLOW_OK;
+}
+
/* Must be called with stream_lock held. */
int GstLibcameraSrcState::processRequest()
{
@@ -278,11 +341,41 @@ int GstLibcameraSrcState::processRequest()
GstFlowReturn ret = GST_FLOW_OK;
gst_flow_combiner_reset(src_->flow_combiner);
- for (GstPad *srcpad : srcpads_) {
+ for (gsize i = 0; i < srcpads_.size(); i++) {
+ GstPad *srcpad = srcpads_[i];
Stream *stream = gst_libcamera_pad_get_stream(srcpad);
GstBuffer *buffer = wrap->detachBuffer(stream);
FrameBuffer *fb = gst_libcamera_buffer_get_frame_buffer(buffer);
+ const StreamConfiguration &stream_cfg = config_->at(i);
+ GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(srcpad);
+
+ if (video_pool) {
+ /* Only set video pool when a copy is needed. */
+ GstBuffer *copy = NULL;
+ const GstVideoInfo info = gst_libcamera_pad_get_video_info(srcpad);
+
+ ret = gst_buffer_pool_acquire_buffer(video_pool, &copy, NULL);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref(buffer);
+ GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+ ("Failed to acquire buffer"),
+ ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+ return -EPIPE;
+ }
+
+ ret = gst_libcamera_video_frame_copy(buffer, copy, &info, stream_cfg.stride);
+ gst_buffer_unref(buffer);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref(copy);
+ GST_ELEMENT_ERROR(src_, RESOURCE, SETTINGS,
+ ("Failed to copy buffer"),
+ ("GstLibcameraSrcState::processRequest() failed: %s", g_strerror(-ret)));
+ return -EPIPE;
+ }
+
+ buffer = copy;
+ }
if (GST_CLOCK_TIME_IS_VALID(wrap->pts_)) {
GST_BUFFER_PTS(buffer) = wrap->pts_;
@@ -299,23 +392,57 @@ int GstLibcameraSrcState::processRequest()
srcpad, ret);
}
- if (ret != GST_FLOW_OK) {
- if (ret == GST_FLOW_EOS) {
- g_autoptr(GstEvent) eos = gst_event_new_eos();
- guint32 seqnum = gst_util_seqnum_next();
- gst_event_set_seqnum(eos, seqnum);
- for (GstPad *srcpad : srcpads_)
- gst_pad_push_event(srcpad, gst_event_ref(eos));
- } else if (ret != GST_FLOW_FLUSHING) {
- GST_ELEMENT_FLOW_ERROR(src_, ret);
+ switch (ret) {
+ case GST_FLOW_OK:
+ break;
+
+ case GST_FLOW_NOT_NEGOTIATED: {
+ bool reconfigure = false;
+ for (GstPad *srcpad : srcpads_) {
+ if (gst_pad_needs_reconfigure(srcpad)) {
+ reconfigure = true;
+ break;
+ }
}
- return -EPIPE;
+ /* If no pads need a reconfiguration something went wrong. */
+ if (!reconfigure)
+ err = -EPIPE;
+
+ break;
+ }
+
+ case GST_FLOW_EOS: {
+ g_autoptr(GstEvent) eos = gst_event_new_eos();
+ guint32 seqnum = gst_util_seqnum_next();
+ gst_event_set_seqnum(eos, seqnum);
+ for (GstPad *srcpad : srcpads_)
+ gst_pad_push_event(srcpad, gst_event_ref(eos));
+
+ err = -EPIPE;
+ break;
+ }
+
+ case GST_FLOW_FLUSHING:
+ err = -EPIPE;
+ break;
+
+ default:
+ GST_ELEMENT_FLOW_ERROR(src_, ret);
+
+ err = -EPIPE;
+ break;
}
return err;
}
+void GstLibcameraSrcState::clearRequests()
+{
+ GLibLocker locker(&lock_);
+ completedRequests_ = {};
+}
+
static bool
gst_libcamera_src_open(GstLibcameraSrc *self)
{
@@ -341,21 +468,22 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
}
if (camera_name) {
- cam = cm->get(self->camera_name);
+ cam = cm->get(camera_name);
if (!cam) {
GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND,
- ("Could not find a camera named '%s'.", self->camera_name),
+ ("Could not find a camera named '%s'.", camera_name),
("libcamera::CameraMananger::get() returned nullptr"));
return false;
}
} else {
- if (cm->cameras().empty()) {
+ auto cameras = cm->cameras();
+ if (cameras.empty()) {
GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND,
("Could not find any supported camera on this system."),
("libcamera::CameraMananger::cameras() is empty"));
return false;
}
- cam = cm->cameras()[0];
+ cam = cameras[0];
}
GST_INFO_OBJECT(self, "Using camera '%s'", cam->id().c_str());
@@ -368,6 +496,8 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
return false;
}
+ self->state->controls_.setCamera(cam);
+
cam->requestCompleted.connect(self->state, &GstLibcameraSrcState::requestCompleted);
/* No need to lock here, we didn't start our threads yet. */
@@ -377,6 +507,149 @@ gst_libcamera_src_open(GstLibcameraSrc *self)
return true;
}
+/* Must be called with stream_lock held. */
+static bool
+gst_libcamera_src_negotiate(GstLibcameraSrc *self)
+{
+ GstLibcameraSrcState *state = self->state;
+ std::vector<GstVideoTransferFunction> transfer(state->srcpads_.size(),
+ GST_VIDEO_TRANSFER_UNKNOWN);
+
+ g_autoptr(GstStructure) element_caps = gst_structure_new_empty("caps");
+
+ for (gsize i = 0; i < state->srcpads_.size(); i++) {
+ GstPad *srcpad = state->srcpads_[i];
+ StreamConfiguration &stream_cfg = state->config_->at(i);
+
+ /* Retrieve the supported caps. */
+ g_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());
+ g_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter);
+ if (gst_caps_is_empty(caps))
+ return false;
+
+ /* Fixate caps and configure the stream. */
+ caps = gst_caps_make_writable(caps);
+ gst_libcamera_configure_stream_from_caps(stream_cfg, caps, &transfer[i]);
+ gst_libcamera_get_framerate_from_caps(caps, element_caps);
+ }
+
+ /* Validate the configuration. */
+ if (state->config_->validate() == CameraConfiguration::Invalid)
+ return false;
+
+ int ret = state->cam_->configure(state->config_.get());
+ if (ret) {
+ GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
+ ("Failed to configure camera: %s", g_strerror(-ret)),
+ ("Camera::configure() failed with error code %i", ret));
+ return false;
+ }
+
+ /* Check frame duration bounds within controls::FrameDurationLimits */
+ gst_libcamera_clamp_and_set_frameduration(state->initControls_,
+ state->cam_->controls(), element_caps);
+
+ /*
+ * Regardless if it has been modified, create clean caps and push the
+ * caps event. Downstream will decide if the caps are acceptable.
+ */
+ for (gsize i = 0; i < state->srcpads_.size(); i++) {
+ GstPad *srcpad = state->srcpads_[i];
+ const StreamConfiguration &stream_cfg = state->config_->at(i);
+
+ g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
+ gst_libcamera_framerate_to_caps(caps, element_caps);
+
+ if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps)))
+ return false;
+ }
+
+ if (self->allocator)
+ g_clear_object(&self->allocator);
+
+ self->allocator = gst_libcamera_allocator_new(state->cam_, state->config_.get());
+ if (!self->allocator) {
+ GST_ELEMENT_ERROR(self, RESOURCE, NO_SPACE_LEFT,
+ ("Failed to allocate memory"),
+ ("gst_libcamera_allocator_new() failed."));
+ return false;
+ }
+
+ for (gsize i = 0; i < state->srcpads_.size(); i++) {
+ GstPad *srcpad = state->srcpads_[i];
+ const StreamConfiguration &stream_cfg = state->config_->at(i);
+ GstBufferPool *video_pool = NULL;
+ GstVideoInfo info;
+
+ g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg, transfer[i]);
+
+ gst_video_info_from_caps(&info, caps);
+ gst_libcamera_pad_set_video_info(srcpad, &info);
+
+ /* Stride mismatch between camera stride and that calculated by video-info. */
+ if (static_cast<unsigned int>(info.stride[0]) != stream_cfg.stride &&
+ GST_VIDEO_INFO_FORMAT(&info) != GST_VIDEO_FORMAT_ENCODED) {
+ GstQuery *query = NULL;
+ const gboolean need_pool = true;
+ gboolean has_video_meta = false;
+
+ gst_libcamera_extrapolate_info(&info, stream_cfg.stride);
+
+ query = gst_query_new_allocation(caps, need_pool);
+ if (!gst_pad_peer_query(srcpad, query))
+ GST_DEBUG_OBJECT(self, "Didn't get downstream ALLOCATION hints");
+ else
+ has_video_meta = gst_query_find_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
+
+ if (!has_video_meta) {
+ GstBufferPool *pool = NULL;
+
+ if (gst_query_get_n_allocation_pools(query) > 0)
+ gst_query_parse_nth_allocation_pool(query, 0, &pool, NULL, NULL, NULL);
+
+ if (pool)
+ video_pool = pool;
+ else {
+ GstStructure *config;
+ guint min_buffers = 3;
+ video_pool = gst_video_buffer_pool_new();
+
+ config = gst_buffer_pool_get_config(video_pool);
+ gst_buffer_pool_config_set_params(config, caps, info.size, min_buffers, 0);
+
+ GST_DEBUG_OBJECT(self, "Own pool config is %" GST_PTR_FORMAT, config);
+
+ gst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(video_pool), config);
+ }
+
+ GST_WARNING_OBJECT(self, "Downstream doesn't support video meta, need to copy frame.");
+
+ if (!gst_buffer_pool_set_active(video_pool, true)) {
+ gst_caps_unref(caps);
+ GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
+ ("Failed to active buffer pool"),
+ ("gst_libcamera_src_negotiate() failed."));
+ return false;
+ }
+ }
+ gst_query_unref(query);
+ }
+
+ GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
+ stream_cfg.stream(), &info);
+ g_signal_connect_swapped(pool, "buffer-notify",
+ G_CALLBACK(gst_task_resume), self->task);
+
+ gst_libcamera_pad_set_pool(srcpad, pool);
+ gst_libcamera_pad_set_video_pool(srcpad, video_pool);
+
+ /* Clear all reconfigure flags. */
+ gst_pad_check_reconfigure(srcpad);
+ }
+
+ return true;
+}
+
static void
gst_libcamera_src_task_run(gpointer user_data)
{
@@ -397,6 +670,39 @@ gst_libcamera_src_task_run(gpointer user_data)
bool doResume = false;
+ g_autoptr(GstEvent) event = self->pending_eos.exchange(nullptr);
+ if (event) {
+ for (GstPad *srcpad : state->srcpads_)
+ gst_pad_push_event(srcpad, gst_event_ref(event));
+
+ return;
+ }
+
+ /* Check if a srcpad requested a renegotiation. */
+ bool reconfigure = false;
+ for (GstPad *srcpad : state->srcpads_) {
+ if (gst_pad_check_reconfigure(srcpad)) {
+ /* Check if the caps even need changing. */
+ g_autoptr(GstCaps) caps = gst_pad_get_current_caps(srcpad);
+ if (!gst_pad_peer_query_accept_caps(srcpad, caps)) {
+ reconfigure = true;
+ break;
+ }
+ }
+ }
+
+ if (reconfigure) {
+ state->cam_->stop();
+ state->clearRequests();
+
+ if (!gst_libcamera_src_negotiate(self)) {
+ GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED);
+ gst_task_stop(self->task);
+ }
+
+ state->cam_->start(&state->initControls_);
+ }
+
/*
* Create and queue one request. If no buffers are available the
* function returns -ENOBUFS, which we ignore here as that's not a
@@ -458,11 +764,8 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);
GLibRecLocker lock(&self->stream_lock);
GstLibcameraSrcState *state = self->state;
- GstFlowReturn flow_ret = GST_FLOW_OK;
gint ret;
- g_autoptr(GstStructure) element_caps = gst_structure_new_empty("caps");
-
GST_DEBUG_OBJECT(self, "Streaming thread has started");
gint stream_id_num = 0;
@@ -490,62 +793,16 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
}
g_assert(state->config_->size() == state->srcpads_.size());
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- GstPad *srcpad = state->srcpads_[i];
- StreamConfiguration &stream_cfg = state->config_->at(i);
-
- /* Retrieve the supported caps. */
- g_autoptr(GstCaps) filter = gst_libcamera_stream_formats_to_caps(stream_cfg.formats());
- g_autoptr(GstCaps) caps = gst_pad_peer_query_caps(srcpad, filter);
- if (gst_caps_is_empty(caps)) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- break;
- }
-
- /* Fixate caps and configure the stream. */
- caps = gst_caps_make_writable(caps);
- gst_libcamera_configure_stream_from_caps(stream_cfg, caps);
- gst_libcamera_get_framerate_from_caps(caps, element_caps);
- }
-
- if (flow_ret != GST_FLOW_OK)
- goto done;
-
- /* Validate the configuration. */
- if (state->config_->validate() == CameraConfiguration::Invalid) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- goto done;
- }
-
- ret = state->cam_->configure(state->config_.get());
- if (ret) {
- GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
- ("Failed to configure camera: %s", g_strerror(-ret)),
- ("Camera::configure() failed with error code %i", ret));
+ if (!gst_libcamera_src_negotiate(self)) {
+ state->initControls_.clear();
+ GST_ELEMENT_FLOW_ERROR(self, GST_FLOW_NOT_NEGOTIATED);
gst_task_stop(task);
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- goto done;
+ return;
}
- /* Check frame duration bounds within controls::FrameDurationLimits */
- gst_libcamera_clamp_and_set_frameduration(state->initControls_,
- state->cam_->controls(), element_caps);
-
- /*
- * Regardless if it has been modified, create clean caps and push the
- * caps event. Downstream will decide if the caps are acceptable.
- */
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- GstPad *srcpad = state->srcpads_[i];
- const StreamConfiguration &stream_cfg = state->config_->at(i);
-
- g_autoptr(GstCaps) caps = gst_libcamera_stream_configuration_to_caps(stream_cfg);
- gst_libcamera_framerate_to_caps(caps, element_caps);
-
- if (!gst_pad_push_event(srcpad, gst_event_new_caps(caps))) {
- flow_ret = GST_FLOW_NOT_NEGOTIATED;
- break;
- }
+ self->flow_combiner = gst_flow_combiner_new();
+ for (GstPad *srcpad : state->srcpads_) {
+ gst_flow_combiner_add_pad(self->flow_combiner, srcpad);
/* Send an open segment event with time format. */
GstSegment segment;
@@ -553,40 +810,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
gst_pad_push_event(srcpad, gst_event_new_segment(&segment));
}
- self->allocator = gst_libcamera_allocator_new(state->cam_, state->config_.get());
- if (!self->allocator) {
- GST_ELEMENT_ERROR(self, RESOURCE, NO_SPACE_LEFT,
- ("Failed to allocate memory"),
- ("gst_libcamera_allocator_new() failed."));
- gst_task_stop(task);
- return;
- }
-
- self->flow_combiner = gst_flow_combiner_new();
- for (gsize i = 0; i < state->srcpads_.size(); i++) {
- GstPad *srcpad = state->srcpads_[i];
- const StreamConfiguration &stream_cfg = state->config_->at(i);
- GstLibcameraPool *pool = gst_libcamera_pool_new(self->allocator,
- stream_cfg.stream());
- g_signal_connect_swapped(pool, "buffer-notify",
- G_CALLBACK(gst_task_resume), task);
-
- gst_libcamera_pad_set_pool(srcpad, pool);
- gst_flow_combiner_add_pad(self->flow_combiner, srcpad);
- }
-
- if (self->auto_focus_mode != controls::AfModeManual) {
- const ControlInfoMap &infoMap = state->cam_->controls();
- if (infoMap.find(&controls::AfMode) != infoMap.end()) {
- state->initControls_.set(controls::AfMode, self->auto_focus_mode);
- } else {
- GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
- ("Failed to enable auto focus"),
- ("AfMode not supported by this camera, "
- "please retry with 'auto-focus-mode=AfModeManual'"));
- }
- }
-
ret = state->cam_->start(&state->initControls_);
if (ret) {
GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS,
@@ -595,17 +818,6 @@ gst_libcamera_src_task_enter(GstTask *task, [[maybe_unused]] GThread *thread,
gst_task_stop(task);
return;
}
-
-done:
- state->initControls_.clear();
- switch (flow_ret) {
- case GST_FLOW_NOT_NEGOTIATED:
- GST_ELEMENT_FLOW_ERROR(self, flow_ret);
- gst_task_stop(task);
- break;
- default:
- break;
- }
}
static void
@@ -619,11 +831,7 @@ gst_libcamera_src_task_leave([[maybe_unused]] GstTask *task,
GST_DEBUG_OBJECT(self, "Streaming thread is about to stop");
state->cam_->stop();
-
- {
- GLibLocker locker(&state->lock_);
- state->completedRequests_ = {};
- }
+ state->clearRequests();
{
GLibRecLocker locker(&self->stream_lock);
@@ -663,17 +871,16 @@ gst_libcamera_src_set_property(GObject *object, guint prop_id,
{
GLibLocker lock(GST_OBJECT(object));
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object);
+ GstLibcameraSrcState *state = self->state;
switch (prop_id) {
case PROP_CAMERA_NAME:
g_free(self->camera_name);
self->camera_name = g_value_dup_string(value);
break;
- case PROP_AUTO_FOCUS_MODE:
- self->auto_focus_mode = static_cast<controls::AfModeEnum>(g_value_get_enum(value));
- break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ if (!state->controls_.setProperty(prop_id - PROP_LAST, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -684,16 +891,15 @@ gst_libcamera_src_get_property(GObject *object, guint prop_id, GValue *value,
{
GLibLocker lock(GST_OBJECT(object));
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(object);
+ GstLibcameraSrcState *state = self->state;
switch (prop_id) {
case PROP_CAMERA_NAME:
g_value_set_string(value, self->camera_name);
break;
- case PROP_AUTO_FOCUS_MODE:
- g_value_set_enum(value, static_cast<gint>(self->auto_focus_mode));
- break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ if (!state->controls_.getProperty(prop_id - PROP_LAST, value, pspec))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
@@ -747,6 +953,27 @@ gst_libcamera_src_change_state(GstElement *element, GstStateChange transition)
return ret;
}
+static gboolean
+gst_libcamera_src_send_event(GstElement *element, GstEvent *event)
+{
+ GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element);
+ gboolean ret = FALSE;
+
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_EOS: {
+ GstEvent *oldEvent = self->pending_eos.exchange(event);
+ gst_clear_event(&oldEvent);
+ ret = TRUE;
+ break;
+ }
+ default:
+ gst_event_unref(event);
+ break;
+ }
+
+ return ret;
+}
+
static void
gst_libcamera_src_finalize(GObject *object)
{
@@ -776,8 +1003,12 @@ gst_libcamera_src_init(GstLibcameraSrc *self)
g_mutex_init(&state->lock_);
- state->srcpads_.push_back(gst_pad_new_from_template(templ, "src"));
- gst_element_add_pad(GST_ELEMENT(self), state->srcpads_.back());
+ GstPad *pad = gst_pad_new_from_template(templ, "src");
+ state->srcpads_.push_back(pad);
+ gst_element_add_pad(GST_ELEMENT(self), pad);
+ gst_child_proxy_child_added(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
+
+ GST_OBJECT_FLAG_SET(self, GST_ELEMENT_FLAG_SOURCE);
/* C-style friend. */
state->src_ = self;
@@ -806,6 +1037,8 @@ gst_libcamera_src_request_new_pad(GstElement *element, GstPadTemplate *templ,
return NULL;
}
+ gst_child_proxy_child_added(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
+
return reinterpret_cast<GstPad *>(g_steal_pointer(&pad));
}
@@ -814,6 +1047,8 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)
{
GstLibcameraSrc *self = GST_LIBCAMERA_SRC(element);
+ gst_child_proxy_child_removed(GST_CHILD_PROXY(self), G_OBJECT(pad), GST_OBJECT_NAME(pad));
+
GST_DEBUG_OBJECT(self, "Pad %" GST_PTR_FORMAT " being released", pad);
{
@@ -823,6 +1058,12 @@ gst_libcamera_src_release_pad(GstElement *element, GstPad *pad)
auto end_iterator = pads.end();
auto pad_iterator = std::find(begin_iterator, end_iterator, pad);
+ GstBufferPool *video_pool = gst_libcamera_pad_get_video_pool(pad);
+ if (video_pool) {
+ gst_buffer_pool_set_active(video_pool, false);
+ gst_object_unref(video_pool);
+ }
+
if (pad_iterator != end_iterator) {
g_object_unref(*pad_iterator);
pads.erase(pad_iterator);
@@ -844,11 +1085,12 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
element_class->request_new_pad = gst_libcamera_src_request_new_pad;
element_class->release_pad = gst_libcamera_src_release_pad;
element_class->change_state = gst_libcamera_src_change_state;
+ element_class->send_event = gst_libcamera_src_send_event;
gst_element_class_set_metadata(element_class,
"libcamera Source", "Source/Video",
"Linux Camera source using libcamera",
- "Nicolas Dufresne <nicolas.dufresne@collabora.com");
+ "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
gst_element_class_add_static_pad_template_with_gtype(element_class,
&src_template,
GST_TYPE_LIBCAMERA_PAD);
@@ -864,12 +1106,35 @@ gst_libcamera_src_class_init(GstLibcameraSrcClass *klass)
| G_PARAM_STATIC_STRINGS));
g_object_class_install_property(object_class, PROP_CAMERA_NAME, spec);
- spec = g_param_spec_enum("auto-focus-mode",
- "Set auto-focus mode",
- "Available options: AfModeManual, "
- "AfModeAuto or AfModeContinuous.",
- gst_libcamera_auto_focus_get_type(),
- static_cast<gint>(controls::AfModeManual),
- G_PARAM_WRITABLE);
- g_object_class_install_property(object_class, PROP_AUTO_FOCUS_MODE, spec);
+ GstCameraControls::installProperties(object_class, PROP_LAST);
+}
+
+/* GstChildProxy implementation */
+static GObject *
+gst_libcamera_src_child_proxy_get_child_by_index(GstChildProxy *child_proxy,
+ guint index)
+{
+ GLibLocker lock(GST_OBJECT(child_proxy));
+ GObject *obj = nullptr;
+
+ obj = reinterpret_cast<GObject *>(g_list_nth_data(GST_ELEMENT(child_proxy)->srcpads, index));
+ if (obj)
+ gst_object_ref(obj);
+
+ return obj;
+}
+
+static guint
+gst_libcamera_src_child_proxy_get_children_count(GstChildProxy *child_proxy)
+{
+ GLibLocker lock(GST_OBJECT(child_proxy));
+ return GST_ELEMENT_CAST(child_proxy)->numsrcpads;
+}
+
+static void
+gst_libcamera_src_child_proxy_init(gpointer g_iface, [[maybe_unused]] gpointer iface_data)
+{
+ GstChildProxyInterface *iface = reinterpret_cast<GstChildProxyInterface *>(g_iface);
+ iface->get_child_by_index = gst_libcamera_src_child_proxy_get_child_by_index;
+ iface->get_children_count = gst_libcamera_src_child_proxy_get_children_count;
}
diff --git a/src/gstreamer/gstlibcamerasrc.h b/src/gstreamer/gstlibcamerasrc.h
index 0a88ba02..a27db9ca 100644
--- a/src/gstreamer/gstlibcamerasrc.h
+++ b/src/gstreamer/gstlibcamerasrc.h
@@ -3,13 +3,11 @@
* Copyright (C) 2019, Collabora Ltd.
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
*
- * gstlibcamerasrc.h - GStreamer Capture Element
+ * GStreamer Capture Element
*/
#pragma once
-#include <libcamera/control_ids.h>
-
#include <gst/gst.h>
G_BEGIN_DECLS
@@ -19,32 +17,3 @@ G_DECLARE_FINAL_TYPE(GstLibcameraSrc, gst_libcamera_src,
GST_LIBCAMERA, SRC, GstElement)
G_END_DECLS
-
-inline GType
-gst_libcamera_auto_focus_get_type()
-{
- static GType type = 0;
- static const GEnumValue values[] = {
- {
- static_cast<gint>(libcamera::controls::AfModeManual),
- "AfModeManual",
- "manual-focus",
- },
- {
- static_cast<gint>(libcamera::controls::AfModeAuto),
- "AfModeAuto",
- "automatic-auto-focus",
- },
- {
- static_cast<gint>(libcamera::controls::AfModeContinuous),
- "AfModeContinuous",
- "continuous-auto-focus",
- },
- { 0, NULL, NULL }
- };
-
- if (!type)
- type = g_enum_register_static("GstLibcameraAutoFocus", values);
-
- return type;
-}
diff --git a/src/gstreamer/meson.build b/src/gstreamer/meson.build
index 20784b71..fd83e073 100644
--- a/src/gstreamer/meson.build
+++ b/src/gstreamer/meson.build
@@ -25,6 +25,17 @@ libcamera_gst_sources = [
'gstlibcamerasrc.cpp',
]
+# Generate gstreamer control properties
+
+gen_gst_controls_template = files('gstlibcamera-controls.cpp.in')
+libcamera_gst_sources += custom_target('gstlibcamera-controls.cpp',
+ input : controls_files,
+ output : 'gstlibcamera-controls.cpp',
+ command : [gen_gst_controls, '-o', '@OUTPUT@',
+ '-t', gen_gst_controls_template, '@INPUT@'],
+ depend_files : [py_mod_controls],
+ env : py_build_env)
+
libcamera_gst_cpp_args = [
'-DVERSION="@0@"'.format(libcamera_git_version),
'-DPACKAGE="@0@"'.format(meson.project_name()),
@@ -46,3 +57,15 @@ libcamera_gst = shared_library('gstlibcamera',
install : true,
install_dir : '@0@/gstreamer-1.0'.format(get_option('libdir')),
)
+
+# Make the plugin visible to GStreamer inside meson devenv.
+fs = import('fs')
+gst_plugin_path = fs.parent(libcamera_gst.full_path())
+
+gst_env = environment()
+gst_env.prepend('GST_PLUGIN_PATH', gst_plugin_path)
+
+# Avoid polluting the system registry.
+gst_env.set('GST_REGISTRY', gst_plugin_path / 'registry.data')
+
+meson.add_devenv(gst_env)
diff --git a/src/ipa/ipa-sign-install.sh b/src/ipa/ipa-sign-install.sh
index bcedb8b5..71696d5a 100755
--- a/src/ipa/ipa-sign-install.sh
+++ b/src/ipa/ipa-sign-install.sh
@@ -4,7 +4,7 @@
#
# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
#
-# ipa-sign-install.sh - Regenerate IPA module signatures when installing
+# Regenerate IPA module signatures when installing
key=$1
shift
diff --git a/src/ipa/ipa-sign.sh b/src/ipa/ipa-sign.sh
index 8673dad1..69024213 100755
--- a/src/ipa/ipa-sign.sh
+++ b/src/ipa/ipa-sign.sh
@@ -4,7 +4,7 @@
#
# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
#
-# ipa-sign.sh - Generate a signature for an IPA module
+# Generate a signature for an IPA module
key="$1"
input="$2"
diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp
index 12927eec..cf68fb59 100644
--- a/src/ipa/ipu3/algorithms/af.cpp
+++ b/src/ipa/ipu3/algorithms/af.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Red Hat
*
- * af.cpp - IPU3 auto focus algorithm
+ * IPU3 auto focus algorithm
*/
#include "af.h"
@@ -11,7 +11,6 @@
#include <chrono>
#include <cmath>
#include <fcntl.h>
-#include <numeric>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -23,8 +22,6 @@
#include <libcamera/ipa/core_ipa_interface.h>
-#include "libipa/histogram.h"
-
/**
* \file af.h
*/
diff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h
index c6168e30..68126d46 100644
--- a/src/ipa/ipu3/algorithms/af.h
+++ b/src/ipa/ipu3/algorithms/af.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Red Hat
*
- * af.h - IPU3 Af algorithm
+ * IPU3 Af algorithm
*/
#pragma once
diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index 606a237a..39d0aebb 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -2,21 +2,22 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * ipu3_agc.cpp - AGC/AEC mean-based control algorithm
+ * AGC/AEC mean-based control algorithm
*/
#include "agc.h"
#include <algorithm>
#include <chrono>
-#include <cmath>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include <libcamera/control_ids.h>
+
#include <libcamera/ipa/core_ipa_interface.h>
+#include "libipa/colours.h"
#include "libipa/histogram.h"
/**
@@ -33,7 +34,7 @@ namespace ipa::ipu3::algorithms {
* \class Agc
* \brief A mean-based auto-exposure algorithm
*
- * This algorithm calculates a shutter time and an analogue gain so that the
+ * This algorithm calculates an exposure time and an analogue gain so that the
* average value of the green channel of the brightest 2% of pixels approaches
* 0.5. The AWB gains are not used here, and all cells in the grid have the same
* weight, like an average-metering case. In this metering mode, the camera uses
@@ -51,29 +52,37 @@ LOG_DEFINE_CATEGORY(IPU3Agc)
static constexpr double kMinAnalogueGain = 1.0;
/* \todo Honour the FrameDurationLimits control instead of hardcoding a limit */
-static constexpr utils::Duration kMaxShutterSpeed = 60ms;
+static constexpr utils::Duration kMaxExposureTime = 60ms;
/* Histogram constants */
static constexpr uint32_t knumHistogramBins = 256;
-/* Target value to reach for the top 2% of the histogram */
-static constexpr double kEvGainTarget = 0.5;
-
-/* Number of frames to wait before calculating stats on minimum exposure */
-static constexpr uint32_t kNumStartupFrames = 10;
+Agc::Agc()
+ : minExposureTime_(0s), maxExposureTime_(0s)
+{
+}
-/*
- * Relative luminance target.
+/**
+ * \brief Initialise the AGC algorithm from tuning files
+ * \param[in] context The shared IPA context
+ * \param[in] tuningData The YamlObject containing Agc tuning data
+ *
+ * This function calls the base class' tuningData parsers to discover which
+ * control values are supported.
*
- * It's a number that's chosen so that, when the camera points at a grey
- * target, the resulting image brightness is considered right.
+ * \return 0 on success or errors from the base class
*/
-static constexpr double kRelativeLuminanceTarget = 0.16;
-
-Agc::Agc()
- : frameCount_(0), minShutterSpeed_(0s),
- maxShutterSpeed_(0s), filteredExposure_(0s)
+int Agc::init(IPAContext &context, const YamlObject &tuningData)
{
+ int ret;
+
+ ret = parseTuningData(tuningData);
+ if (ret)
+ return ret;
+
+ context.ctrlMap.merge(controls());
+
+ return 0;
}
/**
@@ -90,10 +99,11 @@ int Agc::configure(IPAContext &context,
IPAActiveState &activeState = context.activeState;
stride_ = configuration.grid.stride;
+ bdsGrid_ = configuration.grid.bdsGrid;
- minShutterSpeed_ = configuration.agc.minShutterSpeed;
- maxShutterSpeed_ = std::min(configuration.agc.maxShutterSpeed,
- kMaxShutterSpeed);
+ minExposureTime_ = configuration.agc.minExposureTime;
+ maxExposureTime_ = std::min(configuration.agc.maxExposureTime,
+ kMaxExposureTime);
minAnalogueGain_ = std::max(configuration.agc.minAnalogueGain, kMinAnalogueGain);
maxAnalogueGain_ = configuration.agc.maxAnalogueGain;
@@ -102,168 +112,53 @@ int Agc::configure(IPAContext &context,
activeState.agc.gain = minAnalogueGain_;
activeState.agc.exposure = 10ms / configuration.sensor.lineDuration;
- frameCount_ = 0;
+ context.activeState.agc.constraintMode = constraintModes().begin()->first;
+ context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;
+
+ /* \todo Run this again when FrameDurationLimits is passed in */
+ setLimits(minExposureTime_, maxExposureTime_, minAnalogueGain_,
+ maxAnalogueGain_);
+ resetFrameCount();
+
return 0;
}
-/**
- * \brief Estimate the mean value of the top 2% of the histogram
- * \param[in] stats The statistics computed by the ImgU
- * \param[in] grid The grid used to store the statistics in the IPU3
- * \return The mean value of the top 2% of the histogram
- */
-double Agc::measureBrightness(const ipu3_uapi_stats_3a *stats,
- const ipu3_uapi_grid_config &grid) const
+Histogram Agc::parseStatistics(const ipu3_uapi_stats_3a *stats,
+ const ipu3_uapi_grid_config &grid)
{
- /* Initialise the histogram array */
uint32_t hist[knumHistogramBins] = { 0 };
+ rgbTriples_.clear();
+
for (unsigned int cellY = 0; cellY < grid.height; cellY++) {
for (unsigned int cellX = 0; cellX < grid.width; cellX++) {
uint32_t cellPosition = cellY * stride_ + cellX;
const ipu3_uapi_awb_set_item *cell =
reinterpret_cast<const ipu3_uapi_awb_set_item *>(
- &stats->awb_raw_buffer.meta_data[cellPosition]
- );
+ &stats->awb_raw_buffer.meta_data[cellPosition]);
+
+ rgbTriples_.push_back({
+ cell->R_avg,
+ (cell->Gr_avg + cell->Gb_avg) / 2,
+ cell->B_avg
+ });
- uint8_t gr = cell->Gr_avg;
- uint8_t gb = cell->Gb_avg;
/*
* Store the average green value to estimate the
* brightness. Even the overexposed pixels are
* taken into account.
*/
- hist[(gr + gb) / 2]++;
+ hist[(cell->Gr_avg + cell->Gb_avg) / 2]++;
}
}
- /* Estimate the quantile mean of the top 2% of the histogram. */
- return Histogram(Span<uint32_t>(hist)).interQuantileMean(0.98, 1.0);
-}
-
-/**
- * \brief Apply a filter on the exposure value to limit the speed of changes
- * \param[in] exposureValue The target exposure from the AGC algorithm
- *
- * The speed of the filter is adaptive, and will produce the target quicker
- * during startup, or when the target exposure is within 20% of the most recent
- * filter output.
- *
- * \return The filtered exposure
- */
-utils::Duration Agc::filterExposure(utils::Duration exposureValue)
-{
- double speed = 0.2;
-
- /* Adapt instantly if we are in startup phase. */
- if (frameCount_ < kNumStartupFrames)
- speed = 1.0;
-
- /*
- * If we are close to the desired result, go faster to avoid making
- * multiple micro-adjustments.
- * \todo Make this customisable?
- */
- if (filteredExposure_ < 1.2 * exposureValue &&
- filteredExposure_ > 0.8 * exposureValue)
- speed = sqrt(speed);
-
- filteredExposure_ = speed * exposureValue +
- filteredExposure_ * (1.0 - speed);
-
- LOG(IPU3Agc, Debug) << "After filtering, exposure " << filteredExposure_;
-
- return filteredExposure_;
-}
-
-/**
- * \brief Estimate the new exposure and gain values
- * \param[inout] frameContext The shared IPA frame Context
- * \param[in] yGain The gain calculated based on the relative luminance target
- * \param[in] iqMeanGain The gain calculated based on the relative luminance target
- */
-void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
- double yGain, double iqMeanGain)
-{
- const IPASessionConfiguration &configuration = context.configuration;
- /* Get the effective exposure and gain applied on the sensor. */
- uint32_t exposure = frameContext.sensor.exposure;
- double analogueGain = frameContext.sensor.gain;
-
- /* Use the highest of the two gain estimates. */
- double evGain = std::max(yGain, iqMeanGain);
-
- /* Consider within 1% of the target as correctly exposed */
- if (utils::abs_diff(evGain, 1.0) < 0.01)
- LOG(IPU3Agc, Debug) << "We are well exposed (evGain = "
- << evGain << ")";
-
- /* extracted from Rpi::Agc::computeTargetExposure */
-
- /* Calculate the shutter time in seconds */
- utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
-
- /*
- * Update the exposure value for the next computation using the values
- * of exposure and gain really used by the sensor.
- */
- utils::Duration effectiveExposureValue = currentShutter * analogueGain;
-
- LOG(IPU3Agc, Debug) << "Actual total exposure " << currentShutter * analogueGain
- << " Shutter speed " << currentShutter
- << " Gain " << analogueGain
- << " Needed ev gain " << evGain;
-
- /*
- * Calculate the current exposure value for the scene as the latest
- * exposure value applied multiplied by the new estimated gain.
- */
- utils::Duration exposureValue = effectiveExposureValue * evGain;
-
- /* Clamp the exposure value to the min and max authorized */
- utils::Duration maxTotalExposure = maxShutterSpeed_ * maxAnalogueGain_;
- exposureValue = std::min(exposureValue, maxTotalExposure);
- LOG(IPU3Agc, Debug) << "Target total exposure " << exposureValue
- << ", maximum is " << maxTotalExposure;
-
- /*
- * Filter the exposure.
- * \todo estimate if we need to desaturate
- */
- exposureValue = filterExposure(exposureValue);
-
- /*
- * Divide the exposure value as new exposure and gain values.
- *
- * Push the shutter time up to the maximum first, and only then
- * increase the gain.
- */
- utils::Duration shutterTime =
- std::clamp<utils::Duration>(exposureValue / minAnalogueGain_,
- minShutterSpeed_, maxShutterSpeed_);
- double stepGain = std::clamp(exposureValue / shutterTime,
- minAnalogueGain_, maxAnalogueGain_);
- LOG(IPU3Agc, Debug) << "Divided up shutter and gain are "
- << shutterTime << " and "
- << stepGain;
-
- IPAActiveState &activeState = context.activeState;
- /* Update the estimated exposure and gain. */
- activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;
- activeState.agc.gain = stepGain;
+ return Histogram(Span<uint32_t>(hist));
}
/**
* \brief Estimate the relative luminance of the frame with a given gain
- * \param[in] frameContext The shared IPA frame context
- * \param[in] grid The grid used to store the statistics in the IPU3
- * \param[in] stats The IPU3 statistics and ISP results
- * \param[in] gain The gain to apply to the frame
- * \return The relative luminance
- *
- * This function estimates the average relative luminance of the frame that
- * would be output by the sensor if an additional \a gain was applied.
+ * \param[in] gain The gain to apply in estimating luminance
*
* The estimation is based on the AWB statistics for the current frame. Red,
* green and blue averages for all cells are first multiplied by the gain, and
@@ -278,40 +173,22 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
*
* More detailed information can be found in:
* https://en.wikipedia.org/wiki/Relative_luminance
+ *
+ * \return The relative luminance of the frame
*/
-double Agc::estimateLuminance(IPAActiveState &activeState,
- const ipu3_uapi_grid_config &grid,
- const ipu3_uapi_stats_3a *stats,
- double gain)
+double Agc::estimateLuminance(double gain) const
{
- double redSum = 0, greenSum = 0, blueSum = 0;
-
- /* Sum the per-channel averages, saturated to 255. */
- for (unsigned int cellY = 0; cellY < grid.height; cellY++) {
- for (unsigned int cellX = 0; cellX < grid.width; cellX++) {
- uint32_t cellPosition = cellY * stride_ + cellX;
-
- const ipu3_uapi_awb_set_item *cell =
- reinterpret_cast<const ipu3_uapi_awb_set_item *>(
- &stats->awb_raw_buffer.meta_data[cellPosition]
- );
- const uint8_t G_avg = (cell->Gr_avg + cell->Gb_avg) / 2;
+ RGB<double> sum{ 0.0 };
- redSum += std::min(cell->R_avg * gain, 255.0);
- greenSum += std::min(G_avg * gain, 255.0);
- blueSum += std::min(cell->B_avg * gain, 255.0);
- }
+ for (unsigned int i = 0; i < rgbTriples_.size(); i++) {
+ sum.r() += std::min(std::get<0>(rgbTriples_[i]) * gain, 255.0);
+ sum.g() += std::min(std::get<1>(rgbTriples_[i]) * gain, 255.0);
+ sum.b() += std::min(std::get<2>(rgbTriples_[i]) * gain, 255.0);
}
- /*
- * Apply the AWB gains to approximate colours correctly, use the Rec.
- * 601 formula to calculate the relative luminance, and normalize it.
- */
- double ySum = redSum * activeState.awb.gains.red * 0.299
- + greenSum * activeState.awb.gains.green * 0.587
- + blueSum * activeState.awb.gains.blue * 0.114;
-
- return ySum / (grid.height * grid.width) / 255;
+ RGB<double> gains{{ rGain_, gGain_, bGain_ }};
+ double ySum = rec601LuminanceFromRGB(sum * gains);
+ return ySum / (bdsGrid_.height * bdsGrid_.width) / 255;
}
/**
@@ -330,44 +207,36 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
const ipu3_uapi_stats_3a *stats,
ControlList &metadata)
{
- /*
- * Estimate the gain needed to have the proportion of pixels in a given
- * desired range. iqMean is the mean value of the top 2% of the
- * cumulative histogram, and we want it to be as close as possible to a
- * configured target.
- */
- double iqMean = measureBrightness(stats, context.configuration.grid.bdsGrid);
- double iqMeanGain = kEvGainTarget * knumHistogramBins / iqMean;
+ Histogram hist = parseStatistics(stats, context.configuration.grid.bdsGrid);
+ rGain_ = context.activeState.awb.gains.red;
+ gGain_ = context.activeState.awb.gains.blue;
+ bGain_ = context.activeState.awb.gains.green;
/*
- * Estimate the gain needed to achieve a relative luminance target. To
- * account for non-linearity caused by saturation, the value needs to be
- * estimated in an iterative process, as multiplying by a gain will not
- * increase the relative luminance by the same factor if some image
- * regions are saturated.
+ * The Agc algorithm needs to know the effective exposure value that was
+ * applied to the sensor when the statistics were collected.
*/
- double yGain = 1.0;
- double yTarget = kRelativeLuminanceTarget;
-
- for (unsigned int i = 0; i < 8; i++) {
- double yValue = estimateLuminance(context.activeState,
- context.configuration.grid.bdsGrid,
- stats, yGain);
- double extraGain = std::min(10.0, yTarget / (yValue + .001));
-
- yGain *= extraGain;
- LOG(IPU3Agc, Debug) << "Y value: " << yValue
- << ", Y target: " << yTarget
- << ", gives gain " << yGain;
- if (extraGain < 1.01)
- break;
- }
-
- computeExposure(context, frameContext, yGain, iqMeanGain);
- frameCount_++;
-
utils::Duration exposureTime = context.configuration.sensor.lineDuration
* frameContext.sensor.exposure;
+ double analogueGain = frameContext.sensor.gain;
+ utils::Duration effectiveExposureValue = exposureTime * analogueGain;
+
+ utils::Duration newExposureTime;
+ double aGain, dGain;
+ std::tie(newExposureTime, aGain, dGain) =
+ calculateNewEv(context.activeState.agc.constraintMode,
+ context.activeState.agc.exposureMode, hist,
+ effectiveExposureValue);
+
+ LOG(IPU3Agc, Debug)
+ << "Divided up exposure time, analogue gain and digital gain are "
+ << newExposureTime << ", " << aGain << " and " << dGain;
+
+ IPAActiveState &activeState = context.activeState;
+ /* Update the estimated exposure time and gain. */
+ activeState.agc.exposure = newExposureTime / context.configuration.sensor.lineDuration;
+ activeState.agc.gain = aGain;
+
metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
@@ -377,7 +246,6 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
utils::Duration frameDuration = context.configuration.sensor.lineDuration
* vTotal;
metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
-
}
REGISTER_IPA_ALGORITHM(Agc, "Agc")
diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h
index 9d6e3ff1..890c271b 100644
--- a/src/ipa/ipu3/algorithms/agc.h
+++ b/src/ipa/ipu3/algorithms/agc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * agc.h - IPU3 AGC/AEC mean-based control algorithm
+ * IPU3 AGC/AEC mean-based control algorithm
*/
#pragma once
@@ -13,6 +13,9 @@
#include <libcamera/geometry.h>
+#include "libipa/agc_mean_luminance.h"
+#include "libipa/histogram.h"
+
#include "algorithm.h"
namespace libcamera {
@@ -21,12 +24,13 @@ struct IPACameraSensorInfo;
namespace ipa::ipu3::algorithms {
-class Agc : public Algorithm
+class Agc : public Algorithm, public AgcMeanLuminance
{
public:
Agc();
~Agc() = default;
+ int init(IPAContext &context, const YamlObject &tuningData) override;
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
void process(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
@@ -34,27 +38,22 @@ public:
ControlList &metadata) override;
private:
- double measureBrightness(const ipu3_uapi_stats_3a *stats,
- const ipu3_uapi_grid_config &grid) const;
- utils::Duration filterExposure(utils::Duration currentExposure);
- void computeExposure(IPAContext &context, IPAFrameContext &frameContext,
- double yGain, double iqMeanGain);
- double estimateLuminance(IPAActiveState &activeState,
- const ipu3_uapi_grid_config &grid,
- const ipu3_uapi_stats_3a *stats,
- double gain);
-
- uint64_t frameCount_;
+ double estimateLuminance(double gain) const override;
+ Histogram parseStatistics(const ipu3_uapi_stats_3a *stats,
+ const ipu3_uapi_grid_config &grid);
- utils::Duration minShutterSpeed_;
- utils::Duration maxShutterSpeed_;
+ utils::Duration minExposureTime_;
+ utils::Duration maxExposureTime_;
double minAnalogueGain_;
double maxAnalogueGain_;
- utils::Duration filteredExposure_;
-
uint32_t stride_;
+ double rGain_;
+ double gGain_;
+ double bGain_;
+ ipu3_uapi_grid_config bdsGrid_;
+ std::vector<std::tuple<uint8_t, uint8_t, uint8_t>> rgbTriples_;
};
} /* namespace ipa::ipu3::algorithms */
diff --git a/src/ipa/ipu3/algorithms/algorithm.h b/src/ipa/ipu3/algorithms/algorithm.h
index ae134a94..c7801f93 100644
--- a/src/ipa/ipu3/algorithms/algorithm.h
+++ b/src/ipa/ipu3/algorithms/algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * algorithm.h - IPU3 control algorithm interface
+ * IPU3 control algorithm interface
*/
#pragma once
diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp
index 5abd4621..55de05d9 100644
--- a/src/ipa/ipu3/algorithms/awb.cpp
+++ b/src/ipa/ipu3/algorithms/awb.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * awb.cpp - AWB control algorithm
+ * AWB control algorithm
*/
#include "awb.h"
@@ -13,6 +13,8 @@
#include <libcamera/control_ids.h>
+#include "libipa/colours.h"
+
/**
* \file awb.h
*/
@@ -301,51 +303,24 @@ void Awb::prepare(IPAContext &context,
params->use.acc_ccm = 1;
}
-/**
- * The function estimates the correlated color temperature using
- * from RGB color space input.
- * In physics and color science, the Planckian locus or black body locus is
- * the path or locus that the color of an incandescent black body would take
- * in a particular chromaticity space as the blackbody temperature changes.
- *
- * If a narrow range of color temperatures is considered (those encapsulating
- * daylight being the most practical case) one can approximate the Planckian
- * locus in order to calculate the CCT in terms of chromaticity coordinates.
- *
- * More detailed information can be found in:
- * https://en.wikipedia.org/wiki/Color_temperature#Approximation
- */
-uint32_t Awb::estimateCCT(double red, double green, double blue)
-{
- /* Convert the RGB values to CIE tristimulus values (XYZ) */
- double X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue);
- double Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue);
- double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue);
-
- /* Calculate the normalized chromaticity values */
- double x = X / (X + Y + Z);
- double y = Y / (X + Y + Z);
-
- /* Calculate CCT */
- double n = (x - 0.3320) / (0.1858 - y);
- return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;
-}
-
/* Generate an RGB vector with the average values for each zone */
void Awb::generateZones()
{
zones_.clear();
for (unsigned int i = 0; i < kAwbStatsSizeX * kAwbStatsSizeY; i++) {
- RGB zone;
double counted = awbStats_[i].counted;
if (counted >= cellsPerZoneThreshold_) {
- zone.G = awbStats_[i].sum.green / counted;
- if (zone.G >= kMinGreenLevelInZone) {
- zone.R = awbStats_[i].sum.red / counted;
- zone.B = awbStats_[i].sum.blue / counted;
+ RGB<double> zone{{
+ static_cast<double>(awbStats_[i].sum.red),
+ static_cast<double>(awbStats_[i].sum.green),
+ static_cast<double>(awbStats_[i].sum.blue)
+ }};
+
+ zone /= counted;
+
+ if (zone.g() >= kMinGreenLevelInZone)
zones_.push_back(zone);
- }
}
}
}
@@ -412,32 +387,32 @@ void Awb::awbGreyWorld()
* consider some variations, such as normalising all the zones first, or
* doing an L2 average etc.
*/
- std::vector<RGB> &redDerivative(zones_);
- std::vector<RGB> blueDerivative(redDerivative);
+ std::vector<RGB<double>> &redDerivative(zones_);
+ std::vector<RGB<double>> blueDerivative(redDerivative);
std::sort(redDerivative.begin(), redDerivative.end(),
- [](RGB const &a, RGB const &b) {
- return a.G * b.R < b.G * a.R;
+ [](RGB<double> const &a, RGB<double> const &b) {
+ return a.g() * b.r() < b.g() * a.r();
});
std::sort(blueDerivative.begin(), blueDerivative.end(),
- [](RGB const &a, RGB const &b) {
- return a.G * b.B < b.G * a.B;
+ [](RGB<double> const &a, RGB<double> const &b) {
+ return a.g() * b.b() < b.g() * a.b();
});
/* Average the middle half of the values. */
int discard = redDerivative.size() / 4;
- RGB sumRed(0, 0, 0);
- RGB sumBlue(0, 0, 0);
+ RGB<double> sumRed{ 0.0 };
+ RGB<double> sumBlue{ 0.0 };
for (auto ri = redDerivative.begin() + discard,
bi = blueDerivative.begin() + discard;
ri != redDerivative.end() - discard; ri++, bi++)
sumRed += *ri, sumBlue += *bi;
- double redGain = sumRed.G / (sumRed.R + 1),
- blueGain = sumBlue.G / (sumBlue.B + 1);
+ double redGain = sumRed.g() / (sumRed.r() + 1),
+ blueGain = sumBlue.g() / (sumBlue.b() + 1);
/* Color temperature is not relevant in Grey world but still useful to estimate it :-) */
- asyncResults_.temperatureK = estimateCCT(sumRed.R, sumRed.G, sumBlue.B);
+ asyncResults_.temperatureK = estimateCCT({{ sumRed.r(), sumRed.g(), sumBlue.b() }});
/*
* Gain values are unsigned integer value ranging [0, 8) with 13 bit
diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h
index 7a70854e..dbf69c90 100644
--- a/src/ipa/ipu3/algorithms/awb.h
+++ b/src/ipa/ipu3/algorithms/awb.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * awb.h - IPU3 AWB control algorithm
+ * IPU3 AWB control algorithm
*/
#pragma once
@@ -13,6 +13,8 @@
#include <libcamera/geometry.h>
+#include "libcamera/internal/vector.h"
+
#include "algorithm.h"
namespace libcamera {
@@ -48,20 +50,6 @@ public:
ControlList &metadata) override;
private:
- /* \todo Make these structs available to all the ISPs ? */
- 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;
- }
- };
-
struct AwbStatus {
double temperatureK;
double redGain;
@@ -75,11 +63,10 @@ private:
void generateAwbStats(const ipu3_uapi_stats_3a *stats);
void clearAwbStats();
void awbGreyWorld();
- uint32_t estimateCCT(double red, double green, double blue);
static constexpr uint16_t threshold(float value);
static constexpr uint16_t gainValue(double gain);
- std::vector<RGB> zones_;
+ std::vector<RGB<double>> zones_;
Accumulator awbStats_[kAwbStatsSizeX * kAwbStatsSizeY];
AwbStatus asyncResults_;
diff --git a/src/ipa/ipu3/algorithms/blc.cpp b/src/ipa/ipu3/algorithms/blc.cpp
index e838072a..35748fb2 100644
--- a/src/ipa/ipu3/algorithms/blc.cpp
+++ b/src/ipa/ipu3/algorithms/blc.cpp
@@ -2,13 +2,11 @@
/*
* Copyright (C) 2021, Google inc.
*
- * blc.cpp - IPU3 Black Level Correction control
+ * IPU3 Black Level Correction control
*/
#include "blc.h"
-#include <string.h>
-
/**
* \file blc.h
* \brief IPU3 Black Level Correction control
@@ -57,8 +55,8 @@ void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
* tuning processes. This is a first rough approximation.
*/
params->obgrid_param.gr = 64;
- params->obgrid_param.r = 64;
- params->obgrid_param.b = 64;
+ params->obgrid_param.r = 64;
+ params->obgrid_param.b = 64;
params->obgrid_param.gb = 64;
/* Enable the custom black level correction processing */
diff --git a/src/ipa/ipu3/algorithms/blc.h b/src/ipa/ipu3/algorithms/blc.h
index 292bf67b..62748045 100644
--- a/src/ipa/ipu3/algorithms/blc.h
+++ b/src/ipa/ipu3/algorithms/blc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google inc.
*
- * black_correction.h - IPU3 Black Level Correction control
+ * IPU3 Black Level Correction control
*/
#pragma once
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.cpp b/src/ipa/ipu3/algorithms/tone_mapping.cpp
index a169894c..160338c1 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.cpp
+++ b/src/ipa/ipu3/algorithms/tone_mapping.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google inc.
*
- * tone_mapping.cpp - IPU3 ToneMapping and Gamma control
+ * IPU3 ToneMapping and Gamma control
*/
#include "tone_mapping.h"
diff --git a/src/ipa/ipu3/algorithms/tone_mapping.h b/src/ipa/ipu3/algorithms/tone_mapping.h
index 5ae35da5..b2b38010 100644
--- a/src/ipa/ipu3/algorithms/tone_mapping.h
+++ b/src/ipa/ipu3/algorithms/tone_mapping.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google inc.
*
- * tone_mapping.h - IPU3 ToneMapping and Gamma control
+ * IPU3 ToneMapping and Gamma control
*/
#pragma once
diff --git a/src/ipa/ipu3/data/meson.build b/src/ipa/ipu3/data/meson.build
index 1f50b630..0f7cd5c6 100644
--- a/src/ipa/ipu3/data/meson.build
+++ b/src/ipa/ipu3/data/meson.build
@@ -5,4 +5,5 @@ conf_files = files([
])
install_data(conf_files,
- install_dir : ipa_data_dir / 'ipu3')
+ install_dir : ipa_data_dir / 'ipu3',
+ install_tag : 'runtime')
diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
index 959f314f..3b22f791 100644
--- a/src/ipa/ipu3/ipa_context.cpp
+++ b/src/ipa/ipu3/ipa_context.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * ipa_context.cpp - IPU3 IPA Context
+ * IPU3 IPA Context
*/
#include "ipa_context.h"
@@ -39,6 +39,10 @@ namespace libcamera::ipa::ipu3 {
* \struct IPAContext
* \brief Global IPA context data shared between all algorithms
*
+ * \fn IPAContext::IPAContext
+ * \brief Initialize the instance with the given number of frame contexts
+ * \param[in] frameContextSize Size of the frame context ring buffer
+ *
* \var IPAContext::configuration
* \brief The IPA session configuration, immutable during the session
*
@@ -47,6 +51,9 @@ namespace libcamera::ipa::ipu3 {
*
* \var IPAContext::activeState
* \brief The current state of IPA algorithms
+ *
+ * \var IPAContext::ctrlMap
+ * \brief A ControlInfoMap::Map of controls populated by the algorithms
*/
/**
@@ -89,11 +96,11 @@ namespace libcamera::ipa::ipu3 {
* \var IPASessionConfiguration::agc
* \brief AGC parameters configuration of the IPA
*
- * \var IPASessionConfiguration::agc.minShutterSpeed
- * \brief Minimum shutter speed supported with the configured sensor
+ * \var IPASessionConfiguration::agc.minExposureTime
+ * \brief Minimum exposure time supported with the configured sensor
*
- * \var IPASessionConfiguration::agc.maxShutterSpeed
- * \brief Maximum shutter speed supported with the configured sensor
+ * \var IPASessionConfiguration::agc.maxExposureTime
+ * \brief Maximum exposure time supported with the configured sensor
*
* \var IPASessionConfiguration::agc.minAnalogueGain
* \brief Minimum analogue gain supported with the configured sensor
diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
index e9a3863b..97fcf06c 100644
--- a/src/ipa/ipu3/ipa_context.h
+++ b/src/ipa/ipu3/ipa_context.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * ipa_context.h - IPU3 IPA Context
+ * IPU3 IPA Context
*
*/
@@ -12,6 +12,7 @@
#include <libcamera/base/utils.h>
+#include <libcamera/controls.h>
#include <libcamera/geometry.h>
#include <libipa/fc_queue.h>
@@ -32,8 +33,8 @@ struct IPASessionConfiguration {
} af;
struct {
- utils::Duration minShutterSpeed;
- utils::Duration maxShutterSpeed;
+ utils::Duration minExposureTime;
+ utils::Duration maxExposureTime;
double minAnalogueGain;
double maxAnalogueGain;
} agc;
@@ -55,6 +56,8 @@ struct IPAActiveState {
struct {
uint32_t exposure;
double gain;
+ uint32_t constraintMode;
+ uint32_t exposureMode;
} agc;
struct {
@@ -81,10 +84,17 @@ struct IPAFrameContext : public FrameContext {
};
struct IPAContext {
+ IPAContext(unsigned int frameContextSize)
+ : frameContexts(frameContextSize)
+ {
+ }
+
IPASessionConfiguration configuration;
IPAActiveState activeState;
FCQueue<IPAFrameContext> frameContexts;
+
+ ControlInfoMap::Map ctrlMap;
};
} /* namespace ipa::ipu3 */
diff --git a/src/ipa/ipu3/ipu3-ipa-design-guide.rst b/src/ipa/ipu3/ipu3-ipa-design-guide.rst
index 72506397..85d735c6 100644
--- a/src/ipa/ipu3/ipu3-ipa-design-guide.rst
+++ b/src/ipa/ipu3/ipu3-ipa-design-guide.rst
@@ -27,8 +27,8 @@ from applications, and managing events from the pipeline handler.
└─┬───┬───┬──────┬────┬────┬────┬─┴────▼─┬──┘ 1: init()
│ │ │ │ ▲ │ ▲ │ ▲ │ ▲ │ 2: configure()
│1 │2 │3 │4│ │4│ │4│ │4│ │5 3: mapBuffers(), start()
- │ │ │ │ │ │ │ │ │ │ │ │ 4: (▼) queueRequest(), fillParamsBuffer(), processStatsBuffer()
- ▼ ▼ ▼ ▼ │ ▼ │ ▼ │ ▼ │ ▼ (▲) setSensorControls, paramsBufferReady, metadataReady Signals
+ │ │ │ │ │ │ │ │ │ │ │ │ 4: (▼) queueRequest(), computeParams(), processStats()
+ ▼ ▼ ▼ ▼ │ ▼ │ ▼ │ ▼ │ ▼ (▲) setSensorControls, paramsComputed, metadataReady Signals
┌──────────────────┴────┴────┴────┴─────────┐ 5: stop(), unmapBuffers()
│ IPU3 IPA │
│ ┌───────────────────────┐ │
@@ -104,8 +104,8 @@ to operate when running:
- configure()
- queueRequest()
-- fillParamsBuffer()
-- processStatsBuffer()
+- computeParams()
+- processStats()
The configuration phase allows the pipeline-handler to inform the IPA of
the current stream configurations, which is then passed into each
@@ -119,7 +119,7 @@ When configured, the IPA is notified by the pipeline handler of the
Camera ``start()`` event, after which incoming requests will be queued
for processing, requiring a parameter buffer (``ipu3_uapi_params``) to
be populated for the ImgU. This is given to the IPA through
-``fillParamsBuffer()``, and then passed directly to each algorithm
+``computeParams()``, and then passed directly to each algorithm
through the ``prepare()`` call allowing the ISP configuration to be
updated for the needs of each component that the algorithm is
responsible for.
@@ -129,7 +129,7 @@ structure that it modifies, and it should take care to ensure that any
structure set by a use flag is fully initialised to suitable values.
The parameter buffer is returned to the pipeline handler through the
-``paramsBufferReady`` signal, and from there queued to the ImgU along
+``paramsComputed`` signal, and from there queued to the ImgU along
with a raw frame captured with the CIO2.
Post-frame completion
@@ -138,7 +138,7 @@ Post-frame completion
When the capture of an image is completed, and successfully processed
through the ImgU, the generated statistics buffer
(``ipu3_uapi_stats_3a``) is given to the IPA through
-``processStatsBuffer()``. This provides the IPA with an opportunity to
+``processStats()``. This provides the IPA with an opportunity to
examine the results of the ISP and run the calculations required by each
algorithm on the new data. The algorithms may require context from the
operations of other algorithms, for example, the AWB might choose to use
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 08ee6eb3..1cae08bf 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * ipu3.cpp - IPU3 Image Processing Algorithms
+ * IPU3 Image Processing Algorithms
*/
#include <algorithm>
@@ -23,24 +23,22 @@
#include <libcamera/base/utils.h>
#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
#include <libcamera/framebuffer.h>
+#include <libcamera/geometry.h>
+#include <libcamera/request.h>
+
#include <libcamera/ipa/ipa_interface.h>
#include <libcamera/ipa/ipa_module_info.h>
#include <libcamera/ipa/ipu3_ipa_interface.h>
-#include <libcamera/request.h>
#include "libcamera/internal/mapped_framebuffer.h"
#include "libcamera/internal/yaml_parser.h"
-#include "algorithms/af.h"
-#include "algorithms/agc.h"
-#include "algorithms/algorithm.h"
-#include "algorithms/awb.h"
-#include "algorithms/blc.h"
-#include "algorithms/tone_mapping.h"
#include "libipa/camera_sensor_helper.h"
#include "ipa_context.h"
+#include "module.h"
/* Minimum grid width, expressed as a number of cells */
static constexpr uint32_t kMinGridWidth = 16;
@@ -89,14 +87,14 @@ namespace ipa::ipu3 {
* parameter buffer, and adapting the settings of the sensor attached to the
* IPU3 CIO2 through sensor-specific V4L2 controls.
*
- * In fillParamsBuffer(), we populate the ImgU parameter buffer with
+ * In computeParams(), we populate the ImgU parameter buffer with
* settings to configure the device in preparation for handling the frame
* queued in the Request.
*
* When the frame has completed processing, the ImgU will generate a statistics
- * buffer which is given to the IPA with processStatsBuffer(). In this we run the
+ * buffer which is given to the IPA with processStats(). In this we run the
* algorithms to parse the statistics and cache any results for the next
- * fillParamsBuffer() call.
+ * computeParams() call.
*
* The individual algorithms are split into modular components that are called
* iteratively to allow them to process statistics from the ImgU in the order
@@ -114,7 +112,7 @@ namespace ipa::ipu3 {
* blue gains to apply to generate a neutral grey frame overall.
*
* AGC is handled by calculating a histogram of the green channel to estimate an
- * analogue gain and shutter time which will provide a well exposed frame. A
+ * analogue gain and exposure time which will provide a well exposed frame. A
* low-pass IIR filter is used to smooth the changes to the sensor to reduce
* perceivable steps.
*
@@ -157,10 +155,10 @@ public:
void unmapBuffers(const std::vector<unsigned int> &ids) override;
void queueRequest(const uint32_t frame, const ControlList &controls) override;
- void fillParamsBuffer(const uint32_t frame, const uint32_t bufferId) override;
- void processStatsBuffer(const uint32_t frame, const int64_t frameTimestamp,
- const uint32_t bufferId,
- const ControlList &sensorControls) override;
+ void computeParams(const uint32_t frame, const uint32_t bufferId) override;
+ void processStats(const uint32_t frame, const int64_t frameTimestamp,
+ const uint32_t bufferId,
+ const ControlList &sensorControls) override;
protected:
std::string logPrefix() const override;
@@ -189,7 +187,7 @@ private:
};
IPAIPU3::IPAIPU3()
- : context_({ {}, {}, { kMaxFrameContexts } })
+ : context_(kMaxFrameContexts)
{
}
@@ -217,13 +215,13 @@ void IPAIPU3::updateSessionConfiguration(const ControlInfoMap &sensorControls)
/*
* When the AGC computes the new exposure values for a frame, it needs
- * to know the limits for shutter speed and analogue gain.
+ * to know the limits for exposure time and analogue gain.
* As it depends on the sensor, update it with the controls.
*
- * \todo take VBLANK into account for maximum shutter speed
+ * \todo take VBLANK into account for maximum exposure time
*/
- context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;
- context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.minExposureTime = minExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.maxExposureTime = maxExposure * context_.configuration.sensor.lineDuration;
context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);
context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);
}
@@ -287,6 +285,7 @@ void IPAIPU3::updateControls(const IPACameraSensorInfo &sensorInfo,
frameDurations[1],
frameDurations[2]);
+ controls.merge(context_.ctrlMap);
*ipaControls = ControlInfoMap(std::move(controls), controls::controls);
}
@@ -312,8 +311,8 @@ int IPAIPU3::init(const IPASettings &settings,
/* Clean context */
context_.configuration = {};
- context_.configuration.sensor.lineDuration = sensorInfo.minLineLength
- * 1.0s / sensorInfo.pixelRate;
+ context_.configuration.sensor.lineDuration =
+ sensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;
/* Load the tuning data file. */
File file(settings.configurationFile);
@@ -476,8 +475,8 @@ int IPAIPU3::configure(const IPAConfigInfo &configInfo,
context_.frameContexts.clear();
/* Initialise the sensor configuration. */
- context_.configuration.sensor.lineDuration = sensorInfo_.minLineLength
- * 1.0s / sensorInfo_.pixelRate;
+ context_.configuration.sensor.lineDuration =
+ sensorInfo_.minLineLength * 1.0s / sensorInfo_.pixelRate;
context_.configuration.sensor.size = sensorInfo_.outputSize;
/*
@@ -539,7 +538,7 @@ void IPAIPU3::unmapBuffers(const std::vector<unsigned int> &ids)
* Algorithms are expected to fill the IPU3 parameter buffer for the next
* frame given their most recent processing of the ImgU statistics.
*/
-void IPAIPU3::fillParamsBuffer(const uint32_t frame, const uint32_t bufferId)
+void IPAIPU3::computeParams(const uint32_t frame, const uint32_t bufferId)
{
auto it = buffers_.find(bufferId);
if (it == buffers_.end()) {
@@ -567,7 +566,7 @@ void IPAIPU3::fillParamsBuffer(const uint32_t frame, const uint32_t bufferId)
for (auto const &algo : algorithms())
algo->prepare(context_, frame, frameContext, params);
- paramsBufferReady.emit(frame);
+ paramsComputed.emit(frame);
}
/**
@@ -581,9 +580,9 @@ void IPAIPU3::fillParamsBuffer(const uint32_t frame, const uint32_t bufferId)
* statistics are passed to each algorithm module to run their calculations and
* update their state accordingly.
*/
-void IPAIPU3::processStatsBuffer(const uint32_t frame,
- [[maybe_unused]] const int64_t frameTimestamp,
- const uint32_t bufferId, const ControlList &sensorControls)
+void IPAIPU3::processStats(const uint32_t frame,
+ [[maybe_unused]] const int64_t frameTimestamp,
+ const uint32_t bufferId, const ControlList &sensorControls)
{
auto it = buffers_.find(bufferId);
if (it == buffers_.end()) {
@@ -672,7 +671,7 @@ extern "C" {
const struct IPAModuleInfo ipaModuleInfo = {
IPA_MODULE_API_VERSION,
1,
- "PipelineHandlerIPU3",
+ "ipu3",
"ipu3",
};
diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build
index 66c39843..34de6213 100644
--- a/src/ipa/ipu3/meson.build
+++ b/src/ipa/ipu3/meson.build
@@ -12,12 +12,10 @@ ipu3_ipa_sources = files([
ipu3_ipa_sources += ipu3_ipa_algorithms
-mod = shared_module(ipa_name,
- [ipu3_ipa_sources, libcamera_generated_ipa_headers],
+mod = shared_module(ipa_name, ipu3_ipa_sources,
name_prefix : '',
- include_directories : [ipa_includes, libipa_includes],
- dependencies : libcamera_private,
- link_with : libipa,
+ include_directories : [ipa_includes],
+ dependencies : [libcamera_private, libipa_dep],
install : true,
install_dir : ipa_install_dir)
diff --git a/src/ipa/ipu3/module.h b/src/ipa/ipu3/module.h
index d94fc459..60f65cc4 100644
--- a/src/ipa/ipu3/module.h
+++ b/src/ipa/ipu3/module.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas On Board
*
- * module.h - IPU3 IPA Module
+ * IPU3 IPA Module
*/
#pragma once
diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
new file mode 100644
index 00000000..d37a9b66
--- /dev/null
+++ b/src/ipa/libipa/agc_mean_luminance.cpp
@@ -0,0 +1,589 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Base class for mean luminance AGC algorithms
+ */
+
+#include "agc_mean_luminance.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
+#include "exposure_mode_helper.h"
+
+using namespace libcamera::controls;
+
+/**
+ * \file agc_mean_luminance.h
+ * \brief Base class implementing mean luminance AEGC
+ */
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+LOG_DEFINE_CATEGORY(AgcMeanLuminance)
+
+namespace ipa {
+
+/*
+ * Number of frames for which to run the algorithm at full speed, before slowing
+ * down to prevent large and jarring changes in exposure from frame to frame.
+ */
+static constexpr uint32_t kNumStartupFrames = 10;
+
+/*
+ * Default relative luminance target
+ *
+ * This value should be chosen so that when the camera points at a grey target,
+ * the resulting image brightness looks "right". Custom values can be passed
+ * as the relativeLuminanceTarget value in sensor tuning files.
+ */
+static constexpr double kDefaultRelativeLuminanceTarget = 0.16;
+
+/**
+ * \struct AgcMeanLuminance::AgcConstraint
+ * \brief The boundaries and target for an AeConstraintMode constraint
+ *
+ * This structure describes an AeConstraintMode constraint for the purposes of
+ * this algorithm. These constraints are expressed as a pair of quantile
+ * boundaries for a histogram, along with a luminance target and a bounds-type.
+ * The algorithm uses the constraints by ensuring that the defined portion of a
+ * luminance histogram (I.E. lying between the two quantiles) is above or below
+ * the given luminance value.
+ */
+
+/**
+ * \enum AgcMeanLuminance::AgcConstraint::Bound
+ * \brief Specify whether the constraint defines a lower or upper bound
+ * \var AgcMeanLuminance::AgcConstraint::Lower
+ * \brief The constraint defines a lower bound
+ * \var AgcMeanLuminance::AgcConstraint::Upper
+ * \brief The constraint defines an upper bound
+ */
+
+/**
+ * \var AgcMeanLuminance::AgcConstraint::bound
+ * \brief The type of constraint bound
+ */
+
+/**
+ * \var AgcMeanLuminance::AgcConstraint::qLo
+ * \brief The lower quantile to use for the constraint
+ */
+
+/**
+ * \var AgcMeanLuminance::AgcConstraint::qHi
+ * \brief The upper quantile to use for the constraint
+ */
+
+/**
+ * \var AgcMeanLuminance::AgcConstraint::yTarget
+ * \brief The luminance target for the constraint
+ */
+
+/**
+ * \class AgcMeanLuminance
+ * \brief A mean-based auto-exposure algorithm
+ *
+ * This algorithm calculates an exposure time, analogue and digital gain such
+ * that the normalised mean luminance value of an image is driven towards a
+ * target, which itself is discovered from tuning data. The algorithm is a
+ * two-stage process.
+ *
+ * In the first stage, an initial gain value is derived by iteratively comparing
+ * the gain-adjusted mean luminance across the entire image against a target,
+ * and selecting a value which pushes it as closely as possible towards the
+ * target.
+ *
+ * In the second stage we calculate the gain required to drive the average of a
+ * section of a histogram to a target value, where the target and the boundaries
+ * of the section of the histogram used in the calculation are taken from the
+ * values defined for the currently configured AeConstraintMode within the
+ * tuning data. This class provides a helper function to parse those tuning data
+ * to discover the constraints, and so requires a specific format for those
+ * data which is described in \ref parseTuningData(). The gain from the first
+ * stage is then clamped to the gain from this stage.
+ *
+ * The final gain is used to adjust the effective exposure value of the image,
+ * and that new exposure value is divided into exposure time, analogue gain and
+ * digital gain according to the selected AeExposureMode. This class uses the
+ * \ref ExposureModeHelper class to assist in that division, and expects the
+ * data needed to initialise that class to be present in tuning data in a
+ * format described in \ref parseTuningData().
+ *
+ * In order to be able to use this algorithm an IPA module needs to be able to
+ * do the following:
+ *
+ * 1. Provide a luminance estimation across an entire image.
+ * 2. Provide a luminance Histogram for the image to use in calculating
+ * constraint compliance. The precision of the Histogram that is available
+ * will determine the supportable precision of the constraints.
+ *
+ * IPA modules that want to use this class to implement their AEGC algorithm
+ * should derive it and provide an overriding estimateLuminance() function for
+ * this class to use. They must call parseTuningData() in init(), and must also
+ * call setLimits() and resetFrameCounter() in configure(). They may then use
+ * calculateNewEv() in process(). If the limits passed to setLimits() change for
+ * any reason (for example, in response to a FrameDurationLimit control being
+ * passed in queueRequest()) then setLimits() must be called again with the new
+ * values.
+ */
+
+AgcMeanLuminance::AgcMeanLuminance()
+ : frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0)
+{
+}
+
+AgcMeanLuminance::~AgcMeanLuminance() = default;
+
+void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData)
+{
+ relativeLuminanceTarget_ =
+ tuningData["relativeLuminanceTarget"].get<double>(kDefaultRelativeLuminanceTarget);
+}
+
+void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id)
+{
+ for (const auto &[boundName, content] : modeDict.asDict()) {
+ if (boundName != "upper" && boundName != "lower") {
+ LOG(AgcMeanLuminance, Warning)
+ << "Ignoring unknown constraint bound '" << boundName << "'";
+ continue;
+ }
+
+ unsigned int idx = static_cast<unsigned int>(boundName == "upper");
+ AgcConstraint::Bound bound = static_cast<AgcConstraint::Bound>(idx);
+ double qLo = content["qLo"].get<double>().value_or(0.98);
+ double qHi = content["qHi"].get<double>().value_or(1.0);
+ double yTarget =
+ content["yTarget"].getList<double>().value_or(std::vector<double>{ 0.5 }).at(0);
+
+ AgcConstraint constraint = { bound, qLo, qHi, yTarget };
+
+ if (!constraintModes_.count(id))
+ constraintModes_[id] = {};
+
+ if (idx)
+ constraintModes_[id].push_back(constraint);
+ else
+ constraintModes_[id].insert(constraintModes_[id].begin(), constraint);
+ }
+}
+
+int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData)
+{
+ std::vector<ControlValue> availableConstraintModes;
+
+ const YamlObject &yamlConstraintModes = tuningData[controls::AeConstraintMode.name()];
+ if (yamlConstraintModes.isDictionary()) {
+ for (const auto &[modeName, modeDict] : yamlConstraintModes.asDict()) {
+ if (AeConstraintModeNameValueMap.find(modeName) ==
+ AeConstraintModeNameValueMap.end()) {
+ LOG(AgcMeanLuminance, Warning)
+ << "Skipping unknown constraint mode '" << modeName << "'";
+ continue;
+ }
+
+ if (!modeDict.isDictionary()) {
+ LOG(AgcMeanLuminance, Error)
+ << "Invalid constraint mode '" << modeName << "'";
+ return -EINVAL;
+ }
+
+ parseConstraint(modeDict,
+ AeConstraintModeNameValueMap.at(modeName));
+ availableConstraintModes.push_back(
+ AeConstraintModeNameValueMap.at(modeName));
+ }
+ }
+
+ /*
+ * If the tuning data file contains no constraints then we use the
+ * default constraint that the IPU3/RkISP1 Agc algorithms were adhering
+ * to anyway before centralisation; this constraint forces the top 2% of
+ * the histogram to be at least 0.5.
+ */
+ if (constraintModes_.empty()) {
+ AgcConstraint constraint = {
+ AgcConstraint::Bound::Lower,
+ 0.98,
+ 1.0,
+ 0.5
+ };
+
+ constraintModes_[controls::ConstraintNormal].insert(
+ constraintModes_[controls::ConstraintNormal].begin(),
+ constraint);
+ availableConstraintModes.push_back(controls::ConstraintNormal);
+ }
+
+ controls_[&controls::AeConstraintMode] = ControlInfo(availableConstraintModes);
+
+ return 0;
+}
+
+int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData)
+{
+ std::vector<ControlValue> availableExposureModes;
+
+ const YamlObject &yamlExposureModes = tuningData[controls::AeExposureMode.name()];
+ if (yamlExposureModes.isDictionary()) {
+ for (const auto &[modeName, modeValues] : yamlExposureModes.asDict()) {
+ if (AeExposureModeNameValueMap.find(modeName) ==
+ AeExposureModeNameValueMap.end()) {
+ LOG(AgcMeanLuminance, Warning)
+ << "Skipping unknown exposure mode '" << modeName << "'";
+ continue;
+ }
+
+ if (!modeValues.isDictionary()) {
+ LOG(AgcMeanLuminance, Error)
+ << "Invalid exposure mode '" << modeName << "'";
+ return -EINVAL;
+ }
+
+ std::vector<uint32_t> exposureTimes =
+ modeValues["exposureTime"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
+ std::vector<double> gains =
+ modeValues["gain"].getList<double>().value_or(std::vector<double>{});
+
+ if (exposureTimes.size() != gains.size()) {
+ LOG(AgcMeanLuminance, Error)
+ << "Exposure time and gain array sizes unequal";
+ return -EINVAL;
+ }
+
+ if (exposureTimes.empty()) {
+ LOG(AgcMeanLuminance, Error)
+ << "Exposure time and gain arrays are empty";
+ return -EINVAL;
+ }
+
+ std::vector<std::pair<utils::Duration, double>> stages;
+ for (unsigned int i = 0; i < exposureTimes.size(); i++) {
+ stages.push_back({
+ std::chrono::microseconds(exposureTimes[i]),
+ gains[i]
+ });
+ }
+
+ std::shared_ptr<ExposureModeHelper> helper =
+ std::make_shared<ExposureModeHelper>(stages);
+
+ exposureModeHelpers_[AeExposureModeNameValueMap.at(modeName)] = helper;
+ availableExposureModes.push_back(AeExposureModeNameValueMap.at(modeName));
+ }
+ }
+
+ /*
+ * If we don't have any exposure modes in the tuning data we create an
+ * ExposureModeHelper using an empty vector of stages. This will result
+ * in the ExposureModeHelper simply driving the exposure time as high as
+ * possible before touching gain.
+ */
+ if (availableExposureModes.empty()) {
+ int32_t exposureModeId = controls::ExposureNormal;
+ std::vector<std::pair<utils::Duration, double>> stages = { };
+
+ std::shared_ptr<ExposureModeHelper> helper =
+ std::make_shared<ExposureModeHelper>(stages);
+
+ exposureModeHelpers_[exposureModeId] = helper;
+ availableExposureModes.push_back(exposureModeId);
+ }
+
+ controls_[&controls::AeExposureMode] = ControlInfo(availableExposureModes);
+
+ return 0;
+}
+
+/**
+ * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls
+ * \param[in] tuningData the YamlObject representing the tuning data
+ *
+ * This function parses tuning data to build the list of allowed values for the
+ * AeConstraintMode and AeExposureMode controls. Those tuning data must provide
+ * the data in a specific format; the Agc algorithm's tuning data should contain
+ * a dictionary called AeConstraintMode containing per-mode setting dictionaries
+ * with the key being a value from \ref controls::AeConstraintModeNameValueMap.
+ * Each mode dict may contain either a "lower" or "upper" key or both, for
+ * example:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Agc:
+ * AeConstraintMode:
+ * ConstraintNormal:
+ * lower:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: 0.5
+ * ConstraintHighlight:
+ * lower:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: 0.5
+ * upper:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: 0.8
+ *
+ * \endcode
+ *
+ * For the AeExposureMode control the data should contain a dictionary called
+ * AeExposureMode containing per-mode setting dictionaries with the key being a
+ * value from \ref controls::AeExposureModeNameValueMap. Each mode dict should
+ * contain an array of exposure times with the key "exposureTime" and an array
+ * of gain values with the key "gain", in this format:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Agc:
+ * AeExposureMode:
+ * ExposureNormal:
+ * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ * ExposureShort:
+ * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ *
+ * \endcode
+ *
+ * \return 0 on success or a negative error code
+ */
+int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData)
+{
+ int ret;
+
+ parseRelativeLuminanceTarget(tuningData);
+
+ ret = parseConstraintModes(tuningData);
+ if (ret)
+ return ret;
+
+ return parseExposureModes(tuningData);
+}
+
+/**
+ * \brief Set the ExposureModeHelper limits for this class
+ * \param[in] minExposureTime Minimum exposure time to allow
+ * \param[in] maxExposureTime Maximum ewposure time to allow
+ * \param[in] minGain Minimum gain to allow
+ * \param[in] maxGain Maximum gain to allow
+ *
+ * This function calls \ref ExposureModeHelper::setLimits() for each
+ * ExposureModeHelper that has been created for this class.
+ */
+void AgcMeanLuminance::setLimits(utils::Duration minExposureTime,
+ utils::Duration maxExposureTime,
+ double minGain, double maxGain)
+{
+ for (auto &[id, helper] : exposureModeHelpers_)
+ helper->setLimits(minExposureTime, maxExposureTime, minGain, maxGain);
+}
+
+/**
+ * \fn AgcMeanLuminance::constraintModes()
+ * \brief Get the constraint modes that have been parsed from tuning data
+ */
+
+/**
+ * \fn AgcMeanLuminance::exposureModeHelpers()
+ * \brief Get the ExposureModeHelpers that have been parsed from tuning data
+ */
+
+/**
+ * \fn AgcMeanLuminance::controls()
+ * \brief Get the controls that have been generated after parsing tuning data
+ */
+
+/**
+ * \fn AgcMeanLuminance::estimateLuminance(const double gain)
+ * \brief Estimate the luminance of an image, adjusted by a given gain
+ * \param[in] gain The gain with which to adjust the luminance estimate
+ *
+ * This function estimates the average relative luminance of the frame that
+ * would be output by the sensor if an additional \a gain was applied. It is a
+ * pure virtual function because estimation of luminance is a hardware-specific
+ * operation, which depends wholly on the format of the stats that are delivered
+ * to libcamera from the ISP. Derived classes must override this function with
+ * one that calculates the normalised mean luminance value across the entire
+ * image.
+ *
+ * \return The normalised relative luminance of the image
+ */
+
+/**
+ * \brief Estimate the initial gain needed to achieve a relative luminance
+ * target
+ * \return The calculated initial gain
+ */
+double AgcMeanLuminance::estimateInitialGain() const
+{
+ double yTarget = relativeLuminanceTarget_;
+ double yGain = 1.0;
+
+ /*
+ * To account for non-linearity caused by saturation, the value needs to
+ * be estimated in an iterative process, as multiplying by a gain will
+ * not increase the relative luminance by the same factor if some image
+ * regions are saturated.
+ */
+ for (unsigned int i = 0; i < 8; i++) {
+ double yValue = estimateLuminance(yGain);
+ double extra_gain = std::min(10.0, yTarget / (yValue + .001));
+
+ yGain *= extra_gain;
+ LOG(AgcMeanLuminance, Debug) << "Y value: " << yValue
+ << ", Y target: " << yTarget
+ << ", gives gain " << yGain;
+
+ if (utils::abs_diff(extra_gain, 1.0) < 0.01)
+ break;
+ }
+
+ return yGain;
+}
+
+/**
+ * \brief Clamp gain within the bounds of a defined constraint
+ * \param[in] constraintModeIndex The index of the constraint to adhere to
+ * \param[in] hist A histogram over which to calculate inter-quantile means
+ * \param[in] gain The gain to clamp
+ *
+ * \return The gain clamped within the constraint bounds
+ */
+double AgcMeanLuminance::constraintClampGain(uint32_t constraintModeIndex,
+ const Histogram &hist,
+ double gain)
+{
+ std::vector<AgcConstraint> &constraints = constraintModes_[constraintModeIndex];
+ for (const AgcConstraint &constraint : constraints) {
+ double newGain = constraint.yTarget * hist.bins() /
+ hist.interQuantileMean(constraint.qLo, constraint.qHi);
+
+ if (constraint.bound == AgcConstraint::Bound::Lower &&
+ newGain > gain)
+ gain = newGain;
+
+ if (constraint.bound == AgcConstraint::Bound::Upper &&
+ newGain < gain)
+ gain = newGain;
+ }
+
+ return gain;
+}
+
+/**
+ * \brief Apply a filter on the exposure value to limit the speed of changes
+ * \param[in] exposureValue The target exposure from the AGC algorithm
+ *
+ * The speed of the filter is adaptive, and will produce the target quicker
+ * during startup, or when the target exposure is within 20% of the most recent
+ * filter output.
+ *
+ * \return The filtered exposure
+ */
+utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue)
+{
+ double speed = 0.2;
+
+ /* Adapt instantly if we are in startup phase. */
+ if (frameCount_ < kNumStartupFrames)
+ speed = 1.0;
+
+ /*
+ * If we are close to the desired result, go faster to avoid making
+ * multiple micro-adjustments.
+ * \todo Make this customisable?
+ */
+ if (filteredExposure_ < 1.2 * exposureValue &&
+ filteredExposure_ > 0.8 * exposureValue)
+ speed = sqrt(speed);
+
+ filteredExposure_ = speed * exposureValue +
+ filteredExposure_ * (1.0 - speed);
+
+ return filteredExposure_;
+}
+
+/**
+ * \brief Calculate the new exposure value and splut it between exposure time
+ * and gain
+ * \param[in] constraintModeIndex The index of the current constraint mode
+ * \param[in] exposureModeIndex The index of the current exposure mode
+ * \param[in] yHist A Histogram from the ISP statistics to use in constraining
+ * the calculated gain
+ * \param[in] effectiveExposureValue The EV applied to the frame from which the
+ * statistics in use derive
+ *
+ * Calculate a new exposure value to try to obtain the target. The calculated
+ * exposure value is filtered to prevent rapid changes from frame to frame, and
+ * divided into exposure time, analogue and digital gain.
+ *
+ * \return Tuple of exposure time, analogue gain, and digital gain
+ */
+std::tuple<utils::Duration, double, double>
+AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,
+ uint32_t exposureModeIndex,
+ const Histogram &yHist,
+ utils::Duration effectiveExposureValue)
+{
+ /*
+ * The pipeline handler should validate that we have received an allowed
+ * value for AeExposureMode.
+ */
+ std::shared_ptr<ExposureModeHelper> exposureModeHelper =
+ exposureModeHelpers_.at(exposureModeIndex);
+
+ if (effectiveExposureValue == 0s) {
+ LOG(AgcMeanLuminance, Error)
+ << "Effective exposure value is 0. This is a bug in AGC "
+ "and must be fixed for proper operation.";
+ /*
+ * Return an arbitrary exposure time > 0 to ensure regulation
+ * doesn't get stuck with 0 in case the sensor driver allows a
+ * min exposure of 0.
+ */
+ return exposureModeHelper->splitExposure(10ms);
+ }
+
+ double gain = estimateInitialGain();
+ gain = constraintClampGain(constraintModeIndex, yHist, gain);
+
+ /*
+ * We don't check whether we're already close to the target, because
+ * even if the effective exposure value is the same as the last frame's
+ * we could have switched to an exposure mode that would require a new
+ * pass through the splitExposure() function.
+ */
+
+ utils::Duration newExposureValue = effectiveExposureValue * gain;
+
+ /*
+ * We filter the exposure value to make sure changes are not too jarring
+ * from frame to frame.
+ */
+ newExposureValue = filterExposure(newExposureValue);
+
+ frameCount_++;
+ return exposureModeHelper->splitExposure(newExposureValue);
+}
+
+/**
+ * \fn AgcMeanLuminance::resetFrameCount()
+ * \brief Reset the frame counter
+ *
+ * This function resets the internal frame counter, which exists to help the
+ * algorithm decide whether it should respond instantly or not. The expectation
+ * is for derived classes to call this function before each camera start call in
+ * their configure() function.
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h
new file mode 100644
index 00000000..c41391cb
--- /dev/null
+++ b/src/ipa/libipa/agc_mean_luminance.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ agc_mean_luminance.h - Base class for mean luminance AGC algorithms
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include <libcamera/base/utils.h>
+
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+#include "exposure_mode_helper.h"
+#include "histogram.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+class AgcMeanLuminance
+{
+public:
+ AgcMeanLuminance();
+ virtual ~AgcMeanLuminance();
+
+ struct AgcConstraint {
+ enum class Bound {
+ Lower = 0,
+ Upper = 1
+ };
+ Bound bound;
+ double qLo;
+ double qHi;
+ double yTarget;
+ };
+
+ int parseTuningData(const YamlObject &tuningData);
+
+ void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime,
+ double minGain, double maxGain);
+
+ std::map<int32_t, std::vector<AgcConstraint>> constraintModes()
+ {
+ return constraintModes_;
+ }
+
+ std::map<int32_t, std::shared_ptr<ExposureModeHelper>> exposureModeHelpers()
+ {
+ return exposureModeHelpers_;
+ }
+
+ ControlInfoMap::Map controls()
+ {
+ return controls_;
+ }
+
+ std::tuple<utils::Duration, double, double>
+ calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex,
+ const Histogram &yHist, utils::Duration effectiveExposureValue);
+
+ void resetFrameCount()
+ {
+ frameCount_ = 0;
+ }
+
+private:
+ virtual double estimateLuminance(const double gain) const = 0;
+
+ void parseRelativeLuminanceTarget(const YamlObject &tuningData);
+ void parseConstraint(const YamlObject &modeDict, int32_t id);
+ int parseConstraintModes(const YamlObject &tuningData);
+ int parseExposureModes(const YamlObject &tuningData);
+ double estimateInitialGain() const;
+ double constraintClampGain(uint32_t constraintModeIndex,
+ const Histogram &hist,
+ double gain);
+ utils::Duration filterExposure(utils::Duration exposureValue);
+
+ uint64_t frameCount_;
+ utils::Duration filteredExposure_;
+ double relativeLuminanceTarget_;
+
+ std::map<int32_t, std::vector<AgcConstraint>> constraintModes_;
+ std::map<int32_t, std::shared_ptr<ExposureModeHelper>> exposureModeHelpers_;
+ ControlInfoMap::Map controls_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/algorithm.cpp b/src/ipa/libipa/algorithm.cpp
index bc1c29a6..201efdfd 100644
--- a/src/ipa/libipa/algorithm.cpp
+++ b/src/ipa/libipa/algorithm.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * algorithm.cpp - IPA control algorithm interface
+ * IPA control algorithm interface
*/
#include "algorithm.h"
diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h
index 987e3e4c..9a19dbd6 100644
--- a/src/ipa/libipa/algorithm.h
+++ b/src/ipa/libipa/algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * algorithm.h - ISP control algorithm interface
+ * ISP control algorithm interface
*/
#pragma once
diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp
new file mode 100644
index 00000000..214bac8b
--- /dev/null
+++ b/src/ipa/libipa/awb.cpp
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Generic AWB algorithms
+ */
+
+#include "awb.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+/**
+ * \file awb.h
+ * \brief Base classes for AWB algorithms
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Awb)
+
+namespace ipa {
+
+/**
+ * \class AwbResult
+ * \brief The result of an AWB calculation
+ *
+ * This class holds the result of an auto white balance calculation.
+ */
+
+/**
+ * \var AwbResult::gains
+ * \brief The calculated white balance gains
+ */
+
+/**
+ * \var AwbResult::colourTemperature
+ * \brief The calculated colour temperature in Kelvin
+ */
+
+/**
+ * \class AwbStats
+ * \brief An abstraction class wrapping hardware-specific AWB statistics
+ *
+ * IPA modules using an AWB algorithm based on the AwbAlgorithm class need to
+ * implement this class to give the algorithm access to the hardware-specific
+ * statistics data.
+ */
+
+/**
+ * \fn AwbStats::computeColourError()
+ * \brief Compute an error value for when the given gains would be applied
+ * \param[in] gains The gains to apply
+ *
+ * Compute an error value (non-greyness) assuming the given \a gains would be
+ * applied. To keep the actual implementations computationally inexpensive,
+ * the squared colour error shall be returned.
+ *
+ * If the AWB statistics provide multiple zones, the average of the individual
+ * squared errors shall be returned. Averaging/normalizing is necessary so that
+ * the numeric dimensions are the same on all hardware platforms.
+ *
+ * \return The computed error value
+ */
+
+/**
+ * \fn AwbStats::rgbMeans()
+ * \brief Get RGB means of the statistics
+ *
+ * Fetch the RGB means from the statistics. The values of each channel are
+ * dimensionless and only the ratios are used for further calculations. This is
+ * used by the simple grey world model to calculate the gains to apply.
+ *
+ * \return The RGB means
+ */
+
+/**
+ * \class AwbAlgorithm
+ * \brief A base class for auto white balance algorithms
+ *
+ * This class is a base class for auto white balance algorithms. It defines an
+ * interface for the algorithms to implement, and is used by the IPAs to
+ * interact with the concrete implementation.
+ */
+
+/**
+ * \fn AwbAlgorithm::init()
+ * \brief Initialize the algorithm with the given tuning data
+ * \param[in] tuningData The tuning data to use for the algorithm
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+
+/**
+ * \fn AwbAlgorithm::calculateAwb()
+ * \brief Calculate AWB data from the given statistics
+ * \param[in] stats The statistics to use for the calculation
+ * \param[in] lux The lux value of the scene
+ *
+ * Calculate an AwbResult object from the given statistics and lux value. A \a
+ * lux value of 0 means it is unknown or invalid and the algorithm shall ignore
+ * it.
+ *
+ * \return The AWB result
+ */
+
+/**
+ * \fn AwbAlgorithm::gainsFromColourTemperature()
+ * \brief Compute white balance gains from a colour temperature
+ * \param[in] colourTemperature The colour temperature in Kelvin
+ *
+ * Compute the white balance gains from a \a colourTemperature. This function
+ * does not take any statistics into account. It is used to compute the colour
+ * gains when the user manually specifies a colour temperature.
+ *
+ * \return The colour gains or std::nullopt if the conversion is not possible
+ */
+
+/**
+ * \fn AwbAlgorithm::controls()
+ * \brief Get the controls info map for this algorithm
+ *
+ * \return The controls info map
+ */
+
+/**
+ * \fn AwbAlgorithm::handleControls()
+ * \param[in] controls The controls to handle
+ * \brief Handle the controls supplied in a request
+ */
+
+/**
+ * \brief Parse the mode configurations from the tuning data
+ * \param[in] tuningData the YamlObject representing the tuning data
+ * \param[in] def The default value for the AwbMode control
+ *
+ * Utility function to parse the tuning data for an AwbMode entry and read all
+ * provided modes. It adds controls::AwbMode to AwbAlgorithm::controls_ and
+ * populates AwbAlgorithm::modes_. For a list of possible modes see \ref
+ * controls::AwbModeEnum.
+ *
+ * Each mode entry must contain a "lo" and "hi" key to specify the lower and
+ * upper colour temperature of that mode. For example:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Awb:
+ * AwbMode:
+ * AwbAuto:
+ * lo: 2500
+ * hi: 8000
+ * AwbIncandescent:
+ * lo: 2500
+ * hi: 3000
+ * ...
+ * \endcode
+ *
+ * If \a def is supplied but not contained in the the \a tuningData, -EINVAL is
+ * returned.
+ *
+ * \sa controls::AwbModeEnum
+ * \return Zero on success, negative error code otherwise
+ */
+int AwbAlgorithm::parseModeConfigs(const YamlObject &tuningData,
+ const ControlValue &def)
+{
+ std::vector<ControlValue> availableModes;
+
+ const YamlObject &yamlModes = tuningData[controls::AwbMode.name()];
+ if (!yamlModes.isDictionary()) {
+ LOG(Awb, Error)
+ << "AwbModes must be a dictionary.";
+ return -EINVAL;
+ }
+
+ for (const auto &[modeName, modeDict] : yamlModes.asDict()) {
+ if (controls::AwbModeNameValueMap.find(modeName) ==
+ controls::AwbModeNameValueMap.end()) {
+ LOG(Awb, Warning)
+ << "Skipping unknown AWB mode '"
+ << modeName << "'";
+ continue;
+ }
+
+ if (!modeDict.isDictionary()) {
+ LOG(Awb, Error)
+ << "Invalid AWB mode '" << modeName << "'";
+ return -EINVAL;
+ }
+
+ const auto &modeValue = static_cast<controls::AwbModeEnum>(
+ controls::AwbModeNameValueMap.at(modeName));
+
+ ModeConfig &config = modes_[modeValue];
+
+ auto hi = modeDict["hi"].get<double>();
+ if (!hi) {
+ LOG(Awb, Error) << "Failed to read hi param of mode "
+ << modeName;
+ return -EINVAL;
+ }
+ config.ctHi = *hi;
+
+ auto lo = modeDict["lo"].get<double>();
+ if (!lo) {
+ LOG(Awb, Error) << "Failed to read low param of mode "
+ << modeName;
+ return -EINVAL;
+ }
+ config.ctLo = *lo;
+
+ availableModes.push_back(modeValue);
+ }
+
+ if (modes_.empty()) {
+ LOG(Awb, Error) << "No AWB modes configured";
+ return -EINVAL;
+ }
+
+ if (!def.isNone() &&
+ modes_.find(def.get<controls::AwbModeEnum>()) == modes_.end()) {
+ const auto &names = controls::AwbMode.enumerators();
+ LOG(Awb, Error) << names.at(def.get<controls::AwbModeEnum>())
+ << " mode is missing in the configuration.";
+ return -EINVAL;
+ }
+
+ controls_[&controls::AwbMode] = ControlInfo(availableModes, def);
+
+ return 0;
+}
+
+/**
+ * \class AwbAlgorithm::ModeConfig
+ * \brief Holds the configuration of a single AWB mode
+ *
+ * AWB modes limit the regulation of the AWB algorithm to a specific range of
+ * colour temperatures.
+ */
+
+/**
+ * \var AwbAlgorithm::ModeConfig::ctLo
+ * \brief The lowest valid colour temperature of that mode
+ */
+
+/**
+ * \var AwbAlgorithm::ModeConfig::ctHi
+ * \brief The highest valid colour temperature of that mode
+ */
+
+/**
+ * \var AwbAlgorithm::controls_
+ * \brief Controls info map for the controls provided by the algorithm
+ */
+
+/**
+ * \var AwbAlgorithm::modes_
+ * \brief Map of all configured modes
+ * \sa AwbAlgorithm::parseModeConfigs
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h
new file mode 100644
index 00000000..f4a86038
--- /dev/null
+++ b/src/ipa/libipa/awb.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Generic AWB algorithms
+ */
+
+#pragma once
+
+#include <map>
+#include <optional>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/vector.h"
+#include "libcamera/internal/yaml_parser.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+struct AwbResult {
+ RGB<double> gains;
+ double colourTemperature;
+};
+
+struct AwbStats {
+ virtual double computeColourError(const RGB<double> &gains) const = 0;
+ virtual RGB<double> rgbMeans() const = 0;
+
+protected:
+ ~AwbStats() = default;
+};
+
+class AwbAlgorithm
+{
+public:
+ virtual ~AwbAlgorithm() = default;
+
+ virtual int init(const YamlObject &tuningData) = 0;
+ virtual AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) = 0;
+ virtual std::optional<RGB<double>> gainsFromColourTemperature(double colourTemperature) = 0;
+
+ const ControlInfoMap::Map &controls() const
+ {
+ return controls_;
+ }
+
+ virtual void handleControls([[maybe_unused]] const ControlList &controls) {}
+
+protected:
+ int parseModeConfigs(const YamlObject &tuningData,
+ const ControlValue &def = {});
+
+ struct ModeConfig {
+ double ctHi;
+ double ctLo;
+ };
+
+ ControlInfoMap::Map controls_;
+ std::map<controls::AwbModeEnum, AwbAlgorithm::ModeConfig> modes_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp
new file mode 100644
index 00000000..d2bcbd83
--- /dev/null
+++ b/src/ipa/libipa/awb_bayes.cpp
@@ -0,0 +1,505 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Raspberry Pi Ltd
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Implementation of a bayesian AWB algorithm
+ */
+
+#include "awb_bayes.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <map>
+#include <ostream>
+#include <vector>
+
+#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
+#include "colours.h"
+
+/**
+ * \file awb_bayes.h
+ * \brief Implementation of bayesian auto white balance algorithm
+ *
+ * This implementation is based on the initial implementation done by
+ * RaspberryPi.
+ *
+ * \todo Documentation
+ *
+ * \todo Not all the features implemented by RaspberryPi were ported over to
+ * this algorithm because they either rely on hardware features not generally
+ * available or were considered not important enough at the moment.
+ *
+ * The following parts are not implemented:
+ *
+ * - min_pixels: minimum proportion of pixels counted within AWB region for it
+ * to be "useful"
+ * - min_g: minimum G value of those pixels, to be regarded a "useful"
+ * - min_regions: number of AWB regions that must be "useful" in order to do the
+ * AWB calculation
+ * - deltaLimit: clamp on colour error term (so as not to penalize non-grey
+ * excessively)
+ * - bias_proportion: The biasProportion parameter adds a small proportion of
+ * the counted pixels to a region biased to the biasCT colour temperature.
+ * A typical value for biasProportion would be between 0.05 to 0.1.
+ * - bias_ct: CT target for the search bias
+ * - sensitivityR: red sensitivity ratio (set to canonical sensor's R/G divided
+ * by this sensor's R/G)
+ * - sensitivityB: blue sensitivity ratio (set to canonical sensor's B/G divided
+ * by this sensor's B/G)
+ */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Awb)
+
+namespace {
+
+template<typename T>
+class LimitsRecorder
+{
+public:
+ LimitsRecorder()
+ : min_(std::numeric_limits<T>::max()),
+ max_(std::numeric_limits<T>::min())
+ {
+ }
+
+ void record(const T &value)
+ {
+ min_ = std::min(min_, value);
+ max_ = std::max(max_, value);
+ }
+
+ const T &min() const { return min_; }
+ const T &max() const { return max_; }
+
+private:
+ T min_;
+ T max_;
+};
+
+#ifndef __DOXYGEN__
+template<typename T>
+std::ostream &operator<<(std::ostream &out, const LimitsRecorder<T> &v)
+{
+ out << "[ " << v.min() << ", " << v.max() << " ]";
+ return out;
+}
+#endif
+
+} /* namespace */
+
+namespace ipa {
+
+/**
+ * \brief Step size control for CT search
+ */
+constexpr double kSearchStep = 0.2;
+
+/**
+ * \copydoc libcamera::ipa::Interpolator::interpolate()
+ */
+template<>
+void Interpolator<Pwl>::interpolate(const Pwl &a, const Pwl &b, Pwl &dest, double lambda)
+{
+ dest = Pwl::combine(a, b,
+ [=](double /*x*/, double y0, double y1) -> double {
+ return y0 * (1.0 - lambda) + y1 * lambda;
+ });
+}
+
+/**
+ * \class AwbBayes
+ * \brief Implementation of a bayesian auto white balance algorithm
+ *
+ * In a bayesian AWB algorithm the auto white balance estimation is improved by
+ * taking the likelihood of a given lightsource based on the estimated lux level
+ * into account. E.g. If it is very bright we can assume that we are outside and
+ * that colour temperatures around 6500 are preferred.
+ *
+ * The second part of this algorithm is the search for the most likely colour
+ * temperature. It is implemented in AwbBayes::coarseSearch() and in
+ * AwbBayes::fineSearch(). The search works very well without prior likelihoods
+ * and therefore the algorithm itself provides very good results even without
+ * prior likelihoods.
+ */
+
+/**
+ * \var AwbBayes::transversePos_
+ * \brief How far to wander off CT curve towards "more purple"
+ */
+
+/**
+ * \var AwbBayes::transverseNeg_
+ * \brief How far to wander off CT curve towards "more green"
+ */
+
+/**
+ * \var AwbBayes::currentMode_
+ * \brief The currently selected mode
+ */
+
+int AwbBayes::init(const YamlObject &tuningData)
+{
+ int ret = colourGainCurve_.readYaml(tuningData["colourGains"], "ct", "gains");
+ if (ret) {
+ LOG(Awb, Error)
+ << "Failed to parse 'colourGains' "
+ << "parameter from tuning file";
+ return ret;
+ }
+
+ ctR_.clear();
+ ctB_.clear();
+ for (const auto &[ct, g] : colourGainCurve_.data()) {
+ ctR_.append(ct, 1.0 / g[0]);
+ ctB_.append(ct, 1.0 / g[1]);
+ }
+
+ /* We will want the inverse functions of these too. */
+ ctRInverse_ = ctR_.inverse().first;
+ ctBInverse_ = ctB_.inverse().first;
+
+ ret = readPriors(tuningData);
+ if (ret) {
+ LOG(Awb, Error) << "Failed to read priors";
+ return ret;
+ }
+
+ ret = parseModeConfigs(tuningData, controls::AwbAuto);
+ if (ret) {
+ LOG(Awb, Error)
+ << "Failed to parse mode parameter from tuning file";
+ return ret;
+ }
+ currentMode_ = &modes_[controls::AwbAuto];
+
+ transversePos_ = tuningData["transversePos"].get<double>(0.01);
+ transverseNeg_ = tuningData["transverseNeg"].get<double>(0.01);
+ if (transversePos_ <= 0 || transverseNeg_ <= 0) {
+ LOG(Awb, Error) << "AwbConfig: transversePos/Neg must be > 0";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int AwbBayes::readPriors(const YamlObject &tuningData)
+{
+ const auto &priorsList = tuningData["priors"];
+ std::map<uint32_t, Pwl> priors;
+
+ if (!priorsList) {
+ LOG(Awb, Error) << "No priors specified";
+ return -EINVAL;
+ }
+
+ for (const auto &p : priorsList.asList()) {
+ if (!p.contains("lux")) {
+ LOG(Awb, Error) << "Missing lux value";
+ return -EINVAL;
+ }
+
+ uint32_t lux = p["lux"].get<uint32_t>(0);
+ if (priors.count(lux)) {
+ LOG(Awb, Error) << "Duplicate prior for lux value " << lux;
+ return -EINVAL;
+ }
+
+ std::vector<uint32_t> temperatures =
+ p["ct"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
+ std::vector<double> probabilities =
+ p["probability"].getList<double>().value_or(std::vector<double>{});
+
+ if (temperatures.size() != probabilities.size()) {
+ LOG(Awb, Error)
+ << "Ct and probability array sizes are unequal";
+ return -EINVAL;
+ }
+
+ if (temperatures.empty()) {
+ LOG(Awb, Error)
+ << "Ct and probability arrays are empty";
+ return -EINVAL;
+ }
+
+ std::map<int, double> ctToProbability;
+ for (unsigned int i = 0; i < temperatures.size(); i++) {
+ int t = temperatures[i];
+ if (ctToProbability.count(t)) {
+ LOG(Awb, Error) << "Duplicate ct value";
+ return -EINVAL;
+ }
+
+ ctToProbability[t] = probabilities[i];
+ }
+
+ auto &pwl = priors[lux];
+ for (const auto &[ct, prob] : ctToProbability) {
+ if (prob < 1e-6) {
+ LOG(Awb, Error) << "Prior probability must be larger than 1e-6";
+ return -EINVAL;
+ }
+ pwl.append(ct, prob);
+ }
+ }
+
+ if (priors.empty()) {
+ LOG(Awb, Error) << "No priors specified";
+ return -EINVAL;
+ }
+
+ priors_.setData(std::move(priors));
+
+ return 0;
+}
+
+void AwbBayes::handleControls(const ControlList &controls)
+{
+ auto mode = controls.get(controls::AwbMode);
+ if (mode) {
+ auto it = modes_.find(static_cast<controls::AwbModeEnum>(*mode));
+ if (it != modes_.end())
+ currentMode_ = &it->second;
+ else
+ LOG(Awb, Error) << "Unsupported AWB mode " << *mode;
+ }
+}
+
+std::optional<RGB<double>> AwbBayes::gainsFromColourTemperature(double colourTemperature)
+{
+ /*
+ * \todo In the RaspberryPi code, the ct curve was interpolated in
+ * the white point space (1/x) not in gains space. This feels counter
+ * intuitive, as the gains are in linear space. But I can't prove it.
+ */
+ const auto &gains = colourGainCurve_.getInterpolated(colourTemperature);
+ return RGB<double>{ { gains[0], 1.0, gains[1] } };
+}
+
+AwbResult AwbBayes::calculateAwb(const AwbStats &stats, unsigned int lux)
+{
+ ipa::Pwl prior;
+ if (lux > 0) {
+ prior = priors_.getInterpolated(lux);
+ prior.map([](double x, double y) {
+ LOG(Awb, Debug) << "(" << x << "," << y << ")";
+ });
+ } else {
+ prior.append(0, 1.0);
+ }
+
+ double t = coarseSearch(prior, stats);
+ double r = ctR_.eval(t);
+ double b = ctB_.eval(t);
+ LOG(Awb, Debug)
+ << "After coarse search: r " << r << " b " << b << " (gains r "
+ << 1 / r << " b " << 1 / b << ")";
+
+ /*
+ * Original comment from RaspberryPi:
+ * 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 transversely 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, stats);
+ LOG(Awb, Debug)
+ << "After fine search: r " << r << " b " << b << " (gains r "
+ << 1 / r << " b " << 1 / b << ")";
+
+ return { { { 1.0 / r, 1.0, 1.0 / b } }, t };
+}
+
+double AwbBayes::coarseSearch(const ipa::Pwl &prior, const AwbStats &stats) const
+{
+ std::vector<Pwl::Point> points;
+ size_t bestPoint = 0;
+ double t = currentMode_->ctLo;
+ int spanR = -1;
+ int spanB = -1;
+ LimitsRecorder<double> errorLimits;
+ LimitsRecorder<double> priorLogLikelihoodLimits;
+
+ /* Step down the CT curve evaluating log likelihood. */
+ while (true) {
+ double r = ctR_.eval(t, &spanR);
+ double b = ctB_.eval(t, &spanB);
+ RGB<double> gains({ 1 / r, 1.0, 1 / b });
+ double delta2Sum = stats.computeColourError(gains);
+ double priorLogLikelihood = log(prior.eval(prior.domain().clamp(t)));
+ double finalLogLikelihood = delta2Sum - priorLogLikelihood;
+
+ errorLimits.record(delta2Sum);
+ priorLogLikelihoodLimits.record(priorLogLikelihood);
+
+ points.push_back({ { t, finalLogLikelihood } });
+ if (points.back().y() < points[bestPoint].y())
+ bestPoint = points.size() - 1;
+
+ if (t == currentMode_->ctHi)
+ break;
+
+ /*
+ * Ensure even steps along the r/b curve by scaling them by the
+ * current t.
+ */
+ t = std::min(t + t / 10 * kSearchStep, currentMode_->ctHi);
+ }
+
+ t = points[bestPoint].x();
+ LOG(Awb, Debug) << "Coarse search found CT " << t
+ << " error limits:" << errorLimits
+ << " prior log likelihood limits:" << priorLogLikelihoodLimits;
+
+ /*
+ * We have the best point of the search, but refine it with a quadratic
+ * interpolation around its neighbors.
+ */
+ if (points.size() > 2) {
+ bestPoint = std::clamp(bestPoint, std::size_t{ 1 }, points.size() - 2);
+ t = interpolateQuadratic(points[bestPoint - 1],
+ points[bestPoint],
+ points[bestPoint + 1]);
+ LOG(Awb, Debug)
+ << "After quadratic refinement, coarse search has CT "
+ << t;
+ }
+
+ return t;
+}
+
+void AwbBayes::fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior, const AwbStats &stats) const
+{
+ int spanR = -1;
+ int spanB = -1;
+ double step = t / 10 * kSearchStep * 0.1;
+ int nsteps = 5;
+ ctR_.eval(t, &spanR);
+ ctB_.eval(t, &spanB);
+ double rDiff = ctR_.eval(t + nsteps * step, &spanR) -
+ ctR_.eval(t - nsteps * step, &spanR);
+ double bDiff = ctB_.eval(t + nsteps * step, &spanB) -
+ ctB_.eval(t - nsteps * step, &spanB);
+ Pwl::Point transverse({ bDiff, -rDiff });
+ if (transverse.length2() < 1e-6)
+ return;
+ /*
+ * transverse is a unit vector orthogonal to the b vs. r function
+ * (pointing outwards with r and b increasing)
+ */
+ transverse = transverse / transverse.length();
+ double bestLogLikelihood = 0;
+ double bestT = 0;
+ Pwl::Point bestRB(0);
+ double transverseRange = transverseNeg_ + transversePos_;
+ const int maxNumDeltas = 12;
+ LimitsRecorder<double> errorLimits;
+ LimitsRecorder<double> priorLogLikelihoodLimits;
+
+
+ /* a transverse step approximately every 0.01 r/b units */
+ int numDeltas = floor(transverseRange * 100 + 0.5) + 1;
+ numDeltas = std::clamp(numDeltas, 3, maxNumDeltas);
+
+ /*
+ * 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 =
+ log(prior.eval(prior.domain().clamp(tTest)));
+ priorLogLikelihoodLimits.record(priorLogLikelihood);
+ Pwl::Point rbStart{ { ctR_.eval(tTest, &spanR),
+ ctB_.eval(tTest, &spanB) } };
+ Pwl::Point samples[maxNumDeltas];
+ int bestPoint = 0;
+
+ /*
+ * Sample numDeltas points transversely *off* the CT curve
+ * in the range [-transverseNeg, transversePos].
+ * The x values of a sample contains the distance and the y
+ * value contains the corresponding log likelihood.
+ */
+ double transverseStep = transverseRange / (numDeltas - 1);
+ for (int j = 0; j < numDeltas; j++) {
+ auto &p = samples[j];
+ p.x() = -transverseNeg_ + transverseStep * j;
+ Pwl::Point rbTest = rbStart + transverse * p.x();
+ RGB<double> gains({ 1 / rbTest[0], 1.0, 1 / rbTest[1] });
+ double delta2Sum = stats.computeColourError(gains);
+ errorLimits.record(delta2Sum);
+ p.y() = delta2Sum - priorLogLikelihood;
+
+ if (p.y() < samples[bestPoint].y())
+ bestPoint = j;
+ }
+
+ /*
+ * We have all samples transversely across the CT curve,
+ * now let's do a quadratic interpolation for the best result.
+ */
+ bestPoint = std::clamp(bestPoint, 1, numDeltas - 2);
+ double bestOffset = interpolateQuadratic(samples[bestPoint - 1],
+ samples[bestPoint],
+ samples[bestPoint + 1]);
+ Pwl::Point rbTest = rbStart + transverse * bestOffset;
+ RGB<double> gains({ 1 / rbTest[0], 1.0, 1 / rbTest[1] });
+ double delta2Sum = stats.computeColourError(gains);
+ errorLimits.record(delta2Sum);
+ double finalLogLikelihood = delta2Sum - priorLogLikelihood;
+
+ if (bestT == 0 || finalLogLikelihood < bestLogLikelihood) {
+ bestLogLikelihood = finalLogLikelihood;
+ bestT = tTest;
+ bestRB = rbTest;
+ }
+ }
+
+ t = bestT;
+ r = bestRB[0];
+ b = bestRB[1];
+ LOG(Awb, Debug)
+ << "Fine search found t " << t << " r " << r << " b " << b
+ << " error limits: " << errorLimits
+ << " prior log likelihood limits: " << priorLogLikelihoodLimits;
+}
+
+/**
+ * \brief Find extremum of function
+ * \param[in] a First point
+ * \param[in] b Second point
+ * \param[in] c Third point
+ *
+ * Given 3 points on a curve, find the extremum of the function in that interval
+ * by fitting a quadratic.
+ *
+ * \return The x value of the extremum clamped to the interval [a.x(), c.x()]
+ */
+double AwbBayes::interpolateQuadratic(Pwl::Point const &a, Pwl::Point const &b,
+ Pwl::Point const &c) const
+{
+ const double eps = 1e-3;
+ Pwl::Point ca = c - a;
+ Pwl::Point ba = b - a;
+ double denominator = 2 * (ba.y() * ca.x() - ca.y() * ba.x());
+ if (std::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());
+}
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h
new file mode 100644
index 00000000..47ef3cce
--- /dev/null
+++ b/src/ipa/libipa/awb_bayes.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Base class for bayes AWB algorithms
+ */
+
+#pragma once
+
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/vector.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "awb.h"
+#include "interpolator.h"
+#include "pwl.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+class AwbBayes : public AwbAlgorithm
+{
+public:
+ AwbBayes() = default;
+
+ int init(const YamlObject &tuningData) override;
+ AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override;
+ std::optional<RGB<double>> gainsFromColourTemperature(double temperatureK) override;
+ void handleControls(const ControlList &controls) override;
+
+private:
+ int readPriors(const YamlObject &tuningData);
+
+ void fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior,
+ const AwbStats &stats) const;
+ double coarseSearch(const ipa::Pwl &prior, const AwbStats &stats) const;
+ double interpolateQuadratic(ipa::Pwl::Point const &a,
+ ipa::Pwl::Point const &b,
+ ipa::Pwl::Point const &c) const;
+
+ Interpolator<Pwl> priors_;
+ Interpolator<Vector<double, 2>> colourGainCurve_;
+
+ ipa::Pwl ctR_;
+ ipa::Pwl ctB_;
+ ipa::Pwl ctRInverse_;
+ ipa::Pwl ctBInverse_;
+
+ double transversePos_;
+ double transverseNeg_;
+
+ ModeConfig *currentMode_ = nullptr;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp
new file mode 100644
index 00000000..d252edb2
--- /dev/null
+++ b/src/ipa/libipa/awb_grey.cpp
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Implementation of grey world AWB algorithm
+ */
+
+#include "awb_grey.h"
+
+#include <algorithm>
+
+#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
+#include "colours.h"
+
+using namespace libcamera::controls;
+
+/**
+ * \file awb_grey.h
+ * \brief Implementation of a grey world AWB algorithm
+ */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Awb)
+namespace ipa {
+
+/**
+ * \class AwbGrey
+ * \brief A Grey world auto white balance algorithm
+ */
+
+/**
+ * \brief Initialize the algorithm with the given tuning data
+ * \param[in] tuningData The tuning data for the algorithm
+ *
+ * Load the colour temperature curve from the tuning data. If there is no tuning
+ * data available, continue with a warning. Manual colour temperature will not
+ * work in that case.
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+int AwbGrey::init(const YamlObject &tuningData)
+{
+ Interpolator<Vector<double, 2>> gains;
+ int ret = gains.readYaml(tuningData["colourGains"], "ct", "gains");
+ if (ret < 0)
+ LOG(Awb, Warning)
+ << "Failed to parse 'colourGains' "
+ << "parameter from tuning file; "
+ << "manual colour temperature will not work properly";
+ else
+ colourGainCurve_ = gains;
+
+ return 0;
+}
+
+/**
+ * \brief Calculate AWB data from the given statistics
+ * \param[in] stats The statistics to use for the calculation
+ * \param[in] lux The lux value of the scene
+ *
+ * The colour temperature is estimated based on the colours::estimateCCT()
+ * function. The gains are calculated purely based on the RGB means provided by
+ * the \a stats. The colour temperature is not taken into account when
+ * calculating the gains.
+ *
+ * The \a lux parameter is not used in this algorithm.
+ *
+ * \return The AWB result
+ */
+AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] unsigned int lux)
+{
+ AwbResult result;
+ auto means = stats.rgbMeans();
+ result.colourTemperature = estimateCCT(means);
+
+ /*
+ * Estimate the red and blue gains to apply in a grey world. The green
+ * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the
+ * divisor to a minimum value of 1.0.
+ */
+ result.gains.r() = means.g() / std::max(means.r(), 1.0);
+ result.gains.g() = 1.0;
+ result.gains.b() = means.g() / std::max(means.b(), 1.0);
+ return result;
+}
+
+/**
+ * \brief Compute white balance gains from a colour temperature
+ * \param[in] colourTemperature The colour temperature in Kelvin
+ *
+ * Compute the white balance gains from a \a colourTemperature. This function
+ * does not take any statistics into account. It simply interpolates the colour
+ * gains configured in the colour temperature curve.
+ *
+ * \return The colour gains if a colour temperature curve is available,
+ * [1, 1, 1] otherwise.
+ */
+std::optional<RGB<double>> AwbGrey::gainsFromColourTemperature(double colourTemperature)
+{
+ if (!colourGainCurve_) {
+ LOG(Awb, Error) << "No gains defined";
+ return std::nullopt;
+ }
+
+ auto gains = colourGainCurve_->getInterpolated(colourTemperature);
+ return RGB<double>{ { gains[0], 1.0, gains[1] } };
+}
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h
new file mode 100644
index 00000000..f82a368d
--- /dev/null
+++ b/src/ipa/libipa/awb_grey.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * AWB grey world algorithm
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "libcamera/internal/vector.h"
+
+#include "awb.h"
+#include "interpolator.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+class AwbGrey : public AwbAlgorithm
+{
+public:
+ AwbGrey() = default;
+
+ int init(const YamlObject &tuningData) override;
+ AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override;
+ std::optional<RGB<double>> gainsFromColourTemperature(double colourTemperature) override;
+
+private:
+ std::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
index f0ecc383..7c66cd57 100644
--- a/src/ipa/libipa/camera_sensor_helper.cpp
+++ b/src/ipa/libipa/camera_sensor_helper.cpp
@@ -2,12 +2,13 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_sensor_helper.cpp - Helper class that performs sensor-specific
+ * Helper class that performs sensor-specific
* parameter computations
*/
#include "camera_sensor_helper.h"
#include <cmath>
+#include <limits>
#include <libcamera/base/log.h>
@@ -40,6 +41,7 @@ namespace ipa {
*/
/**
+ * \fn CameraSensorHelper::CameraSensorHelper()
* \brief Construct a CameraSensorHelper instance
*
* CameraSensorHelper derived class instances shall never be constructed
@@ -48,6 +50,33 @@ namespace ipa {
*/
/**
+ * \fn CameraSensorHelper::blackLevel()
+ * \brief Fetch the black level of the sensor
+ *
+ * This function returns the black level of the sensor scaled to a 16bit pixel
+ * width. If it is unknown an empty optional is returned.
+ *
+ * \todo Fill the blanks and add pedestal values for all supported sensors. Once
+ * done, drop the std::optional<>.
+ *
+ * Black levels are typically the result of the following phenomena:
+ * - Pedestal added by the sensor to pixel values. They are typically fixed,
+ * sometimes programmable and should be reported in datasheets (but
+ * documentation is not always available).
+ * - Dark currents and other physical effects that add charge to pixels in the
+ * absence of light. Those can depend on the integration time and the sensor
+ * die temperature, and their contribution to pixel values depend on the
+ * sensor gains.
+ *
+ * The pedestal is usually the value with the biggest contribution to the
+ * overall black level. In most cases it is either known before or in rare cases
+ * (there is not a single driver with such a control in the linux kernel) can be
+ * queried from the sensor. This function provides that fixed, known value.
+ *
+ * \return The black level of the sensor, or std::nullopt if not known
+ */
+
+/**
* \brief Compute gain code from the analogue gain absolute value
* \param[in] gain The real gain to pass
*
@@ -58,21 +87,16 @@ namespace ipa {
*/
uint32_t CameraSensorHelper::gainCode(double gain) const
{
- const AnalogueGainConstants &k = gainConstants_;
-
- switch (gainType_) {
- case AnalogueGainLinear:
- ASSERT(k.linear.m0 == 0 || k.linear.m1 == 0);
-
- return (k.linear.c0 - k.linear.c1 * gain) /
- (k.linear.m1 * gain - k.linear.m0);
+ if (auto *l = std::get_if<AnalogueGainLinear>(&gain_)) {
+ ASSERT(l->m0 == 0 || l->m1 == 0);
- case AnalogueGainExponential:
- ASSERT(k.exp.a != 0 && k.exp.m != 0);
+ return (l->c0 - l->c1 * gain) /
+ (l->m1 * gain - l->m0);
+ } else if (auto *e = std::get_if<AnalogueGainExp>(&gain_)) {
+ ASSERT(e->a != 0 && e->m != 0);
- return std::log2(gain / k.exp.a) / k.exp.m;
-
- default:
+ return std::log2(gain / e->a) / e->m;
+ } else {
ASSERT(false);
return 0;
}
@@ -90,38 +114,26 @@ uint32_t CameraSensorHelper::gainCode(double gain) const
*/
double CameraSensorHelper::gain(uint32_t gainCode) const
{
- const AnalogueGainConstants &k = gainConstants_;
double gain = static_cast<double>(gainCode);
- switch (gainType_) {
- case AnalogueGainLinear:
- ASSERT(k.linear.m0 == 0 || k.linear.m1 == 0);
-
- return (k.linear.m0 * gain + k.linear.c0) /
- (k.linear.m1 * gain + k.linear.c1);
+ if (auto *l = std::get_if<AnalogueGainLinear>(&gain_)) {
+ ASSERT(l->m0 == 0 || l->m1 == 0);
- case AnalogueGainExponential:
- ASSERT(k.exp.a != 0 && k.exp.m != 0);
+ return (l->m0 * gain + l->c0) /
+ (l->m1 * gain + l->c1);
+ } else if (auto *e = std::get_if<AnalogueGainExp>(&gain_)) {
+ ASSERT(e->a != 0 && e->m != 0);
- return k.exp.a * std::exp2(k.exp.m * gain);
-
- default:
+ return e->a * std::exp2(e->m * gain);
+ } else {
ASSERT(false);
return 0.0;
}
}
/**
- * \enum CameraSensorHelper::AnalogueGainType
- * \brief The gain calculation modes as defined by the MIPI CCS
- *
- * Describes the image sensor analogue gain capabilities.
- * Two modes are possible, depending on the sensor: Linear and Exponential.
- */
-
-/**
- * \var CameraSensorHelper::AnalogueGainLinear
- * \brief Gain is computed using linear gain estimation
+ * \struct CameraSensorHelper::AnalogueGainLinear
+ * \brief Analogue gain constants for the linear gain model
*
* The relationship between the integer gain parameter and the resulting gain
* multiplier is given by the following equation:
@@ -136,11 +148,27 @@ double CameraSensorHelper::gain(uint32_t gainCode) const
* The full Gain equation therefore reduces to either:
*
* \f$gain=\frac{c0}{m1x+c1}\f$ or \f$\frac{m0x+c0}{c1}\f$
+ *
+ * \var CameraSensorHelper::AnalogueGainLinear::m0
+ * \brief Constant used in the linear gain coding/decoding
+ *
+ * \note Either m0 or m1 shall be zero.
+ *
+ * \var CameraSensorHelper::AnalogueGainLinear::c0
+ * \brief Constant used in the linear gain coding/decoding
+ *
+ * \var CameraSensorHelper::AnalogueGainLinear::m1
+ * \brief Constant used in the linear gain coding/decoding
+ *
+ * \note Either m0 or m1 shall be zero.
+ *
+ * \var CameraSensorHelper::AnalogueGainLinear::c1
+ * \brief Constant used in the linear gain coding/decoding
*/
/**
- * \var CameraSensorHelper::AnalogueGainExponential
- * \brief Gain is expressed using an exponential model
+ * \struct CameraSensorHelper::AnalogueGainExp
+ * \brief Analogue gain constants for the exponential gain model
*
* The relationship between the integer gain parameter and the resulting gain
* multiplier is given by the following equation:
@@ -156,61 +184,22 @@ double CameraSensorHelper::gain(uint32_t gainCode) const
*
* When the gain is expressed in dB, 'a' is equal to 1 and 'm' to
* \f$log_{2}{10^{\frac{1}{20}}}\f$.
- */
-
-/**
- * \struct CameraSensorHelper::AnalogueGainLinearConstants
- * \brief Analogue gain constants for the linear gain model
- *
- * \var CameraSensorHelper::AnalogueGainLinearConstants::m0
- * \brief Constant used in the linear gain coding/decoding
*
- * \note Either m0 or m1 shall be zero.
- *
- * \var CameraSensorHelper::AnalogueGainLinearConstants::c0
- * \brief Constant used in the linear gain coding/decoding
- *
- * \var CameraSensorHelper::AnalogueGainLinearConstants::m1
- * \brief Constant used in the linear gain coding/decoding
- *
- * \note Either m0 or m1 shall be zero.
- *
- * \var CameraSensorHelper::AnalogueGainLinearConstants::c1
- * \brief Constant used in the linear gain coding/decoding
- */
-
-/**
- * \struct CameraSensorHelper::AnalogueGainExpConstants
- * \brief Analogue gain constants for the exponential gain model
- *
- * \var CameraSensorHelper::AnalogueGainExpConstants::a
+ * \var CameraSensorHelper::AnalogueGainExp::a
* \brief Constant used in the exponential gain coding/decoding
*
- * \var CameraSensorHelper::AnalogueGainExpConstants::m
+ * \var CameraSensorHelper::AnalogueGainExp::m
* \brief Constant used in the exponential gain coding/decoding
*/
/**
- * \struct CameraSensorHelper::AnalogueGainConstants
- * \brief Analogue gain model constants
- *
- * This union stores the constants used to calculate the analogue gain. The
- * CameraSensorHelper::gainType_ variable selects which union member is valid.
- *
- * \var CameraSensorHelper::AnalogueGainConstants::linear
- * \brief Constants for the linear gain model
- *
- * \var CameraSensorHelper::AnalogueGainConstants::exp
- * \brief Constants for the exponential gain model
- */
-
-/**
- * \var CameraSensorHelper::gainType_
- * \brief The analogue gain model type
+ * \var CameraSensorHelper::blackLevel_
+ * \brief The black level of the sensor
+ * \sa CameraSensorHelper::blackLevel()
*/
/**
- * \var CameraSensorHelper::gainConstants_
+ * \var CameraSensorHelper::gain_
* \brief The analogue gain parameters used for calculation
*
* The analogue gain is calculated through a formula, and its parameters are
@@ -366,42 +355,168 @@ static constexpr double expGainDb(double step)
return log2_10 * step / 20;
}
-class CameraSensorHelperAr0521 : public CameraSensorHelper
+class CameraSensorHelperAr0144 : public CameraSensorHelper
{
public:
- uint32_t gainCode(double gain) const override;
- double gain(uint32_t gainCode) const override;
+ CameraSensorHelperAr0144()
+ {
+ /* Power-on default value: 168 at 12bits. */
+ blackLevel_ = 2688;
+ }
+
+ uint32_t gainCode(double gain) const override
+ {
+ /* The recommended minimum gain is 1.6842 to avoid artifacts. */
+ gain = std::clamp(gain, 1.0 / (1.0 - 13.0 / 32.0), 18.45);
+
+ /*
+ * The analogue gain is made of a coarse exponential gain in
+ * the range [2^0, 2^4] and a fine inversely linear gain in the
+ * range [1.0, 2.0[. There is an additional fixed 1.153125
+ * multiplier when the coarse gain reaches 2^2.
+ */
+
+ if (gain > 4.0)
+ gain /= 1.153125;
+
+ unsigned int coarse = std::log2(gain);
+ unsigned int fine = (1 - (1 << coarse) / gain) * 32;
+
+ /* The fine gain rounding depends on the coarse gain. */
+ if (coarse == 1 || coarse == 3)
+ fine &= ~1;
+ else if (coarse == 4)
+ fine &= ~3;
+
+ return (coarse << 4) | (fine & 0xf);
+ }
+
+ double gain(uint32_t gainCode) const override
+ {
+ unsigned int coarse = gainCode >> 4;
+ unsigned int fine = gainCode & 0xf;
+ unsigned int d1;
+ double d2, m;
+
+ switch (coarse) {
+ default:
+ case 0:
+ d1 = 1;
+ d2 = 32.0;
+ m = 1.0;
+ break;
+ case 1:
+ d1 = 2;
+ d2 = 16.0;
+ m = 1.0;
+ break;
+ case 2:
+ d1 = 1;
+ d2 = 32.0;
+ m = 1.153125;
+ break;
+ case 3:
+ d1 = 2;
+ d2 = 16.0;
+ m = 1.153125;
+ break;
+ case 4:
+ d1 = 4;
+ d2 = 8.0;
+ m = 1.153125;
+ break;
+ }
+
+ /*
+ * With infinite precision, the calculated gain would be exact,
+ * and the reverse conversion with gainCode() would produce the
+ * same gain code. In the real world, rounding errors may cause
+ * the calculated gain to be lower by an amount negligible for
+ * all purposes, except for the reverse conversion. Converting
+ * the gain to a gain code could then return the quantized value
+ * just lower than the original gain code. To avoid this, tests
+ * showed that adding the machine epsilon to the multiplier m is
+ * sufficient.
+ */
+ m += std::numeric_limits<decltype(m)>::epsilon();
+
+ return m * (1 << coarse) / (1.0 - (fine / d1) / d2);
+ }
private:
static constexpr double kStep_ = 16;
};
+REGISTER_CAMERA_SENSOR_HELPER("ar0144", CameraSensorHelperAr0144)
-uint32_t CameraSensorHelperAr0521::gainCode(double gain) const
+class CameraSensorHelperAr0521 : public CameraSensorHelper
{
- gain = std::clamp(gain, 1.0, 15.5);
- unsigned int coarse = std::log2(gain);
- unsigned int fine = (gain / (1 << coarse) - 1) * kStep_;
+public:
+ uint32_t gainCode(double gain) const override
+ {
+ gain = std::clamp(gain, 1.0, 15.5);
+ unsigned int coarse = std::log2(gain);
+ unsigned int fine = (gain / (1 << coarse) - 1) * kStep_;
- return (coarse << 4) | (fine & 0xf);
-}
+ return (coarse << 4) | (fine & 0xf);
+ }
-double CameraSensorHelperAr0521::gain(uint32_t gainCode) const
-{
- unsigned int coarse = gainCode >> 4;
- unsigned int fine = gainCode & 0xf;
+ double gain(uint32_t gainCode) const override
+ {
+ unsigned int coarse = gainCode >> 4;
+ unsigned int fine = gainCode & 0xf;
- return (1 << coarse) * (1 + fine / kStep_);
-}
+ return (1 << coarse) * (1 + fine / kStep_);
+ }
+private:
+ static constexpr double kStep_ = 16;
+};
REGISTER_CAMERA_SENSOR_HELPER("ar0521", CameraSensorHelperAr0521)
+class CameraSensorHelperGc05a2 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperGc05a2()
+ {
+ /* From datasheet: 64 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 100, 0, 0, 1024 };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("gc05a2", CameraSensorHelperGc05a2)
+
+class CameraSensorHelperGc08a3 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperGc08a3()
+ {
+ /* From datasheet: 64 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 100, 0, 0, 1024 };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("gc08a3", CameraSensorHelperGc08a3)
+
+class CameraSensorHelperImx214 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperImx214()
+ {
+ /* From datasheet: 64 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 0, 512, -1, 512 };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("imx214", CameraSensorHelperImx214)
+
class CameraSensorHelperImx219 : public CameraSensorHelper
{
public:
CameraSensorHelperImx219()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 0, 256, -1, 256 };
+ /* From datasheet: 64 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 0, 256, -1, 256 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx219", CameraSensorHelperImx219)
@@ -411,19 +526,33 @@ class CameraSensorHelperImx258 : public CameraSensorHelper
public:
CameraSensorHelperImx258()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 0, 512, -1, 512 };
+ /* From datasheet: 0x40 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 0, 512, -1, 512 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx258", CameraSensorHelperImx258)
+class CameraSensorHelperImx283 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperImx283()
+ {
+ /* From datasheet: 0x32 at 10bits. */
+ blackLevel_ = 3200;
+ gain_ = AnalogueGainLinear{ 0, 2048, -1, 2048 };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("imx283", CameraSensorHelperImx283)
+
class CameraSensorHelperImx290 : public CameraSensorHelper
{
public:
CameraSensorHelperImx290()
{
- gainType_ = AnalogueGainExponential;
- gainConstants_.exp = { 1.0, expGainDb(0.3) };
+ /* From datasheet: 0xf0 at 12bits. */
+ blackLevel_ = 3840;
+ gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx290", CameraSensorHelperImx290)
@@ -433,8 +562,7 @@ class CameraSensorHelperImx296 : public CameraSensorHelper
public:
CameraSensorHelperImx296()
{
- gainType_ = AnalogueGainExponential;
- gainConstants_.exp = { 1.0, expGainDb(0.1) };
+ gain_ = AnalogueGainExp{ 1.0, expGainDb(0.1) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx296", CameraSensorHelperImx296)
@@ -444,13 +572,39 @@ class CameraSensorHelperImx327 : public CameraSensorHelperImx290
};
REGISTER_CAMERA_SENSOR_HELPER("imx327", CameraSensorHelperImx327)
+class CameraSensorHelperImx335 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperImx335()
+ {
+ /* From datasheet: 0x32 at 10bits. */
+ blackLevel_ = 3200;
+ gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("imx335", CameraSensorHelperImx335)
+
+class CameraSensorHelperImx415 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperImx415()
+ {
+ gain_ = AnalogueGainExp{ 1.0, expGainDb(0.3) };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("imx415", CameraSensorHelperImx415)
+
+class CameraSensorHelperImx462 : public CameraSensorHelperImx290
+{
+};
+REGISTER_CAMERA_SENSOR_HELPER("imx462", CameraSensorHelperImx462)
+
class CameraSensorHelperImx477 : public CameraSensorHelper
{
public:
CameraSensorHelperImx477()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 0, 1024, -1, 1024 };
+ gain_ = AnalogueGainLinear{ 0, 1024, -1, 1024 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx477", CameraSensorHelperImx477)
@@ -464,8 +618,7 @@ public:
* The Sensor Manual doesn't appear to document the gain model.
* This has been validated with some empirical testing only.
*/
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov2685", CameraSensorHelperOv2685)
@@ -475,8 +628,7 @@ class CameraSensorHelperOv2740 : public CameraSensorHelper
public:
CameraSensorHelperOv2740()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov2740", CameraSensorHelperOv2740)
@@ -486,8 +638,9 @@ class CameraSensorHelperOv4689 : public CameraSensorHelper
public:
CameraSensorHelperOv4689()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ /* From datasheet: 0x40 at 12bits. */
+ blackLevel_ = 1024;
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov4689", CameraSensorHelperOv4689)
@@ -497,8 +650,9 @@ class CameraSensorHelperOv5640 : public CameraSensorHelper
public:
CameraSensorHelperOv5640()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 16 };
+ /* From datasheet: 0x10 at 10bits. */
+ blackLevel_ = 1024;
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5640", CameraSensorHelperOv5640)
@@ -508,8 +662,7 @@ class CameraSensorHelperOv5647 : public CameraSensorHelper
public:
CameraSensorHelperOv5647()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 16 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5647", CameraSensorHelperOv5647)
@@ -519,8 +672,7 @@ class CameraSensorHelperOv5670 : public CameraSensorHelper
public:
CameraSensorHelperOv5670()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5670", CameraSensorHelperOv5670)
@@ -530,8 +682,9 @@ class CameraSensorHelperOv5675 : public CameraSensorHelper
public:
CameraSensorHelperOv5675()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ /* From Linux kernel driver: 0x40 at 10bits. */
+ blackLevel_ = 4096;
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5675", CameraSensorHelperOv5675)
@@ -541,26 +694,33 @@ class CameraSensorHelperOv5693 : public CameraSensorHelper
public:
CameraSensorHelperOv5693()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 16 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5693", CameraSensorHelperOv5693)
+class CameraSensorHelperOv64a40 : public CameraSensorHelper
+{
+public:
+ CameraSensorHelperOv64a40()
+ {
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
+ }
+};
+REGISTER_CAMERA_SENSOR_HELPER("ov64a40", CameraSensorHelperOv64a40)
+
class CameraSensorHelperOv8858 : public CameraSensorHelper
{
public:
CameraSensorHelperOv8858()
{
- gainType_ = AnalogueGainLinear;
-
/*
* \todo Validate the selected 1/128 step value as it differs
* from what the sensor manual describes.
*
* See: https://patchwork.linuxtv.org/project/linux-media/patch/20221106171129.166892-2-nicholas@rothemail.net/#142267
*/
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov8858", CameraSensorHelperOv8858)
@@ -570,8 +730,7 @@ class CameraSensorHelperOv8865 : public CameraSensorHelper
public:
CameraSensorHelperOv8865()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov8865", CameraSensorHelperOv8865)
@@ -581,8 +740,7 @@ class CameraSensorHelperOv13858 : public CameraSensorHelper
public:
CameraSensorHelperOv13858()
{
- gainType_ = AnalogueGainLinear;
- gainConstants_.linear = { 1, 0, 0, 128 };
+ gain_ = AnalogueGainLinear{ 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov13858", CameraSensorHelperOv13858)
diff --git a/src/ipa/libipa/camera_sensor_helper.h b/src/ipa/libipa/camera_sensor_helper.h
index 3ea1806c..a9300a64 100644
--- a/src/ipa/libipa/camera_sensor_helper.h
+++ b/src/ipa/libipa/camera_sensor_helper.h
@@ -2,15 +2,16 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_sensor_helper.h - Helper class that performs sensor-specific parameter computations
+ * Helper class that performs sensor-specific parameter computations
*/
#pragma once
-#include <stdint.h>
-
#include <memory>
+#include <optional>
+#include <stdint.h>
#include <string>
+#include <variant>
#include <vector>
#include <libcamera/base/class.h>
@@ -25,34 +26,25 @@ public:
CameraSensorHelper() = default;
virtual ~CameraSensorHelper() = default;
+ std::optional<int16_t> blackLevel() const { return blackLevel_; }
virtual uint32_t gainCode(double gain) const;
virtual double gain(uint32_t gainCode) const;
protected:
- enum AnalogueGainType {
- AnalogueGainLinear,
- AnalogueGainExponential,
- };
-
- struct AnalogueGainLinearConstants {
+ struct AnalogueGainLinear {
int16_t m0;
int16_t c0;
int16_t m1;
int16_t c1;
};
- struct AnalogueGainExpConstants {
+ struct AnalogueGainExp {
double a;
double m;
};
- union AnalogueGainConstants {
- AnalogueGainLinearConstants linear;
- AnalogueGainExpConstants exp;
- };
-
- AnalogueGainType gainType_;
- AnalogueGainConstants gainConstants_;
+ std::optional<int16_t> blackLevel_;
+ std::variant<std::monostate, AnalogueGainLinear, AnalogueGainExp> gain_;
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraSensorHelper)
@@ -88,7 +80,7 @@ public:
}
private:
- std::unique_ptr<CameraSensorHelper> createInstance() const
+ std::unique_ptr<CameraSensorHelper> createInstance() const override
{
return std::make_unique<_Helper>();
}
diff --git a/src/ipa/libipa/colours.cpp b/src/ipa/libipa/colours.cpp
new file mode 100644
index 00000000..97124cf4
--- /dev/null
+++ b/src/ipa/libipa/colours.cpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * libipa miscellaneous colour helpers
+ */
+
+#include "colours.h"
+
+#include <algorithm>
+#include <cmath>
+
+namespace libcamera {
+
+namespace ipa {
+
+/**
+ * \file colours.h
+ * \brief Functions to reduce code duplication between IPA modules
+ */
+
+/**
+ * \brief Estimate luminance from RGB values following ITU-R BT.601
+ * \param[in] rgb The RGB value
+ *
+ * This function estimates a luminance value from a triplet of Red, Green and
+ * Blue values, following the formula defined by ITU-R Recommendation BT.601-7
+ * which can be found at https://www.itu.int/rec/R-REC-BT.601
+ *
+ * \return The estimated luminance value
+ */
+double rec601LuminanceFromRGB(const RGB<double> &rgb)
+{
+ static const Vector<double, 3> rgb2y{{
+ 0.299, 0.587, 0.114
+ }};
+
+ return rgb.dot(rgb2y);
+}
+
+/**
+ * \brief Estimate correlated colour temperature from RGB color space input
+ * \param[in] rgb The RGB value
+ *
+ * This function estimates the correlated color temperature RGB color space
+ * input. In physics and color science, the Planckian locus or black body locus
+ * is the path or locus that the color of an incandescent black body would take
+ * in a particular chromaticity space as the black body temperature changes.
+ *
+ * If a narrow range of color temperatures is considered (those encapsulating
+ * daylight being the most practical case) one can approximate the Planckian
+ * locus in order to calculate the CCT in terms of chromaticity coordinates.
+ *
+ * More detailed information can be found in:
+ * https://en.wikipedia.org/wiki/Color_temperature#Approximation
+ *
+ * \return The estimated color temperature
+ */
+uint32_t estimateCCT(const RGB<double> &rgb)
+{
+ /*
+ * Convert the RGB values to CIE tristimulus values (XYZ) and divide by
+ * the sum of X, Y and Z to calculate the CIE xy chromaticity.
+ */
+ static const Matrix<double, 3, 3> rgb2xyz({
+ -0.14282, 1.54924, -0.95641,
+ -0.32466, 1.57837, -0.73191,
+ -0.68202, 0.77073, 0.56332
+ });
+
+ Vector<double, 3> xyz = rgb2xyz * rgb;
+ xyz /= xyz.sum();
+
+ /* Calculate CCT */
+ double n = (xyz.x() - 0.3320) / (0.1858 - xyz.y());
+ return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;
+}
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/colours.h b/src/ipa/libipa/colours.h
new file mode 100644
index 00000000..d39b2ca8
--- /dev/null
+++ b/src/ipa/libipa/colours.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * libipa miscellaneous colour helpers
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libcamera/internal/vector.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+double rec601LuminanceFromRGB(const RGB<double> &rgb);
+uint32_t estimateCCT(const RGB<double> &rgb);
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/exposure_mode_helper.cpp b/src/ipa/libipa/exposure_mode_helper.cpp
new file mode 100644
index 00000000..0c1e99e3
--- /dev/null
+++ b/src/ipa/libipa/exposure_mode_helper.cpp
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class that performs computations relating to exposure
+ */
+#include "exposure_mode_helper.h"
+
+#include <algorithm>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file exposure_mode_helper.h
+ * \brief Helper class that performs computations relating to exposure
+ *
+ * AEGC algorithms have a need to split exposure between exposure time, analogue
+ * and digital gain. Multiple implementations do so based on paired stages of
+ * exposure time and gain limits; provide a helper to avoid duplicating the code.
+ */
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+LOG_DEFINE_CATEGORY(ExposureModeHelper)
+
+namespace ipa {
+
+/**
+ * \class ExposureModeHelper
+ * \brief Class for splitting exposure into exposure time and total gain
+ *
+ * The ExposureModeHelper class provides a standard interface through which an
+ * AEGC algorithm can divide exposure between exposure time and gain. It is
+ * configured with a set of exposure time and gain pairs and works by initially
+ * fixing gain at 1.0 and increasing exposure time up to the exposure time value
+ * from the first pair in the set in an attempt to meet the required exposure
+ * value.
+ *
+ * If the required exposure is not achievable by the first exposure time value
+ * alone it ramps gain up to the value from the first pair in the set. If the
+ * required exposure is still not met it then allows exposure time to ramp up to
+ * the exposure time value from the second pair in the set, and continues in this
+ * vein until either the required exposure time is met, or else the hardware's
+ * exposure time or gain limits are reached.
+ *
+ * This method allows users to strike a balance between a well-exposed image and
+ * an acceptable frame-rate, as opposed to simply maximising exposure time
+ * followed by gain. The same helpers can be used to perform the latter
+ * operation if needed by passing an empty set of pairs to the initialisation
+ * function.
+ *
+ * The gain values may exceed a camera sensor's analogue gain limits if either
+ * it or the IPA is also capable of digital gain. The configure() function must
+ * be called with the hardware's limits to inform the helper of those
+ * constraints. Any gain that is needed will be applied as analogue gain first
+ * until the hardware's limit is reached, following which digital gain will be
+ * used.
+ */
+
+/**
+ * \brief Construct an ExposureModeHelper instance
+ * \param[in] stages The vector of paired exposure time and gain limits
+ *
+ * The input stages are exposure time and _total_ gain pairs; the gain
+ * encompasses both analogue and digital gain.
+ *
+ * The vector of stages may be empty. In that case, the helper will simply use
+ * the runtime limits set through setLimits() instead.
+ */
+ExposureModeHelper::ExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages)
+{
+ minExposureTime_ = 0us;
+ maxExposureTime_ = 0us;
+ minGain_ = 0;
+ maxGain_ = 0;
+
+ for (const auto &[s, g] : stages) {
+ exposureTimes_.push_back(s);
+ gains_.push_back(g);
+ }
+}
+
+/**
+ * \brief Set the exposure time and gain limits
+ * \param[in] minExposureTime The minimum exposure time supported
+ * \param[in] maxExposureTime The maximum exposure time supported
+ * \param[in] minGain The minimum analogue gain supported
+ * \param[in] maxGain The maximum analogue gain supported
+ *
+ * This function configures the exposure time and analogue gain limits that need
+ * to be adhered to as the helper divides up exposure. Note that this function
+ * *must* be called whenever those limits change and before splitExposure() is
+ * used.
+ *
+ * If the algorithm using the helpers needs to indicate that either exposure time
+ * or analogue gain or both should be fixed it can do so by setting both the
+ * minima and maxima to the same value.
+ */
+void ExposureModeHelper::setLimits(utils::Duration minExposureTime,
+ utils::Duration maxExposureTime,
+ double minGain, double maxGain)
+{
+ minExposureTime_ = minExposureTime;
+ maxExposureTime_ = maxExposureTime;
+ minGain_ = minGain;
+ maxGain_ = maxGain;
+}
+
+utils::Duration ExposureModeHelper::clampExposureTime(utils::Duration exposureTime) const
+{
+ return std::clamp(exposureTime, minExposureTime_, maxExposureTime_);
+}
+
+double ExposureModeHelper::clampGain(double gain) const
+{
+ return std::clamp(gain, minGain_, maxGain_);
+}
+
+/**
+ * \brief Split exposure into exposure time and gain
+ * \param[in] exposure Exposure value
+ *
+ * This function divides a given exposure into exposure time, analogue and
+ * digital gain by iterating through stages of exposure time and gain limits.
+ * At each stage the current stage's exposure time limit is multiplied by the
+ * previous stage's gain limit (or 1.0 initially) to see if the combination of
+ * the two can meet the required exposure. If they cannot then the current
+ * stage's exposure time limit is multiplied by the same stage's gain limit to
+ * see if that combination can meet the required exposure time. If they cannot
+ * then the function moves to consider the next stage.
+ *
+ * When a combination of exposure time and gain _stage_ limits are found that
+ * are sufficient to meet the required exposure, the function attempts to reduce
+ * exposure time as much as possible whilst fixing gain and still meeting the
+ * exposure. If a _runtime_ limit prevents exposure time from being lowered
+ * enough to meet the exposure with gain fixed at the stage limit, gain is also
+ * lowered to compensate.
+ *
+ * Once the exposure time and gain values are ascertained, gain is assigned as
+ * analogue gain as much as possible, with digital gain only in use if the
+ * maximum analogue gain runtime limit is unable to accommodate the exposure
+ * value.
+ *
+ * If no combination of exposure time and gain limits is found that meets the
+ * required exposure, the helper falls-back to simply maximising the exposure
+ * time first, followed by analogue gain, followed by digital gain.
+ *
+ * \return Tuple of exposure time, analogue gain, and digital gain
+ */
+std::tuple<utils::Duration, double, double>
+ExposureModeHelper::splitExposure(utils::Duration exposure) const
+{
+ ASSERT(maxExposureTime_);
+ ASSERT(maxGain_);
+
+ bool gainFixed = minGain_ == maxGain_;
+ bool exposureTimeFixed = minExposureTime_ == maxExposureTime_;
+
+ /*
+ * There's no point entering the loop if we cannot change either gain
+ * nor exposure time anyway.
+ */
+ if (exposureTimeFixed && gainFixed)
+ return { minExposureTime_, minGain_, exposure / (minExposureTime_ * minGain_) };
+
+ utils::Duration exposureTime;
+ double stageGain = 1.0;
+ double gain;
+
+ for (unsigned int stage = 0; stage < gains_.size(); stage++) {
+ double lastStageGain = stage == 0 ? 1.0 : clampGain(gains_[stage - 1]);
+ utils::Duration stageExposureTime = clampExposureTime(exposureTimes_[stage]);
+ stageGain = clampGain(gains_[stage]);
+
+ /*
+ * We perform the clamping on both exposure time and gain in
+ * case the helper has had limits set that prevent those values
+ * being lowered beyond a certain minimum...this can happen at
+ * runtime for various reasons and so would not be known when
+ * the stage limits are initialised.
+ */
+
+ /* Clamp the gain to lastStageGain and regulate exposureTime. */
+ if (stageExposureTime * lastStageGain >= exposure) {
+ exposureTime = clampExposureTime(exposure / clampGain(lastStageGain));
+ gain = clampGain(exposure / exposureTime);
+
+ return { exposureTime, gain, exposure / (exposureTime * gain) };
+ }
+
+ /* Clamp the exposureTime to stageExposureTime and regulate gain. */
+ if (stageExposureTime * stageGain >= exposure) {
+ exposureTime = clampExposureTime(stageExposureTime);
+ gain = clampGain(exposure / exposureTime);
+
+ return { exposureTime, gain, exposure / (exposureTime * gain) };
+ }
+ }
+
+ /*
+ * From here on all we can do is max out the exposure time, followed by
+ * the analogue gain. If we still haven't achieved the target we send
+ * the rest of the exposure time to digital gain. If we were given no
+ * stages to use then the default stageGain of 1.0 is used so that
+ * exposure time is maxed before gain is touched at all.
+ */
+ exposureTime = clampExposureTime(exposure / clampGain(stageGain));
+ gain = clampGain(exposure / exposureTime);
+
+ return { exposureTime, gain, exposure / (exposureTime * gain) };
+}
+
+/**
+ * \fn ExposureModeHelper::minExposureTime()
+ * \brief Retrieve the configured minimum exposure time limit set through
+ * setLimits()
+ * \return The minExposureTime_ value
+ */
+
+/**
+ * \fn ExposureModeHelper::maxExposureTime()
+ * \brief Retrieve the configured maximum exposure time set through setLimits()
+ * \return The maxExposureTime_ value
+ */
+
+/**
+ * \fn ExposureModeHelper::minGain()
+ * \brief Retrieve the configured minimum gain set through setLimits()
+ * \return The minGain_ value
+ */
+
+/**
+ * \fn ExposureModeHelper::maxGain()
+ * \brief Retrieve the configured maximum gain set through setLimits()
+ * \return The maxGain_ value
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/exposure_mode_helper.h b/src/ipa/libipa/exposure_mode_helper.h
new file mode 100644
index 00000000..c5be1b67
--- /dev/null
+++ b/src/ipa/libipa/exposure_mode_helper.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class that performs computations relating to exposure
+ */
+
+#pragma once
+
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <libcamera/base/span.h>
+#include <libcamera/base/utils.h>
+
+namespace libcamera {
+
+namespace ipa {
+
+class ExposureModeHelper
+{
+public:
+ ExposureModeHelper(const Span<std::pair<utils::Duration, double>> stages);
+ ~ExposureModeHelper() = default;
+
+ void setLimits(utils::Duration minExposureTime, utils::Duration maxExposureTime,
+ double minGain, double maxGain);
+
+ std::tuple<utils::Duration, double, double>
+ splitExposure(utils::Duration exposure) const;
+
+ utils::Duration minExposureTime() const { return minExposureTime_; }
+ utils::Duration maxExposureTime() const { return maxExposureTime_; }
+ double minGain() const { return minGain_; }
+ double maxGain() const { return maxGain_; }
+
+private:
+ utils::Duration clampExposureTime(utils::Duration exposureTime) const;
+ double clampGain(double gain) const;
+
+ std::vector<utils::Duration> exposureTimes_;
+ std::vector<double> gains_;
+
+ utils::Duration minExposureTime_;
+ utils::Duration maxExposureTime_;
+ double minGain_;
+ double maxGain_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/fc_queue.cpp b/src/ipa/libipa/fc_queue.cpp
index e812faa5..0365e919 100644
--- a/src/ipa/libipa/fc_queue.cpp
+++ b/src/ipa/libipa/fc_queue.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * fc_queue.cpp - IPA Frame context queue
+ * IPA Frame context queue
*/
#include "fc_queue.h"
diff --git a/src/ipa/libipa/fc_queue.h b/src/ipa/libipa/fc_queue.h
index a589e7e1..a1d13652 100644
--- a/src/ipa/libipa/fc_queue.h
+++ b/src/ipa/libipa/fc_queue.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * fc_queue.h - IPA Frame context queue
+ * IPA Frame context queue
*/
#pragma once
@@ -25,6 +25,7 @@ struct FrameContext {
private:
template<typename T> friend class FCQueue;
uint32_t frame;
+ bool initialised = false;
};
template<typename FrameContext>
@@ -38,8 +39,10 @@ public:
void clear()
{
- for (FrameContext &ctx : contexts_)
+ for (FrameContext &ctx : contexts_) {
+ ctx.initialised = false;
ctx.frame = 0;
+ }
}
FrameContext &alloc(const uint32_t frame)
@@ -83,6 +86,21 @@ public:
<< " has been overwritten by "
<< frameContext.frame;
+ if (frame == 0 && !frameContext.initialised) {
+ /*
+ * If the IPA calls get() at start() time it will get an
+ * un-intialized FrameContext as the below "frame ==
+ * frameContext.frame" check will return success because
+ * FrameContexts are zeroed at creation time.
+ *
+ * Make sure the FrameContext gets initialised if get()
+ * is called before alloc() by the IPA for frame#0.
+ */
+ init(frameContext, frame);
+
+ return frameContext;
+ }
+
if (frame == frameContext.frame)
return frameContext;
@@ -108,6 +126,7 @@ private:
{
frameContext = {};
frameContext.frame = frame;
+ frameContext.initialised = true;
}
std::vector<FrameContext> contexts_;
diff --git a/src/ipa/libipa/fixedpoint.cpp b/src/ipa/libipa/fixedpoint.cpp
new file mode 100644
index 00000000..6b698fc5
--- /dev/null
+++ b/src/ipa/libipa/fixedpoint.cpp
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Fixed / floating point conversions
+ */
+
+#include "fixedpoint.h"
+
+/**
+ * \file fixedpoint.h
+ */
+
+namespace libcamera {
+
+namespace ipa {
+
+/**
+ * \fn R floatingToFixedPoint(T number)
+ * \brief Convert a floating point number to a fixed-point representation
+ * \tparam I Bit width of the integer part of the fixed-point
+ * \tparam F Bit width of the fractional part of the fixed-point
+ * \tparam R Return type of the fixed-point representation
+ * \tparam T Input type of the floating point representation
+ * \param number The floating point number to convert to fixed point
+ * \return The converted value
+ */
+
+/**
+ * \fn R fixedToFloatingPoint(T number)
+ * \brief Convert a fixed-point number to a floating point representation
+ * \tparam I Bit width of the integer part of the fixed-point
+ * \tparam F Bit width of the fractional part of the fixed-point
+ * \tparam R Return type of the floating point representation
+ * \tparam T Input type of the fixed-point representation
+ * \param number The fixed point number to convert to floating point
+ * \return The converted value
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/fixedpoint.h b/src/ipa/libipa/fixedpoint.h
new file mode 100644
index 00000000..709cf50f
--- /dev/null
+++ b/src/ipa/libipa/fixedpoint.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Fixed / floating point conversions
+ */
+
+#pragma once
+
+#include <cmath>
+#include <type_traits>
+
+namespace libcamera {
+
+namespace ipa {
+
+#ifndef __DOXYGEN__
+template<unsigned int I, unsigned int F, typename R, typename T,
+ std::enable_if_t<std::is_integral_v<R> &&
+ std::is_floating_point_v<T>> * = nullptr>
+#else
+template<unsigned int I, unsigned int F, typename R, typename T>
+#endif
+constexpr R floatingToFixedPoint(T number)
+{
+ static_assert(sizeof(int) >= sizeof(R));
+ static_assert(I + F <= sizeof(R) * 8);
+
+ /*
+ * The intermediate cast to int is needed on arm platforms to properly
+ * cast negative values. See
+ * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/
+ */
+ R mask = (1 << (F + I)) - 1;
+ R frac = static_cast<R>(static_cast<int>(std::round(number * (1 << F)))) & mask;
+
+ return frac;
+}
+
+#ifndef __DOXYGEN__
+template<unsigned int I, unsigned int F, typename R, typename T,
+ std::enable_if_t<std::is_floating_point_v<R> &&
+ std::is_integral_v<T>> * = nullptr>
+#else
+template<unsigned int I, unsigned int F, typename R, typename T>
+#endif
+constexpr R fixedToFloatingPoint(T number)
+{
+ static_assert(sizeof(int) >= sizeof(T));
+ static_assert(I + F <= sizeof(T) * 8);
+
+ /*
+ * Recreate the upper bits in case of a negative number by shifting the sign
+ * bit from the fixed point to the first bit of the unsigned and then right shifting
+ * by the same amount which keeps the sign bit in place.
+ * This can be optimized by the compiler quite well.
+ */
+ int remaining_bits = sizeof(int) * 8 - (I + F);
+ int t = static_cast<int>(static_cast<unsigned>(number) << remaining_bits) >> remaining_bits;
+ return static_cast<R>(t) / static_cast<R>(1 << F);
+}
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/histogram.cpp b/src/ipa/libipa/histogram.cpp
index 6b5cde8e..bcf26390 100644
--- a/src/ipa/libipa/histogram.cpp
+++ b/src/ipa/libipa/histogram.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * histogram.cpp - histogram calculations
+ * histogram calculations
*/
#include "histogram.h"
@@ -29,24 +29,46 @@ namespace ipa {
*/
/**
+ * \fn Histogram::Histogram()
+ * \brief Construct an empty Histogram
+ *
+ * This empty constructor exists largely to allow Histograms to be embedded in
+ * other classes which may be created before the contents of the Histogram are
+ * known.
+ */
+
+/**
* \brief Create a cumulative histogram
- * \param[in] data A pre-sorted histogram to be passed
+ * \param[in] data A (non-cumulative) histogram
*/
Histogram::Histogram(Span<const uint32_t> data)
{
- cumulative_.reserve(data.size());
- cumulative_.push_back(0);
- for (const uint32_t &value : data)
- cumulative_.push_back(cumulative_.back() + value);
+ cumulative_.resize(data.size() + 1);
+ cumulative_[0] = 0;
+ for (const auto &[i, value] : utils::enumerate(data))
+ cumulative_[i + 1] = cumulative_[i] + value;
}
/**
+ * \fn Histogram::Histogram(Span<const uint32_t> data, Transform transform)
+ * \brief Create a cumulative histogram
+ * \param[in] data A (non-cumulative) histogram
+ * \param[in] transform The transformation function to apply to every bin
+ */
+
+/**
* \fn Histogram::bins()
* \brief Retrieve the number of bins currently used by the Histogram
* \return Number of bins
*/
/**
+ * \fn Histogram::data()
+ * \brief Retrieve the internal data
+ * \return The data
+ */
+
+/**
* \fn Histogram::total()
* \brief Retrieve the total number of values in the data set
* \return Number of values
@@ -108,7 +130,8 @@ double Histogram::quantile(double q, uint32_t first, uint32_t last) const
if (cumulative_[first + 1] == cumulative_[first])
frac = 0;
else
- frac = (item - cumulative_[first]) / (cumulative_[first + 1] - cumulative_[first]);
+ frac = (q * total() - cumulative_[first])
+ / (cumulative_[first + 1] - cumulative_[first]);
return first + frac;
}
@@ -126,26 +149,37 @@ double Histogram::quantile(double q, uint32_t first, uint32_t last) const
double Histogram::interQuantileMean(double lowQuantile, double highQuantile) const
{
ASSERT(highQuantile > lowQuantile);
- /* Proportion of pixels which lies below lowQuantile */
- double lowPoint = quantile(lowQuantile);
- /* Proportion of pixels which lies below highQuantile */
- double highPoint = quantile(highQuantile, static_cast<uint32_t>(lowPoint));
- double sumBinFreq = 0, cumulFreq = 0;
-
- for (double p_next = floor(lowPoint) + 1.0;
- p_next <= ceil(highPoint);
- lowPoint = p_next, p_next += 1.0) {
- int bin = floor(lowPoint);
+
+ /* Proportion of pixels which lies below lowQuantile and highQuantile. */
+ const double lowPoint = quantile(lowQuantile);
+ const double highPoint = quantile(highQuantile, static_cast<uint32_t>(lowPoint));
+
+ double sumBinFreq = 0;
+ double cumulFreq = 0;
+
+ /*
+ * Calculate the mean pixel value between the low and high points by
+ * summing all the pixels between the two points, and dividing the sum
+ * by the number of pixels. Given the discrete nature of the histogram
+ * data, the sum of the pixels is approximated by accumulating the
+ * product of the bin values (calculated as the mid point of the bin) by
+ * the number of pixels they contain, for each bin in the internal.
+ */
+ for (unsigned bin = std::floor(lowPoint); bin < std::ceil(highPoint); bin++) {
+ const double lowBound = std::max<double>(bin, lowPoint);
+ const double highBound = std::min<double>(bin + 1, highPoint);
+
double freq = (cumulative_[bin + 1] - cumulative_[bin])
- * (std::min(p_next, highPoint) - lowPoint);
+ * (highBound - lowBound);
/* Accumulate weighted bin */
- sumBinFreq += bin * freq;
+ sumBinFreq += (highBound + lowBound) / 2 * freq;
+
/* Accumulate weights */
cumulFreq += freq;
}
- /* add 0.5 to give an average for bin mid-points */
- return sumBinFreq / cumulFreq + 0.5;
+
+ return sumBinFreq / cumulFreq;
}
} /* namespace ipa */
diff --git a/src/ipa/libipa/histogram.h b/src/ipa/libipa/histogram.h
index 05bb4b80..a926002c 100644
--- a/src/ipa/libipa/histogram.h
+++ b/src/ipa/libipa/histogram.h
@@ -2,18 +2,18 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * histogram.h - histogram calculation interface
+ * histogram calculation interface
*/
#pragma once
-#include <assert.h>
#include <limits.h>
#include <stdint.h>
-
+#include <type_traits>
#include <vector>
#include <libcamera/base/span.h>
+#include <libcamera/base/utils.h>
namespace libcamera {
@@ -22,8 +22,21 @@ namespace ipa {
class Histogram
{
public:
+ Histogram() { cumulative_.push_back(0); }
Histogram(Span<const uint32_t> data);
+
+ template<typename Transform,
+ std::enable_if_t<std::is_invocable_v<Transform, uint32_t>> * = nullptr>
+ Histogram(Span<const uint32_t> data, Transform transform)
+ {
+ cumulative_.resize(data.size() + 1);
+ cumulative_[0] = 0;
+ for (const auto &[i, value] : utils::enumerate(data))
+ cumulative_[i + 1] = cumulative_[i] + transform(value);
+ }
+
size_t bins() const { return cumulative_.size() - 1; }
+ const Span<const uint64_t> data() const { return cumulative_; }
uint64_t total() const { return cumulative_[cumulative_.size() - 1]; }
uint64_t cumulativeFrequency(double bin) const;
double quantile(double q, uint32_t first = 0, uint32_t last = UINT_MAX) const;
diff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp
new file mode 100644
index 00000000..f901a86e
--- /dev/null
+++ b/src/ipa/libipa/interpolator.cpp
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class for interpolating objects
+ */
+#include "interpolator.h"
+
+#include <algorithm>
+#include <string>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+#include "interpolator.h"
+
+/**
+ * \file interpolator.h
+ * \brief Helper class for linear interpolating a set of objects
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Interpolator)
+
+namespace ipa {
+
+/**
+ * \class Interpolator
+ * \brief Class for storing, retrieving, and interpolating objects
+ * \tparam T Type of objects stored in the interpolator
+ *
+ * The main use case is to pass a map from color temperatures to corresponding
+ * objects (eg. matrices for color correction), and then requesting a
+ * interpolated object for a specific color temperature. This class will
+ * abstract away the interpolation portion.
+ */
+
+/**
+ * \fn Interpolator::Interpolator()
+ * \brief Construct an empty interpolator
+ */
+
+/**
+ * \fn Interpolator::Interpolator(const std::map<unsigned int, T> &data)
+ * \brief Construct an interpolator from a map of objects
+ * \param data Map from which to construct the interpolator
+ */
+
+/**
+ * \fn Interpolator::Interpolator(std::map<unsigned int, T> &&data)
+ * \brief Construct an interpolator from a map of objects
+ * \param data Map from which to construct the interpolator
+ */
+
+/**
+ * \fn int Interpolator<T>::readYaml(const libcamera::YamlObject &yaml,
+ const std::string &key_name,
+ const std::string &value_name)
+ * \brief Initialize an Interpolator instance from yaml
+ * \tparam T Type of data stored in the interpolator
+ * \param[in] yaml The yaml object that contains the map of unsigned integers to
+ * objects
+ * \param[in] key_name The name of the key in the yaml object
+ * \param[in] value_name The name of the value in the yaml object
+ *
+ * The yaml object is expected to be a list of maps. Each map has two or more
+ * pairs: one of \a key_name to the key value (usually color temperature), and
+ * one or more of \a value_name to the object. This is a bit difficult to
+ * explain, so here is an example (in python, as it is easier to parse than
+ * yaml):
+ * [
+ * {
+ * 'ct': 2860,
+ * 'ccm': [ 2.12089, -0.52461, -0.59629,
+ * -0.85342, 2.80445, -0.95103,
+ * -0.26897, -1.14788, 2.41685 ],
+ * 'offsets': [ 0, 0, 0 ]
+ * },
+ *
+ * {
+ * 'ct': 2960,
+ * 'ccm': [ 2.26962, -0.54174, -0.72789,
+ * -0.77008, 2.60271, -0.83262,
+ * -0.26036, -1.51254, 2.77289 ],
+ * 'offsets': [ 0, 0, 0 ]
+ * },
+ *
+ * {
+ * 'ct': 3603,
+ * 'ccm': [ 2.18644, -0.66148, -0.52496,
+ * -0.77828, 2.69474, -0.91645,
+ * -0.25239, -0.83059, 2.08298 ],
+ * 'offsets': [ 0, 0, 0 ]
+ * },
+ * ]
+ *
+ * In this case, \a key_name would be 'ct', and \a value_name can be either
+ * 'ccm' or 'offsets'. This way multiple interpolators can be defined in
+ * one set of color temperature ranges in the tuning file, and they can be
+ * retrieved separately with the \a value_name parameter.
+ *
+ * \return Zero on success, negative error code otherwise
+ */
+
+/**
+ * \fn void Interpolator<T>::setQuantization(const unsigned int q)
+ * \brief Set the quantization value
+ * \param[in] q The quantization value
+ *
+ * Sets the quantization value. When this is set, 'key' gets quantized to this
+ * size, before doing the interpolation. This can help in reducing the number of
+ * updates pushed to the hardware.
+ *
+ * Note that normally a threshold needs to be combined with quantization.
+ * Otherwise a value that swings around the edge of the quantization step will
+ * lead to constant updates.
+ */
+
+/**
+ * \fn void Interpolator<T>::setData(std::map<unsigned int, T> &&data)
+ * \brief Set the internal map
+ *
+ * Overwrites the internal map using move semantics.
+ */
+
+/**
+ * \fn std::map<unsigned int, T> &Interpolator<T>::data() const
+ * \brief Access the internal map
+ *
+ * \return The internal map
+ */
+
+/**
+ * \fn const T& Interpolator<T>::getInterpolated()
+ * \brief Retrieve an interpolated value for the given key
+ * \param[in] key The unsigned integer key of the object to retrieve
+ * \param[out] quantizedKey If provided, the key value after quantization
+ * \return The object corresponding to the key. The object is cached internally,
+ * so on successive calls with the same key (after quantization) interpolation
+ * is not recalculated.
+ */
+
+/**
+ * \fn void Interpolator<T>::interpolate(const T &a, const T &b, T &dest, double lambda)
+ * \brief Interpolate between two instances of T
+ * \param a The first value to interpolate
+ * \param b The second value to interpolate
+ * \param dest The destination for the interpolated value
+ * \param lambda The interpolation factor (0..1)
+ *
+ * Interpolates between \a a and \a b according to \a lambda. It calculates
+ * dest = a * (1.0 - lambda) + b * lambda;
+ *
+ * If T supports multiplication with double and addition, this function can be
+ * used as is. For other types this function can be overwritten using partial
+ * template specialization.
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h
new file mode 100644
index 00000000..7880db69
--- /dev/null
+++ b/src/ipa/libipa/interpolator.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class for interpolating maps of objects
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cmath>
+#include <map>
+#include <string>
+#include <tuple>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Interpolator)
+
+namespace ipa {
+
+template<typename T>
+class Interpolator
+{
+public:
+ Interpolator() = default;
+ Interpolator(const std::map<unsigned int, T> &data)
+ : data_(data)
+ {
+ }
+ Interpolator(std::map<unsigned int, T> &&data)
+ : data_(std::move(data))
+ {
+ }
+
+ ~Interpolator() = default;
+
+ int readYaml(const libcamera::YamlObject &yaml,
+ const std::string &key_name,
+ const std::string &value_name)
+ {
+ data_.clear();
+ lastInterpolatedKey_.reset();
+
+ if (!yaml.isList()) {
+ LOG(Interpolator, Error) << "yaml object must be a list";
+ return -EINVAL;
+ }
+
+ for (const auto &value : yaml.asList()) {
+ unsigned int ct = std::stoul(value[key_name].get<std::string>(""));
+ std::optional<T> data =
+ value[value_name].get<T>();
+ if (!data) {
+ return -EINVAL;
+ }
+
+ data_[ct] = *data;
+ }
+
+ if (data_.size() < 1) {
+ LOG(Interpolator, Error) << "Need at least one element";
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ void setQuantization(const unsigned int q)
+ {
+ quantization_ = q;
+ }
+
+ void setData(std::map<unsigned int, T> &&data)
+ {
+ data_ = std::move(data);
+ lastInterpolatedKey_.reset();
+ }
+
+ const std::map<unsigned int, T> &data() const
+ {
+ return data_;
+ }
+
+ const T &getInterpolated(unsigned int key, unsigned int *quantizedKey = nullptr)
+ {
+ ASSERT(data_.size() > 0);
+
+ if (quantization_ > 0)
+ key = std::lround(key / static_cast<double>(quantization_)) * quantization_;
+
+ if (quantizedKey)
+ *quantizedKey = key;
+
+ if (lastInterpolatedKey_.has_value() &&
+ *lastInterpolatedKey_ == key)
+ return lastInterpolatedValue_;
+
+ auto it = data_.lower_bound(key);
+
+ if (it == data_.begin())
+ return it->second;
+
+ if (it == data_.end())
+ return std::prev(it)->second;
+
+ if (it->first == key)
+ return it->second;
+
+ auto it2 = std::prev(it);
+ double lambda = (key - it2->first) / static_cast<double>(it->first - it2->first);
+ interpolate(it2->second, it->second, lastInterpolatedValue_, lambda);
+ lastInterpolatedKey_ = key;
+
+ return lastInterpolatedValue_;
+ }
+
+ void interpolate(const T &a, const T &b, T &dest, double lambda)
+ {
+ dest = a * (1.0 - lambda) + b * lambda;
+ }
+
+private:
+ std::map<unsigned int, T> data_;
+ T lastInterpolatedValue_;
+ std::optional<unsigned int> lastInterpolatedKey_;
+ unsigned int quantization_ = 0;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp
new file mode 100644
index 00000000..f607d86c
--- /dev/null
+++ b/src/ipa/libipa/lsc_polynomial.cpp
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * Polynomial class to represent lens shading correction
+ */
+
+#include "lsc_polynomial.h"
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file lsc_polynomial.h
+ * \brief LscPolynomial class
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(LscPolynomial)
+
+namespace ipa {
+
+/**
+ * \class LscPolynomial
+ * \brief Class for handling even polynomials used in lens shading correction
+ *
+ * Shading artifacts of camera lenses can be modeled using even radial
+ * polynomials. This class implements a polynomial with 5 coefficients which
+ * follows the definition of the FixVignetteRadial opcode in the Adobe DNG
+ * specification.
+ */
+
+/**
+ * \fn LscPolynomial::LscPolynomial(double cx = 0.0, double cy = 0.0, double k0 = 0.0,
+ double k1 = 0.0, double k2 = 0.0, double k3 = 0.0,
+ double k4 = 0.0)
+ * \brief Construct a polynomial using the given coefficients
+ * \param cx Center-x relative to the image in normalized coordinates (0..1)
+ * \param cy Center-y relative to the image in normalized coordinates (0..1)
+ * \param k0 Coefficient of the polynomial
+ * \param k1 Coefficient of the polynomial
+ * \param k2 Coefficient of the polynomial
+ * \param k3 Coefficient of the polynomial
+ * \param k4 Coefficient of the polynomial
+ */
+
+/**
+ * \fn LscPolynomial::sampleAtNormalizedPixelPos(double x, double y)
+ * \brief Sample the polynomial at the given normalized pixel position
+ *
+ * This functions samples the polynomial at the given pixel position divided by
+ * the value returned by getM().
+ *
+ * \param x x position in normalized coordinates
+ * \param y y position in normalized coordinates
+ * \return The sampled value
+ */
+
+/**
+ * \fn LscPolynomial::getM()
+ * \brief Get the value m as described in the dng specification
+ *
+ * Returns m according to dng spec. m represents the Euclidean distance
+ * (in pixels) from the optical center to the farthest pixel in the
+ * image.
+ *
+ * \return The sampled value
+ */
+
+/**
+ * \fn LscPolynomial::setReferenceImageSize(const Size &size)
+ * \brief Set the reference image size
+ *
+ * Set the reference image size that is used for subsequent calls to getM() and
+ * sampleAtNormalizedPixelPos()
+ *
+ * \param size The size of the reference image
+ */
+
+} // namespace ipa
+} // namespace libcamera
diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h
new file mode 100644
index 00000000..c898faeb
--- /dev/null
+++ b/src/ipa/libipa/lsc_polynomial.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * Helper for radial polynomial used in lens shading correction.
+ */
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <assert.h>
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/span.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(LscPolynomial)
+
+namespace ipa {
+
+class LscPolynomial
+{
+public:
+ LscPolynomial(double cx = 0.0, double cy = 0.0, double k0 = 0.0,
+ double k1 = 0.0, double k2 = 0.0, double k3 = 0.0,
+ double k4 = 0.0)
+ : cx_(cx), cy_(cy), cnx_(0), cny_(0),
+ coefficients_({ k0, k1, k2, k3, k4 })
+ {
+ }
+
+ double sampleAtNormalizedPixelPos(double x, double y) const
+ {
+ double dx = x - cnx_;
+ double dy = y - cny_;
+ double r = sqrt(dx * dx + dy * dy);
+ double res = 1.0;
+ for (unsigned int i = 0; i < coefficients_.size(); i++) {
+ res += coefficients_[i] * std::pow(r, (i + 1) * 2);
+ }
+ return res;
+ }
+
+ double getM() const
+ {
+ double cpx = imageSize_.width * cx_;
+ double cpy = imageSize_.height * cy_;
+ double mx = std::max(cpx, std::fabs(imageSize_.width - cpx));
+ double my = std::max(cpy, std::fabs(imageSize_.height - cpy));
+
+ return sqrt(mx * mx + my * my);
+ }
+
+ void setReferenceImageSize(const Size &size)
+ {
+ assert(!size.isNull());
+ imageSize_ = size;
+
+ /* Calculate normalized centers */
+ double m = getM();
+ cnx_ = (size.width * cx_) / m;
+ cny_ = (size.height * cy_) / m;
+ }
+
+private:
+ double cx_;
+ double cy_;
+ double cnx_;
+ double cny_;
+ std::array<double, 5> coefficients_;
+
+ Size imageSize_;
+};
+
+} /* namespace ipa */
+
+#ifndef __DOXYGEN__
+
+template<>
+struct YamlObject::Getter<ipa::LscPolynomial> {
+ std::optional<ipa::LscPolynomial> get(const YamlObject &obj) const
+ {
+ std::optional<double> cx = obj["cx"].get<double>();
+ std::optional<double> cy = obj["cy"].get<double>();
+ std::optional<double> k0 = obj["k0"].get<double>();
+ std::optional<double> k1 = obj["k1"].get<double>();
+ std::optional<double> k2 = obj["k2"].get<double>();
+ std::optional<double> k3 = obj["k3"].get<double>();
+ std::optional<double> k4 = obj["k4"].get<double>();
+
+ if (!(cx && cy && k0 && k1 && k2 && k3 && k4))
+ LOG(LscPolynomial, Error)
+ << "Polynomial is missing a parameter";
+
+ return ipa::LscPolynomial(*cx, *cy, *k0, *k1, *k2, *k3, *k4);
+ }
+};
+
+#endif
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp
new file mode 100644
index 00000000..899e8824
--- /dev/null
+++ b/src/ipa/libipa/lux.cpp
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2019, Raspberry Pi Ltd
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class that implements lux estimation
+ */
+#include "lux.h"
+
+#include <algorithm>
+#include <chrono>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+#include "histogram.h"
+
+/**
+ * \file lux.h
+ * \brief Helper class that implements lux estimation
+ *
+ * Estimating the lux level of an image is a common operation that can for
+ * instance be used to adjust the target Y value in AGC or for Bayesian AWB
+ * estimation.
+ */
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+LOG_DEFINE_CATEGORY(Lux)
+
+namespace ipa {
+
+/**
+ * \class Lux
+ * \brief Class that implements lux estimation
+ *
+ * IPAs that wish to use lux estimation should create a Lux algorithm module
+ * that lightly wraps this module by providing the platform-specific luminance
+ * histogram. The Lux entry in the tuning file must then precede the algorithms
+ * that depend on the estimated lux value.
+ */
+
+/**
+ * \var Lux::referenceExposureTime_
+ * \brief The exposure time of the reference image, in microseconds
+ */
+
+/**
+ * \var Lux::referenceAnalogueGain_
+ * \brief The analogue gain of the reference image
+ */
+
+/**
+ * \var Lux::referenceDigitalGain_
+ * \brief The analogue gain of the reference image
+ */
+
+/**
+ * \var Lux::referenceY_
+ * \brief The measured luminance of the reference image, normalized to 1
+ *
+ */
+
+/**
+ * \var Lux::referenceLux_
+ * \brief The estimated lux level of the reference image
+ */
+
+/**
+ * \brief Construct the Lux helper module
+ */
+Lux::Lux()
+{
+}
+
+/**
+ * \brief Parse tuning data
+ * \param[in] tuningData The YamlObject representing the tuning data
+ *
+ * This function parses yaml tuning data for the common Lux module. It requires
+ * reference exposure time, analogue gain, digital gain, and lux values.
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Lux:
+ * referenceExposureTime: 10000
+ * referenceAnalogueGain: 4.0
+ * referenceDigitalGain: 1.0
+ * referenceY: 0.1831
+ * referenceLux: 1000
+ * \endcode
+ *
+ * \return 0 on success or a negative error code
+ */
+int Lux::parseTuningData(const YamlObject &tuningData)
+{
+ auto value = tuningData["referenceExposureTime"].get<double>();
+ if (!value) {
+ LOG(Lux, Error) << "Missing tuning parameter: "
+ << "'referenceExposureTime'";
+ return -EINVAL;
+ }
+ referenceExposureTime_ = *value * 1.0us;
+
+ value = tuningData["referenceAnalogueGain"].get<double>();
+ if (!value) {
+ LOG(Lux, Error) << "Missing tuning parameter: "
+ << "'referenceAnalogueGain'";
+ return -EINVAL;
+ }
+ referenceAnalogueGain_ = *value;
+
+ value = tuningData["referenceDigitalGain"].get<double>();
+ if (!value) {
+ LOG(Lux, Error) << "Missing tuning parameter: "
+ << "'referenceDigitalGain'";
+ return -EINVAL;
+ }
+ referenceDigitalGain_ = *value;
+
+ value = tuningData["referenceY"].get<double>();
+ if (!value) {
+ LOG(Lux, Error) << "Missing tuning parameter: "
+ << "'referenceY'";
+ return -EINVAL;
+ }
+ referenceY_ = *value;
+
+ value = tuningData["referenceLux"].get<double>();
+ if (!value) {
+ LOG(Lux, Error) << "Missing tuning parameter: "
+ << "'referenceLux'";
+ return -EINVAL;
+ }
+ referenceLux_ = *value;
+
+ return 0;
+}
+
+/**
+ * \brief Estimate lux given runtime values
+ * \param[in] exposureTime Exposure time applied to the frame
+ * \param[in] aGain Analogue gain applied to the frame
+ * \param[in] dGain Digital gain applied to the frame
+ * \param[in] yHist Histogram from the ISP statistics
+ *
+ * Estimate the lux given the exposure time, gain, and histogram.
+ *
+ * \return Estimated lux value
+ */
+double Lux::estimateLux(utils::Duration exposureTime,
+ double aGain, double dGain,
+ const Histogram &yHist) const
+{
+ double currentY = yHist.interQuantileMean(0, 1);
+ double exposureTimeRatio = referenceExposureTime_ / exposureTime;
+ double aGainRatio = referenceAnalogueGain_ / aGain;
+ double dGainRatio = referenceDigitalGain_ / dGain;
+ double yRatio = (currentY / yHist.bins()) / referenceY_;
+
+ double estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio *
+ yRatio * referenceLux_;
+
+ LOG(Lux, Debug) << "Estimated lux " << estimatedLux;
+ return estimatedLux;
+}
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h
new file mode 100644
index 00000000..d95bcdaf
--- /dev/null
+++ b/src/ipa/libipa/lux.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2019, Raspberry Pi Ltd
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Helper class that implements lux estimation
+ */
+
+#pragma once
+
+#include <libcamera/base/utils.h>
+
+namespace libcamera {
+
+class YamlObject;
+
+namespace ipa {
+
+class Histogram;
+
+class Lux
+{
+public:
+ Lux();
+
+ int parseTuningData(const YamlObject &tuningData);
+ double estimateLux(utils::Duration exposureTime,
+ double aGain, double dGain,
+ const Histogram &yHist) const;
+
+private:
+ utils::Duration referenceExposureTime_;
+ double referenceAnalogueGain_;
+ double referenceDigitalGain_;
+ double referenceY_;
+ double referenceLux_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
index 016b8e0e..660be940 100644
--- a/src/ipa/libipa/meson.build
+++ b/src/ipa/libipa/meson.build
@@ -1,19 +1,41 @@
# SPDX-License-Identifier: CC0-1.0
libipa_headers = files([
+ 'agc_mean_luminance.h',
'algorithm.h',
+ 'awb_bayes.h',
+ 'awb_grey.h',
+ 'awb.h',
'camera_sensor_helper.h',
+ 'colours.h',
+ 'exposure_mode_helper.h',
'fc_queue.h',
+ 'fixedpoint.h',
'histogram.h',
+ 'interpolator.h',
+ 'lsc_polynomial.h',
+ 'lux.h',
'module.h',
+ 'pwl.h',
])
libipa_sources = files([
+ 'agc_mean_luminance.cpp',
'algorithm.cpp',
+ 'awb_bayes.cpp',
+ 'awb_grey.cpp',
+ 'awb.cpp',
'camera_sensor_helper.cpp',
+ 'colours.cpp',
+ 'exposure_mode_helper.cpp',
'fc_queue.cpp',
+ 'fixedpoint.cpp',
'histogram.cpp',
+ 'interpolator.cpp',
+ 'lsc_polynomial.cpp',
+ 'lux.cpp',
'module.cpp',
+ 'pwl.cpp',
])
libipa_includes = include_directories('..')
@@ -21,3 +43,7 @@ libipa_includes = include_directories('..')
libipa = static_library('ipa', [libipa_sources, libipa_headers],
include_directories : ipa_includes,
dependencies : libcamera_private)
+
+libipa_dep = declare_dependency(sources : libipa_headers,
+ include_directories : libipa_includes,
+ link_with : libipa)
diff --git a/src/ipa/libipa/module.cpp b/src/ipa/libipa/module.cpp
index ee01f12a..64ca9141 100644
--- a/src/ipa/libipa/module.cpp
+++ b/src/ipa/libipa/module.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas On Board
*
- * module.cpp - IPA Module
+ * IPA Module
*/
#include "module.h"
diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h
index 4149a353..0fb51916 100644
--- a/src/ipa/libipa/module.h
+++ b/src/ipa/libipa/module.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas On Board
*
- * module.h - IPA module
+ * IPA module
*/
#pragma once
diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp
new file mode 100644
index 00000000..3fa005ba
--- /dev/null
+++ b/src/ipa/libipa/pwl.cpp
@@ -0,0 +1,462 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2019, Raspberry Pi Ltd
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Piecewise linear functions
+ */
+
+#include "pwl.h"
+
+#include <cmath>
+#include <sstream>
+
+/**
+ * \file pwl.h
+ * \brief Piecewise linear functions
+ */
+
+namespace libcamera {
+
+namespace ipa {
+
+/**
+ * \class Pwl
+ * \brief Describe a univariate piecewise linear function in two-dimensional
+ * real space
+ *
+ * A piecewise linear function is a univariate function that maps reals to
+ * reals, and it is composed of multiple straight-line segments.
+ *
+ * While a mathematical piecewise linear function would usually be defined by
+ * a list of linear functions and for which values of the domain they apply,
+ * this Pwl class is instead defined by a list of points at which these line
+ * segments intersect. These intersecting points are known as knots.
+ *
+ * https://en.wikipedia.org/wiki/Piecewise_linear_function
+ *
+ * A consequence of the Pwl class being defined by knots instead of linear
+ * functions is that the values of the piecewise linear function past the ends
+ * of the function are constants as opposed to linear functions. In a
+ * mathematical piecewise linear function that is defined by multiple linear
+ * functions, the ends of the function are also linear functions and hence grow
+ * to infinity (or negative infinity). However, since this Pwl class is defined
+ * by knots, the y-value of the leftmost and rightmost knots will hold for all
+ * x values to negative infinity and positive infinity, respectively.
+ */
+
+/**
+ * \typedef Pwl::Point
+ * \brief Describe a point in two-dimensional real space
+ */
+
+/**
+ * \class Pwl::Interval
+ * \brief Describe an interval in one-dimensional real space
+ */
+
+/**
+ * \fn Pwl::Interval::Interval(double _start, double _end)
+ * \brief Construct an interval
+ * \param[in] _start Start of the interval
+ * \param[in] _end End of the interval
+ */
+
+/**
+ * \fn Pwl::Interval::contains
+ * \brief Check if a given value falls within the interval
+ * \param[in] value Value to check
+ * \return True if the value falls within the interval, including its bounds,
+ * or false otherwise
+ */
+
+/**
+ * \fn Pwl::Interval::clamp
+ * \brief Clamp a value such that it is within the interval
+ * \param[in] value Value to clamp
+ * \return The clamped value
+ */
+
+/**
+ * \fn Pwl::Interval::length
+ * \brief Compute the length of the interval
+ * \return The length of the interval
+ */
+
+/**
+ * \var Pwl::Interval::start
+ * \brief Start of the interval
+ */
+
+/**
+ * \var Pwl::Interval::end
+ * \brief End of the interval
+ */
+
+/**
+ * \brief Construct an empty piecewise linear function
+ */
+Pwl::Pwl()
+{
+}
+
+/**
+ * \brief Construct a piecewise linear function from a list of 2D points
+ * \param[in] points Vector of points from which to construct the piecewise
+ * linear function
+ *
+ * \a points must be in ascending order of x-value.
+ */
+Pwl::Pwl(const std::vector<Point> &points)
+ : points_(points)
+{
+}
+
+/**
+ * \copydoc Pwl::Pwl(const std::vector<Point> &points)
+ *
+ * The contents of the \a points vector is moved to the newly constructed Pwl
+ * instance.
+ */
+Pwl::Pwl(std::vector<Point> &&points)
+ : points_(std::move(points))
+{
+}
+
+/**
+ * \brief Append a point to the end of the piecewise linear function
+ * \param[in] x x-coordinate of the point to add to the piecewise linear function
+ * \param[in] y y-coordinate of the point to add to the piecewise linear function
+ * \param[in] eps Epsilon for the minimum x distance between points (optional)
+ *
+ * The point's x-coordinate must be greater than the x-coordinate of the last
+ * (= greatest) point already in the piecewise linear function.
+ */
+void Pwl::append(double x, double y, const double eps)
+{
+ if (points_.empty() || points_.back().x() + eps < x)
+ points_.push_back(Point({ x, y }));
+}
+
+/**
+ * \brief Prepend a point to the beginning of the piecewise linear function
+ * \param[in] x x-coordinate of the point to add to the piecewise linear function
+ * \param[in] y y-coordinate of the point to add to the piecewise linear function
+ * \param[in] eps Epsilon for the minimum x distance between points (optional)
+ *
+ * The point's x-coordinate must be less than the x-coordinate of the first
+ * (= smallest) point already in the piecewise linear function.
+ */
+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 }));
+}
+
+/**
+ * \fn Pwl::empty() const
+ * \brief Check if the piecewise linear function is empty
+ * \return True if there are no points in the function, false otherwise
+ */
+
+/**
+ * \fn Pwl::clear()
+ * \brief Clear the piecewise linear function
+ */
+
+/**
+ * \fn Pwl::size() const
+ * \brief Retrieve the number of points in the piecewise linear function
+ * \return The number of points in the piecewise linear function
+ */
+
+/**
+ * \brief Get the domain of the piecewise linear function
+ * \return An interval representing the domain
+ */
+Pwl::Interval Pwl::domain() const
+{
+ return Interval(points_[0].x(), points_[points_.size() - 1].x());
+}
+
+/**
+ * \brief Get the range of the piecewise linear function
+ * \return An interval representing the range
+ */
+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);
+}
+
+/**
+ * \brief Evaluate the piecewise linear function
+ * \param[in] x The x value to input into the function
+ * \param[inout] span Initial guess for span
+ * \param[in] updateSpan Set to true to update span
+ *
+ * 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.
+ *
+ * \return The result of evaluating the piecewise linear function at position \a x
+ */
+double Pwl::eval(double x, int *span, bool updateSpan) const
+{
+ int index = findSpan(x, span && *span != -1
+ ? *span
+ : points_.size() / 2 - 1);
+ if (span && updateSpan)
+ *span = index;
+ return points_[index].y() +
+ (x - points_[index].x()) * (points_[index + 1].y() - points_[index].y()) /
+ (points_[index + 1].x() - points_[index].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;
+}
+
+/**
+ * \brief Compute the inverse function
+ * \param[in] eps Epsilon for the minimum x distance between points (optional)
+ *
+ * The output includes whether the resulting inverse function is a proper
+ * (true) inverse, or only a best effort (e.g. input was non-monotonic).
+ *
+ * \return A pair of the inverse piecewise linear function, and whether or not
+ * the result is a proper/true inverse
+ */
+std::pair<Pwl, bool> Pwl::inverse(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.
+ */
+ bool trueInverse = !(neither || (appended && prepended));
+
+ return { inverse, trueInverse };
+}
+
+/**
+ * \brief Compose two piecewise linear functions together
+ * \param[in] other The "other" piecewise linear function
+ * \param[in] eps Epsilon for the minimum x distance between points (optional)
+ *
+ * The "this" function is done first, and "other" after.
+ *
+ * \return The composed piecewise linear function
+ */
+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({ Point({ 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;
+}
+
+/**
+ * \brief Apply function to (x, y) values at every control point
+ * \param[in] f Function to be applied
+ */
+void Pwl::map(std::function<void(double x, double y)> f) const
+{
+ for (auto &pt : points_)
+ f(pt.x(), pt.y());
+}
+
+/**
+ * \brief Apply function to (x, y0, y1) values wherever either Pwl has a
+ * control point.
+ * \param[in] pwl0 First piecewise linear function
+ * \param[in] pwl1 Second piecewise linear function
+ * \param[in] f Function to be applied
+ *
+ * This applies the function \a f to every parameter (x, y0, y1), where x is
+ * the combined list of x-values from \a pwl0 and \a pwl1, y0 is the y-value
+ * for the given x in \a pwl0, and y1 is the y-value for the same x in \a pwl1.
+ */
+void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1,
+ std::function<void(double x, double y0, double y1)> 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));
+ }
+}
+
+/**
+ * \brief Combine two Pwls
+ * \param[in] pwl0 First piecewise linear function
+ * \param[in] pwl1 Second piecewise linear function
+ * \param[in] f Function to be applied
+ * \param[in] eps Epsilon for the minimum x distance between points (optional)
+ *
+ * Create a new Pwl where the y values are given by running \a f wherever
+ * either pwl has a knot.
+ *
+ * \return The combined pwl
+ */
+Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1,
+ std::function<double(double x, double y0, double y1)> 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;
+}
+
+/**
+ * \brief Multiply the piecewise linear function
+ * \param[in] d Scalar multiplier to multiply the function by
+ * \return This function, after it has been multiplied by \a d
+ */
+Pwl &Pwl::operator*=(double d)
+{
+ for (auto &pt : points_)
+ pt[1] *= d;
+ return *this;
+}
+
+/**
+ * \brief Assemble and return a string describing the piecewise linear function
+ * \return A string describing the piecewise linear function
+ */
+std::string Pwl::toString() const
+{
+ std::stringstream ss;
+ ss << "Pwl { ";
+ for (auto &p : points_)
+ ss << "(" << p.x() << ", " << p.y() << ") ";
+ ss << "}";
+ return ss.str();
+}
+
+} /* namespace ipa */
+
+#ifndef __DOXYGEN__
+/*
+ * The YAML data shall be a list of numerical values with an even number of
+ * elements. They are parsed in pairs into x and y points in the piecewise
+ * linear function, and added in order. x must be monotonically increasing.
+ */
+template<>
+std::optional<ipa::Pwl>
+YamlObject::Getter<ipa::Pwl>::get(const YamlObject &obj) const
+{
+ if (!obj.size() || obj.size() % 2)
+ return std::nullopt;
+
+ ipa::Pwl pwl;
+
+ const auto &list = obj.asList();
+
+ for (auto it = list.begin(); it != list.end(); it++) {
+ auto x = it->get<double>();
+ if (!x)
+ return std::nullopt;
+ auto y = (++it)->get<double>();
+ if (!y)
+ return std::nullopt;
+
+ pwl.append(*x, *y);
+ }
+
+ if (pwl.size() != obj.size() / 2)
+ return std::nullopt;
+
+ return pwl;
+}
+#endif /* __DOXYGEN__ */
+
+} /* namespace libcamera */
diff --git a/src/ipa/libipa/pwl.h b/src/ipa/libipa/pwl.h
new file mode 100644
index 00000000..c1496c30
--- /dev/null
+++ b/src/ipa/libipa/pwl.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2019, Raspberry Pi Ltd
+ *
+ * Piecewise linear functions interface
+ */
+#pragma once
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "libcamera/internal/vector.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+class Pwl
+{
+public:
+ using Point = Vector<double, 2>;
+
+ struct Interval {
+ Interval(double _start, double _end)
+ : start(_start), end(_end) {}
+
+ bool contains(double value)
+ {
+ return value >= start && value <= end;
+ }
+
+ double clamp(double value)
+ {
+ return std::clamp(value, start, end);
+ }
+
+ double length() const { return end - start; }
+
+ double start, end;
+ };
+
+ Pwl();
+ Pwl(const std::vector<Point> &points);
+ Pwl(std::vector<Point> &&points);
+
+ void append(double x, double y, double eps = 1e-6);
+
+ bool empty() const { return points_.empty(); }
+ void clear() { points_.clear(); }
+ size_t size() const { return points_.size(); }
+
+ Interval domain() const;
+ Interval range() const;
+
+ double eval(double x, int *span = nullptr,
+ bool updateSpan = true) const;
+
+ std::pair<Pwl, bool> inverse(double eps = 1e-6) const;
+ Pwl compose(const Pwl &other, double eps = 1e-6) const;
+
+ void map(std::function<void(double x, double y)> f) const;
+
+ static Pwl
+ combine(const Pwl &pwl0, const Pwl &pwl1,
+ std::function<double(double x, double y0, double y1)> f,
+ double eps = 1e-6);
+
+ Pwl &operator*=(double d);
+
+ std::string toString() const;
+
+private:
+ static void map2(const Pwl &pwl0, const Pwl &pwl1,
+ std::function<void(double x, double y0, double y1)> f);
+ void prepend(double x, double y, double eps = 1e-6);
+ int findSpan(double x, int span) const;
+
+ std::vector<Point> points_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
new file mode 100644
index 00000000..70667db3
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -0,0 +1,410 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * agc.cpp - AGC/AEC mean-based control algorithm
+ */
+
+#include "agc.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/property_ids.h>
+
+#include "libipa/colours.h"
+#include "libipa/fixedpoint.h"
+
+namespace libcamera {
+
+using namespace std::literals::chrono_literals;
+
+namespace ipa::mali_c55::algorithms {
+
+LOG_DEFINE_CATEGORY(MaliC55Agc)
+
+/*
+ * Number of histogram bins. This is only true for the specific configuration we
+ * set to the ISP; 4 separate histograms of 256 bins each. If that configuration
+ * ever changes then this constant will need updating.
+ */
+static constexpr unsigned int kNumHistogramBins = 256;
+
+/*
+ * The Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8
+ * format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual
+ * max value which is 8191 * 2^-8.
+ */
+static constexpr double kMinDigitalGain = 1.0;
+static constexpr double kMaxDigitalGain = 31.99609375;
+
+uint32_t AgcStatistics::decodeBinValue(uint16_t binVal)
+{
+ int exponent = (binVal & 0xf000) >> 12;
+ int mantissa = binVal & 0xfff;
+
+ if (!exponent)
+ return mantissa * 2;
+ else
+ return (mantissa + 4096) * std::pow(2, exponent);
+}
+
+/*
+ * We configure the ISP to give us 4 histograms of 256 bins each, with
+ * a single histogram per colour channel (R/Gr/Gb/B). The memory space
+ * containing the data is a single block containing all 4 histograms
+ * with the position of each colour's histogram within it dependent on
+ * the bayer pattern of the data input to the ISP.
+ *
+ * NOTE: The validity of this function depends on the parameters we have
+ * configured. With different skip/offset x, y values not all of the
+ * colour channels would be populated, and they may not be in the same
+ * planes as calculated here.
+ */
+int AgcStatistics::setBayerOrderIndices(BayerFormat::Order bayerOrder)
+{
+ switch (bayerOrder) {
+ case BayerFormat::Order::RGGB:
+ rIndex_ = 0;
+ grIndex_ = 1;
+ gbIndex_ = 2;
+ bIndex_ = 3;
+ break;
+ case BayerFormat::Order::GRBG:
+ grIndex_ = 0;
+ rIndex_ = 1;
+ bIndex_ = 2;
+ gbIndex_ = 3;
+ break;
+ case BayerFormat::Order::GBRG:
+ gbIndex_ = 0;
+ bIndex_ = 1;
+ rIndex_ = 2;
+ grIndex_ = 3;
+ break;
+ case BayerFormat::Order::BGGR:
+ bIndex_ = 0;
+ gbIndex_ = 1;
+ grIndex_ = 2;
+ rIndex_ = 3;
+ break;
+ default:
+ LOG(MaliC55Agc, Error)
+ << "Invalid bayer format " << bayerOrder;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void AgcStatistics::parseStatistics(const mali_c55_stats_buffer *stats)
+{
+ uint32_t r[256], g[256], b[256], y[256];
+
+ /*
+ * We need to decode the bin values for each histogram from their 16-bit
+ * compressed values to a 32-bit value. We also take the average of the
+ * Gr/Gb values into a single green histogram.
+ */
+ for (unsigned int i = 0; i < 256; i++) {
+ r[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * rIndex_)]);
+ g[i] = (decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * grIndex_)]) +
+ decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * gbIndex_)])) / 2;
+ b[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * bIndex_)]);
+
+ y[i] = rec601LuminanceFromRGB({ { static_cast<double>(r[i]),
+ static_cast<double>(g[i]),
+ static_cast<double>(b[i]) } });
+ }
+
+ rHist = Histogram(Span<uint32_t>(r, kNumHistogramBins));
+ gHist = Histogram(Span<uint32_t>(g, kNumHistogramBins));
+ bHist = Histogram(Span<uint32_t>(b, kNumHistogramBins));
+ yHist = Histogram(Span<uint32_t>(y, kNumHistogramBins));
+}
+
+Agc::Agc()
+ : AgcMeanLuminance()
+{
+}
+
+int Agc::init(IPAContext &context, const YamlObject &tuningData)
+{
+ int ret = parseTuningData(tuningData);
+ if (ret)
+ return ret;
+
+ context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true);
+ context.ctrlMap[&controls::DigitalGain] = ControlInfo(
+ static_cast<float>(kMinDigitalGain),
+ static_cast<float>(kMaxDigitalGain),
+ static_cast<float>(kMinDigitalGain)
+ );
+ context.ctrlMap.merge(controls());
+
+ return 0;
+}
+
+int Agc::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ int ret = statistics_.setBayerOrderIndices(context.configuration.sensor.bayerOrder);
+ if (ret)
+ return ret;
+
+ /*
+ * Defaults; we use whatever the sensor's default exposure is and the
+ * minimum analogue gain. AEGC is _active_ by default.
+ */
+ context.activeState.agc.autoEnabled = true;
+ context.activeState.agc.automatic.sensorGain = context.configuration.agc.minAnalogueGain;
+ context.activeState.agc.automatic.exposure = context.configuration.agc.defaultExposure;
+ context.activeState.agc.automatic.ispGain = kMinDigitalGain;
+ context.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain;
+ context.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure;
+ context.activeState.agc.manual.ispGain = kMinDigitalGain;
+ context.activeState.agc.constraintMode = constraintModes().begin()->first;
+ context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;
+
+ /* \todo Run this again when FrameDurationLimits is passed in */
+ setLimits(context.configuration.agc.minShutterSpeed,
+ context.configuration.agc.maxShutterSpeed,
+ context.configuration.agc.minAnalogueGain,
+ context.configuration.agc.maxAnalogueGain);
+
+ resetFrameCount();
+
+ return 0;
+}
+
+void Agc::queueRequest(IPAContext &context, const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ const ControlList &controls)
+{
+ auto &agc = context.activeState.agc;
+
+ const auto &constraintMode = controls.get(controls::AeConstraintMode);
+ agc.constraintMode = constraintMode.value_or(agc.constraintMode);
+
+ const auto &exposureMode = controls.get(controls::AeExposureMode);
+ agc.exposureMode = exposureMode.value_or(agc.exposureMode);
+
+ const auto &agcEnable = controls.get(controls::AeEnable);
+ if (agcEnable && *agcEnable != agc.autoEnabled) {
+ agc.autoEnabled = *agcEnable;
+
+ LOG(MaliC55Agc, Info)
+ << (agc.autoEnabled ? "Enabling" : "Disabling")
+ << " AGC";
+ }
+
+ /*
+ * If the automatic exposure and gain is enabled we have no further work
+ * to do here...
+ */
+ if (agc.autoEnabled)
+ return;
+
+ /*
+ * ...otherwise we need to look for exposure and gain controls and use
+ * those to set the activeState.
+ */
+ const auto &exposure = controls.get(controls::ExposureTime);
+ if (exposure) {
+ agc.manual.exposure = *exposure * 1.0us / context.configuration.sensor.lineDuration;
+
+ LOG(MaliC55Agc, Debug)
+ << "Exposure set to " << agc.manual.exposure
+ << " on request sequence " << frame;
+ }
+
+ const auto &analogueGain = controls.get(controls::AnalogueGain);
+ if (analogueGain) {
+ agc.manual.sensorGain = *analogueGain;
+
+ LOG(MaliC55Agc, Debug)
+ << "Analogue gain set to " << agc.manual.sensorGain
+ << " on request sequence " << frame;
+ }
+
+ const auto &digitalGain = controls.get(controls::DigitalGain);
+ if (digitalGain) {
+ agc.manual.ispGain = *digitalGain;
+
+ LOG(MaliC55Agc, Debug)
+ << "Digital gain set to " << agc.manual.ispGain
+ << " on request sequence " << frame;
+ }
+}
+
+size_t Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext,
+ mali_c55_params_block block)
+{
+ IPAActiveState &activeState = context.activeState;
+ double gain;
+
+ if (activeState.agc.autoEnabled)
+ gain = activeState.agc.automatic.ispGain;
+ else
+ gain = activeState.agc.manual.ispGain;
+
+ block.header->type = MALI_C55_PARAM_BLOCK_DIGITAL_GAIN;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_digital_gain);
+
+ block.digital_gain->gain = floatingToFixedPoint<5, 8, uint16_t, double>(gain);
+ frameContext.agc.ispGain = gain;
+
+ return block.header->size;
+}
+
+size_t Agc::fillParamsBuffer(mali_c55_params_block block,
+ enum mali_c55_param_block_type type)
+{
+ block.header->type = type;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_aexp_hist);
+
+ /* Collect every 3rd pixel horizontally */
+ block.aexp_hist->skip_x = 1;
+ /* Start from first column */
+ block.aexp_hist->offset_x = 0;
+ /* Collect every pixel vertically */
+ block.aexp_hist->skip_y = 0;
+ /* Start from the first row */
+ block.aexp_hist->offset_y = 0;
+ /* 1x scaling (i.e. none) */
+ block.aexp_hist->scale_bottom = 0;
+ block.aexp_hist->scale_top = 0;
+ /* Collect all Bayer planes into 4 separate histograms */
+ block.aexp_hist->plane_mode = 1;
+ /* Tap the data immediately after the digital gain block */
+ block.aexp_hist->tap_point = MALI_C55_AEXP_HIST_TAP_FS;
+
+ return block.header->size;
+}
+
+size_t Agc::fillWeightsArrayBuffer(mali_c55_params_block block,
+ enum mali_c55_param_block_type type)
+{
+ block.header->type = type;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_aexp_weights);
+
+ /* We use every zone - a 15x15 grid */
+ block.aexp_weights->nodes_used_horiz = 15;
+ block.aexp_weights->nodes_used_vert = 15;
+
+ /*
+ * We uniformly weight the zones to 1 - this results in the collected
+ * histograms containing a true pixel count, which we can then use to
+ * approximate colour channel averages for the image.
+ */
+ Span<uint8_t> weights{
+ block.aexp_weights->zone_weights,
+ MALI_C55_MAX_ZONES
+ };
+ std::fill(weights.begin(), weights.end(), 1);
+
+ return block.header->size;
+}
+
+void Agc::prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, mali_c55_params_buffer *params)
+{
+ mali_c55_params_block block;
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillGainParamBlock(context, frameContext, block);
+
+ if (frame > 0)
+ return;
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillParamsBuffer(block,
+ MALI_C55_PARAM_BLOCK_AEXP_HIST);
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillWeightsArrayBuffer(block,
+ MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS);
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillParamsBuffer(block,
+ MALI_C55_PARAM_BLOCK_AEXP_IHIST);
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillWeightsArrayBuffer(block,
+ MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS);
+}
+
+double Agc::estimateLuminance(const double gain) const
+{
+ double rAvg = statistics_.rHist.interQuantileMean(0, 1) * gain;
+ double gAvg = statistics_.gHist.interQuantileMean(0, 1) * gain;
+ double bAvg = statistics_.bHist.interQuantileMean(0, 1) * gain;
+ double yAvg = rec601LuminanceFromRGB({ { rAvg, gAvg, bAvg } });
+
+ return yAvg / kNumHistogramBins;
+}
+
+void Agc::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ [[maybe_unused]] ControlList &metadata)
+{
+ IPASessionConfiguration &configuration = context.configuration;
+ IPAActiveState &activeState = context.activeState;
+
+ if (!stats) {
+ LOG(MaliC55Agc, Error) << "No statistics buffer passed to Agc";
+ return;
+ }
+
+ statistics_.parseStatistics(stats);
+ context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1),
+ statistics_.gHist.interQuantileMean(0, 1),
+ statistics_.bHist.interQuantileMean(0, 1) } });
+
+ /*
+ * The Agc algorithm needs to know the effective exposure value that was
+ * applied to the sensor when the statistics were collected.
+ */
+ uint32_t exposure = frameContext.agc.exposure;
+ double analogueGain = frameContext.agc.sensorGain;
+ double digitalGain = frameContext.agc.ispGain;
+ double totalGain = analogueGain * digitalGain;
+ utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
+ utils::Duration effectiveExposureValue = currentShutter * totalGain;
+
+ utils::Duration shutterTime;
+ double aGain, dGain;
+ std::tie(shutterTime, aGain, dGain) =
+ calculateNewEv(activeState.agc.constraintMode,
+ activeState.agc.exposureMode, statistics_.yHist,
+ effectiveExposureValue);
+
+ dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain);
+
+ LOG(MaliC55Agc, Debug)
+ << "Divided up shutter, analogue gain and digital gain are "
+ << shutterTime << ", " << aGain << " and " << dGain;
+
+ activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
+ activeState.agc.automatic.sensorGain = aGain;
+ activeState.agc.automatic.ispGain = dGain;
+
+ metadata.set(controls::ExposureTime, currentShutter.get<std::micro>());
+ metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);
+ metadata.set(controls::DigitalGain, frameContext.agc.ispGain);
+ metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK);
+}
+
+REGISTER_IPA_ALGORITHM(Agc, "Agc")
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h
new file mode 100644
index 00000000..c5c574e5
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/agc.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * agc.h - Mali C55 AGC/AEC mean-based control algorithm
+ */
+
+#pragma once
+
+#include <libcamera/base/utils.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+#include "libipa/agc_mean_luminance.h"
+#include "libipa/histogram.h"
+
+#include "algorithm.h"
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+class AgcStatistics
+{
+public:
+ AgcStatistics()
+ {
+ }
+
+ int setBayerOrderIndices(BayerFormat::Order bayerOrder);
+ uint32_t decodeBinValue(uint16_t binVal);
+ void parseStatistics(const mali_c55_stats_buffer *stats);
+
+ Histogram rHist;
+ Histogram gHist;
+ Histogram bHist;
+ Histogram yHist;
+private:
+ unsigned int rIndex_;
+ unsigned int grIndex_;
+ unsigned int gbIndex_;
+ unsigned int bIndex_;
+};
+
+class Agc : public Algorithm, public AgcMeanLuminance
+{
+public:
+ Agc();
+ ~Agc() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ double estimateLuminance(const double gain) const override;
+ size_t fillGainParamBlock(IPAContext &context,
+ IPAFrameContext &frameContext,
+ mali_c55_params_block block);
+ size_t fillParamsBuffer(mali_c55_params_block block,
+ enum mali_c55_param_block_type type);
+ size_t fillWeightsArrayBuffer(mali_c55_params_block block,
+ enum mali_c55_param_block_type type);
+
+ AgcStatistics statistics_;
+};
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/algorithm.h b/src/ipa/mali-c55/algorithms/algorithm.h
new file mode 100644
index 00000000..36a3bff0
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/algorithm.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * algorithm.h - Mali-C55 control algorithm interface
+ */
+
+#pragma once
+
+#include <linux/mali-c55-config.h>
+
+#include <libipa/algorithm.h>
+
+#include "module.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55 {
+
+class Algorithm : public libcamera::ipa::Algorithm<Module>
+{
+};
+
+union mali_c55_params_block {
+ struct mali_c55_params_block_header *header;
+ struct mali_c55_params_sensor_off_preshading *sensor_offs;
+ struct mali_c55_params_aexp_hist *aexp_hist;
+ struct mali_c55_params_aexp_weights *aexp_weights;
+ struct mali_c55_params_digital_gain *digital_gain;
+ struct mali_c55_params_awb_gains *awb_gains;
+ struct mali_c55_params_awb_config *awb_config;
+ struct mali_c55_params_mesh_shading_config *shading_config;
+ struct mali_c55_params_mesh_shading_selection *shading_selection;
+ __u8 *data;
+};
+
+} /* namespace ipa::mali_c55 */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
new file mode 100644
index 00000000..050b191b
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/awb.cpp
@@ -0,0 +1,230 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * awb.cpp - Mali C55 grey world auto white balance algorithm
+ */
+
+#include "awb.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libipa/fixedpoint.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+LOG_DEFINE_CATEGORY(MaliC55Awb)
+
+/* Number of frames at which we should run AWB at full speed */
+static constexpr uint32_t kNumStartupFrames = 4;
+
+Awb::Awb()
+{
+}
+
+int Awb::configure([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ /*
+ * Initially we have no idea what the colour balance will be like, so
+ * for the first frame we will make no assumptions and leave the R/B
+ * channels unmodified.
+ */
+ context.activeState.awb.rGain = 1.0;
+ context.activeState.awb.bGain = 1.0;
+
+ return 0;
+}
+
+size_t Awb::fillGainsParamBlock(mali_c55_params_block block, IPAContext &context,
+ IPAFrameContext &frameContext)
+{
+ block.header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_awb_gains);
+
+ double rGain = context.activeState.awb.rGain;
+ double bGain = context.activeState.awb.bGain;
+
+ /*
+ * The gains here map as follows:
+ * gain00 = R
+ * gain01 = Gr
+ * gain10 = Gb
+ * gain11 = B
+ *
+ * This holds true regardless of the bayer order of the input data, as
+ * the mapping is done internally in the ISP.
+ */
+ block.awb_gains->gain00 = floatingToFixedPoint<4, 8, uint16_t, double>(rGain);
+ block.awb_gains->gain01 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
+ block.awb_gains->gain10 = floatingToFixedPoint<4, 8, uint16_t, double>(1.0);
+ block.awb_gains->gain11 = floatingToFixedPoint<4, 8, uint16_t, double>(bGain);
+
+ frameContext.awb.rGain = rGain;
+ frameContext.awb.bGain = bGain;
+
+ return sizeof(struct mali_c55_params_awb_gains);
+}
+
+size_t Awb::fillConfigParamBlock(mali_c55_params_block block)
+{
+ block.header->type = MALI_C55_PARAM_BLOCK_AWB_CONFIG;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_awb_config);
+
+ /* Tap the stats after the purple fringe block */
+ block.awb_config->tap_point = MALI_C55_AWB_STATS_TAP_PF;
+
+ /* Get R/G and B/G ratios as statistics */
+ block.awb_config->stats_mode = MALI_C55_AWB_MODE_RGBG;
+
+ /* Default white level */
+ block.awb_config->white_level = 1023;
+
+ /* Default black level */
+ block.awb_config->black_level = 0;
+
+ /*
+ * By default pixels are included who's colour ratios are bounded in a
+ * region (on a cr ratio x cb ratio graph) defined by four points:
+ * (0.25, 0.25)
+ * (0.25, 1.99609375)
+ * (1.99609375, 1.99609375)
+ * (1.99609375, 0.25)
+ *
+ * The ratios themselves are stored in Q4.8 format.
+ *
+ * \todo should these perhaps be tunable?
+ */
+ block.awb_config->cr_max = 511;
+ block.awb_config->cr_min = 64;
+ block.awb_config->cb_max = 511;
+ block.awb_config->cb_min = 64;
+
+ /* We use the full 15x15 zoning scheme */
+ block.awb_config->nodes_used_horiz = 15;
+ block.awb_config->nodes_used_vert = 15;
+
+ /*
+ * We set the trimming boundaries equivalent to the main boundaries. In
+ * other words; no trimming.
+ */
+ block.awb_config->cr_high = 511;
+ block.awb_config->cr_low = 64;
+ block.awb_config->cb_high = 511;
+ block.awb_config->cb_low = 64;
+
+ return sizeof(struct mali_c55_params_awb_config);
+}
+
+void Awb::prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, mali_c55_params_buffer *params)
+{
+ mali_c55_params_block block;
+ block.data = &params->data[params->total_size];
+
+ params->total_size += fillGainsParamBlock(block, context, frameContext);
+
+ if (frame > 0)
+ return;
+
+ block.data = &params->data[params->total_size];
+ params->total_size += fillConfigParamBlock(block);
+}
+
+void Awb::process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, const mali_c55_stats_buffer *stats,
+ [[maybe_unused]] ControlList &metadata)
+{
+ const struct mali_c55_awb_average_ratios *awb_ratios = stats->awb_ratios;
+
+ /*
+ * The ISP produces average R:G and B:G ratios for zones. We take the
+ * average of all the zones with data and simply invert them to provide
+ * gain figures that we can apply to approximate a grey world.
+ */
+ unsigned int counted_zones = 0;
+ double rgSum = 0, bgSum = 0;
+
+ for (unsigned int i = 0; i < 225; i++) {
+ if (!awb_ratios[i].num_pixels)
+ continue;
+
+ /*
+ * The statistics are in Q4.8 format, so we convert to double
+ * here.
+ */
+ rgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_rg_gr);
+ bgSum += fixedToFloatingPoint<4, 8, double, uint16_t>(awb_ratios[i].avg_bg_br);
+ counted_zones++;
+ }
+
+ /*
+ * Sometimes the first frame's statistics have no valid pixels, in which
+ * case we'll just assume a grey world until they say otherwise.
+ */
+ double rgAvg, bgAvg;
+ if (!counted_zones) {
+ rgAvg = 1.0;
+ bgAvg = 1.0;
+ } else {
+ rgAvg = rgSum / counted_zones;
+ bgAvg = bgSum / counted_zones;
+ }
+
+ /*
+ * The statistics are generated _after_ white balancing is performed in
+ * the ISP. To get the true ratio we therefore have to adjust the stats
+ * figure by the gains that were applied when the statistics for this
+ * frame were generated.
+ */
+ double rRatio = rgAvg / frameContext.awb.rGain;
+ double bRatio = bgAvg / frameContext.awb.bGain;
+
+ /*
+ * And then we can simply invert the ratio to find the gain we should
+ * apply.
+ */
+ double rGain = 1 / rRatio;
+ double bGain = 1 / bRatio;
+
+ /*
+ * Running at full speed, this algorithm results in oscillations in the
+ * colour balance. To remove those we dampen the speed at which it makes
+ * changes in gain, unless we're in the startup phase in which case we
+ * want to fix the miscolouring as quickly as possible.
+ */
+ double speed = frame < kNumStartupFrames ? 1.0 : 0.2;
+ rGain = speed * rGain + context.activeState.awb.rGain * (1.0 - speed);
+ bGain = speed * bGain + context.activeState.awb.bGain * (1.0 - speed);
+
+ context.activeState.awb.rGain = rGain;
+ context.activeState.awb.bGain = bGain;
+
+ metadata.set(controls::ColourGains, {
+ static_cast<float>(frameContext.awb.rGain),
+ static_cast<float>(frameContext.awb.bGain),
+ });
+
+ LOG(MaliC55Awb, Debug) << "For frame number " << frame << ": "
+ << "Average R/G Ratio: " << rgAvg
+ << ", Average B/G Ratio: " << bgAvg
+ << "\nrGain applied to this frame: " << frameContext.awb.rGain
+ << ", bGain applied to this frame: " << frameContext.awb.bGain
+ << "\nrGain to apply: " << context.activeState.awb.rGain
+ << ", bGain to apply: " << context.activeState.awb.bGain;
+}
+
+REGISTER_IPA_ALGORITHM(Awb, "Awb")
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/awb.h b/src/ipa/mali-c55/algorithms/awb.h
new file mode 100644
index 00000000..800c2e83
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/awb.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * awb.h - Mali C55 grey world auto white balance algorithm
+ */
+
+#include "algorithm.h"
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+class Awb : public Algorithm
+{
+public:
+ Awb();
+ ~Awb() = default;
+
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ size_t fillGainsParamBlock(mali_c55_params_block block,
+ IPAContext &context,
+ IPAFrameContext &frameContext);
+ size_t fillConfigParamBlock(mali_c55_params_block block);
+};
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp
new file mode 100644
index 00000000..2a54c86a
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/blc.cpp
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * Mali-C55 sensor offset (black level) correction
+ */
+
+#include "blc.h"
+
+#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+/**
+ * \file blc.h
+ */
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+/**
+ * \class BlackLevelCorrection
+ * \brief MaliC55 Black Level Correction control
+ */
+
+LOG_DEFINE_CATEGORY(MaliC55Blc)
+
+BlackLevelCorrection::BlackLevelCorrection()
+ : tuningParameters_(false)
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context,
+ const YamlObject &tuningData)
+{
+ offset00 = tuningData["offset00"].get<uint32_t>(0);
+ offset01 = tuningData["offset01"].get<uint32_t>(0);
+ offset10 = tuningData["offset10"].get<uint32_t>(0);
+ offset11 = tuningData["offset11"].get<uint32_t>(0);
+
+ if (offset00 > kMaxOffset || offset01 > kMaxOffset ||
+ offset10 > kMaxOffset || offset11 > kMaxOffset) {
+ LOG(MaliC55Blc, Error) << "Invalid black level offsets";
+ return -EINVAL;
+ }
+
+ tuningParameters_ = true;
+
+ LOG(MaliC55Blc, Debug)
+ << "Black levels: 00 " << offset00 << ", 01 " << offset01
+ << ", 10 " << offset10 << ", 11 " << offset11;
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int BlackLevelCorrection::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ /*
+ * If no Black Levels were passed in through tuning data then we could
+ * use the value from the CameraSensorHelper if one is available.
+ */
+ if (context.configuration.sensor.blackLevel &&
+ !(offset00 + offset01 + offset10 + offset11)) {
+ offset00 = context.configuration.sensor.blackLevel;
+ offset01 = context.configuration.sensor.blackLevel;
+ offset10 = context.configuration.sensor.blackLevel;
+ offset11 = context.configuration.sensor.blackLevel;
+ }
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
+ const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params)
+{
+ mali_c55_params_block block;
+ block.data = &params->data[params->total_size];
+
+ if (frame > 0)
+ return;
+
+ if (!tuningParameters_)
+ return;
+
+ block.header->type = MALI_C55_PARAM_BLOCK_SENSOR_OFFS;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(mali_c55_params_sensor_off_preshading);
+
+ block.sensor_offs->chan00 = offset00;
+ block.sensor_offs->chan01 = offset01;
+ block.sensor_offs->chan10 = offset10;
+ block.sensor_offs->chan11 = offset11;
+
+ params->total_size += block.header->size;
+}
+
+void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ [[maybe_unused]] const mali_c55_stats_buffer *stats,
+ ControlList &metadata)
+{
+ /*
+ * Black Level Offsets in tuning data need to be 20-bit, whereas the
+ * metadata expects values from a 16-bit range. Right-shift to remove
+ * the 4 least significant bits.
+ *
+ * The black levels should be reported in the order R, Gr, Gb, B. We
+ * ignore that here given we're using matching values so far, but it
+ * would be safer to check the sensor's bayer order.
+ *
+ * \todo Account for bayer order.
+ */
+ metadata.set(controls::SensorBlackLevels, {
+ static_cast<int32_t>(offset00 >> 4),
+ static_cast<int32_t>(offset01 >> 4),
+ static_cast<int32_t>(offset10 >> 4),
+ static_cast<int32_t>(offset11 >> 4),
+ });
+}
+
+REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/blc.h b/src/ipa/mali-c55/algorithms/blc.h
new file mode 100644
index 00000000..9696e8e9
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/blc.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * Mali-C55 sensor offset (black level) correction
+ */
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+class BlackLevelCorrection : public Algorithm
+{
+public:
+ BlackLevelCorrection();
+ ~BlackLevelCorrection() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const mali_c55_stats_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ static constexpr uint32_t kMaxOffset = 0xfffff;
+
+ bool tuningParameters_;
+ uint32_t offset00;
+ uint32_t offset01;
+ uint32_t offset10;
+ uint32_t offset11;
+};
+
+} /* namespace ipa::mali_c55::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
new file mode 100644
index 00000000..c5afc04d
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/lsc.cpp
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * lsc.cpp - Mali-C55 Lens shading correction algorithm
+ */
+
+#include "lsc.h"
+
+#include "libcamera/internal/yaml_parser.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+LOG_DEFINE_CATEGORY(MaliC55Lsc)
+
+int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
+{
+ if (!tuningData.contains("meshScale")) {
+ LOG(MaliC55Lsc, Error) << "meshScale missing from tuningData";
+ return -EINVAL;
+ }
+
+ meshScale_ = tuningData["meshScale"].get<uint32_t>(0);
+
+ const YamlObject &yamlSets = tuningData["sets"];
+ if (!yamlSets.isList()) {
+ LOG(MaliC55Lsc, Error) << "LSC tables missing or invalid";
+ return -EINVAL;
+ }
+
+ size_t tableSize = 0;
+ const auto &sets = yamlSets.asList();
+ for (const auto &yamlSet : sets) {
+ uint32_t ct = yamlSet["ct"].get<uint32_t>(0);
+
+ if (!ct) {
+ LOG(MaliC55Lsc, Error) << "Invalid colour temperature";
+ return -EINVAL;
+ }
+
+ if (std::count(colourTemperatures_.begin(),
+ colourTemperatures_.end(), ct)) {
+ LOG(MaliC55Lsc, Error)
+ << "Multiple sets found for colour temperature";
+ return -EINVAL;
+ }
+
+ std::vector<uint8_t> rTable =
+ yamlSet["r"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+ std::vector<uint8_t> gTable =
+ yamlSet["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+ std::vector<uint8_t> bTable =
+ yamlSet["b"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+
+ /*
+ * Some validation to do; only 16x16 and 32x32 tables of
+ * coefficients are acceptable, and all tables across all of the
+ * sets must be the same size. The first time we encounter a
+ * table we check that it is an acceptable size and if so make
+ * sure all other tables are of equal size.
+ */
+ if (!tableSize) {
+ if (rTable.size() != 256 && rTable.size() != 1024) {
+ LOG(MaliC55Lsc, Error)
+ << "Invalid table size for colour temperature " << ct;
+ return -EINVAL;
+ }
+ tableSize = rTable.size();
+ }
+
+ if (rTable.size() != tableSize ||
+ gTable.size() != tableSize ||
+ bTable.size() != tableSize) {
+ LOG(MaliC55Lsc, Error)
+ << "Invalid or mismatched table size for colour temperature " << ct;
+ return -EINVAL;
+ }
+
+ if (colourTemperatures_.size() >= 3) {
+ LOG(MaliC55Lsc, Error)
+ << "A maximum of 3 colour temperatures are supported";
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < tableSize; i++) {
+ mesh_[kRedOffset + i] |=
+ (rTable[i] << (colourTemperatures_.size() * 8));
+ mesh_[kGreenOffset + i] |=
+ (gTable[i] << (colourTemperatures_.size() * 8));
+ mesh_[kBlueOffset + i] |=
+ (bTable[i] << (colourTemperatures_.size() * 8));
+ }
+
+ colourTemperatures_.push_back(ct);
+ }
+
+ /*
+ * The mesh has either 16x16 or 32x32 nodes, we tell the driver which it
+ * is based on the number of values in the tuning data's table.
+ */
+ if (tableSize == 256)
+ meshSize_ = 15;
+ else
+ meshSize_ = 31;
+
+ return 0;
+}
+
+size_t Lsc::fillConfigParamsBlock(mali_c55_params_block block) const
+{
+ block.header->type = MALI_C55_PARAM_MESH_SHADING_CONFIG;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_mesh_shading_config);
+
+ block.shading_config->mesh_show = false;
+ block.shading_config->mesh_scale = meshScale_;
+ block.shading_config->mesh_page_r = 0;
+ block.shading_config->mesh_page_g = 1;
+ block.shading_config->mesh_page_b = 2;
+ block.shading_config->mesh_width = meshSize_;
+ block.shading_config->mesh_height = meshSize_;
+
+ std::copy(mesh_.begin(), mesh_.end(), block.shading_config->mesh);
+
+ return block.header->size;
+}
+
+size_t Lsc::fillSelectionParamsBlock(mali_c55_params_block block, uint8_t bank,
+ uint8_t alpha) const
+{
+ block.header->type = MALI_C55_PARAM_MESH_SHADING_SELECTION;
+ block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
+ block.header->size = sizeof(struct mali_c55_params_mesh_shading_selection);
+
+ block.shading_selection->mesh_alpha_bank_r = bank;
+ block.shading_selection->mesh_alpha_bank_g = bank;
+ block.shading_selection->mesh_alpha_bank_b = bank;
+ block.shading_selection->mesh_alpha_r = alpha;
+ block.shading_selection->mesh_alpha_g = alpha;
+ block.shading_selection->mesh_alpha_b = alpha;
+ block.shading_selection->mesh_strength = 0x1000; /* Otherwise known as 1.0 */
+
+ return block.header->size;
+}
+
+std::tuple<uint8_t, uint8_t> Lsc::findBankAndAlpha(uint32_t ct) const
+{
+ unsigned int i;
+
+ ct = std::clamp<uint32_t>(ct, colourTemperatures_.front(),
+ colourTemperatures_.back());
+
+ for (i = 0; i < colourTemperatures_.size() - 1; i++) {
+ if (ct >= colourTemperatures_[i] &&
+ ct <= colourTemperatures_[i + 1])
+ break;
+ }
+
+ /*
+ * With the clamping, we're guaranteed an index into colourTemperatures_
+ * that's <= colourTemperatures_.size() - 1.
+ */
+ uint8_t alpha = (255 * (ct - colourTemperatures_[i])) /
+ (colourTemperatures_[i + 1] - colourTemperatures_[i]);
+
+ return { i, alpha };
+}
+
+void Lsc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params)
+{
+ /*
+ * For each frame we assess the colour temperature of the **last** frame
+ * and then select an appropriately blended table of coefficients based
+ * on that ct. As a bit of a shortcut, if we've only a single table the
+ * handling is somewhat simpler; if it's the first frame we just select
+ * that table and if we're past the first frame then we can just do
+ * nothing - the config will never change.
+ */
+ uint32_t temperatureK = context.activeState.agc.temperatureK;
+ uint8_t bank, alpha;
+
+ if (colourTemperatures_.size() == 1) {
+ if (frame > 0)
+ return;
+
+ bank = 0;
+ alpha = 0;
+ } else {
+ std::tie(bank, alpha) = findBankAndAlpha(temperatureK);
+ }
+
+ mali_c55_params_block block;
+ block.data = &params->data[params->total_size];
+
+ params->total_size += fillSelectionParamsBlock(block, bank, alpha);
+
+ if (frame > 0)
+ return;
+
+ /*
+ * If this is the first frame, we need to load the parsed coefficient
+ * tables from tuning data to the ISP.
+ */
+ block.data = &params->data[params->total_size];
+ params->total_size += fillConfigParamsBlock(block);
+}
+
+REGISTER_IPA_ALGORITHM(Lsc, "Lsc")
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h
new file mode 100644
index 00000000..e613277a
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/lsc.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * lsc.h - Mali-C55 Lens shading correction algorithm
+ */
+
+#include <map>
+#include <tuple>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55::algorithms {
+
+class Lsc : public Algorithm
+{
+public:
+ Lsc() = default;
+ ~Lsc() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ mali_c55_params_buffer *params) override;
+private:
+ static constexpr unsigned int kRedOffset = 0;
+ static constexpr unsigned int kGreenOffset = 1024;
+ static constexpr unsigned int kBlueOffset = 2048;
+
+ size_t fillConfigParamsBlock(mali_c55_params_block block) const;
+ size_t fillSelectionParamsBlock(mali_c55_params_block block,
+ uint8_t bank, uint8_t alpha) const;
+ std::tuple<uint8_t, uint8_t> findBankAndAlpha(uint32_t ct) const;
+
+ std::vector<uint32_t> mesh_ = std::vector<uint32_t>(3072);
+ std::vector<uint32_t> colourTemperatures_;
+ uint32_t meshScale_;
+ uint32_t meshSize_;
+};
+
+} /* namespace ipa::mali_c55::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/algorithms/meson.build b/src/ipa/mali-c55/algorithms/meson.build
new file mode 100644
index 00000000..1665da07
--- /dev/null
+++ b/src/ipa/mali-c55/algorithms/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+
+mali_c55_ipa_algorithms = files([
+ 'agc.cpp',
+ 'awb.cpp',
+ 'blc.cpp',
+ 'lsc.cpp',
+])
diff --git a/src/ipa/mali-c55/data/imx415.yaml b/src/ipa/mali-c55/data/imx415.yaml
new file mode 100644
index 00000000..126b427a
--- /dev/null
+++ b/src/ipa/mali-c55/data/imx415.yaml
@@ -0,0 +1,325 @@
+# SPDX-License-Identifier: CC0-1.0
+%YAML 1.1
+---
+version: 1
+algorithms:
+ - Agc:
+ - Awb:
+ - BlackLevelCorrection:
+ offset00: 51200
+ offset01: 51200
+ offset10: 51200
+ offset11: 51200
+ - Lsc:
+ meshScale: 4 # 1.0 - 2.0 Gain
+ sets:
+ - ct: 2500
+ r: [
+ 21, 20, 19, 17, 15, 14, 12, 11, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 13, 13, 10, 10, 13, 16, 17, 18, 21, 22,
+ 21, 20, 18, 16, 14, 13, 12, 11, 10, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 12, 15, 17, 18, 21, 21,
+ 20, 19, 17, 16, 14, 13, 12, 11, 10, 9, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 8, 8, 8, 11, 15, 17, 18, 21, 21,
+ 19, 19, 17, 15, 14, 13, 12, 11, 10, 8, 8, 7, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 8, 7, 7, 8, 10, 14, 17, 18, 20, 22,
+ 19, 18, 17, 15, 14, 13, 11, 11, 9, 8, 8, 7, 7, 6, 5, 5, 5, 5, 6, 7, 8, 7, 7, 6, 7, 7, 10, 12, 16, 18, 20, 22,
+ 18, 18, 16, 15, 14, 12, 11, 10, 9, 8, 6, 6, 5, 5, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 8, 12, 16, 18, 19, 20,
+ 18, 18, 16, 14, 13, 12, 11, 9, 9, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 4, 4, 5, 5, 5, 5, 6, 8, 11, 15, 18, 18, 19,
+ 18, 17, 15, 14, 13, 12, 11, 9, 8, 7, 6, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 7, 9, 14, 17, 18, 18,
+ 18, 17, 15, 14, 13, 12, 11, 9, 8, 7, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 8, 12, 17, 18, 18,
+ 18, 16, 15, 13, 12, 11, 10, 9, 8, 7, 5, 4, 4, 4, 4, 3, 3, 4, 4, 4, 3, 3, 4, 4, 4, 5, 6, 8, 12, 16, 19, 19,
+ 17, 16, 15, 13, 12, 11, 10, 8, 7, 6, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 4, 5, 6, 9, 12, 16, 19, 20,
+ 17, 15, 15, 13, 12, 11, 10, 8, 6, 6, 5, 4, 3, 3, 3, 2, 3, 3, 4, 4, 3, 3, 2, 3, 4, 5, 6, 9, 11, 16, 19, 20,
+ 17, 15, 15, 14, 11, 11, 10, 8, 6, 5, 5, 4, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 4, 5, 6, 8, 11, 16, 18, 19,
+ 16, 16, 15, 13, 11, 11, 10, 7, 6, 5, 4, 4, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 3, 4, 6, 8, 11, 14, 17, 18,
+ 16, 16, 14, 13, 11, 10, 9, 7, 6, 5, 4, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 3, 4, 6, 8, 10, 14, 17, 18,
+ 16, 15, 14, 13, 13, 10, 9, 7, 6, 4, 4, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 4, 6, 8, 11, 17, 18, 19,
+ 16, 15, 14, 14, 13, 12, 9, 8, 7, 5, 4, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 8, 12, 17, 19, 20,
+ 17, 15, 15, 14, 13, 12, 9, 8, 7, 5, 3, 2, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 6, 8, 13, 16, 19, 21,
+ 17, 16, 15, 13, 13, 12, 9, 8, 7, 5, 3, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 7, 8, 13, 16, 19, 20,
+ 17, 16, 15, 14, 13, 12, 9, 8, 7, 5, 3, 2, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 9, 13, 17, 19, 20,
+ 18, 16, 15, 14, 13, 12, 9, 8, 7, 5, 4, 2, 1, 0, 0, 0, 0, 1, 2, 2, 3, 3, 3, 4, 5, 6, 8, 9, 13, 17, 20, 20,
+ 18, 16, 16, 15, 14, 12, 10, 9, 7, 6, 5, 3, 2, 1, 0, 0, 0, 1, 2, 3, 3, 3, 4, 5, 6, 7, 9, 10, 14, 18, 20, 20,
+ 18, 17, 16, 15, 14, 12, 10, 9, 8, 7, 6, 5, 3, 3, 1, 0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 9, 12, 15, 19, 20, 20,
+ 18, 18, 17, 16, 14, 13, 11, 10, 9, 8, 7, 6, 5, 5, 3, 1, 1, 1, 2, 3, 5, 5, 5, 6, 7, 9, 12, 15, 17, 19, 20, 20,
+ 18, 18, 17, 16, 15, 13, 12, 10, 10, 9, 8, 7, 6, 5, 4, 2, 1, 2, 3, 4, 5, 5, 6, 6, 8, 10, 13, 16, 18, 20, 20, 21,
+ 19, 18, 17, 16, 15, 14, 13, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 3, 3, 5, 5, 6, 7, 10, 11, 14, 17, 19, 20, 21, 22,
+ 20, 19, 18, 17, 16, 15, 13, 12, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 3, 3, 5, 6, 6, 7, 10, 12, 14, 18, 20, 21, 22, 23,
+ 21, 20, 19, 18, 17, 16, 14, 13, 12, 11, 10, 10, 9, 7, 7, 5, 5, 4, 4, 5, 6, 6, 7, 8, 11, 13, 16, 19, 21, 22, 22, 22,
+ 22, 21, 20, 19, 18, 17, 16, 14, 13, 12, 12, 10, 9, 8, 7, 6, 6, 5, 5, 6, 7, 7, 8, 9, 12, 14, 18, 20, 21, 22, 22, 22,
+ 23, 22, 21, 20, 19, 17, 16, 15, 14, 14, 13, 12, 10, 9, 8, 7, 6, 5, 5, 6, 7, 8, 8, 10, 12, 15, 18, 20, 21, 22, 22, 22,
+ 24, 23, 22, 21, 20, 18, 17, 16, 15, 15, 14, 14, 13, 11, 9, 8, 6, 6, 6, 6, 7, 8, 9, 11, 14, 17, 19, 20, 21, 21, 21, 21,
+ 24, 24, 23, 21, 20, 19, 17, 16, 15, 15, 15, 14, 14, 14, 11, 9, 6, 5, 5, 6, 8, 8, 10, 12, 15, 17, 20, 20, 21, 21, 21, 21,
+ ]
+ g: [
+ 19, 18, 17, 15, 13, 12, 10, 9, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 9, 9, 11, 15, 15, 16, 19, 20,
+ 19, 18, 16, 15, 12, 12, 10, 10, 8, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 7, 7, 11, 14, 15, 16, 19, 19,
+ 18, 17, 16, 14, 12, 12, 10, 10, 8, 7, 7, 6, 6, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 10, 14, 15, 17, 19, 19,
+ 17, 17, 16, 14, 12, 12, 10, 10, 8, 7, 6, 6, 6, 5, 5, 4, 5, 5, 6, 6, 6, 7, 7, 5, 6, 6, 9, 13, 15, 17, 18, 20,
+ 17, 17, 15, 14, 12, 11, 10, 9, 8, 7, 6, 5, 5, 4, 4, 4, 4, 4, 5, 6, 6, 6, 5, 5, 5, 6, 8, 11, 15, 17, 18, 20,
+ 17, 17, 15, 13, 12, 11, 9, 9, 8, 7, 5, 5, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 7, 11, 15, 17, 18, 18,
+ 17, 16, 15, 13, 12, 11, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 4, 4, 4, 5, 6, 9, 14, 17, 17, 18,
+ 17, 16, 14, 13, 12, 11, 9, 8, 7, 6, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 6, 8, 13, 16, 17, 17,
+ 17, 15, 14, 13, 12, 11, 9, 8, 7, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 7, 12, 16, 17, 17,
+ 17, 15, 14, 12, 11, 10, 9, 8, 7, 6, 4, 4, 3, 3, 3, 2, 2, 3, 3, 3, 2, 2, 3, 3, 3, 4, 5, 7, 11, 15, 18, 18,
+ 16, 14, 13, 12, 11, 10, 9, 7, 7, 5, 4, 3, 3, 3, 2, 2, 2, 3, 3, 3, 2, 2, 2, 3, 3, 4, 5, 8, 11, 15, 18, 19,
+ 16, 14, 13, 12, 11, 10, 9, 7, 5, 5, 4, 3, 3, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 3, 4, 5, 8, 10, 15, 18, 19,
+ 16, 14, 14, 13, 11, 10, 9, 7, 5, 5, 4, 3, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 3, 3, 5, 7, 10, 15, 17, 18,
+ 16, 15, 14, 12, 11, 10, 9, 7, 5, 5, 4, 3, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 3, 5, 6, 10, 14, 17, 17,
+ 15, 15, 13, 12, 11, 10, 9, 7, 5, 4, 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 2, 2, 3, 5, 7, 10, 14, 17, 18,
+ 15, 14, 13, 12, 12, 10, 9, 7, 6, 4, 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 7, 10, 17, 18, 18,
+ 15, 14, 14, 13, 12, 11, 9, 7, 6, 4, 3, 2, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 7, 12, 17, 19, 19,
+ 16, 14, 14, 13, 12, 12, 9, 7, 6, 4, 3, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 4, 4, 5, 8, 12, 17, 19, 20,
+ 16, 15, 14, 13, 12, 12, 9, 7, 7, 4, 3, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 6, 8, 12, 17, 19, 20,
+ 17, 15, 14, 13, 12, 12, 9, 7, 7, 5, 3, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 8, 12, 17, 19, 20,
+ 18, 15, 15, 14, 13, 12, 9, 8, 7, 5, 4, 2, 1, 0, 0, 0, 0, 1, 2, 2, 3, 3, 3, 4, 5, 6, 7, 9, 13, 17, 19, 20,
+ 18, 16, 15, 14, 13, 12, 9, 9, 7, 6, 5, 3, 2, 1, 0, 0, 0, 1, 2, 3, 3, 3, 4, 5, 6, 7, 9, 10, 14, 18, 20, 20,
+ 18, 16, 16, 15, 13, 12, 10, 9, 8, 7, 6, 5, 3, 3, 1, 0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 9, 12, 15, 19, 20, 20,
+ 18, 18, 16, 16, 14, 13, 10, 10, 9, 8, 7, 6, 5, 5, 3, 1, 1, 1, 2, 3, 5, 5, 5, 6, 6, 8, 11, 15, 17, 19, 20, 20,
+ 18, 18, 16, 16, 14, 13, 12, 10, 9, 9, 8, 7, 6, 5, 4, 3, 1, 2, 3, 5, 5, 5, 5, 6, 7, 10, 12, 15, 18, 20, 20, 20,
+ 18, 18, 17, 16, 15, 14, 12, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 3, 3, 5, 5, 5, 6, 9, 11, 14, 16, 19, 20, 20, 21,
+ 19, 19, 18, 17, 16, 15, 13, 12, 11, 10, 10, 9, 8, 7, 5, 4, 4, 3, 3, 3, 5, 5, 6, 7, 10, 12, 14, 18, 20, 20, 21, 22,
+ 21, 20, 18, 18, 17, 16, 14, 12, 11, 11, 10, 10, 8, 7, 7, 5, 4, 4, 4, 5, 6, 6, 6, 7, 11, 13, 16, 19, 20, 21, 21, 22,
+ 22, 21, 20, 19, 18, 16, 15, 14, 12, 12, 12, 10, 9, 8, 7, 6, 5, 4, 5, 6, 6, 7, 7, 8, 12, 13, 17, 19, 21, 21, 21, 21,
+ 23, 22, 21, 20, 18, 17, 16, 16, 14, 14, 13, 12, 10, 10, 8, 7, 6, 5, 5, 6, 7, 7, 8, 9, 12, 15, 18, 19, 21, 21, 21, 20,
+ 23, 22, 22, 21, 20, 18, 17, 16, 15, 15, 15, 14, 13, 11, 9, 8, 6, 6, 6, 6, 7, 8, 9, 10, 13, 16, 19, 20, 20, 21, 21, 20,
+ 24, 23, 22, 21, 20, 19, 17, 17, 16, 16, 15, 15, 14, 14, 11, 9, 6, 6, 6, 6, 8, 8, 10, 12, 15, 17, 19, 20, 20, 21, 21, 20,
+ ]
+ b: [
+ 11, 9, 9, 7, 6, 5, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 5, 4, 5, 4, 4, 4, 7, 7, 3, 3, 5, 8, 8, 9, 11, 11,
+ 11, 10, 8, 7, 5, 5, 5, 4, 3, 3, 3, 3, 3, 3, 4, 4, 5, 4, 5, 4, 4, 4, 4, 4, 2, 2, 5, 8, 8, 9, 11, 11,
+ 10, 10, 7, 7, 5, 5, 5, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 3, 1, 1, 5, 7, 9, 9, 11, 11,
+ 10, 9, 8, 7, 6, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 2, 1, 1, 4, 7, 9, 10, 12, 13,
+ 9, 9, 8, 7, 6, 5, 5, 5, 4, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 3, 4, 3, 2, 1, 1, 1, 4, 6, 9, 10, 12, 13,
+ 9, 9, 9, 7, 6, 6, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 3, 3, 2, 1, 2, 2, 2, 1, 1, 1, 2, 6, 9, 10, 11, 12,
+ 8, 9, 9, 7, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 3, 3, 2, 1, 1, 1, 1, 0, 0, 1, 2, 5, 9, 11, 11, 11,
+ 8, 9, 9, 7, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 3, 3, 2, 1, 1, 1, 1, 0, 0, 0, 1, 3, 8, 11, 11, 11,
+ 9, 9, 8, 7, 7, 7, 6, 4, 4, 3, 3, 2, 2, 2, 2, 2, 3, 3, 2, 1, 1, 1, 1, 0, 0, 0, 1, 3, 7, 11, 11, 11,
+ 9, 9, 8, 7, 7, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 1, 3, 7, 10, 12, 13,
+ 9, 9, 8, 8, 7, 7, 6, 5, 4, 3, 2, 1, 2, 1, 2, 2, 2, 3, 2, 1, 0, 0, 0, 0, 0, 0, 1, 4, 7, 10, 13, 13,
+ 9, 8, 8, 8, 7, 7, 6, 5, 3, 3, 2, 1, 1, 1, 1, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 1, 2, 4, 7, 11, 13, 13,
+ 9, 8, 8, 7, 7, 6, 6, 5, 3, 3, 2, 2, 2, 1, 1, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 1, 2, 4, 6, 11, 13, 13,
+ 9, 8, 8, 7, 7, 6, 6, 4, 3, 3, 2, 2, 1, 1, 1, 1, 2, 3, 3, 3, 1, 1, 0, 0, 1, 1, 2, 3, 6, 10, 12, 13,
+ 9, 8, 8, 7, 7, 6, 6, 4, 3, 3, 2, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 2, 2, 4, 6, 10, 13, 13,
+ 9, 9, 8, 8, 8, 6, 6, 4, 4, 3, 2, 1, 1, 1, 1, 1, 2, 2, 3, 3, 1, 1, 1, 1, 2, 2, 3, 4, 6, 13, 14, 14,
+ 9, 9, 8, 8, 8, 8, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 5, 9, 13, 15, 15,
+ 10, 9, 9, 9, 10, 10, 6, 6, 5, 3, 2, 1, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 9, 13, 15, 16,
+ 10, 10, 9, 9, 10, 10, 7, 6, 5, 3, 2, 1, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 9, 13, 15, 16,
+ 11, 10, 9, 9, 10, 10, 7, 6, 6, 3, 2, 1, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 9, 13, 15, 16,
+ 12, 10, 10, 10, 10, 10, 7, 6, 6, 4, 3, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 5, 5, 6, 7, 10, 14, 15, 16,
+ 12, 11, 10, 10, 10, 10, 8, 7, 6, 6, 4, 3, 2, 1, 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 8, 11, 15, 16, 16,
+ 12, 12, 11, 11, 10, 10, 9, 8, 7, 6, 6, 4, 4, 3, 2, 2, 2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 7, 10, 13, 15, 16, 15,
+ 12, 12, 12, 12, 11, 10, 10, 8, 8, 7, 7, 6, 5, 5, 3, 2, 2, 2, 3, 4, 5, 5, 5, 5, 6, 7, 10, 13, 14, 16, 16, 16,
+ 12, 12, 13, 12, 12, 11, 11, 9, 8, 8, 8, 7, 6, 6, 5, 4, 3, 3, 3, 5, 6, 6, 6, 6, 7, 9, 11, 14, 15, 17, 17, 16,
+ 13, 13, 13, 13, 12, 12, 11, 11, 10, 10, 9, 8, 7, 7, 6, 5, 5, 4, 4, 4, 5, 6, 6, 6, 9, 10, 12, 14, 16, 17, 17, 18,
+ 13, 13, 14, 13, 13, 13, 12, 12, 11, 10, 10, 9, 8, 7, 6, 6, 6, 5, 4, 4, 6, 6, 6, 7, 9, 11, 12, 16, 17, 17, 17, 18,
+ 15, 15, 15, 15, 14, 13, 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 6, 5, 5, 5, 6, 7, 7, 8, 10, 12, 14, 17, 17, 18, 17, 17,
+ 16, 16, 16, 16, 15, 14, 14, 14, 13, 13, 12, 11, 11, 9, 8, 7, 7, 6, 6, 6, 7, 7, 8, 8, 10, 12, 16, 17, 18, 18, 17, 17,
+ 18, 17, 17, 16, 16, 15, 14, 14, 14, 14, 14, 13, 11, 11, 9, 8, 7, 7, 6, 6, 7, 7, 8, 9, 10, 13, 16, 17, 17, 18, 17, 16,
+ 18, 17, 17, 17, 16, 15, 15, 15, 15, 15, 15, 14, 14, 12, 10, 9, 7, 7, 6, 6, 7, 7, 8, 9, 11, 14, 16, 16, 17, 17, 16, 15,
+ 18, 18, 17, 17, 17, 16, 15, 15, 15, 16, 15, 15, 14, 14, 12, 9, 7, 6, 6, 6, 7, 7, 9, 10, 12, 14, 15, 16, 16, 16, 15, 15,
+ ]
+ - ct: 5500
+ r: [
+ 19, 18, 17, 16, 15, 13, 11, 10, 9, 9, 9, 8, 8, 8, 8, 8, 8, 9, 10, 10, 8, 8, 9, 9, 9, 11, 14, 15, 16, 16, 18, 18,
+ 18, 18, 17, 15, 14, 13, 11, 11, 9, 9, 8, 8, 7, 7, 7, 7, 7, 8, 9, 8, 8, 8, 9, 9, 8, 8, 11, 14, 16, 17, 17, 18,
+ 18, 17, 17, 15, 14, 13, 12, 11, 9, 9, 8, 7, 7, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 7, 7, 10, 13, 16, 17, 18, 18,
+ 17, 17, 16, 15, 14, 12, 11, 11, 9, 8, 7, 7, 6, 5, 5, 5, 5, 6, 8, 7, 8, 8, 8, 6, 6, 6, 9, 12, 16, 16, 18, 19,
+ 17, 17, 16, 14, 13, 12, 11, 10, 10, 8, 7, 7, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 6, 6, 6, 6, 8, 11, 15, 16, 17, 19,
+ 18, 17, 16, 14, 13, 12, 11, 10, 10, 8, 7, 6, 5, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 10, 14, 16, 17, 18,
+ 18, 17, 16, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 3, 4, 5, 5, 5, 5, 5, 6, 10, 13, 16, 16, 17,
+ 18, 16, 15, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 4, 4, 5, 6, 8, 12, 16, 16, 16,
+ 17, 16, 15, 13, 12, 11, 10, 9, 8, 7, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 5, 7, 11, 16, 16, 16,
+ 17, 16, 15, 12, 12, 12, 10, 8, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 5, 7, 10, 16, 16, 16,
+ 16, 16, 14, 12, 12, 11, 10, 8, 7, 6, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 2, 2, 2, 3, 3, 4, 4, 7, 10, 14, 16, 16,
+ 16, 15, 14, 13, 12, 11, 10, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 2, 2, 3, 3, 3, 5, 7, 10, 14, 15, 16,
+ 16, 15, 15, 13, 11, 11, 11, 9, 7, 5, 5, 4, 3, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 5, 6, 9, 14, 15, 16,
+ 16, 15, 15, 13, 11, 11, 11, 10, 7, 5, 4, 4, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 3, 3, 5, 6, 10, 13, 15, 17,
+ 15, 15, 14, 12, 11, 11, 11, 10, 7, 4, 4, 3, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 3, 3, 3, 5, 6, 9, 13, 16, 17,
+ 15, 15, 14, 12, 11, 11, 10, 9, 7, 4, 4, 3, 1, 1, 1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 3, 3, 5, 7, 10, 15, 17, 17,
+ 15, 15, 14, 12, 11, 11, 10, 9, 7, 4, 4, 3, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 5, 7, 11, 16, 17, 18,
+ 16, 15, 15, 12, 12, 11, 10, 9, 6, 4, 4, 3, 1, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 7, 12, 15, 17, 18,
+ 16, 16, 15, 12, 12, 11, 10, 8, 6, 5, 4, 3, 1, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 8, 12, 15, 17, 18,
+ 15, 15, 15, 13, 12, 12, 10, 8, 7, 5, 4, 3, 2, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 7, 9, 12, 16, 17, 18,
+ 15, 15, 15, 13, 13, 12, 10, 8, 7, 5, 5, 3, 2, 1, 0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 7, 9, 13, 16, 18, 18,
+ 15, 15, 15, 14, 13, 12, 10, 8, 7, 6, 5, 5, 3, 2, 1, 0, 1, 1, 1, 2, 3, 3, 3, 5, 5, 6, 8, 10, 14, 17, 18, 19,
+ 16, 16, 16, 15, 13, 12, 10, 9, 8, 7, 6, 6, 5, 4, 2, 1, 1, 1, 1, 2, 3, 3, 5, 5, 6, 7, 9, 12, 15, 18, 18, 19,
+ 17, 16, 16, 16, 13, 12, 11, 10, 9, 8, 7, 7, 6, 5, 4, 2, 1, 1, 2, 3, 4, 4, 5, 5, 6, 8, 11, 14, 16, 18, 19, 19,
+ 18, 18, 17, 16, 14, 13, 12, 10, 9, 8, 8, 7, 7, 5, 5, 3, 2, 2, 4, 4, 5, 5, 5, 5, 7, 10, 12, 16, 17, 19, 19, 20,
+ 18, 18, 17, 16, 15, 13, 12, 10, 10, 9, 9, 9, 7, 6, 5, 4, 3, 3, 4, 4, 5, 5, 5, 6, 7, 11, 15, 17, 18, 19, 19, 20,
+ 19, 18, 18, 17, 16, 14, 13, 11, 10, 10, 9, 9, 8, 6, 5, 5, 4, 4, 4, 4, 6, 6, 6, 7, 9, 11, 15, 17, 19, 19, 20, 21,
+ 20, 19, 19, 19, 17, 16, 13, 12, 11, 11, 10, 9, 9, 7, 6, 5, 5, 4, 4, 5, 6, 7, 7, 8, 9, 12, 15, 18, 19, 19, 20, 20,
+ 21, 20, 20, 19, 19, 16, 16, 13, 12, 12, 11, 11, 9, 8, 7, 6, 6, 5, 5, 6, 7, 7, 8, 8, 10, 13, 17, 19, 19, 20, 20, 20,
+ 22, 21, 20, 20, 19, 17, 16, 14, 13, 13, 14, 12, 11, 9, 8, 7, 6, 5, 5, 6, 7, 7, 8, 9, 11, 15, 17, 19, 19, 20, 20, 20,
+ 22, 22, 21, 20, 19, 18, 16, 15, 14, 14, 15, 15, 13, 11, 9, 8, 7, 5, 5, 6, 7, 8, 9, 10, 13, 16, 18, 18, 19, 20, 19, 19,
+ 22, 22, 21, 20, 19, 19, 16, 16, 15, 15, 15, 15, 15, 13, 10, 9, 7, 5, 5, 6, 8, 8, 10, 11, 14, 16, 18, 18, 19, 19, 19, 19,
+ ]
+ g: [
+ 16, 16, 15, 14, 13, 11, 10, 9, 8, 7, 7, 7, 6, 6, 6, 6, 6, 7, 8, 8, 6, 6, 7, 7, 7, 9, 12, 13, 13, 14, 14, 14,
+ 16, 16, 15, 14, 13, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 10, 12, 13, 14, 14, 14,
+ 16, 15, 15, 13, 13, 11, 10, 9, 8, 7, 7, 6, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 5, 5, 8, 12, 14, 15, 15, 16,
+ 15, 15, 14, 13, 12, 11, 10, 10, 8, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 5, 5, 5, 7, 11, 14, 15, 15, 17,
+ 16, 15, 14, 13, 12, 11, 10, 10, 9, 7, 6, 6, 4, 4, 4, 3, 3, 4, 4, 6, 6, 5, 5, 4, 4, 5, 6, 10, 14, 14, 16, 16,
+ 16, 15, 15, 13, 12, 11, 10, 10, 9, 7, 6, 6, 4, 4, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 9, 13, 14, 15, 16,
+ 16, 15, 15, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 5, 8, 13, 14, 15, 15,
+ 16, 15, 14, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 4, 5, 7, 11, 14, 15, 15,
+ 16, 15, 14, 12, 11, 10, 9, 8, 7, 6, 5, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 4, 6, 10, 14, 15, 14,
+ 16, 15, 14, 12, 11, 11, 9, 8, 7, 6, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 4, 6, 10, 15, 15, 15,
+ 15, 15, 13, 12, 11, 11, 10, 8, 7, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, 6, 9, 13, 15, 15,
+ 15, 14, 13, 12, 11, 11, 10, 8, 7, 5, 5, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 4, 6, 9, 13, 14, 15,
+ 15, 14, 14, 13, 11, 11, 10, 8, 7, 5, 5, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 1, 2, 2, 2, 4, 5, 8, 13, 14, 15,
+ 15, 14, 14, 13, 11, 11, 11, 10, 7, 5, 4, 3, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 1, 2, 2, 2, 4, 5, 8, 13, 14, 15,
+ 15, 14, 13, 12, 11, 11, 10, 9, 7, 4, 4, 3, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 5, 8, 13, 15, 16,
+ 15, 15, 13, 12, 11, 11, 10, 9, 7, 4, 4, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 6, 8, 15, 16, 16,
+ 15, 15, 14, 12, 11, 11, 10, 9, 7, 4, 4, 2, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 6, 11, 15, 16, 17,
+ 15, 15, 15, 12, 12, 11, 10, 10, 7, 4, 4, 2, 1, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 6, 11, 15, 17, 17,
+ 15, 15, 15, 12, 12, 12, 10, 9, 7, 5, 4, 2, 1, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 5, 7, 11, 15, 17, 17,
+ 15, 15, 15, 13, 12, 12, 10, 9, 7, 5, 4, 3, 2, 0, 0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 6, 8, 12, 15, 17, 17,
+ 15, 15, 15, 13, 13, 12, 10, 9, 7, 6, 5, 3, 2, 1, 0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 4, 5, 6, 9, 13, 16, 17, 18,
+ 15, 15, 15, 14, 13, 13, 10, 9, 8, 6, 6, 5, 3, 2, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 10, 14, 17, 18, 18,
+ 15, 16, 16, 15, 13, 13, 11, 9, 8, 7, 6, 6, 5, 4, 2, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 12, 15, 17, 18, 18,
+ 16, 16, 16, 16, 13, 13, 11, 10, 9, 8, 7, 7, 6, 6, 4, 2, 2, 2, 2, 3, 5, 5, 5, 5, 6, 8, 11, 14, 16, 18, 18, 18,
+ 17, 17, 17, 16, 14, 13, 13, 11, 10, 9, 8, 8, 7, 6, 5, 4, 2, 2, 4, 5, 5, 5, 5, 5, 7, 9, 12, 15, 17, 18, 18, 18,
+ 18, 18, 17, 16, 15, 14, 13, 11, 10, 10, 9, 9, 7, 6, 5, 5, 4, 3, 4, 4, 5, 5, 5, 6, 7, 10, 15, 16, 18, 18, 18, 19,
+ 19, 18, 18, 17, 16, 14, 13, 12, 11, 10, 10, 9, 9, 7, 6, 5, 5, 4, 4, 4, 6, 6, 6, 6, 8, 10, 15, 16, 18, 18, 18, 19,
+ 20, 19, 19, 19, 17, 16, 14, 13, 12, 11, 11, 10, 9, 7, 7, 6, 5, 4, 4, 5, 6, 6, 6, 7, 9, 11, 15, 17, 18, 18, 18, 18,
+ 22, 20, 20, 20, 19, 17, 16, 14, 13, 13, 12, 11, 10, 9, 7, 6, 6, 5, 5, 6, 7, 7, 7, 8, 9, 12, 16, 18, 18, 19, 18, 18,
+ 22, 22, 21, 20, 19, 18, 17, 16, 14, 14, 15, 13, 11, 10, 9, 8, 7, 6, 5, 6, 7, 8, 8, 9, 10, 14, 17, 18, 18, 19, 18, 17,
+ 22, 22, 22, 21, 20, 19, 17, 17, 16, 16, 16, 16, 14, 12, 10, 8, 7, 6, 6, 7, 8, 8, 8, 10, 13, 16, 18, 18, 18, 18, 18, 17,
+ 22, 22, 22, 21, 20, 20, 18, 17, 16, 16, 17, 17, 16, 14, 11, 10, 8, 6, 6, 7, 8, 8, 10, 11, 14, 17, 18, 18, 18, 18, 18, 17,
+ ]
+ b: [
+ 13, 12, 12, 12, 11, 9, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 6, 7, 7, 6, 6, 5, 5, 5, 6, 9, 9, 9, 10, 9, 8,
+ 13, 13, 12, 11, 11, 9, 9, 8, 7, 7, 7, 6, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 4, 4, 7, 9, 9, 10, 10, 9,
+ 13, 13, 12, 11, 11, 9, 9, 8, 7, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 6, 6, 5, 5, 5, 3, 3, 6, 9, 10, 11, 10, 11,
+ 13, 13, 12, 11, 11, 10, 9, 9, 7, 7, 6, 5, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 4, 3, 3, 5, 8, 10, 11, 11, 12,
+ 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 6, 6, 4, 4, 3, 3, 3, 4, 4, 5, 5, 5, 4, 3, 2, 3, 4, 8, 11, 11, 11, 12,
+ 13, 13, 13, 11, 11, 10, 9, 9, 8, 7, 6, 6, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 7, 11, 11, 11, 12,
+ 14, 14, 13, 11, 11, 10, 9, 8, 8, 7, 6, 6, 4, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 2, 2, 3, 6, 11, 11, 11, 11,
+ 14, 14, 13, 11, 11, 10, 9, 8, 7, 7, 6, 5, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 9, 11, 11, 11,
+ 14, 14, 13, 12, 11, 11, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 8, 12, 12, 11,
+ 14, 13, 13, 12, 12, 11, 10, 8, 7, 6, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 8, 12, 12, 13,
+ 13, 13, 13, 12, 12, 12, 11, 9, 7, 6, 4, 3, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 4, 8, 12, 12, 13,
+ 14, 13, 13, 12, 12, 11, 11, 9, 7, 6, 5, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 4, 8, 12, 13, 13,
+ 14, 13, 13, 13, 12, 11, 11, 9, 7, 6, 5, 4, 2, 1, 1, 1, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 4, 7, 12, 13, 13,
+ 14, 13, 13, 13, 12, 12, 12, 11, 7, 5, 5, 4, 2, 1, 1, 1, 1, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 4, 7, 12, 13, 13,
+ 14, 14, 13, 13, 12, 12, 11, 11, 7, 5, 4, 3, 1, 1, 1, 1, 1, 2, 3, 3, 2, 2, 2, 3, 3, 3, 4, 4, 7, 12, 13, 14,
+ 14, 14, 13, 13, 12, 12, 11, 11, 8, 5, 4, 3, 1, 1, 0, 0, 1, 1, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 8, 13, 15, 15,
+ 14, 15, 14, 13, 13, 12, 11, 11, 8, 5, 4, 3, 1, 0, 0, 0, 0, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 10, 15, 15, 16,
+ 15, 15, 15, 14, 13, 13, 12, 11, 8, 5, 4, 3, 1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 6, 11, 15, 16, 16,
+ 15, 15, 15, 14, 14, 14, 12, 11, 8, 6, 5, 3, 1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 6, 7, 11, 15, 16, 16,
+ 15, 15, 15, 15, 14, 14, 12, 11, 9, 7, 5, 3, 2, 1, 0, 1, 1, 1, 2, 3, 3, 4, 4, 4, 4, 5, 6, 8, 12, 15, 16, 16,
+ 15, 16, 15, 15, 15, 14, 13, 11, 9, 7, 6, 5, 3, 2, 1, 1, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 7, 9, 12, 16, 16, 16,
+ 15, 16, 16, 15, 15, 15, 13, 11, 10, 8, 7, 6, 4, 3, 2, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 10, 14, 16, 16, 16,
+ 16, 16, 17, 16, 15, 15, 14, 12, 11, 9, 8, 8, 6, 5, 3, 2, 2, 2, 3, 3, 5, 5, 6, 6, 7, 8, 9, 12, 15, 16, 16, 16,
+ 16, 17, 18, 17, 16, 15, 14, 13, 11, 10, 9, 9, 8, 6, 5, 3, 3, 3, 3, 4, 6, 6, 6, 6, 7, 8, 11, 14, 16, 16, 16, 16,
+ 17, 18, 18, 18, 17, 16, 16, 14, 12, 11, 10, 9, 8, 7, 6, 5, 3, 3, 5, 6, 6, 6, 6, 6, 8, 10, 12, 16, 17, 17, 17, 16,
+ 18, 18, 18, 18, 18, 17, 16, 14, 13, 12, 11, 11, 8, 8, 6, 6, 5, 4, 5, 5, 6, 6, 6, 7, 8, 11, 15, 16, 17, 17, 16, 16,
+ 18, 19, 19, 19, 19, 17, 17, 15, 14, 13, 12, 11, 11, 8, 7, 6, 6, 5, 5, 5, 7, 7, 7, 8, 9, 11, 15, 17, 17, 17, 16, 16,
+ 20, 20, 20, 20, 19, 19, 17, 17, 15, 14, 14, 12, 11, 9, 8, 7, 7, 6, 6, 6, 8, 8, 8, 8, 9, 12, 15, 18, 18, 16, 16, 16,
+ 22, 22, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 12, 11, 10, 8, 8, 7, 7, 7, 8, 8, 8, 9, 10, 13, 17, 18, 18, 16, 16, 15,
+ 23, 22, 22, 22, 22, 21, 20, 20, 18, 18, 19, 16, 14, 13, 11, 10, 9, 8, 7, 7, 8, 9, 9, 10, 11, 15, 17, 18, 17, 17, 16, 14,
+ 23, 23, 23, 23, 23, 22, 21, 21, 20, 20, 20, 19, 18, 15, 12, 11, 10, 8, 8, 8, 9, 9, 10, 11, 13, 17, 17, 17, 17, 16, 15, 13,
+ 23, 23, 24, 24, 23, 23, 22, 21, 21, 21, 20, 20, 19, 17, 14, 12, 11, 9, 8, 9, 9, 10, 10, 12, 15, 17, 17, 17, 16, 16, 15, 13,
+ ]
+ - ct: 8500
+ r: [
+ 18, 17, 16, 15, 13, 12, 10, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 8, 8, 10, 13, 14, 15, 17, 18,
+ 17, 17, 16, 14, 12, 11, 10, 10, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 10, 13, 14, 15, 17, 17,
+ 17, 16, 15, 13, 12, 11, 10, 10, 8, 8, 7, 7, 7, 6, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 6, 6, 9, 12, 14, 15, 17, 17,
+ 16, 16, 15, 13, 12, 11, 10, 10, 9, 7, 7, 7, 6, 6, 5, 5, 6, 6, 7, 7, 7, 7, 7, 5, 5, 5, 8, 11, 14, 15, 17, 19,
+ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 7, 10, 13, 15, 17, 19,
+ 15, 15, 14, 13, 12, 11, 9, 9, 8, 7, 6, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 4, 4, 4, 4, 5, 6, 9, 13, 15, 16, 17,
+ 15, 15, 13, 12, 11, 11, 9, 8, 8, 6, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 5, 8, 13, 15, 15, 16,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 5, 7, 11, 14, 15, 15,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 2, 3, 3, 3, 3, 4, 6, 10, 14, 15, 15,
+ 15, 13, 13, 11, 11, 10, 9, 8, 7, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 4, 6, 10, 13, 16, 16,
+ 14, 13, 12, 11, 10, 10, 9, 7, 7, 5, 4, 3, 3, 3, 3, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 3, 4, 6, 9, 13, 16, 17,
+ 14, 13, 12, 11, 10, 10, 9, 7, 6, 5, 4, 3, 3, 3, 2, 2, 2, 3, 3, 3, 2, 2, 1, 2, 2, 3, 4, 6, 9, 13, 16, 17,
+ 14, 13, 12, 12, 10, 10, 9, 7, 5, 5, 4, 3, 3, 2, 2, 2, 2, 3, 3, 3, 2, 2, 1, 1, 2, 3, 4, 6, 9, 13, 15, 17,
+ 14, 13, 12, 11, 10, 9, 9, 7, 5, 5, 4, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 1, 1, 2, 3, 4, 6, 9, 12, 15, 16,
+ 13, 13, 12, 11, 10, 9, 8, 7, 5, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 4, 6, 8, 12, 15, 16,
+ 13, 13, 12, 11, 11, 9, 8, 7, 6, 4, 3, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 3, 3, 4, 6, 9, 15, 16, 16,
+ 13, 13, 12, 12, 11, 10, 9, 7, 6, 4, 3, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 4, 6, 10, 15, 17, 18,
+ 14, 13, 13, 12, 12, 11, 9, 7, 6, 4, 3, 2, 1, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 7, 11, 15, 17, 18,
+ 14, 13, 13, 12, 12, 11, 9, 7, 7, 4, 3, 1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 4, 4, 5, 7, 11, 15, 17, 18,
+ 15, 13, 13, 12, 12, 11, 8, 7, 6, 4, 3, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 11, 15, 17, 18,
+ 15, 14, 13, 13, 12, 11, 8, 8, 7, 5, 4, 2, 1, 0, 0, 0, 0, 1, 2, 2, 2, 2, 3, 3, 5, 5, 7, 8, 11, 15, 17, 18,
+ 15, 14, 14, 13, 12, 11, 9, 8, 7, 6, 5, 3, 2, 1, 0, 0, 0, 1, 2, 2, 3, 3, 3, 5, 5, 6, 8, 9, 13, 16, 18, 18,
+ 15, 14, 14, 13, 12, 11, 9, 9, 8, 7, 6, 5, 3, 3, 1, 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 8, 11, 14, 17, 18, 18,
+ 16, 16, 15, 14, 13, 12, 10, 9, 8, 8, 7, 6, 5, 5, 3, 1, 1, 1, 2, 3, 4, 4, 5, 5, 6, 8, 10, 13, 15, 17, 18, 18,
+ 16, 16, 15, 14, 14, 12, 11, 10, 9, 9, 8, 7, 6, 5, 4, 3, 1, 1, 2, 4, 4, 5, 5, 5, 7, 9, 11, 14, 16, 18, 18, 19,
+ 16, 16, 15, 15, 14, 13, 12, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 3, 3, 4, 5, 5, 6, 9, 10, 13, 15, 18, 18, 19, 19,
+ 17, 17, 16, 15, 15, 14, 12, 11, 11, 10, 10, 9, 8, 7, 6, 5, 4, 3, 3, 3, 5, 5, 6, 6, 9, 11, 13, 17, 18, 19, 19, 20,
+ 19, 18, 17, 17, 15, 15, 13, 12, 11, 11, 10, 10, 9, 8, 7, 5, 5, 4, 4, 5, 6, 6, 6, 7, 10, 12, 15, 18, 19, 19, 19, 20,
+ 19, 19, 18, 17, 17, 16, 14, 13, 12, 12, 11, 10, 9, 8, 7, 6, 6, 5, 5, 6, 6, 6, 7, 8, 11, 12, 16, 18, 19, 19, 19, 19,
+ 20, 19, 19, 18, 17, 16, 15, 14, 13, 13, 12, 12, 10, 9, 8, 7, 6, 5, 5, 6, 6, 7, 8, 9, 11, 14, 17, 18, 19, 19, 19, 19,
+ 20, 20, 19, 18, 18, 17, 15, 15, 14, 14, 14, 13, 12, 11, 9, 7, 6, 5, 5, 6, 7, 7, 8, 9, 12, 15, 17, 18, 18, 19, 18, 18,
+ 21, 20, 20, 19, 18, 18, 15, 15, 14, 14, 14, 14, 13, 13, 11, 9, 6, 5, 5, 6, 7, 7, 9, 10, 13, 15, 18, 18, 18, 18, 18, 18,
+ ]
+ g: [
+ 16, 16, 15, 13, 12, 10, 9, 8, 7, 7, 7, 6, 6, 6, 7, 7, 7, 6, 6, 6, 6, 6, 11, 11, 6, 6, 9, 12, 12, 13, 15, 15,
+ 16, 15, 14, 13, 11, 10, 9, 9, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 9, 12, 12, 13, 15, 15,
+ 15, 15, 14, 12, 11, 11, 9, 9, 8, 7, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 5, 4, 5, 8, 11, 13, 14, 15, 15,
+ 15, 15, 14, 12, 11, 10, 9, 9, 8, 6, 6, 6, 5, 5, 4, 4, 4, 4, 6, 6, 6, 6, 6, 4, 4, 4, 7, 11, 13, 14, 15, 17,
+ 15, 15, 13, 12, 11, 10, 9, 9, 8, 6, 6, 5, 5, 4, 4, 3, 3, 4, 4, 5, 5, 5, 4, 4, 3, 4, 6, 9, 13, 14, 15, 17,
+ 15, 14, 13, 12, 11, 10, 9, 8, 8, 6, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 5, 8, 13, 14, 14, 15,
+ 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 4, 7, 12, 14, 14, 14,
+ 14, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 4, 6, 11, 14, 14, 14,
+ 14, 13, 12, 11, 11, 10, 9, 8, 7, 6, 4, 4, 3, 3, 3, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 5, 10, 14, 14, 14,
+ 15, 13, 12, 11, 11, 10, 9, 8, 7, 6, 4, 4, 3, 3, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 3, 5, 10, 13, 15, 16,
+ 14, 13, 12, 11, 11, 10, 9, 7, 7, 5, 4, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 1, 2, 2, 2, 2, 3, 6, 9, 13, 16, 16,
+ 14, 13, 12, 11, 10, 10, 9, 7, 5, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 4, 6, 8, 13, 16, 16,
+ 14, 13, 13, 12, 10, 10, 9, 7, 5, 5, 4, 3, 2, 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 2, 2, 4, 5, 8, 13, 15, 16,
+ 14, 13, 13, 12, 10, 10, 9, 7, 5, 5, 4, 3, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 3, 5, 8, 12, 14, 15,
+ 14, 13, 12, 11, 10, 10, 9, 7, 6, 4, 3, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 2, 3, 3, 5, 8, 12, 15, 15,
+ 14, 13, 12, 11, 11, 10, 9, 7, 6, 4, 3, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 3, 4, 5, 8, 14, 16, 16,
+ 14, 13, 13, 12, 12, 10, 9, 7, 6, 4, 3, 2, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 10, 15, 16, 17,
+ 14, 13, 13, 13, 12, 12, 9, 7, 7, 4, 3, 2, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 6, 11, 15, 17, 17,
+ 14, 14, 13, 12, 12, 12, 9, 8, 7, 5, 3, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 7, 11, 15, 17, 17,
+ 15, 14, 13, 13, 12, 12, 9, 8, 7, 5, 3, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 7, 11, 15, 17, 17,
+ 16, 14, 14, 13, 13, 12, 9, 8, 7, 5, 4, 2, 1, 0, 0, 0, 0, 1, 2, 2, 3, 3, 3, 3, 5, 5, 6, 8, 12, 16, 17, 17,
+ 16, 15, 14, 14, 13, 12, 10, 9, 7, 6, 5, 4, 2, 1, 0, 0, 0, 1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 13, 17, 17, 17,
+ 16, 15, 15, 14, 13, 12, 10, 10, 8, 7, 6, 5, 4, 3, 2, 0, 1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 8, 11, 14, 17, 17, 17,
+ 16, 16, 15, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 5, 3, 2, 1, 1, 2, 3, 5, 5, 5, 5, 6, 7, 10, 13, 16, 17, 17, 17,
+ 17, 17, 15, 15, 14, 13, 12, 11, 10, 9, 9, 7, 6, 6, 5, 3, 2, 2, 3, 4, 5, 5, 5, 6, 6, 9, 11, 14, 16, 18, 18, 18,
+ 17, 17, 16, 15, 15, 14, 13, 12, 11, 10, 10, 9, 7, 7, 5, 4, 3, 2, 3, 4, 5, 5, 5, 6, 9, 10, 13, 15, 18, 18, 18, 18,
+ 18, 17, 17, 16, 15, 15, 14, 12, 11, 11, 11, 9, 8, 7, 6, 5, 4, 3, 3, 4, 5, 5, 6, 6, 9, 11, 13, 17, 18, 18, 18, 18,
+ 20, 19, 18, 18, 16, 16, 14, 13, 12, 11, 11, 10, 9, 8, 7, 5, 5, 4, 4, 5, 6, 6, 6, 7, 10, 11, 15, 18, 18, 18, 18, 18,
+ 20, 20, 19, 18, 18, 17, 15, 14, 13, 13, 12, 11, 10, 9, 8, 6, 6, 5, 5, 6, 6, 7, 7, 8, 11, 12, 16, 18, 18, 18, 18, 18,
+ 22, 21, 20, 19, 18, 17, 17, 17, 15, 15, 14, 13, 11, 10, 8, 7, 6, 6, 6, 6, 7, 7, 8, 8, 11, 14, 17, 18, 18, 18, 18, 17,
+ 22, 22, 21, 20, 19, 18, 17, 17, 17, 16, 16, 15, 14, 12, 10, 8, 7, 6, 6, 7, 7, 8, 8, 10, 13, 16, 18, 18, 18, 18, 18, 16,
+ 22, 22, 22, 21, 20, 19, 18, 17, 17, 17, 16, 16, 15, 15, 12, 10, 7, 6, 6, 7, 8, 8, 9, 11, 14, 16, 18, 18, 18, 18, 17, 16,
+ ]
+ b: [
+ 13, 13, 13, 11, 10, 9, 9, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 9, 4, 4, 7, 9, 9, 9, 10, 10,
+ 13, 13, 12, 11, 10, 9, 9, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 7, 9, 9, 10, 10, 10,
+ 13, 13, 12, 11, 10, 10, 9, 9, 7, 7, 6, 6, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 4, 3, 3, 6, 9, 10, 11, 11, 11,
+ 13, 13, 12, 11, 10, 10, 9, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 5, 3, 2, 3, 5, 9, 10, 11, 11, 13,
+ 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 6, 5, 5, 4, 4, 3, 3, 4, 4, 5, 5, 5, 3, 2, 2, 3, 5, 8, 11, 11, 12, 13,
+ 13, 12, 12, 11, 11, 10, 9, 8, 8, 7, 5, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 7, 11, 11, 11, 11,
+ 13, 13, 12, 11, 11, 10, 9, 8, 8, 7, 5, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 2, 2, 2, 3, 6, 11, 11, 11, 11,
+ 13, 13, 12, 11, 11, 11, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 5, 10, 12, 12, 11,
+ 13, 13, 13, 11, 11, 11, 10, 8, 7, 6, 5, 4, 3, 3, 3, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 4, 9, 12, 12, 12,
+ 14, 13, 13, 12, 11, 11, 10, 8, 7, 6, 4, 4, 3, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 4, 9, 12, 13, 14,
+ 13, 13, 12, 12, 11, 11, 10, 8, 7, 6, 4, 3, 2, 2, 2, 1, 2, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 4, 8, 12, 14, 14,
+ 13, 13, 12, 12, 11, 11, 11, 8, 7, 6, 5, 3, 2, 2, 1, 1, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 4, 8, 12, 14, 14,
+ 13, 13, 12, 12, 12, 11, 11, 8, 7, 6, 5, 3, 2, 1, 1, 1, 1, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 4, 8, 12, 14, 14,
+ 13, 13, 13, 12, 12, 11, 11, 8, 6, 6, 5, 3, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 7, 12, 13, 14,
+ 13, 13, 13, 12, 12, 11, 10, 8, 6, 5, 4, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 7, 12, 14, 14,
+ 14, 14, 13, 13, 13, 11, 10, 8, 7, 5, 4, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 8, 14, 16, 16,
+ 14, 14, 13, 13, 13, 12, 11, 9, 7, 5, 4, 2, 1, 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 6, 10, 15, 16, 16,
+ 15, 14, 14, 14, 15, 15, 11, 9, 8, 5, 4, 2, 1, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 7, 11, 15, 16, 17,
+ 15, 15, 14, 14, 15, 15, 12, 10, 9, 6, 4, 2, 1, 0, 0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 11, 15, 16, 16,
+ 15, 15, 15, 15, 15, 15, 12, 10, 9, 6, 5, 2, 1, 0, 0, 0, 1, 1, 1, 3, 3, 3, 4, 4, 5, 6, 6, 7, 11, 15, 16, 17,
+ 16, 16, 15, 16, 15, 15, 12, 11, 10, 7, 5, 3, 2, 1, 0, 0, 1, 1, 2, 3, 4, 4, 4, 5, 6, 6, 7, 9, 12, 16, 16, 16,
+ 16, 16, 16, 16, 16, 15, 13, 12, 10, 9, 6, 5, 3, 2, 1, 1, 1, 2, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 14, 16, 17, 16,
+ 16, 16, 16, 16, 16, 15, 14, 12, 11, 10, 8, 6, 5, 4, 2, 1, 1, 2, 3, 4, 5, 5, 6, 6, 6, 7, 9, 12, 15, 16, 16, 16,
+ 17, 17, 18, 17, 16, 16, 14, 13, 12, 11, 9, 8, 6, 5, 4, 2, 2, 2, 3, 4, 6, 6, 6, 6, 7, 8, 11, 14, 16, 17, 16, 16,
+ 17, 17, 18, 18, 17, 16, 16, 14, 13, 12, 11, 9, 8, 7, 5, 4, 2, 3, 4, 6, 6, 6, 6, 7, 8, 10, 12, 15, 17, 17, 17, 16,
+ 18, 18, 18, 18, 18, 17, 16, 15, 14, 13, 12, 11, 9, 8, 6, 5, 4, 4, 4, 5, 6, 6, 7, 7, 10, 11, 14, 16, 17, 17, 17, 17,
+ 18, 18, 19, 19, 19, 18, 17, 16, 15, 14, 14, 11, 10, 9, 7, 6, 6, 5, 5, 5, 6, 7, 7, 7, 10, 12, 14, 17, 17, 17, 17, 17,
+ 20, 20, 20, 20, 20, 19, 18, 17, 16, 15, 14, 13, 11, 10, 9, 7, 6, 6, 6, 6, 7, 7, 7, 8, 11, 12, 15, 18, 18, 17, 17, 16,
+ 22, 21, 21, 21, 21, 21, 20, 19, 17, 16, 16, 14, 13, 11, 10, 8, 7, 7, 7, 7, 8, 8, 8, 9, 11, 13, 17, 18, 18, 17, 16, 15,
+ 23, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 16, 14, 13, 11, 9, 8, 8, 8, 8, 8, 8, 9, 10, 12, 15, 18, 18, 18, 17, 16, 14,
+ 23, 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 18, 15, 13, 11, 9, 8, 8, 8, 9, 9, 10, 11, 13, 16, 18, 18, 17, 17, 15, 14,
+ 24, 24, 24, 24, 24, 23, 22, 22, 22, 22, 20, 20, 19, 18, 15, 13, 10, 9, 9, 9, 9, 10, 11, 12, 15, 17, 18, 18, 17, 16, 15, 13,
+ ]
+...
diff --git a/src/ipa/mali-c55/data/meson.build b/src/ipa/mali-c55/data/meson.build
new file mode 100644
index 00000000..8a5fdd36
--- /dev/null
+++ b/src/ipa/mali-c55/data/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: CC0-1.0
+
+conf_files = files([
+ 'imx415.yaml',
+ 'uncalibrated.yaml'
+])
+
+install_data(conf_files,
+ install_dir : ipa_data_dir / 'mali-c55')
diff --git a/src/ipa/mali-c55/data/uncalibrated.yaml b/src/ipa/mali-c55/data/uncalibrated.yaml
new file mode 100644
index 00000000..6dcc0295
--- /dev/null
+++ b/src/ipa/mali-c55/data/uncalibrated.yaml
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: CC0-1.0
+%YAML 1.1
+---
+version: 1
+algorithms:
+ - Agc:
+...
diff --git a/src/ipa/mali-c55/ipa_context.cpp b/src/ipa/mali-c55/ipa_context.cpp
new file mode 100644
index 00000000..99f76ecd
--- /dev/null
+++ b/src/ipa/mali-c55/ipa_context.cpp
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * ipa_context.cpp - MaliC55 IPA Context
+ */
+
+#include "ipa_context.h"
+
+/**
+ * \file ipa_context.h
+ * \brief Context and state information shared between the algorithms
+ */
+
+namespace libcamera::ipa::mali_c55 {
+
+/**
+ * \struct IPASessionConfiguration
+ * \brief Session configuration for the IPA module
+ *
+ * The session configuration contains all IPA configuration parameters that
+ * remain constant during the capture session, from IPA module start to stop.
+ * It is typically set during the configure() operation of the IPA module, but
+ * may also be updated in the start() operation.
+ */
+
+/**
+ * \struct IPAActiveState
+ * \brief Active state for algorithms
+ *
+ * The active state contains all algorithm-specific data that needs to be
+ * maintained by algorithms across frames. Unlike the session configuration,
+ * the active state is mutable and constantly updated by algorithms. The active
+ * state is accessible through the IPAContext structure.
+ *
+ * The active state stores two distinct categories of information:
+ *
+ * - The consolidated value of all algorithm controls. Requests passed to
+ * the queueRequest() function store values for controls that the
+ * application wants to modify for that particular frame, and the
+ * queueRequest() function updates the active state with those values.
+ * The active state thus contains a consolidated view of the value of all
+ * controls handled by the algorithm.
+ *
+ * - The value of parameters computed by the algorithm when running in auto
+ * mode. Algorithms running in auto mode compute new parameters every
+ * time statistics buffers are received (either synchronously, or
+ * possibly in a background thread). The latest computed value of those
+ * parameters is stored in the active state in the process() function.
+ *
+ * Each of the members in the active state belongs to a specific algorithm. A
+ * member may be read by any algorithm, but shall only be written by its owner.
+ */
+
+/**
+ * \struct IPAFrameContext
+ * \brief Per-frame context for algorithms
+ *
+ * The frame context stores two distinct categories of information:
+ *
+ * - The value of the controls to be applied to the frame. These values are
+ * typically set in the queueRequest() function, from the consolidated
+ * control values stored in the active state. The frame context thus stores
+ * values for all controls related to the algorithm, not limited to the
+ * controls specified in the corresponding request, but consolidated from all
+ * requests that have been queued so far.
+ *
+ * For controls that can be set manually or computed by an algorithm
+ * (depending on the algorithm operation mode), such as for instance the
+ * colour gains for the AWB algorithm, the control value will be stored in
+ * the frame context in the queueRequest() function only when operating in
+ * manual mode. When operating in auto mode, the values are computed by the
+ * algorithm in process(), stored in the active state, and copied to the
+ * frame context in prepare(), just before being stored in the ISP parameters
+ * buffer.
+ *
+ * The queueRequest() function can also store ancillary data in the frame
+ * context, such as flags to indicate if (and what) control values have
+ * changed compared to the previous request.
+ *
+ * - Status information computed by the algorithm for a frame. For instance,
+ * the colour temperature estimated by the AWB algorithm from ISP statistics
+ * calculated on a frame is stored in the frame context for that frame in
+ * the process() function.
+ */
+
+/**
+ * \struct IPAContext
+ * \brief Global IPA context data shared between all algorithms
+ *
+ * \var IPAContext::configuration
+ * \brief The IPA session configuration, immutable during the session
+ *
+ * \var IPAContext::activeState
+ * \brief The IPA active state, storing the latest state for all algorithms
+ *
+ * \var IPAContext::frameContexts
+ * \brief Ring buffer of per-frame contexts
+ */
+
+} /* namespace libcamera::ipa::mali_c55 */
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
new file mode 100644
index 00000000..5e3e2fbd
--- /dev/null
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * ipa_context.h - Mali-C55 IPA Context
+ */
+
+#pragma once
+
+#include <libcamera/base/utils.h>
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+#include <libipa/fc_queue.h>
+
+namespace libcamera {
+
+namespace ipa::mali_c55 {
+
+struct IPASessionConfiguration {
+ struct {
+ utils::Duration minShutterSpeed;
+ utils::Duration maxShutterSpeed;
+ uint32_t defaultExposure;
+ double minAnalogueGain;
+ double maxAnalogueGain;
+ } agc;
+
+ struct {
+ BayerFormat::Order bayerOrder;
+ utils::Duration lineDuration;
+ uint32_t blackLevel;
+ } sensor;
+};
+
+struct IPAActiveState {
+ struct {
+ struct {
+ uint32_t exposure;
+ double sensorGain;
+ double ispGain;
+ } automatic;
+ struct {
+ uint32_t exposure;
+ double sensorGain;
+ double ispGain;
+ } manual;
+ bool autoEnabled;
+ uint32_t constraintMode;
+ uint32_t exposureMode;
+ uint32_t temperatureK;
+ } agc;
+
+ struct {
+ double rGain;
+ double bGain;
+ } awb;
+};
+
+struct IPAFrameContext : public FrameContext {
+ struct {
+ uint32_t exposure;
+ double sensorGain;
+ double ispGain;
+ } agc;
+
+ struct {
+ double rGain;
+ double bGain;
+ } awb;
+};
+
+struct IPAContext {
+ IPAContext(unsigned int frameContextSize)
+ : frameContexts(frameContextSize)
+ {
+ }
+
+ IPASessionConfiguration configuration;
+ IPAActiveState activeState;
+
+ FCQueue<IPAFrameContext> frameContexts;
+
+ ControlInfoMap::Map ctrlMap;
+};
+
+} /* namespace ipa::mali_c55 */
+
+} /* namespace libcamera*/
diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp
new file mode 100644
index 00000000..c6941a95
--- /dev/null
+++ b/src/ipa/mali-c55/mali-c55.cpp
@@ -0,0 +1,399 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * mali-c55.cpp - Mali-C55 ISP image processing algorithms
+ */
+
+#include <map>
+#include <string.h>
+#include <vector>
+
+#include <linux/mali-c55-config.h>
+#include <linux/v4l2-controls.h>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/mali-c55_ipa_interface.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/mapped_framebuffer.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "algorithms/algorithm.h"
+#include "libipa/camera_sensor_helper.h"
+
+#include "ipa_context.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPAMaliC55)
+
+using namespace std::literals::chrono_literals;
+
+namespace ipa::mali_c55 {
+
+/* Maximum number of frame contexts to be held */
+static constexpr uint32_t kMaxFrameContexts = 16;
+
+class IPAMaliC55 : public IPAMaliC55Interface, public Module
+{
+public:
+ IPAMaliC55();
+
+ int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
+ ControlInfoMap *ipaControls) override;
+ int start() override;
+ void stop() override;
+ int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
+ ControlInfoMap *ipaControls) override;
+ void mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly) override;
+ void unmapBuffers(const std::vector<IPABuffer> &buffers) override;
+ void queueRequest(const uint32_t request, const ControlList &controls) override;
+ void fillParams(unsigned int request, uint32_t bufferId) override;
+ void processStats(unsigned int request, unsigned int bufferId,
+ const ControlList &sensorControls) override;
+
+protected:
+ std::string logPrefix() const override;
+
+private:
+ void updateSessionConfiguration(const IPACameraSensorInfo &info,
+ const ControlInfoMap &sensorControls,
+ BayerFormat::Order bayerOrder);
+ void updateControls(const IPACameraSensorInfo &sensorInfo,
+ const ControlInfoMap &sensorControls,
+ ControlInfoMap *ipaControls);
+ void setControls();
+
+ std::map<unsigned int, MappedFrameBuffer> buffers_;
+
+ ControlInfoMap sensorControls_;
+
+ /* Interface to the Camera Helper */
+ std::unique_ptr<CameraSensorHelper> camHelper_;
+
+ /* Local parameter storage */
+ struct IPAContext context_;
+};
+
+namespace {
+
+} /* namespace */
+
+IPAMaliC55::IPAMaliC55()
+ : context_(kMaxFrameContexts)
+{
+}
+
+std::string IPAMaliC55::logPrefix() const
+{
+ return "mali-c55";
+}
+
+int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
+ ControlInfoMap *ipaControls)
+{
+ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+ if (!camHelper_) {
+ LOG(IPAMaliC55, Error)
+ << "Failed to create camera sensor helper for "
+ << settings.sensorModel;
+ return -ENODEV;
+ }
+
+ File file(settings.configurationFile);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ int ret = file.error();
+ LOG(IPAMaliC55, Error)
+ << "Failed to open configuration file "
+ << settings.configurationFile << ": " << strerror(-ret);
+ return ret;
+ }
+
+ std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);
+ if (!data)
+ return -EINVAL;
+
+ if (!data->contains("algorithms")) {
+ LOG(IPAMaliC55, Error)
+ << "Tuning file doesn't contain any algorithm";
+ return -EINVAL;
+ }
+
+ int ret = createAlgorithms(context_, (*data)["algorithms"]);
+ if (ret)
+ return ret;
+
+ updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);
+
+ return 0;
+}
+
+void IPAMaliC55::setControls()
+{
+ IPAActiveState &activeState = context_.activeState;
+ uint32_t exposure;
+ uint32_t gain;
+
+ if (activeState.agc.autoEnabled) {
+ exposure = activeState.agc.automatic.exposure;
+ gain = camHelper_->gainCode(activeState.agc.automatic.sensorGain);
+ } else {
+ exposure = activeState.agc.manual.exposure;
+ gain = camHelper_->gainCode(activeState.agc.manual.sensorGain);
+ }
+
+ ControlList ctrls(sensorControls_);
+ ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));
+
+ setSensorControls.emit(ctrls);
+}
+
+int IPAMaliC55::start()
+{
+ return 0;
+}
+
+void IPAMaliC55::stop()
+{
+ context_.frameContexts.clear();
+}
+
+void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info,
+ const ControlInfoMap &sensorControls,
+ BayerFormat::Order bayerOrder)
+{
+ context_.configuration.sensor.bayerOrder = bayerOrder;
+
+ const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
+ int32_t minExposure = v4l2Exposure.min().get<int32_t>();
+ int32_t maxExposure = v4l2Exposure.max().get<int32_t>();
+ int32_t defExposure = v4l2Exposure.def().get<int32_t>();
+
+ const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
+ int32_t minGain = v4l2Gain.min().get<int32_t>();
+ int32_t maxGain = v4l2Gain.max().get<int32_t>();
+
+ /*
+ * When the AGC computes the new exposure values for a frame, it needs
+ * to know the limits for shutter speed and analogue gain.
+ * As it depends on the sensor, update it with the controls.
+ *
+ * \todo take VBLANK into account for maximum shutter speed
+ */
+ context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;
+ context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;
+ context_.configuration.agc.defaultExposure = defExposure;
+ context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);
+ context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);
+
+ if (camHelper_->blackLevel().has_value()) {
+ /*
+ * The black level from CameraSensorHelper is a 16-bit value.
+ * The Mali-C55 ISP expects 20-bit settings, so we shift it to
+ * the appropriate width
+ */
+ context_.configuration.sensor.blackLevel =
+ camHelper_->blackLevel().value() << 4;
+ }
+}
+
+void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
+ const ControlInfoMap &sensorControls,
+ ControlInfoMap *ipaControls)
+{
+ ControlInfoMap::Map ctrlMap;
+
+ /*
+ * Compute the frame duration limits.
+ *
+ * The frame length is computed assuming a fixed line length combined
+ * with the vertical frame sizes.
+ */
+ const ControlInfo &v4l2HBlank = sensorControls.find(V4L2_CID_HBLANK)->second;
+ uint32_t hblank = v4l2HBlank.def().get<int32_t>();
+ uint32_t lineLength = sensorInfo.outputSize.width + hblank;
+
+ const ControlInfo &v4l2VBlank = sensorControls.find(V4L2_CID_VBLANK)->second;
+ std::array<uint32_t, 3> frameHeights{
+ v4l2VBlank.min().get<int32_t>() + sensorInfo.outputSize.height,
+ v4l2VBlank.max().get<int32_t>() + sensorInfo.outputSize.height,
+ v4l2VBlank.def().get<int32_t>() + sensorInfo.outputSize.height,
+ };
+
+ std::array<int64_t, 3> frameDurations;
+ for (unsigned int i = 0; i < frameHeights.size(); ++i) {
+ uint64_t frameSize = lineLength * frameHeights[i];
+ frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);
+ }
+
+ ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],
+ frameDurations[1],
+ frameDurations[2]);
+
+ /*
+ * Compute exposure time limits from the V4L2_CID_EXPOSURE control
+ * limits and the line duration.
+ */
+ double lineDuration = sensorInfo.minLineLength / sensorInfo.pixelRate;
+
+ const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
+ int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;
+ int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration;
+ int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration;
+ ctrlMap[&controls::ExposureTime] = ControlInfo(minExposure, maxExposure, defExposure);
+
+ /* Compute the analogue gain limits. */
+ const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
+ float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());
+ float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());
+ float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());
+ ctrlMap[&controls::AnalogueGain] = ControlInfo(minGain, maxGain, defGain);
+
+ /*
+ * Merge in any controls that we support either statically or from the
+ * algorithms.
+ */
+ ctrlMap.merge(context_.ctrlMap);
+
+ *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
+}
+
+int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
+ ControlInfoMap *ipaControls)
+{
+ sensorControls_ = ipaConfig.sensorControls;
+
+ /* Clear the IPA context before the streaming session. */
+ context_.configuration = {};
+ context_.activeState = {};
+ context_.frameContexts.clear();
+
+ const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
+
+ updateSessionConfiguration(info, ipaConfig.sensorControls,
+ static_cast<BayerFormat::Order>(bayerOrder));
+ updateControls(info, ipaConfig.sensorControls, ipaControls);
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+
+ int ret = algo->configure(context_, info);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void IPAMaliC55::mapBuffers(const std::vector<IPABuffer> &buffers, bool readOnly)
+{
+ for (const IPABuffer &buffer : buffers) {
+ const FrameBuffer fb(buffer.planes);
+ buffers_.emplace(
+ buffer.id,
+ MappedFrameBuffer(
+ &fb,
+ readOnly ? MappedFrameBuffer::MapFlag::Read
+ : MappedFrameBuffer::MapFlag::ReadWrite));
+ }
+}
+
+void IPAMaliC55::unmapBuffers(const std::vector<IPABuffer> &buffers)
+{
+ for (const IPABuffer &buffer : buffers) {
+ auto it = buffers_.find(buffer.id);
+ if (it == buffers_.end())
+ continue;
+
+ buffers_.erase(buffer.id);
+ }
+}
+
+void IPAMaliC55::queueRequest(const uint32_t request, const ControlList &controls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.alloc(request);
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+
+ algo->queueRequest(context_, request, frameContext, controls);
+ }
+}
+
+void IPAMaliC55::fillParams(unsigned int request,
+ [[maybe_unused]] uint32_t bufferId)
+{
+ struct mali_c55_params_buffer *params;
+ IPAFrameContext &frameContext = context_.frameContexts.get(request);
+
+ params = reinterpret_cast<mali_c55_params_buffer *>(
+ buffers_.at(bufferId).planes()[0].data());
+ memset(params, 0, sizeof(mali_c55_params_buffer));
+
+ params->version = MALI_C55_PARAM_BUFFER_V1;
+
+ for (auto const &algo : algorithms()) {
+ algo->prepare(context_, request, frameContext, params);
+
+ ASSERT(params->total_size <= MALI_C55_PARAMS_MAX_SIZE);
+ }
+
+ paramsComputed.emit(request);
+}
+
+void IPAMaliC55::processStats(unsigned int request, unsigned int bufferId,
+ const ControlList &sensorControls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.get(request);
+ const mali_c55_stats_buffer *stats = nullptr;
+
+ stats = reinterpret_cast<mali_c55_stats_buffer *>(
+ buffers_.at(bufferId).planes()[0].data());
+
+ frameContext.agc.exposure =
+ sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();
+ frameContext.agc.sensorGain =
+ camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());
+
+ ControlList metadata(controls::controls);
+
+ for (auto const &a : algorithms()) {
+ Algorithm *algo = static_cast<Algorithm *>(a.get());
+
+ algo->process(context_, request, frameContext, stats, metadata);
+ }
+
+ setControls();
+
+ statsProcessed.emit(request, metadata);
+}
+
+} /* namespace ipa::mali_c55 */
+
+/*
+ * External IPA module interface
+ */
+extern "C" {
+const struct IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 1,
+ "mali-c55",
+ "mali-c55",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::mali_c55::IPAMaliC55();
+}
+
+} /* extern "C" */
+
+} /* namespace libcamera */
diff --git a/src/ipa/mali-c55/meson.build b/src/ipa/mali-c55/meson.build
new file mode 100644
index 00000000..864d90ec
--- /dev/null
+++ b/src/ipa/mali-c55/meson.build
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: CC0-1.0
+
+subdir('algorithms')
+subdir('data')
+
+ipa_name = 'ipa_mali_c55'
+
+mali_c55_ipa_sources = files([
+ 'ipa_context.cpp',
+ 'mali-c55.cpp'
+])
+
+mali_c55_ipa_sources += mali_c55_ipa_algorithms
+
+mod = shared_module(ipa_name,
+ mali_c55_ipa_sources,
+ name_prefix : '',
+ include_directories : [ipa_includes, libipa_includes],
+ dependencies : libcamera_private,
+ 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
+
+ipa_names += ipa_name
diff --git a/src/ipa/mali-c55/module.h b/src/ipa/mali-c55/module.h
new file mode 100644
index 00000000..1d85ec1f
--- /dev/null
+++ b/src/ipa/mali-c55/module.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * module.h - Mali-C55 IPA Module
+ */
+
+#pragma once
+
+#include <linux/mali-c55-config.h>
+
+#include <libcamera/ipa/mali-c55_ipa_interface.h>
+
+#include <libipa/module.h>
+
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::mali_c55 {
+
+using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,
+ mali_c55_params_buffer, mali_c55_stats_buffer>;
+
+} /* namespace ipa::mali_c55 */
+
+} /* namespace libcamera*/
diff --git a/src/ipa/meson.build b/src/ipa/meson.build
index 903eb52b..68f64b9a 100644
--- a/src/ipa/meson.build
+++ b/src/ipa/meson.build
@@ -4,7 +4,7 @@ ipa_includes = [
libcamera_includes,
]
-ipa_install_dir = libcamera_libdir
+ipa_install_dir = libcamera_libdir / 'ipa'
ipa_data_dir = libcamera_datadir / 'ipa'
ipa_sysconf_dir = libcamera_sysconfdir / 'ipa'
@@ -41,6 +41,9 @@ ipa_names = []
subdirs = []
foreach pipeline : pipelines
+ # The current implementation expects the IPA module name to match the
+ # pipeline name.
+ # \todo Make the IPA naming scheme more flexible.
if not ipa_modules.contains(pipeline)
continue
endif
@@ -72,5 +75,6 @@ if ipa_sign_module
# install time, which invalidates the signatures.
meson.add_install_script('ipa-sign-install.sh',
ipa_priv_key.full_path(),
- enabled_ipa_modules)
+ enabled_ipa_modules,
+ install_tag : 'runtime')
endif
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index e5aeb342..137a0750 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * agc.cpp - AGC/AEC mean-based control algorithm
+ * AGC/AEC mean-based control algorithm
*/
#include "agc.h"
@@ -10,6 +10,8 @@
#include <algorithm>
#include <chrono>
#include <cmath>
+#include <tuple>
+#include <vector>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
@@ -17,6 +19,8 @@
#include <libcamera/control_ids.h>
#include <libcamera/ipa/core_ipa_interface.h>
+#include "libcamera/internal/yaml_parser.h"
+
#include "libipa/histogram.h"
/**
@@ -36,35 +40,129 @@ namespace ipa::rkisp1::algorithms {
LOG_DEFINE_CATEGORY(RkISP1Agc)
-/* Minimum limit for analogue gain value */
-static constexpr double kMinAnalogueGain = 1.0;
+int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData)
+{
+ if (!tuningData.isDictionary())
+ LOG(RkISP1Agc, Warning)
+ << "'AeMeteringMode' parameter not found in tuning file";
+
+ for (const auto &[key, value] : tuningData.asDict()) {
+ if (controls::AeMeteringModeNameValueMap.find(key) ==
+ controls::AeMeteringModeNameValueMap.end()) {
+ LOG(RkISP1Agc, Warning)
+ << "Skipping unknown metering mode '" << key << "'";
+ continue;
+ }
-/* \todo Honour the FrameDurationLimits control instead of hardcoding a limit */
-static constexpr utils::Duration kMaxShutterSpeed = 60ms;
+ std::vector<uint8_t> weights =
+ value.getList<uint8_t>().value_or(std::vector<uint8_t>{});
+ if (weights.size() != context.hw->numHistogramWeights) {
+ LOG(RkISP1Agc, Warning)
+ << "Failed to read metering mode'" << key << "'";
+ continue;
+ }
-/* Number of frames to wait before calculating stats on minimum exposure */
-static constexpr uint32_t kNumStartupFrames = 10;
+ meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights;
+ }
-/* Target value to reach for the top 2% of the histogram */
-static constexpr double kEvGainTarget = 0.5;
+ if (meteringModes_.empty()) {
+ LOG(RkISP1Agc, Warning)
+ << "No metering modes read from tuning file; defaulting to matrix";
+ std::vector<uint8_t> weights(context.hw->numHistogramWeights, 1);
-/*
- * Relative luminance target.
- *
- * It's a number that's chosen so that, when the camera points at a grey
- * target, the resulting image brightness is considered right.
- *
- * \todo Why is the value different between IPU3 and RkISP1 ?
- */
-static constexpr double kRelativeLuminanceTarget = 0.4;
+ meteringModes_[controls::MeteringMatrix] = weights;
+ }
+
+ std::vector<ControlValue> meteringModes;
+ std::vector<int> meteringModeKeys = utils::map_keys(meteringModes_);
+ std::transform(meteringModeKeys.begin(), meteringModeKeys.end(),
+ std::back_inserter(meteringModes),
+ [](int x) { return ControlValue(x); });
+ context.ctrlMap[&controls::AeMeteringMode] = ControlInfo(meteringModes);
+
+ return 0;
+}
+
+uint8_t Agc::computeHistogramPredivider(const Size &size,
+ enum rkisp1_cif_isp_histogram_mode mode)
+{
+ /*
+ * The maximum number of pixels that could potentially be in one bin is
+ * if all the pixels of the image are in it, multiplied by 3 for the
+ * three color channels. The counter for each bin is 16 bits wide, so
+ * `factor` thus contains the number of times we'd wrap around. This is
+ * obviously the number of pixels that we need to skip to make sure
+ * that we don't wrap around, but we compute the square root of it
+ * instead, as the skip that we need to program is for both the x and y
+ * directions.
+ *
+ * Even though it looks like dividing into a counter of 65536 would
+ * overflow by 1, this is apparently fine according to the hardware
+ * documentation, and this successfully gets the expected documented
+ * predivider size for cases where:
+ * (width / predivider) * (height / predivider) * 3 == 65536.
+ *
+ * There's a bit of extra rounding math to make sure the rounding goes
+ * the correct direction so that the square of the step is big enough
+ * to encompass the `factor` number of pixels that we need to skip.
+ *
+ * \todo Take into account weights. That is, if the weights are low
+ * enough we can potentially reduce the predivider to increase
+ * precision. This needs some investigation however, as this hardware
+ * behavior is undocumented and is only an educated guess.
+ */
+ int count = mode == RKISP1_CIF_ISP_HISTOGRAM_MODE_RGB_COMBINED ? 3 : 1;
+ double factor = size.width * size.height * count / 65536.0;
+ double root = std::sqrt(factor);
+ uint8_t predivider = static_cast<uint8_t>(std::ceil(root));
+
+ return std::clamp<uint8_t>(predivider, 3, 127);
+}
Agc::Agc()
- : frameCount_(0), numCells_(0), numHistBins_(0), filteredExposure_(0s)
{
supportsRaw_ = true;
}
/**
+ * \brief Initialise the AGC algorithm from tuning files
+ * \param[in] context The shared IPA context
+ * \param[in] tuningData The YamlObject containing Agc tuning data
+ *
+ * This function calls the base class' tuningData parsers to discover which
+ * control values are supported.
+ *
+ * \return 0 on success or errors from the base class
+ */
+int Agc::init(IPAContext &context, const YamlObject &tuningData)
+{
+ int ret;
+
+ ret = parseTuningData(tuningData);
+ if (ret)
+ return ret;
+
+ const YamlObject &yamlMeteringModes = tuningData["AeMeteringMode"];
+ ret = parseMeteringModes(context, yamlMeteringModes);
+ if (ret)
+ return ret;
+
+ context.ctrlMap[&controls::ExposureTimeMode] =
+ ControlInfo({ { ControlValue(controls::ExposureTimeModeAuto),
+ ControlValue(controls::ExposureTimeModeManual) } },
+ ControlValue(controls::ExposureTimeModeAuto));
+ context.ctrlMap[&controls::AnalogueGainMode] =
+ ControlInfo({ { ControlValue(controls::AnalogueGainModeAuto),
+ ControlValue(controls::AnalogueGainModeManual) } },
+ ControlValue(controls::AnalogueGainModeAuto));
+ /* \todo Move this to the Camera class */
+ context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true, true);
+ context.ctrlMap.merge(controls());
+
+ return 0;
+}
+
+/**
* \brief Configure the AGC given a configInfo
* \param[in] context The shared IPA context
* \param[in] configInfo The IPA configuration data
@@ -79,35 +177,33 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
10ms / context.configuration.sensor.lineDuration;
context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain;
context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure;
- context.activeState.agc.autoEnabled = !context.configuration.raw;
+ context.activeState.agc.autoExposureEnabled = !context.configuration.raw;
+ context.activeState.agc.autoGainEnabled = !context.configuration.raw;
- /*
- * According to the RkISP1 documentation:
- * - versions < V12 have RKISP1_CIF_ISP_AE_MEAN_MAX_V10 entries,
- * - versions >= V12 have RKISP1_CIF_ISP_AE_MEAN_MAX_V12 entries.
- */
- if (context.configuration.hw.revision < RKISP1_V12) {
- numCells_ = RKISP1_CIF_ISP_AE_MEAN_MAX_V10;
- numHistBins_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10;
- } else {
- numCells_ = RKISP1_CIF_ISP_AE_MEAN_MAX_V12;
- numHistBins_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12;
- }
+ context.activeState.agc.constraintMode =
+ static_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);
+ context.activeState.agc.exposureMode =
+ static_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);
+ context.activeState.agc.meteringMode =
+ static_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);
- /*
- * Define the measurement window for AGC as a centered rectangle
- * covering 3/4 of the image width and height.
- */
- context.configuration.agc.measureWindow.h_offs = configInfo.outputSize.width / 8;
- context.configuration.agc.measureWindow.v_offs = configInfo.outputSize.height / 8;
- context.configuration.agc.measureWindow.h_size = 3 * configInfo.outputSize.width / 4;
- context.configuration.agc.measureWindow.v_size = 3 * configInfo.outputSize.height / 4;
+ /* Limit the frame duration to match current initialisation */
+ ControlInfo &frameDurationLimits = context.ctrlMap[&controls::FrameDurationLimits];
+ context.activeState.agc.minFrameDuration = std::chrono::microseconds(frameDurationLimits.min().get<int64_t>());
+ context.activeState.agc.maxFrameDuration = std::chrono::microseconds(frameDurationLimits.max().get<int64_t>());
+
+ context.configuration.agc.measureWindow.h_offs = 0;
+ context.configuration.agc.measureWindow.v_offs = 0;
+ context.configuration.agc.measureWindow.h_size = configInfo.outputSize.width;
+ context.configuration.agc.measureWindow.v_size = configInfo.outputSize.height;
+
+ setLimits(context.configuration.sensor.minExposureTime,
+ context.configuration.sensor.maxExposureTime,
+ context.configuration.sensor.minAnalogueGain,
+ context.configuration.sensor.maxAnalogueGain);
+
+ resetFrameCount();
- /*
- * \todo Use the upcoming per-frame context API that will provide a
- * frame index
- */
- frameCount_ = 0;
return 0;
}
@@ -122,18 +218,47 @@ void Agc::queueRequest(IPAContext &context,
auto &agc = context.activeState.agc;
if (!context.configuration.raw) {
- const auto &agcEnable = controls.get(controls::AeEnable);
- if (agcEnable && *agcEnable != agc.autoEnabled) {
- agc.autoEnabled = *agcEnable;
+ const auto &aeEnable = controls.get(controls::ExposureTimeMode);
+ if (aeEnable &&
+ (*aeEnable == controls::ExposureTimeModeAuto) != agc.autoExposureEnabled) {
+ agc.autoExposureEnabled = (*aeEnable == controls::ExposureTimeModeAuto);
+
+ LOG(RkISP1Agc, Debug)
+ << (agc.autoExposureEnabled ? "Enabling" : "Disabling")
+ << " AGC (exposure)";
+
+ /*
+ * If we go from auto -> manual with no manual control
+ * set, use the last computed value, which we don't
+ * know until prepare() so save this information.
+ *
+ * \todo Check the previous frame at prepare() time
+ * instead of saving a flag here
+ */
+ if (!agc.autoExposureEnabled && !controls.get(controls::ExposureTime))
+ frameContext.agc.autoExposureModeChange = true;
+ }
+
+ const auto &agEnable = controls.get(controls::AnalogueGainMode);
+ if (agEnable &&
+ (*agEnable == controls::AnalogueGainModeAuto) != agc.autoGainEnabled) {
+ agc.autoGainEnabled = (*agEnable == controls::AnalogueGainModeAuto);
LOG(RkISP1Agc, Debug)
- << (agc.autoEnabled ? "Enabling" : "Disabling")
- << " AGC";
+ << (agc.autoGainEnabled ? "Enabling" : "Disabling")
+ << " AGC (gain)";
+ /*
+ * If we go from auto -> manual with no manual control
+ * set, use the last computed value, which we don't
+ * know until prepare() so save this information.
+ */
+ if (!agc.autoGainEnabled && !controls.get(controls::AnalogueGain))
+ frameContext.agc.autoGainModeChange = true;
}
}
const auto &exposure = controls.get(controls::ExposureTime);
- if (exposure && !agc.autoEnabled) {
+ if (exposure && !agc.autoExposureEnabled) {
agc.manual.exposure = *exposure * 1.0us
/ context.configuration.sensor.lineDuration;
@@ -142,183 +267,150 @@ void Agc::queueRequest(IPAContext &context,
}
const auto &gain = controls.get(controls::AnalogueGain);
- if (gain && !agc.autoEnabled) {
+ if (gain && !agc.autoGainEnabled) {
agc.manual.gain = *gain;
LOG(RkISP1Agc, Debug) << "Set gain to " << agc.manual.gain;
}
- frameContext.agc.autoEnabled = agc.autoEnabled;
+ frameContext.agc.autoExposureEnabled = agc.autoExposureEnabled;
+ frameContext.agc.autoGainEnabled = agc.autoGainEnabled;
- if (!frameContext.agc.autoEnabled) {
+ if (!frameContext.agc.autoExposureEnabled)
frameContext.agc.exposure = agc.manual.exposure;
+ if (!frameContext.agc.autoGainEnabled)
frameContext.agc.gain = agc.manual.gain;
+
+ const auto &meteringMode = controls.get(controls::AeMeteringMode);
+ if (meteringMode) {
+ frameContext.agc.updateMetering = agc.meteringMode != *meteringMode;
+ agc.meteringMode =
+ static_cast<controls::AeMeteringModeEnum>(*meteringMode);
}
+ frameContext.agc.meteringMode = agc.meteringMode;
+
+ const auto &exposureMode = controls.get(controls::AeExposureMode);
+ if (exposureMode)
+ agc.exposureMode =
+ static_cast<controls::AeExposureModeEnum>(*exposureMode);
+ frameContext.agc.exposureMode = agc.exposureMode;
+
+ const auto &constraintMode = controls.get(controls::AeConstraintMode);
+ if (constraintMode)
+ agc.constraintMode =
+ static_cast<controls::AeConstraintModeEnum>(*constraintMode);
+ frameContext.agc.constraintMode = agc.constraintMode;
+
+ const auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);
+ if (frameDurationLimits) {
+ /* Limit the control value to the limits in ControlInfo */
+ ControlInfo &limits = context.ctrlMap[&controls::FrameDurationLimits];
+ int64_t minFrameDuration =
+ std::clamp((*frameDurationLimits).front(),
+ limits.min().get<int64_t>(),
+ limits.max().get<int64_t>());
+ int64_t maxFrameDuration =
+ std::clamp((*frameDurationLimits).back(),
+ limits.min().get<int64_t>(),
+ limits.max().get<int64_t>());
+
+ agc.minFrameDuration = std::chrono::microseconds(minFrameDuration);
+ agc.maxFrameDuration = std::chrono::microseconds(maxFrameDuration);
+ }
+ frameContext.agc.minFrameDuration = agc.minFrameDuration;
+ frameContext.agc.maxFrameDuration = agc.maxFrameDuration;
}
/**
* \copydoc libcamera::ipa::Algorithm::prepare
*/
void Agc::prepare(IPAContext &context, const uint32_t frame,
- IPAFrameContext &frameContext, rkisp1_params_cfg *params)
-{
- if (frameContext.agc.autoEnabled) {
- frameContext.agc.exposure = context.activeState.agc.automatic.exposure;
- frameContext.agc.gain = context.activeState.agc.automatic.gain;
- }
-
- if (frame > 0)
- return;
-
- /* Configure the measurement window. */
- params->meas.aec_config.meas_window = context.configuration.agc.measureWindow;
- /* Use a continuous method for measure. */
- params->meas.aec_config.autostop = RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP_0;
- /* Estimate Y as (R + G + B) x (85/256). */
- params->meas.aec_config.mode = RKISP1_CIF_ISP_EXP_MEASURING_MODE_1;
-
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_AEC;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_AEC;
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_AEC;
-
- /* Configure histogram. */
- params->meas.hst_config.meas_window = context.configuration.agc.measureWindow;
- /* Produce the luminance histogram. */
- params->meas.hst_config.mode = RKISP1_CIF_ISP_HISTOGRAM_MODE_Y_HISTOGRAM;
- /* Set an average weighted histogram. */
- for (unsigned int histBin = 0; histBin < numHistBins_; histBin++)
- params->meas.hst_config.hist_weight[histBin] = 1;
- /* Step size can't be less than 3. */
- params->meas.hst_config.histogram_predivider = 4;
-
- /* Update the configuration for histogram. */
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_HST;
- /* Enable the histogram measure unit. */
- params->module_ens |= RKISP1_CIF_ISP_MODULE_HST;
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_HST;
-}
-
-/**
- * \brief Apply a filter on the exposure value to limit the speed of changes
- * \param[in] exposureValue The target exposure from the AGC algorithm
- *
- * The speed of the filter is adaptive, and will produce the target quicker
- * during startup, or when the target exposure is within 20% of the most recent
- * filter output.
- *
- * \return The filtered exposure
- */
-utils::Duration Agc::filterExposure(utils::Duration exposureValue)
+ IPAFrameContext &frameContext, RkISP1Params *params)
{
- double speed = 0.2;
+ uint32_t activeAutoExposure = context.activeState.agc.automatic.exposure;
+ double activeAutoGain = context.activeState.agc.automatic.gain;
- /* Adapt instantly if we are in startup phase. */
- if (frameCount_ < kNumStartupFrames)
- speed = 1.0;
+ /* Populate exposure and gain in auto mode */
+ if (frameContext.agc.autoExposureEnabled)
+ frameContext.agc.exposure = activeAutoExposure;
+ if (frameContext.agc.autoGainEnabled)
+ frameContext.agc.gain = activeAutoGain;
/*
- * If we are close to the desired result, go faster to avoid making
- * multiple micro-adjustments.
- * \todo Make this customisable?
+ * Populate manual exposure and gain from the active auto values when
+ * transitioning from auto to manual
*/
- if (filteredExposure_ < 1.2 * exposureValue &&
- filteredExposure_ > 0.8 * exposureValue)
- speed = sqrt(speed);
-
- filteredExposure_ = speed * exposureValue +
- filteredExposure_ * (1.0 - speed);
-
- LOG(RkISP1Agc, Debug) << "After filtering, exposure " << filteredExposure_;
-
- return filteredExposure_;
-}
-
-/**
- * \brief Estimate the new exposure and gain values
- * \param[inout] context The shared IPA Context
- * \param[in] frameContext The FrameContext for this frame
- * \param[in] yGain The gain calculated on the current brightness level
- * \param[in] iqMeanGain The gain calculated based on the relative luminance target
- */
-void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
- double yGain, double iqMeanGain)
-{
- IPASessionConfiguration &configuration = context.configuration;
- IPAActiveState &activeState = context.activeState;
-
- /* Get the effective exposure and gain applied on the sensor. */
- uint32_t exposure = frameContext.sensor.exposure;
- double analogueGain = frameContext.sensor.gain;
-
- /* Use the highest of the two gain estimates. */
- double evGain = std::max(yGain, iqMeanGain);
-
- utils::Duration minShutterSpeed = configuration.sensor.minShutterSpeed;
- utils::Duration maxShutterSpeed = std::min(configuration.sensor.maxShutterSpeed,
- kMaxShutterSpeed);
-
- double minAnalogueGain = std::max(configuration.sensor.minAnalogueGain,
- kMinAnalogueGain);
- double maxAnalogueGain = configuration.sensor.maxAnalogueGain;
+ if (!frameContext.agc.autoExposureEnabled && frameContext.agc.autoExposureModeChange) {
+ context.activeState.agc.manual.exposure = activeAutoExposure;
+ frameContext.agc.exposure = activeAutoExposure;
+ }
+ if (!frameContext.agc.autoGainEnabled && frameContext.agc.autoGainModeChange) {
+ context.activeState.agc.manual.gain = activeAutoGain;
+ frameContext.agc.gain = activeAutoGain;
+ }
- /* Consider within 1% of the target as correctly exposed. */
- if (utils::abs_diff(evGain, 1.0) < 0.01)
+ if (frame > 0 && !frameContext.agc.updateMetering)
return;
- /* extracted from Rpi::Agc::computeTargetExposure. */
-
- /* Calculate the shutter time in seconds. */
- utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
-
- /*
- * Update the exposure value for the next computation using the values
- * of exposure and gain really used by the sensor.
- */
- utils::Duration effectiveExposureValue = currentShutter * analogueGain;
-
- LOG(RkISP1Agc, Debug) << "Actual total exposure " << currentShutter * analogueGain
- << " Shutter speed " << currentShutter
- << " Gain " << analogueGain
- << " Needed ev gain " << evGain;
-
/*
- * Calculate the current exposure value for the scene as the latest
- * exposure value applied multiplied by the new estimated gain.
+ * Configure the AEC measurements. Set the window, measure
+ * continuously, and estimate Y as (R + G + B) x (85/256).
*/
- utils::Duration exposureValue = effectiveExposureValue * evGain;
+ auto aecConfig = params->block<BlockType::Aec>();
+ aecConfig.setEnabled(true);
- /* Clamp the exposure value to the min and max authorized. */
- utils::Duration maxTotalExposure = maxShutterSpeed * maxAnalogueGain;
- exposureValue = std::min(exposureValue, maxTotalExposure);
- LOG(RkISP1Agc, Debug) << "Target total exposure " << exposureValue
- << ", maximum is " << maxTotalExposure;
+ aecConfig->meas_window = context.configuration.agc.measureWindow;
+ aecConfig->autostop = RKISP1_CIF_ISP_EXP_CTRL_AUTOSTOP_0;
+ aecConfig->mode = RKISP1_CIF_ISP_EXP_MEASURING_MODE_1;
/*
- * Divide the exposure value as new exposure and gain values.
- * \todo estimate if we need to desaturate
+ * Configure the histogram measurement. Set the window, produce a
+ * luminance histogram, and set the weights and predivider.
*/
- exposureValue = filterExposure(exposureValue);
-
- /*
- * Push the shutter time up to the maximum first, and only then
- * increase the gain.
- */
- utils::Duration shutterTime = std::clamp<utils::Duration>(exposureValue / minAnalogueGain,
- minShutterSpeed, maxShutterSpeed);
- double stepGain = std::clamp(exposureValue / shutterTime,
- minAnalogueGain, maxAnalogueGain);
- LOG(RkISP1Agc, Debug) << "Divided up shutter and gain are "
- << shutterTime << " and "
- << stepGain;
+ auto hstConfig = params->block<BlockType::Hst>();
+ hstConfig.setEnabled(true);
+
+ hstConfig->meas_window = context.configuration.agc.measureWindow;
+ hstConfig->mode = RKISP1_CIF_ISP_HISTOGRAM_MODE_Y_HISTOGRAM;
+
+ Span<uint8_t> weights{
+ hstConfig->hist_weight,
+ context.hw->numHistogramWeights
+ };
+ std::vector<uint8_t> &modeWeights = meteringModes_.at(frameContext.agc.meteringMode);
+ std::copy(modeWeights.begin(), modeWeights.end(), weights.begin());
+
+ struct rkisp1_cif_isp_window window = hstConfig->meas_window;
+ Size windowSize = { window.h_size, window.v_size };
+ hstConfig->histogram_predivider =
+ computeHistogramPredivider(windowSize,
+ static_cast<rkisp1_cif_isp_histogram_mode>(hstConfig->mode));
+}
- /* Update the estimated exposure and gain. */
- activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
- activeState.agc.automatic.gain = stepGain;
+void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
+ ControlList &metadata)
+{
+ utils::Duration exposureTime = context.configuration.sensor.lineDuration
+ * frameContext.sensor.exposure;
+ metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
+ metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
+ metadata.set(controls::FrameDuration, frameContext.agc.frameDuration.get<std::micro>());
+ metadata.set(controls::ExposureTimeMode,
+ frameContext.agc.autoExposureEnabled
+ ? controls::ExposureTimeModeAuto
+ : controls::ExposureTimeModeManual);
+ metadata.set(controls::AnalogueGainMode,
+ frameContext.agc.autoGainEnabled
+ ? controls::AnalogueGainModeAuto
+ : controls::AnalogueGainModeManual);
+
+ metadata.set(controls::AeMeteringMode, frameContext.agc.meteringMode);
+ metadata.set(controls::AeExposureMode, frameContext.agc.exposureMode);
+ metadata.set(controls::AeConstraintMode, frameContext.agc.constraintMode);
}
/**
* \brief Estimate the relative luminance of the frame with a given gain
- * \param[in] ae The RkISP1 statistics and ISP results
* \param[in] gain The gain to apply to the frame
*
* This function estimates the average relative luminance of the frame that
@@ -332,8 +424,6 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
* YUV doesn't take into account the fact that the R, G and B components
* contribute differently to the relative luminance.
*
- * \todo Have a dedicated YUV algorithm ?
- *
* The values are normalized to the [0.0, 1.0] range, where 1.0 corresponds to a
* theoretical perfect reflector of 100% reference white.
*
@@ -342,46 +432,43 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
*
* \return The relative luminance
*/
-double Agc::estimateLuminance(const rkisp1_cif_isp_ae_stat *ae,
- double gain)
+double Agc::estimateLuminance(double gain) const
{
+ ASSERT(expMeans_.size() == weights_.size());
double ySum = 0.0;
+ double wSum = 0.0;
/* Sum the averages, saturated to 255. */
- for (unsigned int aeCell = 0; aeCell < numCells_; aeCell++)
- ySum += std::min(ae->exp_mean[aeCell] * gain, 255.0);
+ for (unsigned i = 0; i < expMeans_.size(); i++) {
+ double w = weights_[i];
+ ySum += std::min(expMeans_[i] * gain, 255.0) * w;
+ wSum += w;
+ }
/* \todo Weight with the AWB gains */
- return ySum / numCells_ / 255;
+ return ySum / wSum / 255;
}
/**
- * \brief Estimate the mean value of the top 2% of the histogram
- * \param[in] hist The histogram statistics computed by the ImgU
- * \return The mean value of the top 2% of the histogram
+ * \brief Process frame duration and compute vblank
+ * \param[in] context The shared IPA context
+ * \param[in] frameContext The current frame context
+ * \param[in] frameDuration The target frame duration
+ *
+ * Compute and populate vblank from the target frame duration.
*/
-double Agc::measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const
+void Agc::processFrameDuration(IPAContext &context,
+ IPAFrameContext &frameContext,
+ utils::Duration frameDuration)
{
- Histogram histogram{ Span<const uint32_t>(hist->hist_bins, numHistBins_) };
- /* Estimate the quantile mean of the top 2% of the histogram. */
- return histogram.interQuantileMean(0.98, 1.0);
-}
+ IPACameraSensorInfo &sensorInfo = context.sensorInfo;
+ utils::Duration lineDuration = context.configuration.sensor.lineDuration;
-void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
- ControlList &metadata)
-{
- utils::Duration exposureTime = context.configuration.sensor.lineDuration
- * frameContext.sensor.exposure;
- metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
- metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
+ frameContext.agc.vblank = (frameDuration / lineDuration) - sensorInfo.outputSize.height;
- /* \todo Use VBlank value calculated from each frame exposure. */
- uint32_t vTotal = context.configuration.sensor.size.height
- + context.configuration.sensor.defVBlank;
- utils::Duration frameDuration = context.configuration.sensor.lineDuration
- * vTotal;
- metadata.set(controls::FrameDuration, frameDuration.get<std::micro>());
+ /* Update frame duration accounting for line length quantization. */
+ frameContext.agc.frameDuration = (sensorInfo.outputSize.height + frameContext.agc.vblank) * lineDuration;
}
/**
@@ -400,10 +487,20 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
ControlList &metadata)
{
if (!stats) {
+ processFrameDuration(context, frameContext,
+ frameContext.agc.minFrameDuration);
+ fillMetadata(context, frameContext, metadata);
+ return;
+ }
+
+ if (!(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP)) {
fillMetadata(context, frameContext, metadata);
+ LOG(RkISP1Agc, Error) << "AUTOEXP data is missing in statistics";
return;
}
+ const utils::Duration &lineDuration = context.configuration.sensor.lineDuration;
+
/*
* \todo Verify that the exposure and gain applied by the sensor for
* this frame match what has been requested. This isn't a hard
@@ -413,40 +510,77 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
*/
const rkisp1_cif_isp_stat *params = &stats->params;
- ASSERT(stats->meas_type & RKISP1_CIF_ISP_STAT_AUTOEXP);
- const rkisp1_cif_isp_ae_stat *ae = &params->ae;
- const rkisp1_cif_isp_hist_stat *hist = &params->hist;
-
- double iqMean = measureBrightness(hist);
- double iqMeanGain = kEvGainTarget * numHistBins_ / iqMean;
+ /* The lower 4 bits are fractional and meant to be discarded. */
+ Histogram hist({ params->hist.hist_bins, context.hw->numHistogramBins },
+ [](uint32_t x) { return x >> 4; });
+ expMeans_ = { params->ae.exp_mean, context.hw->numAeCells };
+ std::vector<uint8_t> &modeWeights = meteringModes_.at(frameContext.agc.meteringMode);
+ weights_ = { modeWeights.data(), modeWeights.size() };
/*
- * Estimate the gain needed to achieve a relative luminance target. To
- * account for non-linearity caused by saturation, the value needs to be
- * estimated in an iterative process, as multiplying by a gain will not
- * increase the relative luminance by the same factor if some image
- * regions are saturated.
+ * Set the AGC limits using the fixed exposure time and/or gain in
+ * manual mode, or the sensor limits in auto mode.
*/
- double yGain = 1.0;
- double yTarget = kRelativeLuminanceTarget;
-
- for (unsigned int i = 0; i < 8; i++) {
- double yValue = estimateLuminance(ae, yGain);
- double extra_gain = std::min(10.0, yTarget / (yValue + .001));
-
- yGain *= extra_gain;
- LOG(RkISP1Agc, Debug) << "Y value: " << yValue
- << ", Y target: " << yTarget
- << ", gives gain " << yGain;
- if (extra_gain < 1.01)
- break;
+ utils::Duration minExposureTime;
+ utils::Duration maxExposureTime;
+ double minAnalogueGain;
+ double maxAnalogueGain;
+
+ if (frameContext.agc.autoExposureEnabled) {
+ minExposureTime = context.configuration.sensor.minExposureTime;
+ maxExposureTime = std::clamp(frameContext.agc.maxFrameDuration,
+ context.configuration.sensor.minExposureTime,
+ context.configuration.sensor.maxExposureTime);
+ } else {
+ minExposureTime = context.configuration.sensor.lineDuration
+ * frameContext.agc.exposure;
+ maxExposureTime = minExposureTime;
}
- computeExposure(context, frameContext, yGain, iqMeanGain);
- frameCount_++;
+ if (frameContext.agc.autoGainEnabled) {
+ minAnalogueGain = context.configuration.sensor.minAnalogueGain;
+ maxAnalogueGain = context.configuration.sensor.maxAnalogueGain;
+ } else {
+ minAnalogueGain = frameContext.agc.gain;
+ maxAnalogueGain = frameContext.agc.gain;
+ }
+
+ setLimits(minExposureTime, maxExposureTime, minAnalogueGain, maxAnalogueGain);
+
+ /*
+ * The Agc algorithm needs to know the effective exposure value that was
+ * applied to the sensor when the statistics were collected.
+ */
+ utils::Duration exposureTime = lineDuration * frameContext.sensor.exposure;
+ double analogueGain = frameContext.sensor.gain;
+ utils::Duration effectiveExposureValue = exposureTime * analogueGain;
+
+ utils::Duration newExposureTime;
+ double aGain, dGain;
+ std::tie(newExposureTime, aGain, dGain) =
+ calculateNewEv(frameContext.agc.constraintMode,
+ frameContext.agc.exposureMode,
+ hist, effectiveExposureValue);
+
+ LOG(RkISP1Agc, Debug)
+ << "Divided up exposure time, analogue gain and digital gain are "
+ << newExposureTime << ", " << aGain << " and " << dGain;
+
+ IPAActiveState &activeState = context.activeState;
+ /* Update the estimated exposure and gain. */
+ activeState.agc.automatic.exposure = newExposureTime / lineDuration;
+ activeState.agc.automatic.gain = aGain;
+
+ /*
+ * Expand the target frame duration so that we do not run faster than
+ * the minimum frame duration when we have short exposures.
+ */
+ processFrameDuration(context, frameContext,
+ std::max(frameContext.agc.minFrameDuration, newExposureTime));
fillMetadata(context, frameContext, metadata);
+ expMeans_ = {};
}
REGISTER_IPA_ALGORITHM(Agc, "Agc")
diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
index 8a222637..7867eed9 100644
--- a/src/ipa/rkisp1/algorithms/agc.h
+++ b/src/ipa/rkisp1/algorithms/agc.h
@@ -2,29 +2,33 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * agc.h - RkISP1 AGC/AEC mean-based control algorithm
+ * RkISP1 AGC/AEC mean-based control algorithm
*/
#pragma once
#include <linux/rkisp1-config.h>
+#include <libcamera/base/span.h>
#include <libcamera/base/utils.h>
#include <libcamera/geometry.h>
+#include "libipa/agc_mean_luminance.h"
+
#include "algorithm.h"
namespace libcamera {
namespace ipa::rkisp1::algorithms {
-class Agc : public Algorithm
+class Agc : public Algorithm, public AgcMeanLuminance
{
public:
Agc();
~Agc() = default;
+ int init(IPAContext &context, const YamlObject &tuningData) override;
int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
void queueRequest(IPAContext &context,
const uint32_t frame,
@@ -32,27 +36,28 @@ public:
const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
void process(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const rkisp1_stat_buffer *stats,
ControlList &metadata) override;
private:
- void computeExposure(IPAContext &Context, IPAFrameContext &frameContext,
- double yGain, double iqMeanGain);
- utils::Duration filterExposure(utils::Duration exposureValue);
- double estimateLuminance(const rkisp1_cif_isp_ae_stat *ae, double gain);
- double measureBrightness(const rkisp1_cif_isp_hist_stat *hist) const;
+ int parseMeteringModes(IPAContext &context, const YamlObject &tuningData);
+ uint8_t computeHistogramPredivider(const Size &size,
+ enum rkisp1_cif_isp_histogram_mode mode);
+
void fillMetadata(IPAContext &context, IPAFrameContext &frameContext,
ControlList &metadata);
+ double estimateLuminance(double gain) const override;
+ void processFrameDuration(IPAContext &context,
+ IPAFrameContext &frameContext,
+ utils::Duration frameDuration);
- uint64_t frameCount_;
-
- uint32_t numCells_;
- uint32_t numHistBins_;
+ Span<const uint8_t> expMeans_;
+ Span<const uint8_t> weights_;
- utils::Duration filteredExposure_;
+ std::map<int32_t, std::vector<uint8_t>> meteringModes_;
};
} /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/algorithm.h b/src/ipa/rkisp1/algorithms/algorithm.h
index 9454c9a1..715cfcd8 100644
--- a/src/ipa/rkisp1/algorithms/algorithm.h
+++ b/src/ipa/rkisp1/algorithms/algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas On Board
*
- * algorithm.h - RkISP1 control algorithm interface
+ * RkISP1 control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp
index 744f4a38..399fb51b 100644
--- a/src/ipa/rkisp1/algorithms/awb.cpp
+++ b/src/ipa/rkisp1/algorithms/awb.cpp
@@ -2,20 +2,24 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * awb.cpp - AWB control algorithm
+ * AWB control algorithm
*/
#include "awb.h"
#include <algorithm>
-#include <cmath>
-#include <iomanip>
+#include <ios>
#include <libcamera/base/log.h>
#include <libcamera/control_ids.h>
+
#include <libcamera/ipa/core_ipa_interface.h>
+#include "libipa/awb_bayes.h"
+#include "libipa/awb_grey.h"
+#include "libipa/colours.h"
+
/**
* \file awb.h
*/
@@ -31,27 +35,107 @@ namespace ipa::rkisp1::algorithms {
LOG_DEFINE_CATEGORY(RkISP1Awb)
+constexpr int32_t kMinColourTemperature = 2500;
+constexpr int32_t kMaxColourTemperature = 10000;
+constexpr int32_t kDefaultColourTemperature = 5000;
+
/* Minimum mean value below which AWB can't operate. */
constexpr double kMeanMinThreshold = 2.0;
+class RkISP1AwbStats final : public AwbStats
+{
+public:
+ RkISP1AwbStats(const RGB<double> &rgbMeans)
+ : rgbMeans_(rgbMeans)
+ {
+ rg_ = rgbMeans_.r() / rgbMeans_.g();
+ bg_ = rgbMeans_.b() / rgbMeans_.g();
+ }
+
+ double computeColourError(const RGB<double> &gains) const override
+ {
+ /*
+ * Compute the sum of the squared colour error (non-greyness) as
+ * it appears in the log likelihood equation.
+ */
+ double deltaR = gains.r() * rg_ - 1.0;
+ double deltaB = gains.b() * bg_ - 1.0;
+ double delta2 = deltaR * deltaR + deltaB * deltaB;
+
+ return delta2;
+ }
+
+ RGB<double> rgbMeans() const override
+ {
+ return rgbMeans_;
+ }
+
+private:
+ RGB<double> rgbMeans_;
+ double rg_;
+ double bg_;
+};
+
Awb::Awb()
: rgbMode_(false)
{
}
/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Awb::init(IPAContext &context, const YamlObject &tuningData)
+{
+ auto &cmap = context.ctrlMap;
+ cmap[&controls::ColourTemperature] = ControlInfo(kMinColourTemperature,
+ kMaxColourTemperature,
+ kDefaultColourTemperature);
+ cmap[&controls::AwbEnable] = ControlInfo(false, true);
+ cmap[&controls::ColourGains] = ControlInfo(0.0f, 3.996f, 1.0f);
+
+ if (!tuningData.contains("algorithm"))
+ LOG(RkISP1Awb, Info) << "No AWB algorithm specified."
+ << " Default to grey world";
+
+ auto mode = tuningData["algorithm"].get<std::string>("grey");
+ if (mode == "grey") {
+ awbAlgo_ = std::make_unique<AwbGrey>();
+ } else if (mode == "bayes") {
+ awbAlgo_ = std::make_unique<AwbBayes>();
+ } else {
+ LOG(RkISP1Awb, Error) << "Unknown AWB algorithm: " << mode;
+ return -EINVAL;
+ }
+ LOG(RkISP1Awb, Debug) << "Using AWB algorithm: " << mode;
+
+ int ret = awbAlgo_->init(tuningData);
+ if (ret) {
+ LOG(RkISP1Awb, Error) << "Failed to init AWB algorithm";
+ return ret;
+ }
+
+ const auto &src = awbAlgo_->controls();
+ cmap.insert(src.begin(), src.end());
+
+ return 0;
+}
+
+/**
* \copydoc libcamera::ipa::Algorithm::configure
*/
int Awb::configure(IPAContext &context,
const IPACameraSensorInfo &configInfo)
{
- context.activeState.awb.gains.manual.red = 1.0;
- context.activeState.awb.gains.manual.blue = 1.0;
- context.activeState.awb.gains.manual.green = 1.0;
- context.activeState.awb.gains.automatic.red = 1.0;
- context.activeState.awb.gains.automatic.blue = 1.0;
- context.activeState.awb.gains.automatic.green = 1.0;
+ context.activeState.awb.manual.gains = RGB<double>{ 1.0 };
+ auto gains = awbAlgo_->gainsFromColourTemperature(kDefaultColourTemperature);
+ if (gains)
+ context.activeState.awb.automatic.gains = *gains;
+ else
+ context.activeState.awb.automatic.gains = RGB<double>{ 1.0 };
+
context.activeState.awb.autoEnabled = true;
+ context.activeState.awb.manual.temperatureK = kDefaultColourTemperature;
+ context.activeState.awb.automatic.temperatureK = kDefaultColourTemperature;
/*
* Define the measurement window for AWB as a centered rectangle
@@ -85,64 +169,83 @@ void Awb::queueRequest(IPAContext &context,
<< (*awbEnable ? "Enabling" : "Disabling") << " AWB";
}
- const auto &colourGains = controls.get(controls::ColourGains);
- if (colourGains && !awb.autoEnabled) {
- awb.gains.manual.red = (*colourGains)[0];
- awb.gains.manual.blue = (*colourGains)[1];
-
- LOG(RkISP1Awb, Debug)
- << "Set colour gains to red: " << awb.gains.manual.red
- << ", blue: " << awb.gains.manual.blue;
- }
+ awbAlgo_->handleControls(controls);
frameContext.awb.autoEnabled = awb.autoEnabled;
- if (!awb.autoEnabled) {
- frameContext.awb.gains.red = awb.gains.manual.red;
- frameContext.awb.gains.green = 1.0;
- frameContext.awb.gains.blue = awb.gains.manual.blue;
+ if (awb.autoEnabled)
+ return;
+
+ const auto &colourGains = controls.get(controls::ColourGains);
+ const auto &colourTemperature = controls.get(controls::ColourTemperature);
+ bool update = false;
+ if (colourGains) {
+ awb.manual.gains.r() = (*colourGains)[0];
+ awb.manual.gains.b() = (*colourGains)[1];
+ /*
+ * \todo Colour temperature reported in metadata is now
+ * incorrect, as we can't deduce the temperature from the gains.
+ * This will be fixed with the bayes AWB algorithm.
+ */
+ update = true;
+ } else if (colourTemperature) {
+ awb.manual.temperatureK = *colourTemperature;
+ const auto &gains = awbAlgo_->gainsFromColourTemperature(*colourTemperature);
+ if (gains) {
+ awb.manual.gains.r() = gains->r();
+ awb.manual.gains.b() = gains->b();
+ update = true;
+ }
}
+
+ if (update)
+ LOG(RkISP1Awb, Debug)
+ << "Set colour gains to " << awb.manual.gains;
+
+ frameContext.awb.gains = awb.manual.gains;
+ frameContext.awb.temperatureK = awb.manual.temperatureK;
}
/**
* \copydoc libcamera::ipa::Algorithm::prepare
*/
void Awb::prepare(IPAContext &context, const uint32_t frame,
- IPAFrameContext &frameContext, rkisp1_params_cfg *params)
+ IPAFrameContext &frameContext, RkISP1Params *params)
{
/*
* This is the latest time we can read the active state. This is the
* most up-to-date automatic values we can read.
*/
if (frameContext.awb.autoEnabled) {
- frameContext.awb.gains.red = context.activeState.awb.gains.automatic.red;
- frameContext.awb.gains.green = context.activeState.awb.gains.automatic.green;
- frameContext.awb.gains.blue = context.activeState.awb.gains.automatic.blue;
+ const auto &awb = context.activeState.awb;
+ frameContext.awb.gains = awb.automatic.gains;
+ frameContext.awb.temperatureK = awb.automatic.temperatureK;
}
- params->others.awb_gain_config.gain_green_b = 256 * frameContext.awb.gains.green;
- params->others.awb_gain_config.gain_blue = 256 * frameContext.awb.gains.blue;
- params->others.awb_gain_config.gain_red = 256 * frameContext.awb.gains.red;
- params->others.awb_gain_config.gain_green_r = 256 * frameContext.awb.gains.green;
+ auto gainConfig = params->block<BlockType::AwbGain>();
+ gainConfig.setEnabled(true);
- /* Update the gains. */
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_AWB_GAIN;
+ gainConfig->gain_green_b = std::clamp<int>(256 * frameContext.awb.gains.g(), 0, 0x3ff);
+ gainConfig->gain_blue = std::clamp<int>(256 * frameContext.awb.gains.b(), 0, 0x3ff);
+ gainConfig->gain_red = std::clamp<int>(256 * frameContext.awb.gains.r(), 0, 0x3ff);
+ gainConfig->gain_green_r = std::clamp<int>(256 * frameContext.awb.gains.g(), 0, 0x3ff);
/* If we have already set the AWB measurement parameters, return. */
if (frame > 0)
return;
- rkisp1_cif_isp_awb_meas_config &awb_config = params->meas.awb_meas_config;
+ auto awbConfig = params->block<BlockType::Awb>();
+ awbConfig.setEnabled(true);
/* Configure the measure window for AWB. */
- awb_config.awb_wnd = context.configuration.awb.measureWindow;
+ awbConfig->awb_wnd = context.configuration.awb.measureWindow;
/* Number of frames to use to estimate the means (0 means 1 frame). */
- awb_config.frames = 0;
+ awbConfig->frames = 0;
/* Select RGB or YCbCr means measurement. */
if (rgbMode_) {
- awb_config.awb_mode = RKISP1_CIF_ISP_AWB_MODE_RGB;
+ awbConfig->awb_mode = RKISP1_CIF_ISP_AWB_MODE_RGB;
/*
* For RGB-based measurements, pixels are selected with maximum
@@ -150,19 +253,19 @@ void Awb::prepare(IPAContext &context, const uint32_t frame,
* awb_ref_cr, awb_min_y and awb_ref_cb respectively. The other
* values are not used, set them to 0.
*/
- awb_config.awb_ref_cr = 250;
- awb_config.min_y = 250;
- awb_config.awb_ref_cb = 250;
+ awbConfig->awb_ref_cr = 250;
+ awbConfig->min_y = 250;
+ awbConfig->awb_ref_cb = 250;
- awb_config.max_y = 0;
- awb_config.min_c = 0;
- awb_config.max_csum = 0;
+ awbConfig->max_y = 0;
+ awbConfig->min_c = 0;
+ awbConfig->max_csum = 0;
} else {
- awb_config.awb_mode = RKISP1_CIF_ISP_AWB_MODE_YCBCR;
+ awbConfig->awb_mode = RKISP1_CIF_ISP_AWB_MODE_YCBCR;
/* Set the reference Cr and Cb (AWB target) to white. */
- awb_config.awb_ref_cb = 128;
- awb_config.awb_ref_cr = 128;
+ awbConfig->awb_ref_cb = 128;
+ awbConfig->awb_ref_cr = 128;
/*
* Filter out pixels based on luminance and chrominance values.
@@ -170,36 +273,11 @@ void Awb::prepare(IPAContext &context, const uint32_t frame,
* range, while the acceptable chroma values are specified with
* a minimum of 16 and a maximum Cb+Cr sum of 250.
*/
- awb_config.min_y = 16;
- awb_config.max_y = 250;
- awb_config.min_c = 16;
- awb_config.max_csum = 250;
+ awbConfig->min_y = 16;
+ awbConfig->max_y = 250;
+ awbConfig->min_c = 16;
+ awbConfig->max_csum = 250;
}
-
- /* Enable the AWB gains. */
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_AWB_GAIN;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_AWB_GAIN;
-
- /* Update the AWB measurement parameters and enable the AWB module. */
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_AWB;
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_AWB;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_AWB;
-}
-
-uint32_t Awb::estimateCCT(double red, double green, double blue)
-{
- /* Convert the RGB values to CIE tristimulus values (XYZ) */
- double X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue);
- double Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue);
- double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue);
-
- /* Calculate the normalized chromaticity values */
- double x = X / (X + Y + Z);
- double y = Y / (X + Y + Z);
-
- /* Calculate CCT */
- double n = (x - 0.3320) / (0.1858 - y);
- return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33;
}
/**
@@ -211,41 +289,111 @@ void Awb::process(IPAContext &context,
const rkisp1_stat_buffer *stats,
ControlList &metadata)
{
+ IPAActiveState &activeState = context.activeState;
+
+ metadata.set(controls::AwbEnable, frameContext.awb.autoEnabled);
+ metadata.set(controls::ColourGains, {
+ static_cast<float>(frameContext.awb.gains.r()),
+ static_cast<float>(frameContext.awb.gains.b())
+ });
+ metadata.set(controls::ColourTemperature, frameContext.awb.temperatureK);
+
+ if (!stats || !(stats->meas_type & RKISP1_CIF_ISP_STAT_AWB)) {
+ LOG(RkISP1Awb, Error) << "AWB data is missing in statistics";
+ return;
+ }
+
const rkisp1_cif_isp_stat *params = &stats->params;
const rkisp1_cif_isp_awb_stat *awb = &params->awb;
- IPAActiveState &activeState = context.activeState;
- double greenMean;
- double redMean;
- double blueMean;
+
+ if (awb->awb_mean[0].cnt == 0) {
+ LOG(RkISP1Awb, Debug) << "AWB statistics are empty";
+ return;
+ }
+
+ RGB<double> rgbMeans = calculateRgbMeans(frameContext, awb);
+
+ /*
+ * If the means are too small we don't have enough information to
+ * meaningfully calculate gains. Freeze the algorithm in that case.
+ */
+ if (rgbMeans.r() < kMeanMinThreshold && rgbMeans.g() < kMeanMinThreshold &&
+ rgbMeans.b() < kMeanMinThreshold)
+ return;
+
+ RkISP1AwbStats awbStats{ rgbMeans };
+ AwbResult awbResult = awbAlgo_->calculateAwb(awbStats, frameContext.lux.lux);
+
+ /*
+ * Clamp the gain values to the hardware, which expresses gains as Q2.8
+ * unsigned integer values. Set the minimum just above zero to avoid
+ * divisions by zero when computing the raw means in subsequent
+ * iterations.
+ */
+ awbResult.gains = awbResult.gains.max(1.0 / 256).min(1023.0 / 256);
+
+ /* Filter the values to avoid oscillations. */
+ double speed = 0.2;
+ double ct = awbResult.colourTemperature;
+ ct = ct * speed + activeState.awb.automatic.temperatureK * (1 - speed);
+ awbResult.gains = awbResult.gains * speed +
+ activeState.awb.automatic.gains * (1 - speed);
+
+ activeState.awb.automatic.temperatureK = static_cast<unsigned int>(ct);
+ activeState.awb.automatic.gains = awbResult.gains;
+
+ LOG(RkISP1Awb, Debug)
+ << std::showpoint
+ << "Means " << rgbMeans << ", gains "
+ << activeState.awb.automatic.gains << ", temp "
+ << activeState.awb.automatic.temperatureK << "K";
+}
+
+RGB<double> Awb::calculateRgbMeans(const IPAFrameContext &frameContext, const rkisp1_cif_isp_awb_stat *awb) const
+{
+ Vector<double, 3> rgbMeans;
if (rgbMode_) {
- greenMean = awb->awb_mean[0].mean_y_or_g;
- redMean = awb->awb_mean[0].mean_cr_or_r;
- blueMean = awb->awb_mean[0].mean_cb_or_b;
+ rgbMeans = {{
+ static_cast<double>(awb->awb_mean[0].mean_cr_or_r),
+ static_cast<double>(awb->awb_mean[0].mean_y_or_g),
+ static_cast<double>(awb->awb_mean[0].mean_cb_or_b)
+ }};
} else {
/* Get the YCbCr mean values */
- double yMean = awb->awb_mean[0].mean_y_or_g;
- double cbMean = awb->awb_mean[0].mean_cb_or_b;
- double crMean = awb->awb_mean[0].mean_cr_or_r;
+ Vector<double, 3> yuvMeans({
+ static_cast<double>(awb->awb_mean[0].mean_y_or_g),
+ static_cast<double>(awb->awb_mean[0].mean_cb_or_b),
+ static_cast<double>(awb->awb_mean[0].mean_cr_or_r)
+ });
/*
- * Convert from YCbCr to RGB.
- * The hardware uses the following formulas:
- * Y = 16 + 0.2500 R + 0.5000 G + 0.1094 B
+ * Convert from YCbCr to RGB. The hardware uses the following
+ * formulas:
+ *
+ * Y = 16 + 0.2500 R + 0.5000 G + 0.1094 B
* Cb = 128 - 0.1406 R - 0.2969 G + 0.4375 B
* Cr = 128 + 0.4375 R - 0.3750 G - 0.0625 B
*
- * The inverse matrix is thus:
+ * This seems to be based on limited range BT.601 with Q1.6
+ * precision.
+ *
+ * The inverse matrix is:
+ *
* [[1,1636, -0,0623, 1,6008]
* [1,1636, -0,4045, -0,7949]
* [1,1636, 1,9912, -0,0250]]
*/
- yMean -= 16;
- cbMean -= 128;
- crMean -= 128;
- redMean = 1.1636 * yMean - 0.0623 * cbMean + 1.6008 * crMean;
- greenMean = 1.1636 * yMean - 0.4045 * cbMean - 0.7949 * crMean;
- blueMean = 1.1636 * yMean + 1.9912 * cbMean - 0.0250 * crMean;
+ static const Matrix<double, 3, 3> yuv2rgbMatrix({
+ 1.1636, -0.0623, 1.6008,
+ 1.1636, -0.4045, -0.7949,
+ 1.1636, 1.9912, -0.0250
+ });
+ static const Vector<double, 3> yuv2rgbOffset({
+ 16, 128, 128
+ });
+
+ rgbMeans = yuv2rgbMatrix * (yuvMeans - yuv2rgbOffset);
/*
* Due to hardware rounding errors in the YCbCr means, the
@@ -253,73 +401,23 @@ void Awb::process(IPAContext &context,
* negative gains, messing up calculation. Prevent this by
* clamping the means to positive values.
*/
- redMean = std::max(redMean, 0.0);
- greenMean = std::max(greenMean, 0.0);
- blueMean = std::max(blueMean, 0.0);
+ rgbMeans = rgbMeans.max(0.0);
}
/*
- * The ISP computes the AWB means after applying the colour gains,
- * divide by the gains that were used to get the raw means from the
- * sensor.
- */
- redMean /= frameContext.awb.gains.red;
- greenMean /= frameContext.awb.gains.green;
- blueMean /= frameContext.awb.gains.blue;
-
- /*
- * If the means are too small we don't have enough information to
- * meaningfully calculate gains. Freeze the algorithm in that case.
- */
- if (redMean < kMeanMinThreshold && greenMean < kMeanMinThreshold &&
- blueMean < kMeanMinThreshold) {
- frameContext.awb.temperatureK = activeState.awb.temperatureK;
- return;
- }
-
- activeState.awb.temperatureK = estimateCCT(redMean, greenMean, blueMean);
-
- /*
- * Estimate the red and blue gains to apply in a grey world. The green
- * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the
- * divisor to a minimum value of 1.0.
+ * The ISP computes the AWB means after applying the CCM. Apply the
+ * inverse as we want to get the raw means before the colour gains.
*/
- double redGain = greenMean / std::max(redMean, 1.0);
- double blueGain = greenMean / std::max(blueMean, 1.0);
+ rgbMeans = frameContext.ccm.ccm.inverse() * rgbMeans;
/*
- * Clamp the gain values to the hardware, which expresses gains as Q2.8
- * unsigned integer values. Set the minimum just above zero to avoid
- * divisions by zero when computing the raw means in subsequent
- * iterations.
+ * The ISP computes the AWB means after applying the colour gains,
+ * divide by the gains that were used to get the raw means from the
+ * sensor. Apply a minimum value to avoid divisions by near-zero.
*/
- redGain = std::clamp(redGain, 1.0 / 256, 1023.0 / 256);
- blueGain = std::clamp(blueGain, 1.0 / 256, 1023.0 / 256);
-
- /* Filter the values to avoid oscillations. */
- double speed = 0.2;
- redGain = speed * redGain + (1 - speed) * activeState.awb.gains.automatic.red;
- blueGain = speed * blueGain + (1 - speed) * activeState.awb.gains.automatic.blue;
-
- activeState.awb.gains.automatic.red = redGain;
- activeState.awb.gains.automatic.blue = blueGain;
- activeState.awb.gains.automatic.green = 1.0;
-
- frameContext.awb.temperatureK = activeState.awb.temperatureK;
-
- metadata.set(controls::AwbEnable, frameContext.awb.autoEnabled);
- metadata.set(controls::ColourGains, {
- static_cast<float>(frameContext.awb.gains.red),
- static_cast<float>(frameContext.awb.gains.blue)
- });
- metadata.set(controls::ColourTemperature, frameContext.awb.temperatureK);
+ rgbMeans /= frameContext.awb.gains.max(0.01);
- LOG(RkISP1Awb, Debug) << std::showpoint
- << "Means [" << redMean << ", " << greenMean << ", " << blueMean
- << "], gains [" << activeState.awb.gains.automatic.red << ", "
- << activeState.awb.gains.automatic.green << ", "
- << activeState.awb.gains.automatic.blue << "], temp "
- << frameContext.awb.temperatureK << "K";
+ return rgbMeans;
}
REGISTER_IPA_ALGORITHM(Awb, "Awb")
diff --git a/src/ipa/rkisp1/algorithms/awb.h b/src/ipa/rkisp1/algorithms/awb.h
index 9d45a442..02651cc7 100644
--- a/src/ipa/rkisp1/algorithms/awb.h
+++ b/src/ipa/rkisp1/algorithms/awb.h
@@ -2,11 +2,16 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * awb.h - AWB control algorithm
+ * AWB control algorithm
*/
#pragma once
+#include "libcamera/internal/vector.h"
+
+#include "libipa/awb.h"
+#include "libipa/interpolator.h"
+
#include "algorithm.h"
namespace libcamera {
@@ -19,20 +24,24 @@ public:
Awb();
~Awb() = default;
+ int init(IPAContext &context, const YamlObject &tuningData) override;
int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
void queueRequest(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
void process(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const rkisp1_stat_buffer *stats,
ControlList &metadata) override;
private:
- uint32_t estimateCCT(double red, double green, double blue);
+ RGB<double> calculateRgbMeans(const IPAFrameContext &frameContext,
+ const rkisp1_cif_isp_awb_stat *awb) const;
+
+ std::unique_ptr<AwbAlgorithm> awbAlgo_;
bool rgbMode_;
};
diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp
index 15324fb1..98cb7145 100644
--- a/src/ipa/rkisp1/algorithms/blc.cpp
+++ b/src/ipa/rkisp1/algorithms/blc.cpp
@@ -2,13 +2,17 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * blc.cpp - RkISP1 Black Level Correction control
+ * RkISP1 Black Level Correction control
*/
#include "blc.h"
+#include <linux/videodev2.h>
+
#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
#include "libcamera/internal/yaml_parser.h"
/**
@@ -36,22 +40,62 @@ namespace ipa::rkisp1::algorithms {
LOG_DEFINE_CATEGORY(RkISP1Blc)
BlackLevelCorrection::BlackLevelCorrection()
- : tuningParameters_(false)
{
+ /*
+ * This is a bit of a hack. In raw mode no black level correction
+ * happens. This flag is used to ensure the metadata gets populated with
+ * the black level which is needed to capture proper raw images for
+ * tuning.
+ */
+ supportsRaw_ = true;
}
/**
* \copydoc libcamera::ipa::Algorithm::init
*/
-int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context,
- const YamlObject &tuningData)
+int BlackLevelCorrection::init(IPAContext &context, const YamlObject &tuningData)
{
- blackLevelRed_ = tuningData["R"].get<int16_t>(256);
- blackLevelGreenR_ = tuningData["Gr"].get<int16_t>(256);
- blackLevelGreenB_ = tuningData["Gb"].get<int16_t>(256);
- blackLevelBlue_ = tuningData["B"].get<int16_t>(256);
-
- tuningParameters_ = true;
+ std::optional<int16_t> levelRed = tuningData["R"].get<int16_t>();
+ std::optional<int16_t> levelGreenR = tuningData["Gr"].get<int16_t>();
+ std::optional<int16_t> levelGreenB = tuningData["Gb"].get<int16_t>();
+ std::optional<int16_t> levelBlue = tuningData["B"].get<int16_t>();
+ bool tuningHasLevels = levelRed && levelGreenR && levelGreenB && levelBlue;
+
+ auto blackLevel = context.camHelper->blackLevel();
+ if (!blackLevel) {
+ /*
+ * Not all camera sensor helpers have been updated with black
+ * levels. Print a warning and fall back to the levels from the
+ * tuning data to preserve backward compatibility. This should
+ * be removed once all helpers provide the data.
+ */
+ LOG(RkISP1Blc, Warning)
+ << "No black levels provided by camera sensor helper"
+ << ", please fix";
+
+ blackLevelRed_ = levelRed.value_or(4096);
+ blackLevelGreenR_ = levelGreenR.value_or(4096);
+ blackLevelGreenB_ = levelGreenB.value_or(4096);
+ blackLevelBlue_ = levelBlue.value_or(4096);
+ } else if (tuningHasLevels) {
+ /*
+ * If black levels are provided in the tuning file, use them to
+ * avoid breaking existing camera tuning. This is deprecated and
+ * will be removed.
+ */
+ LOG(RkISP1Blc, Warning)
+ << "Deprecated: black levels overwritten by tuning file";
+
+ blackLevelRed_ = *levelRed;
+ blackLevelGreenR_ = *levelGreenR;
+ blackLevelGreenB_ = *levelGreenB;
+ blackLevelBlue_ = *levelBlue;
+ } else {
+ blackLevelRed_ = *blackLevel;
+ blackLevelGreenR_ = *blackLevel;
+ blackLevelGreenB_ = *blackLevel;
+ blackLevelBlue_ = *blackLevel;
+ }
LOG(RkISP1Blc, Debug)
<< "Black levels: red " << blackLevelRed_
@@ -62,29 +106,80 @@ int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context,
return 0;
}
+int BlackLevelCorrection::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ /*
+ * BLC on ISP versions that include the companding block requires usage
+ * of the extensible parameters format.
+ */
+ supported_ = context.configuration.paramFormat == V4L2_META_FMT_RK_ISP1_EXT_PARAMS ||
+ !context.hw->compand;
+
+ if (!supported_)
+ LOG(RkISP1Blc, Warning)
+ << "BLC in companding block requires extensible parameters";
+
+ return 0;
+}
+
/**
* \copydoc libcamera::ipa::Algorithm::prepare
*/
-void BlackLevelCorrection::prepare([[maybe_unused]] IPAContext &context,
+void BlackLevelCorrection::prepare(IPAContext &context,
const uint32_t frame,
[[maybe_unused]] IPAFrameContext &frameContext,
- rkisp1_params_cfg *params)
+ RkISP1Params *params)
{
+ if (context.configuration.raw)
+ return;
+
if (frame > 0)
return;
- if (!tuningParameters_)
+ if (!supported_)
return;
- params->others.bls_config.enable_auto = 0;
- params->others.bls_config.fixed_val.r = blackLevelRed_;
- params->others.bls_config.fixed_val.gr = blackLevelGreenR_;
- params->others.bls_config.fixed_val.gb = blackLevelGreenB_;
- params->others.bls_config.fixed_val.b = blackLevelBlue_;
+ if (context.hw->compand) {
+ auto config = params->block<BlockType::CompandBls>();
+ config.setEnabled(true);
+
+ /*
+ * Scale up to the 20-bit black levels used by the companding
+ * block.
+ */
+ config->r = blackLevelRed_ << 4;
+ config->gr = blackLevelGreenR_ << 4;
+ config->gb = blackLevelGreenB_ << 4;
+ config->b = blackLevelBlue_ << 4;
+ } else {
+ auto config = params->block<BlockType::Bls>();
+ config.setEnabled(true);
+
+ config->enable_auto = 0;
+
+ /* Scale down to the 12-bit black levels used by the BLS block. */
+ config->fixed_val.r = blackLevelRed_ >> 4;
+ config->fixed_val.gr = blackLevelGreenR_ >> 4;
+ config->fixed_val.gb = blackLevelGreenB_ >> 4;
+ config->fixed_val.b = blackLevelBlue_ >> 4;
+ }
+}
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_BLS;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_BLS;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_BLS;
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void BlackLevelCorrection::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ [[maybe_unused]] const rkisp1_stat_buffer *stats,
+ ControlList &metadata)
+{
+ metadata.set(controls::SensorBlackLevels,
+ { static_cast<int32_t>(blackLevelRed_),
+ static_cast<int32_t>(blackLevelGreenR_),
+ static_cast<int32_t>(blackLevelGreenB_),
+ static_cast<int32_t>(blackLevelBlue_) });
}
REGISTER_IPA_ALGORITHM(BlackLevelCorrection, "BlackLevelCorrection")
diff --git a/src/ipa/rkisp1/algorithms/blc.h b/src/ipa/rkisp1/algorithms/blc.h
index 0b1a2d43..f797ae44 100644
--- a/src/ipa/rkisp1/algorithms/blc.h
+++ b/src/ipa/rkisp1/algorithms/blc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * blc.h - RkISP1 Black Level Correction control
+ * RkISP1 Black Level Correction control
*/
#pragma once
@@ -20,12 +20,19 @@ public:
~BlackLevelCorrection() = default;
int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const rkisp1_stat_buffer *stats,
+ ControlList &metadata) override;
private:
- bool tuningParameters_;
+ bool supported_;
+
int16_t blackLevelRed_;
int16_t blackLevelGreenR_;
int16_t blackLevelGreenB_;
diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp
new file mode 100644
index 00000000..de2b6fe7
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/ccm.cpp
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 Color Correction Matrix control algorithm
+ */
+
+#include "ccm.h"
+
+#include <map>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+#include "libipa/fixedpoint.h"
+#include "libipa/interpolator.h"
+
+/**
+ * \file ccm.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class Ccm
+ * \brief A color correction matrix algorithm
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Ccm)
+
+constexpr Matrix<float, 3, 3> kIdentity3x3 = Matrix<float, 3, 3>::identity();
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
+{
+ auto &cmap = context.ctrlMap;
+ cmap[&controls::ColourCorrectionMatrix] = ControlInfo(
+ ControlValue(-8.0f),
+ ControlValue(7.993f),
+ ControlValue(kIdentity3x3.data()));
+
+ int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
+ if (ret < 0) {
+ LOG(RkISP1Ccm, Warning)
+ << "Failed to parse 'ccm' "
+ << "parameter from tuning file; falling back to unit matrix";
+ ccm_.setData({ { 0, kIdentity3x3 } });
+ }
+
+ ret = offsets_.readYaml(tuningData["ccms"], "ct", "offsets");
+ if (ret < 0) {
+ LOG(RkISP1Ccm, Warning)
+ << "Failed to parse 'offsets' "
+ << "parameter from tuning file; falling back to zero offsets";
+
+ offsets_.setData({ { 0, Matrix<int16_t, 3, 1>({ 0, 0, 0 }) } });
+ }
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int Ccm::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ auto &as = context.activeState;
+ as.ccm.manual = kIdentity3x3;
+ as.ccm.automatic = ccm_.getInterpolated(as.awb.automatic.temperatureK);
+ return 0;
+}
+
+void Ccm::queueRequest(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls)
+{
+ /* Nothing to do here, the ccm will be calculated in prepare() */
+ if (frameContext.awb.autoEnabled)
+ return;
+
+ auto &ccm = context.activeState.ccm;
+
+ const auto &colourTemperature = controls.get(controls::ColourTemperature);
+ const auto &ccmMatrix = controls.get(controls::ColourCorrectionMatrix);
+ if (ccmMatrix) {
+ ccm.manual = Matrix<float, 3, 3>(*ccmMatrix);
+ LOG(RkISP1Ccm, Debug)
+ << "Setting manual CCM from CCM control to " << ccm.manual;
+ } else if (colourTemperature) {
+ ccm.manual = ccm_.getInterpolated(*colourTemperature);
+ LOG(RkISP1Ccm, Debug)
+ << "Setting manual CCM from CT control to " << ccm.manual;
+ }
+
+ frameContext.ccm.ccm = ccm.manual;
+}
+
+void Ccm::setParameters(struct rkisp1_cif_isp_ctk_config &config,
+ const Matrix<float, 3, 3> &matrix,
+ const Matrix<int16_t, 3, 1> &offsets)
+{
+ /*
+ * 4 bit integer and 7 bit fractional, ranging from -8 (0x400) to
+ * +7.9921875 (0x3ff)
+ */
+ for (unsigned int i = 0; i < 3; i++) {
+ for (unsigned int j = 0; j < 3; j++)
+ config.coeff[i][j] =
+ floatingToFixedPoint<4, 7, uint16_t, double>(matrix[i][j]);
+ }
+
+ for (unsigned int i = 0; i < 3; i++)
+ config.ct_offset[i] = offsets[i][0] & 0xfff;
+
+ LOG(RkISP1Ccm, Debug) << "Setting matrix " << matrix;
+ LOG(RkISP1Ccm, Debug) << "Setting offsets " << offsets;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Ccm::prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, RkISP1Params *params)
+{
+ if (!frameContext.awb.autoEnabled) {
+ auto config = params->block<BlockType::Ctk>();
+ config.setEnabled(true);
+ setParameters(*config, frameContext.ccm.ccm, Matrix<int16_t, 3, 1>());
+ return;
+ }
+
+ uint32_t ct = frameContext.awb.temperatureK;
+ if (frame > 0 && ct == ct_) {
+ frameContext.ccm.ccm = context.activeState.ccm.automatic;
+ return;
+ }
+
+ ct_ = ct;
+ Matrix<float, 3, 3> ccm = ccm_.getInterpolated(ct);
+ Matrix<int16_t, 3, 1> offsets = offsets_.getInterpolated(ct);
+
+ context.activeState.ccm.automatic = ccm;
+ frameContext.ccm.ccm = ccm;
+
+ auto config = params->block<BlockType::Ctk>();
+ config.setEnabled(true);
+ setParameters(*config, ccm, offsets);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Ccm::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] const rkisp1_stat_buffer *stats,
+ ControlList &metadata)
+{
+ metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data());
+}
+
+REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/ccm.h b/src/ipa/rkisp1/algorithms/ccm.h
new file mode 100644
index 00000000..c301e6e5
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/ccm.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 Color Correction Matrix control algorithm
+ */
+
+#pragma once
+
+#include <linux/rkisp1-config.h>
+
+#include "libcamera/internal/matrix.h"
+
+#include "libipa/interpolator.h"
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+class Ccm : public Algorithm
+{
+public:
+ Ccm() {}
+ ~Ccm() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void queueRequest(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ RkISP1Params *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const rkisp1_stat_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ void parseYaml(const YamlObject &tuningData);
+ void setParameters(struct rkisp1_cif_isp_ctk_config &config,
+ const Matrix<float, 3, 3> &matrix,
+ const Matrix<int16_t, 3, 1> &offsets);
+
+ unsigned int ct_;
+ Interpolator<Matrix<float, 3, 3>> ccm_;
+ Interpolator<Matrix<int16_t, 3, 1>> offsets_;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp
index eaa56c37..d1fff699 100644
--- a/src/ipa/rkisp1/algorithms/cproc.cpp
+++ b/src/ipa/rkisp1/algorithms/cproc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * cproc.cpp - RkISP1 Color Processing control
+ * RkISP1 Color Processing control
*/
#include "cproc.h"
@@ -33,20 +33,71 @@ namespace ipa::rkisp1::algorithms {
LOG_DEFINE_CATEGORY(RkISP1CProc)
+namespace {
+
+constexpr float kDefaultBrightness = 0.0f;
+constexpr float kDefaultContrast = 1.0f;
+constexpr float kDefaultSaturation = 1.0f;
+
+int convertBrightness(const float v)
+{
+ return std::clamp<int>(std::lround(v * 128), -128, 127);
+}
+
+int convertContrastOrSaturation(const float v)
+{
+ return std::clamp<int>(std::lround(v * 128), 0, 255);
+}
+
+} /* namespace */
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int ColorProcessing::init(IPAContext &context,
+ [[maybe_unused]] const YamlObject &tuningData)
+{
+ auto &cmap = context.ctrlMap;
+
+ cmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness);
+ cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast);
+ cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation);
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int ColorProcessing::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ auto &cproc = context.activeState.cproc;
+
+ cproc.brightness = convertBrightness(kDefaultBrightness);
+ cproc.contrast = convertContrastOrSaturation(kDefaultContrast);
+ cproc.saturation = convertContrastOrSaturation(kDefaultSaturation);
+
+ return 0;
+}
+
/**
* \copydoc libcamera::ipa::Algorithm::queueRequest
*/
void ColorProcessing::queueRequest(IPAContext &context,
- [[maybe_unused]] const uint32_t frame,
+ const uint32_t frame,
IPAFrameContext &frameContext,
const ControlList &controls)
{
auto &cproc = context.activeState.cproc;
bool update = false;
+ if (frame == 0)
+ update = true;
+
const auto &brightness = controls.get(controls::Brightness);
if (brightness) {
- int value = std::clamp<int>(std::lround(*brightness * 128), -128, 127);
+ int value = convertBrightness(*brightness);
if (cproc.brightness != value) {
cproc.brightness = value;
update = true;
@@ -57,7 +108,7 @@ void ColorProcessing::queueRequest(IPAContext &context,
const auto &contrast = controls.get(controls::Contrast);
if (contrast) {
- int value = std::clamp<int>(std::lround(*contrast * 128), 0, 255);
+ int value = convertContrastOrSaturation(*contrast);
if (cproc.contrast != value) {
cproc.contrast = value;
update = true;
@@ -68,7 +119,7 @@ void ColorProcessing::queueRequest(IPAContext &context,
const auto saturation = controls.get(controls::Saturation);
if (saturation) {
- int value = std::clamp<int>(std::lround(*saturation * 128), 0, 255);
+ int value = convertContrastOrSaturation(*saturation);
if (cproc.saturation != value) {
cproc.saturation = value;
update = true;
@@ -89,19 +140,17 @@ void ColorProcessing::queueRequest(IPAContext &context,
void ColorProcessing::prepare([[maybe_unused]] IPAContext &context,
[[maybe_unused]] const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params)
+ RkISP1Params *params)
{
/* Check if the algorithm configuration has been updated. */
if (!frameContext.cproc.update)
return;
- params->others.cproc_config.brightness = frameContext.cproc.brightness;
- params->others.cproc_config.contrast = frameContext.cproc.contrast;
- params->others.cproc_config.sat = frameContext.cproc.saturation;
-
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_CPROC;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_CPROC;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_CPROC;
+ auto config = params->block<BlockType::Cproc>();
+ config.setEnabled(true);
+ config->brightness = frameContext.cproc.brightness;
+ config->contrast = frameContext.cproc.contrast;
+ config->sat = frameContext.cproc.saturation;
}
REGISTER_IPA_ALGORITHM(ColorProcessing, "ColorProcessing")
diff --git a/src/ipa/rkisp1/algorithms/cproc.h b/src/ipa/rkisp1/algorithms/cproc.h
index ba6e901a..fd38fd17 100644
--- a/src/ipa/rkisp1/algorithms/cproc.h
+++ b/src/ipa/rkisp1/algorithms/cproc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * cproc.h - RkISP1 Color Processing control
+ * RkISP1 Color Processing control
*/
#pragma once
@@ -21,12 +21,15 @@ public:
ColorProcessing() = default;
~ColorProcessing() = default;
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
void queueRequest(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
};
} /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp
index 80a1b734..78946281 100644
--- a/src/ipa/rkisp1/algorithms/dpcc.cpp
+++ b/src/ipa/rkisp1/algorithms/dpcc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * dpcc.cpp - RkISP1 Defect Pixel Cluster Correction control
+ * RkISP1 Defect Pixel Cluster Correction control
*/
#include "dpcc.h"
@@ -232,16 +232,14 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context,
void DefectPixelClusterCorrection::prepare([[maybe_unused]] IPAContext &context,
const uint32_t frame,
[[maybe_unused]] IPAFrameContext &frameContext,
- rkisp1_params_cfg *params)
+ RkISP1Params *params)
{
if (frame > 0)
return;
- params->others.dpcc_config = config_;
-
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_DPCC;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_DPCC;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_DPCC;
+ auto config = params->block<BlockType::Dpcc>();
+ config.setEnabled(true);
+ *config = config_;
}
REGISTER_IPA_ALGORITHM(DefectPixelClusterCorrection, "DefectPixelClusterCorrection")
diff --git a/src/ipa/rkisp1/algorithms/dpcc.h b/src/ipa/rkisp1/algorithms/dpcc.h
index b1fac7d1..b77766c3 100644
--- a/src/ipa/rkisp1/algorithms/dpcc.h
+++ b/src/ipa/rkisp1/algorithms/dpcc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * dpcc.h - RkISP1 Defect Pixel Cluster Correction control
+ * RkISP1 Defect Pixel Cluster Correction control
*/
#pragma once
@@ -22,7 +22,7 @@ public:
int init(IPAContext &context, const YamlObject &tuningData) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
private:
rkisp1_cif_isp_dpcc_config config_;
diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
index 5bd7e59f..cb6095da 100644
--- a/src/ipa/rkisp1/algorithms/dpf.cpp
+++ b/src/ipa/rkisp1/algorithms/dpf.cpp
@@ -2,12 +2,14 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * dpf.cpp - RkISP1 Denoise Pre-Filter control
+ * RkISP1 Denoise Pre-Filter control
*/
#include "dpf.h"
-#include <cmath>
+#include <algorithm>
+#include <string>
+#include <vector>
#include <libcamera/base/log.h>
@@ -215,15 +217,21 @@ void Dpf::queueRequest(IPAContext &context,
* \copydoc libcamera::ipa::Algorithm::prepare
*/
void Dpf::prepare(IPAContext &context, const uint32_t frame,
- IPAFrameContext &frameContext, rkisp1_params_cfg *params)
+ IPAFrameContext &frameContext, RkISP1Params *params)
{
- if (frame == 0) {
- params->others.dpf_config = config_;
- params->others.dpf_strength_config = strengthConfig_;
+ if (!frameContext.dpf.update && frame > 0)
+ return;
+
+ auto config = params->block<BlockType::Dpf>();
+ config.setEnabled(frameContext.dpf.denoise);
+
+ if (frameContext.dpf.denoise) {
+ *config = config_;
const auto &awb = context.configuration.awb;
const auto &lsc = context.configuration.lsc;
- auto &mode = params->others.dpf_config.gain.mode;
+
+ auto &mode = config->gain.mode;
/*
* The DPF needs to take into account the total amount of
@@ -241,15 +249,12 @@ void Dpf::prepare(IPAContext &context, const uint32_t frame,
mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_LSC_GAINS;
else
mode = RKISP1_CIF_ISP_DPF_GAIN_USAGE_DISABLED;
-
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_DPF |
- RKISP1_CIF_ISP_MODULE_DPF_STRENGTH;
}
- if (frameContext.dpf.update) {
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_DPF;
- if (frameContext.dpf.denoise)
- params->module_ens |= RKISP1_CIF_ISP_MODULE_DPF;
+ if (frame == 0) {
+ auto strengthConfig = params->block<BlockType::DpfStrength>();
+ strengthConfig.setEnabled(true);
+ *strengthConfig = strengthConfig_;
}
}
diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h
index 58f29f74..2dd8cd36 100644
--- a/src/ipa/rkisp1/algorithms/dpf.h
+++ b/src/ipa/rkisp1/algorithms/dpf.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * dpf.h - RkISP1 Denoise Pre-Filter control
+ * RkISP1 Denoise Pre-Filter control
*/
#pragma once
@@ -27,7 +27,7 @@ public:
const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
private:
struct rkisp1_cif_isp_dpf_config config_;
diff --git a/src/ipa/rkisp1/algorithms/filter.cpp b/src/ipa/rkisp1/algorithms/filter.cpp
index 4b89c05a..7598ef8a 100644
--- a/src/ipa/rkisp1/algorithms/filter.cpp
+++ b/src/ipa/rkisp1/algorithms/filter.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * filter.cpp - RkISP1 Filter control
+ * RkISP1 Filter control
*/
#include "filter.h"
@@ -104,7 +104,7 @@ void Filter::queueRequest(IPAContext &context,
*/
void Filter::prepare([[maybe_unused]] IPAContext &context,
[[maybe_unused]] const uint32_t frame,
- IPAFrameContext &frameContext, rkisp1_params_cfg *params)
+ IPAFrameContext &frameContext, RkISP1Params *params)
{
/* Check if the algorithm configuration has been updated. */
if (!frameContext.filter.update)
@@ -160,23 +160,25 @@ void Filter::prepare([[maybe_unused]] IPAContext &context,
uint8_t denoise = frameContext.filter.denoise;
uint8_t sharpness = frameContext.filter.sharpness;
- auto &flt_config = params->others.flt_config;
-
- flt_config.fac_sh0 = filt_fac_sh0[sharpness];
- flt_config.fac_sh1 = filt_fac_sh1[sharpness];
- flt_config.fac_mid = filt_fac_mid[sharpness];
- flt_config.fac_bl0 = filt_fac_bl0[sharpness];
- flt_config.fac_bl1 = filt_fac_bl1[sharpness];
-
- flt_config.lum_weight = kFiltLumWeightDefault;
- flt_config.mode = kFiltModeDefault;
- flt_config.thresh_sh0 = filt_thresh_sh0[denoise];
- flt_config.thresh_sh1 = filt_thresh_sh1[denoise];
- flt_config.thresh_bl0 = filt_thresh_bl0[denoise];
- flt_config.thresh_bl1 = filt_thresh_bl1[denoise];
- flt_config.grn_stage1 = stage1_select[denoise];
- flt_config.chr_v_mode = filt_chr_v_mode[denoise];
- flt_config.chr_h_mode = filt_chr_h_mode[denoise];
+
+ auto config = params->block<BlockType::Flt>();
+ config.setEnabled(true);
+
+ config->fac_sh0 = filt_fac_sh0[sharpness];
+ config->fac_sh1 = filt_fac_sh1[sharpness];
+ config->fac_mid = filt_fac_mid[sharpness];
+ config->fac_bl0 = filt_fac_bl0[sharpness];
+ config->fac_bl1 = filt_fac_bl1[sharpness];
+
+ config->lum_weight = kFiltLumWeightDefault;
+ config->mode = kFiltModeDefault;
+ config->thresh_sh0 = filt_thresh_sh0[denoise];
+ config->thresh_sh1 = filt_thresh_sh1[denoise];
+ config->thresh_bl0 = filt_thresh_bl0[denoise];
+ config->thresh_bl1 = filt_thresh_bl1[denoise];
+ config->grn_stage1 = stage1_select[denoise];
+ config->chr_v_mode = filt_chr_v_mode[denoise];
+ config->chr_h_mode = filt_chr_h_mode[denoise];
/*
* Combined high denoising and high sharpening requires some
@@ -186,27 +188,23 @@ void Filter::prepare([[maybe_unused]] IPAContext &context,
*/
if (denoise == 9) {
if (sharpness > 3)
- flt_config.grn_stage1 = 2;
+ config->grn_stage1 = 2;
} else if (denoise == 10) {
if (sharpness > 5)
- flt_config.grn_stage1 = 2;
+ config->grn_stage1 = 2;
else if (sharpness > 3)
- flt_config.grn_stage1 = 1;
+ config->grn_stage1 = 1;
}
if (denoise > 7) {
if (sharpness > 7) {
- flt_config.fac_bl0 /= 2;
- flt_config.fac_bl1 /= 4;
+ config->fac_bl0 /= 2;
+ config->fac_bl1 /= 4;
} else if (sharpness > 4) {
- flt_config.fac_bl0 = flt_config.fac_bl0 * 3 / 4;
- flt_config.fac_bl1 /= 2;
+ config->fac_bl0 = config->fac_bl0 * 3 / 4;
+ config->fac_bl1 /= 2;
}
}
-
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_FLT;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_FLT;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_FLT;
}
REGISTER_IPA_ALGORITHM(Filter, "Filter")
diff --git a/src/ipa/rkisp1/algorithms/filter.h b/src/ipa/rkisp1/algorithms/filter.h
index 3fd882ea..8f858e57 100644
--- a/src/ipa/rkisp1/algorithms/filter.h
+++ b/src/ipa/rkisp1/algorithms/filter.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * filter.h - RkISP1 Filter control
+ * RkISP1 Filter control
*/
#pragma once
@@ -26,7 +26,7 @@ public:
const ControlList &controls) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
};
} /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
new file mode 100644
index 00000000..a9493678
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/goc.cpp
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 Gamma out control
+ */
+#include "goc.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libcamera/internal/yaml_parser.h"
+
+#include "linux/rkisp1-config.h"
+
+/**
+ * \file goc.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class GammaOutCorrection
+ * \brief RkISP1 Gamma out correction
+ *
+ * This algorithm implements the gamma out curve for the RkISP1. It defaults to
+ * a gamma value of 2.2.
+ *
+ * As gamma is internally represented as a piecewise linear function with only
+ * 17 knots, the difference between gamma=2.2 and sRGB gamma is minimal.
+ * Therefore sRGB gamma was not implemented as special case.
+ *
+ * Useful links:
+ * - https://www.cambridgeincolour.com/tutorials/gamma-correction.htm
+ * - https://en.wikipedia.org/wiki/SRGB
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Gamma)
+
+const float kDefaultGamma = 2.2f;
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int GammaOutCorrection::init(IPAContext &context, const YamlObject &tuningData)
+{
+ if (context.hw->numGammaOutSamples !=
+ RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10) {
+ LOG(RkISP1Gamma, Error)
+ << "Gamma is not implemented for RkISP1 V12";
+ return -EINVAL;
+ }
+
+ defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
+ context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
+
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::configure
+ */
+int GammaOutCorrection::configure(IPAContext &context,
+ [[maybe_unused]] const IPACameraSensorInfo &configInfo)
+{
+ context.activeState.goc.gamma = defaultGamma_;
+ return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void GammaOutCorrection::queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls)
+{
+ if (frame == 0)
+ frameContext.goc.update = true;
+
+ const auto &gamma = controls.get(controls::Gamma);
+ if (gamma) {
+ context.activeState.goc.gamma = *gamma;
+ frameContext.goc.update = true;
+ LOG(RkISP1Gamma, Debug) << "Set gamma to " << *gamma;
+ }
+
+ frameContext.goc.gamma = context.activeState.goc.gamma;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void GammaOutCorrection::prepare(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ RkISP1Params *params)
+{
+ ASSERT(context.hw->numGammaOutSamples ==
+ RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10);
+
+ if (!frameContext.goc.update)
+ return;
+
+ /*
+ * The logarithmic segments as specified in the reference.
+ * Plus an additional 0 to make the loop easier
+ */
+ static constexpr std::array<unsigned int, RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10> segments = {
+ 64, 64, 64, 64, 128, 128, 128, 128, 256,
+ 256, 256, 512, 512, 512, 512, 512, 0
+ };
+
+ auto config = params->block<BlockType::Goc>();
+ config.setEnabled(true);
+
+ __u16 *gamma_y = config->gamma_y;
+
+ unsigned x = 0;
+ for (const auto [i, size] : utils::enumerate(segments)) {
+ gamma_y[i] = std::pow(x / 4096.0, 1.0 / frameContext.goc.gamma) * 1023.0;
+ x += size;
+ }
+
+ config->mode = RKISP1_CIF_ISP_GOC_MODE_LOGARITHMIC;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void GammaOutCorrection::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] const rkisp1_stat_buffer *stats,
+ ControlList &metadata)
+{
+ metadata.set(controls::Gamma, frameContext.goc.gamma);
+}
+
+REGISTER_IPA_ALGORITHM(GammaOutCorrection, "GammaOutCorrection")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h
new file mode 100644
index 00000000..bb2ddfc9
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/goc.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 Gamma out control
+ */
+
+#pragma once
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+class GammaOutCorrection : public Algorithm
+{
+public:
+ GammaOutCorrection() = default;
+ ~GammaOutCorrection() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context,
+ const IPACameraSensorInfo &configInfo) override;
+ void queueRequest(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const ControlList &controls) override;
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ RkISP1Params *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const rkisp1_stat_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ float defaultGamma_;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp
index b9f87912..9604c0ac 100644
--- a/src/ipa/rkisp1/algorithms/gsl.cpp
+++ b/src/ipa/rkisp1/algorithms/gsl.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * gsl.cpp - RkISP1 Gamma Sensor Linearization control
+ * RkISP1 Gamma Sensor Linearization control
*/
#include "gsl.h"
@@ -119,24 +119,20 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
void GammaSensorLinearization::prepare([[maybe_unused]] IPAContext &context,
const uint32_t frame,
[[maybe_unused]] IPAFrameContext &frameContext,
- rkisp1_params_cfg *params)
+ RkISP1Params *params)
{
if (frame > 0)
return;
- params->others.sdg_config.xa_pnts.gamma_dx0 = gammaDx_[0];
- params->others.sdg_config.xa_pnts.gamma_dx1 = gammaDx_[1];
+ auto config = params->block<BlockType::Sdg>();
+ config.setEnabled(true);
- std::copy(curveYr_.begin(), curveYr_.end(),
- params->others.sdg_config.curve_r.gamma_y);
- std::copy(curveYg_.begin(), curveYg_.end(),
- params->others.sdg_config.curve_g.gamma_y);
- std::copy(curveYb_.begin(), curveYb_.end(),
- params->others.sdg_config.curve_b.gamma_y);
+ config->xa_pnts.gamma_dx0 = gammaDx_[0];
+ config->xa_pnts.gamma_dx1 = gammaDx_[1];
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_SDG;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_SDG;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_SDG;
+ std::copy(curveYr_.begin(), curveYr_.end(), config->curve_r.gamma_y);
+ std::copy(curveYg_.begin(), curveYg_.end(), config->curve_g.gamma_y);
+ std::copy(curveYb_.begin(), curveYb_.end(), config->curve_b.gamma_y);
}
REGISTER_IPA_ALGORITHM(GammaSensorLinearization, "GammaSensorLinearization")
diff --git a/src/ipa/rkisp1/algorithms/gsl.h b/src/ipa/rkisp1/algorithms/gsl.h
index 0f1116a7..91cf6efa 100644
--- a/src/ipa/rkisp1/algorithms/gsl.h
+++ b/src/ipa/rkisp1/algorithms/gsl.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * gsl.h - RkISP1 Gamma Sensor Linearization control
+ * RkISP1 Gamma Sensor Linearization control
*/
#pragma once
@@ -22,7 +22,7 @@ public:
int init(IPAContext &context, const YamlObject &tuningData) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
private:
uint32_t gammaDx_[2];
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index a7ccedb1..e7301bfe 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * lsc.cpp - RkISP1 Lens Shading Correction control
+ * RkISP1 Lens Shading Correction control
*/
#include "lsc.h"
@@ -16,6 +16,7 @@
#include "libcamera/internal/yaml_parser.h"
+#include "libipa/lsc_polynomial.h"
#include "linux/rkisp1-config.h"
/**
@@ -24,6 +25,36 @@
namespace libcamera {
+namespace ipa {
+
+constexpr int kColourTemperatureChangeThreshhold = 10;
+
+template<typename T>
+void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,
+ std::vector<T> &dest, double lambda)
+{
+ assert(a.size() == b.size());
+ dest.resize(a.size());
+ for (size_t i = 0; i < a.size(); i++) {
+ dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;
+ }
+}
+
+template<>
+void Interpolator<rkisp1::algorithms::LensShadingCorrection::Components>::
+ interpolate(const rkisp1::algorithms::LensShadingCorrection::Components &a,
+ const rkisp1::algorithms::LensShadingCorrection::Components &b,
+ rkisp1::algorithms::LensShadingCorrection::Components &dest,
+ double lambda)
+{
+ interpolateVector(a.r, b.r, dest.r, lambda);
+ interpolateVector(a.gr, b.gr, dest.gr, lambda);
+ interpolateVector(a.gb, b.gb, dest.gb, lambda);
+ interpolateVector(a.b, b.b, dest.b, lambda);
+}
+
+} /* namespace ipa */
+
namespace ipa::rkisp1::algorithms {
/**
@@ -40,6 +71,200 @@ namespace ipa::rkisp1::algorithms {
LOG_DEFINE_CATEGORY(RkISP1Lsc)
+class LscPolynomialLoader
+{
+public:
+ LscPolynomialLoader(const Size &sensorSize,
+ const Rectangle &cropRectangle,
+ const std::vector<double> &xSizes,
+ const std::vector<double> &ySizes)
+ : sensorSize_(sensorSize),
+ cropRectangle_(cropRectangle),
+ xSizes_(xSizes),
+ ySizes_(ySizes)
+ {
+ }
+
+ int parseLscData(const YamlObject &yamlSets,
+ std::map<unsigned int, LensShadingCorrection::Components> &lscData)
+ {
+ const auto &sets = yamlSets.asList();
+ for (const auto &yamlSet : sets) {
+ std::optional<LscPolynomial> pr, pgr, pgb, pb;
+ uint32_t ct = yamlSet["ct"].get<uint32_t>(0);
+
+ if (lscData.count(ct)) {
+ LOG(RkISP1Lsc, Error)
+ << "Multiple sets found for "
+ << "color temperature " << ct;
+ return -EINVAL;
+ }
+
+ LensShadingCorrection::Components &set = lscData[ct];
+ pr = yamlSet["r"].get<LscPolynomial>();
+ pgr = yamlSet["gr"].get<LscPolynomial>();
+ pgb = yamlSet["gb"].get<LscPolynomial>();
+ pb = yamlSet["b"].get<LscPolynomial>();
+
+ if (!(pr || pgr || pgb || pb)) {
+ LOG(RkISP1Lsc, Error)
+ << "Failed to parse polynomial for "
+ << "colour temperature " << ct;
+ return -EINVAL;
+ }
+
+ set.ct = ct;
+ pr->setReferenceImageSize(sensorSize_);
+ pgr->setReferenceImageSize(sensorSize_);
+ pgb->setReferenceImageSize(sensorSize_);
+ pb->setReferenceImageSize(sensorSize_);
+ set.r = samplePolynomial(*pr);
+ set.gr = samplePolynomial(*pgr);
+ set.gb = samplePolynomial(*pgb);
+ set.b = samplePolynomial(*pb);
+ }
+
+ if (lscData.empty()) {
+ LOG(RkISP1Lsc, Error) << "Failed to load any sets";
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+private:
+ /*
+ * The lsc grid has custom spacing defined on half the range (see
+ * parseSizes() for details). For easier handling this function converts
+ * the spaces vector to positions and mirrors them. E.g.:
+ *
+ * input: | 0.2 | 0.3 |
+ * output: 0.0 0.2 0.5 0.8 1.0
+ */
+ std::vector<double> sizesListToPositions(const std::vector<double> &sizes)
+ {
+ const int half = sizes.size();
+ std::vector<double> res(half * 2 + 1);
+ double x = 0.0;
+
+ res[half] = 0.5;
+ for (int i = 1; i <= half; i++) {
+ x += sizes[half - i];
+ res[half - i] = 0.5 - x;
+ res[half + i] = 0.5 + x;
+ }
+
+ return res;
+ }
+
+ std::vector<uint16_t> samplePolynomial(const LscPolynomial &poly)
+ {
+ constexpr int k = RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
+
+ double m = poly.getM();
+ double x0 = cropRectangle_.x / m;
+ double y0 = cropRectangle_.y / m;
+ double w = cropRectangle_.width / m;
+ double h = cropRectangle_.height / m;
+ std::vector<uint16_t> res;
+
+ assert(xSizes_.size() * 2 + 1 == k);
+ assert(ySizes_.size() * 2 + 1 == k);
+
+ res.reserve(k * k);
+
+ std::vector<double> xPos(sizesListToPositions(xSizes_));
+ std::vector<double> yPos(sizesListToPositions(ySizes_));
+
+ for (int y = 0; y < k; y++) {
+ for (int x = 0; x < k; x++) {
+ double xp = x0 + xPos[x] * w;
+ double yp = y0 + yPos[y] * h;
+ /*
+ * The hardware uses 2.10 fixed point format and
+ * limits the legal values to [1..3.999]. Scale
+ * and clamp the sampled value accordingly.
+ */
+ int v = static_cast<int>(
+ poly.sampleAtNormalizedPixelPos(xp, yp) *
+ 1024);
+ v = std::min(std::max(v, 1024), 4095);
+ res.push_back(v);
+ }
+ }
+ return res;
+ }
+
+ Size sensorSize_;
+ Rectangle cropRectangle_;
+ const std::vector<double> &xSizes_;
+ const std::vector<double> &ySizes_;
+};
+
+class LscTableLoader
+{
+public:
+ int parseLscData(const YamlObject &yamlSets,
+ std::map<unsigned int, LensShadingCorrection::Components> &lscData)
+ {
+ const auto &sets = yamlSets.asList();
+
+ for (const auto &yamlSet : sets) {
+ uint32_t ct = yamlSet["ct"].get<uint32_t>(0);
+
+ if (lscData.count(ct)) {
+ LOG(RkISP1Lsc, Error)
+ << "Multiple sets found for color temperature "
+ << ct;
+ return -EINVAL;
+ }
+
+ LensShadingCorrection::Components &set = lscData[ct];
+
+ set.ct = ct;
+ set.r = parseTable(yamlSet, "r");
+ set.gr = parseTable(yamlSet, "gr");
+ set.gb = parseTable(yamlSet, "gb");
+ set.b = parseTable(yamlSet, "b");
+
+ if (set.r.empty() || set.gr.empty() ||
+ set.gb.empty() || set.b.empty()) {
+ LOG(RkISP1Lsc, Error)
+ << "Set for color temperature " << ct
+ << " is missing tables";
+ return -EINVAL;
+ }
+ }
+
+ if (lscData.empty()) {
+ LOG(RkISP1Lsc, Error) << "Failed to load any sets";
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+private:
+ std::vector<uint16_t> parseTable(const YamlObject &tuningData,
+ const char *prop)
+ {
+ static constexpr unsigned int kLscNumSamples =
+ RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
+
+ std::vector<uint16_t> table =
+ tuningData[prop].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+ if (table.size() != kLscNumSamples) {
+ LOG(RkISP1Lsc, Error)
+ << "Invalid '" << prop << "' values: expected "
+ << kLscNumSamples
+ << " elements, got " << table.size();
+ return {};
+ }
+
+ return table;
+ }
+};
+
static std::vector<double> parseSizes(const YamlObject &tuningData,
const char *prop)
{
@@ -70,28 +295,10 @@ static std::vector<double> parseSizes(const YamlObject &tuningData,
return sizes;
}
-static std::vector<uint16_t> parseTable(const YamlObject &tuningData,
- const char *prop)
-{
- static constexpr unsigned int kLscNumSamples =
- RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
-
- std::vector<uint16_t> table =
- tuningData[prop].getList<uint16_t>().value_or(std::vector<uint16_t>{});
- if (table.size() != kLscNumSamples) {
- LOG(RkISP1Lsc, Error)
- << "Invalid '" << prop << "' values: expected "
- << kLscNumSamples
- << " elements, got " << table.size();
- return {};
- }
-
- return table;
-}
-
LensShadingCorrection::LensShadingCorrection()
- : lastCt_({ 0, 0 })
+ : lastAppliedCt_(0), lastAppliedQuantizedCt_(0)
{
+ sets_.setQuantization(kColourTemperatureChangeThreshhold);
}
/**
@@ -114,38 +321,30 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,
return -EINVAL;
}
- const auto &sets = yamlSets.asList();
- for (const auto &yamlSet : sets) {
- uint32_t ct = yamlSet["ct"].get<uint32_t>(0);
-
- if (sets_.count(ct)) {
- LOG(RkISP1Lsc, Error)
- << "Multiple sets found for color temperature "
- << ct;
- return -EINVAL;
- }
-
- Components &set = sets_[ct];
-
- set.ct = ct;
- set.r = parseTable(yamlSet, "r");
- set.gr = parseTable(yamlSet, "gr");
- set.gb = parseTable(yamlSet, "gb");
- set.b = parseTable(yamlSet, "b");
-
- if (set.r.empty() || set.gr.empty() ||
- set.gb.empty() || set.b.empty()) {
- LOG(RkISP1Lsc, Error)
- << "Set for color temperature " << ct
- << " is missing tables";
- return -EINVAL;
- }
+ std::map<unsigned int, Components> lscData;
+ int res = 0;
+ std::string type = tuningData["type"].get<std::string>("table");
+ if (type == "table") {
+ LOG(RkISP1Lsc, Debug) << "Loading tabular LSC data.";
+ auto loader = LscTableLoader();
+ res = loader.parseLscData(yamlSets, lscData);
+ } else if (type == "polynomial") {
+ LOG(RkISP1Lsc, Debug) << "Loading polynomial LSC data.";
+ auto loader = LscPolynomialLoader(context.sensorInfo.activeAreaSize,
+ context.sensorInfo.analogCrop,
+ xSize_,
+ ySize_);
+ res = loader.parseLscData(yamlSets, lscData);
+ } else {
+ LOG(RkISP1Lsc, Error) << "Unsupported LSC data type '"
+ << type << "'";
+ res = -EINVAL;
}
- if (sets_.empty()) {
- LOG(RkISP1Lsc, Error) << "Failed to load any sets";
- return -EINVAL;
- }
+ if (res)
+ return res;
+
+ sets_.setData(std::move(lscData));
return 0;
}
@@ -185,18 +384,12 @@ int LensShadingCorrection::configure(IPAContext &context,
return 0;
}
-void LensShadingCorrection::setParameters(rkisp1_params_cfg *params)
+void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)
{
- struct rkisp1_cif_isp_lsc_config &config = params->others.lsc_config;
-
memcpy(config.x_grad_tbl, xGrad_, sizeof(config.x_grad_tbl));
memcpy(config.y_grad_tbl, yGrad_, sizeof(config.y_grad_tbl));
memcpy(config.x_size_tbl, xSizes_, sizeof(config.x_size_tbl));
memcpy(config.y_size_tbl, ySizes_, sizeof(config.y_size_tbl));
-
- params->module_en_update |= RKISP1_CIF_ISP_MODULE_LSC;
- params->module_ens |= RKISP1_CIF_ISP_MODULE_LSC;
- params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_LSC;
}
void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,
@@ -208,131 +401,34 @@ void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,
std::copy(set.b.begin(), set.b.end(), &config.b_data_tbl[0][0]);
}
-/*
- * Interpolate LSC parameters based on color temperature value.
- */
-void LensShadingCorrection::interpolateTable(rkisp1_cif_isp_lsc_config &config,
- const Components &set0,
- const Components &set1,
- const uint32_t ct)
-{
- double coeff0 = (set1.ct - ct) / static_cast<double>(set1.ct - set0.ct);
- double coeff1 = (ct - set0.ct) / static_cast<double>(set1.ct - set0.ct);
-
- for (unsigned int i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++i) {
- for (unsigned int j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++j) {
- unsigned int sample = i * RKISP1_CIF_ISP_LSC_SAMPLES_MAX + j;
-
- config.r_data_tbl[i][j] =
- set0.r[sample] * coeff0 +
- set1.r[sample] * coeff1;
-
- config.gr_data_tbl[i][j] =
- set0.gr[sample] * coeff0 +
- set1.gr[sample] * coeff1;
-
- config.gb_data_tbl[i][j] =
- set0.gb[sample] * coeff0 +
- set1.gb[sample] * coeff1;
-
- config.b_data_tbl[i][j] =
- set0.b[sample] * coeff0 +
- set1.b[sample] * coeff1;
- }
- }
-}
-
/**
* \copydoc libcamera::ipa::Algorithm::prepare
*/
-void LensShadingCorrection::prepare(IPAContext &context,
- const uint32_t frame,
- [[maybe_unused]] IPAFrameContext &frameContext,
- rkisp1_params_cfg *params)
+void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ RkISP1Params *params)
{
- struct rkisp1_cif_isp_lsc_config &config = params->others.lsc_config;
-
- /*
- * If there is only one set, the configuration has already been done
- * for first frame.
- */
- if (sets_.size() == 1 && frame > 0)
+ uint32_t ct = frameContext.awb.temperatureK;
+ if (std::abs(static_cast<int>(ct) - static_cast<int>(lastAppliedCt_)) <
+ kColourTemperatureChangeThreshhold)
return;
-
- /*
- * If there is only one set, pick it. We can ignore lastCt_, as it will
- * never be relevant.
- */
- if (sets_.size() == 1) {
- setParameters(params);
- copyTable(config, sets_.cbegin()->second);
- return;
- }
-
- uint32_t ct = context.activeState.awb.temperatureK;
- ct = std::clamp(ct, sets_.cbegin()->first, sets_.crbegin()->first);
-
- /*
- * If the original is the same, then it means the same adjustment would
- * be made. If the adjusted is the same, then it means that it's the
- * same as what was actually applied. Thus in these cases we can skip
- * reprogramming the LSC.
- *
- * original == adjusted can only happen if an interpolation
- * happened, or if original has an exact entry in sets_. This means
- * that if original != adjusted, then original was adjusted to
- * the nearest available entry in sets_, resulting in adjusted.
- * Clearly, any ct value that is in between original and adjusted
- * will be adjusted to the same adjusted value, so we can skip
- * reprogramming the LSC table.
- *
- * We also skip updating the original value, as the last one had a
- * larger bound and thus a larger range of ct values that will be
- * adjusted to the same adjusted.
- */
- if ((lastCt_.original <= ct && ct <= lastCt_.adjusted) ||
- (lastCt_.adjusted <= ct && ct <= lastCt_.original))
+ unsigned int quantizedCt;
+ const Components &set = sets_.getInterpolated(ct, &quantizedCt);
+ if (lastAppliedQuantizedCt_ == quantizedCt)
return;
- setParameters(params);
+ auto config = params->block<BlockType::Lsc>();
+ config.setEnabled(true);
+ setParameters(*config);
+ copyTable(*config, set);
- /*
- * The color temperature matches exactly one of the available LSC tables.
- */
- if (sets_.count(ct)) {
- copyTable(config, sets_[ct]);
- lastCt_ = { ct, ct };
- return;
- }
-
- /* No shortcuts left; we need to round or interpolate */
- auto iter = sets_.upper_bound(ct);
- const Components &set1 = iter->second;
- const Components &set0 = (--iter)->second;
- uint32_t ct0 = set0.ct;
- uint32_t ct1 = set1.ct;
- uint32_t diff0 = ct - ct0;
- uint32_t diff1 = ct1 - ct;
- static constexpr double kThreshold = 0.1;
- float threshold = kThreshold * (ct1 - ct0);
-
- if (diff0 < threshold || diff1 < threshold) {
- const Components &set = diff0 < diff1 ? set0 : set1;
- LOG(RkISP1Lsc, Debug) << "using LSC table for " << set.ct;
- copyTable(config, set);
- lastCt_ = { ct, set.ct };
- return;
- }
+ lastAppliedCt_ = ct;
+ lastAppliedQuantizedCt_ = quantizedCt;
- /*
- * ct is not within 10% of the difference between the neighbouring
- * color temperatures, so we need to interpolate.
- */
LOG(RkISP1Lsc, Debug)
- << "ct is " << ct << ", interpolating between "
- << ct0 << " and " << ct1;
- interpolateTable(config, set0, set1, ct);
- lastCt_ = { ct, ct };
+ << "ct is " << ct << ", quantized to "
+ << quantizedCt;
}
REGISTER_IPA_ALGORITHM(LensShadingCorrection, "LensShadingCorrection")
diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
index e2a93a56..5a0824e3 100644
--- a/src/ipa/rkisp1/algorithms/lsc.h
+++ b/src/ipa/rkisp1/algorithms/lsc.h
@@ -2,13 +2,15 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * lsc.h - RkISP1 Lens Shading Correction control
+ * RkISP1 Lens Shading Correction control
*/
#pragma once
#include <map>
+#include "libipa/interpolator.h"
+
#include "algorithm.h"
namespace libcamera {
@@ -25,9 +27,8 @@ public:
int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override;
void prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
- rkisp1_params_cfg *params) override;
+ RkISP1Params *params) override;
-private:
struct Components {
uint32_t ct;
std::vector<uint16_t> r;
@@ -36,23 +37,23 @@ private:
std::vector<uint16_t> b;
};
- void setParameters(rkisp1_params_cfg *params);
+private:
+ void setParameters(rkisp1_cif_isp_lsc_config &config);
void copyTable(rkisp1_cif_isp_lsc_config &config, const Components &set0);
void interpolateTable(rkisp1_cif_isp_lsc_config &config,
const Components &set0, const Components &set1,
const uint32_t ct);
- std::map<uint32_t, Components> sets_;
+ ipa::Interpolator<Components> sets_;
std::vector<double> xSize_;
std::vector<double> ySize_;
uint16_t xGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];
uint16_t yGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];
uint16_t xSizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];
uint16_t ySizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];
- struct {
- uint32_t original;
- uint32_t adjusted;
- } lastCt_;
+
+ unsigned int lastAppliedCt_;
+ unsigned int lastAppliedQuantizedCt_;
};
} /* namespace ipa::rkisp1::algorithms */
diff --git a/src/ipa/rkisp1/algorithms/lux.cpp b/src/ipa/rkisp1/algorithms/lux.cpp
new file mode 100644
index 00000000..a467767e
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/lux.cpp
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * lux.cpp - RkISP1 Lux control
+ */
+
+#include "lux.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libipa/histogram.h"
+#include "libipa/lux.h"
+
+/**
+ * \file lux.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class Lux
+ * \brief RkISP1 Lux control
+ *
+ * The Lux algorithm is responsible for estimating the lux level of the image.
+ * It doesn't take or generate any controls, but it provides a lux level for
+ * other algorithms (such as AGC) to use.
+ */
+
+/**
+ * \brief Construct an rkisp1 Lux algo module
+ */
+Lux::Lux()
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Lux::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
+{
+ return lux_.parseTuningData(tuningData);
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::process
+ */
+void Lux::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const rkisp1_stat_buffer *stats,
+ ControlList &metadata)
+{
+ utils::Duration exposureTime = context.configuration.sensor.lineDuration
+ * frameContext.sensor.exposure;
+ double gain = frameContext.sensor.gain;
+
+ /* \todo Deduplicate the histogram calculation from AGC */
+ const rkisp1_cif_isp_stat *params = &stats->params;
+ Histogram yHist({ params->hist.hist_bins, context.hw->numHistogramBins },
+ [](uint32_t x) { return x >> 4; });
+
+ double lux = lux_.estimateLux(exposureTime, gain, 1.0, yHist);
+ frameContext.lux.lux = lux;
+ metadata.set(controls::Lux, lux);
+}
+
+REGISTER_IPA_ALGORITHM(Lux, "Lux")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/lux.h b/src/ipa/rkisp1/algorithms/lux.h
new file mode 100644
index 00000000..8a90de55
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/lux.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * lux.h - RkISP1 Lux control
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include "libipa/lux.h"
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+class Lux : public Algorithm
+{
+public:
+ Lux();
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const rkisp1_stat_buffer *stats,
+ ControlList &metadata) override;
+
+private:
+ ipa::Lux lux_;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
index 93a48329..c66b0b70 100644
--- a/src/ipa/rkisp1/algorithms/meson.build
+++ b/src/ipa/rkisp1/algorithms/meson.build
@@ -4,10 +4,13 @@ rkisp1_ipa_algorithms = files([
'agc.cpp',
'awb.cpp',
'blc.cpp',
+ 'ccm.cpp',
'cproc.cpp',
'dpcc.cpp',
'dpf.cpp',
'filter.cpp',
+ 'goc.cpp',
'gsl.cpp',
'lsc.cpp',
+ 'lux.cpp',
])
diff --git a/src/ipa/rkisp1/data/imx219.yaml b/src/ipa/rkisp1/data/imx219.yaml
index cbcc43b8..0d99cb52 100644
--- a/src/ipa/rkisp1/data/imx219.yaml
+++ b/src/ipa/rkisp1/data/imx219.yaml
@@ -6,10 +6,6 @@ algorithms:
- Agc:
- Awb:
- BlackLevelCorrection:
- R: 256
- Gr: 256
- Gb: 256
- B: 256
- LensShadingCorrection:
x-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
y-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
diff --git a/src/ipa/rkisp1/data/imx258.yaml b/src/ipa/rkisp1/data/imx258.yaml
index 43dddf20..202af36a 100644
--- a/src/ipa/rkisp1/data/imx258.yaml
+++ b/src/ipa/rkisp1/data/imx258.yaml
@@ -5,6 +5,7 @@ version: 1
algorithms:
- Agc:
- Awb:
+ - BlackLevelCorrection:
- LensShadingCorrection:
x-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
y-size: [ 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625 ]
diff --git a/src/ipa/rkisp1/data/meson.build b/src/ipa/rkisp1/data/meson.build
index f5e9fa75..1e3522b2 100644
--- a/src/ipa/rkisp1/data/meson.build
+++ b/src/ipa/rkisp1/data/meson.build
@@ -2,10 +2,15 @@
conf_files = files([
'imx219.yaml',
+ 'imx258.yaml',
+ 'ov2685.yaml',
'ov4689.yaml',
'ov5640.yaml',
+ 'ov5695.yaml',
+ 'ov8858.yaml',
'uncalibrated.yaml',
])
install_data(conf_files,
- install_dir : ipa_data_dir / 'rkisp1')
+ install_dir : ipa_data_dir / 'rkisp1',
+ install_tag : 'runtime')
diff --git a/src/ipa/rkisp1/data/ov4689.yaml b/src/ipa/rkisp1/data/ov4689.yaml
index 2068684c..60901296 100644
--- a/src/ipa/rkisp1/data/ov4689.yaml
+++ b/src/ipa/rkisp1/data/ov4689.yaml
@@ -6,8 +6,4 @@ algorithms:
- Agc:
- Awb:
- BlackLevelCorrection:
- R: 66
- Gr: 66
- Gb: 66
- B: 66
...
diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
index 897b83cb..4b21d412 100644
--- a/src/ipa/rkisp1/data/ov5640.yaml
+++ b/src/ipa/rkisp1/data/ov5640.yaml
@@ -6,10 +6,6 @@ algorithms:
- Agc:
- Awb:
- BlackLevelCorrection:
- R: 256
- Gr: 256
- Gb: 256
- B: 256
- ColorProcessing:
- GammaSensorLinearization:
x-intervals: [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]
diff --git a/src/ipa/rkisp1/data/uncalibrated.yaml b/src/ipa/rkisp1/data/uncalibrated.yaml
index a7bbd8d8..60901296 100644
--- a/src/ipa/rkisp1/data/uncalibrated.yaml
+++ b/src/ipa/rkisp1/data/uncalibrated.yaml
@@ -5,4 +5,5 @@ version: 1
algorithms:
- Agc:
- Awb:
+ - BlackLevelCorrection:
...
diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
index 9bbf3684..65096105 100644
--- a/src/ipa/rkisp1/ipa_context.cpp
+++ b/src/ipa/rkisp1/ipa_context.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * ipa_context.cpp - RkISP1 IPA Context
+ * RkISP1 IPA Context
*/
#include "ipa_context.h"
@@ -15,6 +15,25 @@
namespace libcamera::ipa::rkisp1 {
/**
+ * \struct IPAHwSettings
+ * \brief RkISP1 version-specific hardware parameters
+ */
+
+/**
+ * \var IPAHwSettings::numAeCells
+ * \brief Number of cells in the AE exposure means grid
+ *
+ * \var IPAHwSettings::numHistogramBins
+ * \brief Number of bins in the histogram
+ *
+ * \var IPAHwSettings::numHistogramWeights
+ * \brief Number of weights in the histogram grid
+ *
+ * \var IPAHwSettings::numGammaOutSamples
+ * \brief Number of samples in the gamma out table
+ */
+
+/**
* \struct IPASessionConfiguration
* \brief Session configuration for the IPA module
*
@@ -33,14 +52,6 @@ namespace libcamera::ipa::rkisp1 {
*/
/**
- * \var IPASessionConfiguration::hw
- * \brief RkISP1-specific hardware information
- *
- * \var IPASessionConfiguration::hw.revision
- * \brief Hardware revision of the ISP
- */
-
-/**
* \var IPASessionConfiguration::awb
* \brief AWB parameters configuration of the IPA
*
@@ -67,11 +78,11 @@ namespace libcamera::ipa::rkisp1 {
* \var IPASessionConfiguration::sensor
* \brief Sensor-specific configuration of the IPA
*
- * \var IPASessionConfiguration::sensor.minShutterSpeed
- * \brief Minimum shutter speed supported with the sensor
+ * \var IPASessionConfiguration::sensor.minExposureTime
+ * \brief Minimum exposure time supported with the sensor
*
- * \var IPASessionConfiguration::sensor.maxShutterSpeed
- * \brief Maximum shutter speed supported with the sensor
+ * \var IPASessionConfiguration::sensor.maxExposureTime
+ * \brief Maximum exposure time supported with the sensor
*
* \var IPASessionConfiguration::sensor.minAnalogueGain
* \brief Minimum analogue gain supported with the sensor
@@ -95,6 +106,11 @@ namespace libcamera::ipa::rkisp1 {
*/
/**
+ * \var IPASessionConfiguration::paramFormat
+ * \brief The fourcc of the parameters buffers format
+ */
+
+/**
* \struct IPAActiveState
* \brief Active state for algorithms
*
@@ -126,54 +142,86 @@ namespace libcamera::ipa::rkisp1 {
* \var IPAActiveState::agc
* \brief State for the Automatic Gain Control algorithm
*
- * The exposure and gain are the latest values computed by the AGC algorithm.
+ * The \a automatic variables track the latest values computed by algorithm
+ * based on the latest processed statistics. All other variables track the
+ * consolidated controls requested in queued requests.
*
- * \var IPAActiveState::agc.exposure
- * \brief Exposure time expressed as a number of lines
+ * \struct IPAActiveState::agc.manual
+ * \brief Manual exposure time and analog gain (set through requests)
*
- * \var IPAActiveState::agc.gain
- * \brief Analogue gain multiplier
- */
-
-/**
- * \var IPAActiveState::awb
- * \brief State for the Automatic White Balance algorithm
+ * \var IPAActiveState::agc.manual.exposure
+ * \brief Manual exposure time expressed as a number of lines as set by the
+ * ExposureTime control
*
- * \struct IPAActiveState::awb.gains
- * \brief White balance gains
+ * \var IPAActiveState::agc.manual.gain
+ * \brief Manual analogue gain as set by the AnalogueGain control
*
- * \struct IPAActiveState::awb.gains.manual
- * \brief Manual white balance gains (set through requests)
+ * \struct IPAActiveState::agc.automatic
+ * \brief Automatic exposure time and analog gain (computed by the algorithm)
*
- * \var IPAActiveState::awb.gains.manual.red
- * \brief Manual white balance gain for R channel
+ * \var IPAActiveState::agc.automatic.exposure
+ * \brief Automatic exposure time expressed as a number of lines
*
- * \var IPAActiveState::awb.gains.manual.green
- * \brief Manual white balance gain for G channel
+ * \var IPAActiveState::agc.automatic.gain
+ * \brief Automatic analogue gain multiplier
*
- * \var IPAActiveState::awb.gains.manual.blue
- * \brief Manual white balance gain for B channel
+ * \var IPAActiveState::agc.autoExposureEnabled
+ * \brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control
*
- * \struct IPAActiveState::awb.gains.automatic
- * \brief Automatic white balance gains (computed by the algorithm)
+ * \var IPAActiveState::agc.autoGainEnabled
+ * \brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control
*
- * \var IPAActiveState::awb.gains.automatic.red
- * \brief Automatic white balance gain for R channel
+ * \var IPAActiveState::agc.constraintMode
+ * \brief Constraint mode as set by the AeConstraintMode control
*
- * \var IPAActiveState::awb.gains.automatic.green
- * \brief Automatic white balance gain for G channel
+ * \var IPAActiveState::agc.exposureMode
+ * \brief Exposure mode as set by the AeExposureMode control
*
- * \var IPAActiveState::awb.gains.automatic.blue
- * \brief Automatic white balance gain for B channel
+ * \var IPAActiveState::agc.meteringMode
+ * \brief Metering mode as set by the AeMeteringMode control
*
- * \var IPAActiveState::awb.temperatureK
- * \brief Estimated color temperature
+ * \var IPAActiveState::agc.minFrameDuration
+ * \brief Minimum frame duration as set by the FrameDurationLimits control
+ *
+ * \var IPAActiveState::agc.maxFrameDuration
+ * \brief Maximum frame duration as set by the FrameDurationLimits control
+ */
+
+/**
+ * \var IPAActiveState::awb
+ * \brief State for the Automatic White Balance algorithm
+ *
+ * \struct IPAActiveState::awb::AwbState
+ * \brief Struct for the AWB regulation state
+ *
+ * \var IPAActiveState::awb::AwbState.gains
+ * \brief White balance gains
+ *
+ * \var IPAActiveState::awb::AwbState.temperatureK
+ * \brief Color temperature
+ *
+ * \var IPAActiveState::awb.manual
+ * \brief Manual regulation state (set through requests)
+ *
+ * \var IPAActiveState::awb.automatic
+ * \brief Automatic regulation state (computed by the algorithm)
*
* \var IPAActiveState::awb.autoEnabled
* \brief Whether the Auto White Balance algorithm is enabled
*/
/**
+ * \var IPAActiveState::ccm
+ * \brief State for the Colour Correction Matrix algorithm
+ *
+ * \var IPAActiveState::ccm.manual
+ * \brief Manual CCM (set through requests)
+ *
+ * \var IPAActiveState::awb.automatic
+ * \brief Automatic CCM (computed by the algorithm)
+ */
+
+/**
* \var IPAActiveState::cproc
* \brief State for the Color Processing algorithm
*
@@ -207,6 +255,14 @@ namespace libcamera::ipa::rkisp1 {
*/
/**
+ * \var IPAActiveState::goc
+ * \brief State for the goc algorithm
+ *
+ * \var IPAActiveState::goc.gamma
+ * \brief Gamma value applied as 1.0/gamma
+ */
+
+/**
* \struct IPAFrameContext
* \brief Per-frame context for algorithms
*
@@ -243,15 +299,57 @@ namespace libcamera::ipa::rkisp1 {
* \brief Automatic Gain Control parameters for this frame
*
* The exposure and gain are provided by the AGC algorithm, and are to be
- * applied to the sensor in order to take effect for this frame.
+ * applied to the sensor in order to take effect for this frame. Additionally
+ * the vertical blanking period is determined to maintain a consistent frame
+ * rate matched to the FrameDurationLimits as set by the user.
*
* \var IPAFrameContext::agc.exposure
- * \brief Exposure time expressed as a number of lines
+ * \brief Exposure time expressed as a number of lines computed by the algorithm
*
* \var IPAFrameContext::agc.gain
- * \brief Analogue gain multiplier
+ * \brief Analogue gain multiplier computed by the algorithm
*
* The gain should be adapted to the sensor specific gain code before applying.
+ *
+ * \var IPAFrameContext::agc.vblank
+ * \brief Vertical blanking parameter computed by the algorithm
+ *
+ * \var IPAFrameContext::agc.autoExposureEnabled
+ * \brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control
+ *
+ * \var IPAFrameContext::agc.autoGainEnabled
+ * \brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control
+ *
+ * \var IPAFrameContext::agc.constraintMode
+ * \brief Constraint mode as set by the AeConstraintMode control
+ *
+ * \var IPAFrameContext::agc.exposureMode
+ * \brief Exposure mode as set by the AeExposureMode control
+ *
+ * \var IPAFrameContext::agc.meteringMode
+ * \brief Metering mode as set by the AeMeteringMode control
+ *
+ * \var IPAFrameContext::agc.minFrameDuration
+ * \brief Minimum frame duration as set by the FrameDurationLimits control
+ *
+ * \var IPAFrameContext::agc.maxFrameDuration
+ * \brief Maximum frame duration as set by the FrameDurationLimits control
+ *
+ * \var IPAFrameContext::agc.frameDuration
+ * \brief The actual FrameDuration used by the algorithm for the frame
+ *
+ * \var IPAFrameContext::agc.updateMetering
+ * \brief Indicate if new ISP AGC metering parameters need to be applied
+ *
+ * \var IPAFrameContext::agc.autoExposureModeChange
+ * \brief Indicate if autoExposureEnabled has changed from true in the previous
+ * frame to false in the current frame, and no manual exposure value has been
+ * supplied in the current frame.
+ *
+ * \var IPAFrameContext::agc.autoGainModeChange
+ * \brief Indicate if autoGainEnabled has changed from true in the previous
+ * frame to false in the current frame, and no manual gain value has been
+ * supplied in the current frame.
*/
/**
@@ -261,23 +359,25 @@ namespace libcamera::ipa::rkisp1 {
* \struct IPAFrameContext::awb.gains
* \brief White balance gains
*
- * \var IPAFrameContext::awb.gains.red
- * \brief White balance gain for R channel
- *
- * \var IPAFrameContext::awb.gains.green
- * \brief White balance gain for G channel
- *
- * \var IPAFrameContext::awb.gains.blue
- * \brief White balance gain for B channel
- *
* \var IPAFrameContext::awb.temperatureK
- * \brief Estimated color temperature
+ * \brief Color temperature used for processing this frame
+ *
+ * This does not match the color temperature estimated for this frame as the
+ * measurements were taken on a previous frame.
*
* \var IPAFrameContext::awb.autoEnabled
* \brief Whether the Auto White Balance algorithm is enabled
*/
/**
+ * \var IPAFrameContext::ccm
+ * \brief Colour Correction Matrix parameters for this frame
+ *
+ * \struct IPAFrameContext::ccm.ccm
+ * \brief Colour Correction Matrix
+ */
+
+/**
* \var IPAFrameContext::cproc
* \brief Color Processing parameters for this frame
*
@@ -323,6 +423,18 @@ namespace libcamera::ipa::rkisp1 {
*/
/**
+ * \var IPAFrameContext::goc
+ * \brief Gamma out correction parameters for this frame
+ *
+ * \var IPAFrameContext::goc.gamma
+ * \brief Gamma value applied as 1.0/gamma
+ *
+ * \var IPAFrameContext::goc.update
+ * \brief Indicates if the goc parameters have been updated compared to the
+ * previous frame
+ */
+
+/**
* \var IPAFrameContext::sensor
* \brief Sensor configuration that used been used for this frame
*
@@ -337,6 +449,12 @@ namespace libcamera::ipa::rkisp1 {
* \struct IPAContext
* \brief Global IPA context data shared between all algorithms
*
+ * \var IPAContext::hw
+ * \brief RkISP1 version-specific hardware parameters
+ *
+ * \var IPAContext::sensorInfo
+ * \brief The IPA session sensorInfo, immutable during the session
+ *
* \var IPAContext::configuration
* \brief The IPA session configuration, immutable during the session
*
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index b9b20653..f0d50421 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -2,24 +2,43 @@
/*
* Copyright (C) 2021-2022, Ideas On Board
*
- * ipa_context.h - RkISP1 IPA Context
+ * RkISP1 IPA Context
*
*/
#pragma once
+#include <memory>
+
#include <linux/rkisp1-config.h>
#include <libcamera/base/utils.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
#include <libcamera/geometry.h>
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/debug_controls.h"
+#include "libcamera/internal/matrix.h"
+#include "libcamera/internal/vector.h"
+
+#include <libipa/camera_sensor_helper.h>
#include <libipa/fc_queue.h>
namespace libcamera {
namespace ipa::rkisp1 {
+struct IPAHwSettings {
+ unsigned int numAeCells;
+ unsigned int numHistogramBins;
+ unsigned int numHistogramWeights;
+ unsigned int numGammaOutSamples;
+ bool compand;
+};
+
struct IPASessionConfiguration {
struct {
struct rkisp1_cif_isp_window measureWindow;
@@ -35,8 +54,8 @@ struct IPASessionConfiguration {
} lsc;
struct {
- utils::Duration minShutterSpeed;
- utils::Duration maxShutterSpeed;
+ utils::Duration minExposureTime;
+ utils::Duration maxExposureTime;
double minAnalogueGain;
double maxAnalogueGain;
@@ -45,11 +64,8 @@ struct IPASessionConfiguration {
Size size;
} sensor;
- struct {
- rkisp1_cif_isp_version revision;
- } hw;
-
bool raw;
+ uint32_t paramFormat;
};
struct IPAActiveState {
@@ -63,28 +79,33 @@ struct IPAActiveState {
double gain;
} automatic;
- bool autoEnabled;
+ bool autoExposureEnabled;
+ bool autoGainEnabled;
+ controls::AeConstraintModeEnum constraintMode;
+ controls::AeExposureModeEnum exposureMode;
+ controls::AeMeteringModeEnum meteringMode;
+ utils::Duration minFrameDuration;
+ utils::Duration maxFrameDuration;
} agc;
struct {
- struct {
- struct {
- double red;
- double green;
- double blue;
- } manual;
- struct {
- double red;
- double green;
- double blue;
- } automatic;
- } gains;
+ struct AwbState {
+ RGB<double> gains;
+ unsigned int temperatureK;
+ };
+
+ AwbState manual;
+ AwbState automatic;
- unsigned int temperatureK;
bool autoEnabled;
} awb;
struct {
+ Matrix<float, 3, 3> manual;
+ Matrix<float, 3, 3> automatic;
+ } ccm;
+
+ struct {
int8_t brightness;
uint8_t contrast;
uint8_t saturation;
@@ -98,24 +119,34 @@ struct IPAActiveState {
uint8_t denoise;
uint8_t sharpness;
} filter;
+
+ struct {
+ double gamma;
+ } goc;
};
struct IPAFrameContext : public FrameContext {
struct {
uint32_t exposure;
double gain;
- bool autoEnabled;
+ uint32_t vblank;
+ bool autoExposureEnabled;
+ bool autoGainEnabled;
+ controls::AeConstraintModeEnum constraintMode;
+ controls::AeExposureModeEnum exposureMode;
+ controls::AeMeteringModeEnum meteringMode;
+ utils::Duration minFrameDuration;
+ utils::Duration maxFrameDuration;
+ utils::Duration frameDuration;
+ bool updateMetering;
+ bool autoExposureModeChange;
+ bool autoGainModeChange;
} agc;
struct {
- struct {
- double red;
- double green;
- double blue;
- } gains;
-
- unsigned int temperatureK;
+ RGB<double> gains;
bool autoEnabled;
+ unsigned int temperatureK;
} awb;
struct {
@@ -137,16 +168,43 @@ struct IPAFrameContext : public FrameContext {
} filter;
struct {
+ double gamma;
+ bool update;
+ } goc;
+
+ struct {
uint32_t exposure;
double gain;
} sensor;
+
+ struct {
+ Matrix<float, 3, 3> ccm;
+ } ccm;
+
+ struct {
+ double lux;
+ } lux;
};
struct IPAContext {
+ IPAContext(unsigned int frameContextSize)
+ : hw(nullptr), frameContexts(frameContextSize)
+ {
+ }
+
+ const IPAHwSettings *hw;
+ IPACameraSensorInfo sensorInfo;
IPASessionConfiguration configuration;
IPAActiveState activeState;
FCQueue<IPAFrameContext> frameContexts;
+
+ ControlInfoMap::Map ctrlMap;
+
+ DebugMetadata debugMetadata;
+
+ /* Interface to the Camera Helper */
+ std::unique_ptr<CameraSensorHelper> camHelper;
};
} /* namespace ipa::rkisp1 */
diff --git a/src/ipa/rkisp1/meson.build b/src/ipa/rkisp1/meson.build
index e813da53..26a9fa40 100644
--- a/src/ipa/rkisp1/meson.build
+++ b/src/ipa/rkisp1/meson.build
@@ -7,17 +7,16 @@ ipa_name = 'ipa_rkisp1'
rkisp1_ipa_sources = files([
'ipa_context.cpp',
+ 'params.cpp',
'rkisp1.cpp',
])
rkisp1_ipa_sources += rkisp1_ipa_algorithms
-mod = shared_module(ipa_name,
- [rkisp1_ipa_sources, libcamera_generated_ipa_headers],
+mod = shared_module(ipa_name, rkisp1_ipa_sources,
name_prefix : '',
- include_directories : [ipa_includes, libipa_includes],
- dependencies : libcamera_private,
- link_with : libipa,
+ include_directories : [ipa_includes],
+ dependencies : [libcamera_private, libipa_dep],
install : true,
install_dir : ipa_install_dir)
diff --git a/src/ipa/rkisp1/module.h b/src/ipa/rkisp1/module.h
index 89f83208..69e9bc82 100644
--- a/src/ipa/rkisp1/module.h
+++ b/src/ipa/rkisp1/module.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Ideas On Board
*
- * module.h - RkISP1 IPA Module
+ * RkISP1 IPA Module
*/
#pragma once
@@ -14,13 +14,14 @@
#include <libipa/module.h>
#include "ipa_context.h"
+#include "params.h"
namespace libcamera {
namespace ipa::rkisp1 {
using Module = ipa::Module<IPAContext, IPAFrameContext, IPACameraSensorInfo,
- rkisp1_params_cfg, rkisp1_stat_buffer>;
+ RkISP1Params, rkisp1_stat_buffer>;
} /* namespace ipa::rkisp1 */
diff --git a/src/ipa/rkisp1/params.cpp b/src/ipa/rkisp1/params.cpp
new file mode 100644
index 00000000..4c0b051c
--- /dev/null
+++ b/src/ipa/rkisp1/params.cpp
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 ISP Parameters
+ */
+
+#include "params.h"
+
+#include <map>
+#include <stddef.h>
+#include <string.h>
+
+#include <linux/rkisp1-config.h>
+#include <linux/videodev2.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(RkISP1Params)
+
+namespace ipa::rkisp1 {
+
+namespace {
+
+struct BlockTypeInfo {
+ enum rkisp1_ext_params_block_type type;
+ size_t size;
+ size_t offset;
+ uint32_t enableBit;
+};
+
+#define RKISP1_BLOCK_TYPE_ENTRY(block, id, type, category, bit) \
+ { BlockType::block, { \
+ RKISP1_EXT_PARAMS_BLOCK_TYPE_##id, \
+ sizeof(struct rkisp1_cif_isp_##type##_config), \
+ offsetof(struct rkisp1_params_cfg, category.type##_config), \
+ RKISP1_CIF_ISP_MODULE_##bit, \
+ } }
+
+#define RKISP1_BLOCK_TYPE_ENTRY_MEAS(block, id, type) \
+ RKISP1_BLOCK_TYPE_ENTRY(block, id##_MEAS, type, meas, id)
+
+#define RKISP1_BLOCK_TYPE_ENTRY_OTHERS(block, id, type) \
+ RKISP1_BLOCK_TYPE_ENTRY(block, id, type, others, id)
+
+#define RKISP1_BLOCK_TYPE_ENTRY_EXT(block, id, type) \
+ { BlockType::block, { \
+ RKISP1_EXT_PARAMS_BLOCK_TYPE_##id, \
+ sizeof(struct rkisp1_cif_isp_##type##_config), \
+ 0, 0, \
+ } }
+
+const std::map<BlockType, BlockTypeInfo> kBlockTypeInfo = {
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Bls, BLS, bls),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Dpcc, DPCC, dpcc),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Sdg, SDG, sdg),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(AwbGain, AWB_GAIN, awb_gain),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Flt, FLT, flt),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Bdm, BDM, bdm),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Ctk, CTK, ctk),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Goc, GOC, goc),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Dpf, DPF, dpf),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(DpfStrength, DPF_STRENGTH, dpf_strength),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Cproc, CPROC, cproc),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Ie, IE, ie),
+ RKISP1_BLOCK_TYPE_ENTRY_OTHERS(Lsc, LSC, lsc),
+ RKISP1_BLOCK_TYPE_ENTRY_MEAS(Awb, AWB, awb_meas),
+ RKISP1_BLOCK_TYPE_ENTRY_MEAS(Hst, HST, hst),
+ RKISP1_BLOCK_TYPE_ENTRY_MEAS(Aec, AEC, aec),
+ RKISP1_BLOCK_TYPE_ENTRY_MEAS(Afc, AFC, afc),
+ RKISP1_BLOCK_TYPE_ENTRY_EXT(CompandBls, COMPAND_BLS, compand_bls),
+ RKISP1_BLOCK_TYPE_ENTRY_EXT(CompandExpand, COMPAND_EXPAND, compand_curve),
+ RKISP1_BLOCK_TYPE_ENTRY_EXT(CompandCompress, COMPAND_COMPRESS, compand_curve),
+};
+
+} /* namespace */
+
+RkISP1ParamsBlockBase::RkISP1ParamsBlockBase(RkISP1Params *params, BlockType type,
+ const Span<uint8_t> &data)
+ : params_(params), type_(type)
+{
+ if (params_->format() == V4L2_META_FMT_RK_ISP1_EXT_PARAMS) {
+ header_ = data.subspan(0, sizeof(rkisp1_ext_params_block_header));
+ data_ = data.subspan(sizeof(rkisp1_ext_params_block_header));
+ } else {
+ data_ = data;
+ }
+}
+
+void RkISP1ParamsBlockBase::setEnabled(bool enabled)
+{
+ /*
+ * For the legacy fixed format, blocks are enabled in the top-level
+ * header. Delegate to the RkISP1Params class.
+ */
+ if (params_->format() == V4L2_META_FMT_RK_ISP1_PARAMS)
+ return params_->setBlockEnabled(type_, enabled);
+
+ /*
+ * For the extensible format, set the enable and disable flags in the
+ * block header directly.
+ */
+ struct rkisp1_ext_params_block_header *header =
+ reinterpret_cast<struct rkisp1_ext_params_block_header *>(header_.data());
+ header->flags &= ~(RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE |
+ RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE);
+ header->flags |= enabled ? RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE
+ : RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE;
+}
+
+RkISP1Params::RkISP1Params(uint32_t format, Span<uint8_t> data)
+ : format_(format), data_(data), used_(0)
+{
+ if (format_ == V4L2_META_FMT_RK_ISP1_EXT_PARAMS) {
+ struct rkisp1_ext_params_cfg *cfg =
+ reinterpret_cast<struct rkisp1_ext_params_cfg *>(data.data());
+
+ cfg->version = RKISP1_EXT_PARAM_BUFFER_V1;
+ cfg->data_size = 0;
+
+ used_ += offsetof(struct rkisp1_ext_params_cfg, data);
+ } else {
+ memset(data.data(), 0, data.size());
+ used_ = sizeof(struct rkisp1_params_cfg);
+ }
+}
+
+void RkISP1Params::setBlockEnabled(BlockType type, bool enabled)
+{
+ const BlockTypeInfo &info = kBlockTypeInfo.at(type);
+
+ struct rkisp1_params_cfg *cfg =
+ reinterpret_cast<struct rkisp1_params_cfg *>(data_.data());
+ if (enabled)
+ cfg->module_ens |= info.enableBit;
+ else
+ cfg->module_ens &= ~info.enableBit;
+}
+
+Span<uint8_t> RkISP1Params::block(BlockType type)
+{
+ auto infoIt = kBlockTypeInfo.find(type);
+ if (infoIt == kBlockTypeInfo.end()) {
+ LOG(RkISP1Params, Error)
+ << "Invalid parameters block type "
+ << utils::to_underlying(type);
+ return {};
+ }
+
+ const BlockTypeInfo &info = infoIt->second;
+
+ /*
+ * For the legacy format, return a block referencing the fixed location
+ * of the data.
+ */
+ if (format_ == V4L2_META_FMT_RK_ISP1_PARAMS) {
+ /*
+ * Blocks available only in extended parameters have an offset
+ * of 0. Return nullptr in that case.
+ */
+ if (info.offset == 0) {
+ LOG(RkISP1Params, Error)
+ << "Block type " << utils::to_underlying(type)
+ << " unavailable in fixed parameters format";
+ return {};
+ }
+
+ struct rkisp1_params_cfg *cfg =
+ reinterpret_cast<struct rkisp1_params_cfg *>(data_.data());
+
+ cfg->module_cfg_update |= info.enableBit;
+ cfg->module_en_update |= info.enableBit;
+
+ return data_.subspan(info.offset, info.size);
+ }
+
+ /*
+ * For the extensible format, allocate memory for the block, including
+ * the header. Look up the block in the cache first. If an algorithm
+ * requests the same block type twice, it should get the same block.
+ */
+ auto cacheIt = blocks_.find(type);
+ if (cacheIt != blocks_.end())
+ return cacheIt->second;
+
+ /* Make sure we don't run out of space. */
+ size_t size = sizeof(struct rkisp1_ext_params_block_header)
+ + ((info.size + 7) & ~7);
+ if (size > data_.size() - used_) {
+ LOG(RkISP1Params, Error)
+ << "Out of memory to allocate block type "
+ << utils::to_underlying(type);
+ return {};
+ }
+
+ /* Allocate a new block, clear its memory, and initialize its header. */
+ Span<uint8_t> block = data_.subspan(used_, size);
+ used_ += size;
+
+ struct rkisp1_ext_params_cfg *cfg =
+ reinterpret_cast<struct rkisp1_ext_params_cfg *>(data_.data());
+ cfg->data_size += size;
+
+ memset(block.data(), 0, block.size());
+
+ struct rkisp1_ext_params_block_header *header =
+ reinterpret_cast<struct rkisp1_ext_params_block_header *>(block.data());
+ header->type = info.type;
+ header->size = block.size();
+
+ /* Update the cache. */
+ blocks_[type] = block;
+
+ return block;
+}
+
+} /* namespace ipa::rkisp1 */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/params.h b/src/ipa/rkisp1/params.h
new file mode 100644
index 00000000..40450e34
--- /dev/null
+++ b/src/ipa/rkisp1/params.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ *
+ * RkISP1 ISP Parameters
+ */
+
+#pragma once
+
+#include <map>
+#include <stdint.h>
+
+#include <linux/rkisp1-config.h>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/span.h>
+
+namespace libcamera {
+
+namespace ipa::rkisp1 {
+
+enum class BlockType {
+ Bls,
+ Dpcc,
+ Sdg,
+ AwbGain,
+ Flt,
+ Bdm,
+ Ctk,
+ Goc,
+ Dpf,
+ DpfStrength,
+ Cproc,
+ Ie,
+ Lsc,
+ Awb,
+ Hst,
+ Aec,
+ Afc,
+ CompandBls,
+ CompandExpand,
+ CompandCompress,
+};
+
+namespace details {
+
+template<BlockType B>
+struct block_type {
+};
+
+#define RKISP1_DEFINE_BLOCK_TYPE(blockType, blockStruct) \
+template<> \
+struct block_type<BlockType::blockType> { \
+ using type = struct rkisp1_cif_isp_##blockStruct##_config; \
+};
+
+RKISP1_DEFINE_BLOCK_TYPE(Bls, bls)
+RKISP1_DEFINE_BLOCK_TYPE(Dpcc, dpcc)
+RKISP1_DEFINE_BLOCK_TYPE(Sdg, sdg)
+RKISP1_DEFINE_BLOCK_TYPE(AwbGain, awb_gain)
+RKISP1_DEFINE_BLOCK_TYPE(Flt, flt)
+RKISP1_DEFINE_BLOCK_TYPE(Bdm, bdm)
+RKISP1_DEFINE_BLOCK_TYPE(Ctk, ctk)
+RKISP1_DEFINE_BLOCK_TYPE(Goc, goc)
+RKISP1_DEFINE_BLOCK_TYPE(Dpf, dpf)
+RKISP1_DEFINE_BLOCK_TYPE(DpfStrength, dpf_strength)
+RKISP1_DEFINE_BLOCK_TYPE(Cproc, cproc)
+RKISP1_DEFINE_BLOCK_TYPE(Ie, ie)
+RKISP1_DEFINE_BLOCK_TYPE(Lsc, lsc)
+RKISP1_DEFINE_BLOCK_TYPE(Awb, awb_meas)
+RKISP1_DEFINE_BLOCK_TYPE(Hst, hst)
+RKISP1_DEFINE_BLOCK_TYPE(Aec, aec)
+RKISP1_DEFINE_BLOCK_TYPE(Afc, afc)
+RKISP1_DEFINE_BLOCK_TYPE(CompandBls, compand_bls)
+RKISP1_DEFINE_BLOCK_TYPE(CompandExpand, compand_curve)
+RKISP1_DEFINE_BLOCK_TYPE(CompandCompress, compand_curve)
+
+} /* namespace details */
+
+class RkISP1Params;
+
+class RkISP1ParamsBlockBase
+{
+public:
+ RkISP1ParamsBlockBase(RkISP1Params *params, BlockType type,
+ const Span<uint8_t> &data);
+
+ Span<uint8_t> data() const { return data_; }
+
+ void setEnabled(bool enabled);
+
+private:
+ LIBCAMERA_DISABLE_COPY(RkISP1ParamsBlockBase)
+
+ RkISP1Params *params_;
+ BlockType type_;
+ Span<uint8_t> header_;
+ Span<uint8_t> data_;
+};
+
+template<BlockType B>
+class RkISP1ParamsBlock : public RkISP1ParamsBlockBase
+{
+public:
+ using Type = typename details::block_type<B>::type;
+
+ RkISP1ParamsBlock(RkISP1Params *params, const Span<uint8_t> &data)
+ : RkISP1ParamsBlockBase(params, B, data)
+ {
+ }
+
+ const Type *operator->() const
+ {
+ return reinterpret_cast<const Type *>(data().data());
+ }
+
+ Type *operator->()
+ {
+ return reinterpret_cast<Type *>(data().data());
+ }
+
+ const Type &operator*() const &
+ {
+ return *reinterpret_cast<const Type *>(data().data());
+ }
+
+ Type &operator*() &
+ {
+ return *reinterpret_cast<Type *>(data().data());
+ }
+};
+
+class RkISP1Params
+{
+public:
+ RkISP1Params(uint32_t format, Span<uint8_t> data);
+
+ template<BlockType B>
+ RkISP1ParamsBlock<B> block()
+ {
+ return RkISP1ParamsBlock<B>(this, block(B));
+ }
+
+ uint32_t format() const { return format_; }
+ size_t size() const { return used_; }
+
+private:
+ friend class RkISP1ParamsBlockBase;
+
+ Span<uint8_t> block(BlockType type);
+ void setBlockEnabled(BlockType type, bool enabled);
+
+ uint32_t format_;
+
+ Span<uint8_t> data_;
+ size_t used_;
+
+ std::map<BlockType, Span<uint8_t>> blocks_;
+};
+
+} /* namespace ipa::rkisp1 */
+
+} /* namespace libcamera*/
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index 6544c925..1ed7d7d9 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -2,12 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * rkisp1.cpp - RkISP1 Image Processing Algorithms
+ * RkISP1 Image Processing Algorithms
*/
#include <algorithm>
-#include <math.h>
-#include <queue>
+#include <array>
+#include <chrono>
#include <stdint.h>
#include <string.h>
@@ -18,20 +18,22 @@
#include <libcamera/base/log.h>
#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
#include <libcamera/framebuffer.h>
+#include <libcamera/request.h>
+
#include <libcamera/ipa/ipa_interface.h>
#include <libcamera/ipa/ipa_module_info.h>
#include <libcamera/ipa/rkisp1_ipa_interface.h>
-#include <libcamera/request.h>
#include "libcamera/internal/formats.h"
#include "libcamera/internal/mapped_framebuffer.h"
#include "libcamera/internal/yaml_parser.h"
#include "algorithms/algorithm.h"
-#include "libipa/camera_sensor_helper.h"
#include "ipa_context.h"
+#include "params.h"
namespace libcamera {
@@ -63,9 +65,9 @@ public:
void unmapBuffers(const std::vector<unsigned int> &ids) override;
void queueRequest(const uint32_t frame, const ControlList &controls) override;
- void fillParamsBuffer(const uint32_t frame, const uint32_t bufferId) override;
- void processStatsBuffer(const uint32_t frame, const uint32_t bufferId,
- const ControlList &sensorControls) override;
+ void computeParams(const uint32_t frame, const uint32_t bufferId) override;
+ void processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls) override;
protected:
std::string logPrefix() const override;
@@ -81,29 +83,39 @@ private:
ControlInfoMap sensorControls_;
- /* revision-specific data */
- rkisp1_cif_isp_version hwRevision_;
- unsigned int hwHistBinNMax_;
- unsigned int hwGammaOutMaxSamples_;
- unsigned int hwHistogramWeightGridsSize_;
-
- /* Interface to the Camera Helper */
- std::unique_ptr<CameraSensorHelper> camHelper_;
-
/* Local parameter storage */
struct IPAContext context_;
};
namespace {
+const IPAHwSettings ipaHwSettingsV10{
+ RKISP1_CIF_ISP_AE_MEAN_MAX_V10,
+ RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10,
+ RKISP1_CIF_ISP_HISTOGRAM_WEIGHT_GRIDS_SIZE_V10,
+ RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10,
+ false,
+};
+
+const IPAHwSettings ipaHwSettingsIMX8MP{
+ RKISP1_CIF_ISP_AE_MEAN_MAX_V10,
+ RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10,
+ RKISP1_CIF_ISP_HISTOGRAM_WEIGHT_GRIDS_SIZE_V10,
+ RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10,
+ true,
+};
+
+const IPAHwSettings ipaHwSettingsV12{
+ RKISP1_CIF_ISP_AE_MEAN_MAX_V12,
+ RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12,
+ RKISP1_CIF_ISP_HISTOGRAM_WEIGHT_GRIDS_SIZE_V12,
+ RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V12,
+ false,
+};
+
/* List of controls handled by the RkISP1 IPA */
const ControlInfoMap::Map rkisp1Controls{
- { &controls::AeEnable, ControlInfo(false, true) },
- { &controls::AwbEnable, ControlInfo(false, true) },
- { &controls::ColourGains, ControlInfo(0.0f, 3.996f, 1.0f) },
- { &controls::Brightness, ControlInfo(-1.0f, 0.993f, 0.0f) },
- { &controls::Contrast, ControlInfo(0.0f, 1.993f, 1.0f) },
- { &controls::Saturation, ControlInfo(0.0f, 1.993f, 1.0f) },
+ { &controls::DebugMetadataEnable, ControlInfo(false, true, false) },
{ &controls::Sharpness, ControlInfo(0.0f, 10.0f, 1.0f) },
{ &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) },
};
@@ -111,7 +123,7 @@ const ControlInfoMap::Map rkisp1Controls{
} /* namespace */
IPARkISP1::IPARkISP1()
- : context_({ {}, {}, { kMaxFrameContexts } })
+ : context_(kMaxFrameContexts)
{
}
@@ -128,14 +140,13 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
/* \todo Add support for other revisions */
switch (hwRevision) {
case RKISP1_V10:
- hwHistBinNMax_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V10;
- hwGammaOutMaxSamples_ = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10;
- hwHistogramWeightGridsSize_ = RKISP1_CIF_ISP_HISTOGRAM_WEIGHT_GRIDS_SIZE_V10;
+ context_.hw = &ipaHwSettingsV10;
+ break;
+ case RKISP1_V_IMX8MP:
+ context_.hw = &ipaHwSettingsIMX8MP;
break;
case RKISP1_V12:
- hwHistBinNMax_ = RKISP1_CIF_ISP_HIST_BIN_N_MAX_V12;
- hwGammaOutMaxSamples_ = RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V12;
- hwHistogramWeightGridsSize_ = RKISP1_CIF_ISP_HISTOGRAM_WEIGHT_GRIDS_SIZE_V12;
+ context_.hw = &ipaHwSettingsV12;
break;
default:
LOG(IPARkISP1, Error)
@@ -146,19 +157,18 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
LOG(IPARkISP1, Debug) << "Hardware revision is " << hwRevision;
- /* Cache the value to set it in configure. */
- hwRevision_ = static_cast<rkisp1_cif_isp_version>(hwRevision);
+ context_.sensorInfo = sensorInfo;
- camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
- if (!camHelper_) {
+ context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+ if (!context_.camHelper) {
LOG(IPARkISP1, Error)
<< "Failed to create camera sensor helper for "
<< settings.sensorModel;
return -ENODEV;
}
- context_.configuration.sensor.lineDuration = sensorInfo.minLineLength
- * 1.0s / sensorInfo.pixelRate;
+ context_.configuration.sensor.lineDuration =
+ sensorInfo.minLineLength * 1.0s / sensorInfo.pixelRate;
/* Load the tuning data file. */
File file(settings.configurationFile);
@@ -199,8 +209,7 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
int IPARkISP1::start()
{
- setControls(0);
-
+ /* \todo Properly handle startup controls. */
return 0;
}
@@ -232,8 +241,7 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
context_.activeState = {};
context_.frameContexts.clear();
- /* Set the hardware revision for the algorithms. */
- context_.configuration.hw.revision = hwRevision_;
+ context_.configuration.paramFormat = ipaConfig.paramFormat;
const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
const ControlInfo vBlank = sensorControls_.find(V4L2_CID_VBLANK)->second;
@@ -246,17 +254,19 @@ int IPARkISP1::configure(const IPAConfigInfo &ipaConfig,
/*
* When the AGC computes the new exposure values for a frame, it needs
- * to know the limits for shutter speed and analogue gain.
- * As it depends on the sensor, update it with the controls.
+ * to know the limits for exposure time and analogue gain. As it depends
+ * on the sensor, update it with the controls.
*
- * \todo take VBLANK into account for maximum shutter speed
+ * \todo take VBLANK into account for maximum exposure time
*/
- context_.configuration.sensor.minShutterSpeed =
+ context_.configuration.sensor.minExposureTime =
minExposure * context_.configuration.sensor.lineDuration;
- context_.configuration.sensor.maxShutterSpeed =
+ context_.configuration.sensor.maxExposureTime =
maxExposure * context_.configuration.sensor.lineDuration;
- context_.configuration.sensor.minAnalogueGain = camHelper_->gain(minGain);
- context_.configuration.sensor.maxAnalogueGain = camHelper_->gain(maxGain);
+ context_.configuration.sensor.minAnalogueGain =
+ context_.camHelper->gain(minGain);
+ context_.configuration.sensor.maxAnalogueGain =
+ context_.camHelper->gain(maxGain);
context_.configuration.raw = std::any_of(streamConfig.begin(), streamConfig.end(),
[](auto &cfg) -> bool {
@@ -314,6 +324,7 @@ void IPARkISP1::unmapBuffers(const std::vector<unsigned int> &ids)
void IPARkISP1::queueRequest(const uint32_t frame, const ControlList &controls)
{
IPAFrameContext &frameContext = context_.frameContexts.alloc(frame);
+ context_.debugMetadata.enableByControl(controls);
for (auto const &a : algorithms()) {
Algorithm *algo = static_cast<Algorithm *>(a.get());
@@ -323,25 +334,21 @@ void IPARkISP1::queueRequest(const uint32_t frame, const ControlList &controls)
}
}
-void IPARkISP1::fillParamsBuffer(const uint32_t frame, const uint32_t bufferId)
+void IPARkISP1::computeParams(const uint32_t frame, const uint32_t bufferId)
{
IPAFrameContext &frameContext = context_.frameContexts.get(frame);
- rkisp1_params_cfg *params =
- reinterpret_cast<rkisp1_params_cfg *>(
- mappedBuffers_.at(bufferId).planes()[0].data());
-
- /* Prepare parameters buffer. */
- memset(params, 0, sizeof(*params));
+ RkISP1Params params(context_.configuration.paramFormat,
+ mappedBuffers_.at(bufferId).planes()[0]);
for (auto const &algo : algorithms())
- algo->prepare(context_, frame, frameContext, params);
+ algo->prepare(context_, frame, frameContext, &params);
- paramsBufferReady.emit(frame);
+ paramsComputed.emit(frame, params.size());
}
-void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId,
- const ControlList &sensorControls)
+void IPARkISP1::processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls)
{
IPAFrameContext &frameContext = context_.frameContexts.get(frame);
@@ -357,7 +364,7 @@ void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId
frameContext.sensor.exposure =
sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();
frameContext.sensor.gain =
- camHelper_->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());
+ context_.camHelper->gain(sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>());
ControlList metadata(controls::controls);
@@ -370,6 +377,7 @@ void IPARkISP1::processStatsBuffer(const uint32_t frame, const uint32_t bufferId
setControls(frame);
+ context_.debugMetadata.moveEntries(metadata);
metadataReady.emit(frame, metadata);
}
@@ -394,9 +402,9 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo,
/* Compute the analogue gain limits. */
const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
- float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>());
- float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>());
- float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>());
+ float minGain = context_.camHelper->gain(v4l2Gain.min().get<int32_t>());
+ float maxGain = context_.camHelper->gain(v4l2Gain.max().get<int32_t>());
+ float defGain = context_.camHelper->gain(v4l2Gain.def().get<int32_t>());
ctrlMap.emplace(std::piecewise_construct,
std::forward_as_tuple(&controls::AnalogueGain),
std::forward_as_tuple(minGain, maxGain, defGain));
@@ -424,10 +432,11 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo,
frameDurations[i] = frameSize / (sensorInfo.pixelRate / 1000000U);
}
- ctrlMap[&controls::FrameDurationLimits] = ControlInfo(frameDurations[0],
- frameDurations[1],
- frameDurations[2]);
+ /* \todo Move this (and other agc-related controls) to agc */
+ context_.ctrlMap[&controls::FrameDurationLimits] =
+ ControlInfo(frameDurations[0], frameDurations[1], frameDurations[2]);
+ ctrlMap.insert(context_.ctrlMap.begin(), context_.ctrlMap.end());
*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
}
@@ -440,11 +449,17 @@ void IPARkISP1::setControls(unsigned int frame)
IPAFrameContext &frameContext = context_.frameContexts.get(frame);
uint32_t exposure = frameContext.agc.exposure;
- uint32_t gain = camHelper_->gainCode(frameContext.agc.gain);
+ uint32_t gain = context_.camHelper->gainCode(frameContext.agc.gain);
+ uint32_t vblank = frameContext.agc.vblank;
+
+ LOG(IPARkISP1, Debug)
+ << "Set controls for frame " << frame << ": exposure " << exposure
+ << ", gain " << frameContext.agc.gain << ", vblank " << vblank;
ControlList ctrls(sensorControls_);
ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));
ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));
+ ctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank));
setSensorControls.emit(frame, ctrls);
}
@@ -459,7 +474,7 @@ extern "C" {
const struct IPAModuleInfo ipaModuleInfo = {
IPA_MODULE_API_VERSION,
1,
- "PipelineHandlerRkISP1",
+ "rkisp1",
"rkisp1",
};
diff --git a/src/ipa/rpi/cam_helper/cam_helper.cpp b/src/ipa/rpi/cam_helper/cam_helper.cpp
index ddd5e9a4..a78db9c1 100644
--- a/src/ipa/rpi/cam_helper/cam_helper.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * cam_helper.cpp - helper information for different sensors
+ * helper information for different sensors
*/
#include <linux/videodev2.h>
@@ -156,17 +156,9 @@ void CamHelper::setCameraMode(const CameraMode &mode)
}
}
-void CamHelper::getDelays(int &exposureDelay, int &gainDelay,
- int &vblankDelay, int &hblankDelay) const
+void CamHelper::setHwConfig(const Controller::HardwareConfig &hwConfig)
{
- /*
- * These values are correct for many sensors. Other sensors will
- * need to over-ride this function.
- */
- exposureDelay = 2;
- gainDelay = 1;
- vblankDelay = 2;
- hblankDelay = 2;
+ hwConfig_ = hwConfig;
}
bool CamHelper::sensorEmbeddedDataPresent() const
@@ -241,7 +233,7 @@ void CamHelper::parseEmbeddedData(Span<const uint8_t> buffer,
return;
}
- deviceStatus.shutterSpeed = parsedDeviceStatus.shutterSpeed;
+ deviceStatus.exposureTime = parsedDeviceStatus.exposureTime;
deviceStatus.analogueGain = parsedDeviceStatus.analogueGain;
deviceStatus.frameLength = parsedDeviceStatus.frameLength;
deviceStatus.lineLength = parsedDeviceStatus.lineLength;
diff --git a/src/ipa/rpi/cam_helper/cam_helper.h b/src/ipa/rpi/cam_helper/cam_helper.h
index 58a4b202..4a826690 100644
--- a/src/ipa/rpi/cam_helper/cam_helper.h
+++ b/src/ipa/rpi/cam_helper/cam_helper.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * cam_helper.h - helper class providing camera information
+ * helper class providing camera information
*/
#pragma once
@@ -36,11 +36,6 @@ namespace RPiController {
* 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.
@@ -76,6 +71,7 @@ public:
CamHelper(std::unique_ptr<MdParser> parser, unsigned int frameIntegrationDiff);
virtual ~CamHelper();
void setCameraMode(const CameraMode &mode);
+ void setHwConfig(const Controller::HardwareConfig &hwConfig);
virtual void prepare(libcamera::Span<const uint8_t> buffer,
Metadata &metadata);
virtual void process(StatisticsPtr &stats, Metadata &metadata);
@@ -91,8 +87,6 @@ public:
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;
@@ -108,6 +102,7 @@ protected:
std::unique_ptr<MdParser> parser_;
CameraMode mode_;
+ Controller::HardwareConfig hwConfig_;
private:
/*
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp
index c3337ed0..ba01153e 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * cam_helper_imx219.cpp - camera helper for imx219 sensor
+ * camera helper for imx219 sensor
*/
#include <assert.h>
@@ -99,7 +99,7 @@ void CamHelperImx219::populateMetadata(const MdParser::RegisterMap &registers,
deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 +
registers.at(lineLengthLoReg));
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ deviceStatus.exposureTime = 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);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp
new file mode 100644
index 00000000..efc03193
--- /dev/null
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx283.cpp
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2024, Raspberry Pi Ltd
+ *
+ * cam_helper_Imx283.cpp - camera information for Imx283 sensor
+ */
+
+#include <assert.h>
+
+#include "cam_helper.h"
+
+using namespace RPiController;
+
+class CamHelperImx283 : public CamHelper
+{
+public:
+ CamHelperImx283();
+ uint32_t gainCode(double gain) const override;
+ double gain(uint32_t gainCode) 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 = 4;
+};
+
+/*
+ * Imx283 doesn't output metadata, so we have to use the delayed controls which
+ * works by counting frames.
+ */
+
+CamHelperImx283::CamHelperImx283()
+ : CamHelper({}, frameIntegrationDiff)
+{
+}
+
+uint32_t CamHelperImx283::gainCode(double gain) const
+{
+ return static_cast<uint32_t>(2048.0 - 2048.0 / gain);
+}
+
+double CamHelperImx283::gain(uint32_t gainCode) const
+{
+ return static_cast<double>(2048.0 / (2048 - gainCode));
+}
+
+unsigned int CamHelperImx283::hideFramesModeSwitch() const
+{
+ /* After a mode switch, we seem to get 1 bad frame. */
+ return 1;
+}
+
+static CamHelper *create()
+{
+ return new CamHelperImx283();
+}
+
+static RegisterCamHelper reg("imx283", &create);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp
index d98b51cd..c1aa8528 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp
@@ -2,10 +2,10 @@
/*
* Copyright (C) 2021, Raspberry Pi Ltd
*
- * cam_helper_imx290.cpp - camera helper for imx290 sensor
+ * camera helper for imx290 sensor
*/
-#include <math.h>
+#include <cmath>
#include "cam_helper.h"
@@ -17,8 +17,6 @@ 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 hideFramesStartup() const override;
unsigned int hideFramesModeSwitch() const override;
@@ -37,22 +35,13 @@ CamHelperImx290::CamHelperImx290()
uint32_t CamHelperImx290::gainCode(double gain) const
{
- int code = 66.6667 * log10(gain);
+ int code = 66.6667 * std::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;
+ return std::pow(10, 0.015 * gainCode);
}
unsigned int CamHelperImx290::hideFramesStartup() const
@@ -73,3 +62,5 @@ static CamHelper *create()
}
static RegisterCamHelper reg("imx290", &create);
+static RegisterCamHelper reg327("imx327", &create);
+static RegisterCamHelper reg462("imx462", &create);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp
index ecb845e7..ac7ee2ea 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * cam_helper_imx296.cpp - Camera helper for IMX296 sensor
+ * Camera helper for IMX296 sensor
*/
#include <algorithm>
@@ -23,8 +23,6 @@ public:
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;
@@ -66,15 +64,6 @@ Duration CamHelperImx296::exposure(uint32_t exposureLines,
return std::max<uint32_t>(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();
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx415.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx415.cpp
new file mode 100644
index 00000000..c0a09eee
--- /dev/null
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx415.cpp
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2025, Raspberry Pi Ltd
+ *
+ * camera helper for imx415 sensor
+ */
+
+#include <cmath>
+
+#include "cam_helper.h"
+
+using namespace RPiController;
+
+class CamHelperImx415 : public CamHelper
+{
+public:
+ CamHelperImx415();
+ uint32_t gainCode(double gain) const override;
+ double gain(uint32_t gainCode) const override;
+ unsigned int hideFramesStartup() 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 = 8;
+};
+
+CamHelperImx415::CamHelperImx415()
+ : CamHelper({}, frameIntegrationDiff)
+{
+}
+
+uint32_t CamHelperImx415::gainCode(double gain) const
+{
+ int code = 66.6667 * std::log10(gain);
+ return std::max(0, std::min(code, 0xf0));
+}
+
+double CamHelperImx415::gain(uint32_t gainCode) const
+{
+ return std::pow(10, 0.015 * gainCode);
+}
+
+unsigned int CamHelperImx415::hideFramesStartup() const
+{
+ /* On startup, we seem to get 1 bad frame. */
+ return 1;
+}
+
+unsigned int CamHelperImx415::hideFramesModeSwitch() const
+{
+ /* After a mode switch, we seem to get 1 bad frame. */
+ return 1;
+}
+
+static CamHelper *create()
+{
+ return new CamHelperImx415();
+}
+
+static RegisterCamHelper reg("imx415", &create);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp
index bc769ca7..a72ac67d 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * cam_helper_imx477.cpp - camera helper for imx477 sensor
+ * camera helper for imx477 sensor
*/
#include <algorithm>
@@ -51,8 +51,6 @@ public:
void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override;
std::pair<uint32_t, uint32_t> 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:
@@ -112,7 +110,7 @@ void CamHelperImx477::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m
DeviceStatus parsedDeviceStatus;
metadata.get("device.status", parsedDeviceStatus);
- parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed;
+ parsedDeviceStatus.exposureTime = deviceStatus.exposureTime;
parsedDeviceStatus.frameLength = deviceStatus.frameLength;
metadata.set("device.status", parsedDeviceStatus);
@@ -159,15 +157,6 @@ std::pair<uint32_t, uint32_t> CamHelperImx477::getBlanking(Duration &exposure,
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;
@@ -180,7 +169,7 @@ void CamHelperImx477::populateMetadata(const MdParser::RegisterMap &registers,
deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 +
registers.at(lineLengthLoReg));
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ deviceStatus.exposureTime = 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);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp
index c7262aa0..10cbea48 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp
@@ -3,7 +3,7 @@
* Based on cam_helper_imx477.cpp
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * cam_helper_imx519.cpp - camera helper for imx519 sensor
+ * camera helper for imx519 sensor
* Copyright (C) 2021, Arducam Technology co., Ltd.
*/
@@ -51,8 +51,6 @@ public:
void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override;
std::pair<uint32_t, uint32_t> 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:
@@ -112,7 +110,7 @@ void CamHelperImx519::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m
DeviceStatus parsedDeviceStatus;
metadata.get("device.status", parsedDeviceStatus);
- parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed;
+ parsedDeviceStatus.exposureTime = deviceStatus.exposureTime;
parsedDeviceStatus.frameLength = deviceStatus.frameLength;
metadata.set("device.status", parsedDeviceStatus);
@@ -159,15 +157,6 @@ std::pair<uint32_t, uint32_t> CamHelperImx519::getBlanking(Duration &exposure,
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;
@@ -180,7 +169,7 @@ void CamHelperImx519::populateMetadata(const MdParser::RegisterMap &registers,
deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 +
registers.at(lineLengthLoReg));
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ deviceStatus.exposureTime = 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);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp
index dce39cd5..6150909c 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
- * cam_helper_imx708.cpp - camera helper for imx708 sensor
+ * camera helper for imx708 sensor
*/
#include <cmath>
@@ -54,8 +54,6 @@ public:
void process(StatisticsPtr &stats, Metadata &metadata) override;
std::pair<uint32_t, uint32_t> 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;
@@ -66,7 +64,7 @@ private:
* Smallest difference between the frame length and integration time,
* in units of lines.
*/
- static constexpr int frameIntegrationDiff = 22;
+ static constexpr int frameIntegrationDiff = 48;
/* 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. */
@@ -155,7 +153,7 @@ void CamHelperImx708::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m
DeviceStatus parsedDeviceStatus;
metadata.get("device.status", parsedDeviceStatus);
- parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed;
+ parsedDeviceStatus.exposureTime = deviceStatus.exposureTime;
parsedDeviceStatus.frameLength = deviceStatus.frameLength;
metadata.set("device.status", parsedDeviceStatus);
@@ -208,15 +206,6 @@ std::pair<uint32_t, uint32_t> CamHelperImx708::getBlanking(Duration &exposure,
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;
@@ -255,7 +244,7 @@ void CamHelperImx708::populateMetadata(const MdParser::RegisterMap &registers,
deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 +
registers.at(lineLengthLoReg));
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ deviceStatus.exposureTime = 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);
@@ -269,7 +258,7 @@ bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len,
{
size_t step = bpp >> 1; /* bytes per PDAF grid entry */
- if (bpp < 10 || bpp > 12 || len < 194 * step || ptr[0] != 0 || ptr[1] >= 0x40) {
+ if (bpp < 10 || bpp > 14 || len < 194 * step || ptr[0] != 0 || ptr[1] >= 0x40) {
LOG(IPARPI, Error) << "PDAF data in unsupported format";
return false;
}
diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp
index 5a99083d..40d6b6d7 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * cam_helper_ov5647.cpp - camera information for ov5647 sensor
+ * camera information for ov5647 sensor
*/
#include <assert.h>
@@ -17,8 +17,6 @@ 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;
@@ -52,19 +50,6 @@ double CamHelperOv5647::gain(uint32_t gainCode) const
return static_cast<double>(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
{
/*
diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov64a40.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov64a40.cpp
new file mode 100644
index 00000000..980495a8
--- /dev/null
+++ b/src/ipa/rpi/cam_helper/cam_helper_ov64a40.cpp
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2021, Raspberry Pi Ltd
+ * Copyright (C) 2023, Ideas on Board Oy.
+ *
+ * camera information for ov64a40 sensor
+ */
+
+#include <assert.h>
+
+#include "cam_helper.h"
+
+using namespace RPiController;
+
+class CamHelperOv64a40 : public CamHelper
+{
+public:
+ CamHelperOv64a40();
+ uint32_t gainCode(double gain) const override;
+ double gain(uint32_t gainCode) const override;
+ double getModeSensitivity(const CameraMode &mode) const override;
+
+private:
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ static constexpr int frameIntegrationDiff = 32;
+};
+
+CamHelperOv64a40::CamHelperOv64a40()
+ : CamHelper({}, frameIntegrationDiff)
+{
+}
+
+uint32_t CamHelperOv64a40::gainCode(double gain) const
+{
+ return static_cast<uint32_t>(gain * 128.0);
+}
+
+double CamHelperOv64a40::gain(uint32_t gainCode) const
+{
+ return static_cast<double>(gainCode) / 128.0;
+}
+
+double CamHelperOv64a40::getModeSensitivity(const CameraMode &mode) const
+{
+ if (mode.binX >= 2 && mode.scaleX >= 4) {
+ return 4.0;
+ } else if (mode.binX >= 2 && mode.scaleX >= 2) {
+ return 2.0;
+ } else {
+ return 1.0;
+ }
+}
+
+static CamHelper *create()
+{
+ return new CamHelperOv64a40();
+}
+
+static RegisterCamHelper reg("ov64a40", &create);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov7251.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov7251.cpp
new file mode 100644
index 00000000..fc7b999f
--- /dev/null
+++ b/src/ipa/rpi/cam_helper/cam_helper_ov7251.cpp
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2021, Raspberry Pi Ltd
+ *
+ * camera information for ov7251 sensor
+ */
+
+#include <assert.h>
+
+#include "cam_helper.h"
+
+using namespace RPiController;
+
+class CamHelperOv7251 : public CamHelper
+{
+public:
+ CamHelperOv7251();
+ uint32_t gainCode(double gain) const override;
+ double gain(uint32_t gainCode) const override;
+
+private:
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ static constexpr int frameIntegrationDiff = 4;
+};
+
+/*
+ * OV7251 doesn't output metadata, so we have to use the "unicam parser" which
+ * works by counting frames.
+ */
+
+CamHelperOv7251::CamHelperOv7251()
+ : CamHelper({}, frameIntegrationDiff)
+{
+}
+
+uint32_t CamHelperOv7251::gainCode(double gain) const
+{
+ return static_cast<uint32_t>(gain * 16.0);
+}
+
+double CamHelperOv7251::gain(uint32_t gainCode) const
+{
+ return static_cast<double>(gainCode) / 16.0;
+}
+
+static CamHelper *create()
+{
+ return new CamHelperOv7251();
+}
+
+static RegisterCamHelper reg("ov7251", &create);
diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp
index 86c5bc4c..e93a4691 100644
--- a/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp
+++ b/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Raspberry Pi Ltd
*
- * cam_helper_ov9281.cpp - camera information for ov9281 sensor
+ * camera information for ov9281 sensor
*/
#include <assert.h>
@@ -17,15 +17,13 @@ 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;
+ static constexpr int frameIntegrationDiff = 25;
};
/*
@@ -48,16 +46,6 @@ double CamHelperOv9281::gain(uint32_t gainCode) const
return static_cast<double>(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();
diff --git a/src/ipa/rpi/cam_helper/md_parser.h b/src/ipa/rpi/cam_helper/md_parser.h
index 77d557aa..227c376c 100644
--- a/src/ipa/rpi/cam_helper/md_parser.h
+++ b/src/ipa/rpi/cam_helper/md_parser.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * md_parser.h - image sensor metadata parser interface
+ * image sensor metadata parser interface
*/
#pragma once
diff --git a/src/ipa/rpi/cam_helper/md_parser_smia.cpp b/src/ipa/rpi/cam_helper/md_parser_smia.cpp
index 210787ed..c7bdcf94 100644
--- a/src/ipa/rpi/cam_helper/md_parser_smia.cpp
+++ b/src/ipa/rpi/cam_helper/md_parser_smia.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * md_parser_smia.cpp - SMIA specification based embedded data parser
+ * SMIA specification based embedded data parser
*/
#include <libcamera/base/log.h>
@@ -86,10 +86,13 @@ MdParserSmia::ParseStatus MdParserSmia::findRegs(libcamera::Span<const uint8_t>
while (1) {
int tag = buffer[currentOffset++];
- if ((bitsPerPixel_ == 10 &&
- (currentOffset + 1 - currentLineStart) % 5 == 0) ||
- (bitsPerPixel_ == 12 &&
- (currentOffset + 1 - currentLineStart) % 3 == 0)) {
+ /* Non-dummy bytes come in even-sized blocks: skip can only ever follow tag */
+ while ((bitsPerPixel_ == 10 &&
+ (currentOffset + 1 - currentLineStart) % 5 == 0) ||
+ (bitsPerPixel_ == 12 &&
+ (currentOffset + 1 - currentLineStart) % 3 == 0) ||
+ (bitsPerPixel_ == 14 &&
+ (currentOffset - currentLineStart) % 7 >= 4)) {
if (buffer[currentOffset++] != RegSkip)
return BadDummy;
}
diff --git a/src/ipa/rpi/cam_helper/meson.build b/src/ipa/rpi/cam_helper/meson.build
index bdf2db8e..abf02147 100644
--- a/src/ipa/rpi/cam_helper/meson.build
+++ b/src/ipa/rpi/cam_helper/meson.build
@@ -4,11 +4,15 @@ rpi_ipa_cam_helper_sources = files([
'cam_helper.cpp',
'cam_helper_ov5647.cpp',
'cam_helper_imx219.cpp',
+ 'cam_helper_imx283.cpp',
'cam_helper_imx290.cpp',
'cam_helper_imx296.cpp',
+ 'cam_helper_imx415.cpp',
'cam_helper_imx477.cpp',
'cam_helper_imx519.cpp',
'cam_helper_imx708.cpp',
+ 'cam_helper_ov64a40.cpp',
+ 'cam_helper_ov7251.cpp',
'cam_helper_ov9281.cpp',
'md_parser_smia.cpp',
])
diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
index f28eb36b..e0a93daa 100644
--- a/src/ipa/rpi/common/ipa_base.cpp
+++ b/src/ipa/rpi/common/ipa_base.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2023, Raspberry Pi Ltd
*
- * ipa_base.cpp - Raspberry Pi IPA base class
+ * Raspberry Pi IPA base class
*/
#include "ipa_base.h"
@@ -24,6 +24,7 @@
#include "controller/ccm_status.h"
#include "controller/contrast_algorithm.h"
#include "controller/denoise_algorithm.h"
+#include "controller/hdr_algorithm.h"
#include "controller/lux_status.h"
#include "controller/sharpen_algorithm.h"
#include "controller/statistics.h"
@@ -54,9 +55,19 @@ constexpr Duration controllerMinFrameDuration = 1.0s / 30.0;
/* List of controls handled by the Raspberry Pi IPA */
const ControlInfoMap::Map ipaControls{
- { &controls::AeEnable, ControlInfo(false, true) },
- { &controls::ExposureTime, ControlInfo(0, 66666) },
- { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f) },
+ /* \todo Move this to the Camera class */
+ { &controls::AeEnable, ControlInfo(false, true, true) },
+ { &controls::ExposureTimeMode,
+ ControlInfo(static_cast<int32_t>(controls::ExposureTimeModeAuto),
+ static_cast<int32_t>(controls::ExposureTimeModeManual),
+ static_cast<int32_t>(controls::ExposureTimeModeAuto)) },
+ { &controls::ExposureTime,
+ ControlInfo(1, 66666, static_cast<int32_t>(defaultExposureTime.get<std::micro>())) },
+ { &controls::AnalogueGainMode,
+ ControlInfo(static_cast<int32_t>(controls::AnalogueGainModeAuto),
+ static_cast<int32_t>(controls::AnalogueGainModeManual),
+ static_cast<int32_t>(controls::AnalogueGainModeAuto)) },
+ { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f, 1.0f) },
{ &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) },
{ &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
{ &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
@@ -67,10 +78,14 @@ const ControlInfoMap::Map ipaControls{
{ &controls::AeFlickerPeriod, ControlInfo(100, 1000000) },
{ &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
{ &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
+ { &controls::HdrMode, ControlInfo(controls::HdrModeValues) },
{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.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) }
+ { &controls::FrameDurationLimits,
+ ControlInfo(INT64_C(33333), INT64_C(120000),
+ static_cast<int64_t>(defaultMinFrameDuration.get<std::micro>())) },
+ { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) },
+ { &controls::rpi::StatsOutputEnable, ControlInfo(false, true, false) },
};
/* IPA controls handled conditionally, if the sensor is not mono */
@@ -78,6 +93,7 @@ const ControlInfoMap::Map ipaColourControls{
{ &controls::AwbEnable, ControlInfo(false, true) },
{ &controls::AwbMode, ControlInfo(controls::AwbModeValues) },
{ &controls::ColourGains, ControlInfo(0.0f, 32.0f) },
+ { &controls::ColourTemperature, ControlInfo(100, 100000) },
{ &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) },
};
@@ -93,6 +109,13 @@ const ControlInfoMap::Map ipaAfControls{
{ &controls::LensPosition, ControlInfo(0.0f, 32.0f, 1.0f) }
};
+/* Platform specific controls */
+const std::map<const std::string, ControlInfoMap::Map> platformControls {
+ { "pisp", {
+ { &controls::rpi::ScalerCrops, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }
+ } },
+};
+
} /* namespace */
LOG_DEFINE_CATEGORY(IPARPI)
@@ -100,8 +123,9 @@ LOG_DEFINE_CATEGORY(IPARPI)
namespace ipa::RPi {
IpaBase::IpaBase()
- : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), frameCount_(0),
- mistrustCount_(0), lastRunTimestamp_(0), firstStart_(true), flickerState_({ 0, 0s })
+ : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), statsMetadataOutput_(false),
+ stitchSwapBuffers_(false), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
+ firstStart_(true), flickerState_({ 0, 0s })
{
}
@@ -123,18 +147,8 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini
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;
+ /* Pass out the sensor metadata to the pipeline handler */
+ int sensorMetadata = helper_->sensorEmbeddedDataPresent();
result->sensorConfig.sensorMetadata = sensorMetadata;
/* Load the tuning file for this sensor. */
@@ -149,12 +163,17 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini
lensPresent_ = params.lensPresent;
controller_.initialise();
+ helper_->setHwConfig(controller_.getHardwareConfig());
/* Return the controls handled by the IPA */
ControlInfoMap::Map ctrlMap = ipaControls;
if (lensPresent_)
ctrlMap.merge(ControlInfoMap::Map(ipaAfControls));
+ auto platformCtrlsIt = platformControls.find(controller_.getTarget());
+ if (platformCtrlsIt != platformControls.end())
+ ctrlMap.merge(ControlInfoMap::Map(platformCtrlsIt->second));
+
monoSensor_ = params.sensorInfo.cfaPattern == properties::draft::ColorFilterArrangementEnum::MONO;
if (!monoSensor_)
ctrlMap.merge(ControlInfoMap::Map(ipaColourControls));
@@ -209,7 +228,7 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa
/* Supply initial values for gain and exposure. */
AgcStatus agcStatus;
- agcStatus.shutterTime = defaultExposureTime;
+ agcStatus.exposureTime = defaultExposureTime;
agcStatus.analogueGain = defaultAnalogueGain;
applyAGC(&agcStatus, ctrls);
@@ -243,15 +262,18 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa
ControlInfoMap::Map ctrlMap = ipaControls;
ctrlMap[&controls::FrameDurationLimits] =
ControlInfo(static_cast<int64_t>(mode_.minFrameDuration.get<std::micro>()),
- static_cast<int64_t>(mode_.maxFrameDuration.get<std::micro>()));
+ static_cast<int64_t>(mode_.maxFrameDuration.get<std::micro>()),
+ static_cast<int64_t>(defaultMinFrameDuration.get<std::micro>()));
ctrlMap[&controls::AnalogueGain] =
ControlInfo(static_cast<float>(mode_.minAnalogueGain),
- static_cast<float>(mode_.maxAnalogueGain));
+ static_cast<float>(mode_.maxAnalogueGain),
+ static_cast<float>(defaultAnalogueGain));
ctrlMap[&controls::ExposureTime] =
- ControlInfo(static_cast<int32_t>(mode_.minShutter.get<std::micro>()),
- static_cast<int32_t>(mode_.maxShutter.get<std::micro>()));
+ ControlInfo(static_cast<int32_t>(mode_.minExposureTime.get<std::micro>()),
+ static_cast<int32_t>(mode_.maxExposureTime.get<std::micro>()),
+ static_cast<int32_t>(defaultExposureTime.get<std::micro>()));
/* Declare colour processing related controls for non-mono sensors. */
if (!monoSensor_)
@@ -284,16 +306,18 @@ void IpaBase::start(const ControlList &controls, StartResult *result)
/* SwitchMode may supply updated exposure/gain values to use. */
AgcStatus agcStatus;
- agcStatus.shutterTime = 0.0s;
+ agcStatus.exposureTime = 0.0s;
agcStatus.analogueGain = 0.0;
metadata.get("agc.status", agcStatus);
- if (agcStatus.shutterTime && agcStatus.analogueGain) {
+ if (agcStatus.exposureTime && agcStatus.analogueGain) {
ControlList ctrls(sensorCtrls_);
applyAGC(&agcStatus, ctrls);
result->controls = std::move(ctrls);
setCameraTimeoutValue();
}
+ /* Make a note of this as it tells us the HDR status of the first few frames. */
+ hdrStatus_ = agcStatus.hdr;
/*
* Initialise frame counts, and decide how many frames must be hidden or
@@ -397,11 +421,17 @@ void IpaBase::prepareIsp(const PrepareParams &params)
* 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.
+ *
+ * Note if the HDR mode has changed, as things like tonemaps may need updating.
*/
AgcStatus agcStatus;
+ bool hdrChange = false;
RPiController::Metadata &delayedMetadata = rpiMetadata_[params.delayContext];
- if (!delayedMetadata.get<AgcStatus>("agc.status", agcStatus))
+ if (!delayedMetadata.get<AgcStatus>("agc.status", agcStatus)) {
rpiMetadata.set("agc.delayed_status", agcStatus);
+ hdrChange = agcStatus.hdr.mode != hdrStatus_.mode;
+ hdrStatus_ = agcStatus.hdr;
+ }
/*
* This may overwrite the DeviceStatus using values from the sensor
@@ -412,7 +442,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)
/* Allow a 10% margin on the comparison below. */
Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;
if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ &&
- delta < controllerMinFrameDuration * 0.9) {
+ delta < controllerMinFrameDuration * 0.9 && !hdrChange) {
/*
* Ensure we merge the previous frame's metadata with the current
* frame. This will not overwrite exposure/gain values for the
@@ -449,7 +479,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)
reportMetadata(ipaContext);
/* Ready to push the input buffer into the ISP. */
- prepareIspComplete.emit(params.buffers, false);
+ prepareIspComplete.emit(params.buffers, stitchSwapBuffers_);
}
void IpaBase::processStats(const ProcessParams &params)
@@ -532,6 +562,33 @@ void IpaBase::setMode(const IPACameraSensorInfo &sensorInfo)
mode_.maxLineLength = sensorInfo.maxLineLength * (1.0s / sensorInfo.pixelRate);
/*
+ * Ensure that the maximum pixel processing rate does not exceed the ISP
+ * hardware capabilities. If it does, try adjusting the minimum line
+ * length to compensate if possible.
+ */
+ Duration minPixelTime = controller_.getHardwareConfig().minPixelProcessingTime;
+ Duration pixelTime = mode_.minLineLength / mode_.width;
+ if (minPixelTime && pixelTime < minPixelTime) {
+ Duration adjustedLineLength = minPixelTime * mode_.width;
+ if (adjustedLineLength <= mode_.maxLineLength) {
+ LOG(IPARPI, Info)
+ << "Adjusting mode minimum line length from " << mode_.minLineLength
+ << " to " << adjustedLineLength << " because of ISP constraints.";
+ mode_.minLineLength = adjustedLineLength;
+ } else {
+ LOG(IPARPI, Error)
+ << "Sensor minimum line length of " << pixelTime * mode_.width
+ << " (" << 1us / pixelTime << " MPix/s)"
+ << " is below the minimum allowable ISP limit of "
+ << adjustedLineLength
+ << " (" << 1us / minPixelTime << " MPix/s) ";
+ LOG(IPARPI, Error)
+ << "THIS WILL CAUSE IMAGE CORRUPTION!!! "
+ << "Please update the camera sensor driver to allow more horizontal blanking control.";
+ }
+ }
+
+ /*
* Set the frame length limits for the mode to ensure exposure and
* framerate calculations are clipped appropriately.
*/
@@ -549,16 +606,26 @@ void IpaBase::setMode(const IPACameraSensorInfo &sensorInfo)
mode_.sensitivity = helper_->getModeSensitivity(mode_);
const ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN);
- const ControlInfo &shutterCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE);
+ const ControlInfo &exposureTimeCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE);
mode_.minAnalogueGain = helper_->gain(gainCtrl.min().get<int32_t>());
mode_.maxAnalogueGain = helper_->gain(gainCtrl.max().get<int32_t>());
- /* Shutter speed is calculated based on the limits of the frame durations. */
- mode_.minShutter = helper_->exposure(shutterCtrl.min().get<int32_t>(), mode_.minLineLength);
- mode_.maxShutter = Duration::max();
- helper_->getBlanking(mode_.maxShutter,
- mode_.minFrameDuration, mode_.maxFrameDuration);
+ /*
+ * We need to give the helper the min/max frame durations so it can calculate
+ * the correct exposure limits below.
+ */
+ helper_->setCameraMode(mode_);
+
+ /*
+ * Exposure time is calculated based on the limits of the frame
+ * durations.
+ */
+ mode_.minExposureTime = helper_->exposure(exposureTimeCtrl.min().get<int32_t>(),
+ mode_.minLineLength);
+ mode_.maxExposureTime = Duration::max();
+ helper_->getBlanking(mode_.maxExposureTime, mode_.minFrameDuration,
+ mode_.maxFrameDuration);
}
void IpaBase::setCameraTimeoutValue()
@@ -643,14 +710,6 @@ static const std::map<int32_t, std::string> AwbModeTable = {
{ controls::AwbCustom, "custom" },
};
-static const std::map<int32_t, RPiController::DenoiseMode> 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<int32_t, RPiController::AfAlgorithm::AfMode> AfModeTable = {
{ controls::AfModeManual, RPiController::AfAlgorithm::AfModeManual },
{ controls::AfModeAuto, RPiController::AfAlgorithm::AfModeAuto },
@@ -669,9 +728,21 @@ static const std::map<int32_t, RPiController::AfAlgorithm::AfPause> AfPauseTable
{ controls::AfPauseResume, RPiController::AfAlgorithm::AfPauseResume },
};
+static const std::map<int32_t, std::string> HdrModeTable = {
+ { controls::HdrModeOff, "Off" },
+ { controls::HdrModeMultiExposureUnmerged, "MultiExposureUnmerged" },
+ { controls::HdrModeMultiExposure, "MultiExposure" },
+ { controls::HdrModeSingleExposure, "SingleExposure" },
+ { controls::HdrModeNight, "Night" },
+};
+
void IpaBase::applyControls(const ControlList &controls)
{
+ using RPiController::AgcAlgorithm;
using RPiController::AfAlgorithm;
+ using RPiController::ContrastAlgorithm;
+ using RPiController::DenoiseAlgorithm;
+ using RPiController::HdrAlgorithm;
/* Clear the return metadata buffer. */
libcameraMetadata_.clear();
@@ -693,6 +764,42 @@ void IpaBase::applyControls(const ControlList &controls)
af->setMode(mode->second);
}
+ /*
+ * Because some AE controls are mode-specific, handle the AE-related
+ * mode changes first.
+ */
+ const auto analogueGainMode = controls.get(controls::AnalogueGainMode);
+ const auto exposureTimeMode = controls.get(controls::ExposureTimeMode);
+
+ if (analogueGainMode || exposureTimeMode) {
+ RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
+ controller_.getAlgorithm("agc"));
+ if (agc) {
+ if (analogueGainMode) {
+ if (*analogueGainMode == controls::AnalogueGainModeManual)
+ agc->disableAutoGain();
+ else
+ agc->enableAutoGain();
+
+ libcameraMetadata_.set(controls::AnalogueGainMode,
+ *analogueGainMode);
+ }
+
+ if (exposureTimeMode) {
+ if (*exposureTimeMode == controls::ExposureTimeModeManual)
+ agc->disableAutoExposure();
+ else
+ agc->enableAutoExposure();
+
+ libcameraMetadata_.set(controls::ExposureTimeMode,
+ *exposureTimeMode);
+ }
+ } else {
+ LOG(IPARPI, Warning)
+ << "Could not set AnalogueGainMode or ExposureTimeMode - no AGC algorithm";
+ }
+ }
+
/* Iterate over controls */
for (auto const &ctrl : controls) {
LOG(IPARPI, Debug) << "Request ctrl: "
@@ -700,23 +807,8 @@ void IpaBase::applyControls(const ControlList &controls)
<< " = " << ctrl.second.toString();
switch (ctrl.first) {
- case controls::AE_ENABLE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set AE_ENABLE - no AGC algorithm";
- break;
- }
-
- if (ctrl.second.get<bool>() == false)
- agc->disableAuto(0);
- else
- agc->enableAuto(0);
-
- libcameraMetadata_.set(controls::AeEnable, ctrl.second.get<bool>());
- break;
- }
+ case controls::EXPOSURE_TIME_MODE:
+ break; /* We already handled this one above */
case controls::EXPOSURE_TIME: {
RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
@@ -727,13 +819,23 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ /*
+ * Ignore manual exposure time when the auto exposure
+ * algorithm is running.
+ */
+ if (agc->autoExposureEnabled())
+ break;
+
/* The control provides units of microseconds. */
- agc->setFixedShutter(0, ctrl.second.get<int32_t>() * 1.0us);
+ agc->setFixedExposureTime(0, ctrl.second.get<int32_t>() * 1.0us);
libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get<int32_t>());
break;
}
+ case controls::ANALOGUE_GAIN_MODE:
+ break; /* We already handled this one above */
+
case controls::ANALOGUE_GAIN: {
RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
controller_.getAlgorithm("agc"));
@@ -743,6 +845,13 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ /*
+ * Ignore manual analogue gain value when the auto gain
+ * algorithm is running.
+ */
+ if (agc->autoGainEnabled())
+ break;
+
agc->setFixedAnalogueGain(0, ctrl.second.get<float>());
libcameraMetadata_.set(controls::AnalogueGain,
@@ -781,7 +890,7 @@ void IpaBase::applyControls(const ControlList &controls)
int32_t idx = ctrl.second.get<int32_t>();
if (ConstraintModeTable.count(idx)) {
- agc->setConstraintMode(0, ConstraintModeTable.at(idx));
+ agc->setConstraintMode(ConstraintModeTable.at(idx));
libcameraMetadata_.set(controls::AeConstraintMode, idx);
} else {
LOG(IPARPI, Error) << "Constraint mode " << idx
@@ -799,9 +908,16 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ /*
+ * Ignore AE_EXPOSURE_MODE if the shutter or the gain
+ * are in auto mode.
+ */
+ if (agc->autoExposureEnabled() || agc->autoGainEnabled())
+ break;
+
int32_t idx = ctrl.second.get<int32_t>();
if (ExposureModeTable.count(idx)) {
- agc->setExposureMode(0, ExposureModeTable.at(idx));
+ agc->setExposureMode(ExposureModeTable.at(idx));
libcameraMetadata_.set(controls::AeExposureMode, idx);
} else {
LOG(IPARPI, Error) << "Exposure mode " << idx
@@ -830,6 +946,17 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ case controls::AE_ENABLE: {
+ /*
+ * The AeEnable control is now just a wrapper that will already have been
+ * converted to ExposureTimeMode and AnalogueGainMode equivalents, so there
+ * would be nothing to do here. Nonetheless, "handle" the control so as to
+ * avoid warnings from the "default:" clause of the switch statement.
+ */
+
+ break;
+ }
+
case controls::AE_FLICKER_MODE: {
RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
controller_.getAlgorithm("agc"));
@@ -844,12 +971,12 @@ void IpaBase::applyControls(const ControlList &controls)
switch (mode) {
case controls::FlickerOff:
- agc->setFlickerPeriod(0, 0us);
+ agc->setFlickerPeriod(0us);
break;
case controls::FlickerManual:
- agc->setFlickerPeriod(0, flickerState_.manualPeriod);
+ agc->setFlickerPeriod(flickerState_.manualPeriod);
break;
@@ -883,7 +1010,7 @@ void IpaBase::applyControls(const ControlList &controls)
* first, and the period updated after, or vice versa.
*/
if (flickerState_.mode == controls::FlickerManual)
- agc->setFlickerPeriod(0, flickerState_.manualPeriod);
+ agc->setFlickerPeriod(flickerState_.manualPeriod);
break;
}
@@ -957,6 +1084,25 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ case controls::COLOUR_TEMPERATURE: {
+ /* Silently ignore this control for a mono sensor. */
+ if (monoSensor_)
+ break;
+
+ auto temperatureK = ctrl.second.get<int32_t>();
+ RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
+ controller_.getAlgorithm("awb"));
+ if (!awb) {
+ LOG(IPARPI, Warning)
+ << "Could not set COLOUR_TEMPERATURE - no AWB algorithm";
+ break;
+ }
+
+ awb->setColourTemperature(temperatureK);
+ /* This metadata will get reported back automatically. */
+ break;
+ }
+
case controls::BRIGHTNESS: {
RPiController::ContrastAlgorithm *contrast = dynamic_cast<RPiController::ContrastAlgorithm *>(
controller_.getAlgorithm("contrast"));
@@ -1021,6 +1167,7 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ case controls::rpi::SCALER_CROPS:
case controls::SCALER_CROP: {
/* We do nothing with this, but should avoid the warning below. */
break;
@@ -1032,36 +1179,11 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
- case controls::NOISE_REDUCTION_MODE: {
- RPiController::DenoiseAlgorithm *sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(
- controller_.getAlgorithm("SDN"));
- /* Some platforms may have a combined "denoise" algorithm instead. */
- if (!sdn)
- sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(
- controller_.getAlgorithm("denoise"));
- if (!sdn) {
- LOG(IPARPI, Warning)
- << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- 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";
- }
+ case controls::draft::NOISE_REDUCTION_MODE:
+ /* Handled below in handleControls() */
+ libcameraMetadata_.set(controls::draft::NoiseReductionMode,
+ ctrl.second.get<int32_t>());
break;
- }
case controls::AF_MODE:
break; /* We already handled this one above */
@@ -1168,6 +1290,61 @@ void IpaBase::applyControls(const ControlList &controls)
break;
}
+ case controls::HDR_MODE: {
+ HdrAlgorithm *hdr = dynamic_cast<HdrAlgorithm *>(controller_.getAlgorithm("hdr"));
+ if (!hdr) {
+ LOG(IPARPI, Warning) << "No HDR algorithm available";
+ break;
+ }
+
+ auto mode = HdrModeTable.find(ctrl.second.get<int32_t>());
+ if (mode == HdrModeTable.end()) {
+ LOG(IPARPI, Warning) << "Unrecognised HDR mode";
+ break;
+ }
+
+ AgcAlgorithm *agc = dynamic_cast<AgcAlgorithm *>(controller_.getAlgorithm("agc"));
+ if (!agc) {
+ LOG(IPARPI, Warning) << "HDR requires an AGC algorithm";
+ break;
+ }
+
+ if (hdr->setMode(mode->second) == 0) {
+ agc->setActiveChannels(hdr->getChannels());
+
+ /* We also disable adpative contrast enhancement if HDR is running. */
+ ContrastAlgorithm *contrast =
+ dynamic_cast<ContrastAlgorithm *>(controller_.getAlgorithm("contrast"));
+ if (contrast) {
+ if (mode->second == "Off")
+ contrast->restoreCe();
+ else
+ contrast->enableCe(false);
+ }
+
+ DenoiseAlgorithm *denoise =
+ dynamic_cast<DenoiseAlgorithm *>(controller_.getAlgorithm("denoise"));
+ if (denoise) {
+ /* \todo - make the HDR mode say what denoise it wants? */
+ if (mode->second == "Night")
+ denoise->setConfig("night");
+ else if (mode->second == "SingleExposure")
+ denoise->setConfig("hdr");
+ /* MultiExposure doesn't need extra extra denoise. */
+ else
+ denoise->setConfig("normal");
+ }
+ } else
+ LOG(IPARPI, Warning)
+ << "HDR mode " << mode->second << " not supported";
+
+ break;
+ }
+
+ case controls::rpi::STATS_OUTPUT_ENABLE:
+ statsMetadataOutput_ = ctrl.second.get<bool>();
+ break;
+
default:
LOG(IPARPI, Warning)
<< "Ctrl " << controls::controls.at(ctrl.first)->name()
@@ -1190,7 +1367,7 @@ void IpaBase::fillDeviceStatus(const ControlList &sensorControls, unsigned int i
int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get<int32_t>();
deviceStatus.lineLength = helper_->hblankToLineLength(hblank);
- deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength);
+ deviceStatus.exposureTime = helper_->exposure(exposureLines, deviceStatus.lineLength);
deviceStatus.analogueGain = helper_->gain(gainCode);
deviceStatus.frameLength = mode_.height + vblank;
@@ -1217,7 +1394,7 @@ void IpaBase::reportMetadata(unsigned int ipaContext)
DeviceStatus *deviceStatus = rpiMetadata.getLocked<DeviceStatus>("device.status");
if (deviceStatus) {
libcameraMetadata_.set(controls::ExposureTime,
- deviceStatus->shutterSpeed.get<std::micro>());
+ deviceStatus->exposureTime.get<std::micro>());
libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain);
libcameraMetadata_.set(controls::FrameDuration,
helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get<std::micro>());
@@ -1228,9 +1405,19 @@ void IpaBase::reportMetadata(unsigned int ipaContext)
}
AgcPrepareStatus *agcPrepareStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status");
- if (agcPrepareStatus) {
- libcameraMetadata_.set(controls::AeLocked, agcPrepareStatus->locked);
+ if (agcPrepareStatus)
libcameraMetadata_.set(controls::DigitalGain, agcPrepareStatus->digitalGain);
+
+ RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
+ controller_.getAlgorithm("agc"));
+ if (agc) {
+ if (!agc->autoExposureEnabled() && !agc->autoGainEnabled())
+ libcameraMetadata_.set(controls::AeState, controls::AeStateIdle);
+ else if (agcPrepareStatus)
+ libcameraMetadata_.set(controls::AeState,
+ agcPrepareStatus->locked
+ ? controls::AeStateConverged
+ : controls::AeStateSearching);
}
LuxStatus *luxStatus = rpiMetadata.getLocked<LuxStatus>("lux.status");
@@ -1315,6 +1502,35 @@ void IpaBase::reportMetadata(unsigned int ipaContext)
libcameraMetadata_.set(controls::AfPauseState, p);
}
+ /*
+ * THe HDR algorithm sets the HDR channel into the agc.status at the time that those
+ * AGC parameters were calculated several frames ago, so it comes back to us now in
+ * the delayed_status. If this frame is too soon after a mode switch for the
+ * delayed_status to be available, we use the HDR status that came out of the
+ * switchMode call.
+ */
+ const AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
+ const HdrStatus &hdrStatus = agcStatus ? agcStatus->hdr : hdrStatus_;
+ if (!hdrStatus.mode.empty() && hdrStatus.mode != "Off") {
+ int32_t hdrMode = controls::HdrModeOff;
+ for (auto const &[mode, name] : HdrModeTable) {
+ if (hdrStatus.mode == name) {
+ hdrMode = mode;
+ break;
+ }
+ }
+ libcameraMetadata_.set(controls::HdrMode, hdrMode);
+
+ if (hdrStatus.channel == "short")
+ libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelShort);
+ else if (hdrStatus.channel == "long")
+ libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelLong);
+ else if (hdrStatus.channel == "medium")
+ libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelMedium);
+ else
+ libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelNone);
+ }
+
metadataReady.emit(libcameraMetadata_);
}
@@ -1339,15 +1555,15 @@ void IpaBase::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDu
/*
* Calculate the maximum exposure time possible for the AGC to use.
- * getBlanking() will update maxShutter with the largest exposure
+ * getBlanking() will update maxExposureTime with the largest exposure
* value possible.
*/
- Duration maxShutter = Duration::max();
- helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_);
+ Duration maxExposureTime = Duration::max();
+ helper_->getBlanking(maxExposureTime, minFrameDuration_, maxFrameDuration_);
RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
controller_.getAlgorithm("agc"));
- agc->setMaxShutter(maxShutter);
+ agc->setMaxExposureTime(maxExposureTime);
}
void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
@@ -1364,14 +1580,14 @@ void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
gainCode = std::clamp<int32_t>(gainCode, minGainCode, maxGainCode);
/* getBlanking might clip exposure time to the fps limits. */
- Duration exposure = agcStatus->shutterTime;
+ Duration exposure = agcStatus->exposureTime;
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: "
+ << " (Exposure lines: " << exposureLines << ", AGC requested "
+ << agcStatus->exposureTime << ") Gain: "
<< agcStatus->analogueGain << " (Gain Code: "
<< gainCode << ")";
diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
index eaa9f711..1a811beb 100644
--- a/src/ipa/rpi/common/ipa_base.h
+++ b/src/ipa/rpi/common/ipa_base.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Raspberry Pi Ltd
*
- * ipa_base.h - Raspberry Pi IPA base class
+ * Raspberry Pi IPA base class
*/
#pragma once
@@ -22,6 +22,7 @@
#include "controller/agc_status.h"
#include "controller/camera_mode.h"
#include "controller/controller.h"
+#include "controller/hdr_status.h"
#include "controller/metadata.h"
namespace libcamera {
@@ -48,6 +49,11 @@ public:
void processStats(const ProcessParams &params) override;
protected:
+ bool monoSensor() const
+ {
+ return monoSensor_;
+ }
+
/* Raspberry Pi controller specific defines. */
std::unique_ptr<RPiController::CamHelper> helper_;
RPiController::Controller controller_;
@@ -61,6 +67,14 @@ protected:
/* Track the frame length times over FrameLengthsQueueSize frames. */
std::deque<utils::Duration> frameLengths_;
utils::Duration lastTimeout_;
+ ControlList libcameraMetadata_;
+ bool statsMetadataOutput_;
+
+ /* Remember the HDR status after a mode switch. */
+ HdrStatus hdrStatus_;
+
+ /* Whether the stitch block (if available) needs to swap buffers. */
+ bool stitchSwapBuffers_;
private:
/* Number of metadata objects available in the context list. */
@@ -89,7 +103,6 @@ private:
bool lensPresent_;
bool monoSensor_;
- ControlList libcameraMetadata_;
std::array<RPiController::Metadata, numMetadataContexts> rpiMetadata_;
diff --git a/src/ipa/rpi/controller/af_status.h b/src/ipa/rpi/controller/af_status.h
index 92c08812..c1487cc4 100644
--- a/src/ipa/rpi/controller/af_status.h
+++ b/src/ipa/rpi/controller/af_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
- * af_status.h - AF control algorithm status
+ * AF control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/agc_algorithm.h b/src/ipa/rpi/controller/agc_algorithm.h
index b8986560..fdaa10e6 100644
--- a/src/ipa/rpi/controller/agc_algorithm.h
+++ b/src/ipa/rpi/controller/agc_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * agc_algorithm.h - AGC/AEC control algorithm interface
+ * AGC/AEC control algorithm interface
*/
#pragma once
@@ -22,17 +22,20 @@ public:
virtual unsigned int getConvergenceFrames() const = 0;
virtual std::vector<double> const &getWeights() const = 0;
virtual void setEv(unsigned int channel, double ev) = 0;
- virtual void setFlickerPeriod(unsigned int channel,
- libcamera::utils::Duration flickerPeriod) = 0;
- virtual void setFixedShutter(unsigned int channel,
- libcamera::utils::Duration fixedShutter) = 0;
- virtual void setMaxShutter(libcamera::utils::Duration maxShutter) = 0;
+ virtual void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) = 0;
+ virtual void setFixedExposureTime(unsigned int channel,
+ libcamera::utils::Duration fixedExposureTime) = 0;
+ virtual void setMaxExposureTime(libcamera::utils::Duration maxExposureTime) = 0;
virtual void setFixedAnalogueGain(unsigned int channel, double fixedAnalogueGain) = 0;
virtual void setMeteringMode(std::string const &meteringModeName) = 0;
- virtual void setExposureMode(unsigned int channel, std::string const &exposureModeName) = 0;
- virtual void setConstraintMode(unsigned int channel, std::string const &contraintModeName) = 0;
- virtual void enableAuto(unsigned int channel) = 0;
- virtual void disableAuto(unsigned int channel) = 0;
+ virtual void setExposureMode(std::string const &exposureModeName) = 0;
+ virtual void setConstraintMode(std::string const &contraintModeName) = 0;
+ virtual void enableAutoExposure() = 0;
+ virtual void disableAutoExposure() = 0;
+ virtual bool autoExposureEnabled() const = 0;
+ virtual void enableAutoGain() = 0;
+ virtual void disableAutoGain() = 0;
+ virtual bool autoGainEnabled() const = 0;
virtual void setActiveChannels(const std::vector<unsigned int> &activeChannels) = 0;
};
diff --git a/src/ipa/rpi/controller/agc_status.h b/src/ipa/rpi/controller/agc_status.h
index 68f89958..9308b156 100644
--- a/src/ipa/rpi/controller/agc_status.h
+++ b/src/ipa/rpi/controller/agc_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * agc_status.h - AGC/AEC control algorithm status
+ * AGC/AEC control algorithm status
*/
#pragma once
@@ -28,7 +28,7 @@
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;
+ libcamera::utils::Duration exposureTime;
double analogueGain;
std::string exposureMode;
std::string constraintMode;
@@ -36,7 +36,7 @@ struct AgcStatus {
double ev;
libcamera::utils::Duration flickerPeriod;
int floatingRegionEnable;
- libcamera::utils::Duration fixedShutter;
+ libcamera::utils::Duration fixedExposureTime;
double fixedAnalogueGain;
unsigned int channel;
HdrStatus hdr;
diff --git a/src/ipa/rpi/controller/algorithm.cpp b/src/ipa/rpi/controller/algorithm.cpp
index a957fde5..beed47a1 100644
--- a/src/ipa/rpi/controller/algorithm.cpp
+++ b/src/ipa/rpi/controller/algorithm.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * algorithm.cpp - ISP control algorithms
+ * ISP control algorithms
*/
#include "algorithm.h"
diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h
index 4aa814eb..1971bfdc 100644
--- a/src/ipa/rpi/controller/algorithm.h
+++ b/src/ipa/rpi/controller/algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * algorithm.h - ISP control algorithm interface
+ * ISP control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/alsc_status.h b/src/ipa/rpi/controller/alsc_status.h
index 49a9f4a0..329e8a37 100644
--- a/src/ipa/rpi/controller/alsc_status.h
+++ b/src/ipa/rpi/controller/alsc_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * alsc_status.h - ALSC (auto lens shading correction) control algorithm status
+ * ALSC (auto lens shading correction) control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/awb_algorithm.h b/src/ipa/rpi/controller/awb_algorithm.h
index 8462c4db..d941ed4e 100644
--- a/src/ipa/rpi/controller/awb_algorithm.h
+++ b/src/ipa/rpi/controller/awb_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * awb_algorithm.h - AWB control algorithm interface
+ * AWB control algorithm interface
*/
#pragma once
@@ -16,8 +16,10 @@ public:
AwbAlgorithm(Controller *controller) : Algorithm(controller) {}
/* An AWB algorithm must provide the following: */
virtual unsigned int getConvergenceFrames() const = 0;
+ virtual void initialValues(double &gainR, double &gainB) = 0;
virtual void setMode(std::string const &modeName) = 0;
virtual void setManualGains(double manualR, double manualB) = 0;
+ virtual void setColourTemperature(double temperatureK) = 0;
virtual void enableAuto() = 0;
virtual void disableAuto() = 0;
};
diff --git a/src/ipa/rpi/controller/awb_status.h b/src/ipa/rpi/controller/awb_status.h
index dd5a79e3..125df1a0 100644
--- a/src/ipa/rpi/controller/awb_status.h
+++ b/src/ipa/rpi/controller/awb_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * awb_status.h - AWB control algorithm status
+ * AWB control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/black_level_algorithm.h b/src/ipa/rpi/controller/black_level_algorithm.h
new file mode 100644
index 00000000..ce044e59
--- /dev/null
+++ b/src/ipa/rpi/controller/black_level_algorithm.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2023, Raspberry Pi Ltd
+ *
+ * black level control algorithm interface
+ */
+#pragma once
+
+#include "algorithm.h"
+
+namespace RPiController {
+
+class BlackLevelAlgorithm : public Algorithm
+{
+public:
+ BlackLevelAlgorithm(Controller *controller)
+ : Algorithm(controller) {}
+ /* A black level algorithm must provide the following: */
+ virtual void initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG,
+ uint16_t &blackLevelB) = 0;
+};
+
+} /* namespace RPiController */
diff --git a/src/ipa/rpi/controller/black_level_status.h b/src/ipa/rpi/controller/black_level_status.h
index fd5e4ccb..57a0705a 100644
--- a/src/ipa/rpi/controller/black_level_status.h
+++ b/src/ipa/rpi/controller/black_level_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * black_level_status.h - black level control algorithm status
+ * black level control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/cac_status.h b/src/ipa/rpi/controller/cac_status.h
index 475d4c5c..adffce41 100644
--- a/src/ipa/rpi/controller/cac_status.h
+++ b/src/ipa/rpi/controller/cac_status.h
@@ -6,8 +6,6 @@
*/
#pragma once
-#include "pwl.h"
-
struct CacStatus {
std::vector<double> lutRx;
std::vector<double> lutRy;
diff --git a/src/ipa/rpi/controller/camera_mode.h b/src/ipa/rpi/controller/camera_mode.h
index 63b11778..61162b32 100644
--- a/src/ipa/rpi/controller/camera_mode.h
+++ b/src/ipa/rpi/controller/camera_mode.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2020, Raspberry Pi Ltd
*
- * camera_mode.h - description of a particular operating mode of a sensor
+ * description of a particular operating mode of a sensor
*/
#pragma once
@@ -50,9 +50,9 @@ struct CameraMode {
double sensitivity;
/* pixel clock rate */
uint64_t pixelRate;
- /* Mode specific shutter speed limits */
- libcamera::utils::Duration minShutter;
- libcamera::utils::Duration maxShutter;
+ /* Mode specific exposure time limits */
+ libcamera::utils::Duration minExposureTime;
+ libcamera::utils::Duration maxExposureTime;
/* 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
index e2c4d771..6678ba75 100644
--- a/src/ipa/rpi/controller/ccm_algorithm.h
+++ b/src/ipa/rpi/controller/ccm_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * ccm_algorithm.h - CCM (colour correction matrix) control algorithm interface
+ * CCM (colour correction matrix) control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/ccm_status.h b/src/ipa/rpi/controller/ccm_status.h
index 5e28ee7c..c81bcd42 100644
--- a/src/ipa/rpi/controller/ccm_status.h
+++ b/src/ipa/rpi/controller/ccm_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * ccm_status.h - CCM (colour correction matrix) control algorithm status
+ * CCM (colour correction matrix) control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/contrast_algorithm.h b/src/ipa/rpi/controller/contrast_algorithm.h
index 895b36b0..2e983350 100644
--- a/src/ipa/rpi/controller/contrast_algorithm.h
+++ b/src/ipa/rpi/controller/contrast_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * contrast_algorithm.h - contrast (gamma) control algorithm interface
+ * contrast (gamma) control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/contrast_status.h b/src/ipa/rpi/controller/contrast_status.h
index fb9fe4ba..1f175872 100644
--- a/src/ipa/rpi/controller/contrast_status.h
+++ b/src/ipa/rpi/controller/contrast_status.h
@@ -2,11 +2,11 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * contrast_status.h - contrast (gamma) control algorithm status
+ * contrast (gamma) control algorithm status
*/
#pragma once
-#include "pwl.h"
+#include "libipa/pwl.h"
/*
* The "contrast" algorithm creates a gamma curve, optionally doing a little bit
@@ -14,7 +14,7 @@
*/
struct ContrastStatus {
- RPiController::Pwl gammaCurve;
+ libcamera::ipa::Pwl gammaCurve;
double brightness;
double contrast;
};
diff --git a/src/ipa/rpi/controller/controller.cpp b/src/ipa/rpi/controller/controller.cpp
index e62becd8..651fff63 100644
--- a/src/ipa/rpi/controller/controller.cpp
+++ b/src/ipa/rpi/controller/controller.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * controller.cpp - ISP controller
+ * ISP controller
*/
#include <assert.h>
@@ -17,6 +17,7 @@
using namespace RPiController;
using namespace libcamera;
+using namespace std::literals::chrono_literals;
LOG_DEFINE_CATEGORY(RPiController)
@@ -37,6 +38,8 @@ static const std::map<std::string, Controller::HardwareConfig> HardwareConfigMap
.numGammaPoints = 33,
.pipelineWidth = 13,
.statsInline = false,
+ .minPixelProcessingTime = 0s,
+ .dataBufferStrided = true,
}
},
{
@@ -51,6 +54,25 @@ static const std::map<std::string, Controller::HardwareConfig> HardwareConfigMap
.numGammaPoints = 64,
.pipelineWidth = 16,
.statsInline = true,
+
+ /*
+ * The constraint below is on the rate of pixels going
+ * from CSI2 peripheral to ISP-FE (400Mpix/s, plus tiny
+ * overheads per scanline, for which 380Mpix/s is a
+ * conservative bound).
+ *
+ * There is a 64kbit data FIFO before the bottleneck,
+ * which means that in all reasonable cases the
+ * constraint applies at a timescale >= 1 scanline, so
+ * adding horizontal blanking can prevent loss.
+ *
+ * If the backlog were to grow beyond 64kbit during a
+ * single scanline, there could still be loss. This
+ * could happen using 4 lanes at 1.5Gbps at 10bpp with
+ * frames wider than ~16,000 pixels.
+ */
+ .minPixelProcessingTime = 1.0us / 380,
+ .dataBufferStrided = false,
}
},
};
diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h
index 6e5f5952..fdb46557 100644
--- a/src/ipa/rpi/controller/controller.h
+++ b/src/ipa/rpi/controller/controller.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * controller.h - ISP controller interface
+ * ISP controller interface
*/
#pragma once
@@ -15,6 +15,7 @@
#include <vector>
#include <string>
+#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "camera_mode.h"
@@ -47,6 +48,8 @@ public:
unsigned int numGammaPoints;
unsigned int pipelineWidth;
bool statsInline;
+ libcamera::utils::Duration minPixelProcessingTime;
+ bool dataBufferStrided;
};
Controller();
diff --git a/src/ipa/rpi/controller/denoise_algorithm.h b/src/ipa/rpi/controller/denoise_algorithm.h
index 444cbc25..b9a2a33c 100644
--- a/src/ipa/rpi/controller/denoise_algorithm.h
+++ b/src/ipa/rpi/controller/denoise_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Raspberry Pi Ltd
*
- * denoise.h - Denoise control algorithm interface
+ * Denoise control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/denoise_status.h b/src/ipa/rpi/controller/denoise_status.h
index 4d2bd291..eead6086 100644
--- a/src/ipa/rpi/controller/denoise_status.h
+++ b/src/ipa/rpi/controller/denoise_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * denoise_status.h - Denoise control algorithm status
+ * Denoise control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/device_status.cpp b/src/ipa/rpi/controller/device_status.cpp
index c907efdd..1695764d 100644
--- a/src/ipa/rpi/controller/device_status.cpp
+++ b/src/ipa/rpi/controller/device_status.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Raspberry Pi Ltd
*
- * device_status.cpp - device (image sensor) status
+ * device (image sensor) status
*/
#include "device_status.h"
@@ -10,7 +10,7 @@ using namespace libcamera; /* for the Duration operator<< overload */
std::ostream &operator<<(std::ostream &out, const DeviceStatus &d)
{
- out << "Exposure: " << d.shutterSpeed
+ out << "Exposure time: " << d.exposureTime
<< " Frame length: " << d.frameLength
<< " Line length: " << d.lineLength
<< " Gain: " << d.analogueGain;
diff --git a/src/ipa/rpi/controller/device_status.h b/src/ipa/rpi/controller/device_status.h
index c45db749..b1792035 100644
--- a/src/ipa/rpi/controller/device_status.h
+++ b/src/ipa/rpi/controller/device_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * device_status.h - device (image sensor) status
+ * device (image sensor) status
*/
#pragma once
@@ -12,21 +12,21 @@
#include <libcamera/base/utils.h>
/*
- * Definition of "device metadata" which stores things like shutter time and
+ * Definition of "device metadata" which stores things like exposure time and
* analogue gain that downstream control algorithms will want to know.
*/
struct DeviceStatus {
DeviceStatus()
- : shutterSpeed(std::chrono::seconds(0)), frameLength(0),
+ : exposureTime(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;
+ /* time the image is exposed */
+ libcamera::utils::Duration exposureTime;
/* frame length given in number of lines */
uint32_t frameLength;
/* line length for the current frame */
diff --git a/src/ipa/rpi/controller/dpc_status.h b/src/ipa/rpi/controller/dpc_status.h
index 46d0cf34..9f30d5d9 100644
--- a/src/ipa/rpi/controller/dpc_status.h
+++ b/src/ipa/rpi/controller/dpc_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * dpc_status.h - DPC (defective pixel correction) control algorithm status
+ * DPC (defective pixel correction) control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/geq_status.h b/src/ipa/rpi/controller/geq_status.h
index 2d749fc9..cb107a48 100644
--- a/src/ipa/rpi/controller/geq_status.h
+++ b/src/ipa/rpi/controller/geq_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * geq_status.h - GEQ (green equalisation) control algorithm status
+ * GEQ (green equalisation) control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/hdr_algorithm.h b/src/ipa/rpi/controller/hdr_algorithm.h
index f622e099..b889d8fd 100644
--- a/src/ipa/rpi/controller/hdr_algorithm.h
+++ b/src/ipa/rpi/controller/hdr_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Raspberry Pi Ltd
*
- * hdr_algorithm.h - HDR control algorithm interface
+ * HDR control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/hdr_status.h b/src/ipa/rpi/controller/hdr_status.h
index 24b1a935..a4955778 100644
--- a/src/ipa/rpi/controller/hdr_status.h
+++ b/src/ipa/rpi/controller/hdr_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023 Raspberry Pi Ltd
*
- * hdr_status.h - HDR control algorithm status
+ * HDR control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/histogram.cpp b/src/ipa/rpi/controller/histogram.cpp
index 0a27ba2c..13089839 100644
--- a/src/ipa/rpi/controller/histogram.cpp
+++ b/src/ipa/rpi/controller/histogram.cpp
@@ -2,9 +2,9 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * histogram.cpp - histogram calculations
+ * histogram calculations
*/
-#include <math.h>
+#include <cmath>
#include <stdio.h>
#include "histogram.h"
@@ -47,23 +47,29 @@ double Histogram::quantile(double q, int first, int last) const
double Histogram::interBinMean(double binLo, double binHi) const
{
- assert(binHi > binLo);
+ assert(binHi >= binLo);
double sumBinFreq = 0, cumulFreq = 0;
- for (double binNext = floor(binLo) + 1.0; binNext <= ceil(binHi);
+ for (double binNext = std::floor(binLo) + 1.0; binNext <= std::ceil(binHi);
binLo = binNext, binNext += 1.0) {
- int bin = floor(binLo);
+ int bin = std::floor(binLo);
double freq = (cumulative_[bin + 1] - cumulative_[bin]) *
(std::min(binNext, binHi) - binLo);
sumBinFreq += bin * freq;
cumulFreq += freq;
}
+
+ if (cumulFreq == 0) {
+ /* interval had zero width or contained no weight? */
+ return binHi;
+ }
+
/* add 0.5 to give an average for bin mid-points */
return sumBinFreq / cumulFreq + 0.5;
}
double Histogram::interQuantileMean(double qLo, double qHi) const
{
- assert(qHi > qLo);
+ assert(qHi >= qLo);
double pLo = quantile(qLo);
double pHi = quantile(qHi, (int)pLo);
return interBinMean(pLo, pHi);
diff --git a/src/ipa/rpi/controller/histogram.h b/src/ipa/rpi/controller/histogram.h
index e2c5509b..ab4e5e31 100644
--- a/src/ipa/rpi/controller/histogram.h
+++ b/src/ipa/rpi/controller/histogram.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * histogram.h - histogram calculation interface
+ * histogram calculation interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/lux_status.h b/src/ipa/rpi/controller/lux_status.h
index 5eb9faac..d8729f43 100644
--- a/src/ipa/rpi/controller/lux_status.h
+++ b/src/ipa/rpi/controller/lux_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * lux_status.h - Lux control algorithm status
+ * Lux control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/meson.build b/src/ipa/rpi/controller/meson.build
index 32a4d31c..74b74888 100644
--- a/src/ipa/rpi/controller/meson.build
+++ b/src/ipa/rpi/controller/meson.build
@@ -5,7 +5,6 @@ rpi_ipa_controller_sources = files([
'controller.cpp',
'device_status.cpp',
'histogram.cpp',
- 'pwl.cpp',
'rpi/af.cpp',
'rpi/agc.cpp',
'rpi/agc_channel.cpp',
@@ -32,4 +31,5 @@ rpi_ipa_controller_deps = [
]
rpi_ipa_controller_lib = static_library('rpi_ipa_controller', rpi_ipa_controller_sources,
+ include_directories : libipa_includes,
dependencies : rpi_ipa_controller_deps)
diff --git a/src/ipa/rpi/controller/metadata.h b/src/ipa/rpi/controller/metadata.h
index a232dcb1..77d3b074 100644
--- a/src/ipa/rpi/controller/metadata.h
+++ b/src/ipa/rpi/controller/metadata.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * metadata.h - general metadata class
+ * general metadata class
*/
#pragma once
@@ -12,6 +12,7 @@
#include <map>
#include <mutex>
#include <string>
+#include <utility>
#include <libcamera/base/thread_annotations.h>
@@ -36,10 +37,10 @@ public:
}
template<typename T>
- void set(std::string const &tag, T const &value)
+ void set(std::string const &tag, T &&value)
{
std::scoped_lock lock(mutex_);
- data_[tag] = value;
+ data_[tag] = std::forward<T>(value);
}
template<typename T>
@@ -90,6 +91,12 @@ public:
data_.insert(other.data_.begin(), other.data_.end());
}
+ void erase(std::string const &tag)
+ {
+ std::scoped_lock lock(mutex_);
+ eraseLocked(tag);
+ }
+
template<typename T>
T *getLocked(std::string const &tag)
{
@@ -104,10 +111,18 @@ public:
}
template<typename T>
- void setLocked(std::string const &tag, T const &value)
+ void setLocked(std::string const &tag, T &&value)
{
/* Use this only if you're holding the lock yourself. */
- data_[tag] = value;
+ data_[tag] = std::forward<T>(value);
+ }
+
+ void eraseLocked(std::string const &tag)
+ {
+ auto it = data_.find(tag);
+ if (it == data_.end())
+ return;
+ data_.erase(it);
}
/*
diff --git a/src/ipa/rpi/controller/noise_status.h b/src/ipa/rpi/controller/noise_status.h
index da194f71..1919da32 100644
--- a/src/ipa/rpi/controller/noise_status.h
+++ b/src/ipa/rpi/controller/noise_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * noise_status.h - Noise control algorithm status
+ * Noise control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/pdaf_data.h b/src/ipa/rpi/controller/pdaf_data.h
index 470510f2..779b987d 100644
--- a/src/ipa/rpi/controller/pdaf_data.h
+++ b/src/ipa/rpi/controller/pdaf_data.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
- * pdaf_data.h - PDAF Metadata
+ * PDAF Metadata
*/
#pragma once
diff --git a/src/ipa/rpi/controller/pwl.cpp b/src/ipa/rpi/controller/pwl.cpp
deleted file mode 100644
index 70c2e24b..00000000
--- a/src/ipa/rpi/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 <cassert>
-#include <cmath>
-#include <stdexcept>
-
-#include "pwl.h"
-
-using namespace RPiController;
-
-int Pwl::read(const libcamera::YamlObject &params)
-{
- 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<double>();
- if (!x)
- return -EINVAL;
- if (it != list.begin() && *x <= points_.back().x)
- return -EINVAL;
-
- auto y = (++it)->get<double>();
- 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<void(double x, double y)> f) const
-{
- for (auto &pt : points_)
- f(pt.x, pt.y);
-}
-
-void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1,
- std::function<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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
deleted file mode 100644
index aacf6039..00000000
--- a/src/ipa/rpi/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 <functional>
-#include <math.h>
-#include <vector>
-
-#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<Point> const &points) : points_(points) {}
- int read(const libcamera::YamlObject &params);
- 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<void(double x, double y)> 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<void(double x, double y0, double y1)> 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<double(double x, double y0, double y1)> 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<Point> points_;
-};
-
-} /* namespace RPiController */
diff --git a/src/ipa/rpi/controller/region_stats.h b/src/ipa/rpi/controller/region_stats.h
index a8860dc8..c60f7d9a 100644
--- a/src/ipa/rpi/controller/region_stats.h
+++ b/src/ipa/rpi/controller/region_stats.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
- * region_stats.h - Raspberry Pi region based statistics container
+ * Raspberry Pi region based statistics container
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp
index ed0c8a94..2157eb94 100644
--- a/src/ipa/rpi/controller/rpi/af.cpp
+++ b/src/ipa/rpi/controller/rpi/af.cpp
@@ -2,13 +2,13 @@
/*
* Copyright (C) 2022-2023, Raspberry Pi Ltd
*
- * af.cpp - Autofocus control algorithm
+ * Autofocus control algorithm
*/
#include "af.h"
+#include <cmath>
#include <iomanip>
-#include <math.h>
#include <stdlib.h>
#include <libcamera/base/log.h>
@@ -139,7 +139,7 @@ int Af::CfgParams::read(const libcamera::YamlObject &params)
readNumber<uint32_t>(skipFrames, params, "skip_frames");
if (params.contains("map"))
- map.read(params["map"]);
+ map = params["map"].get<ipa::Pwl>(ipa::Pwl{});
else
LOG(RPiAf, Warning) << "No map defined";
@@ -721,7 +721,7 @@ bool Af::setLensPosition(double dioptres, int *hwpos)
if (mode_ == AfModeManual) {
LOG(RPiAf, Debug) << "setLensPosition: " << dioptres;
- ftarget_ = cfg_.map.domain().clip(dioptres);
+ ftarget_ = cfg_.map.domain().clamp(dioptres);
changed = !(initted_ && fsmooth_ == ftarget_);
updateLensPosition();
}
diff --git a/src/ipa/rpi/controller/rpi/af.h b/src/ipa/rpi/controller/rpi/af.h
index 6d2bae67..317a51f3 100644
--- a/src/ipa/rpi/controller/rpi/af.h
+++ b/src/ipa/rpi/controller/rpi/af.h
@@ -2,14 +2,15 @@
/*
* Copyright (C) 2022-2023, Raspberry Pi Ltd
*
- * af.h - Autofocus control algorithm
+ * Autofocus control algorithm
*/
#pragma once
#include "../af_algorithm.h"
#include "../af_status.h"
#include "../pdaf_data.h"
-#include "../pwl.h"
+
+#include "libipa/pwl.h"
/*
* This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.
@@ -100,7 +101,7 @@ private:
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 */
+ libcamera::ipa::Pwl map; /* converts dioptres -> lens driver position */
CfgParams();
int read(const libcamera::YamlObject &params);
diff --git a/src/ipa/rpi/controller/rpi/agc.cpp b/src/ipa/rpi/controller/rpi/agc.cpp
index 32eb3624..02bfdb4a 100644
--- a/src/ipa/rpi/controller/rpi/agc.cpp
+++ b/src/ipa/rpi/controller/rpi/agc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * agc.cpp - AGC/AEC control algorithm
+ * AGC/AEC control algorithm
*/
#include "agc.h"
@@ -74,22 +74,62 @@ int Agc::checkChannel(unsigned int channelIndex) const
return 0;
}
-void Agc::disableAuto(unsigned int channelIndex)
+void Agc::disableAutoExposure()
{
- if (checkChannel(channelIndex))
- return;
+ LOG(RPiAgc, Debug) << "disableAutoExposure";
- LOG(RPiAgc, Debug) << "disableAuto for channel " << channelIndex;
- channelData_[channelIndex].channel.disableAuto();
+ /* All channels are enabled/disabled together. */
+ for (auto &data : channelData_)
+ data.channel.disableAutoExposure();
}
-void Agc::enableAuto(unsigned int channelIndex)
+void Agc::enableAutoExposure()
{
- if (checkChannel(channelIndex))
- return;
+ LOG(RPiAgc, Debug) << "enableAutoExposure";
+
+ /* All channels are enabled/disabled together. */
+ for (auto &data : channelData_)
+ data.channel.enableAutoExposure();
+}
+
+bool Agc::autoExposureEnabled() const
+{
+ LOG(RPiAgc, Debug) << "autoExposureEnabled";
- LOG(RPiAgc, Debug) << "enableAuto for channel " << channelIndex;
- channelData_[channelIndex].channel.enableAuto();
+ /*
+ * We always have at least one channel, and since all channels are
+ * enabled and disabled together we can simply check the first one.
+ */
+ return channelData_[0].channel.autoExposureEnabled();
+}
+
+void Agc::disableAutoGain()
+{
+ LOG(RPiAgc, Debug) << "disableAutoGain";
+
+ /* All channels are enabled/disabled together. */
+ for (auto &data : channelData_)
+ data.channel.disableAutoGain();
+}
+
+void Agc::enableAutoGain()
+{
+ LOG(RPiAgc, Debug) << "enableAutoGain";
+
+ /* All channels are enabled/disabled together. */
+ for (auto &data : channelData_)
+ data.channel.enableAutoGain();
+}
+
+bool Agc::autoGainEnabled() const
+{
+ LOG(RPiAgc, Debug) << "autoGainEnabled";
+
+ /*
+ * We always have at least one channel, and since all channels are
+ * enabled and disabled together we can simply check the first one.
+ */
+ return channelData_[0].channel.autoGainEnabled();
}
unsigned int Agc::getConvergenceFrames() const
@@ -118,31 +158,30 @@ void Agc::setEv(unsigned int channelIndex, double ev)
channelData_[channelIndex].channel.setEv(ev);
}
-void Agc::setFlickerPeriod(unsigned int channelIndex, Duration flickerPeriod)
+void Agc::setFlickerPeriod(Duration flickerPeriod)
{
- if (checkChannel(channelIndex))
- return;
+ LOG(RPiAgc, Debug) << "setFlickerPeriod " << flickerPeriod;
- LOG(RPiAgc, Debug) << "setFlickerPeriod " << flickerPeriod
- << " for channel " << channelIndex;
- channelData_[channelIndex].channel.setFlickerPeriod(flickerPeriod);
+ /* Flicker period will be the same across all channels. */
+ for (auto &data : channelData_)
+ data.channel.setFlickerPeriod(flickerPeriod);
}
-void Agc::setMaxShutter(Duration maxShutter)
+void Agc::setMaxExposureTime(Duration maxExposureTime)
{
/* Frame durations will be the same across all channels too. */
for (auto &data : channelData_)
- data.channel.setMaxShutter(maxShutter);
+ data.channel.setMaxExposureTime(maxExposureTime);
}
-void Agc::setFixedShutter(unsigned int channelIndex, Duration fixedShutter)
+void Agc::setFixedExposureTime(unsigned int channelIndex, Duration fixedExposureTime)
{
if (checkChannel(channelIndex))
return;
- LOG(RPiAgc, Debug) << "setFixedShutter " << fixedShutter
+ LOG(RPiAgc, Debug) << "setFixedExposureTime " << fixedExposureTime
<< " for channel " << channelIndex;
- channelData_[channelIndex].channel.setFixedShutter(fixedShutter);
+ channelData_[channelIndex].channel.setFixedExposureTime(fixedExposureTime);
}
void Agc::setFixedAnalogueGain(unsigned int channelIndex, double fixedAnalogueGain)
@@ -162,22 +201,22 @@ void Agc::setMeteringMode(std::string const &meteringModeName)
data.channel.setMeteringMode(meteringModeName);
}
-void Agc::setExposureMode(unsigned int channelIndex, std::string const &exposureModeName)
+void Agc::setExposureMode(std::string const &exposureModeName)
{
- if (checkChannel(channelIndex))
- return;
+ LOG(RPiAgc, Debug) << "setExposureMode " << exposureModeName;
- LOG(RPiAgc, Debug) << "setExposureMode " << exposureModeName
- << " for channel " << channelIndex;
- channelData_[channelIndex].channel.setExposureMode(exposureModeName);
+ /* Exposure mode will be the same across all channels. */
+ for (auto &data : channelData_)
+ data.channel.setExposureMode(exposureModeName);
}
-void Agc::setConstraintMode(unsigned int channelIndex, std::string const &constraintModeName)
+void Agc::setConstraintMode(std::string const &constraintModeName)
{
- if (checkChannel(channelIndex))
- return;
+ LOG(RPiAgc, Debug) << "setConstraintMode " << constraintModeName;
- channelData_[channelIndex].channel.setConstraintMode(constraintModeName);
+ /* Constraint mode will be the same across all channels. */
+ for (auto &data : channelData_)
+ data.channel.setConstraintMode(constraintModeName);
}
template<typename T>
diff --git a/src/ipa/rpi/controller/rpi/agc.h b/src/ipa/rpi/controller/rpi/agc.h
index 90890439..c3a940bf 100644
--- a/src/ipa/rpi/controller/rpi/agc.h
+++ b/src/ipa/rpi/controller/rpi/agc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * agc.h - AGC/AEC control algorithm
+ * AGC/AEC control algorithm
*/
#pragma once
@@ -31,20 +31,21 @@ public:
unsigned int getConvergenceFrames() const override;
std::vector<double> const &getWeights() const override;
void setEv(unsigned int channel, double ev) override;
- void setFlickerPeriod(unsigned int channelIndex,
- libcamera::utils::Duration flickerPeriod) override;
- void setMaxShutter(libcamera::utils::Duration maxShutter) override;
- void setFixedShutter(unsigned int channelIndex,
- libcamera::utils::Duration fixedShutter) override;
+ void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) override;
+ void setMaxExposureTime(libcamera::utils::Duration maxExposureTime) override;
+ void setFixedExposureTime(unsigned int channelIndex,
+ libcamera::utils::Duration fixedExposureTime) override;
void setFixedAnalogueGain(unsigned int channelIndex,
double fixedAnalogueGain) override;
void setMeteringMode(std::string const &meteringModeName) override;
- void setExposureMode(unsigned int channelIndex,
- std::string const &exposureModeName) override;
- void setConstraintMode(unsigned int channelIndex,
- std::string const &contraintModeName) override;
- void enableAuto(unsigned int channelIndex) override;
- void disableAuto(unsigned int channelIndex) override;
+ void setExposureMode(std::string const &exposureModeName) override;
+ void setConstraintMode(std::string const &contraintModeName) override;
+ void enableAutoExposure() override;
+ void disableAutoExposure() override;
+ bool autoExposureEnabled() const override;
+ void enableAutoGain() override;
+ void disableAutoGain() override;
+ bool autoGainEnabled() const override;
void switchMode(CameraMode const &cameraMode, Metadata *metadata) override;
void prepare(Metadata *imageMetadata) override;
void process(StatisticsPtr &stats, Metadata *imageMetadata) override;
diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
index 1e7eae06..b2999364 100644
--- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
+++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Raspberry Pi Ltd
*
- * agc_channel.cpp - AGC/AEC control algorithm
+ * AGC/AEC control algorithm
*/
#include "agc_channel.h"
@@ -12,6 +12,10 @@
#include <libcamera/base/log.h>
+#include "libcamera/internal/vector.h"
+
+#include "libipa/colours.h"
+
#include "../awb_status.h"
#include "../device_status.h"
#include "../histogram.h"
@@ -65,7 +69,7 @@ int AgcExposureMode::read(const libcamera::YamlObject &params)
auto value = params["shutter"].getList<double>();
if (!value)
return -EINVAL;
- std::transform(value->begin(), value->end(), std::back_inserter(shutter),
+ std::transform(value->begin(), value->end(), std::back_inserter(exposureTime),
[](double v) { return v * 1us; });
value = params["gain"].getList<double>();
@@ -73,13 +77,13 @@ int AgcExposureMode::read(const libcamera::YamlObject &params)
return -EINVAL;
gain = std::move(*value);
- if (shutter.size() < 2 || gain.size() < 2) {
+ if (exposureTime.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()) {
+ if (exposureTime.size() != gain.size()) {
LOG(RPiAgc, Error)
<< "AgcExposureMode: expect same number of exposure and gain entries in exposure profile";
return -EINVAL;
@@ -130,7 +134,8 @@ int AgcConstraint::read(const libcamera::YamlObject &params)
return -EINVAL;
qHi = *value;
- return yTarget.read(params["y_target"]);
+ yTarget = params["y_target"].get<ipa::Pwl>(ipa::Pwl{});
+ return yTarget.empty() ? -EINVAL : 0;
}
static std::tuple<int, AgcConstraintMode>
@@ -237,9 +242,9 @@ int AgcConfig::read(const libcamera::YamlObject &params)
return ret;
}
- ret = yTarget.read(params["y_target"]);
- if (ret)
- return ret;
+ yTarget = params["y_target"].get<ipa::Pwl>(ipa::Pwl{});
+ if (yTarget.empty())
+ return -EINVAL;
speed = params["speed"].get<double>(0.2);
startupFrames = params["startup_frames"].get<uint16_t>(10);
@@ -253,11 +258,13 @@ int AgcConfig::read(const libcamera::YamlObject &params)
stableRegion = params["stable_region"].get<double>(0.02);
+ desaturate = params["desaturate"].get<int>(1);
+
return 0;
}
AgcChannel::ExposureValues::ExposureValues()
- : shutter(0s), analogueGain(0),
+ : exposureTime(0s), analogueGain(0),
totalExposure(0s), totalExposureNoDG(0s)
{
}
@@ -266,9 +273,13 @@ AgcChannel::AgcChannel()
: meteringMode_(nullptr), exposureMode_(nullptr), constraintMode_(nullptr),
frameCount_(0), lockCount_(0),
lastTargetExposure_(0s), ev_(1.0), flickerPeriod_(0s),
- maxShutter_(0s), fixedShutter_(0s), fixedAnalogueGain_(0.0)
+ maxExposureTime_(0s), fixedExposureTime_(0s), fixedAnalogueGain_(0.0)
{
- memset(&awb_, 0, sizeof(awb_));
+ /* Set AWB default values in case early frames have no updates in metadata. */
+ awb_.gainR = 1.0;
+ awb_.gainG = 1.0;
+ awb_.gainB = 1.0;
+
/*
* Setting status_.totalExposureValue_ to zero initially tells us
* it's not been calculated yet (i.e. Process hasn't yet run).
@@ -303,31 +314,49 @@ int AgcChannel::read(const libcamera::YamlObject &params,
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;
+ /* Set up the "last exposure time/gain" values, in case AGC starts "disabled". */
+ status_.exposureTime = config_.defaultExposureTime;
status_.analogueGain = config_.defaultAnalogueGain;
return 0;
}
-void AgcChannel::disableAuto()
+void AgcChannel::disableAutoExposure()
+{
+ fixedExposureTime_ = status_.exposureTime;
+}
+
+void AgcChannel::enableAutoExposure()
+{
+ fixedExposureTime_ = 0s;
+}
+
+bool AgcChannel::autoExposureEnabled() const
+{
+ return fixedExposureTime_ == 0s;
+}
+
+void AgcChannel::disableAutoGain()
{
- fixedShutter_ = status_.shutterTime;
fixedAnalogueGain_ = status_.analogueGain;
}
-void AgcChannel::enableAuto()
+void AgcChannel::enableAutoGain()
{
- fixedShutter_ = 0s;
fixedAnalogueGain_ = 0;
}
+bool AgcChannel::autoGainEnabled() const
+{
+ return fixedAnalogueGain_ == 0;
+}
+
unsigned int AgcChannel::getConvergenceFrames() const
{
/*
- * If shutter and gain have been explicitly set, there is no
+ * If exposure time and gain have been explicitly set, there is no
* convergence to happen, so no need to drop any frames - return zero.
*/
- if (fixedShutter_ && fixedAnalogueGain_)
+ if (fixedExposureTime_ && fixedAnalogueGain_)
return 0;
else
return config_.convergenceFrames;
@@ -355,16 +384,16 @@ void AgcChannel::setFlickerPeriod(Duration flickerPeriod)
flickerPeriod_ = flickerPeriod;
}
-void AgcChannel::setMaxShutter(Duration maxShutter)
+void AgcChannel::setMaxExposureTime(Duration maxExposureTime)
{
- maxShutter_ = maxShutter;
+ maxExposureTime_ = maxExposureTime;
}
-void AgcChannel::setFixedShutter(Duration fixedShutter)
+void AgcChannel::setFixedExposureTime(Duration fixedExposureTime)
{
- fixedShutter_ = fixedShutter;
+ fixedExposureTime_ = fixedExposureTime;
/* Set this in case someone calls disableAuto() straight after. */
- status_.shutterTime = limitShutter(fixedShutter_);
+ status_.exposureTime = limitExposureTime(fixedExposureTime_);
}
void AgcChannel::setFixedAnalogueGain(double fixedAnalogueGain)
@@ -404,23 +433,22 @@ void AgcChannel::switchMode(CameraMode const &cameraMode,
double lastSensitivity = mode_.sensitivity;
mode_ = cameraMode;
- Duration fixedShutter = limitShutter(fixedShutter_);
- if (fixedShutter && fixedAnalogueGain_) {
+ Duration fixedExposureTime = limitExposureTime(fixedExposureTime_);
+ if (fixedExposureTime && 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_.totalExposureNoDG = fixedExposureTime_ * fixedAnalogueGain_;
target_.totalExposure = target_.totalExposureNoDG / minColourGain;
/* Equivalent of filterExposure. This resets any "history". */
filtered_ = target_;
/* Equivalent of divideUpExposure. */
- filtered_.shutter = fixedShutter;
+ filtered_.exposureTime = fixedExposureTime;
filtered_.analogueGain = fixedAnalogueGain_;
} else if (status_.totalExposureValue) {
/*
@@ -442,14 +470,15 @@ void AgcChannel::switchMode(CameraMode const &cameraMode,
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.
+ * We come through here on startup, when at least one of the
+ * exposure time 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_.exposureTime = fixedExposureTime ? fixedExposureTime : config_.defaultExposureTime;
filtered_.analogueGain = fixedAnalogueGain_ ? fixedAnalogueGain_ : config_.defaultAnalogueGain;
}
@@ -462,6 +491,9 @@ void AgcChannel::prepare(Metadata *imageMetadata)
AgcStatus delayedStatus;
AgcPrepareStatus prepareStatus;
+ /* Fetch the AWB status now because AWB also sets it in the prepare method. */
+ fetchAwbStatus(imageMetadata);
+
if (!imageMetadata->get("agc.delayed_status", delayedStatus))
totalExposureValue = delayedStatus.totalExposureValue;
@@ -472,7 +504,7 @@ void AgcChannel::prepare(Metadata *imageMetadata)
/* Process has run, so we have meaningful values. */
DeviceStatus deviceStatus;
if (imageMetadata->get("device.status", deviceStatus) == 0) {
- Duration actualExposure = deviceStatus.shutterSpeed *
+ Duration actualExposure = deviceStatus.exposureTime *
deviceStatus.analogueGain;
if (actualExposure) {
double digitalGain = totalExposureValue / actualExposure;
@@ -505,8 +537,6 @@ void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus,
* configuration, that kind of thing.
*/
housekeepConfig();
- /* Fetch the AWB status immediately, so that we can assume it's there. */
- fetchAwbStatus(imageMetadata);
/* Get the current exposure values for the frame that's just arrived. */
fetchCurrentExposure(deviceStatus);
/* Compute the total gain we require relative to the current exposure. */
@@ -528,7 +558,7 @@ void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus,
*/
bool desaturate = applyDigitalGain(gain, targetY, channelBound);
/*
- * The last thing is to divide up the exposure value into a shutter time
+ * The last thing is to divide up the exposure value into a exposure time
* and analogue gain, according to the current exposure mode.
*/
divideUpExposure();
@@ -544,7 +574,7 @@ bool AgcChannel::updateLockStatus(DeviceStatus const &deviceStatus)
const double resetMargin = 1.5;
/* Add 200us to the exposure time error to allow for line quantisation. */
- Duration exposureError = lastDeviceStatus_.shutterSpeed * errorFactor + 200us;
+ Duration exposureError = lastDeviceStatus_.exposureTime * errorFactor + 200us;
double gainError = lastDeviceStatus_.analogueGain * errorFactor;
Duration targetError = lastTargetExposure_ * errorFactor;
@@ -553,15 +583,15 @@ bool AgcChannel::updateLockStatus(DeviceStatus const &deviceStatus)
* 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 &&
+ if (deviceStatus.exposureTime > lastDeviceStatus_.exposureTime - exposureError &&
+ deviceStatus.exposureTime < lastDeviceStatus_.exposureTime + 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 ||
+ else if (deviceStatus.exposureTime < lastDeviceStatus_.exposureTime - resetMargin * exposureError ||
+ deviceStatus.exposureTime > lastDeviceStatus_.exposureTime + resetMargin * exposureError ||
deviceStatus.analogueGain < lastDeviceStatus_.analogueGain - resetMargin * gainError ||
deviceStatus.analogueGain > lastDeviceStatus_.analogueGain + resetMargin * gainError ||
status_.targetExposureValue < lastTargetExposure_ - resetMargin * targetError ||
@@ -579,11 +609,11 @@ void AgcChannel::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_.fixedExposureTime = limitExposureTime(fixedExposureTime_);
status_.fixedAnalogueGain = fixedAnalogueGain_;
status_.flickerPeriod = flickerPeriod_;
- LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedShutter "
- << status_.fixedShutter << " fixedAnalogueGain "
+ LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedExposureTime "
+ << status_.fixedExposureTime << " fixedAnalogueGain "
<< status_.fixedAnalogueGain;
/*
* Make sure the "mode" pointers point to the up-to-date things, if
@@ -627,17 +657,14 @@ void AgcChannel::housekeepConfig()
void AgcChannel::fetchCurrentExposure(DeviceStatus const &deviceStatus)
{
- current_.shutter = deviceStatus.shutterSpeed;
+ current_.exposureTime = deviceStatus.exposureTime;
current_.analogueGain = deviceStatus.analogueGain;
current_.totalExposure = 0s; /* this value is unused */
- current_.totalExposureNoDG = current_.shutter * current_.analogueGain;
+ current_.totalExposureNoDG = current_.exposureTime * current_.analogueGain;
}
void AgcChannel::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";
}
@@ -674,12 +701,13 @@ static double computeInitialY(StatisticsPtr &stats, AwbStatus const &awb,
* Note that the weights are applied by the IPA to the statistics directly,
* before they are given to us here.
*/
- double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0;
+ RGB<double> sum{ 0.0 };
+ double pixelSum = 0;
for (unsigned int i = 0; i < stats->agcRegions.numRegions(); i++) {
auto &region = stats->agcRegions.get(i);
- rSum += std::min<double>(region.val.rSum * gain, (maxVal - 1) * region.counted);
- gSum += std::min<double>(region.val.gSum * gain, (maxVal - 1) * region.counted);
- bSum += std::min<double>(region.val.bSum * gain, (maxVal - 1) * region.counted);
+ sum.r() += std::min<double>(region.val.rSum * gain, (maxVal - 1) * region.counted);
+ sum.g() += std::min<double>(region.val.gSum * gain, (maxVal - 1) * region.counted);
+ sum.b() += std::min<double>(region.val.bSum * gain, (maxVal - 1) * region.counted);
pixelSum += region.counted;
}
if (pixelSum == 0.0) {
@@ -687,14 +715,11 @@ static double computeInitialY(StatisticsPtr &stats, AwbStatus const &awb,
return 0;
}
- double ySum;
/* Factor in the AWB correction if needed. */
- if (stats->agcStatsPos == Statistics::AgcStatsPos::PreWb) {
- ySum = rSum * awb.gainR * .299 +
- gSum * awb.gainG * .587 +
- bSum * awb.gainB * .114;
- } else
- ySum = rSum * .299 + gSum * .587 + bSum * .114;
+ if (stats->agcStatsPos == Statistics::AgcStatsPos::PreWb)
+ sum *= RGB<double>{ { awb.gainR, awb.gainG, awb.gainB } };
+
+ double ySum = ipa::rec601LuminanceFromRGB(sum);
return ySum / pixelSum / (1 << 16);
}
@@ -712,7 +737,7 @@ 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 = c.yTarget.eval(c.yTarget.domain().clamp(lux));
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
double iqm = h.interQuantileMean(c.qLo, c.qHi);
return (targetY * h.bins()) / iqm;
@@ -731,7 +756,7 @@ void AgcChannel::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata,
* 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 = config_.yTarget.eval(config_.yTarget.domain().clamp(lux.lux));
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
/*
@@ -771,17 +796,17 @@ void AgcChannel::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata,
void AgcChannel::computeTargetExposure(double gain)
{
- if (status_.fixedShutter && status_.fixedAnalogueGain) {
+ if (status_.fixedExposureTime && 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.
+ * When analogue gain and exposure time 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;
+ status_.fixedExposureTime * status_.fixedAnalogueGain / minColourGain;
} else {
/*
* The statistics reflect the image without digital gain, so the final
@@ -789,12 +814,12 @@ void AgcChannel::computeTargetExposure(double gain)
*/
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 maxExposureTime = status_.fixedExposureTime
+ ? status_.fixedExposureTime
+ : exposureMode_->exposureTime.back();
+ maxExposureTime = limitExposureTime(maxExposureTime);
Duration maxTotalExposure =
- maxShutter *
+ maxExposureTime *
(status_.fixedAnalogueGain != 0.0
? status_.fixedAnalogueGain
: exposureMode_->gain.back());
@@ -860,8 +885,10 @@ bool AgcChannel::applyDigitalGain(double gain, double targetY, bool channelBound
* quickly (and we then approach the correct value more quickly from
* below).
*/
- bool desaturate = !channelBound &&
- targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
+ bool desaturate = false;
+ if (config_.desaturate)
+ desaturate = !channelBound &&
+ targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
if (desaturate)
dg /= config_.fastReduceThreshold;
LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate;
@@ -876,12 +903,16 @@ void AgcChannel::filterExposure()
double stableRegion = config_.stableRegion;
/*
- * AGC adapts instantly if both shutter and gain are directly specified
- * or we're in the startup phase.
+ * AGC adapts instantly if both exposure time and gain are directly
+ * specified or we're in the startup phase. Also disable the stable
+ * region, because we want to reflect any user exposure/gain updates,
+ * however small.
*/
- if ((status_.fixedShutter && status_.fixedAnalogueGain) ||
- frameCount_ <= config_.startupFrames)
+ if ((status_.fixedExposureTime && status_.fixedAnalogueGain) ||
+ frameCount_ <= config_.startupFrames) {
speed = 1.0;
+ stableRegion = 0.0;
+ }
if (!filtered_.totalExposure) {
filtered_.totalExposure = target_.totalExposure;
} else if (filtered_.totalExposure * (1.0 - stableRegion) < target_.totalExposure &&
@@ -905,34 +936,34 @@ void AgcChannel::filterExposure()
void AgcChannel::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.
+ * Sending the fixed exposure time/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;
+ Duration exposureTime;
double analogueGain;
- shutterTime = status_.fixedShutter ? status_.fixedShutter
- : exposureMode_->shutter[0];
- shutterTime = limitShutter(shutterTime);
+ exposureTime = status_.fixedExposureTime ? status_.fixedExposureTime
+ : exposureMode_->exposureTime[0];
+ exposureTime = limitExposureTime(exposureTime);
analogueGain = status_.fixedAnalogueGain != 0.0 ? status_.fixedAnalogueGain
: exposureMode_->gain[0];
analogueGain = limitGain(analogueGain);
- if (shutterTime * analogueGain < exposureValue) {
+ if (exposureTime * 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;
+ if (!status_.fixedExposureTime) {
+ Duration stageExposureTime =
+ limitExposureTime(exposureMode_->exposureTime[stage]);
+ if (stageExposureTime * analogueGain >= exposureValue) {
+ exposureTime = exposureValue / analogueGain;
break;
}
- shutterTime = stageShutter;
+ exposureTime = stageExposureTime;
}
if (status_.fixedAnalogueGain == 0.0) {
- if (exposureMode_->gain[stage] * shutterTime >= exposureValue) {
- analogueGain = exposureValue / shutterTime;
+ if (exposureMode_->gain[stage] * exposureTime >= exposureValue) {
+ analogueGain = exposureValue / exposureTime;
break;
}
analogueGain = exposureMode_->gain[stage];
@@ -940,18 +971,19 @@ void AgcChannel::divideUpExposure()
}
}
}
- LOG(RPiAgc, Debug) << "Divided up shutter and gain are " << shutterTime << " and "
- << analogueGain;
+ LOG(RPiAgc, Debug)
+ << "Divided up exposure time and gain are " << exposureTime
+ << " and " << analogueGain;
/*
- * Finally adjust shutter time for flicker avoidance (require both
- * shutter and gain not to be fixed).
+ * Finally adjust exposure time for flicker avoidance (require both
+ * exposure time and gain not to be fixed).
*/
- if (!status_.fixedShutter && !status_.fixedAnalogueGain &&
+ if (!status_.fixedExposureTime && !status_.fixedAnalogueGain &&
status_.flickerPeriod) {
- int flickerPeriods = shutterTime / status_.flickerPeriod;
+ int flickerPeriods = exposureTime / status_.flickerPeriod;
if (flickerPeriods) {
- Duration newShutterTime = flickerPeriods * status_.flickerPeriod;
- analogueGain *= shutterTime / newShutterTime;
+ Duration newExposureTime = flickerPeriods * status_.flickerPeriod;
+ analogueGain *= exposureTime / newExposureTime;
/*
* We should still not allow the ag to go over the
* largest value in the exposure mode. Note that this
@@ -960,20 +992,20 @@ void AgcChannel::divideUpExposure()
*/
analogueGain = std::min(analogueGain, exposureMode_->gain.back());
analogueGain = limitGain(analogueGain);
- shutterTime = newShutterTime;
+ exposureTime = newExposureTime;
}
- LOG(RPiAgc, Debug) << "After flicker avoidance, shutter "
- << shutterTime << " gain " << analogueGain;
+ LOG(RPiAgc, Debug) << "After flicker avoidance, exposure time "
+ << exposureTime << " gain " << analogueGain;
}
- filtered_.shutter = shutterTime;
+ filtered_.exposureTime = exposureTime;
filtered_.analogueGain = analogueGain;
}
void AgcChannel::writeAndFinish(Metadata *imageMetadata, bool desaturate)
{
status_.totalExposureValue = filtered_.totalExposure;
- status_.targetExposureValue = desaturate ? 0s : target_.totalExposureNoDG;
- status_.shutterTime = filtered_.shutter;
+ status_.targetExposureValue = desaturate ? 0s : target_.totalExposure;
+ status_.exposureTime = filtered_.exposureTime;
status_.analogueGain = filtered_.analogueGain;
/*
* Write to metadata as well, in case anyone wants to update the camera
@@ -982,32 +1014,32 @@ void AgcChannel::writeAndFinish(Metadata *imageMetadata, bool desaturate)
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
+ LOG(RPiAgc, Debug) << "Camera exposure update: exposure time " << filtered_.exposureTime
<< " analogue gain " << filtered_.analogueGain;
}
-Duration AgcChannel::limitShutter(Duration shutter)
+Duration AgcChannel::limitExposureTime(Duration exposureTime)
{
/*
- * shutter == 0 is a special case for fixed shutter values, and must pass
- * through unchanged
+ * exposureTime == 0 is a special case for fixed exposure time values,
+ * and must pass through unchanged.
*/
- if (!shutter)
- return shutter;
+ if (!exposureTime)
+ return exposureTime;
- shutter = std::clamp(shutter, mode_.minShutter, maxShutter_);
- return shutter;
+ exposureTime = std::clamp(exposureTime, mode_.minExposureTime, maxExposureTime_);
+ return exposureTime;
}
double AgcChannel::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.
+ * 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
+ * gain == 0.0 is a special case for fixed exposure time values, and
+ * must pass through unchanged.
*/
if (!gain)
return gain;
diff --git a/src/ipa/rpi/controller/rpi/agc_channel.h b/src/ipa/rpi/controller/rpi/agc_channel.h
index c1808422..fa697e6f 100644
--- a/src/ipa/rpi/controller/rpi/agc_channel.h
+++ b/src/ipa/rpi/controller/rpi/agc_channel.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Raspberry Pi Ltd
*
- * agc_channel.h - AGC/AEC control algorithm
+ * AGC/AEC control algorithm
*/
#pragma once
@@ -12,10 +12,11 @@
#include <libcamera/base/utils.h>
+#include <libipa/pwl.h>
+
#include "../agc_status.h"
#include "../awb_status.h"
#include "../controller.h"
-#include "../pwl.h"
/* This is our implementation of AGC. */
@@ -29,7 +30,7 @@ struct AgcMeteringMode {
};
struct AgcExposureMode {
- std::vector<libcamera::utils::Duration> shutter;
+ std::vector<libcamera::utils::Duration> exposureTime;
std::vector<double> gain;
int read(const libcamera::YamlObject &params);
};
@@ -40,7 +41,7 @@ struct AgcConstraint {
Bound bound;
double qLo;
double qHi;
- Pwl yTarget;
+ libcamera::ipa::Pwl yTarget;
int read(const libcamera::YamlObject &params);
};
@@ -61,7 +62,7 @@ struct AgcConfig {
std::map<std::string, AgcExposureMode> exposureModes;
std::map<std::string, AgcConstraintMode> constraintModes;
std::vector<AgcChannelConstraint> channelConstraints;
- Pwl yTarget;
+ libcamera::ipa::Pwl yTarget;
double speed;
uint16_t startupFrames;
unsigned int convergenceFrames;
@@ -76,6 +77,7 @@ struct AgcConfig {
libcamera::utils::Duration defaultExposureTime;
double defaultAnalogueGain;
double stableRegion;
+ bool desaturate;
};
class AgcChannel
@@ -88,14 +90,18 @@ public:
std::vector<double> const &getWeights() const;
void setEv(double ev);
void setFlickerPeriod(libcamera::utils::Duration flickerPeriod);
- void setMaxShutter(libcamera::utils::Duration maxShutter);
- void setFixedShutter(libcamera::utils::Duration fixedShutter);
+ void setMaxExposureTime(libcamera::utils::Duration maxExposureTime);
+ void setFixedExposureTime(libcamera::utils::Duration fixedExposureTime);
void setFixedAnalogueGain(double fixedAnalogueGain);
void setMeteringMode(std::string const &meteringModeName);
void setExposureMode(std::string const &exposureModeName);
void setConstraintMode(std::string const &contraintModeName);
- void enableAuto();
- void disableAuto();
+ void enableAutoExposure();
+ void disableAutoExposure();
+ bool autoExposureEnabled() const;
+ void enableAutoGain();
+ void disableAutoGain();
+ bool autoGainEnabled() const;
void switchMode(CameraMode const &cameraMode, Metadata *metadata);
void prepare(Metadata *imageMetadata);
void process(StatisticsPtr &stats, DeviceStatus const &deviceStatus, Metadata *imageMetadata,
@@ -115,7 +121,7 @@ private:
bool applyDigitalGain(double gain, double targetY, bool channelBound);
void divideUpExposure();
void writeAndFinish(Metadata *imageMetadata, bool desaturate);
- libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter);
+ libcamera::utils::Duration limitExposureTime(libcamera::utils::Duration exposureTime);
double limitGain(double gain) const;
AgcMeteringMode *meteringMode_;
AgcExposureMode *exposureMode_;
@@ -126,7 +132,7 @@ private:
struct ExposureValues {
ExposureValues();
- libcamera::utils::Duration shutter;
+ libcamera::utils::Duration exposureTime;
double analogueGain;
libcamera::utils::Duration totalExposure;
libcamera::utils::Duration totalExposureNoDG; /* without digital gain */
@@ -144,8 +150,8 @@ private:
std::string constraintModeName_;
double ev_;
libcamera::utils::Duration flickerPeriod_;
- libcamera::utils::Duration maxShutter_;
- libcamera::utils::Duration fixedShutter_;
+ libcamera::utils::Duration maxExposureTime_;
+ libcamera::utils::Duration fixedExposureTime_;
double fixedAnalogueGain_;
};
diff --git a/src/ipa/rpi/controller/rpi/alsc.cpp b/src/ipa/rpi/controller/rpi/alsc.cpp
index b7413611..21edb819 100644
--- a/src/ipa/rpi/controller/rpi/alsc.cpp
+++ b/src/ipa/rpi/controller/rpi/alsc.cpp
@@ -2,13 +2,14 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * alsc.cpp - ALSC (auto lens shading correction) control algorithm
+ * ALSC (auto lens shading correction) control algorithm
*/
#include <algorithm>
+#include <cmath>
#include <functional>
-#include <math.h>
#include <numeric>
+#include <vector>
#include <libcamera/base/log.h>
#include <libcamera/base/span.h>
@@ -251,12 +252,12 @@ static bool compareModes(CameraMode const &cm0, CameraMode const &cm1)
*/
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);
+ int leftDiff = std::abs(cm0.cropX - cm1.cropX);
+ int topDiff = std::abs(cm0.cropY - cm1.cropY);
+ int rightDiff = std::abs(cm0.cropX + cm0.scaleX * cm0.width -
+ cm1.cropX - cm1.scaleX * cm1.width);
+ int bottomDiff = std::abs(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
@@ -496,8 +497,9 @@ void resampleCalTable(const Array2D<double> &calTableIn,
* Precalculate and cache the x sampling locations and phases to save
* recomputing them on every row.
*/
- int xLo[X], xHi[X];
- double xf[X];
+ std::vector<int> xLo(X);
+ std::vector<int> xHi(X);
+ std::vector<double> xf(X);
double scaleX = cameraMode.sensorWidth /
(cameraMode.width * cameraMode.scaleX);
double xOff = cameraMode.cropX / (double)cameraMode.sensorWidth;
@@ -548,7 +550,9 @@ static void calculateCrCb(const RgbyRegions &awbRegion, Array2D<double> &cr,
for (unsigned int i = 0; i < cr.size(); i++) {
auto s = awbRegion.get(i);
- if (s.counted <= minCount || s.val.gSum / s.counted <= minG) {
+ /* Do not return unreliable, or zero, colour ratio statistics. */
+ if (s.counted <= minCount || s.val.gSum / s.counted <= minG ||
+ s.val.rSum / s.counted <= minG || s.val.bSum / s.counted <= minG) {
cr[i] = cb[i] = InsufficientData;
continue;
}
@@ -728,7 +732,7 @@ static double gaussSeidel2Sor(const SparseArray<double> &M, double omega,
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))
+ if (std::abs(lambda[i] - oldLambda[i]) > std::abs(maxDiff))
maxDiff = lambda[i] - oldLambda[i];
}
return maxDiff;
@@ -760,7 +764,7 @@ static void runMatrixIterations(const Array2D<double> &C,
constructM(C, W, M);
double lastMaxDiff = std::numeric_limits<double>::max();
for (unsigned int i = 0; i < nIter; i++) {
- double maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound));
+ double maxDiff = std::abs(gaussSeidel2Sor(M, omega, lambda, lambdaBound));
if (maxDiff < threshold) {
LOG(RPiAlsc, Debug)
<< "Stop after " << i + 1 << " iterations";
diff --git a/src/ipa/rpi/controller/rpi/alsc.h b/src/ipa/rpi/controller/rpi/alsc.h
index 0b6d9478..31087982 100644
--- a/src/ipa/rpi/controller/rpi/alsc.h
+++ b/src/ipa/rpi/controller/rpi/alsc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * alsc.h - ALSC (auto lens shading correction) control algorithm
+ * ALSC (auto lens shading correction) control algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/awb.cpp b/src/ipa/rpi/controller/rpi/awb.cpp
index 5ae0c2fa..365b595f 100644
--- a/src/ipa/rpi/controller/rpi/awb.cpp
+++ b/src/ipa/rpi/controller/rpi/awb.cpp
@@ -2,10 +2,11 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * awb.cpp - AWB control algorithm
+ * AWB control algorithm
*/
#include <assert.h>
+#include <cmath>
#include <functional>
#include <libcamera/base/log.h>
@@ -20,6 +21,8 @@ using namespace libcamera;
LOG_DEFINE_CATEGORY(RPiAwb)
+constexpr double kDefaultCT = 4500.0;
+
#define NAME "rpi.awb"
/*
@@ -49,10 +52,11 @@ int AwbPrior::read(const libcamera::YamlObject &params)
return -EINVAL;
lux = *value;
- return prior.read(params["prior"]);
+ prior = params["prior"].get<ipa::Pwl>(ipa::Pwl{});
+ return prior.empty() ? -EINVAL : 0;
}
-static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject &params)
+static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject &params)
{
if (params.size() % 3) {
LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry";
@@ -103,8 +107,8 @@ int AwbConfig::read(const libcamera::YamlObject &params)
if (ret)
return ret;
/* We will want the inverse functions of these too. */
- ctRInverse = ctR.inverse();
- ctBInverse = ctB.inverse();
+ ctRInverse = ctR.inverse().first;
+ ctBInverse = ctB.inverse().first;
}
if (params.contains("priors")) {
@@ -121,7 +125,7 @@ int AwbConfig::read(const libcamera::YamlObject &params)
}
if (priors.empty()) {
LOG(RPiAwb, Error) << "AwbConfig: no AWB priors configured";
- return ret;
+ return -EINVAL;
}
}
if (params.contains("modes")) {
@@ -161,11 +165,18 @@ int AwbConfig::read(const libcamera::YamlObject &params)
bayes = false;
}
}
- fast = params[fast].get<int>(bayes); /* default to fast for Bayesian, otherwise slow */
whitepointR = params["whitepoint_r"].get<double>(0.0);
whitepointB = params["whitepoint_b"].get<double>(0.0);
if (bayes == false)
sensitivityR = sensitivityB = 1.0; /* nor do sensitivities make any sense */
+ /*
+ * The biasProportion parameter adds a small proportion of the counted
+ * pixles to a region biased to the biasCT colour temperature.
+ *
+ * A typical value for biasProportion would be between 0.05 to 0.1.
+ */
+ biasProportion = params["bias_proportion"].get<double>(0.0);
+ biasCT = params["bias_ct"].get<double>(kDefaultCT);
return 0;
}
@@ -207,19 +218,25 @@ void Awb::initialise()
* them.
*/
if (!config_.ctR.empty() && !config_.ctB.empty()) {
- syncResults_.temperatureK = config_.ctR.domain().clip(4000);
+ syncResults_.temperatureK = config_.ctR.domain().clamp(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_.temperatureK = kDefaultCT;
syncResults_.gainR = syncResults_.gainG = syncResults_.gainB = 1.0;
}
prevSyncResults_ = syncResults_;
asyncResults_ = syncResults_;
}
+void Awb::initialValues(double &gainR, double &gainB)
+{
+ gainR = syncResults_.gainR;
+ gainB = syncResults_.gainB;
+}
+
void Awb::disableAuto()
{
/* Freeze the most recent values, and treat them as manual gains */
@@ -267,14 +284,32 @@ void Awb::setManualGains(double manualR, double manualB)
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_));
+ double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clamp(1 / manualR_));
+ double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clamp(1 / manualB_));
prevSyncResults_.temperatureK = (ctR + ctB) / 2;
syncResults_.temperatureK = prevSyncResults_.temperatureK;
}
}
}
+void Awb::setColourTemperature(double temperatureK)
+{
+ if (!config_.bayes) {
+ LOG(RPiAwb, Warning) << "AWB uncalibrated - cannot set colour temperature";
+ return;
+ }
+
+ temperatureK = config_.ctR.domain().clamp(temperatureK);
+ manualR_ = 1 / config_.ctR.eval(temperatureK);
+ manualB_ = 1 / config_.ctB.eval(temperatureK);
+
+ syncResults_.temperatureK = temperatureK;
+ syncResults_.gainR = manualR_;
+ syncResults_.gainG = 1.0;
+ syncResults_.gainB = manualB_;
+ prevSyncResults_ = syncResults_;
+}
+
void Awb::switchMode([[maybe_unused]] CameraMode const &cameraMode,
Metadata *metadata)
{
@@ -400,7 +435,8 @@ void Awb::asyncFunc()
static void generateStats(std::vector<Awb::RGB> &zones,
StatisticsPtr &stats, double minPixels,
- double minG, Metadata &globalMetadata)
+ double minG, Metadata &globalMetadata,
+ double biasProportion, double biasCtR, double biasCtB)
{
std::scoped_lock<RPiController::Metadata> l(globalMetadata);
@@ -413,6 +449,14 @@ static void generateStats(std::vector<Awb::RGB> &zones,
continue;
zone.R = region.val.rSum / region.counted;
zone.B = region.val.bSum / region.counted;
+ /*
+ * Add some bias samples to allow the search to tend to a
+ * bias CT in failure cases.
+ */
+ const unsigned int proportion = biasProportion * region.counted;
+ zone.R += proportion * biasCtR;
+ zone.B += proportion * biasCtB;
+ zone.G += proportion * 1.0;
/* Factor in the ALSC applied colour shading correction if required. */
const AlscStatus *alscStatus = globalMetadata.getLocked<AlscStatus>("alsc.status");
if (stats->colourStatsPos == Statistics::ColourStatsPos::PreLsc && alscStatus) {
@@ -432,8 +476,11 @@ void Awb::prepareStats()
* LSC has already been applied to the stats in this pipeline, so stop
* any LSC compensation. We also ignore config_.fast in this version.
*/
+ const double biasCtR = config_.bayes ? config_.ctR.eval(config_.biasCT) : 0;
+ const double biasCtB = config_.bayes ? config_.ctB.eval(config_.biasCT) : 0;
generateStats(zones_, statistics_, config_.minPixels,
- config_.minG, getGlobalMetadata());
+ config_.minG, getGlobalMetadata(),
+ config_.biasProportion, biasCtR, biasCtB);
/*
* apply sensitivities, so values appear to come from our "canonical"
* sensor.
@@ -462,7 +509,7 @@ double Awb::computeDelta2Sum(double gainR, double gainB)
return delta2Sum;
}
-Pwl Awb::interpolatePrior()
+ipa::Pwl Awb::interpolatePrior()
{
/*
* Interpolate the prior log likelihood function for our current lux
@@ -479,7 +526,7 @@ Pwl Awb::interpolatePrior()
idx++;
double lux0 = config_.priors[idx].lux,
lux1 = config_.priors[idx + 1].lux;
- return Pwl::combine(config_.priors[idx].prior,
+ return ipa::Pwl::combine(config_.priors[idx].prior,
config_.priors[idx + 1].prior,
[&](double /*x*/, double y0, double y1) {
return y0 + (y1 - y0) *
@@ -488,26 +535,26 @@ Pwl Awb::interpolatePrior()
}
}
-static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b,
- Pwl::Point const &c)
+static double interpolateQuadatric(ipa::Pwl::Point const &a, ipa::Pwl::Point const &b,
+ ipa::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));
+ ipa::Pwl::Point ca = c - a, ba = b - a;
+ double denominator = 2 * (ba.y() * ca.x() - ca.y() * ba.x());
+ if (std::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);
+ return a.y() < c.y() - eps ? a.x() : (c.y() < a.y() - eps ? c.x() : b.x());
}
-double Awb::coarseSearch(Pwl const &prior)
+double Awb::coarseSearch(ipa::Pwl const &prior)
{
points_.clear(); /* assume doesn't deallocate memory */
size_t bestPoint = 0;
@@ -519,22 +566,22 @@ double Awb::coarseSearch(Pwl const &prior)
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 priorLogLikelihood = prior.eval(prior.domain().clamp(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)
+ points_.push_back(ipa::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;
+ 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
@@ -553,7 +600,7 @@ double Awb::coarseSearch(Pwl const &prior)
return t;
}
-void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
+void Awb::fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior)
{
int spanR = -1, spanB = -1;
config_.ctR.eval(t, &spanR);
@@ -564,14 +611,14 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
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)
+ ipa::Pwl::Point transverse({ bDiff, -rDiff });
+ if (transverse.length2() < 1e-6)
return;
/*
* unit vector orthogonal to the b vs. r function (pointing outwards
* with r and b increasing)
*/
- transverse = transverse / transverse.len();
+ transverse = transverse / transverse.length();
double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0;
double transverseRange = config_.transverseNeg + config_.transversePos;
const int maxNumDeltas = 12;
@@ -586,26 +633,26 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
for (int i = -nsteps; i <= nsteps; i++) {
double tTest = t + i * step;
double priorLogLikelihood =
- prior.eval(prior.domain().clip(tTest));
+ prior.eval(prior.domain().clamp(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];
+ ipa::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;
+ points[j][0] = -config_.transverseNeg +
+ (transverseRange * j) / (numDeltas - 1);
+ ipa::Pwl::Point rbTest = ipa::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;
+ points[j][1] = delta2Sum - priorLogLikelihood;
LOG(RPiAwb, Debug)
<< "At t " << tTest << " r " << rTest << " b "
- << bTest << ": " << points[j].y;
- if (points[j].y < points[bestPoint].y)
+ << bTest << ": " << points[j].y();
+ if (points[j].y() < points[bestPoint].y())
bestPoint = j;
}
/*
@@ -613,11 +660,11 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
* 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;
+ ipa::Pwl::Point rbTest = ipa::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;
@@ -647,7 +694,7 @@ void Awb::awbBayes()
* Get the current prior, and scale according to how many zones are
* valid... not entirely sure about this.
*/
- Pwl prior = interpolatePrior();
+ ipa::Pwl prior = interpolatePrior();
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
prior.map([](double x, double y) {
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
@@ -709,7 +756,11 @@ void Awb::awbGrey()
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 */
+ /*
+ * The grey world model can't estimate the colour temperature, use a
+ * default value.
+ */
+ asyncResults_.temperatureK = kDefaultCT;
asyncResults_.gainR = gainR;
asyncResults_.gainG = 1.0;
asyncResults_.gainB = gainB;
diff --git a/src/ipa/rpi/controller/rpi/awb.h b/src/ipa/rpi/controller/rpi/awb.h
index e7d49cd8..2fb91254 100644
--- a/src/ipa/rpi/controller/rpi/awb.h
+++ b/src/ipa/rpi/controller/rpi/awb.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * awb.h - AWB control algorithm
+ * AWB control algorithm
*/
#pragma once
@@ -10,11 +10,14 @@
#include <condition_variable>
#include <thread>
+#include <libcamera/geometry.h>
+
#include "../awb_algorithm.h"
-#include "../pwl.h"
#include "../awb_status.h"
#include "../statistics.h"
+#include "libipa/pwl.h"
+
namespace RPiController {
/* Control algorithm to perform AWB calculations. */
@@ -28,7 +31,7 @@ struct AwbMode {
struct AwbPrior {
int read(const libcamera::YamlObject &params);
double lux; /* lux level */
- Pwl prior; /* maps CT to prior log likelihood for this lux level */
+ libcamera::ipa::Pwl prior; /* maps CT to prior log likelihood for this lux level */
};
struct AwbConfig {
@@ -40,11 +43,10 @@ struct AwbConfig {
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 */
+ libcamera::ipa::Pwl ctR; /* function maps CT to r (= R/G) */
+ libcamera::ipa::Pwl ctB; /* function maps CT to b (= B/G) */
+ libcamera::ipa::Pwl ctRInverse; /* inverse of ctR */
+ libcamera::ipa::Pwl ctBInverse; /* inverse of ctB */
/* table of illuminant priors at different lux levels */
std::vector<AwbPrior> priors;
/* AWB "modes" (determines the search range) */
@@ -84,6 +86,10 @@ struct AwbConfig {
double whitepointR;
double whitepointB;
bool bayes; /* use Bayesian algorithm */
+ /* proportion of counted samples to add for the search bias */
+ double biasProportion;
+ /* CT target for the search bias */
+ double biasCT;
};
class Awb : public AwbAlgorithm
@@ -95,8 +101,10 @@ public:
void initialise() override;
int read(const libcamera::YamlObject &params) override;
unsigned int getConvergenceFrames() const override;
+ void initialValues(double &gainR, double &gainB) override;
void setMode(std::string const &name) override;
void setManualGains(double manualR, double manualB) override;
+ void setColourTemperature(double temperatureK) override;
void enableAuto() override;
void disableAuto() override;
void switchMode(CameraMode const &cameraMode, Metadata *metadata) override;
@@ -160,11 +168,11 @@ private:
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);
+ libcamera::ipa::Pwl interpolatePrior();
+ double coarseSearch(libcamera::ipa::Pwl const &prior);
+ void fineSearch(double &t, double &r, double &b, libcamera::ipa::Pwl const &prior);
std::vector<RGB> zones_;
- std::vector<Pwl::Point> points_;
+ std::vector<libcamera::ipa::Pwl::Point> points_;
/* manual r setting */
double manualR_;
/* manual b setting */
diff --git a/src/ipa/rpi/controller/rpi/black_level.cpp b/src/ipa/rpi/controller/rpi/black_level.cpp
index 85baec3f..4c968f14 100644
--- a/src/ipa/rpi/controller/rpi/black_level.cpp
+++ b/src/ipa/rpi/controller/rpi/black_level.cpp
@@ -2,10 +2,9 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * black_level.cpp - black level control algorithm
+ * black level control algorithm
*/
-#include <math.h>
#include <stdint.h>
#include <libcamera/base/log.h>
@@ -22,7 +21,7 @@ LOG_DEFINE_CATEGORY(RPiBlackLevel)
#define NAME "rpi.black_level"
BlackLevel::BlackLevel(Controller *controller)
- : Algorithm(controller)
+ : BlackLevelAlgorithm(controller)
{
}
@@ -45,6 +44,14 @@ int BlackLevel::read(const libcamera::YamlObject &params)
return 0;
}
+void BlackLevel::initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG,
+ uint16_t &blackLevelB)
+{
+ blackLevelR = blackLevelR_;
+ blackLevelG = blackLevelG_;
+ blackLevelB = blackLevelB_;
+}
+
void BlackLevel::prepare(Metadata *imageMetadata)
{
/*
diff --git a/src/ipa/rpi/controller/rpi/black_level.h b/src/ipa/rpi/controller/rpi/black_level.h
index 2403f7f7..f50729db 100644
--- a/src/ipa/rpi/controller/rpi/black_level.h
+++ b/src/ipa/rpi/controller/rpi/black_level.h
@@ -2,23 +2,25 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * black_level.h - black level control algorithm
+ * black level control algorithm
*/
#pragma once
-#include "../algorithm.h"
+#include "../black_level_algorithm.h"
#include "../black_level_status.h"
/* This is our implementation of the "black level algorithm". */
namespace RPiController {
-class BlackLevel : public Algorithm
+class BlackLevel : public BlackLevelAlgorithm
{
public:
BlackLevel(Controller *controller);
char const *name() const override;
int read(const libcamera::YamlObject &params) override;
+ void initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG,
+ uint16_t &blackLevelB) override;
void prepare(Metadata *imageMetadata) override;
private:
diff --git a/src/ipa/rpi/controller/rpi/cac.cpp b/src/ipa/rpi/controller/rpi/cac.cpp
index 7c123da1..17779ad5 100644
--- a/src/ipa/rpi/controller/rpi/cac.cpp
+++ b/src/ipa/rpi/controller/rpi/cac.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023 Raspberry Pi Ltd
*
- * cac.cpp - Chromatic Aberration Correction algorithm
+ * Chromatic Aberration Correction algorithm
*/
#include "cac.h"
@@ -27,40 +27,23 @@ char const *Cac::name() const
return NAME;
}
-int Cac::read(const libcamera::YamlObject &params)
-{
- arrayToSet(params["lut_rx"], config_.lutRx);
- arrayToSet(params["lut_ry"], config_.lutRy);
- arrayToSet(params["lut_bx"], config_.lutBx);
- arrayToSet(params["lut_by"], config_.lutBy);
- cacStatus_.lutRx = config_.lutRx;
- cacStatus_.lutRy = config_.lutRy;
- cacStatus_.lutBx = config_.lutBx;
- cacStatus_.lutBy = config_.lutBy;
- double strength = params["strength"].get<double>(1);
- setStrength(config_.lutRx, cacStatus_.lutRx, strength);
- setStrength(config_.lutBx, cacStatus_.lutBx, strength);
- setStrength(config_.lutRy, cacStatus_.lutRy, strength);
- setStrength(config_.lutBy, cacStatus_.lutBy, strength);
- return 0;
-}
-
-void Cac::initialise()
-{
-}
-
-void Cac::arrayToSet(const libcamera::YamlObject &params, std::vector<double> &inputArray)
+static bool arrayToSet(const libcamera::YamlObject &params, std::vector<double> &inputArray, const Size &size)
{
int num = 0;
- const Size &size = getHardwareConfig().cacRegions;
- inputArray.resize((size.width + 1) * (size.height + 1));
+ int max_num = (size.width + 1) * (size.height + 1);
+ inputArray.resize(max_num);
+
for (const auto &p : params.asList()) {
+ if (num == max_num)
+ return false;
inputArray[num++] = p.get<double>(0);
}
+
+ return num == max_num;
}
-void Cac::setStrength(std::vector<double> &inputArray, std::vector<double> &outputArray,
- double strengthFactor)
+static void setStrength(std::vector<double> &inputArray, std::vector<double> &outputArray,
+ double strengthFactor)
{
int num = 0;
for (const auto &p : inputArray) {
@@ -68,9 +51,52 @@ void Cac::setStrength(std::vector<double> &inputArray, std::vector<double> &outp
}
}
+int Cac::read(const libcamera::YamlObject &params)
+{
+ config_.enabled = params.contains("lut_rx") && params.contains("lut_ry") &&
+ params.contains("lut_bx") && params.contains("lut_by");
+ if (!config_.enabled)
+ return 0;
+
+ const Size &size = getHardwareConfig().cacRegions;
+
+ if (!arrayToSet(params["lut_rx"], config_.lutRx, size)) {
+ LOG(RPiCac, Error) << "Bad CAC lut_rx table";
+ return -EINVAL;
+ }
+
+ if (!arrayToSet(params["lut_ry"], config_.lutRy, size)) {
+ LOG(RPiCac, Error) << "Bad CAC lut_ry table";
+ return -EINVAL;
+ }
+
+ if (!arrayToSet(params["lut_bx"], config_.lutBx, size)) {
+ LOG(RPiCac, Error) << "Bad CAC lut_bx table";
+ return -EINVAL;
+ }
+
+ if (!arrayToSet(params["lut_by"], config_.lutBy, size)) {
+ LOG(RPiCac, Error) << "Bad CAC lut_by table";
+ return -EINVAL;
+ }
+
+ double strength = params["strength"].get<double>(1);
+ cacStatus_.lutRx = config_.lutRx;
+ cacStatus_.lutRy = config_.lutRy;
+ cacStatus_.lutBx = config_.lutBx;
+ cacStatus_.lutBy = config_.lutBy;
+ setStrength(config_.lutRx, cacStatus_.lutRx, strength);
+ setStrength(config_.lutBx, cacStatus_.lutBx, strength);
+ setStrength(config_.lutRy, cacStatus_.lutRy, strength);
+ setStrength(config_.lutBy, cacStatus_.lutBy, strength);
+
+ return 0;
+}
+
void Cac::prepare(Metadata *imageMetadata)
{
- imageMetadata->set("cac.status", cacStatus_);
+ if (config_.enabled)
+ imageMetadata->set("cac.status", cacStatus_);
}
// Register algorithm with the system.
diff --git a/src/ipa/rpi/controller/rpi/cac.h b/src/ipa/rpi/controller/rpi/cac.h
index 419180ab..a7b14c00 100644
--- a/src/ipa/rpi/controller/rpi/cac.h
+++ b/src/ipa/rpi/controller/rpi/cac.h
@@ -12,6 +12,7 @@
namespace RPiController {
struct CacConfig {
+ bool enabled;
std::vector<double> lutRx;
std::vector<double> lutRy;
std::vector<double> lutBx;
@@ -24,15 +25,11 @@ public:
Cac(Controller *controller = NULL);
char const *name() const override;
int read(const libcamera::YamlObject &params) override;
- void initialise() override;
void prepare(Metadata *imageMetadata) override;
- void setStrength(std::vector<double> &inputArray, std::vector<double> &outputArray,
- double strengthFactor);
private:
CacConfig config_;
CacStatus cacStatus_;
- void arrayToSet(const libcamera::YamlObject &params, std::vector<double> &inputArray);
};
} // namespace RPiController
diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp
index 2e2e6664..8607f152 100644
--- a/src/ipa/rpi/controller/rpi/ccm.cpp
+++ b/src/ipa/rpi/controller/rpi/ccm.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * ccm.cpp - CCM (colour correction matrix) control algorithm
+ * CCM (colour correction matrix) control algorithm
*/
#include <libcamera/base/log.h>
@@ -29,34 +29,7 @@ LOG_DEFINE_CATEGORY(RPiCcm)
#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 &params)
-{
- double *ptr = (double *)m;
-
- if (params.size() != 9) {
- LOG(RPiCcm, Error) << "Wrong number of values in CCM";
- return -EINVAL;
- }
-
- for (const auto &param : params.asList()) {
- auto value = param.get<double>();
- if (!value)
- return -EINVAL;
- *ptr++ = *value;
- }
-
- return 0;
-}
+using Matrix3x3 = Matrix<double, 3, 3>;
Ccm::Ccm(Controller *controller)
: CcmAlgorithm(controller), saturation_(1.0) {}
@@ -68,12 +41,10 @@ char const *Ccm::name() const
int Ccm::read(const libcamera::YamlObject &params)
{
- int ret;
-
if (params.contains("saturation")) {
- ret = config_.saturation.read(params["saturation"]);
- if (ret)
- return ret;
+ config_.saturation = params["saturation"].get<ipa::Pwl>(ipa::Pwl{});
+ if (config_.saturation.empty())
+ return -EINVAL;
}
for (auto &p : params["ccms"].asList()) {
@@ -83,9 +54,12 @@ int Ccm::read(const libcamera::YamlObject &params)
CtCcm ctCcm;
ctCcm.ct = *value;
- ret = ctCcm.ccm.read(p["ccm"]);
- if (ret)
- return ret;
+
+ auto ccm = p["ccm"].get<Matrix3x3>();
+ if (!ccm)
+ return -EINVAL;
+
+ ctCcm.ccm = *ccm;
if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) {
LOG(RPiCcm, Error)
@@ -113,8 +87,10 @@ void Ccm::initialise()
{
}
+namespace {
+
template<typename T>
-static bool getLocked(Metadata *metadata, std::string const &tag, T &value)
+bool getLocked(Metadata *metadata, std::string const &tag, T &value)
{
T *ptr = metadata->getLocked<T>(tag);
if (ptr == nullptr)
@@ -123,7 +99,7 @@ static bool getLocked(Metadata *metadata, std::string const &tag, T &value)
return true;
}
-Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
+Matrix3x3 calculateCcm(std::vector<CtCcm> const &ccms, double ct)
{
if (ct <= ccms.front().ct)
return ccms.front().ccm;
@@ -139,16 +115,25 @@ Matrix calculateCcm(std::vector<CtCcm> const &ccms, double ct)
}
}
-Matrix applySaturation(Matrix const &ccm, double saturation)
+Matrix3x3 applySaturation(Matrix3x3 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);
+ static const Matrix3x3 RGB2Y({ 0.299, 0.587, 0.114,
+ -0.169, -0.331, 0.500,
+ 0.500, -0.419, -0.081 });
+
+ static const Matrix3x3 Y2RGB({ 1.000, 0.000, 1.402,
+ 1.000, -0.345, -0.714,
+ 1.000, 1.771, 0.000 });
+
+ Matrix3x3 S({ 1, 0, 0,
+ 0, saturation, 0,
+ 0, 0, saturation });
+
return Y2RGB * S * RGB2Y * ccm;
}
+} /* namespace */
+
void Ccm::prepare(Metadata *imageMetadata)
{
bool awbOk = false, luxOk = false;
@@ -166,18 +151,18 @@ void Ccm::prepare(Metadata *imageMetadata)
LOG(RPiCcm, Warning) << "no colour temperature found";
if (!luxOk)
LOG(RPiCcm, Warning) << "no lux value found";
- Matrix ccm = calculateCcm(config_.ccms, awb.temperatureK);
+ Matrix3x3 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));
+ config_.saturation.domain().clamp(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]));
+ std::max(-8.0, std::min(7.9999, ccm[j][i]));
LOG(RPiCcm, Debug)
<< "colour temperature " << awb.temperatureK << "K";
LOG(RPiCcm, Debug)
diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h
index 286d0b33..c05dbb17 100644
--- a/src/ipa/rpi/controller/rpi/ccm.h
+++ b/src/ipa/rpi/controller/rpi/ccm.h
@@ -2,59 +2,29 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * ccm.h - CCM (colour correction matrix) control algorithm
+ * CCM (colour correction matrix) control algorithm
*/
#pragma once
#include <vector>
+#include "libcamera/internal/matrix.h"
+#include <libipa/pwl.h>
+
#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 &params);
-};
-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;
+ libcamera::Matrix<double, 3, 3> ccm;
};
struct CcmConfig {
std::vector<CtCcm> ccms;
- Pwl saturation;
+ libcamera::ipa::Pwl saturation;
};
class Ccm : public CcmAlgorithm
diff --git a/src/ipa/rpi/controller/rpi/contrast.cpp b/src/ipa/rpi/controller/rpi/contrast.cpp
index 4e038a02..fe866a54 100644
--- a/src/ipa/rpi/controller/rpi/contrast.cpp
+++ b/src/ipa/rpi/controller/rpi/contrast.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * contrast.cpp - contrast (gamma) control algorithm
+ * contrast (gamma) control algorithm
*/
#include <stdint.h>
@@ -53,7 +53,9 @@ int Contrast::read(const libcamera::YamlObject &params)
config_.hiHistogram = params["hi_histogram"].get<double>(0.95);
config_.hiLevel = params["hi_level"].get<double>(0.95);
config_.hiMax = params["hi_max"].get<double>(2000);
- return config_.gammaCurve.read(params["gamma_curve"]);
+
+ config_.gammaCurve = params["gamma_curve"].get<ipa::Pwl>(ipa::Pwl{});
+ return config_.gammaCurve.empty() ? -EINVAL : 0;
}
void Contrast::setBrightness(double brightness)
@@ -92,10 +94,12 @@ void Contrast::prepare(Metadata *imageMetadata)
imageMetadata->set("contrast.status", status_);
}
-Pwl computeStretchCurve(Histogram const &histogram,
+namespace {
+
+ipa::Pwl computeStretchCurve(Histogram const &histogram,
ContrastConfig const &config)
{
- Pwl enhance;
+ ipa::Pwl enhance;
enhance.append(0, 0);
/*
* If the start of the histogram is rather empty, try to pull it down a
@@ -136,10 +140,10 @@ Pwl computeStretchCurve(Histogram const &histogram,
return enhance;
}
-Pwl applyManualContrast(Pwl const &gammaCurve, double brightness,
- double contrast)
+ipa::Pwl applyManualContrast(ipa::Pwl const &gammaCurve, double brightness,
+ double contrast)
{
- Pwl newGammaCurve;
+ ipa::Pwl newGammaCurve;
LOG(RPiContrast, Debug)
<< "Manual brightness " << brightness << " contrast " << contrast;
gammaCurve.map([&](double x, double y) {
@@ -151,6 +155,8 @@ Pwl applyManualContrast(Pwl const &gammaCurve, double brightness,
return newGammaCurve;
}
+} /* namespace */
+
void Contrast::process(StatisticsPtr &stats,
[[maybe_unused]] Metadata *imageMetadata)
{
@@ -160,7 +166,7 @@ void Contrast::process(StatisticsPtr &stats,
* 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;
+ ipa::Pwl gammaCurve = config_.gammaCurve;
if (ceEnable_) {
if (config_.loMax != 0 || config_.hiMax != 0)
gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve);
diff --git a/src/ipa/rpi/controller/rpi/contrast.h b/src/ipa/rpi/controller/rpi/contrast.h
index 59aa70dc..c0f7db98 100644
--- a/src/ipa/rpi/controller/rpi/contrast.h
+++ b/src/ipa/rpi/controller/rpi/contrast.h
@@ -2,14 +2,15 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * contrast.h - contrast (gamma) control algorithm
+ * contrast (gamma) control algorithm
*/
#pragma once
#include <mutex>
+#include <libipa/pwl.h>
+
#include "../contrast_algorithm.h"
-#include "../pwl.h"
namespace RPiController {
@@ -26,7 +27,7 @@ struct ContrastConfig {
double hiHistogram;
double hiLevel;
double hiMax;
- Pwl gammaCurve;
+ libcamera::ipa::Pwl gammaCurve;
};
class Contrast : public ContrastAlgorithm
diff --git a/src/ipa/rpi/controller/rpi/denoise.cpp b/src/ipa/rpi/controller/rpi/denoise.cpp
index 154ee604..ba851658 100644
--- a/src/ipa/rpi/controller/rpi/denoise.cpp
+++ b/src/ipa/rpi/controller/rpi/denoise.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022 Raspberry Pi Ltd
*
- * Denoise.cpp - Denoise (spatial, colour, temporal) control algorithm
+ * Denoise (spatial, colour, temporal) control algorithm
*/
#include "denoise.h"
diff --git a/src/ipa/rpi/controller/rpi/dpc.cpp b/src/ipa/rpi/controller/rpi/dpc.cpp
index be3871df..8aac03f7 100644
--- a/src/ipa/rpi/controller/rpi/dpc.cpp
+++ b/src/ipa/rpi/controller/rpi/dpc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * dpc.cpp - DPC (defective pixel correction) control algorithm
+ * DPC (defective pixel correction) control algorithm
*/
#include <libcamera/base/log.h>
diff --git a/src/ipa/rpi/controller/rpi/dpc.h b/src/ipa/rpi/controller/rpi/dpc.h
index 84a05604..9cefb06d 100644
--- a/src/ipa/rpi/controller/rpi/dpc.h
+++ b/src/ipa/rpi/controller/rpi/dpc.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * dpc.h - DPC (defective pixel correction) control algorithm
+ * DPC (defective pixel correction) control algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/focus.h b/src/ipa/rpi/controller/rpi/focus.h
index 8556039d..ee014be9 100644
--- a/src/ipa/rpi/controller/rpi/focus.h
+++ b/src/ipa/rpi/controller/rpi/focus.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * focus.h - focus algorithm
+ * focus algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/geq.cpp b/src/ipa/rpi/controller/rpi/geq.cpp
index 510870e9..40e7191b 100644
--- a/src/ipa/rpi/controller/rpi/geq.cpp
+++ b/src/ipa/rpi/controller/rpi/geq.cpp
@@ -2,14 +2,13 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * geq.cpp - GEQ (green equalisation) control algorithm
+ * GEQ (green equalisation) control algorithm
*/
#include <libcamera/base/log.h>
#include "../device_status.h"
#include "../lux_status.h"
-#include "../pwl.h"
#include "geq.h"
@@ -45,9 +44,9 @@ int Geq::read(const libcamera::YamlObject &params)
}
if (params.contains("strength")) {
- int ret = config_.strength.read(params["strength"]);
- if (ret)
- return ret;
+ config_.strength = params["strength"].get<ipa::Pwl>(ipa::Pwl{});
+ if (config_.strength.empty())
+ return -EINVAL;
}
return 0;
@@ -67,7 +66,7 @@ void Geq::prepare(Metadata *imageMetadata)
GeqStatus geqStatus = {};
double strength = config_.strength.empty()
? 1.0
- : config_.strength.eval(config_.strength.domain().clip(luxStatus.lux));
+ : config_.strength.eval(config_.strength.domain().clamp(luxStatus.lux));
strength *= deviceStatus.analogueGain;
double offset = config_.offset * strength;
double slope = config_.slope * strength;
diff --git a/src/ipa/rpi/controller/rpi/geq.h b/src/ipa/rpi/controller/rpi/geq.h
index ee3a52ff..e8b9f427 100644
--- a/src/ipa/rpi/controller/rpi/geq.h
+++ b/src/ipa/rpi/controller/rpi/geq.h
@@ -2,10 +2,12 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * geq.h - GEQ (green equalisation) control algorithm
+ * GEQ (green equalisation) control algorithm
*/
#pragma once
+#include <libipa/pwl.h>
+
#include "../algorithm.h"
#include "../geq_status.h"
@@ -16,7 +18,7 @@ namespace RPiController {
struct GeqConfig {
uint16_t offset;
double slope;
- Pwl strength; /* lux to strength factor */
+ libcamera::ipa::Pwl strength; /* lux to strength factor */
};
class Geq : public Algorithm
diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp
index fb580548..f3da8291 100644
--- a/src/ipa/rpi/controller/rpi/hdr.cpp
+++ b/src/ipa/rpi/controller/rpi/hdr.cpp
@@ -2,11 +2,13 @@
/*
* Copyright (C) 2023 Raspberry Pi Ltd
*
- * hdr.cpp - HDR control algorithm
+ * HDR control algorithm
*/
#include "hdr.h"
+#include <cmath>
+
#include <libcamera/base/log.h>
#include "../agc_status.h"
@@ -39,25 +41,52 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
channelMap[v.get<unsigned int>().value()] = k;
/* Lens shading related parameters. */
- if (params.contains("spatial_gain")) {
- spatialGain.read(params["spatial_gain"]);
- diffusion = params["diffusion"].get<unsigned int>(3);
- /* Clip to an arbitrary limit just to stop typos from killing the system! */
- const unsigned int MAX_DIFFUSION = 15;
- if (diffusion > MAX_DIFFUSION) {
- diffusion = MAX_DIFFUSION;
- LOG(RPiHdr, Warning) << "Diffusion value clipped to " << MAX_DIFFUSION;
- }
+ if (params.contains("spatial_gain_curve")) {
+ spatialGainCurve = params["spatial_gain_curve"].get<ipa::Pwl>(ipa::Pwl{});
+ } else if (params.contains("spatial_gain")) {
+ double spatialGain = params["spatial_gain"].get<double>(2.0);
+ spatialGainCurve.append(0.0, spatialGain);
+ spatialGainCurve.append(0.01, spatialGain);
+ spatialGainCurve.append(0.06, 1.0); /* maybe make this programmable? */
+ spatialGainCurve.append(1.0, 1.0);
+ }
+
+ diffusion = params["diffusion"].get<unsigned int>(3);
+ /* Clip to an arbitrary limit just to stop typos from killing the system! */
+ const unsigned int MAX_DIFFUSION = 15;
+ if (diffusion > MAX_DIFFUSION) {
+ diffusion = MAX_DIFFUSION;
+ LOG(RPiHdr, Warning) << "Diffusion value clipped to " << MAX_DIFFUSION;
}
/* Read any tonemap parameters. */
tonemapEnable = params["tonemap_enable"].get<int>(0);
- detailConstant = params["detail_constant"].get<uint16_t>(50);
- detailSlope = params["detail_slope"].get<double>(8.0);
+ detailConstant = params["detail_constant"].get<uint16_t>(0);
+ detailSlope = params["detail_slope"].get<double>(0.0);
iirStrength = params["iir_strength"].get<double>(8.0);
strength = params["strength"].get<double>(1.5);
if (tonemapEnable)
- tonemap.read(params["tonemap"]);
+ tonemap = params["tonemap"].get<ipa::Pwl>(ipa::Pwl{});
+ speed = params["speed"].get<double>(1.0);
+ if (params.contains("hi_quantile_targets")) {
+ hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
+ if (hiQuantileTargets.empty() || hiQuantileTargets.size() % 2)
+ LOG(RPiHdr, Fatal) << "hi_quantile_targets much be even and non-empty";
+ } else
+ hiQuantileTargets = { 0.95, 0.65, 0.5, 0.28, 0.3, 0.25 };
+ hiQuantileMaxGain = params["hi_quantile_max_gain"].get<double>(1.6);
+ if (params.contains("quantile_targets")) {
+ quantileTargets = params["quantile_targets"].getList<double>().value();
+ if (quantileTargets.empty() || quantileTargets.size() % 2)
+ LOG(RPiHdr, Fatal) << "quantile_targets much be even and non-empty";
+ } else
+ quantileTargets = { 0.2, 0.03, 1.0, 0.15 };
+ powerMin = params["power_min"].get<double>(0.65);
+ powerMax = params["power_max"].get<double>(1.0);
+ if (params.contains("contrast_adjustments")) {
+ contrastAdjustments = params["contrast_adjustments"].getList<double>().value();
+ } else
+ contrastAdjustments = { 0.5, 0.75 };
/* Read any stitch parameters. */
stitchEnable = params["stitch_enable"].get<int>(0);
@@ -159,7 +188,7 @@ void Hdr::prepare(Metadata *imageMetadata)
}
HdrConfig &config = it->second;
- if (config.spatialGain.empty())
+ if (config.spatialGainCurve.empty())
return;
AlscStatus alscStatus{}; /* some compilers seem to require the braces */
@@ -183,7 +212,7 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config
/* When there's a change of HDR mode we start over with a new tonemap curve. */
if (delayedStatus_.mode != previousMode_) {
previousMode_ = delayedStatus_.mode;
- tonemap_ = Pwl();
+ tonemap_ = ipa::Pwl();
}
/* No tonemapping. No need to output a tonemap.status. */
@@ -205,10 +234,61 @@ bool Hdr::updateTonemap([[maybe_unused]] StatisticsPtr &stats, HdrConfig &config
return true;
/*
- * If we wanted to build or adjust tonemaps dynamically, this would be the place
- * to do it. But for now we seem to be getting by without.
+ * Create a tonemap dynamically. We have three ingredients.
+ *
+ * 1. We have a list of "hi quantiles" and "targets". We use these to judge if
+ * the image does seem to be reasonably saturated. If it isn't, we calculate
+ * a gain that we will feed as a linear factor into the tonemap generation.
+ * This prevents unsaturated images from beoming quite so "flat".
+ *
+ * 2. We have a list of quantile/target pairs for the bottom of the histogram.
+ * We use these to calculate how much gain we must apply to the bottom of the
+ * tonemap. We apply this gain as a power curve so as not to blow out the top
+ * end.
+ *
+ * 3. Finally, when we generate the tonemap, we have some contrast adjustments
+ * for the bottom because we know that power curves can start quite steeply and
+ * cause a washed-out look.
*/
+ /* Compute the linear gain from the headroom for saturation at the top. */
+ double gain = 10; /* arbitrary, but hiQuantileMaxGain will clamp it later */
+ for (unsigned int i = 0; i < config.hiQuantileTargets.size(); i += 2) {
+ double quantile = config.hiQuantileTargets[i];
+ double target = config.hiQuantileTargets[i + 1];
+ double value = stats->yHist.interQuantileMean(quantile, 1.0) / 1024.0;
+ double newGain = target / (value + 0.01);
+ gain = std::min(gain, newGain);
+ }
+ gain = std::clamp(gain, 1.0, config.hiQuantileMaxGain);
+
+ /* Compute the power curve from the amount of gain needed at the bottom. */
+ double min_power = 2; /* arbitrary, but config.powerMax will clamp it later */
+ for (unsigned int i = 0; i < config.quantileTargets.size(); i += 2) {
+ double quantile = config.quantileTargets[i];
+ double target = config.quantileTargets[i + 1];
+ double value = stats->yHist.interQuantileMean(0, quantile) / 1024.0;
+ value = std::min(value * gain, 1.0);
+ double power = log(target + 1e-6) / log(value + 1e-6);
+ min_power = std::min(min_power, power);
+ }
+ double power = std::clamp(min_power, config.powerMin, config.powerMax);
+
+ /* Generate the tonemap, including the contrast adjustment factors. */
+ libcamera::ipa::Pwl tonemap;
+ tonemap.append(0, 0);
+ for (unsigned int i = 0; i <= 6; i++) {
+ double x = 1 << (i + 9); /* x loops from 512 to 32768 inclusive */
+ double y = pow(std::min(x * gain, 65535.0) / 65536.0, power) * 65536;
+ if (i < config.contrastAdjustments.size())
+ y *= config.contrastAdjustments[i];
+ if (!tonemap_.empty())
+ y = y * config.speed + tonemap_.eval(x) * (1 - config.speed);
+ tonemap.append(x, y);
+ }
+ tonemap.append(65535, 65535);
+ tonemap_ = tonemap;
+
return true;
}
@@ -255,7 +335,7 @@ static void averageGains(std::vector<double> &src, std::vector<double> &dst, con
void Hdr::updateGains(StatisticsPtr &stats, HdrConfig &config)
{
- if (config.spatialGain.empty())
+ if (config.spatialGainCurve.empty())
return;
/* When alternating exposures, only compute these gains for the short frame. */
@@ -270,7 +350,7 @@ void Hdr::updateGains(StatisticsPtr &stats, HdrConfig &config)
double g = region.val.gSum / counted;
double b = region.val.bSum / counted;
double brightness = std::max({ r, g, b }) / 65535;
- gains_[0][i] = config.spatialGain.eval(brightness);
+ gains_[0][i] = config.spatialGainCurve.eval(brightness);
}
/* Ping-pong between the two gains_ buffers. */
diff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h
index 980aa3d1..5c2f3988 100644
--- a/src/ipa/rpi/controller/rpi/hdr.h
+++ b/src/ipa/rpi/controller/rpi/hdr.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023, Raspberry Pi Ltd
*
- * hdr.h - HDR control algorithm
+ * HDR control algorithm
*/
#pragma once
@@ -12,9 +12,10 @@
#include <libcamera/geometry.h>
+#include <libipa/pwl.h>
+
#include "../hdr_algorithm.h"
#include "../hdr_status.h"
-#include "../pwl.h"
/* This is our implementation of an HDR algorithm. */
@@ -26,7 +27,7 @@ struct HdrConfig {
std::map<unsigned int, std::string> channelMap;
/* Lens shading related parameters. */
- Pwl spatialGain; /* Brightness to gain curve for different image regions. */
+ libcamera::ipa::Pwl spatialGainCurve; /* Brightness to gain curve for different image regions. */
unsigned int diffusion; /* How much to diffuse the gain spatially. */
/* Tonemap related parameters. */
@@ -35,7 +36,15 @@ struct HdrConfig {
double detailSlope;
double iirStrength;
double strength;
- Pwl tonemap;
+ libcamera::ipa::Pwl tonemap;
+ /* These relate to adaptive tonemap calculation. */
+ double speed;
+ std::vector<double> hiQuantileTargets; /* quantiles to check for unsaturated images */
+ double hiQuantileMaxGain; /* the max gain we'll apply when unsaturated */
+ std::vector<double> quantileTargets; /* target values for histogram quantiles */
+ double powerMin; /* minimum tonemap power */
+ double powerMax; /* maximum tonemap power */
+ std::vector<double> contrastAdjustments; /* any contrast adjustment factors */
/* Stitch related parameters. */
bool stitchEnable;
@@ -67,7 +76,7 @@ private:
HdrStatus status_; /* track the current HDR mode and channel */
HdrStatus delayedStatus_; /* track the delayed HDR mode and channel */
std::string previousMode_;
- Pwl tonemap_;
+ libcamera::ipa::Pwl tonemap_;
libcamera::Size regions_; /* stats regions */
unsigned int numRegions_; /* total number of stats regions */
std::vector<double> gains_[2];
diff --git a/src/ipa/rpi/controller/rpi/lux.cpp b/src/ipa/rpi/controller/rpi/lux.cpp
index 06625f3a..27b89a8f 100644
--- a/src/ipa/rpi/controller/rpi/lux.cpp
+++ b/src/ipa/rpi/controller/rpi/lux.cpp
@@ -2,9 +2,8 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * lux.cpp - Lux control algorithm
+ * Lux control algorithm
*/
-#include <math.h>
#include <libcamera/base/log.h>
@@ -41,7 +40,7 @@ int Lux::read(const libcamera::YamlObject &params)
auto value = params["reference_shutter_speed"].get<double>();
if (!value)
return -EINVAL;
- referenceShutterSpeed_ = *value * 1.0us;
+ referenceExposureTime_ = *value * 1.0us;
value = params["reference_gain"].get<double>();
if (!value)
@@ -83,11 +82,11 @@ void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata)
double currentAperture = deviceStatus.aperture.value_or(currentAperture_);
double currentY = stats->yHist.interQuantileMean(0, 1);
double gainRatio = referenceGain_ / currentGain;
- double shutterSpeedRatio =
- referenceShutterSpeed_ / deviceStatus.shutterSpeed;
+ double exposureTimeRatio =
+ referenceExposureTime_ / deviceStatus.exposureTime;
double apertureRatio = referenceAperture_ / currentAperture;
double yRatio = currentY * (65536 / stats->yHist.bins()) / referenceY_;
- double estimatedLux = shutterSpeedRatio * gainRatio *
+ double estimatedLux = exposureTimeRatio * gainRatio *
apertureRatio * apertureRatio *
yRatio * referenceLux_;
LuxStatus status;
diff --git a/src/ipa/rpi/controller/rpi/lux.h b/src/ipa/rpi/controller/rpi/lux.h
index 89411a54..da007fe9 100644
--- a/src/ipa/rpi/controller/rpi/lux.h
+++ b/src/ipa/rpi/controller/rpi/lux.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * lux.h - Lux control algorithm
+ * Lux control algorithm
*/
#pragma once
@@ -32,7 +32,7 @@ private:
* These values define the conditions of the reference image, against
* which we compare the new image.
*/
- libcamera::utils::Duration referenceShutterSpeed_;
+ libcamera::utils::Duration referenceExposureTime_;
double referenceGain_;
double referenceAperture_; /* units of 1/f */
double referenceY_; /* out of 65536 */
diff --git a/src/ipa/rpi/controller/rpi/noise.cpp b/src/ipa/rpi/controller/rpi/noise.cpp
index bcd8b9ed..145175fb 100644
--- a/src/ipa/rpi/controller/rpi/noise.cpp
+++ b/src/ipa/rpi/controller/rpi/noise.cpp
@@ -2,10 +2,10 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * noise.cpp - Noise control algorithm
+ * Noise control algorithm
*/
-#include <math.h>
+#include <cmath>
#include <libcamera/base/log.h>
@@ -69,7 +69,7 @@ void Noise::prepare(Metadata *imageMetadata)
* make some adjustments based on the camera mode (such as
* binning), if we knew how to discover it...
*/
- double factor = sqrt(deviceStatus.analogueGain) / modeFactor_;
+ double factor = std::sqrt(deviceStatus.analogueGain) / modeFactor_;
struct NoiseStatus status;
status.noiseConstant = referenceConstant_ * factor;
status.noiseSlope = referenceSlope_ * factor;
diff --git a/src/ipa/rpi/controller/rpi/noise.h b/src/ipa/rpi/controller/rpi/noise.h
index 74c31e64..6deae1f0 100644
--- a/src/ipa/rpi/controller/rpi/noise.h
+++ b/src/ipa/rpi/controller/rpi/noise.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * noise.h - Noise control algorithm
+ * Noise control algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/saturation.cpp b/src/ipa/rpi/controller/rpi/saturation.cpp
index 813540e5..b83c5887 100644
--- a/src/ipa/rpi/controller/rpi/saturation.cpp
+++ b/src/ipa/rpi/controller/rpi/saturation.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022 Raspberry Pi Ltd
*
- * saturation.cpp - Saturation control algorithm
+ * Saturation control algorithm
*/
#include "saturation.h"
diff --git a/src/ipa/rpi/controller/rpi/sdn.cpp b/src/ipa/rpi/controller/rpi/sdn.cpp
index 6743919e..619178a8 100644
--- a/src/ipa/rpi/controller/rpi/sdn.cpp
+++ b/src/ipa/rpi/controller/rpi/sdn.cpp
@@ -2,10 +2,11 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * sdn.cpp - SDN (spatial denoise) control algorithm
+ * SDN (spatial denoise) control algorithm
*/
#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
#include "../denoise_status.h"
#include "../noise_status.h"
@@ -60,7 +61,7 @@ void Sdn::prepare(Metadata *imageMetadata)
status.noiseConstant = noiseStatus.noiseConstant * deviation_;
status.noiseSlope = noiseStatus.noiseSlope * deviation_;
status.strength = strength_;
- status.mode = static_cast<std::underlying_type_t<DenoiseMode>>(mode_);
+ status.mode = utils::to_underlying(mode_);
imageMetadata->set("denoise.status", status);
LOG(RPiSdn, Debug)
<< "programmed constant " << status.noiseConstant
diff --git a/src/ipa/rpi/controller/rpi/sdn.h b/src/ipa/rpi/controller/rpi/sdn.h
index 9dd73c38..cb226de8 100644
--- a/src/ipa/rpi/controller/rpi/sdn.h
+++ b/src/ipa/rpi/controller/rpi/sdn.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * sdn.h - SDN (spatial denoise) control algorithm
+ * SDN (spatial denoise) control algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/sharpen.cpp b/src/ipa/rpi/controller/rpi/sharpen.cpp
index 4f6f020a..1d143ff5 100644
--- a/src/ipa/rpi/controller/rpi/sharpen.cpp
+++ b/src/ipa/rpi/controller/rpi/sharpen.cpp
@@ -2,10 +2,10 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * sharpen.cpp - sharpening control algorithm
+ * sharpening control algorithm
*/
-#include <math.h>
+#include <cmath>
#include <libcamera/base/log.h>
@@ -68,7 +68,7 @@ void Sharpen::prepare(Metadata *imageMetadata)
* 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_);
+ double userStrengthSqrt = std::sqrt(userStrength_);
struct SharpenStatus status;
/*
* Binned modes seem to need the sharpening toned down with this
diff --git a/src/ipa/rpi/controller/rpi/sharpen.h b/src/ipa/rpi/controller/rpi/sharpen.h
index 8bb7631e..96ccd609 100644
--- a/src/ipa/rpi/controller/rpi/sharpen.h
+++ b/src/ipa/rpi/controller/rpi/sharpen.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * sharpen.h - sharpening control algorithm
+ * sharpening control algorithm
*/
#pragma once
diff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp
index 5f8b2bf2..3422adfe 100644
--- a/src/ipa/rpi/controller/rpi/tonemap.cpp
+++ b/src/ipa/rpi/controller/rpi/tonemap.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022 Raspberry Pi Ltd
*
- * tonemap.cpp - Tonemap control algorithm
+ * Tonemap control algorithm
*/
#include "tonemap.h"
@@ -33,7 +33,7 @@ int Tonemap::read(const libcamera::YamlObject &params)
config_.detailSlope = params["detail_slope"].get<double>(0.1);
config_.iirStrength = params["iir_strength"].get<double>(1.0);
config_.strength = params["strength"].get<double>(1.0);
- config_.tonemap.read(params["tone_curve"]);
+ config_.tonemap = params["tone_curve"].get<ipa::Pwl>(ipa::Pwl{});
return 0;
}
diff --git a/src/ipa/rpi/controller/rpi/tonemap.h b/src/ipa/rpi/controller/rpi/tonemap.h
index f25aa47f..ba0cf5c4 100644
--- a/src/ipa/rpi/controller/rpi/tonemap.h
+++ b/src/ipa/rpi/controller/rpi/tonemap.h
@@ -6,8 +6,9 @@
*/
#pragma once
+#include <libipa/pwl.h>
+
#include "algorithm.h"
-#include "pwl.h"
namespace RPiController {
@@ -16,7 +17,7 @@ struct TonemapConfig {
double detailSlope;
double iirStrength;
double strength;
- Pwl tonemap;
+ libcamera::ipa::Pwl tonemap;
};
class Tonemap : public Algorithm
diff --git a/src/ipa/rpi/controller/saturation_status.h b/src/ipa/rpi/controller/saturation_status.h
index 337b66a3..c7fadc99 100644
--- a/src/ipa/rpi/controller/saturation_status.h
+++ b/src/ipa/rpi/controller/saturation_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022 Raspberry Pi Ltd
*
- * saturation_status.h - Saturation control algorithm status
+ * Saturation control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/sharpen_algorithm.h b/src/ipa/rpi/controller/sharpen_algorithm.h
index 3be21c32..abd82cb2 100644
--- a/src/ipa/rpi/controller/sharpen_algorithm.h
+++ b/src/ipa/rpi/controller/sharpen_algorithm.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * sharpen_algorithm.h - sharpness control algorithm interface
+ * sharpness control algorithm interface
*/
#pragma once
diff --git a/src/ipa/rpi/controller/sharpen_status.h b/src/ipa/rpi/controller/sharpen_status.h
index 106166db..74910199 100644
--- a/src/ipa/rpi/controller/sharpen_status.h
+++ b/src/ipa/rpi/controller/sharpen_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Raspberry Pi Ltd
*
- * sharpen_status.h - Sharpen control algorithm status
+ * Sharpen control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/statistics.h b/src/ipa/rpi/controller/statistics.h
index 015d4efc..cbd81161 100644
--- a/src/ipa/rpi/controller/statistics.h
+++ b/src/ipa/rpi/controller/statistics.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
- * statistics.h - Raspberry Pi generic statistics structure
+ * Raspberry Pi generic statistics structure
*/
#pragma once
diff --git a/src/ipa/rpi/controller/stitch_status.h b/src/ipa/rpi/controller/stitch_status.h
index b17800ed..7812f3e3 100644
--- a/src/ipa/rpi/controller/stitch_status.h
+++ b/src/ipa/rpi/controller/stitch_status.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2023 Raspberry Pi Ltd
*
- * stitch_status.h - stitch control algorithm status
+ * stitch control algorithm status
*/
#pragma once
diff --git a/src/ipa/rpi/controller/tonemap_status.h b/src/ipa/rpi/controller/tonemap_status.h
index 0e639946..0364ff66 100644
--- a/src/ipa/rpi/controller/tonemap_status.h
+++ b/src/ipa/rpi/controller/tonemap_status.h
@@ -2,16 +2,16 @@
/*
* Copyright (C) 2022 Raspberry Pi Ltd
*
- * hdr.h - Tonemap control algorithm status
+ * Tonemap control algorithm status
*/
#pragma once
-#include "pwl.h"
+#include <libipa/pwl.h>
struct TonemapStatus {
uint16_t detailConstant;
double detailSlope;
double iirStrength;
double strength;
- RPiController::Pwl tonemap;
+ libcamera::ipa::Pwl tonemap;
};
diff --git a/src/ipa/rpi/pisp/data/imx219.json b/src/ipa/rpi/pisp/data/imx219.json
new file mode 100644
index 00000000..5254e60d
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx219.json
@@ -0,0 +1,1187 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 21965,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 800,
+ "reference_Y": 11460
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 3.661
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 239,
+ "slope": 0.00766
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2860.0, 0.9514, 0.4156,
+ 2960.0, 0.9289, 0.4372,
+ 3603.0, 0.8305, 0.5251,
+ 4650.0, 0.6756, 0.6433,
+ 5858.0, 0.6193, 0.6807,
+ 7580.0, 0.5019, 0.7495
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.03392,
+ "transverse_neg": 0.034
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 10.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 10.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.418, 1.428, 1.446, 1.454, 1.454, 1.451, 1.441, 1.428, 1.411, 1.391, 1.371, 1.349, 1.334, 1.327, 1.325, 1.325, 1.325, 1.325, 1.331, 1.344, 1.363, 1.383, 1.402, 1.418, 1.433, 1.446, 1.452, 1.453, 1.446, 1.435, 1.415, 1.404,
+ 1.428, 1.442, 1.453, 1.455, 1.454, 1.447, 1.431, 1.413, 1.392, 1.371, 1.349, 1.331, 1.318, 1.307, 1.299, 1.299, 1.299, 1.303, 1.313, 1.328, 1.344, 1.363, 1.383, 1.404, 1.424, 1.439, 1.451, 1.453, 1.453, 1.445, 1.431, 1.415,
+ 1.436, 1.448, 1.453, 1.455, 1.449, 1.435, 1.415, 1.393, 1.369, 1.345, 1.322, 1.303, 1.287, 1.276, 1.269, 1.268, 1.268, 1.272, 1.283, 1.298, 1.316, 1.337, 1.362, 1.384, 1.406, 1.427, 1.444, 1.454, 1.454, 1.452, 1.438, 1.426,
+ 1.441, 1.451, 1.454, 1.451, 1.439, 1.422, 1.396, 1.372, 1.345, 1.319, 1.295, 1.274, 1.257, 1.245, 1.239, 1.238, 1.238, 1.245, 1.255, 1.269, 1.289, 1.311, 1.336, 1.362, 1.388, 1.412, 1.433, 1.448, 1.454, 1.453, 1.445, 1.433,
+ 1.445, 1.452, 1.452, 1.445, 1.428, 1.405, 1.379, 1.349, 1.319, 1.295, 1.269, 1.247, 1.229, 1.219, 1.212, 1.211, 1.211, 1.217, 1.228, 1.242, 1.261, 1.286, 1.311, 1.339, 1.367, 1.395, 1.419, 1.439, 1.452, 1.452, 1.451, 1.436,
+ 1.448, 1.451, 1.451, 1.435, 1.414, 1.387, 1.358, 1.327, 1.296, 1.269, 1.245, 1.222, 1.205, 1.193, 1.187, 1.185, 1.186, 1.191, 1.202, 1.217, 1.237, 1.261, 1.286, 1.316, 1.346, 1.378, 1.404, 1.429, 1.445, 1.451, 1.451, 1.442,
+ 1.448, 1.448, 1.445, 1.427, 1.401, 1.371, 1.338, 1.306, 1.274, 1.245, 1.222, 1.199, 1.183, 1.171, 1.164, 1.162, 1.162, 1.168, 1.181, 1.194, 1.215, 1.237, 1.264, 1.294, 1.325, 1.359, 1.389, 1.418, 1.441, 1.449, 1.449, 1.443,
+ 1.449, 1.448, 1.438, 1.415, 1.387, 1.352, 1.318, 1.284, 1.252, 1.223, 1.199, 1.179, 1.161, 1.149, 1.142, 1.142, 1.142, 1.149, 1.159, 1.174, 1.194, 1.215, 1.242, 1.272, 1.307, 1.341, 1.376, 1.405, 1.431, 1.447, 1.447, 1.444,
+ 1.448, 1.447, 1.431, 1.405, 1.373, 1.336, 1.301, 1.264, 1.234, 1.204, 1.179, 1.161, 1.143, 1.131, 1.124, 1.123, 1.123, 1.131, 1.141, 1.156, 1.174, 1.197, 1.224, 1.254, 1.288, 1.324, 1.361, 1.394, 1.423, 1.442, 1.444, 1.444,
+ 1.447, 1.442, 1.424, 1.393, 1.359, 1.322, 1.284, 1.248, 1.216, 1.187, 1.162, 1.143, 1.128, 1.115, 1.109, 1.108, 1.108, 1.113, 1.124, 1.139, 1.156, 1.179, 1.206, 1.236, 1.272, 1.309, 1.347, 1.382, 1.411, 1.435, 1.443, 1.444,
+ 1.444, 1.439, 1.417, 1.383, 1.347, 1.308, 1.271, 1.233, 1.201, 1.173, 1.147, 1.128, 1.115, 1.101, 1.095, 1.093, 1.093, 1.099, 1.111, 1.124, 1.142, 1.165, 1.191, 1.222, 1.258, 1.296, 1.333, 1.372, 1.404, 1.429, 1.441, 1.442,
+ 1.443, 1.434, 1.409, 1.375, 1.336, 1.297, 1.257, 1.221, 1.189, 1.159, 1.136, 1.116, 1.101, 1.092, 1.083, 1.082, 1.082, 1.089, 1.099, 1.111, 1.131, 1.153, 1.181, 1.211, 1.246, 1.284, 1.324, 1.361, 1.398, 1.425, 1.441, 1.441,
+ 1.443, 1.431, 1.405, 1.369, 1.328, 1.287, 1.247, 1.211, 1.178, 1.149, 1.126, 1.107, 1.092, 1.083, 1.075, 1.073, 1.073, 1.082, 1.089, 1.101, 1.121, 1.143, 1.171, 1.201, 1.237, 1.274, 1.314, 1.353, 1.389, 1.421, 1.439, 1.441,
+ 1.442, 1.429, 1.401, 1.364, 1.323, 1.279, 1.241, 1.205, 1.172, 1.144, 1.119, 1.101, 1.085, 1.075, 1.071, 1.067, 1.067, 1.073, 1.082, 1.096, 1.114, 1.136, 1.163, 1.194, 1.229, 1.268, 1.308, 1.348, 1.387, 1.417, 1.439, 1.439,
+ 1.443, 1.429, 1.399, 1.362, 1.319, 1.276, 1.237, 1.199, 1.169, 1.141, 1.115, 1.096, 1.081, 1.071, 1.066, 1.063, 1.066, 1.068, 1.078, 1.092, 1.109, 1.132, 1.159, 1.191, 1.226, 1.263, 1.304, 1.346, 1.384, 1.416, 1.438, 1.439,
+ 1.443, 1.428, 1.399, 1.361, 1.319, 1.276, 1.236, 1.199, 1.167, 1.139, 1.115, 1.096, 1.081, 1.071, 1.064, 1.062, 1.062, 1.067, 1.077, 1.091, 1.109, 1.131, 1.158, 1.189, 1.224, 1.262, 1.303, 1.345, 1.383, 1.416, 1.438, 1.439,
+ 1.444, 1.429, 1.399, 1.361, 1.319, 1.276, 1.236, 1.199, 1.167, 1.139, 1.116, 1.096, 1.081, 1.071, 1.064, 1.063, 1.063, 1.067, 1.077, 1.091, 1.109, 1.131, 1.159, 1.189, 1.224, 1.262, 1.303, 1.345, 1.384, 1.416, 1.438, 1.441,
+ 1.444, 1.431, 1.402, 1.364, 1.322, 1.281, 1.239, 1.202, 1.171, 1.142, 1.118, 1.099, 1.084, 1.073, 1.069, 1.065, 1.067, 1.071, 1.079, 1.094, 1.112, 1.135, 1.163, 1.191, 1.227, 1.265, 1.307, 1.348, 1.386, 1.418, 1.438, 1.441,
+ 1.447, 1.433, 1.406, 1.369, 1.328, 1.286, 1.244, 1.209, 1.177, 1.148, 1.124, 1.105, 1.089, 1.081, 1.073, 1.071, 1.071, 1.079, 1.085, 1.099, 1.118, 1.141, 1.168, 1.198, 1.233, 1.271, 1.312, 1.352, 1.391, 1.422, 1.441, 1.444,
+ 1.448, 1.438, 1.412, 1.376, 1.335, 1.295, 1.255, 1.218, 1.186, 1.157, 1.134, 1.113, 1.098, 1.089, 1.081, 1.079, 1.079, 1.085, 1.094, 1.107, 1.125, 1.149, 1.175, 1.207, 1.242, 1.281, 1.319, 1.359, 1.396, 1.425, 1.445, 1.447,
+ 1.449, 1.443, 1.417, 1.384, 1.345, 1.305, 1.266, 1.229, 1.197, 1.169, 1.145, 1.124, 1.111, 1.098, 1.091, 1.089, 1.089, 1.094, 1.107, 1.118, 1.137, 1.159, 1.187, 1.218, 1.253, 1.291, 1.329, 1.369, 1.405, 1.433, 1.447, 1.449,
+ 1.453, 1.449, 1.425, 1.395, 1.358, 1.318, 1.281, 1.244, 1.211, 1.183, 1.158, 1.138, 1.124, 1.111, 1.104, 1.103, 1.103, 1.107, 1.118, 1.133, 1.151, 1.174, 1.201, 1.232, 1.267, 1.304, 1.344, 1.379, 1.413, 1.437, 1.449, 1.449,
+ 1.457, 1.453, 1.434, 1.405, 1.371, 1.335, 1.297, 1.261, 1.229, 1.199, 1.174, 1.155, 1.138, 1.126, 1.119, 1.117, 1.117, 1.124, 1.133, 1.149, 1.167, 1.189, 1.217, 1.248, 1.284, 1.319, 1.357, 1.393, 1.423, 1.444, 1.452, 1.452,
+ 1.459, 1.457, 1.443, 1.418, 1.385, 1.352, 1.314, 1.279, 1.246, 1.218, 1.193, 1.174, 1.155, 1.144, 1.137, 1.136, 1.136, 1.141, 1.151, 1.167, 1.187, 1.208, 1.236, 1.267, 1.301, 1.337, 1.373, 1.405, 1.434, 1.453, 1.455, 1.455,
+ 1.461, 1.461, 1.454, 1.429, 1.401, 1.369, 1.333, 1.301, 1.269, 1.239, 1.216, 1.193, 1.177, 1.165, 1.158, 1.156, 1.156, 1.161, 1.171, 1.187, 1.208, 1.229, 1.258, 1.288, 1.321, 1.356, 1.389, 1.419, 1.445, 1.459, 1.459, 1.455,
+ 1.462, 1.462, 1.459, 1.442, 1.418, 1.386, 1.354, 1.322, 1.292, 1.262, 1.239, 1.216, 1.199, 1.187, 1.179, 1.178, 1.178, 1.184, 1.194, 1.208, 1.229, 1.253, 1.279, 1.309, 1.342, 1.375, 1.406, 1.433, 1.452, 1.464, 1.464, 1.454,
+ 1.461, 1.465, 1.465, 1.454, 1.431, 1.405, 1.376, 1.346, 1.316, 1.288, 1.262, 1.242, 1.223, 1.212, 1.205, 1.203, 1.203, 1.208, 1.218, 1.234, 1.253, 1.279, 1.305, 1.334, 1.363, 1.393, 1.421, 1.445, 1.461, 1.465, 1.464, 1.452,
+ 1.459, 1.465, 1.466, 1.461, 1.443, 1.421, 1.395, 1.368, 1.341, 1.316, 1.288, 1.268, 1.251, 1.238, 1.232, 1.229, 1.229, 1.235, 1.246, 1.261, 1.279, 1.305, 1.331, 1.356, 1.385, 1.411, 1.435, 1.454, 1.466, 1.466, 1.464, 1.451,
+ 1.454, 1.465, 1.467, 1.466, 1.456, 1.436, 1.414, 1.389, 1.367, 1.341, 1.318, 1.297, 1.279, 1.269, 1.261, 1.259, 1.259, 1.265, 1.274, 1.288, 1.308, 1.331, 1.355, 1.381, 1.404, 1.428, 1.447, 1.462, 1.468, 1.467, 1.457, 1.445,
+ 1.447, 1.459, 1.466, 1.467, 1.463, 1.451, 1.434, 1.411, 1.389, 1.367, 1.344, 1.325, 1.311, 1.297, 1.292, 1.289, 1.289, 1.295, 1.303, 1.317, 1.336, 1.356, 1.381, 1.402, 1.423, 1.441, 1.457, 1.467, 1.468, 1.463, 1.451, 1.439,
+ 1.438, 1.449, 1.462, 1.464, 1.464, 1.459, 1.446, 1.429, 1.408, 1.388, 1.369, 1.353, 1.339, 1.329, 1.321, 1.321, 1.321, 1.325, 1.333, 1.348, 1.362, 1.379, 1.401, 1.421, 1.439, 1.454, 1.463, 1.465, 1.465, 1.456, 1.442, 1.427,
+ 1.429, 1.439, 1.454, 1.464, 1.464, 1.459, 1.449, 1.435, 1.421, 1.402, 1.385, 1.369, 1.353, 1.341, 1.338, 1.337, 1.337, 1.338, 1.348, 1.362, 1.378, 1.395, 1.411, 1.429, 1.445, 1.455, 1.463, 1.464, 1.457, 1.447, 1.427, 1.419
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.163, 2.177, 2.194, 2.196, 2.197, 2.192, 2.181, 2.161, 2.139, 2.113, 2.088, 2.063, 2.047, 2.041, 2.036, 2.036, 2.036, 2.037, 2.046, 2.059, 2.083, 2.113, 2.135, 2.158, 2.181, 2.193, 2.205, 2.205, 2.202, 2.189, 2.171, 2.158,
+ 2.169, 2.184, 2.195, 2.196, 2.194, 2.182, 2.163, 2.141, 2.116, 2.088, 2.063, 2.042, 2.025, 2.013, 2.004, 2.004, 2.006, 2.011, 2.022, 2.038, 2.059, 2.083, 2.113, 2.137, 2.162, 2.182, 2.197, 2.204, 2.203, 2.199, 2.183, 2.171,
+ 2.177, 2.187, 2.193, 2.193, 2.184, 2.166, 2.142, 2.116, 2.087, 2.057, 2.033, 2.008, 1.991, 1.977, 1.969, 1.969, 1.969, 1.975, 1.988, 2.006, 2.028, 2.055, 2.083, 2.114, 2.139, 2.166, 2.187, 2.199, 2.202, 2.201, 2.189, 2.179,
+ 2.183, 2.189, 2.192, 2.186, 2.172, 2.146, 2.119, 2.089, 2.058, 2.026, 2.001, 1.975, 1.956, 1.942, 1.934, 1.932, 1.933, 1.941, 1.955, 1.971, 1.995, 2.023, 2.055, 2.084, 2.119, 2.146, 2.171, 2.191, 2.201, 2.201, 2.194, 2.183,
+ 2.186, 2.189, 2.189, 2.177, 2.158, 2.127, 2.096, 2.059, 2.026, 1.998, 1.969, 1.944, 1.925, 1.911, 1.901, 1.901, 1.903, 1.912, 1.924, 1.941, 1.964, 1.995, 2.023, 2.058, 2.091, 2.126, 2.155, 2.181, 2.195, 2.199, 2.198, 2.188,
+ 2.189, 2.189, 2.184, 2.166, 2.138, 2.108, 2.071, 2.036, 1.999, 1.969, 1.941, 1.914, 1.894, 1.879, 1.871, 1.871, 1.872, 1.879, 1.893, 1.913, 1.937, 1.964, 1.997, 2.029, 2.065, 2.104, 2.137, 2.169, 2.187, 2.199, 2.199, 2.189,
+ 2.187, 2.186, 2.176, 2.154, 2.123, 2.087, 2.044, 2.011, 1.974, 1.941, 1.913, 1.887, 1.868, 1.852, 1.844, 1.843, 1.844, 1.852, 1.866, 1.885, 1.912, 1.937, 1.972, 2.004, 2.042, 2.081, 2.119, 2.154, 2.179, 2.195, 2.196, 2.193,
+ 2.187, 2.181, 2.167, 2.141, 2.103, 2.062, 2.023, 1.984, 1.947, 1.916, 1.887, 1.864, 1.841, 1.828, 1.821, 1.819, 1.819, 1.828, 1.842, 1.862, 1.885, 1.913, 1.945, 1.982, 2.021, 2.058, 2.102, 2.137, 2.168, 2.192, 2.193, 2.193,
+ 2.182, 2.181, 2.161, 2.127, 2.083, 2.044, 2.002, 1.961, 1.924, 1.891, 1.864, 1.841, 1.819, 1.806, 1.797, 1.797, 1.797, 1.805, 1.819, 1.841, 1.862, 1.892, 1.924, 1.959, 1.999, 2.041, 2.082, 2.123, 2.161, 2.185, 2.191, 2.192,
+ 2.182, 2.172, 2.149, 2.112, 2.069, 2.026, 1.982, 1.941, 1.904, 1.871, 1.841, 1.819, 1.799, 1.785, 1.776, 1.776, 1.778, 1.784, 1.798, 1.819, 1.841, 1.869, 1.903, 1.939, 1.977, 2.021, 2.067, 2.108, 2.145, 2.174, 2.189, 2.191,
+ 2.181, 2.167, 2.139, 2.098, 2.056, 2.006, 1.965, 1.921, 1.883, 1.851, 1.823, 1.799, 1.783, 1.767, 1.759, 1.758, 1.758, 1.767, 1.783, 1.798, 1.825, 1.851, 1.883, 1.919, 1.959, 2.004, 2.049, 2.094, 2.136, 2.167, 2.187, 2.189,
+ 2.179, 2.163, 2.131, 2.087, 2.041, 1.994, 1.948, 1.907, 1.871, 1.835, 1.806, 1.784, 1.767, 1.754, 1.744, 1.742, 1.742, 1.752, 1.767, 1.783, 1.808, 1.838, 1.869, 1.905, 1.945, 1.989, 2.036, 2.083, 2.128, 2.159, 2.183, 2.187,
+ 2.178, 2.161, 2.126, 2.082, 2.032, 1.982, 1.936, 1.896, 1.857, 1.823, 1.795, 1.772, 1.754, 1.744, 1.732, 1.731, 1.732, 1.742, 1.752, 1.771, 1.796, 1.824, 1.857, 1.895, 1.934, 1.977, 2.024, 2.071, 2.116, 2.154, 2.181, 2.185,
+ 2.177, 2.157, 2.121, 2.074, 2.025, 1.973, 1.927, 1.886, 1.849, 1.815, 1.787, 1.765, 1.746, 1.732, 1.725, 1.722, 1.724, 1.732, 1.743, 1.762, 1.786, 1.813, 1.848, 1.886, 1.924, 1.969, 2.017, 2.066, 2.111, 2.153, 2.179, 2.183,
+ 2.177, 2.155, 2.119, 2.072, 2.022, 1.969, 1.925, 1.881, 1.844, 1.811, 1.782, 1.758, 1.739, 1.725, 1.721, 1.717, 1.721, 1.724, 1.739, 1.757, 1.781, 1.809, 1.842, 1.879, 1.921, 1.965, 2.012, 2.062, 2.108, 2.151, 2.179, 2.182,
+ 2.177, 2.156, 2.121, 2.071, 2.021, 1.968, 1.922, 1.879, 1.842, 1.811, 1.781, 1.757, 1.739, 1.725, 1.717, 1.715, 1.715, 1.723, 1.737, 1.757, 1.779, 1.808, 1.841, 1.877, 1.918, 1.963, 2.011, 2.061, 2.107, 2.148, 2.179, 2.183,
+ 2.178, 2.157, 2.121, 2.072, 2.021, 1.969, 1.922, 1.881, 1.842, 1.811, 1.781, 1.758, 1.739, 1.726, 1.718, 1.717, 1.718, 1.723, 1.737, 1.757, 1.781, 1.809, 1.841, 1.877, 1.918, 1.964, 2.012, 2.061, 2.108, 2.149, 2.179, 2.183,
+ 2.178, 2.159, 2.124, 2.074, 2.024, 1.974, 1.926, 1.885, 1.847, 1.813, 1.784, 1.762, 1.743, 1.731, 1.725, 1.719, 1.723, 1.728, 1.742, 1.762, 1.785, 1.814, 1.847, 1.881, 1.922, 1.966, 2.017, 2.065, 2.109, 2.151, 2.181, 2.184,
+ 2.181, 2.163, 2.129, 2.082, 2.032, 1.982, 1.934, 1.891, 1.854, 1.822, 1.794, 1.769, 1.751, 1.739, 1.731, 1.727, 1.728, 1.739, 1.747, 1.768, 1.791, 1.821, 1.852, 1.889, 1.929, 1.972, 2.022, 2.071, 2.117, 2.155, 2.182, 2.189,
+ 2.184, 2.169, 2.135, 2.091, 2.041, 1.994, 1.947, 1.902, 1.865, 1.833, 1.805, 1.779, 1.762, 1.751, 1.739, 1.739, 1.739, 1.747, 1.761, 1.779, 1.803, 1.831, 1.864, 1.898, 1.941, 1.984, 2.033, 2.079, 2.123, 2.163, 2.188, 2.193,
+ 2.185, 2.174, 2.142, 2.099, 2.054, 2.004, 1.959, 1.917, 1.879, 1.846, 1.819, 1.794, 1.779, 1.762, 1.754, 1.753, 1.753, 1.761, 1.777, 1.793, 1.816, 1.843, 1.877, 1.913, 1.953, 1.995, 2.043, 2.091, 2.135, 2.169, 2.191, 2.196,
+ 2.191, 2.179, 2.154, 2.118, 2.069, 2.023, 1.977, 1.935, 1.898, 1.865, 1.834, 1.813, 1.794, 1.779, 1.769, 1.769, 1.769, 1.777, 1.793, 1.809, 1.834, 1.863, 1.895, 1.929, 1.972, 2.015, 2.061, 2.105, 2.145, 2.178, 2.195, 2.199,
+ 2.197, 2.188, 2.166, 2.129, 2.087, 2.041, 1.997, 1.956, 1.918, 1.884, 1.855, 1.834, 1.813, 1.798, 1.788, 1.788, 1.788, 1.796, 1.809, 1.832, 1.853, 1.881, 1.912, 1.949, 1.991, 2.033, 2.076, 2.119, 2.159, 2.187, 2.202, 2.205,
+ 2.202, 2.197, 2.176, 2.148, 2.106, 2.065, 2.021, 1.979, 1.943, 1.909, 1.879, 1.855, 1.835, 1.819, 1.811, 1.811, 1.811, 1.818, 1.832, 1.853, 1.875, 1.904, 1.937, 1.972, 2.013, 2.055, 2.097, 2.138, 2.175, 2.197, 2.206, 2.207,
+ 2.205, 2.202, 2.189, 2.162, 2.126, 2.084, 2.044, 2.004, 1.967, 1.935, 1.907, 1.879, 1.861, 1.845, 1.838, 1.835, 1.835, 1.844, 1.855, 1.875, 1.902, 1.928, 1.961, 1.998, 2.033, 2.076, 2.118, 2.155, 2.186, 2.205, 2.208, 2.208,
+ 2.207, 2.205, 2.195, 2.175, 2.145, 2.108, 2.069, 2.029, 1.996, 1.963, 1.934, 1.908, 1.885, 1.872, 1.864, 1.863, 1.863, 1.869, 1.884, 1.902, 1.928, 1.956, 1.989, 2.023, 2.059, 2.099, 2.137, 2.172, 2.199, 2.212, 2.213, 2.209,
+ 2.207, 2.207, 2.203, 2.188, 2.162, 2.128, 2.094, 2.058, 2.023, 1.993, 1.963, 1.936, 1.916, 1.899, 1.893, 1.892, 1.893, 1.899, 1.912, 1.929, 1.956, 1.986, 2.016, 2.049, 2.084, 2.121, 2.156, 2.187, 2.208, 2.215, 2.215, 2.208,
+ 2.205, 2.208, 2.209, 2.199, 2.178, 2.149, 2.117, 2.083, 2.052, 2.023, 1.993, 1.967, 1.947, 1.933, 1.925, 1.922, 1.922, 1.929, 1.943, 1.961, 1.986, 2.015, 2.045, 2.076, 2.109, 2.143, 2.173, 2.198, 2.214, 2.218, 2.216, 2.205,
+ 2.201, 2.207, 2.211, 2.211, 2.193, 2.168, 2.141, 2.112, 2.082, 2.052, 2.025, 2.001, 1.981, 1.967, 1.959, 1.958, 1.958, 1.967, 1.975, 1.992, 2.018, 2.046, 2.076, 2.105, 2.136, 2.163, 2.189, 2.208, 2.217, 2.217, 2.212, 2.203,
+ 2.194, 2.204, 2.212, 2.213, 2.203, 2.187, 2.165, 2.139, 2.112, 2.083, 2.055, 2.034, 2.016, 2.001, 1.993, 1.993, 1.994, 1.999, 2.011, 2.027, 2.051, 2.077, 2.105, 2.133, 2.158, 2.181, 2.202, 2.217, 2.218, 2.218, 2.206, 2.193,
+ 2.185, 2.198, 2.213, 2.214, 2.212, 2.201, 2.184, 2.163, 2.135, 2.111, 2.089, 2.071, 2.052, 2.039, 2.032, 2.031, 2.031, 2.036, 2.048, 2.065, 2.085, 2.106, 2.131, 2.155, 2.178, 2.198, 2.212, 2.219, 2.219, 2.215, 2.201, 2.185,
+ 2.176, 2.191, 2.208, 2.217, 2.216, 2.205, 2.195, 2.177, 2.156, 2.133, 2.109, 2.089, 2.071, 2.055, 2.053, 2.053, 2.053, 2.057, 2.065, 2.085, 2.105, 2.123, 2.149, 2.171, 2.192, 2.205, 2.217, 2.219, 2.219, 2.202, 2.185, 2.181
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.518, 2.513, 2.503, 2.496, 2.488, 2.484, 2.485, 2.485, 2.486, 2.487, 2.487, 2.489, 2.494, 2.496, 2.496, 2.497, 2.499, 2.499, 2.496, 2.495, 2.492, 2.491, 2.491, 2.491, 2.492, 2.493, 2.495, 2.501, 2.508, 2.516, 2.528, 2.533,
+ 2.515, 2.508, 2.495, 2.487, 2.483, 2.481, 2.482, 2.483, 2.485, 2.487, 2.489, 2.491, 2.495, 2.497, 2.498, 2.501, 2.502, 2.502, 2.499, 2.496, 2.494, 2.491, 2.491, 2.489, 2.489, 2.491, 2.493, 2.496, 2.502, 2.511, 2.521, 2.531,
+ 2.507, 2.495, 2.486, 2.482, 2.478, 2.477, 2.481, 2.482, 2.484, 2.488, 2.491, 2.495, 2.499, 2.502, 2.506, 2.508, 2.509, 2.508, 2.505, 2.501, 2.497, 2.493, 2.491, 2.489, 2.488, 2.489, 2.489, 2.492, 2.496, 2.501, 2.511, 2.524,
+ 2.501, 2.487, 2.482, 2.481, 2.478, 2.477, 2.481, 2.483, 2.487, 2.491, 2.501, 2.503, 2.509, 2.511, 2.518, 2.519, 2.519, 2.519, 2.516, 2.509, 2.504, 2.498, 2.495, 2.493, 2.489, 2.489, 2.488, 2.489, 2.492, 2.498, 2.505, 2.523,
+ 2.499, 2.484, 2.481, 2.476, 2.476, 2.476, 2.481, 2.485, 2.492, 2.501, 2.509, 2.514, 2.519, 2.524, 2.528, 2.531, 2.533, 2.533, 2.525, 2.519, 2.514, 2.507, 2.501, 2.497, 2.493, 2.489, 2.489, 2.488, 2.491, 2.494, 2.501, 2.514,
+ 2.497, 2.483, 2.478, 2.476, 2.476, 2.478, 2.482, 2.491, 2.499, 2.509, 2.515, 2.522, 2.528, 2.535, 2.539, 2.541, 2.543, 2.542, 2.539, 2.529, 2.522, 2.516, 2.507, 2.502, 2.497, 2.491, 2.489, 2.488, 2.489, 2.492, 2.498, 2.514,
+ 2.492, 2.479, 2.476, 2.475, 2.476, 2.481, 2.488, 2.496, 2.505, 2.516, 2.524, 2.532, 2.541, 2.545, 2.552, 2.554, 2.554, 2.554, 2.548, 2.541, 2.532, 2.522, 2.516, 2.507, 2.502, 2.494, 2.491, 2.489, 2.489, 2.492, 2.494, 2.511,
+ 2.491, 2.479, 2.476, 2.477, 2.478, 2.482, 2.491, 2.502, 2.514, 2.524, 2.533, 2.543, 2.548, 2.555, 2.562, 2.566, 2.567, 2.562, 2.557, 2.551, 2.541, 2.531, 2.523, 2.512, 2.506, 2.498, 2.493, 2.491, 2.491, 2.491, 2.493, 2.507,
+ 2.489, 2.478, 2.476, 2.477, 2.481, 2.485, 2.494, 2.507, 2.517, 2.529, 2.542, 2.548, 2.557, 2.563, 2.567, 2.571, 2.572, 2.571, 2.565, 2.558, 2.549, 2.538, 2.528, 2.521, 2.509, 2.501, 2.494, 2.492, 2.491, 2.491, 2.491, 2.505,
+ 2.488, 2.478, 2.477, 2.478, 2.482, 2.489, 2.499, 2.509, 2.523, 2.538, 2.548, 2.556, 2.563, 2.568, 2.573, 2.577, 2.578, 2.577, 2.573, 2.564, 2.555, 2.543, 2.535, 2.524, 2.515, 2.504, 2.495, 2.492, 2.489, 2.488, 2.489, 2.501,
+ 2.486, 2.476, 2.475, 2.477, 2.483, 2.491, 2.503, 2.515, 2.529, 2.542, 2.553, 2.562, 2.568, 2.574, 2.581, 2.583, 2.584, 2.581, 2.578, 2.571, 2.562, 2.551, 2.539, 2.531, 2.517, 2.508, 2.497, 2.492, 2.488, 2.487, 2.489, 2.498,
+ 2.486, 2.476, 2.475, 2.479, 2.484, 2.492, 2.504, 2.519, 2.533, 2.544, 2.557, 2.566, 2.573, 2.581, 2.584, 2.588, 2.588, 2.586, 2.581, 2.575, 2.567, 2.555, 2.546, 2.534, 2.517, 2.509, 2.499, 2.492, 2.489, 2.485, 2.488, 2.497,
+ 2.487, 2.476, 2.476, 2.479, 2.486, 2.494, 2.506, 2.521, 2.535, 2.549, 2.559, 2.571, 2.578, 2.583, 2.589, 2.591, 2.591, 2.591, 2.587, 2.579, 2.571, 2.559, 2.551, 2.538, 2.523, 2.513, 2.503, 2.493, 2.489, 2.486, 2.487, 2.499,
+ 2.486, 2.475, 2.475, 2.479, 2.486, 2.495, 2.509, 2.525, 2.541, 2.555, 2.563, 2.573, 2.582, 2.588, 2.591, 2.594, 2.595, 2.592, 2.591, 2.585, 2.574, 2.564, 2.552, 2.541, 2.525, 2.514, 2.503, 2.493, 2.489, 2.486, 2.486, 2.501,
+ 2.486, 2.475, 2.475, 2.479, 2.488, 2.497, 2.509, 2.526, 2.542, 2.556, 2.564, 2.575, 2.584, 2.591, 2.595, 2.596, 2.597, 2.595, 2.592, 2.587, 2.577, 2.568, 2.554, 2.542, 2.527, 2.515, 2.504, 2.494, 2.491, 2.487, 2.487, 2.505,
+ 2.484, 2.476, 2.475, 2.478, 2.488, 2.498, 2.509, 2.526, 2.542, 2.555, 2.565, 2.576, 2.584, 2.589, 2.595, 2.598, 2.598, 2.597, 2.593, 2.587, 2.578, 2.569, 2.556, 2.543, 2.528, 2.515, 2.504, 2.494, 2.489, 2.485, 2.485, 2.501,
+ 2.484, 2.475, 2.475, 2.478, 2.489, 2.498, 2.509, 2.524, 2.539, 2.553, 2.565, 2.576, 2.584, 2.589, 2.594, 2.597, 2.597, 2.596, 2.593, 2.587, 2.577, 2.569, 2.555, 2.543, 2.529, 2.515, 2.503, 2.496, 2.491, 2.485, 2.486, 2.497,
+ 2.484, 2.474, 2.474, 2.479, 2.487, 2.497, 2.509, 2.523, 2.539, 2.551, 2.563, 2.574, 2.581, 2.587, 2.592, 2.595, 2.596, 2.595, 2.591, 2.584, 2.574, 2.567, 2.554, 2.541, 2.526, 2.514, 2.503, 2.495, 2.489, 2.485, 2.486, 2.497,
+ 2.484, 2.475, 2.475, 2.478, 2.485, 2.494, 2.507, 2.522, 2.535, 2.546, 2.559, 2.568, 2.579, 2.584, 2.589, 2.592, 2.593, 2.592, 2.588, 2.579, 2.571, 2.562, 2.551, 2.537, 2.524, 2.514, 2.501, 2.493, 2.489, 2.486, 2.487, 2.498,
+ 2.485, 2.476, 2.475, 2.477, 2.485, 2.491, 2.506, 2.519, 2.531, 2.544, 2.555, 2.563, 2.571, 2.581, 2.584, 2.589, 2.589, 2.588, 2.583, 2.576, 2.566, 2.555, 2.546, 2.534, 2.522, 2.511, 2.499, 2.491, 2.488, 2.486, 2.487, 2.502,
+ 2.487, 2.477, 2.475, 2.477, 2.483, 2.489, 2.503, 2.515, 2.525, 2.541, 2.551, 2.559, 2.567, 2.573, 2.579, 2.582, 2.583, 2.582, 2.576, 2.569, 2.562, 2.549, 2.542, 2.527, 2.518, 2.505, 2.497, 2.491, 2.489, 2.487, 2.487, 2.502,
+ 2.487, 2.478, 2.475, 2.477, 2.482, 2.489, 2.497, 2.512, 2.522, 2.536, 2.544, 2.551, 2.562, 2.566, 2.573, 2.578, 2.578, 2.575, 2.571, 2.564, 2.556, 2.548, 2.536, 2.523, 2.513, 2.503, 2.493, 2.489, 2.487, 2.486, 2.487, 2.502,
+ 2.488, 2.479, 2.477, 2.478, 2.482, 2.488, 2.496, 2.505, 2.516, 2.528, 2.538, 2.547, 2.553, 2.561, 2.565, 2.569, 2.569, 2.568, 2.564, 2.558, 2.549, 2.541, 2.531, 2.517, 2.509, 2.499, 2.492, 2.488, 2.486, 2.484, 2.486, 2.503,
+ 2.492, 2.482, 2.479, 2.479, 2.482, 2.487, 2.491, 2.501, 2.512, 2.523, 2.531, 2.541, 2.549, 2.552, 2.558, 2.561, 2.562, 2.559, 2.558, 2.552, 2.542, 2.535, 2.525, 2.514, 2.505, 2.497, 2.491, 2.486, 2.485, 2.484, 2.487, 2.503,
+ 2.495, 2.483, 2.479, 2.479, 2.482, 2.487, 2.491, 2.498, 2.508, 2.515, 2.526, 2.533, 2.541, 2.547, 2.551, 2.554, 2.555, 2.554, 2.552, 2.541, 2.537, 2.527, 2.519, 2.507, 2.502, 2.495, 2.488, 2.485, 2.484, 2.485, 2.488, 2.503,
+ 2.499, 2.485, 2.483, 2.481, 2.482, 2.486, 2.489, 2.494, 2.504, 2.511, 2.519, 2.527, 2.531, 2.539, 2.542, 2.546, 2.546, 2.545, 2.539, 2.535, 2.527, 2.522, 2.509, 2.505, 2.497, 2.491, 2.486, 2.485, 2.485, 2.487, 2.491, 2.506,
+ 2.499, 2.489, 2.483, 2.481, 2.481, 2.483, 2.488, 2.491, 2.499, 2.506, 2.512, 2.519, 2.524, 2.529, 2.535, 2.537, 2.536, 2.534, 2.532, 2.525, 2.522, 2.514, 2.506, 2.499, 2.492, 2.489, 2.485, 2.484, 2.485, 2.488, 2.492, 2.506,
+ 2.507, 2.494, 2.486, 2.483, 2.482, 2.482, 2.486, 2.488, 2.495, 2.501, 2.507, 2.511, 2.517, 2.519, 2.523, 2.525, 2.525, 2.523, 2.523, 2.521, 2.514, 2.506, 2.502, 2.496, 2.491, 2.488, 2.485, 2.485, 2.487, 2.489, 2.496, 2.516,
+ 2.511, 2.503, 2.489, 2.486, 2.485, 2.485, 2.485, 2.487, 2.489, 2.495, 2.501, 2.505, 2.509, 2.514, 2.517, 2.519, 2.518, 2.517, 2.515, 2.511, 2.505, 2.501, 2.495, 2.492, 2.488, 2.486, 2.485, 2.486, 2.488, 2.492, 2.499, 2.519,
+ 2.517, 2.505, 2.494, 2.489, 2.487, 2.486, 2.486, 2.486, 2.489, 2.491, 2.496, 2.499, 2.503, 2.506, 2.508, 2.509, 2.511, 2.509, 2.507, 2.503, 2.501, 2.496, 2.493, 2.489, 2.485, 2.485, 2.486, 2.487, 2.491, 2.495, 2.505, 2.526,
+ 2.526, 2.516, 2.504, 2.494, 2.493, 2.489, 2.489, 2.489, 2.489, 2.491, 2.496, 2.498, 2.501, 2.504, 2.506, 2.506, 2.506, 2.505, 2.503, 2.501, 2.499, 2.496, 2.494, 2.491, 2.487, 2.486, 2.489, 2.492, 2.497, 2.505, 2.517, 2.528,
+ 2.529, 2.526, 2.508, 2.502, 2.501, 2.498, 2.495, 2.495, 2.495, 2.495, 2.497, 2.499, 2.501, 2.503, 2.504, 2.506, 2.505, 2.505, 2.503, 2.501, 2.499, 2.496, 2.495, 2.494, 2.492, 2.494, 2.494, 2.498, 2.504, 2.513, 2.525, 2.536
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.427, 1.425, 1.423, 1.422, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.422, 1.423, 1.424, 1.425, 1.426, 1.426, 1.426, 1.425, 1.425, 1.424, 1.422, 1.421, 1.421, 1.421, 1.421, 1.422, 1.422, 1.422, 1.424, 1.424, 1.426, 1.428,
+ 1.426, 1.424, 1.422, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.422, 1.423, 1.424, 1.425, 1.426, 1.427, 1.427, 1.427, 1.426, 1.425, 1.424, 1.422, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.422, 1.424, 1.427,
+ 1.423, 1.421, 1.421, 1.419, 1.419, 1.418, 1.419, 1.419, 1.421, 1.423, 1.425, 1.426, 1.428, 1.429, 1.431, 1.431, 1.431, 1.431, 1.429, 1.426, 1.424, 1.422, 1.421, 1.421, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.422, 1.425,
+ 1.422, 1.419, 1.419, 1.419, 1.418, 1.418, 1.419, 1.421, 1.422, 1.426, 1.428, 1.429, 1.433, 1.434, 1.436, 1.436, 1.436, 1.434, 1.432, 1.429, 1.426, 1.424, 1.423, 1.422, 1.421, 1.419, 1.419, 1.419, 1.419, 1.419, 1.421, 1.425,
+ 1.422, 1.419, 1.419, 1.418, 1.418, 1.419, 1.419, 1.422, 1.425, 1.429, 1.432, 1.435, 1.436, 1.438, 1.439, 1.439, 1.441, 1.439, 1.435, 1.433, 1.429, 1.427, 1.425, 1.423, 1.422, 1.419, 1.419, 1.418, 1.418, 1.418, 1.419, 1.425,
+ 1.422, 1.419, 1.418, 1.418, 1.418, 1.419, 1.421, 1.424, 1.428, 1.432, 1.436, 1.437, 1.439, 1.442, 1.443, 1.445, 1.444, 1.443, 1.441, 1.436, 1.434, 1.431, 1.427, 1.425, 1.422, 1.421, 1.419, 1.418, 1.418, 1.418, 1.419, 1.424,
+ 1.422, 1.418, 1.417, 1.418, 1.419, 1.421, 1.423, 1.427, 1.431, 1.436, 1.438, 1.442, 1.444, 1.446, 1.448, 1.449, 1.448, 1.446, 1.445, 1.441, 1.436, 1.434, 1.429, 1.427, 1.423, 1.421, 1.419, 1.418, 1.418, 1.418, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.418, 1.419, 1.421, 1.424, 1.429, 1.434, 1.438, 1.442, 1.445, 1.447, 1.449, 1.451, 1.452, 1.452, 1.449, 1.447, 1.445, 1.441, 1.436, 1.433, 1.429, 1.425, 1.422, 1.419, 1.419, 1.418, 1.417, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.419, 1.419, 1.423, 1.426, 1.432, 1.436, 1.441, 1.445, 1.448, 1.449, 1.452, 1.453, 1.454, 1.454, 1.453, 1.451, 1.447, 1.444, 1.439, 1.433, 1.431, 1.427, 1.422, 1.421, 1.419, 1.418, 1.417, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.419, 1.421, 1.423, 1.428, 1.433, 1.439, 1.443, 1.448, 1.449, 1.453, 1.454, 1.455, 1.456, 1.456, 1.454, 1.453, 1.449, 1.446, 1.441, 1.437, 1.433, 1.429, 1.423, 1.421, 1.419, 1.418, 1.416, 1.417, 1.423,
+ 1.421, 1.417, 1.417, 1.419, 1.422, 1.424, 1.429, 1.435, 1.441, 1.444, 1.449, 1.453, 1.454, 1.456, 1.458, 1.459, 1.458, 1.456, 1.454, 1.451, 1.448, 1.442, 1.439, 1.435, 1.429, 1.426, 1.421, 1.419, 1.418, 1.416, 1.417, 1.422,
+ 1.419, 1.418, 1.417, 1.419, 1.422, 1.425, 1.429, 1.436, 1.442, 1.446, 1.451, 1.454, 1.456, 1.458, 1.461, 1.461, 1.461, 1.459, 1.456, 1.453, 1.451, 1.446, 1.441, 1.436, 1.431, 1.427, 1.422, 1.419, 1.418, 1.416, 1.417, 1.422,
+ 1.419, 1.418, 1.418, 1.421, 1.423, 1.426, 1.431, 1.437, 1.444, 1.449, 1.452, 1.456, 1.458, 1.461, 1.462, 1.463, 1.463, 1.461, 1.458, 1.454, 1.452, 1.447, 1.443, 1.438, 1.432, 1.428, 1.423, 1.421, 1.419, 1.417, 1.417, 1.421,
+ 1.419, 1.418, 1.417, 1.421, 1.423, 1.428, 1.432, 1.439, 1.445, 1.451, 1.453, 1.457, 1.459, 1.462, 1.464, 1.465, 1.465, 1.463, 1.461, 1.457, 1.453, 1.449, 1.444, 1.441, 1.432, 1.429, 1.425, 1.421, 1.419, 1.417, 1.418, 1.422,
+ 1.418, 1.417, 1.417, 1.419, 1.423, 1.428, 1.433, 1.439, 1.446, 1.451, 1.453, 1.457, 1.461, 1.464, 1.465, 1.466, 1.466, 1.464, 1.462, 1.459, 1.454, 1.451, 1.445, 1.441, 1.436, 1.429, 1.425, 1.422, 1.421, 1.417, 1.417, 1.423,
+ 1.417, 1.416, 1.416, 1.419, 1.423, 1.428, 1.433, 1.441, 1.446, 1.451, 1.454, 1.458, 1.461, 1.463, 1.465, 1.466, 1.466, 1.465, 1.463, 1.459, 1.454, 1.451, 1.446, 1.441, 1.437, 1.431, 1.426, 1.422, 1.421, 1.418, 1.418, 1.423,
+ 1.417, 1.416, 1.417, 1.418, 1.423, 1.428, 1.433, 1.439, 1.445, 1.451, 1.453, 1.457, 1.461, 1.463, 1.465, 1.466, 1.466, 1.464, 1.462, 1.459, 1.454, 1.451, 1.446, 1.441, 1.437, 1.431, 1.426, 1.422, 1.419, 1.417, 1.417, 1.422,
+ 1.417, 1.416, 1.416, 1.418, 1.422, 1.428, 1.433, 1.438, 1.444, 1.449, 1.453, 1.456, 1.459, 1.462, 1.464, 1.465, 1.465, 1.463, 1.461, 1.458, 1.453, 1.449, 1.445, 1.441, 1.435, 1.429, 1.426, 1.421, 1.419, 1.417, 1.417, 1.422,
+ 1.418, 1.416, 1.416, 1.418, 1.421, 1.426, 1.432, 1.438, 1.443, 1.447, 1.451, 1.454, 1.458, 1.459, 1.462, 1.463, 1.463, 1.462, 1.459, 1.455, 1.451, 1.447, 1.443, 1.439, 1.434, 1.429, 1.425, 1.421, 1.419, 1.417, 1.417, 1.422,
+ 1.418, 1.416, 1.416, 1.418, 1.421, 1.425, 1.431, 1.435, 1.442, 1.445, 1.449, 1.452, 1.455, 1.458, 1.458, 1.461, 1.461, 1.459, 1.456, 1.453, 1.449, 1.445, 1.442, 1.436, 1.433, 1.427, 1.425, 1.421, 1.419, 1.418, 1.418, 1.422,
+ 1.419, 1.416, 1.415, 1.417, 1.419, 1.424, 1.429, 1.434, 1.439, 1.443, 1.446, 1.449, 1.452, 1.454, 1.456, 1.457, 1.457, 1.456, 1.453, 1.451, 1.447, 1.443, 1.441, 1.435, 1.431, 1.426, 1.424, 1.421, 1.419, 1.418, 1.418, 1.422,
+ 1.419, 1.416, 1.415, 1.416, 1.419, 1.422, 1.426, 1.433, 1.437, 1.441, 1.444, 1.447, 1.449, 1.452, 1.453, 1.455, 1.455, 1.453, 1.451, 1.447, 1.444, 1.441, 1.438, 1.432, 1.428, 1.424, 1.421, 1.419, 1.418, 1.417, 1.417, 1.421,
+ 1.419, 1.416, 1.415, 1.416, 1.418, 1.421, 1.425, 1.431, 1.435, 1.438, 1.442, 1.445, 1.446, 1.449, 1.451, 1.451, 1.451, 1.451, 1.447, 1.445, 1.443, 1.439, 1.434, 1.431, 1.427, 1.422, 1.421, 1.418, 1.417, 1.417, 1.417, 1.421,
+ 1.418, 1.416, 1.415, 1.416, 1.417, 1.421, 1.423, 1.428, 1.433, 1.437, 1.439, 1.442, 1.444, 1.446, 1.448, 1.449, 1.449, 1.447, 1.445, 1.443, 1.439, 1.437, 1.432, 1.429, 1.425, 1.422, 1.419, 1.417, 1.417, 1.416, 1.416, 1.419,
+ 1.418, 1.416, 1.416, 1.416, 1.417, 1.421, 1.422, 1.426, 1.429, 1.433, 1.436, 1.438, 1.441, 1.443, 1.445, 1.446, 1.445, 1.445, 1.443, 1.439, 1.437, 1.434, 1.431, 1.427, 1.424, 1.421, 1.419, 1.417, 1.417, 1.416, 1.416, 1.421,
+ 1.419, 1.417, 1.416, 1.416, 1.417, 1.421, 1.422, 1.424, 1.427, 1.429, 1.432, 1.436, 1.437, 1.439, 1.442, 1.443, 1.443, 1.441, 1.439, 1.437, 1.434, 1.431, 1.429, 1.425, 1.422, 1.421, 1.419, 1.417, 1.416, 1.416, 1.417, 1.419,
+ 1.421, 1.418, 1.416, 1.417, 1.418, 1.421, 1.421, 1.423, 1.424, 1.427, 1.429, 1.432, 1.434, 1.436, 1.438, 1.439, 1.439, 1.438, 1.436, 1.434, 1.431, 1.429, 1.426, 1.423, 1.422, 1.421, 1.418, 1.417, 1.417, 1.417, 1.417, 1.421,
+ 1.423, 1.419, 1.418, 1.418, 1.419, 1.419, 1.421, 1.422, 1.423, 1.424, 1.427, 1.429, 1.432, 1.432, 1.434, 1.435, 1.435, 1.434, 1.433, 1.431, 1.429, 1.426, 1.424, 1.422, 1.421, 1.419, 1.418, 1.417, 1.417, 1.417, 1.418, 1.421,
+ 1.425, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.421, 1.421, 1.423, 1.424, 1.426, 1.428, 1.431, 1.431, 1.432, 1.432, 1.431, 1.431, 1.428, 1.425, 1.425, 1.422, 1.421, 1.419, 1.419, 1.418, 1.418, 1.418, 1.418, 1.419, 1.425,
+ 1.426, 1.422, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.421, 1.422, 1.424, 1.426, 1.427, 1.428, 1.429, 1.429, 1.429, 1.427, 1.424, 1.423, 1.422, 1.421, 1.419, 1.418, 1.418, 1.418, 1.418, 1.418, 1.418, 1.419, 1.426,
+ 1.428, 1.425, 1.421, 1.421, 1.421, 1.421, 1.421, 1.419, 1.419, 1.421, 1.422, 1.423, 1.424, 1.426, 1.426, 1.426, 1.426, 1.425, 1.424, 1.424, 1.422, 1.422, 1.421, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.423, 1.426,
+ 1.429, 1.427, 1.424, 1.422, 1.422, 1.422, 1.421, 1.421, 1.421, 1.422, 1.422, 1.422, 1.424, 1.425, 1.426, 1.426, 1.425, 1.425, 1.424, 1.423, 1.422, 1.422, 1.421, 1.421, 1.421, 1.421, 1.419, 1.419, 1.421, 1.422, 1.424, 1.426
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.964, 2.872, 2.691, 2.544, 2.416, 2.302, 2.196, 2.093, 2.006, 1.928, 1.852, 1.801, 1.769, 1.752, 1.743, 1.743, 1.743, 1.746, 1.759, 1.784, 1.824, 1.888, 1.968, 2.052, 2.149, 2.253, 2.359, 2.483, 2.626, 2.785, 2.988, 3.051,
+ 2.872, 2.748, 2.583, 2.442, 2.313, 2.201, 2.104, 2.012, 1.928, 1.852, 1.791, 1.742, 1.701, 1.671, 1.651, 1.643, 1.643, 1.659, 1.685, 1.721, 1.768, 1.824, 1.888, 1.971, 2.068, 2.152, 2.259, 2.381, 2.514, 2.669, 2.853, 2.988,
+ 2.761, 2.655, 2.497, 2.356, 2.226, 2.114, 2.012, 1.928, 1.845, 1.769, 1.707, 1.653, 1.612, 1.583, 1.562, 1.556, 1.556, 1.572, 1.599, 1.635, 1.681, 1.742, 1.806, 1.888, 1.971, 2.068, 2.175, 2.292, 2.431, 2.576, 2.747, 2.853,
+ 2.679, 2.571, 2.415, 2.275, 2.151, 2.035, 1.936, 1.845, 1.769, 1.689, 1.623, 1.572, 1.532, 1.501, 1.481, 1.473, 1.473, 1.492, 1.517, 1.556, 1.599, 1.659, 1.731, 1.806, 1.895, 1.992, 2.101, 2.218, 2.349, 2.493, 2.664, 2.753,
+ 2.609, 2.492, 2.339, 2.204, 2.079, 1.971, 1.865, 1.772, 1.689, 1.619, 1.551, 1.499, 1.457, 1.423, 1.405, 1.397, 1.397, 1.411, 1.438, 1.477, 1.525, 1.585, 1.659, 1.731, 1.823, 1.922, 2.027, 2.148, 2.275, 2.422, 2.586, 2.683,
+ 2.545, 2.426, 2.279, 2.139, 2.014, 1.903, 1.799, 1.702, 1.619, 1.551, 1.482, 1.427, 1.385, 1.353, 1.331, 1.325, 1.325, 1.338, 1.364, 1.403, 1.455, 1.522, 1.585, 1.665, 1.757, 1.858, 1.963, 2.081, 2.207, 2.356, 2.518, 2.615,
+ 2.489, 2.367, 2.218, 2.079, 1.956, 1.844, 1.739, 1.642, 1.559, 1.482, 1.426, 1.363, 1.321, 1.287, 1.266, 1.259, 1.259, 1.274, 1.301, 1.339, 1.395, 1.455, 1.523, 1.606, 1.697, 1.797, 1.905, 2.024, 2.154, 2.296, 2.455, 2.563,
+ 2.439, 2.316, 2.164, 2.028, 1.906, 1.793, 1.686, 1.589, 1.505, 1.427, 1.363, 1.308, 1.261, 1.229, 1.207, 1.202, 1.202, 1.215, 1.242, 1.283, 1.339, 1.395, 1.467, 1.551, 1.639, 1.742, 1.851, 1.972, 2.104, 2.243, 2.402, 2.515,
+ 2.398, 2.262, 2.116, 1.982, 1.861, 1.745, 1.639, 1.541, 1.456, 1.377, 1.308, 1.261, 1.208, 1.177, 1.157, 1.153, 1.153, 1.167, 1.191, 1.233, 1.283, 1.343, 1.418, 1.499, 1.591, 1.696, 1.804, 1.928, 2.057, 2.194, 2.352, 2.471,
+ 2.363, 2.222, 2.078, 1.942, 1.818, 1.706, 1.597, 1.501, 1.412, 1.334, 1.266, 1.208, 1.171, 1.134, 1.113, 1.109, 1.109, 1.123, 1.149, 1.191, 1.233, 1.296, 1.371, 1.457, 1.546, 1.654, 1.768, 1.886, 2.014, 2.155, 2.312, 2.436,
+ 2.334, 2.188, 2.042, 1.909, 1.783, 1.668, 1.561, 1.464, 1.374, 1.295, 1.228, 1.171, 1.134, 1.098, 1.076, 1.072, 1.072, 1.087, 1.119, 1.149, 1.196, 1.259, 1.332, 1.419, 1.514, 1.616, 1.728, 1.849, 1.981, 2.123, 2.276, 2.406,
+ 2.306, 2.159, 2.015, 1.881, 1.753, 1.639, 1.533, 1.434, 1.341, 1.263, 1.195, 1.139, 1.098, 1.074, 1.046, 1.044, 1.045, 1.059, 1.087, 1.119, 1.165, 1.227, 1.302, 1.387, 1.482, 1.586, 1.698, 1.819, 1.953, 2.093, 2.248, 2.383,
+ 2.291, 2.141, 1.991, 1.856, 1.732, 1.615, 1.508, 1.409, 1.318, 1.238, 1.171, 1.114, 1.074, 1.046, 1.027, 1.023, 1.025, 1.043, 1.059, 1.095, 1.142, 1.203, 1.278, 1.362, 1.456, 1.559, 1.673, 1.796, 1.928, 2.071, 2.225, 2.359,
+ 2.279, 2.118, 1.972, 1.839, 1.715, 1.599, 1.488, 1.389, 1.298, 1.219, 1.153, 1.097, 1.057, 1.027, 1.018, 1.009, 1.013, 1.025, 1.044, 1.078, 1.125, 1.186, 1.258, 1.342, 1.438, 1.541, 1.655, 1.779, 1.909, 2.053, 2.211, 2.351,
+ 2.274, 2.108, 1.963, 1.831, 1.706, 1.588, 1.477, 1.376, 1.288, 1.207, 1.139, 1.086, 1.049, 1.021, 1.005, 1.002, 1.004, 1.013, 1.035, 1.069, 1.116, 1.176, 1.246, 1.331, 1.427, 1.531, 1.645, 1.767, 1.899, 2.045, 2.197, 2.351,
+ 2.274, 2.106, 1.961, 1.827, 1.701, 1.585, 1.474, 1.374, 1.285, 1.206, 1.139, 1.085, 1.047, 1.019, 1.003, 1.001, 1.001, 1.012, 1.033, 1.067, 1.113, 1.173, 1.245, 1.329, 1.423, 1.529, 1.642, 1.765, 1.897, 2.042, 2.196, 2.349,
+ 2.274, 2.108, 1.961, 1.827, 1.701, 1.585, 1.474, 1.374, 1.285, 1.206, 1.139, 1.085, 1.047, 1.021, 1.005, 1.001, 1.004, 1.012, 1.033, 1.068, 1.113, 1.173, 1.246, 1.329, 1.423, 1.529, 1.642, 1.766, 1.897, 2.042, 2.198, 2.349,
+ 2.278, 2.116, 1.968, 1.833, 1.707, 1.591, 1.482, 1.382, 1.291, 1.214, 1.147, 1.091, 1.055, 1.028, 1.016, 1.006, 1.012, 1.018, 1.039, 1.074, 1.121, 1.182, 1.255, 1.339, 1.433, 1.538, 1.651, 1.777, 1.911, 2.051, 2.207, 2.351,
+ 2.283, 2.127, 1.979, 1.846, 1.723, 1.605, 1.496, 1.397, 1.309, 1.229, 1.162, 1.108, 1.067, 1.041, 1.027, 1.018, 1.018, 1.036, 1.051, 1.087, 1.136, 1.197, 1.269, 1.354, 1.448, 1.554, 1.664, 1.789, 1.922, 2.065, 2.222, 2.365,
+ 2.298, 2.145, 1.999, 1.865, 1.744, 1.627, 1.518, 1.421, 1.331, 1.251, 1.183, 1.129, 1.087, 1.065, 1.041, 1.036, 1.036, 1.051, 1.074, 1.107, 1.158, 1.219, 1.292, 1.378, 1.471, 1.575, 1.687, 1.809, 1.942, 2.085, 2.239, 2.378,
+ 2.315, 2.174, 2.024, 1.893, 1.768, 1.652, 1.543, 1.445, 1.355, 1.278, 1.211, 1.155, 1.116, 1.087, 1.066, 1.061, 1.061, 1.074, 1.105, 1.137, 1.186, 1.248, 1.322, 1.405, 1.498, 1.602, 1.713, 1.835, 1.965, 2.109, 2.267, 2.399,
+ 2.341, 2.206, 2.057, 1.923, 1.799, 1.685, 1.576, 1.479, 1.392, 1.312, 1.244, 1.187, 1.154, 1.116, 1.096, 1.092, 1.092, 1.106, 1.137, 1.173, 1.221, 1.282, 1.356, 1.439, 1.532, 1.635, 1.747, 1.869, 1.997, 2.141, 2.298, 2.425,
+ 2.375, 2.244, 2.098, 1.965, 1.839, 1.722, 1.614, 1.519, 1.434, 1.355, 1.288, 1.234, 1.187, 1.155, 1.136, 1.132, 1.132, 1.147, 1.173, 1.219, 1.263, 1.324, 1.398, 1.479, 1.571, 1.674, 1.784, 1.904, 2.035, 2.177, 2.336, 2.455,
+ 2.414, 2.286, 2.144, 2.011, 1.883, 1.767, 1.661, 1.566, 1.479, 1.401, 1.335, 1.286, 1.234, 1.202, 1.183, 1.178, 1.178, 1.195, 1.222, 1.263, 1.313, 1.372, 1.444, 1.526, 1.618, 1.718, 1.827, 1.951, 2.081, 2.221, 2.379, 2.498,
+ 2.463, 2.339, 2.191, 2.056, 1.931, 1.819, 1.712, 1.616, 1.529, 1.452, 1.392, 1.335, 1.286, 1.254, 1.235, 1.232, 1.232, 1.248, 1.275, 1.313, 1.371, 1.425, 1.495, 1.576, 1.671, 1.768, 1.877, 1.999, 2.128, 2.269, 2.428, 2.541,
+ 2.514, 2.396, 2.247, 2.112, 1.988, 1.873, 1.766, 1.671, 1.588, 1.513, 1.452, 1.392, 1.348, 1.316, 1.298, 1.292, 1.292, 1.307, 1.336, 1.373, 1.425, 1.486, 1.552, 1.636, 1.728, 1.826, 1.933, 2.051, 2.183, 2.327, 2.488, 2.587,
+ 2.573, 2.459, 2.307, 2.171, 2.049, 1.931, 1.828, 1.731, 1.649, 1.582, 1.513, 1.459, 1.415, 1.381, 1.363, 1.358, 1.358, 1.373, 1.399, 1.439, 1.486, 1.552, 1.617, 1.696, 1.787, 1.888, 1.995, 2.112, 2.244, 2.391, 2.552, 2.652,
+ 2.635, 2.525, 2.377, 2.239, 2.111, 1.996, 1.895, 1.799, 1.719, 1.649, 1.582, 1.531, 1.486, 1.454, 1.434, 1.429, 1.429, 1.444, 1.469, 1.507, 1.555, 1.617, 1.692, 1.766, 1.854, 1.954, 2.065, 2.181, 2.313, 2.459, 2.623, 2.722,
+ 2.714, 2.604, 2.452, 2.313, 2.188, 2.071, 1.966, 1.876, 1.799, 1.719, 1.656, 1.604, 1.562, 1.529, 1.511, 1.504, 1.504, 1.519, 1.544, 1.583, 1.632, 1.692, 1.766, 1.839, 1.929, 2.029, 2.138, 2.259, 2.391, 2.539, 2.712, 2.811,
+ 2.809, 2.698, 2.537, 2.396, 2.277, 2.163, 2.053, 1.965, 1.876, 1.799, 1.741, 1.688, 1.643, 1.613, 1.592, 1.586, 1.586, 1.601, 1.628, 1.666, 1.715, 1.773, 1.839, 1.927, 2.012, 2.111, 2.222, 2.342, 2.477, 2.625, 2.811, 2.926,
+ 2.921, 2.809, 2.637, 2.493, 2.376, 2.256, 2.149, 2.053, 1.966, 1.893, 1.832, 1.778, 1.736, 1.708, 1.687, 1.681, 1.681, 1.696, 1.721, 1.757, 1.806, 1.864, 1.929, 2.012, 2.106, 2.199, 2.313, 2.437, 2.577, 2.731, 2.926, 3.051,
+ 3.029, 2.921, 2.745, 2.591, 2.474, 2.355, 2.246, 2.146, 2.049, 1.966, 1.893, 1.832, 1.799, 1.776, 1.768, 1.768, 1.768, 1.771, 1.783, 1.809, 1.864, 1.929, 2.012, 2.097, 2.195, 2.297, 2.412, 2.539, 2.682, 2.846, 3.051, 3.123
+ ],
+ "sigma": 0.00463,
+ "sigma_Cb": 0.00149
+ }
+ },
+ {
+ "rpi.contrast":
+ {
+ "ce_enable": 1,
+ "lo_max": 1000,
+ "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": 2860,
+ "ccm":
+ [
+ 2.12089, -0.52461, -0.59629,
+ -0.85342, 2.80445, -0.95103,
+ -0.26897, -1.14788, 2.41685
+ ]
+ },
+ {
+ "ct": 2960,
+ "ccm":
+ [
+ 2.26962, -0.54174, -0.72789,
+ -0.77008, 2.60271, -0.83262,
+ -0.26036, -1.51254, 2.77289
+ ]
+ },
+ {
+ "ct": 3603,
+ "ccm":
+ [
+ 2.18644, -0.66148, -0.52496,
+ -0.77828, 2.69474, -0.91645,
+ -0.25239, -0.83059, 2.08298
+ ]
+ },
+ {
+ "ct": 4650,
+ "ccm":
+ [
+ 2.18174, -0.70887, -0.47287,
+ -0.70196, 2.76426, -1.06231,
+ -0.25157, -0.71978, 1.97135
+ ]
+ },
+ {
+ "ct": 5858,
+ "ccm":
+ [
+ 2.32392, -0.88421, -0.43971,
+ -0.63821, 2.58348, -0.94527,
+ -0.28541, -0.54112, 1.82653
+ ]
+ },
+ {
+ "ct": 7580,
+ "ccm":
+ [
+ 2.21175, -0.53242, -0.67933,
+ -0.57875, 3.07922, -1.50047,
+ -0.27709, -0.73338, 2.01048
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx219_noir.json b/src/ipa/rpi/pisp/data/imx219_noir.json
new file mode 100644
index 00000000..8a8ad330
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx219_noir.json
@@ -0,0 +1,1112 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 21965,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 800,
+ "reference_Y": 11460
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 3.661
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 239,
+ "slope": 0.00766
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 10.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 10.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.418, 1.428, 1.446, 1.454, 1.454, 1.451, 1.441, 1.428, 1.411, 1.391, 1.371, 1.349, 1.334, 1.327, 1.325, 1.325, 1.325, 1.325, 1.331, 1.344, 1.363, 1.383, 1.402, 1.418, 1.433, 1.446, 1.452, 1.453, 1.446, 1.435, 1.415, 1.404,
+ 1.428, 1.442, 1.453, 1.455, 1.454, 1.447, 1.431, 1.413, 1.392, 1.371, 1.349, 1.331, 1.318, 1.307, 1.299, 1.299, 1.299, 1.303, 1.313, 1.328, 1.344, 1.363, 1.383, 1.404, 1.424, 1.439, 1.451, 1.453, 1.453, 1.445, 1.431, 1.415,
+ 1.436, 1.448, 1.453, 1.455, 1.449, 1.435, 1.415, 1.393, 1.369, 1.345, 1.322, 1.303, 1.287, 1.276, 1.269, 1.268, 1.268, 1.272, 1.283, 1.298, 1.316, 1.337, 1.362, 1.384, 1.406, 1.427, 1.444, 1.454, 1.454, 1.452, 1.438, 1.426,
+ 1.441, 1.451, 1.454, 1.451, 1.439, 1.422, 1.396, 1.372, 1.345, 1.319, 1.295, 1.274, 1.257, 1.245, 1.239, 1.238, 1.238, 1.245, 1.255, 1.269, 1.289, 1.311, 1.336, 1.362, 1.388, 1.412, 1.433, 1.448, 1.454, 1.453, 1.445, 1.433,
+ 1.445, 1.452, 1.452, 1.445, 1.428, 1.405, 1.379, 1.349, 1.319, 1.295, 1.269, 1.247, 1.229, 1.219, 1.212, 1.211, 1.211, 1.217, 1.228, 1.242, 1.261, 1.286, 1.311, 1.339, 1.367, 1.395, 1.419, 1.439, 1.452, 1.452, 1.451, 1.436,
+ 1.448, 1.451, 1.451, 1.435, 1.414, 1.387, 1.358, 1.327, 1.296, 1.269, 1.245, 1.222, 1.205, 1.193, 1.187, 1.185, 1.186, 1.191, 1.202, 1.217, 1.237, 1.261, 1.286, 1.316, 1.346, 1.378, 1.404, 1.429, 1.445, 1.451, 1.451, 1.442,
+ 1.448, 1.448, 1.445, 1.427, 1.401, 1.371, 1.338, 1.306, 1.274, 1.245, 1.222, 1.199, 1.183, 1.171, 1.164, 1.162, 1.162, 1.168, 1.181, 1.194, 1.215, 1.237, 1.264, 1.294, 1.325, 1.359, 1.389, 1.418, 1.441, 1.449, 1.449, 1.443,
+ 1.449, 1.448, 1.438, 1.415, 1.387, 1.352, 1.318, 1.284, 1.252, 1.223, 1.199, 1.179, 1.161, 1.149, 1.142, 1.142, 1.142, 1.149, 1.159, 1.174, 1.194, 1.215, 1.242, 1.272, 1.307, 1.341, 1.376, 1.405, 1.431, 1.447, 1.447, 1.444,
+ 1.448, 1.447, 1.431, 1.405, 1.373, 1.336, 1.301, 1.264, 1.234, 1.204, 1.179, 1.161, 1.143, 1.131, 1.124, 1.123, 1.123, 1.131, 1.141, 1.156, 1.174, 1.197, 1.224, 1.254, 1.288, 1.324, 1.361, 1.394, 1.423, 1.442, 1.444, 1.444,
+ 1.447, 1.442, 1.424, 1.393, 1.359, 1.322, 1.284, 1.248, 1.216, 1.187, 1.162, 1.143, 1.128, 1.115, 1.109, 1.108, 1.108, 1.113, 1.124, 1.139, 1.156, 1.179, 1.206, 1.236, 1.272, 1.309, 1.347, 1.382, 1.411, 1.435, 1.443, 1.444,
+ 1.444, 1.439, 1.417, 1.383, 1.347, 1.308, 1.271, 1.233, 1.201, 1.173, 1.147, 1.128, 1.115, 1.101, 1.095, 1.093, 1.093, 1.099, 1.111, 1.124, 1.142, 1.165, 1.191, 1.222, 1.258, 1.296, 1.333, 1.372, 1.404, 1.429, 1.441, 1.442,
+ 1.443, 1.434, 1.409, 1.375, 1.336, 1.297, 1.257, 1.221, 1.189, 1.159, 1.136, 1.116, 1.101, 1.092, 1.083, 1.082, 1.082, 1.089, 1.099, 1.111, 1.131, 1.153, 1.181, 1.211, 1.246, 1.284, 1.324, 1.361, 1.398, 1.425, 1.441, 1.441,
+ 1.443, 1.431, 1.405, 1.369, 1.328, 1.287, 1.247, 1.211, 1.178, 1.149, 1.126, 1.107, 1.092, 1.083, 1.075, 1.073, 1.073, 1.082, 1.089, 1.101, 1.121, 1.143, 1.171, 1.201, 1.237, 1.274, 1.314, 1.353, 1.389, 1.421, 1.439, 1.441,
+ 1.442, 1.429, 1.401, 1.364, 1.323, 1.279, 1.241, 1.205, 1.172, 1.144, 1.119, 1.101, 1.085, 1.075, 1.071, 1.067, 1.067, 1.073, 1.082, 1.096, 1.114, 1.136, 1.163, 1.194, 1.229, 1.268, 1.308, 1.348, 1.387, 1.417, 1.439, 1.439,
+ 1.443, 1.429, 1.399, 1.362, 1.319, 1.276, 1.237, 1.199, 1.169, 1.141, 1.115, 1.096, 1.081, 1.071, 1.066, 1.063, 1.066, 1.068, 1.078, 1.092, 1.109, 1.132, 1.159, 1.191, 1.226, 1.263, 1.304, 1.346, 1.384, 1.416, 1.438, 1.439,
+ 1.443, 1.428, 1.399, 1.361, 1.319, 1.276, 1.236, 1.199, 1.167, 1.139, 1.115, 1.096, 1.081, 1.071, 1.064, 1.062, 1.062, 1.067, 1.077, 1.091, 1.109, 1.131, 1.158, 1.189, 1.224, 1.262, 1.303, 1.345, 1.383, 1.416, 1.438, 1.439,
+ 1.444, 1.429, 1.399, 1.361, 1.319, 1.276, 1.236, 1.199, 1.167, 1.139, 1.116, 1.096, 1.081, 1.071, 1.064, 1.063, 1.063, 1.067, 1.077, 1.091, 1.109, 1.131, 1.159, 1.189, 1.224, 1.262, 1.303, 1.345, 1.384, 1.416, 1.438, 1.441,
+ 1.444, 1.431, 1.402, 1.364, 1.322, 1.281, 1.239, 1.202, 1.171, 1.142, 1.118, 1.099, 1.084, 1.073, 1.069, 1.065, 1.067, 1.071, 1.079, 1.094, 1.112, 1.135, 1.163, 1.191, 1.227, 1.265, 1.307, 1.348, 1.386, 1.418, 1.438, 1.441,
+ 1.447, 1.433, 1.406, 1.369, 1.328, 1.286, 1.244, 1.209, 1.177, 1.148, 1.124, 1.105, 1.089, 1.081, 1.073, 1.071, 1.071, 1.079, 1.085, 1.099, 1.118, 1.141, 1.168, 1.198, 1.233, 1.271, 1.312, 1.352, 1.391, 1.422, 1.441, 1.444,
+ 1.448, 1.438, 1.412, 1.376, 1.335, 1.295, 1.255, 1.218, 1.186, 1.157, 1.134, 1.113, 1.098, 1.089, 1.081, 1.079, 1.079, 1.085, 1.094, 1.107, 1.125, 1.149, 1.175, 1.207, 1.242, 1.281, 1.319, 1.359, 1.396, 1.425, 1.445, 1.447,
+ 1.449, 1.443, 1.417, 1.384, 1.345, 1.305, 1.266, 1.229, 1.197, 1.169, 1.145, 1.124, 1.111, 1.098, 1.091, 1.089, 1.089, 1.094, 1.107, 1.118, 1.137, 1.159, 1.187, 1.218, 1.253, 1.291, 1.329, 1.369, 1.405, 1.433, 1.447, 1.449,
+ 1.453, 1.449, 1.425, 1.395, 1.358, 1.318, 1.281, 1.244, 1.211, 1.183, 1.158, 1.138, 1.124, 1.111, 1.104, 1.103, 1.103, 1.107, 1.118, 1.133, 1.151, 1.174, 1.201, 1.232, 1.267, 1.304, 1.344, 1.379, 1.413, 1.437, 1.449, 1.449,
+ 1.457, 1.453, 1.434, 1.405, 1.371, 1.335, 1.297, 1.261, 1.229, 1.199, 1.174, 1.155, 1.138, 1.126, 1.119, 1.117, 1.117, 1.124, 1.133, 1.149, 1.167, 1.189, 1.217, 1.248, 1.284, 1.319, 1.357, 1.393, 1.423, 1.444, 1.452, 1.452,
+ 1.459, 1.457, 1.443, 1.418, 1.385, 1.352, 1.314, 1.279, 1.246, 1.218, 1.193, 1.174, 1.155, 1.144, 1.137, 1.136, 1.136, 1.141, 1.151, 1.167, 1.187, 1.208, 1.236, 1.267, 1.301, 1.337, 1.373, 1.405, 1.434, 1.453, 1.455, 1.455,
+ 1.461, 1.461, 1.454, 1.429, 1.401, 1.369, 1.333, 1.301, 1.269, 1.239, 1.216, 1.193, 1.177, 1.165, 1.158, 1.156, 1.156, 1.161, 1.171, 1.187, 1.208, 1.229, 1.258, 1.288, 1.321, 1.356, 1.389, 1.419, 1.445, 1.459, 1.459, 1.455,
+ 1.462, 1.462, 1.459, 1.442, 1.418, 1.386, 1.354, 1.322, 1.292, 1.262, 1.239, 1.216, 1.199, 1.187, 1.179, 1.178, 1.178, 1.184, 1.194, 1.208, 1.229, 1.253, 1.279, 1.309, 1.342, 1.375, 1.406, 1.433, 1.452, 1.464, 1.464, 1.454,
+ 1.461, 1.465, 1.465, 1.454, 1.431, 1.405, 1.376, 1.346, 1.316, 1.288, 1.262, 1.242, 1.223, 1.212, 1.205, 1.203, 1.203, 1.208, 1.218, 1.234, 1.253, 1.279, 1.305, 1.334, 1.363, 1.393, 1.421, 1.445, 1.461, 1.465, 1.464, 1.452,
+ 1.459, 1.465, 1.466, 1.461, 1.443, 1.421, 1.395, 1.368, 1.341, 1.316, 1.288, 1.268, 1.251, 1.238, 1.232, 1.229, 1.229, 1.235, 1.246, 1.261, 1.279, 1.305, 1.331, 1.356, 1.385, 1.411, 1.435, 1.454, 1.466, 1.466, 1.464, 1.451,
+ 1.454, 1.465, 1.467, 1.466, 1.456, 1.436, 1.414, 1.389, 1.367, 1.341, 1.318, 1.297, 1.279, 1.269, 1.261, 1.259, 1.259, 1.265, 1.274, 1.288, 1.308, 1.331, 1.355, 1.381, 1.404, 1.428, 1.447, 1.462, 1.468, 1.467, 1.457, 1.445,
+ 1.447, 1.459, 1.466, 1.467, 1.463, 1.451, 1.434, 1.411, 1.389, 1.367, 1.344, 1.325, 1.311, 1.297, 1.292, 1.289, 1.289, 1.295, 1.303, 1.317, 1.336, 1.356, 1.381, 1.402, 1.423, 1.441, 1.457, 1.467, 1.468, 1.463, 1.451, 1.439,
+ 1.438, 1.449, 1.462, 1.464, 1.464, 1.459, 1.446, 1.429, 1.408, 1.388, 1.369, 1.353, 1.339, 1.329, 1.321, 1.321, 1.321, 1.325, 1.333, 1.348, 1.362, 1.379, 1.401, 1.421, 1.439, 1.454, 1.463, 1.465, 1.465, 1.456, 1.442, 1.427,
+ 1.429, 1.439, 1.454, 1.464, 1.464, 1.459, 1.449, 1.435, 1.421, 1.402, 1.385, 1.369, 1.353, 1.341, 1.338, 1.337, 1.337, 1.338, 1.348, 1.362, 1.378, 1.395, 1.411, 1.429, 1.445, 1.455, 1.463, 1.464, 1.457, 1.447, 1.427, 1.419
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.163, 2.177, 2.194, 2.196, 2.197, 2.192, 2.181, 2.161, 2.139, 2.113, 2.088, 2.063, 2.047, 2.041, 2.036, 2.036, 2.036, 2.037, 2.046, 2.059, 2.083, 2.113, 2.135, 2.158, 2.181, 2.193, 2.205, 2.205, 2.202, 2.189, 2.171, 2.158,
+ 2.169, 2.184, 2.195, 2.196, 2.194, 2.182, 2.163, 2.141, 2.116, 2.088, 2.063, 2.042, 2.025, 2.013, 2.004, 2.004, 2.006, 2.011, 2.022, 2.038, 2.059, 2.083, 2.113, 2.137, 2.162, 2.182, 2.197, 2.204, 2.203, 2.199, 2.183, 2.171,
+ 2.177, 2.187, 2.193, 2.193, 2.184, 2.166, 2.142, 2.116, 2.087, 2.057, 2.033, 2.008, 1.991, 1.977, 1.969, 1.969, 1.969, 1.975, 1.988, 2.006, 2.028, 2.055, 2.083, 2.114, 2.139, 2.166, 2.187, 2.199, 2.202, 2.201, 2.189, 2.179,
+ 2.183, 2.189, 2.192, 2.186, 2.172, 2.146, 2.119, 2.089, 2.058, 2.026, 2.001, 1.975, 1.956, 1.942, 1.934, 1.932, 1.933, 1.941, 1.955, 1.971, 1.995, 2.023, 2.055, 2.084, 2.119, 2.146, 2.171, 2.191, 2.201, 2.201, 2.194, 2.183,
+ 2.186, 2.189, 2.189, 2.177, 2.158, 2.127, 2.096, 2.059, 2.026, 1.998, 1.969, 1.944, 1.925, 1.911, 1.901, 1.901, 1.903, 1.912, 1.924, 1.941, 1.964, 1.995, 2.023, 2.058, 2.091, 2.126, 2.155, 2.181, 2.195, 2.199, 2.198, 2.188,
+ 2.189, 2.189, 2.184, 2.166, 2.138, 2.108, 2.071, 2.036, 1.999, 1.969, 1.941, 1.914, 1.894, 1.879, 1.871, 1.871, 1.872, 1.879, 1.893, 1.913, 1.937, 1.964, 1.997, 2.029, 2.065, 2.104, 2.137, 2.169, 2.187, 2.199, 2.199, 2.189,
+ 2.187, 2.186, 2.176, 2.154, 2.123, 2.087, 2.044, 2.011, 1.974, 1.941, 1.913, 1.887, 1.868, 1.852, 1.844, 1.843, 1.844, 1.852, 1.866, 1.885, 1.912, 1.937, 1.972, 2.004, 2.042, 2.081, 2.119, 2.154, 2.179, 2.195, 2.196, 2.193,
+ 2.187, 2.181, 2.167, 2.141, 2.103, 2.062, 2.023, 1.984, 1.947, 1.916, 1.887, 1.864, 1.841, 1.828, 1.821, 1.819, 1.819, 1.828, 1.842, 1.862, 1.885, 1.913, 1.945, 1.982, 2.021, 2.058, 2.102, 2.137, 2.168, 2.192, 2.193, 2.193,
+ 2.182, 2.181, 2.161, 2.127, 2.083, 2.044, 2.002, 1.961, 1.924, 1.891, 1.864, 1.841, 1.819, 1.806, 1.797, 1.797, 1.797, 1.805, 1.819, 1.841, 1.862, 1.892, 1.924, 1.959, 1.999, 2.041, 2.082, 2.123, 2.161, 2.185, 2.191, 2.192,
+ 2.182, 2.172, 2.149, 2.112, 2.069, 2.026, 1.982, 1.941, 1.904, 1.871, 1.841, 1.819, 1.799, 1.785, 1.776, 1.776, 1.778, 1.784, 1.798, 1.819, 1.841, 1.869, 1.903, 1.939, 1.977, 2.021, 2.067, 2.108, 2.145, 2.174, 2.189, 2.191,
+ 2.181, 2.167, 2.139, 2.098, 2.056, 2.006, 1.965, 1.921, 1.883, 1.851, 1.823, 1.799, 1.783, 1.767, 1.759, 1.758, 1.758, 1.767, 1.783, 1.798, 1.825, 1.851, 1.883, 1.919, 1.959, 2.004, 2.049, 2.094, 2.136, 2.167, 2.187, 2.189,
+ 2.179, 2.163, 2.131, 2.087, 2.041, 1.994, 1.948, 1.907, 1.871, 1.835, 1.806, 1.784, 1.767, 1.754, 1.744, 1.742, 1.742, 1.752, 1.767, 1.783, 1.808, 1.838, 1.869, 1.905, 1.945, 1.989, 2.036, 2.083, 2.128, 2.159, 2.183, 2.187,
+ 2.178, 2.161, 2.126, 2.082, 2.032, 1.982, 1.936, 1.896, 1.857, 1.823, 1.795, 1.772, 1.754, 1.744, 1.732, 1.731, 1.732, 1.742, 1.752, 1.771, 1.796, 1.824, 1.857, 1.895, 1.934, 1.977, 2.024, 2.071, 2.116, 2.154, 2.181, 2.185,
+ 2.177, 2.157, 2.121, 2.074, 2.025, 1.973, 1.927, 1.886, 1.849, 1.815, 1.787, 1.765, 1.746, 1.732, 1.725, 1.722, 1.724, 1.732, 1.743, 1.762, 1.786, 1.813, 1.848, 1.886, 1.924, 1.969, 2.017, 2.066, 2.111, 2.153, 2.179, 2.183,
+ 2.177, 2.155, 2.119, 2.072, 2.022, 1.969, 1.925, 1.881, 1.844, 1.811, 1.782, 1.758, 1.739, 1.725, 1.721, 1.717, 1.721, 1.724, 1.739, 1.757, 1.781, 1.809, 1.842, 1.879, 1.921, 1.965, 2.012, 2.062, 2.108, 2.151, 2.179, 2.182,
+ 2.177, 2.156, 2.121, 2.071, 2.021, 1.968, 1.922, 1.879, 1.842, 1.811, 1.781, 1.757, 1.739, 1.725, 1.717, 1.715, 1.715, 1.723, 1.737, 1.757, 1.779, 1.808, 1.841, 1.877, 1.918, 1.963, 2.011, 2.061, 2.107, 2.148, 2.179, 2.183,
+ 2.178, 2.157, 2.121, 2.072, 2.021, 1.969, 1.922, 1.881, 1.842, 1.811, 1.781, 1.758, 1.739, 1.726, 1.718, 1.717, 1.718, 1.723, 1.737, 1.757, 1.781, 1.809, 1.841, 1.877, 1.918, 1.964, 2.012, 2.061, 2.108, 2.149, 2.179, 2.183,
+ 2.178, 2.159, 2.124, 2.074, 2.024, 1.974, 1.926, 1.885, 1.847, 1.813, 1.784, 1.762, 1.743, 1.731, 1.725, 1.719, 1.723, 1.728, 1.742, 1.762, 1.785, 1.814, 1.847, 1.881, 1.922, 1.966, 2.017, 2.065, 2.109, 2.151, 2.181, 2.184,
+ 2.181, 2.163, 2.129, 2.082, 2.032, 1.982, 1.934, 1.891, 1.854, 1.822, 1.794, 1.769, 1.751, 1.739, 1.731, 1.727, 1.728, 1.739, 1.747, 1.768, 1.791, 1.821, 1.852, 1.889, 1.929, 1.972, 2.022, 2.071, 2.117, 2.155, 2.182, 2.189,
+ 2.184, 2.169, 2.135, 2.091, 2.041, 1.994, 1.947, 1.902, 1.865, 1.833, 1.805, 1.779, 1.762, 1.751, 1.739, 1.739, 1.739, 1.747, 1.761, 1.779, 1.803, 1.831, 1.864, 1.898, 1.941, 1.984, 2.033, 2.079, 2.123, 2.163, 2.188, 2.193,
+ 2.185, 2.174, 2.142, 2.099, 2.054, 2.004, 1.959, 1.917, 1.879, 1.846, 1.819, 1.794, 1.779, 1.762, 1.754, 1.753, 1.753, 1.761, 1.777, 1.793, 1.816, 1.843, 1.877, 1.913, 1.953, 1.995, 2.043, 2.091, 2.135, 2.169, 2.191, 2.196,
+ 2.191, 2.179, 2.154, 2.118, 2.069, 2.023, 1.977, 1.935, 1.898, 1.865, 1.834, 1.813, 1.794, 1.779, 1.769, 1.769, 1.769, 1.777, 1.793, 1.809, 1.834, 1.863, 1.895, 1.929, 1.972, 2.015, 2.061, 2.105, 2.145, 2.178, 2.195, 2.199,
+ 2.197, 2.188, 2.166, 2.129, 2.087, 2.041, 1.997, 1.956, 1.918, 1.884, 1.855, 1.834, 1.813, 1.798, 1.788, 1.788, 1.788, 1.796, 1.809, 1.832, 1.853, 1.881, 1.912, 1.949, 1.991, 2.033, 2.076, 2.119, 2.159, 2.187, 2.202, 2.205,
+ 2.202, 2.197, 2.176, 2.148, 2.106, 2.065, 2.021, 1.979, 1.943, 1.909, 1.879, 1.855, 1.835, 1.819, 1.811, 1.811, 1.811, 1.818, 1.832, 1.853, 1.875, 1.904, 1.937, 1.972, 2.013, 2.055, 2.097, 2.138, 2.175, 2.197, 2.206, 2.207,
+ 2.205, 2.202, 2.189, 2.162, 2.126, 2.084, 2.044, 2.004, 1.967, 1.935, 1.907, 1.879, 1.861, 1.845, 1.838, 1.835, 1.835, 1.844, 1.855, 1.875, 1.902, 1.928, 1.961, 1.998, 2.033, 2.076, 2.118, 2.155, 2.186, 2.205, 2.208, 2.208,
+ 2.207, 2.205, 2.195, 2.175, 2.145, 2.108, 2.069, 2.029, 1.996, 1.963, 1.934, 1.908, 1.885, 1.872, 1.864, 1.863, 1.863, 1.869, 1.884, 1.902, 1.928, 1.956, 1.989, 2.023, 2.059, 2.099, 2.137, 2.172, 2.199, 2.212, 2.213, 2.209,
+ 2.207, 2.207, 2.203, 2.188, 2.162, 2.128, 2.094, 2.058, 2.023, 1.993, 1.963, 1.936, 1.916, 1.899, 1.893, 1.892, 1.893, 1.899, 1.912, 1.929, 1.956, 1.986, 2.016, 2.049, 2.084, 2.121, 2.156, 2.187, 2.208, 2.215, 2.215, 2.208,
+ 2.205, 2.208, 2.209, 2.199, 2.178, 2.149, 2.117, 2.083, 2.052, 2.023, 1.993, 1.967, 1.947, 1.933, 1.925, 1.922, 1.922, 1.929, 1.943, 1.961, 1.986, 2.015, 2.045, 2.076, 2.109, 2.143, 2.173, 2.198, 2.214, 2.218, 2.216, 2.205,
+ 2.201, 2.207, 2.211, 2.211, 2.193, 2.168, 2.141, 2.112, 2.082, 2.052, 2.025, 2.001, 1.981, 1.967, 1.959, 1.958, 1.958, 1.967, 1.975, 1.992, 2.018, 2.046, 2.076, 2.105, 2.136, 2.163, 2.189, 2.208, 2.217, 2.217, 2.212, 2.203,
+ 2.194, 2.204, 2.212, 2.213, 2.203, 2.187, 2.165, 2.139, 2.112, 2.083, 2.055, 2.034, 2.016, 2.001, 1.993, 1.993, 1.994, 1.999, 2.011, 2.027, 2.051, 2.077, 2.105, 2.133, 2.158, 2.181, 2.202, 2.217, 2.218, 2.218, 2.206, 2.193,
+ 2.185, 2.198, 2.213, 2.214, 2.212, 2.201, 2.184, 2.163, 2.135, 2.111, 2.089, 2.071, 2.052, 2.039, 2.032, 2.031, 2.031, 2.036, 2.048, 2.065, 2.085, 2.106, 2.131, 2.155, 2.178, 2.198, 2.212, 2.219, 2.219, 2.215, 2.201, 2.185,
+ 2.176, 2.191, 2.208, 2.217, 2.216, 2.205, 2.195, 2.177, 2.156, 2.133, 2.109, 2.089, 2.071, 2.055, 2.053, 2.053, 2.053, 2.057, 2.065, 2.085, 2.105, 2.123, 2.149, 2.171, 2.192, 2.205, 2.217, 2.219, 2.219, 2.202, 2.185, 2.181
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.518, 2.513, 2.503, 2.496, 2.488, 2.484, 2.485, 2.485, 2.486, 2.487, 2.487, 2.489, 2.494, 2.496, 2.496, 2.497, 2.499, 2.499, 2.496, 2.495, 2.492, 2.491, 2.491, 2.491, 2.492, 2.493, 2.495, 2.501, 2.508, 2.516, 2.528, 2.533,
+ 2.515, 2.508, 2.495, 2.487, 2.483, 2.481, 2.482, 2.483, 2.485, 2.487, 2.489, 2.491, 2.495, 2.497, 2.498, 2.501, 2.502, 2.502, 2.499, 2.496, 2.494, 2.491, 2.491, 2.489, 2.489, 2.491, 2.493, 2.496, 2.502, 2.511, 2.521, 2.531,
+ 2.507, 2.495, 2.486, 2.482, 2.478, 2.477, 2.481, 2.482, 2.484, 2.488, 2.491, 2.495, 2.499, 2.502, 2.506, 2.508, 2.509, 2.508, 2.505, 2.501, 2.497, 2.493, 2.491, 2.489, 2.488, 2.489, 2.489, 2.492, 2.496, 2.501, 2.511, 2.524,
+ 2.501, 2.487, 2.482, 2.481, 2.478, 2.477, 2.481, 2.483, 2.487, 2.491, 2.501, 2.503, 2.509, 2.511, 2.518, 2.519, 2.519, 2.519, 2.516, 2.509, 2.504, 2.498, 2.495, 2.493, 2.489, 2.489, 2.488, 2.489, 2.492, 2.498, 2.505, 2.523,
+ 2.499, 2.484, 2.481, 2.476, 2.476, 2.476, 2.481, 2.485, 2.492, 2.501, 2.509, 2.514, 2.519, 2.524, 2.528, 2.531, 2.533, 2.533, 2.525, 2.519, 2.514, 2.507, 2.501, 2.497, 2.493, 2.489, 2.489, 2.488, 2.491, 2.494, 2.501, 2.514,
+ 2.497, 2.483, 2.478, 2.476, 2.476, 2.478, 2.482, 2.491, 2.499, 2.509, 2.515, 2.522, 2.528, 2.535, 2.539, 2.541, 2.543, 2.542, 2.539, 2.529, 2.522, 2.516, 2.507, 2.502, 2.497, 2.491, 2.489, 2.488, 2.489, 2.492, 2.498, 2.514,
+ 2.492, 2.479, 2.476, 2.475, 2.476, 2.481, 2.488, 2.496, 2.505, 2.516, 2.524, 2.532, 2.541, 2.545, 2.552, 2.554, 2.554, 2.554, 2.548, 2.541, 2.532, 2.522, 2.516, 2.507, 2.502, 2.494, 2.491, 2.489, 2.489, 2.492, 2.494, 2.511,
+ 2.491, 2.479, 2.476, 2.477, 2.478, 2.482, 2.491, 2.502, 2.514, 2.524, 2.533, 2.543, 2.548, 2.555, 2.562, 2.566, 2.567, 2.562, 2.557, 2.551, 2.541, 2.531, 2.523, 2.512, 2.506, 2.498, 2.493, 2.491, 2.491, 2.491, 2.493, 2.507,
+ 2.489, 2.478, 2.476, 2.477, 2.481, 2.485, 2.494, 2.507, 2.517, 2.529, 2.542, 2.548, 2.557, 2.563, 2.567, 2.571, 2.572, 2.571, 2.565, 2.558, 2.549, 2.538, 2.528, 2.521, 2.509, 2.501, 2.494, 2.492, 2.491, 2.491, 2.491, 2.505,
+ 2.488, 2.478, 2.477, 2.478, 2.482, 2.489, 2.499, 2.509, 2.523, 2.538, 2.548, 2.556, 2.563, 2.568, 2.573, 2.577, 2.578, 2.577, 2.573, 2.564, 2.555, 2.543, 2.535, 2.524, 2.515, 2.504, 2.495, 2.492, 2.489, 2.488, 2.489, 2.501,
+ 2.486, 2.476, 2.475, 2.477, 2.483, 2.491, 2.503, 2.515, 2.529, 2.542, 2.553, 2.562, 2.568, 2.574, 2.581, 2.583, 2.584, 2.581, 2.578, 2.571, 2.562, 2.551, 2.539, 2.531, 2.517, 2.508, 2.497, 2.492, 2.488, 2.487, 2.489, 2.498,
+ 2.486, 2.476, 2.475, 2.479, 2.484, 2.492, 2.504, 2.519, 2.533, 2.544, 2.557, 2.566, 2.573, 2.581, 2.584, 2.588, 2.588, 2.586, 2.581, 2.575, 2.567, 2.555, 2.546, 2.534, 2.517, 2.509, 2.499, 2.492, 2.489, 2.485, 2.488, 2.497,
+ 2.487, 2.476, 2.476, 2.479, 2.486, 2.494, 2.506, 2.521, 2.535, 2.549, 2.559, 2.571, 2.578, 2.583, 2.589, 2.591, 2.591, 2.591, 2.587, 2.579, 2.571, 2.559, 2.551, 2.538, 2.523, 2.513, 2.503, 2.493, 2.489, 2.486, 2.487, 2.499,
+ 2.486, 2.475, 2.475, 2.479, 2.486, 2.495, 2.509, 2.525, 2.541, 2.555, 2.563, 2.573, 2.582, 2.588, 2.591, 2.594, 2.595, 2.592, 2.591, 2.585, 2.574, 2.564, 2.552, 2.541, 2.525, 2.514, 2.503, 2.493, 2.489, 2.486, 2.486, 2.501,
+ 2.486, 2.475, 2.475, 2.479, 2.488, 2.497, 2.509, 2.526, 2.542, 2.556, 2.564, 2.575, 2.584, 2.591, 2.595, 2.596, 2.597, 2.595, 2.592, 2.587, 2.577, 2.568, 2.554, 2.542, 2.527, 2.515, 2.504, 2.494, 2.491, 2.487, 2.487, 2.505,
+ 2.484, 2.476, 2.475, 2.478, 2.488, 2.498, 2.509, 2.526, 2.542, 2.555, 2.565, 2.576, 2.584, 2.589, 2.595, 2.598, 2.598, 2.597, 2.593, 2.587, 2.578, 2.569, 2.556, 2.543, 2.528, 2.515, 2.504, 2.494, 2.489, 2.485, 2.485, 2.501,
+ 2.484, 2.475, 2.475, 2.478, 2.489, 2.498, 2.509, 2.524, 2.539, 2.553, 2.565, 2.576, 2.584, 2.589, 2.594, 2.597, 2.597, 2.596, 2.593, 2.587, 2.577, 2.569, 2.555, 2.543, 2.529, 2.515, 2.503, 2.496, 2.491, 2.485, 2.486, 2.497,
+ 2.484, 2.474, 2.474, 2.479, 2.487, 2.497, 2.509, 2.523, 2.539, 2.551, 2.563, 2.574, 2.581, 2.587, 2.592, 2.595, 2.596, 2.595, 2.591, 2.584, 2.574, 2.567, 2.554, 2.541, 2.526, 2.514, 2.503, 2.495, 2.489, 2.485, 2.486, 2.497,
+ 2.484, 2.475, 2.475, 2.478, 2.485, 2.494, 2.507, 2.522, 2.535, 2.546, 2.559, 2.568, 2.579, 2.584, 2.589, 2.592, 2.593, 2.592, 2.588, 2.579, 2.571, 2.562, 2.551, 2.537, 2.524, 2.514, 2.501, 2.493, 2.489, 2.486, 2.487, 2.498,
+ 2.485, 2.476, 2.475, 2.477, 2.485, 2.491, 2.506, 2.519, 2.531, 2.544, 2.555, 2.563, 2.571, 2.581, 2.584, 2.589, 2.589, 2.588, 2.583, 2.576, 2.566, 2.555, 2.546, 2.534, 2.522, 2.511, 2.499, 2.491, 2.488, 2.486, 2.487, 2.502,
+ 2.487, 2.477, 2.475, 2.477, 2.483, 2.489, 2.503, 2.515, 2.525, 2.541, 2.551, 2.559, 2.567, 2.573, 2.579, 2.582, 2.583, 2.582, 2.576, 2.569, 2.562, 2.549, 2.542, 2.527, 2.518, 2.505, 2.497, 2.491, 2.489, 2.487, 2.487, 2.502,
+ 2.487, 2.478, 2.475, 2.477, 2.482, 2.489, 2.497, 2.512, 2.522, 2.536, 2.544, 2.551, 2.562, 2.566, 2.573, 2.578, 2.578, 2.575, 2.571, 2.564, 2.556, 2.548, 2.536, 2.523, 2.513, 2.503, 2.493, 2.489, 2.487, 2.486, 2.487, 2.502,
+ 2.488, 2.479, 2.477, 2.478, 2.482, 2.488, 2.496, 2.505, 2.516, 2.528, 2.538, 2.547, 2.553, 2.561, 2.565, 2.569, 2.569, 2.568, 2.564, 2.558, 2.549, 2.541, 2.531, 2.517, 2.509, 2.499, 2.492, 2.488, 2.486, 2.484, 2.486, 2.503,
+ 2.492, 2.482, 2.479, 2.479, 2.482, 2.487, 2.491, 2.501, 2.512, 2.523, 2.531, 2.541, 2.549, 2.552, 2.558, 2.561, 2.562, 2.559, 2.558, 2.552, 2.542, 2.535, 2.525, 2.514, 2.505, 2.497, 2.491, 2.486, 2.485, 2.484, 2.487, 2.503,
+ 2.495, 2.483, 2.479, 2.479, 2.482, 2.487, 2.491, 2.498, 2.508, 2.515, 2.526, 2.533, 2.541, 2.547, 2.551, 2.554, 2.555, 2.554, 2.552, 2.541, 2.537, 2.527, 2.519, 2.507, 2.502, 2.495, 2.488, 2.485, 2.484, 2.485, 2.488, 2.503,
+ 2.499, 2.485, 2.483, 2.481, 2.482, 2.486, 2.489, 2.494, 2.504, 2.511, 2.519, 2.527, 2.531, 2.539, 2.542, 2.546, 2.546, 2.545, 2.539, 2.535, 2.527, 2.522, 2.509, 2.505, 2.497, 2.491, 2.486, 2.485, 2.485, 2.487, 2.491, 2.506,
+ 2.499, 2.489, 2.483, 2.481, 2.481, 2.483, 2.488, 2.491, 2.499, 2.506, 2.512, 2.519, 2.524, 2.529, 2.535, 2.537, 2.536, 2.534, 2.532, 2.525, 2.522, 2.514, 2.506, 2.499, 2.492, 2.489, 2.485, 2.484, 2.485, 2.488, 2.492, 2.506,
+ 2.507, 2.494, 2.486, 2.483, 2.482, 2.482, 2.486, 2.488, 2.495, 2.501, 2.507, 2.511, 2.517, 2.519, 2.523, 2.525, 2.525, 2.523, 2.523, 2.521, 2.514, 2.506, 2.502, 2.496, 2.491, 2.488, 2.485, 2.485, 2.487, 2.489, 2.496, 2.516,
+ 2.511, 2.503, 2.489, 2.486, 2.485, 2.485, 2.485, 2.487, 2.489, 2.495, 2.501, 2.505, 2.509, 2.514, 2.517, 2.519, 2.518, 2.517, 2.515, 2.511, 2.505, 2.501, 2.495, 2.492, 2.488, 2.486, 2.485, 2.486, 2.488, 2.492, 2.499, 2.519,
+ 2.517, 2.505, 2.494, 2.489, 2.487, 2.486, 2.486, 2.486, 2.489, 2.491, 2.496, 2.499, 2.503, 2.506, 2.508, 2.509, 2.511, 2.509, 2.507, 2.503, 2.501, 2.496, 2.493, 2.489, 2.485, 2.485, 2.486, 2.487, 2.491, 2.495, 2.505, 2.526,
+ 2.526, 2.516, 2.504, 2.494, 2.493, 2.489, 2.489, 2.489, 2.489, 2.491, 2.496, 2.498, 2.501, 2.504, 2.506, 2.506, 2.506, 2.505, 2.503, 2.501, 2.499, 2.496, 2.494, 2.491, 2.487, 2.486, 2.489, 2.492, 2.497, 2.505, 2.517, 2.528,
+ 2.529, 2.526, 2.508, 2.502, 2.501, 2.498, 2.495, 2.495, 2.495, 2.495, 2.497, 2.499, 2.501, 2.503, 2.504, 2.506, 2.505, 2.505, 2.503, 2.501, 2.499, 2.496, 2.495, 2.494, 2.492, 2.494, 2.494, 2.498, 2.504, 2.513, 2.525, 2.536
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.427, 1.425, 1.423, 1.422, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.422, 1.423, 1.424, 1.425, 1.426, 1.426, 1.426, 1.425, 1.425, 1.424, 1.422, 1.421, 1.421, 1.421, 1.421, 1.422, 1.422, 1.422, 1.424, 1.424, 1.426, 1.428,
+ 1.426, 1.424, 1.422, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.422, 1.423, 1.424, 1.425, 1.426, 1.427, 1.427, 1.427, 1.426, 1.425, 1.424, 1.422, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.421, 1.422, 1.424, 1.427,
+ 1.423, 1.421, 1.421, 1.419, 1.419, 1.418, 1.419, 1.419, 1.421, 1.423, 1.425, 1.426, 1.428, 1.429, 1.431, 1.431, 1.431, 1.431, 1.429, 1.426, 1.424, 1.422, 1.421, 1.421, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.422, 1.425,
+ 1.422, 1.419, 1.419, 1.419, 1.418, 1.418, 1.419, 1.421, 1.422, 1.426, 1.428, 1.429, 1.433, 1.434, 1.436, 1.436, 1.436, 1.434, 1.432, 1.429, 1.426, 1.424, 1.423, 1.422, 1.421, 1.419, 1.419, 1.419, 1.419, 1.419, 1.421, 1.425,
+ 1.422, 1.419, 1.419, 1.418, 1.418, 1.419, 1.419, 1.422, 1.425, 1.429, 1.432, 1.435, 1.436, 1.438, 1.439, 1.439, 1.441, 1.439, 1.435, 1.433, 1.429, 1.427, 1.425, 1.423, 1.422, 1.419, 1.419, 1.418, 1.418, 1.418, 1.419, 1.425,
+ 1.422, 1.419, 1.418, 1.418, 1.418, 1.419, 1.421, 1.424, 1.428, 1.432, 1.436, 1.437, 1.439, 1.442, 1.443, 1.445, 1.444, 1.443, 1.441, 1.436, 1.434, 1.431, 1.427, 1.425, 1.422, 1.421, 1.419, 1.418, 1.418, 1.418, 1.419, 1.424,
+ 1.422, 1.418, 1.417, 1.418, 1.419, 1.421, 1.423, 1.427, 1.431, 1.436, 1.438, 1.442, 1.444, 1.446, 1.448, 1.449, 1.448, 1.446, 1.445, 1.441, 1.436, 1.434, 1.429, 1.427, 1.423, 1.421, 1.419, 1.418, 1.418, 1.418, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.418, 1.419, 1.421, 1.424, 1.429, 1.434, 1.438, 1.442, 1.445, 1.447, 1.449, 1.451, 1.452, 1.452, 1.449, 1.447, 1.445, 1.441, 1.436, 1.433, 1.429, 1.425, 1.422, 1.419, 1.419, 1.418, 1.417, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.419, 1.419, 1.423, 1.426, 1.432, 1.436, 1.441, 1.445, 1.448, 1.449, 1.452, 1.453, 1.454, 1.454, 1.453, 1.451, 1.447, 1.444, 1.439, 1.433, 1.431, 1.427, 1.422, 1.421, 1.419, 1.418, 1.417, 1.418, 1.423,
+ 1.421, 1.418, 1.418, 1.419, 1.421, 1.423, 1.428, 1.433, 1.439, 1.443, 1.448, 1.449, 1.453, 1.454, 1.455, 1.456, 1.456, 1.454, 1.453, 1.449, 1.446, 1.441, 1.437, 1.433, 1.429, 1.423, 1.421, 1.419, 1.418, 1.416, 1.417, 1.423,
+ 1.421, 1.417, 1.417, 1.419, 1.422, 1.424, 1.429, 1.435, 1.441, 1.444, 1.449, 1.453, 1.454, 1.456, 1.458, 1.459, 1.458, 1.456, 1.454, 1.451, 1.448, 1.442, 1.439, 1.435, 1.429, 1.426, 1.421, 1.419, 1.418, 1.416, 1.417, 1.422,
+ 1.419, 1.418, 1.417, 1.419, 1.422, 1.425, 1.429, 1.436, 1.442, 1.446, 1.451, 1.454, 1.456, 1.458, 1.461, 1.461, 1.461, 1.459, 1.456, 1.453, 1.451, 1.446, 1.441, 1.436, 1.431, 1.427, 1.422, 1.419, 1.418, 1.416, 1.417, 1.422,
+ 1.419, 1.418, 1.418, 1.421, 1.423, 1.426, 1.431, 1.437, 1.444, 1.449, 1.452, 1.456, 1.458, 1.461, 1.462, 1.463, 1.463, 1.461, 1.458, 1.454, 1.452, 1.447, 1.443, 1.438, 1.432, 1.428, 1.423, 1.421, 1.419, 1.417, 1.417, 1.421,
+ 1.419, 1.418, 1.417, 1.421, 1.423, 1.428, 1.432, 1.439, 1.445, 1.451, 1.453, 1.457, 1.459, 1.462, 1.464, 1.465, 1.465, 1.463, 1.461, 1.457, 1.453, 1.449, 1.444, 1.441, 1.432, 1.429, 1.425, 1.421, 1.419, 1.417, 1.418, 1.422,
+ 1.418, 1.417, 1.417, 1.419, 1.423, 1.428, 1.433, 1.439, 1.446, 1.451, 1.453, 1.457, 1.461, 1.464, 1.465, 1.466, 1.466, 1.464, 1.462, 1.459, 1.454, 1.451, 1.445, 1.441, 1.436, 1.429, 1.425, 1.422, 1.421, 1.417, 1.417, 1.423,
+ 1.417, 1.416, 1.416, 1.419, 1.423, 1.428, 1.433, 1.441, 1.446, 1.451, 1.454, 1.458, 1.461, 1.463, 1.465, 1.466, 1.466, 1.465, 1.463, 1.459, 1.454, 1.451, 1.446, 1.441, 1.437, 1.431, 1.426, 1.422, 1.421, 1.418, 1.418, 1.423,
+ 1.417, 1.416, 1.417, 1.418, 1.423, 1.428, 1.433, 1.439, 1.445, 1.451, 1.453, 1.457, 1.461, 1.463, 1.465, 1.466, 1.466, 1.464, 1.462, 1.459, 1.454, 1.451, 1.446, 1.441, 1.437, 1.431, 1.426, 1.422, 1.419, 1.417, 1.417, 1.422,
+ 1.417, 1.416, 1.416, 1.418, 1.422, 1.428, 1.433, 1.438, 1.444, 1.449, 1.453, 1.456, 1.459, 1.462, 1.464, 1.465, 1.465, 1.463, 1.461, 1.458, 1.453, 1.449, 1.445, 1.441, 1.435, 1.429, 1.426, 1.421, 1.419, 1.417, 1.417, 1.422,
+ 1.418, 1.416, 1.416, 1.418, 1.421, 1.426, 1.432, 1.438, 1.443, 1.447, 1.451, 1.454, 1.458, 1.459, 1.462, 1.463, 1.463, 1.462, 1.459, 1.455, 1.451, 1.447, 1.443, 1.439, 1.434, 1.429, 1.425, 1.421, 1.419, 1.417, 1.417, 1.422,
+ 1.418, 1.416, 1.416, 1.418, 1.421, 1.425, 1.431, 1.435, 1.442, 1.445, 1.449, 1.452, 1.455, 1.458, 1.458, 1.461, 1.461, 1.459, 1.456, 1.453, 1.449, 1.445, 1.442, 1.436, 1.433, 1.427, 1.425, 1.421, 1.419, 1.418, 1.418, 1.422,
+ 1.419, 1.416, 1.415, 1.417, 1.419, 1.424, 1.429, 1.434, 1.439, 1.443, 1.446, 1.449, 1.452, 1.454, 1.456, 1.457, 1.457, 1.456, 1.453, 1.451, 1.447, 1.443, 1.441, 1.435, 1.431, 1.426, 1.424, 1.421, 1.419, 1.418, 1.418, 1.422,
+ 1.419, 1.416, 1.415, 1.416, 1.419, 1.422, 1.426, 1.433, 1.437, 1.441, 1.444, 1.447, 1.449, 1.452, 1.453, 1.455, 1.455, 1.453, 1.451, 1.447, 1.444, 1.441, 1.438, 1.432, 1.428, 1.424, 1.421, 1.419, 1.418, 1.417, 1.417, 1.421,
+ 1.419, 1.416, 1.415, 1.416, 1.418, 1.421, 1.425, 1.431, 1.435, 1.438, 1.442, 1.445, 1.446, 1.449, 1.451, 1.451, 1.451, 1.451, 1.447, 1.445, 1.443, 1.439, 1.434, 1.431, 1.427, 1.422, 1.421, 1.418, 1.417, 1.417, 1.417, 1.421,
+ 1.418, 1.416, 1.415, 1.416, 1.417, 1.421, 1.423, 1.428, 1.433, 1.437, 1.439, 1.442, 1.444, 1.446, 1.448, 1.449, 1.449, 1.447, 1.445, 1.443, 1.439, 1.437, 1.432, 1.429, 1.425, 1.422, 1.419, 1.417, 1.417, 1.416, 1.416, 1.419,
+ 1.418, 1.416, 1.416, 1.416, 1.417, 1.421, 1.422, 1.426, 1.429, 1.433, 1.436, 1.438, 1.441, 1.443, 1.445, 1.446, 1.445, 1.445, 1.443, 1.439, 1.437, 1.434, 1.431, 1.427, 1.424, 1.421, 1.419, 1.417, 1.417, 1.416, 1.416, 1.421,
+ 1.419, 1.417, 1.416, 1.416, 1.417, 1.421, 1.422, 1.424, 1.427, 1.429, 1.432, 1.436, 1.437, 1.439, 1.442, 1.443, 1.443, 1.441, 1.439, 1.437, 1.434, 1.431, 1.429, 1.425, 1.422, 1.421, 1.419, 1.417, 1.416, 1.416, 1.417, 1.419,
+ 1.421, 1.418, 1.416, 1.417, 1.418, 1.421, 1.421, 1.423, 1.424, 1.427, 1.429, 1.432, 1.434, 1.436, 1.438, 1.439, 1.439, 1.438, 1.436, 1.434, 1.431, 1.429, 1.426, 1.423, 1.422, 1.421, 1.418, 1.417, 1.417, 1.417, 1.417, 1.421,
+ 1.423, 1.419, 1.418, 1.418, 1.419, 1.419, 1.421, 1.422, 1.423, 1.424, 1.427, 1.429, 1.432, 1.432, 1.434, 1.435, 1.435, 1.434, 1.433, 1.431, 1.429, 1.426, 1.424, 1.422, 1.421, 1.419, 1.418, 1.417, 1.417, 1.417, 1.418, 1.421,
+ 1.425, 1.421, 1.419, 1.419, 1.419, 1.421, 1.421, 1.421, 1.421, 1.423, 1.424, 1.426, 1.428, 1.431, 1.431, 1.432, 1.432, 1.431, 1.431, 1.428, 1.425, 1.425, 1.422, 1.421, 1.419, 1.419, 1.418, 1.418, 1.418, 1.418, 1.419, 1.425,
+ 1.426, 1.422, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.421, 1.422, 1.424, 1.426, 1.427, 1.428, 1.429, 1.429, 1.429, 1.427, 1.424, 1.423, 1.422, 1.421, 1.419, 1.418, 1.418, 1.418, 1.418, 1.418, 1.418, 1.419, 1.426,
+ 1.428, 1.425, 1.421, 1.421, 1.421, 1.421, 1.421, 1.419, 1.419, 1.421, 1.422, 1.423, 1.424, 1.426, 1.426, 1.426, 1.426, 1.425, 1.424, 1.424, 1.422, 1.422, 1.421, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.423, 1.426,
+ 1.429, 1.427, 1.424, 1.422, 1.422, 1.422, 1.421, 1.421, 1.421, 1.422, 1.422, 1.422, 1.424, 1.425, 1.426, 1.426, 1.425, 1.425, 1.424, 1.423, 1.422, 1.422, 1.421, 1.421, 1.421, 1.421, 1.419, 1.419, 1.421, 1.422, 1.424, 1.426
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.964, 2.872, 2.691, 2.544, 2.416, 2.302, 2.196, 2.093, 2.006, 1.928, 1.852, 1.801, 1.769, 1.752, 1.743, 1.743, 1.743, 1.746, 1.759, 1.784, 1.824, 1.888, 1.968, 2.052, 2.149, 2.253, 2.359, 2.483, 2.626, 2.785, 2.988, 3.051,
+ 2.872, 2.748, 2.583, 2.442, 2.313, 2.201, 2.104, 2.012, 1.928, 1.852, 1.791, 1.742, 1.701, 1.671, 1.651, 1.643, 1.643, 1.659, 1.685, 1.721, 1.768, 1.824, 1.888, 1.971, 2.068, 2.152, 2.259, 2.381, 2.514, 2.669, 2.853, 2.988,
+ 2.761, 2.655, 2.497, 2.356, 2.226, 2.114, 2.012, 1.928, 1.845, 1.769, 1.707, 1.653, 1.612, 1.583, 1.562, 1.556, 1.556, 1.572, 1.599, 1.635, 1.681, 1.742, 1.806, 1.888, 1.971, 2.068, 2.175, 2.292, 2.431, 2.576, 2.747, 2.853,
+ 2.679, 2.571, 2.415, 2.275, 2.151, 2.035, 1.936, 1.845, 1.769, 1.689, 1.623, 1.572, 1.532, 1.501, 1.481, 1.473, 1.473, 1.492, 1.517, 1.556, 1.599, 1.659, 1.731, 1.806, 1.895, 1.992, 2.101, 2.218, 2.349, 2.493, 2.664, 2.753,
+ 2.609, 2.492, 2.339, 2.204, 2.079, 1.971, 1.865, 1.772, 1.689, 1.619, 1.551, 1.499, 1.457, 1.423, 1.405, 1.397, 1.397, 1.411, 1.438, 1.477, 1.525, 1.585, 1.659, 1.731, 1.823, 1.922, 2.027, 2.148, 2.275, 2.422, 2.586, 2.683,
+ 2.545, 2.426, 2.279, 2.139, 2.014, 1.903, 1.799, 1.702, 1.619, 1.551, 1.482, 1.427, 1.385, 1.353, 1.331, 1.325, 1.325, 1.338, 1.364, 1.403, 1.455, 1.522, 1.585, 1.665, 1.757, 1.858, 1.963, 2.081, 2.207, 2.356, 2.518, 2.615,
+ 2.489, 2.367, 2.218, 2.079, 1.956, 1.844, 1.739, 1.642, 1.559, 1.482, 1.426, 1.363, 1.321, 1.287, 1.266, 1.259, 1.259, 1.274, 1.301, 1.339, 1.395, 1.455, 1.523, 1.606, 1.697, 1.797, 1.905, 2.024, 2.154, 2.296, 2.455, 2.563,
+ 2.439, 2.316, 2.164, 2.028, 1.906, 1.793, 1.686, 1.589, 1.505, 1.427, 1.363, 1.308, 1.261, 1.229, 1.207, 1.202, 1.202, 1.215, 1.242, 1.283, 1.339, 1.395, 1.467, 1.551, 1.639, 1.742, 1.851, 1.972, 2.104, 2.243, 2.402, 2.515,
+ 2.398, 2.262, 2.116, 1.982, 1.861, 1.745, 1.639, 1.541, 1.456, 1.377, 1.308, 1.261, 1.208, 1.177, 1.157, 1.153, 1.153, 1.167, 1.191, 1.233, 1.283, 1.343, 1.418, 1.499, 1.591, 1.696, 1.804, 1.928, 2.057, 2.194, 2.352, 2.471,
+ 2.363, 2.222, 2.078, 1.942, 1.818, 1.706, 1.597, 1.501, 1.412, 1.334, 1.266, 1.208, 1.171, 1.134, 1.113, 1.109, 1.109, 1.123, 1.149, 1.191, 1.233, 1.296, 1.371, 1.457, 1.546, 1.654, 1.768, 1.886, 2.014, 2.155, 2.312, 2.436,
+ 2.334, 2.188, 2.042, 1.909, 1.783, 1.668, 1.561, 1.464, 1.374, 1.295, 1.228, 1.171, 1.134, 1.098, 1.076, 1.072, 1.072, 1.087, 1.119, 1.149, 1.196, 1.259, 1.332, 1.419, 1.514, 1.616, 1.728, 1.849, 1.981, 2.123, 2.276, 2.406,
+ 2.306, 2.159, 2.015, 1.881, 1.753, 1.639, 1.533, 1.434, 1.341, 1.263, 1.195, 1.139, 1.098, 1.074, 1.046, 1.044, 1.045, 1.059, 1.087, 1.119, 1.165, 1.227, 1.302, 1.387, 1.482, 1.586, 1.698, 1.819, 1.953, 2.093, 2.248, 2.383,
+ 2.291, 2.141, 1.991, 1.856, 1.732, 1.615, 1.508, 1.409, 1.318, 1.238, 1.171, 1.114, 1.074, 1.046, 1.027, 1.023, 1.025, 1.043, 1.059, 1.095, 1.142, 1.203, 1.278, 1.362, 1.456, 1.559, 1.673, 1.796, 1.928, 2.071, 2.225, 2.359,
+ 2.279, 2.118, 1.972, 1.839, 1.715, 1.599, 1.488, 1.389, 1.298, 1.219, 1.153, 1.097, 1.057, 1.027, 1.018, 1.009, 1.013, 1.025, 1.044, 1.078, 1.125, 1.186, 1.258, 1.342, 1.438, 1.541, 1.655, 1.779, 1.909, 2.053, 2.211, 2.351,
+ 2.274, 2.108, 1.963, 1.831, 1.706, 1.588, 1.477, 1.376, 1.288, 1.207, 1.139, 1.086, 1.049, 1.021, 1.005, 1.002, 1.004, 1.013, 1.035, 1.069, 1.116, 1.176, 1.246, 1.331, 1.427, 1.531, 1.645, 1.767, 1.899, 2.045, 2.197, 2.351,
+ 2.274, 2.106, 1.961, 1.827, 1.701, 1.585, 1.474, 1.374, 1.285, 1.206, 1.139, 1.085, 1.047, 1.019, 1.003, 1.001, 1.001, 1.012, 1.033, 1.067, 1.113, 1.173, 1.245, 1.329, 1.423, 1.529, 1.642, 1.765, 1.897, 2.042, 2.196, 2.349,
+ 2.274, 2.108, 1.961, 1.827, 1.701, 1.585, 1.474, 1.374, 1.285, 1.206, 1.139, 1.085, 1.047, 1.021, 1.005, 1.001, 1.004, 1.012, 1.033, 1.068, 1.113, 1.173, 1.246, 1.329, 1.423, 1.529, 1.642, 1.766, 1.897, 2.042, 2.198, 2.349,
+ 2.278, 2.116, 1.968, 1.833, 1.707, 1.591, 1.482, 1.382, 1.291, 1.214, 1.147, 1.091, 1.055, 1.028, 1.016, 1.006, 1.012, 1.018, 1.039, 1.074, 1.121, 1.182, 1.255, 1.339, 1.433, 1.538, 1.651, 1.777, 1.911, 2.051, 2.207, 2.351,
+ 2.283, 2.127, 1.979, 1.846, 1.723, 1.605, 1.496, 1.397, 1.309, 1.229, 1.162, 1.108, 1.067, 1.041, 1.027, 1.018, 1.018, 1.036, 1.051, 1.087, 1.136, 1.197, 1.269, 1.354, 1.448, 1.554, 1.664, 1.789, 1.922, 2.065, 2.222, 2.365,
+ 2.298, 2.145, 1.999, 1.865, 1.744, 1.627, 1.518, 1.421, 1.331, 1.251, 1.183, 1.129, 1.087, 1.065, 1.041, 1.036, 1.036, 1.051, 1.074, 1.107, 1.158, 1.219, 1.292, 1.378, 1.471, 1.575, 1.687, 1.809, 1.942, 2.085, 2.239, 2.378,
+ 2.315, 2.174, 2.024, 1.893, 1.768, 1.652, 1.543, 1.445, 1.355, 1.278, 1.211, 1.155, 1.116, 1.087, 1.066, 1.061, 1.061, 1.074, 1.105, 1.137, 1.186, 1.248, 1.322, 1.405, 1.498, 1.602, 1.713, 1.835, 1.965, 2.109, 2.267, 2.399,
+ 2.341, 2.206, 2.057, 1.923, 1.799, 1.685, 1.576, 1.479, 1.392, 1.312, 1.244, 1.187, 1.154, 1.116, 1.096, 1.092, 1.092, 1.106, 1.137, 1.173, 1.221, 1.282, 1.356, 1.439, 1.532, 1.635, 1.747, 1.869, 1.997, 2.141, 2.298, 2.425,
+ 2.375, 2.244, 2.098, 1.965, 1.839, 1.722, 1.614, 1.519, 1.434, 1.355, 1.288, 1.234, 1.187, 1.155, 1.136, 1.132, 1.132, 1.147, 1.173, 1.219, 1.263, 1.324, 1.398, 1.479, 1.571, 1.674, 1.784, 1.904, 2.035, 2.177, 2.336, 2.455,
+ 2.414, 2.286, 2.144, 2.011, 1.883, 1.767, 1.661, 1.566, 1.479, 1.401, 1.335, 1.286, 1.234, 1.202, 1.183, 1.178, 1.178, 1.195, 1.222, 1.263, 1.313, 1.372, 1.444, 1.526, 1.618, 1.718, 1.827, 1.951, 2.081, 2.221, 2.379, 2.498,
+ 2.463, 2.339, 2.191, 2.056, 1.931, 1.819, 1.712, 1.616, 1.529, 1.452, 1.392, 1.335, 1.286, 1.254, 1.235, 1.232, 1.232, 1.248, 1.275, 1.313, 1.371, 1.425, 1.495, 1.576, 1.671, 1.768, 1.877, 1.999, 2.128, 2.269, 2.428, 2.541,
+ 2.514, 2.396, 2.247, 2.112, 1.988, 1.873, 1.766, 1.671, 1.588, 1.513, 1.452, 1.392, 1.348, 1.316, 1.298, 1.292, 1.292, 1.307, 1.336, 1.373, 1.425, 1.486, 1.552, 1.636, 1.728, 1.826, 1.933, 2.051, 2.183, 2.327, 2.488, 2.587,
+ 2.573, 2.459, 2.307, 2.171, 2.049, 1.931, 1.828, 1.731, 1.649, 1.582, 1.513, 1.459, 1.415, 1.381, 1.363, 1.358, 1.358, 1.373, 1.399, 1.439, 1.486, 1.552, 1.617, 1.696, 1.787, 1.888, 1.995, 2.112, 2.244, 2.391, 2.552, 2.652,
+ 2.635, 2.525, 2.377, 2.239, 2.111, 1.996, 1.895, 1.799, 1.719, 1.649, 1.582, 1.531, 1.486, 1.454, 1.434, 1.429, 1.429, 1.444, 1.469, 1.507, 1.555, 1.617, 1.692, 1.766, 1.854, 1.954, 2.065, 2.181, 2.313, 2.459, 2.623, 2.722,
+ 2.714, 2.604, 2.452, 2.313, 2.188, 2.071, 1.966, 1.876, 1.799, 1.719, 1.656, 1.604, 1.562, 1.529, 1.511, 1.504, 1.504, 1.519, 1.544, 1.583, 1.632, 1.692, 1.766, 1.839, 1.929, 2.029, 2.138, 2.259, 2.391, 2.539, 2.712, 2.811,
+ 2.809, 2.698, 2.537, 2.396, 2.277, 2.163, 2.053, 1.965, 1.876, 1.799, 1.741, 1.688, 1.643, 1.613, 1.592, 1.586, 1.586, 1.601, 1.628, 1.666, 1.715, 1.773, 1.839, 1.927, 2.012, 2.111, 2.222, 2.342, 2.477, 2.625, 2.811, 2.926,
+ 2.921, 2.809, 2.637, 2.493, 2.376, 2.256, 2.149, 2.053, 1.966, 1.893, 1.832, 1.778, 1.736, 1.708, 1.687, 1.681, 1.681, 1.696, 1.721, 1.757, 1.806, 1.864, 1.929, 2.012, 2.106, 2.199, 2.313, 2.437, 2.577, 2.731, 2.926, 3.051,
+ 3.029, 2.921, 2.745, 2.591, 2.474, 2.355, 2.246, 2.146, 2.049, 1.966, 1.893, 1.832, 1.799, 1.776, 1.768, 1.768, 1.768, 1.771, 1.783, 1.809, 1.864, 1.929, 2.012, 2.097, 2.195, 2.297, 2.412, 2.539, 2.682, 2.846, 3.051, 3.123
+ ],
+ "sigma": 0.00463,
+ "sigma_Cb": 0.00149
+ }
+ },
+ {
+ "rpi.contrast":
+ {
+ "ce_enable": 1,
+ "lo_max": 1000,
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx283.json b/src/ipa/rpi/pisp/data/imx283.json
new file mode 100644
index 00000000..2e90512c
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx283.json
@@ -0,0 +1,1334 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3200
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 10857,
+ "reference_gain": 1.49,
+ "reference_aperture": 1.0,
+ "reference_lux": 1050,
+ "reference_Y": 13959
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.147
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 249,
+ "slope": 0.02036
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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": 6000,
+ "hi": 6800,
+ }
+ },
+ "bayes": 1,
+ "ct_curve":
+ [
+ 2500.0, 0.9437, 0.2866,
+ 2820.0, 0.8496, 0.3541,
+ 2830.0, 0.8309, 0.3681,
+ 2885.0, 0.8183, 0.3778,
+ 3601.0, 0.6946, 0.4786,
+ 3615.0, 0.6929, 0.4801,
+ 3622.0, 0.6905, 0.4821,
+ 4345.0, 0.6012, 0.5628,
+ 4410.0, 0.5956, 0.5682,
+ 4486.0, 0.5892, 0.5743,
+ 4576.0, 0.5794, 0.5837,
+ 5672.0, 0.5232, 0.6392,
+ 5710.0, 0.5188, 0.6436,
+ 6850.0, 0.4862, 0.6773
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.02634,
+ "transverse_neg": 0.02491
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 2940,
+ "table":
+ [
+ 1.023, 1.024, 1.028, 1.032, 1.034, 1.036, 1.037, 1.038, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.036, 1.035, 1.033, 1.031, 1.026, 1.022, 1.019, 1.014, 1.012,
+ 1.021, 1.023, 1.026, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.027, 1.025, 1.021, 1.016, 1.012, 1.009,
+ 1.019, 1.022, 1.024, 1.026, 1.028, 1.029, 1.029, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.027, 1.026, 1.024, 1.021, 1.016, 1.011, 1.009,
+ 1.021, 1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.024, 1.022, 1.018, 1.012, 1.011,
+ 1.021, 1.024, 1.025, 1.027, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.025, 1.023, 1.019, 1.014, 1.011,
+ 1.023, 1.025, 1.026, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.027, 1.025, 1.023, 1.021, 1.015, 1.011,
+ 1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.028, 1.026, 1.023, 1.021, 1.015, 1.012,
+ 1.023, 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.028, 1.026, 1.024, 1.021, 1.016, 1.012,
+ 1.023, 1.025, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.028, 1.027, 1.025, 1.022, 1.017, 1.014,
+ 1.024, 1.025, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.025, 1.023, 1.018, 1.015,
+ 1.024, 1.026, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.025, 1.025, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+ 1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+ 1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+ 1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.028, 1.026, 1.023, 1.018, 1.016,
+ 1.025, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.027, 1.026, 1.023, 1.018, 1.016,
+ 1.025, 1.027, 1.028, 1.028, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.016,
+ 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.015,
+ 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.025, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.025, 1.023, 1.018, 1.016,
+ 1.025, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.028, 1.027, 1.025, 1.022, 1.017, 1.015,
+ 1.025, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.027, 1.025, 1.022, 1.017, 1.015,
+ 1.025, 1.027, 1.028, 1.029, 1.029, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.027, 1.025, 1.022, 1.017, 1.014,
+ 1.025, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.028, 1.028, 1.029, 1.028, 1.026, 1.024, 1.021, 1.016, 1.014,
+ 1.025, 1.027, 1.028, 1.029, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.026, 1.023, 1.021, 1.014, 1.012,
+ 1.024, 1.027, 1.028, 1.029, 1.031, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.019, 1.014, 1.011,
+ 1.024, 1.027, 1.028, 1.029, 1.031, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.025, 1.022, 1.018, 1.012, 1.009,
+ 1.024, 1.026, 1.028, 1.029, 1.032, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.028, 1.027, 1.026, 1.025, 1.022, 1.018, 1.011, 1.009,
+ 1.023, 1.026, 1.028, 1.029, 1.032, 1.033, 1.033, 1.033, 1.033, 1.032, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.025, 1.024, 1.021, 1.016, 1.011, 1.008,
+ 1.022, 1.026, 1.028, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.033, 1.032, 1.032, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.028, 1.027, 1.025, 1.023, 1.021, 1.015, 1.009, 1.007,
+ 1.022, 1.025, 1.028, 1.031, 1.032, 1.033, 1.034, 1.034, 1.035, 1.034, 1.034, 1.033, 1.032, 1.032, 1.032, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.028, 1.027, 1.025, 1.023, 1.019, 1.014, 1.008, 1.006,
+ 1.021, 1.024, 1.027, 1.029, 1.031, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.032, 1.032, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.031, 1.029, 1.027, 1.026, 1.024, 1.021, 1.017, 1.013, 1.007, 1.004,
+ 1.019, 1.022, 1.026, 1.028, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.033, 1.032, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.028, 1.026, 1.024, 1.021, 1.018, 1.015, 1.011, 1.004, 1.002,
+ 1.018, 1.021, 1.025, 1.027, 1.029, 1.031, 1.032, 1.033, 1.033, 1.034, 1.034, 1.034, 1.034, 1.034, 1.033, 1.033, 1.032, 1.032, 1.032, 1.031, 1.031, 1.029, 1.029, 1.027, 1.025, 1.023, 1.019, 1.017, 1.013, 1.009, 1.002, 1.001
+ ]
+ },
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1.027, 1.032, 1.036, 1.039, 1.044, 1.047, 1.049, 1.051, 1.051, 1.052, 1.052, 1.051, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.053, 1.053, 1.053, 1.053, 1.052, 1.051, 1.049, 1.047, 1.043, 1.039, 1.036, 1.031, 1.021, 1.015,
+ 1.027, 1.029, 1.035, 1.038, 1.041, 1.042, 1.042, 1.042, 1.043, 1.043, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.045, 1.045, 1.044, 1.044, 1.043, 1.041, 1.037, 1.033, 1.026, 1.018, 1.015,
+ 1.025, 1.029, 1.033, 1.036, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.044, 1.043, 1.042, 1.039, 1.037, 1.033, 1.026, 1.018, 1.014,
+ 1.026, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.028, 1.021, 1.017,
+ 1.028, 1.032, 1.035, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.041, 1.039, 1.036, 1.031, 1.023, 1.019,
+ 1.029, 1.033, 1.036, 1.038, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.041, 1.039, 1.036, 1.032, 1.024, 1.019,
+ 1.029, 1.034, 1.036, 1.038, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.042, 1.043, 1.043, 1.043, 1.043, 1.042, 1.039, 1.036, 1.033, 1.024, 1.021,
+ 1.029, 1.034, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.043, 1.043, 1.043, 1.042, 1.041, 1.037, 1.033, 1.026, 1.022,
+ 1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.027, 1.024,
+ 1.031, 1.033, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.042, 1.043, 1.043, 1.042, 1.041, 1.038, 1.034, 1.028, 1.025,
+ 1.031, 1.034, 1.036, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.039, 1.035, 1.028, 1.025,
+ 1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+ 1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+ 1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.042, 1.042, 1.041, 1.039, 1.035, 1.029, 1.025,
+ 1.031, 1.034, 1.036, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.025,
+ 1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+ 1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+ 1.031, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.038, 1.034, 1.028, 1.024,
+ 1.031, 1.034, 1.037, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.027, 1.024,
+ 1.031, 1.034, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.027, 1.023,
+ 1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.039, 1.037, 1.033, 1.026, 1.022,
+ 1.029, 1.033, 1.036, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.038, 1.036, 1.032, 1.025, 1.021,
+ 1.029, 1.033, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.035, 1.031, 1.023, 1.019,
+ 1.029, 1.033, 1.036, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.037, 1.034, 1.029, 1.021, 1.018,
+ 1.028, 1.032, 1.035, 1.038, 1.039, 1.039, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.037, 1.033, 1.028, 1.019, 1.017,
+ 1.028, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.036, 1.032, 1.027, 1.018, 1.015,
+ 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.039, 1.038, 1.036, 1.035, 1.031, 1.025, 1.016, 1.013,
+ 1.025, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.034, 1.029, 1.022, 1.014, 1.011,
+ 1.023, 1.029, 1.034, 1.037, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.035, 1.033, 1.028, 1.021, 1.012, 1.009,
+ 1.022, 1.027, 1.032, 1.036, 1.038, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.035, 1.033, 1.031, 1.025, 1.018, 1.009, 1.005,
+ 1.019, 1.024, 1.029, 1.034, 1.036, 1.039, 1.039, 1.041, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.033, 1.031, 1.026, 1.021, 1.014, 1.005, 1.003,
+ 1.017, 1.022, 1.028, 1.032, 1.036, 1.038, 1.039, 1.041, 1.041, 1.042, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.036, 1.034, 1.032, 1.028, 1.024, 1.019, 1.012, 1.003, 1.001
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 1.021, 1.033, 1.041, 1.046, 1.051, 1.054, 1.057, 1.061, 1.064, 1.066, 1.068, 1.068, 1.068, 1.068, 1.068, 1.069, 1.069, 1.069, 1.069, 1.069, 1.068, 1.067, 1.064, 1.062, 1.058, 1.056, 1.052, 1.047, 1.041, 1.031, 1.019, 1.012,
+ 1.021, 1.029, 1.037, 1.043, 1.048, 1.053, 1.056, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.058, 1.054, 1.049, 1.044, 1.039, 1.029, 1.018, 1.012,
+ 1.023, 1.029, 1.037, 1.043, 1.048, 1.052, 1.055, 1.057, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.061, 1.059, 1.057, 1.054, 1.049, 1.044, 1.039, 1.029, 1.018, 1.015,
+ 1.025, 1.032, 1.039, 1.045, 1.049, 1.054, 1.057, 1.058, 1.059, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.062, 1.062, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.058, 1.055, 1.051, 1.046, 1.041, 1.033, 1.022, 1.018,
+ 1.027, 1.035, 1.041, 1.046, 1.051, 1.054, 1.057, 1.058, 1.059, 1.061, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.061, 1.059, 1.056, 1.053, 1.048, 1.043, 1.036, 1.025, 1.019,
+ 1.029, 1.036, 1.043, 1.049, 1.052, 1.055, 1.057, 1.058, 1.059, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.061, 1.058, 1.054, 1.051, 1.045, 1.039, 1.027, 1.021,
+ 1.031, 1.039, 1.045, 1.049, 1.053, 1.056, 1.058, 1.059, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.046, 1.039, 1.028, 1.023,
+ 1.032, 1.039, 1.045, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.061, 1.057, 1.053, 1.048, 1.041, 1.031, 1.025,
+ 1.033, 1.039, 1.046, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.061, 1.062, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.058, 1.054, 1.049, 1.043, 1.032, 1.027,
+ 1.034, 1.041, 1.046, 1.051, 1.054, 1.056, 1.057, 1.059, 1.059, 1.061, 1.062, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.054, 1.049, 1.044, 1.034, 1.029,
+ 1.034, 1.041, 1.046, 1.051, 1.054, 1.056, 1.058, 1.059, 1.059, 1.061, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.055, 1.051, 1.044, 1.035, 1.029,
+ 1.035, 1.041, 1.047, 1.051, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.045, 1.035, 1.029,
+ 1.036, 1.042, 1.047, 1.052, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+ 1.036, 1.043, 1.048, 1.052, 1.054, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.056, 1.052, 1.046, 1.036, 1.031,
+ 1.037, 1.043, 1.048, 1.052, 1.055, 1.057, 1.059, 1.061, 1.061, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+ 1.037, 1.044, 1.048, 1.053, 1.055, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+ 1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.052, 1.045, 1.036, 1.031,
+ 1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.059, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.065, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.045, 1.036, 1.031,
+ 1.037, 1.044, 1.049, 1.053, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.065, 1.065, 1.065, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.056, 1.051, 1.044, 1.035, 1.031,
+ 1.037, 1.043, 1.049, 1.053, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.055, 1.051, 1.044, 1.034, 1.029,
+ 1.035, 1.042, 1.048, 1.053, 1.056, 1.058, 1.059, 1.061, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.059, 1.055, 1.051, 1.043, 1.034, 1.027,
+ 1.034, 1.042, 1.048, 1.053, 1.056, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.058, 1.054, 1.049, 1.042, 1.032, 1.026,
+ 1.034, 1.041, 1.047, 1.053, 1.056, 1.058, 1.061, 1.061, 1.062, 1.062, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.061, 1.059, 1.057, 1.053, 1.048, 1.041, 1.031, 1.026,
+ 1.034, 1.041, 1.047, 1.052, 1.056, 1.059, 1.061, 1.061, 1.062, 1.062, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.058, 1.056, 1.052, 1.046, 1.038, 1.028, 1.024,
+ 1.032, 1.039, 1.045, 1.052, 1.056, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.062, 1.061, 1.059, 1.057, 1.055, 1.051, 1.045, 1.037, 1.026, 1.022,
+ 1.032, 1.038, 1.044, 1.049, 1.054, 1.058, 1.061, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.062, 1.061, 1.059, 1.057, 1.054, 1.049, 1.044, 1.036, 1.024, 1.019,
+ 1.029, 1.037, 1.044, 1.049, 1.054, 1.058, 1.059, 1.062, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.063, 1.062, 1.061, 1.058, 1.055, 1.052, 1.048, 1.042, 1.033, 1.022, 1.017,
+ 1.027, 1.035, 1.042, 1.049, 1.054, 1.057, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.059, 1.057, 1.054, 1.051, 1.047, 1.039, 1.031, 1.019, 1.014,
+ 1.025, 1.033, 1.041, 1.047, 1.052, 1.056, 1.058, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.064, 1.064, 1.064, 1.064, 1.064, 1.063, 1.063, 1.063, 1.062, 1.059, 1.056, 1.053, 1.049, 1.044, 1.037, 1.028, 1.016, 1.012,
+ 1.022, 1.031, 1.038, 1.045, 1.051, 1.053, 1.056, 1.059, 1.061, 1.062, 1.063, 1.063, 1.063, 1.063, 1.064, 1.063, 1.063, 1.063, 1.063, 1.063, 1.063, 1.062, 1.059, 1.057, 1.054, 1.051, 1.047, 1.041, 1.034, 1.025, 1.013, 1.007,
+ 1.019, 1.026, 1.035, 1.042, 1.047, 1.051, 1.054, 1.057, 1.059, 1.061, 1.062, 1.062, 1.062, 1.063, 1.063, 1.063, 1.062, 1.063, 1.062, 1.062, 1.061, 1.059, 1.057, 1.054, 1.051, 1.047, 1.042, 1.036, 1.029, 1.019, 1.007, 1.005,
+ 1.016, 1.024, 1.033, 1.041, 1.046, 1.049, 1.053, 1.055, 1.057, 1.059, 1.061, 1.062, 1.062, 1.063, 1.063, 1.062, 1.062, 1.062, 1.062, 1.061, 1.059, 1.057, 1.055, 1.052, 1.049, 1.045, 1.039, 1.033, 1.027, 1.016, 1.005, 1.001
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 2940,
+ "table":
+ [
+ 1.001, 1.001, 1.014, 1.027, 1.038, 1.047, 1.051, 1.054, 1.058, 1.061, 1.062, 1.064, 1.066, 1.066, 1.066, 1.067, 1.067, 1.067, 1.067, 1.067, 1.067, 1.066, 1.064, 1.063, 1.061, 1.057, 1.055, 1.049, 1.043, 1.032, 1.026, 1.018,
+ 1.001, 1.013, 1.023, 1.032, 1.042, 1.049, 1.056, 1.063, 1.066, 1.069, 1.073, 1.076, 1.077, 1.078, 1.079, 1.079, 1.081, 1.081, 1.079, 1.079, 1.078, 1.077, 1.076, 1.073, 1.071, 1.066, 1.061, 1.053, 1.046, 1.037, 1.031, 1.026,
+ 1.008, 1.019, 1.031, 1.042, 1.049, 1.056, 1.063, 1.066, 1.069, 1.073, 1.076, 1.078, 1.078, 1.081, 1.082, 1.082, 1.082, 1.081, 1.081, 1.081, 1.079, 1.079, 1.077, 1.076, 1.073, 1.071, 1.066, 1.061, 1.053, 1.046, 1.035, 1.032,
+ 1.012, 1.023, 1.036, 1.045, 1.053, 1.059, 1.065, 1.068, 1.072, 1.075, 1.078, 1.079, 1.081, 1.083, 1.084, 1.084, 1.084, 1.084, 1.083, 1.083, 1.081, 1.081, 1.079, 1.078, 1.076, 1.073, 1.069, 1.063, 1.056, 1.049, 1.039, 1.034,
+ 1.015, 1.027, 1.038, 1.048, 1.056, 1.061, 1.067, 1.071, 1.074, 1.076, 1.079, 1.082, 1.084, 1.085, 1.086, 1.086, 1.086, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.075, 1.072, 1.066, 1.059, 1.051, 1.041, 1.038,
+ 1.018, 1.031, 1.041, 1.051, 1.058, 1.064, 1.069, 1.073, 1.075, 1.079, 1.082, 1.084, 1.085, 1.087, 1.088, 1.088, 1.088, 1.088, 1.087, 1.087, 1.086, 1.084, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.061, 1.052, 1.043, 1.038,
+ 1.021, 1.033, 1.045, 1.053, 1.059, 1.066, 1.071, 1.074, 1.078, 1.081, 1.084, 1.085, 1.087, 1.088, 1.089, 1.089, 1.089, 1.089, 1.089, 1.088, 1.087, 1.086, 1.085, 1.083, 1.081, 1.079, 1.075, 1.071, 1.062, 1.054, 1.044, 1.039,
+ 1.023, 1.035, 1.046, 1.055, 1.062, 1.067, 1.072, 1.075, 1.079, 1.083, 1.085, 1.087, 1.088, 1.089, 1.091, 1.091, 1.091, 1.091, 1.091, 1.091, 1.089, 1.087, 1.086, 1.084, 1.082, 1.081, 1.076, 1.072, 1.065, 1.055, 1.045, 1.039,
+ 1.025, 1.036, 1.048, 1.056, 1.063, 1.069, 1.073, 1.077, 1.081, 1.085, 1.087, 1.088, 1.089, 1.091, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.088, 1.087, 1.085, 1.084, 1.081, 1.078, 1.074, 1.066, 1.057, 1.047, 1.042,
+ 1.028, 1.039, 1.051, 1.058, 1.065, 1.071, 1.075, 1.079, 1.083, 1.086, 1.088, 1.089, 1.091, 1.092, 1.093, 1.093, 1.093, 1.093, 1.093, 1.093, 1.092, 1.091, 1.088, 1.086, 1.084, 1.082, 1.079, 1.074, 1.067, 1.058, 1.047, 1.043,
+ 1.029, 1.041, 1.051, 1.059, 1.067, 1.071, 1.076, 1.081, 1.083, 1.087, 1.088, 1.091, 1.092, 1.093, 1.093, 1.094, 1.094, 1.094, 1.094, 1.094, 1.093, 1.092, 1.089, 1.087, 1.085, 1.083, 1.079, 1.074, 1.068, 1.059, 1.048, 1.044,
+ 1.031, 1.042, 1.053, 1.061, 1.068, 1.072, 1.077, 1.081, 1.084, 1.087, 1.089, 1.091, 1.093, 1.093, 1.094, 1.095, 1.095, 1.095, 1.095, 1.095, 1.094, 1.092, 1.089, 1.088, 1.086, 1.083, 1.081, 1.075, 1.069, 1.061, 1.049, 1.044,
+ 1.032, 1.043, 1.054, 1.062, 1.068, 1.073, 1.078, 1.081, 1.085, 1.088, 1.089, 1.092, 1.093, 1.094, 1.095, 1.095, 1.096, 1.096, 1.096, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.083, 1.081, 1.075, 1.069, 1.061, 1.049, 1.044,
+ 1.033, 1.044, 1.055, 1.062, 1.069, 1.073, 1.079, 1.082, 1.085, 1.088, 1.091, 1.093, 1.094, 1.095, 1.096, 1.096, 1.096, 1.097, 1.096, 1.096, 1.095, 1.093, 1.092, 1.089, 1.087, 1.084, 1.081, 1.076, 1.071, 1.062, 1.049, 1.046,
+ 1.034, 1.045, 1.056, 1.064, 1.069, 1.074, 1.079, 1.082, 1.086, 1.089, 1.091, 1.093, 1.094, 1.095, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.084, 1.081, 1.077, 1.071, 1.062, 1.052, 1.046,
+ 1.034, 1.046, 1.057, 1.064, 1.069, 1.074, 1.079, 1.083, 1.086, 1.089, 1.091, 1.093, 1.094, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.084, 1.081, 1.077, 1.071, 1.063, 1.052, 1.046,
+ 1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.089, 1.091, 1.093, 1.094, 1.096, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.092, 1.089, 1.087, 1.085, 1.081, 1.077, 1.071, 1.063, 1.052, 1.047,
+ 1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.088, 1.091, 1.092, 1.094, 1.095, 1.096, 1.097, 1.097, 1.097, 1.097, 1.096, 1.095, 1.094, 1.091, 1.089, 1.087, 1.085, 1.082, 1.077, 1.071, 1.063, 1.052, 1.046,
+ 1.035, 1.046, 1.057, 1.064, 1.069, 1.075, 1.079, 1.083, 1.086, 1.088, 1.091, 1.092, 1.093, 1.095, 1.096, 1.096, 1.097, 1.097, 1.096, 1.095, 1.095, 1.093, 1.091, 1.089, 1.087, 1.084, 1.082, 1.077, 1.071, 1.062, 1.052, 1.047,
+ 1.035, 1.046, 1.056, 1.063, 1.069, 1.074, 1.077, 1.082, 1.085, 1.087, 1.091, 1.091, 1.093, 1.094, 1.095, 1.096, 1.096, 1.096, 1.095, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.085, 1.081, 1.077, 1.071, 1.062, 1.052, 1.047,
+ 1.033, 1.045, 1.055, 1.063, 1.068, 1.073, 1.077, 1.082, 1.084, 1.087, 1.089, 1.091, 1.093, 1.093, 1.094, 1.095, 1.095, 1.095, 1.095, 1.095, 1.094, 1.093, 1.091, 1.089, 1.086, 1.084, 1.081, 1.076, 1.069, 1.062, 1.051, 1.047,
+ 1.032, 1.045, 1.054, 1.062, 1.067, 1.072, 1.077, 1.081, 1.083, 1.087, 1.089, 1.091, 1.092, 1.093, 1.093, 1.094, 1.094, 1.094, 1.094, 1.094, 1.093, 1.092, 1.091, 1.088, 1.086, 1.083, 1.079, 1.076, 1.069, 1.061, 1.051, 1.047,
+ 1.031, 1.044, 1.053, 1.061, 1.066, 1.072, 1.077, 1.081, 1.083, 1.087, 1.088, 1.091, 1.091, 1.093, 1.093, 1.093, 1.094, 1.094, 1.094, 1.093, 1.092, 1.091, 1.089, 1.088, 1.086, 1.083, 1.079, 1.075, 1.069, 1.059, 1.051, 1.046,
+ 1.031, 1.043, 1.051, 1.061, 1.065, 1.072, 1.076, 1.079, 1.083, 1.086, 1.087, 1.091, 1.091, 1.091, 1.092, 1.093, 1.093, 1.093, 1.093, 1.092, 1.091, 1.091, 1.089, 1.086, 1.085, 1.083, 1.079, 1.074, 1.066, 1.059, 1.051, 1.045,
+ 1.029, 1.039, 1.051, 1.059, 1.065, 1.071, 1.074, 1.077, 1.082, 1.085, 1.087, 1.088, 1.091, 1.091, 1.091, 1.092, 1.092, 1.092, 1.092, 1.091, 1.091, 1.089, 1.088, 1.085, 1.084, 1.082, 1.077, 1.071, 1.065, 1.058, 1.048, 1.043,
+ 1.028, 1.037, 1.049, 1.057, 1.063, 1.068, 1.073, 1.076, 1.081, 1.082, 1.085, 1.087, 1.089, 1.089, 1.091, 1.091, 1.091, 1.091, 1.091, 1.091, 1.089, 1.088, 1.086, 1.085, 1.083, 1.079, 1.076, 1.071, 1.064, 1.056, 1.046, 1.041,
+ 1.025, 1.035, 1.047, 1.055, 1.061, 1.067, 1.071, 1.075, 1.078, 1.081, 1.084, 1.086, 1.087, 1.088, 1.089, 1.089, 1.089, 1.089, 1.089, 1.089, 1.088, 1.087, 1.085, 1.084, 1.081, 1.078, 1.075, 1.069, 1.062, 1.054, 1.044, 1.039,
+ 1.023, 1.032, 1.044, 1.052, 1.059, 1.065, 1.069, 1.073, 1.076, 1.079, 1.081, 1.084, 1.085, 1.086, 1.087, 1.087, 1.088, 1.088, 1.088, 1.087, 1.087, 1.085, 1.084, 1.081, 1.079, 1.076, 1.072, 1.067, 1.059, 1.052, 1.041, 1.037,
+ 1.019, 1.028, 1.039, 1.048, 1.057, 1.062, 1.067, 1.071, 1.074, 1.077, 1.079, 1.081, 1.083, 1.084, 1.085, 1.085, 1.086, 1.086, 1.085, 1.085, 1.084, 1.083, 1.081, 1.079, 1.077, 1.073, 1.069, 1.063, 1.056, 1.048, 1.038, 1.034,
+ 1.016, 1.025, 1.035, 1.045, 1.053, 1.059, 1.064, 1.069, 1.072, 1.076, 1.077, 1.079, 1.081, 1.081, 1.082, 1.083, 1.084, 1.084, 1.084, 1.083, 1.083, 1.081, 1.079, 1.077, 1.074, 1.069, 1.065, 1.059, 1.053, 1.044, 1.034, 1.029,
+ 1.011, 1.022, 1.032, 1.042, 1.051, 1.057, 1.062, 1.067, 1.069, 1.074, 1.076, 1.077, 1.079, 1.079, 1.081, 1.081, 1.081, 1.082, 1.082, 1.081, 1.081, 1.078, 1.077, 1.075, 1.071, 1.066, 1.061, 1.056, 1.049, 1.041, 1.029, 1.028,
+ 1.008, 1.019, 1.031, 1.039, 1.048, 1.056, 1.061, 1.065, 1.069, 1.073, 1.075, 1.076, 1.078, 1.079, 1.079, 1.079, 1.081, 1.081, 1.081, 1.081, 1.078, 1.078, 1.076, 1.072, 1.069, 1.064, 1.058, 1.054, 1.047, 1.038, 1.029, 1.026
+ ]
+ },
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1.001, 1.005, 1.011, 1.015, 1.019, 1.021, 1.021, 1.022, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.025, 1.025, 1.024, 1.024, 1.024, 1.022, 1.021, 1.018, 1.015, 1.011,
+ 1.005, 1.008, 1.014, 1.021, 1.025, 1.028, 1.031, 1.032, 1.034, 1.035, 1.036, 1.037, 1.038, 1.038, 1.039, 1.038, 1.038, 1.038, 1.038, 1.037, 1.037, 1.036, 1.036, 1.035, 1.035, 1.034, 1.033, 1.029, 1.028, 1.023, 1.021, 1.021,
+ 1.009, 1.014, 1.021, 1.025, 1.028, 1.031, 1.032, 1.034, 1.035, 1.037, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.037, 1.036, 1.036, 1.036, 1.036, 1.034, 1.033, 1.029, 1.028, 1.023, 1.022,
+ 1.011, 1.016, 1.023, 1.027, 1.029, 1.031, 1.034, 1.035, 1.037, 1.038, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.037, 1.036, 1.034, 1.032, 1.029, 1.025, 1.022,
+ 1.013, 1.018, 1.024, 1.027, 1.031, 1.033, 1.035, 1.037, 1.038, 1.039, 1.041, 1.042, 1.042, 1.043, 1.043, 1.043, 1.042, 1.042, 1.042, 1.041, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.037, 1.035, 1.034, 1.029, 1.025, 1.023,
+ 1.014, 1.019, 1.025, 1.029, 1.031, 1.034, 1.037, 1.038, 1.039, 1.041, 1.042, 1.043, 1.043, 1.044, 1.044, 1.044, 1.043, 1.043, 1.043, 1.043, 1.042, 1.041, 1.041, 1.039, 1.039, 1.038, 1.038, 1.037, 1.034, 1.029, 1.026, 1.024,
+ 1.016, 1.021, 1.027, 1.031, 1.033, 1.035, 1.038, 1.039, 1.041, 1.042, 1.043, 1.044, 1.044, 1.045, 1.045, 1.045, 1.044, 1.044, 1.044, 1.044, 1.043, 1.042, 1.042, 1.041, 1.041, 1.039, 1.038, 1.038, 1.035, 1.031, 1.027, 1.025,
+ 1.017, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.042, 1.043, 1.044, 1.045, 1.046, 1.046, 1.046, 1.046, 1.046, 1.046, 1.045, 1.045, 1.044, 1.043, 1.043, 1.042, 1.041, 1.041, 1.039, 1.038, 1.036, 1.032, 1.028, 1.025,
+ 1.018, 1.023, 1.028, 1.032, 1.035, 1.038, 1.041, 1.041, 1.043, 1.045, 1.046, 1.046, 1.046, 1.047, 1.047, 1.047, 1.047, 1.047, 1.047, 1.046, 1.045, 1.044, 1.044, 1.043, 1.042, 1.041, 1.041, 1.039, 1.037, 1.032, 1.028, 1.026,
+ 1.019, 1.024, 1.029, 1.033, 1.036, 1.039, 1.041, 1.042, 1.044, 1.046, 1.046, 1.047, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.048, 1.047, 1.047, 1.045, 1.044, 1.044, 1.043, 1.042, 1.041, 1.039, 1.037, 1.033, 1.029, 1.027,
+ 1.021, 1.026, 1.029, 1.034, 1.037, 1.039, 1.042, 1.043, 1.045, 1.046, 1.047, 1.047, 1.048, 1.048, 1.049, 1.049, 1.049, 1.049, 1.048, 1.048, 1.047, 1.047, 1.046, 1.044, 1.044, 1.043, 1.041, 1.039, 1.037, 1.034, 1.029, 1.027,
+ 1.022, 1.026, 1.032, 1.035, 1.038, 1.039, 1.043, 1.044, 1.045, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.044, 1.043, 1.041, 1.039, 1.038, 1.034, 1.029, 1.027,
+ 1.023, 1.027, 1.032, 1.035, 1.039, 1.041, 1.043, 1.044, 1.045, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.041, 1.039, 1.035, 1.029, 1.027,
+ 1.024, 1.028, 1.033, 1.036, 1.039, 1.041, 1.043, 1.045, 1.046, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.047, 1.046, 1.045, 1.043, 1.042, 1.041, 1.039, 1.035, 1.029, 1.029,
+ 1.024, 1.029, 1.034, 1.037, 1.039, 1.041, 1.044, 1.045, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.045, 1.044, 1.042, 1.041, 1.039, 1.036, 1.031, 1.029,
+ 1.024, 1.029, 1.034, 1.037, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.039, 1.037, 1.031, 1.029,
+ 1.025, 1.031, 1.035, 1.037, 1.039, 1.042, 1.045, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.045, 1.043, 1.042, 1.041, 1.037, 1.032, 1.031,
+ 1.026, 1.031, 1.035, 1.037, 1.039, 1.043, 1.045, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.047, 1.047, 1.045, 1.044, 1.042, 1.041, 1.037, 1.032, 1.031,
+ 1.026, 1.031, 1.035, 1.038, 1.039, 1.043, 1.044, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.048, 1.048, 1.047, 1.045, 1.044, 1.043, 1.041, 1.037, 1.032, 1.031,
+ 1.025, 1.031, 1.035, 1.038, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.045, 1.044, 1.043, 1.041, 1.037, 1.032, 1.031,
+ 1.024, 1.031, 1.035, 1.038, 1.039, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.052, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.044, 1.043, 1.041, 1.037, 1.033, 1.031,
+ 1.025, 1.031, 1.035, 1.037, 1.039, 1.041, 1.044, 1.046, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.037, 1.033, 1.031,
+ 1.024, 1.031, 1.034, 1.037, 1.038, 1.041, 1.044, 1.045, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.037, 1.034, 1.032,
+ 1.024, 1.031, 1.034, 1.037, 1.038, 1.041, 1.043, 1.045, 1.046, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.038, 1.034, 1.032,
+ 1.024, 1.029, 1.034, 1.037, 1.038, 1.041, 1.043, 1.044, 1.046, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.048, 1.048, 1.047, 1.046, 1.045, 1.043, 1.041, 1.038, 1.034, 1.031,
+ 1.024, 1.028, 1.034, 1.037, 1.038, 1.041, 1.042, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.049, 1.048, 1.047, 1.047, 1.045, 1.044, 1.043, 1.039, 1.038, 1.033, 1.031,
+ 1.022, 1.027, 1.034, 1.035, 1.038, 1.039, 1.041, 1.043, 1.044, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.047, 1.047, 1.046, 1.045, 1.044, 1.043, 1.039, 1.037, 1.032, 1.029,
+ 1.022, 1.026, 1.032, 1.034, 1.037, 1.039, 1.041, 1.042, 1.044, 1.045, 1.046, 1.047, 1.048, 1.048, 1.048, 1.049, 1.049, 1.049, 1.049, 1.049, 1.049, 1.047, 1.047, 1.046, 1.045, 1.045, 1.044, 1.041, 1.039, 1.036, 1.031, 1.029,
+ 1.021, 1.025, 1.029, 1.033, 1.036, 1.038, 1.039, 1.041, 1.042, 1.044, 1.045, 1.046, 1.047, 1.047, 1.047, 1.047, 1.048, 1.048, 1.048, 1.047, 1.047, 1.047, 1.045, 1.045, 1.045, 1.044, 1.042, 1.039, 1.038, 1.034, 1.029, 1.028,
+ 1.019, 1.023, 1.028, 1.032, 1.035, 1.038, 1.039, 1.041, 1.042, 1.044, 1.044, 1.045, 1.045, 1.046, 1.046, 1.046, 1.047, 1.047, 1.047, 1.046, 1.046, 1.045, 1.045, 1.045, 1.044, 1.042, 1.041, 1.038, 1.037, 1.033, 1.029, 1.027,
+ 1.017, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.043, 1.044, 1.045, 1.045, 1.045, 1.045, 1.046, 1.046, 1.046, 1.046, 1.046, 1.045, 1.045, 1.045, 1.044, 1.043, 1.041, 1.039, 1.038, 1.035, 1.031, 1.029, 1.027,
+ 1.016, 1.022, 1.027, 1.031, 1.034, 1.037, 1.039, 1.041, 1.041, 1.043, 1.043, 1.044, 1.045, 1.045, 1.045, 1.045, 1.046, 1.046, 1.046, 1.045, 1.045, 1.045, 1.045, 1.044, 1.043, 1.041, 1.038, 1.038, 1.035, 1.031, 1.029, 1.028
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 1.001, 1.003, 1.005, 1.008, 1.009, 1.011, 1.011, 1.011, 1.011, 1.012, 1.012, 1.012, 1.013, 1.013, 1.013, 1.013, 1.013, 1.013, 1.013, 1.012, 1.012, 1.013, 1.013, 1.013, 1.015, 1.015, 1.015, 1.014, 1.014, 1.014, 1.013, 1.012,
+ 1.007, 1.008, 1.011, 1.015, 1.017, 1.019, 1.019, 1.021, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.022, 1.022, 1.022, 1.022, 1.023, 1.023, 1.023, 1.024, 1.024, 1.023, 1.023, 1.022, 1.022, 1.021,
+ 1.008, 1.011, 1.015, 1.017, 1.019, 1.021, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.023, 1.024, 1.024, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.023, 1.024, 1.024, 1.024, 1.024, 1.023, 1.023, 1.022, 1.022,
+ 1.009, 1.012, 1.016, 1.018, 1.019, 1.021, 1.021, 1.021, 1.022, 1.023, 1.023, 1.023, 1.024, 1.024, 1.025, 1.024, 1.024, 1.024, 1.024, 1.023, 1.023, 1.023, 1.024, 1.024, 1.024, 1.025, 1.025, 1.024, 1.024, 1.023, 1.022, 1.021,
+ 1.009, 1.013, 1.016, 1.018, 1.019, 1.021, 1.021, 1.022, 1.023, 1.023, 1.024, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.024, 1.024, 1.024, 1.024, 1.025, 1.025, 1.025, 1.025, 1.025, 1.024, 1.023, 1.022, 1.021,
+ 1.011, 1.014, 1.017, 1.019, 1.019, 1.021, 1.022, 1.023, 1.023, 1.024, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.026, 1.026, 1.026, 1.024, 1.022, 1.022, 1.022,
+ 1.011, 1.014, 1.018, 1.019, 1.021, 1.022, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.027, 1.026, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.022, 1.022, 1.022,
+ 1.012, 1.015, 1.018, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.023, 1.022, 1.021,
+ 1.012, 1.015, 1.018, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.026, 1.025, 1.023, 1.022, 1.021,
+ 1.013, 1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.021, 1.021,
+ 1.014, 1.017, 1.019, 1.021, 1.022, 1.023, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.021, 1.021,
+ 1.014, 1.017, 1.021, 1.021, 1.022, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.024, 1.021, 1.021,
+ 1.014, 1.017, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.024, 1.021, 1.021,
+ 1.015, 1.017, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.024, 1.022, 1.021,
+ 1.015, 1.018, 1.021, 1.022, 1.023, 1.024, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.025, 1.022, 1.021,
+ 1.016, 1.019, 1.022, 1.022, 1.023, 1.024, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.027, 1.027, 1.026, 1.023, 1.022,
+ 1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.026, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.022,
+ 1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.027, 1.026, 1.023, 1.022,
+ 1.016, 1.019, 1.022, 1.023, 1.023, 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.028, 1.028, 1.026, 1.024, 1.023,
+ 1.016, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.026, 1.024, 1.023,
+ 1.015, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.027, 1.028, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.025, 1.024,
+ 1.016, 1.019, 1.022, 1.023, 1.023, 1.024, 1.025, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.029, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.026, 1.025, 1.025,
+ 1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.025, 1.026, 1.026, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.026, 1.025,
+ 1.016, 1.019, 1.021, 1.023, 1.023, 1.024, 1.025, 1.025, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.027, 1.027, 1.026,
+ 1.016, 1.019, 1.021, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026,
+ 1.016, 1.018, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026,
+ 1.016, 1.018, 1.021, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.026, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.026, 1.026,
+ 1.016, 1.017, 1.021, 1.022, 1.022, 1.023, 1.024, 1.024, 1.024, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.029, 1.028, 1.028, 1.027, 1.026, 1.026,
+ 1.015, 1.016, 1.019, 1.021, 1.022, 1.023, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.026, 1.026,
+ 1.014, 1.016, 1.019, 1.021, 1.022, 1.023, 1.023, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.027, 1.027, 1.026,
+ 1.015, 1.016, 1.019, 1.021, 1.021, 1.023, 1.024, 1.024, 1.025, 1.025, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.027, 1.028, 1.027, 1.027, 1.027,
+ 1.015, 1.017, 1.019, 1.019, 1.021, 1.023, 1.024, 1.025, 1.025, 1.025, 1.026, 1.026, 1.025, 1.026, 1.026, 1.026, 1.026, 1.027, 1.027, 1.027, 1.028, 1.028, 1.028, 1.028, 1.028, 1.027, 1.027, 1.028, 1.028, 1.028, 1.027, 1.029
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.319, 1.289, 1.238, 1.198, 1.169, 1.139, 1.116, 1.094, 1.082, 1.074, 1.068, 1.064, 1.062, 1.061, 1.061, 1.061, 1.061, 1.062, 1.064, 1.065, 1.068, 1.074, 1.084, 1.101, 1.123, 1.148, 1.181, 1.221, 1.277, 1.356, 1.468, 1.477,
+ 1.292, 1.255, 1.208, 1.171, 1.145, 1.121, 1.099, 1.086, 1.073, 1.064, 1.058, 1.053, 1.049, 1.047, 1.046, 1.045, 1.048, 1.049, 1.051, 1.053, 1.059, 1.065, 1.074, 1.089, 1.105, 1.125, 1.152, 1.189, 1.238, 1.309, 1.408, 1.468,
+ 1.269, 1.234, 1.189, 1.155, 1.136, 1.112, 1.086, 1.073, 1.064, 1.057, 1.052, 1.047, 1.043, 1.041, 1.041, 1.041, 1.043, 1.047, 1.048, 1.049, 1.053, 1.059, 1.065, 1.074, 1.089, 1.109, 1.136, 1.171, 1.216, 1.278, 1.369, 1.408,
+ 1.249, 1.216, 1.173, 1.142, 1.125, 1.105, 1.078, 1.066, 1.058, 1.053, 1.047, 1.043, 1.038, 1.036, 1.035, 1.035, 1.039, 1.042, 1.043, 1.044, 1.048, 1.053, 1.059, 1.066, 1.079, 1.098, 1.122, 1.155, 1.197, 1.252, 1.337, 1.369,
+ 1.233, 1.199, 1.159, 1.129, 1.111, 1.095, 1.073, 1.061, 1.054, 1.049, 1.044, 1.038, 1.033, 1.031, 1.029, 1.029, 1.033, 1.037, 1.037, 1.037, 1.042, 1.047, 1.052, 1.059, 1.071, 1.087, 1.111, 1.142, 1.181, 1.232, 1.307, 1.337,
+ 1.217, 1.185, 1.146, 1.116, 1.097, 1.083, 1.069, 1.055, 1.049, 1.045, 1.039, 1.033, 1.029, 1.027, 1.025, 1.024, 1.025, 1.028, 1.029, 1.031, 1.035, 1.041, 1.047, 1.053, 1.063, 1.079, 1.101, 1.131, 1.168, 1.215, 1.282, 1.308,
+ 1.206, 1.172, 1.134, 1.106, 1.088, 1.076, 1.064, 1.051, 1.045, 1.041, 1.034, 1.029, 1.026, 1.023, 1.021, 1.021, 1.021, 1.021, 1.024, 1.027, 1.031, 1.035, 1.041, 1.048, 1.058, 1.072, 1.092, 1.121, 1.157, 1.201, 1.262, 1.288,
+ 1.196, 1.161, 1.125, 1.099, 1.081, 1.071, 1.059, 1.047, 1.041, 1.035, 1.029, 1.025, 1.022, 1.019, 1.018, 1.017, 1.017, 1.018, 1.021, 1.023, 1.027, 1.031, 1.036, 1.044, 1.053, 1.066, 1.085, 1.113, 1.148, 1.191, 1.247, 1.273,
+ 1.188, 1.152, 1.118, 1.093, 1.076, 1.064, 1.052, 1.044, 1.037, 1.031, 1.026, 1.022, 1.019, 1.017, 1.015, 1.015, 1.014, 1.015, 1.017, 1.019, 1.023, 1.027, 1.033, 1.041, 1.049, 1.061, 1.078, 1.105, 1.139, 1.181, 1.235, 1.261,
+ 1.182, 1.145, 1.112, 1.088, 1.072, 1.059, 1.049, 1.041, 1.035, 1.028, 1.023, 1.019, 1.016, 1.014, 1.012, 1.012, 1.011, 1.012, 1.015, 1.017, 1.021, 1.024, 1.029, 1.037, 1.045, 1.056, 1.074, 1.099, 1.133, 1.173, 1.224, 1.251,
+ 1.178, 1.139, 1.106, 1.083, 1.068, 1.056, 1.046, 1.039, 1.032, 1.026, 1.021, 1.017, 1.014, 1.011, 1.009, 1.009, 1.009, 1.009, 1.012, 1.015, 1.018, 1.022, 1.028, 1.035, 1.042, 1.054, 1.069, 1.095, 1.127, 1.168, 1.217, 1.244,
+ 1.174, 1.134, 1.102, 1.081, 1.065, 1.053, 1.044, 1.037, 1.029, 1.024, 1.019, 1.015, 1.012, 1.009, 1.007, 1.006, 1.006, 1.007, 1.009, 1.012, 1.016, 1.019, 1.027, 1.034, 1.041, 1.051, 1.067, 1.092, 1.123, 1.161, 1.211, 1.239,
+ 1.173, 1.131, 1.099, 1.078, 1.062, 1.051, 1.042, 1.035, 1.028, 1.023, 1.018, 1.013, 1.009, 1.007, 1.005, 1.004, 1.004, 1.005, 1.007, 1.011, 1.014, 1.018, 1.025, 1.032, 1.039, 1.049, 1.064, 1.089, 1.119, 1.158, 1.207, 1.236,
+ 1.171, 1.129, 1.097, 1.075, 1.061, 1.049, 1.041, 1.033, 1.027, 1.021, 1.017, 1.012, 1.008, 1.005, 1.004, 1.003, 1.003, 1.003, 1.005, 1.009, 1.013, 1.017, 1.025, 1.031, 1.037, 1.047, 1.062, 1.087, 1.117, 1.155, 1.205, 1.233,
+ 1.169, 1.128, 1.097, 1.074, 1.059, 1.049, 1.041, 1.033, 1.026, 1.021, 1.015, 1.011, 1.007, 1.005, 1.004, 1.002, 1.002, 1.002, 1.004, 1.008, 1.012, 1.017, 1.024, 1.031, 1.036, 1.046, 1.061, 1.085, 1.116, 1.155, 1.203, 1.233,
+ 1.169, 1.128, 1.097, 1.074, 1.059, 1.048, 1.039, 1.031, 1.025, 1.021, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.001, 1.002, 1.004, 1.008, 1.012, 1.017, 1.024, 1.031, 1.036, 1.046, 1.061, 1.084, 1.115, 1.155, 1.203, 1.233,
+ 1.169, 1.129, 1.098, 1.075, 1.059, 1.048, 1.039, 1.031, 1.025, 1.019, 1.015, 1.011, 1.006, 1.004, 1.002, 1.001, 1.001, 1.002, 1.004, 1.008, 1.012, 1.017, 1.025, 1.031, 1.036, 1.046, 1.061, 1.084, 1.116, 1.155, 1.204, 1.234,
+ 1.169, 1.131, 1.099, 1.076, 1.061, 1.049, 1.039, 1.032, 1.025, 1.021, 1.015, 1.011, 1.007, 1.005, 1.003, 1.001, 1.001, 1.002, 1.005, 1.009, 1.013, 1.017, 1.024, 1.031, 1.037, 1.047, 1.061, 1.085, 1.118, 1.158, 1.208, 1.237,
+ 1.171, 1.134, 1.102, 1.078, 1.062, 1.051, 1.041, 1.033, 1.026, 1.021, 1.016, 1.011, 1.009, 1.006, 1.004, 1.002, 1.002, 1.003, 1.006, 1.012, 1.015, 1.018, 1.023, 1.031, 1.038, 1.048, 1.064, 1.088, 1.121, 1.162, 1.212, 1.241,
+ 1.174, 1.139, 1.106, 1.081, 1.064, 1.052, 1.043, 1.034, 1.027, 1.022, 1.017, 1.013, 1.009, 1.008, 1.005, 1.004, 1.004, 1.005, 1.008, 1.013, 1.017, 1.019, 1.024, 1.031, 1.039, 1.051, 1.067, 1.092, 1.125, 1.167, 1.219, 1.246,
+ 1.179, 1.145, 1.112, 1.085, 1.067, 1.054, 1.045, 1.036, 1.029, 1.023, 1.018, 1.014, 1.011, 1.008, 1.007, 1.006, 1.007, 1.008, 1.009, 1.014, 1.018, 1.019, 1.025, 1.032, 1.041, 1.054, 1.071, 1.096, 1.131, 1.174, 1.228, 1.255,
+ 1.186, 1.152, 1.117, 1.089, 1.069, 1.057, 1.046, 1.038, 1.031, 1.025, 1.019, 1.016, 1.013, 1.009, 1.009, 1.008, 1.009, 1.009, 1.011, 1.014, 1.018, 1.021, 1.027, 1.034, 1.044, 1.056, 1.075, 1.102, 1.137, 1.182, 1.239, 1.268,
+ 1.194, 1.159, 1.123, 1.095, 1.075, 1.061, 1.049, 1.041, 1.033, 1.027, 1.022, 1.018, 1.015, 1.013, 1.011, 1.011, 1.011, 1.011, 1.013, 1.016, 1.019, 1.024, 1.031, 1.037, 1.047, 1.061, 1.082, 1.109, 1.147, 1.194, 1.256, 1.281,
+ 1.203, 1.169, 1.132, 1.102, 1.081, 1.065, 1.053, 1.044, 1.036, 1.029, 1.026, 1.021, 1.018, 1.015, 1.013, 1.013, 1.013, 1.014, 1.015, 1.018, 1.022, 1.028, 1.034, 1.041, 1.052, 1.067, 1.091, 1.121, 1.157, 1.207, 1.273, 1.299,
+ 1.214, 1.179, 1.142, 1.111, 1.088, 1.069, 1.057, 1.047, 1.039, 1.033, 1.028, 1.024, 1.021, 1.018, 1.016, 1.015, 1.015, 1.016, 1.018, 1.021, 1.026, 1.031, 1.038, 1.046, 1.058, 1.075, 1.101, 1.133, 1.171, 1.222, 1.293, 1.319,
+ 1.227, 1.194, 1.153, 1.121, 1.096, 1.076, 1.062, 1.052, 1.043, 1.036, 1.031, 1.027, 1.023, 1.021, 1.019, 1.019, 1.018, 1.019, 1.021, 1.025, 1.029, 1.035, 1.042, 1.051, 1.065, 1.085, 1.112, 1.145, 1.185, 1.239, 1.318, 1.346,
+ 1.244, 1.211, 1.167, 1.133, 1.107, 1.088, 1.069, 1.057, 1.047, 1.041, 1.034, 1.031, 1.026, 1.024, 1.022, 1.022, 1.022, 1.023, 1.025, 1.029, 1.034, 1.039, 1.047, 1.059, 1.075, 1.096, 1.124, 1.158, 1.202, 1.261, 1.346, 1.381,
+ 1.264, 1.229, 1.183, 1.147, 1.119, 1.098, 1.077, 1.063, 1.053, 1.045, 1.039, 1.034, 1.031, 1.027, 1.026, 1.026, 1.026, 1.027, 1.029, 1.034, 1.039, 1.045, 1.054, 1.068, 1.086, 1.109, 1.138, 1.175, 1.222, 1.288, 1.381, 1.423,
+ 1.287, 1.251, 1.201, 1.163, 1.134, 1.109, 1.086, 1.071, 1.059, 1.051, 1.044, 1.039, 1.034, 1.032, 1.031, 1.031, 1.031, 1.032, 1.036, 1.039, 1.045, 1.053, 1.064, 1.079, 1.101, 1.125, 1.156, 1.195, 1.246, 1.321, 1.423, 1.479,
+ 1.314, 1.275, 1.222, 1.182, 1.151, 1.123, 1.098, 1.082, 1.068, 1.058, 1.051, 1.045, 1.041, 1.037, 1.036, 1.036, 1.037, 1.039, 1.041, 1.047, 1.053, 1.063, 1.076, 1.093, 1.115, 1.141, 1.174, 1.216, 1.275, 1.361, 1.479, 1.556,
+ 1.349, 1.307, 1.246, 1.203, 1.169, 1.138, 1.114, 1.095, 1.079, 1.067, 1.058, 1.052, 1.048, 1.045, 1.043, 1.043, 1.044, 1.046, 1.049, 1.055, 1.063, 1.075, 1.091, 1.111, 1.134, 1.161, 1.196, 1.242, 1.311, 1.407, 1.556, 1.617,
+ 1.389, 1.333, 1.266, 1.219, 1.184, 1.152, 1.127, 1.106, 1.089, 1.076, 1.066, 1.058, 1.053, 1.049, 1.049, 1.049, 1.049, 1.051, 1.055, 1.062, 1.072, 1.085, 1.102, 1.127, 1.151, 1.177, 1.213, 1.264, 1.339, 1.445, 1.617, 1.667
+ ],
+ "sigma": 0.00092,
+ "sigma_Cb": 0.00149
+ }
+ },
+ {
+ "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.82604, -0.41219, -0.41384,
+ -0.51919, 1.83221, -0.31302,
+ 0.23201, -1.42044, 2.18842
+ ]
+ },
+ {
+ "ct": 2820,
+ "ccm":
+ [
+ 1.80891, -0.47916, -0.32974,
+ -0.47311, 1.83395, -0.36084,
+ 0.21814, -1.22973, 2.01158
+ ]
+ },
+ {
+ "ct": 2830,
+ "ccm":
+ [
+ 1.80397, -0.51779, -0.28617,
+ -0.64069, 2.16622, -0.52553,
+ 0.12013, -0.95702, 1.83689
+ ]
+ },
+ {
+ "ct": 2885,
+ "ccm":
+ [
+ 1.78861, -0.50175, -0.28685,
+ -0.63703, 2.14176, -0.50473,
+ 0.08715, -0.86455, 1.77741
+ ]
+ },
+ {
+ "ct": 3601,
+ "ccm":
+ [
+ 1.85135, -0.56992, -0.28143,
+ -0.56285, 2.08651, -0.52366,
+ 0.03737, -0.70813, 1.67076
+ ]
+ },
+ {
+ "ct": 3615,
+ "ccm":
+ [
+ 1.87447, -0.60511, -0.26936,
+ -0.55592, 2.07733, -0.52141,
+ 0.04105, -0.70347, 1.66242
+ ]
+ },
+ {
+ "ct": 3622,
+ "ccm":
+ [
+ 1.85269, -0.58224, -0.27044,
+ -0.55133, 2.06403, -0.51271,
+ 0.03952, -0.69055, 1.65103
+ ]
+ },
+ {
+ "ct": 4345,
+ "ccm":
+ [
+ 1.81525, -0.56996, -0.24529,
+ -0.49203, 2.16996, -0.67793,
+ 0.02513, -0.67456, 1.64943
+ ]
+ },
+ {
+ "ct": 4410,
+ "ccm":
+ [
+ 1.83312, -0.59611, -0.23701,
+ -0.48332, 2.15123, -0.66791,
+ 0.02629, -0.67203, 1.64574
+ ]
+ },
+ {
+ "ct": 4486,
+ "ccm":
+ [
+ 1.84759, -0.60181, -0.24578,
+ -0.47792, 2.13471, -0.65679,
+ 0.02056, -0.65959, 1.63903
+ ]
+ },
+ {
+ "ct": 4576,
+ "ccm":
+ [
+ 1.83733, -0.58859, -0.24874,
+ -0.47443, 2.13699, -0.66255,
+ 0.01842, -0.65402, 1.63561
+ ]
+ },
+ {
+ "ct": 5657,
+ "ccm":
+ [
+ 1.84437, -0.57638, -0.26799,
+ -0.44569, 2.04163, -0.59593,
+ -0.01759, -0.52787, 1.54546
+ ]
+ },
+ {
+ "ct": 5672,
+ "ccm":
+ [
+ 1.83986, -0.57025, -0.26962,
+ -0.44974, 2.04763, -0.59789,
+ -0.03246, -0.51626, 1.54872
+ ]
+ },
+ {
+ "ct": 5710,
+ "ccm":
+ [
+ 1.83822, -0.57688, -0.26134,
+ -0.44263, 2.03779, -0.59516,
+ -0.02552, -0.52605, 1.55157
+ ]
+ },
+ {
+ "ct": 6850,
+ "ccm":
+ [
+ 1.80507, -0.22489, -0.58017,
+ -0.48609, 2.48778, -1.00168,
+ -0.10995, -0.63701, 1.74696
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.cac": { }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "night": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+}
diff --git a/src/ipa/rpi/pisp/data/imx290.json b/src/ipa/rpi/pisp/data/imx290.json
new file mode 100644
index 00000000..37421e85
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx290.json
@@ -0,0 +1,341 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ },
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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 ]
+ },
+ "short":
+ {
+ "shutter": [ 10, 5000, 10000, 20000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.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.604, 2.365, 2.2, 2.039, 1.916, 1.799, 1.707, 1.622, 1.552, 1.487, 1.435, 1.389, 1.356, 1.332, 1.317, 1.31, 1.308, 1.313, 1.324, 1.344, 1.37, 1.41, 1.454, 1.508, 1.567, 1.641, 1.719, 1.82, 1.925, 2.073, 2.221,
+ 2.749, 2.521, 2.294, 2.134, 1.979, 1.861, 1.749, 1.661, 1.578, 1.511, 1.448, 1.398, 1.354, 1.322, 1.3, 1.285, 1.278, 1.277, 1.281, 1.292, 1.311, 1.336, 1.374, 1.416, 1.469, 1.526, 1.596, 1.671, 1.77, 1.872, 2.019, 2.166,
+ 2.654, 2.438, 2.223, 2.069, 1.919, 1.807, 1.7, 1.614, 1.534, 1.469, 1.409, 1.361, 1.318, 1.288, 1.267, 1.254, 1.247, 1.245, 1.25, 1.259, 1.277, 1.302, 1.338, 1.379, 1.43, 1.485, 1.552, 1.623, 1.719, 1.819, 1.965, 2.112,
+ 2.563, 2.359, 2.155, 2.007, 1.863, 1.755, 1.653, 1.571, 1.493, 1.43, 1.372, 1.325, 1.284, 1.256, 1.236, 1.223, 1.217, 1.216, 1.219, 1.229, 1.246, 1.269, 1.305, 1.344, 1.393, 1.446, 1.51, 1.578, 1.672, 1.77, 1.914, 2.059,
+ 2.494, 2.299, 2.103, 1.961, 1.822, 1.718, 1.619, 1.538, 1.461, 1.399, 1.343, 1.298, 1.259, 1.232, 1.213, 1.2, 1.194, 1.193, 1.196, 1.205, 1.222, 1.245, 1.279, 1.318, 1.365, 1.416, 1.481, 1.549, 1.641, 1.735, 1.875, 2.015,
+ 2.425, 2.238, 2.05, 1.914, 1.782, 1.681, 1.585, 1.505, 1.429, 1.369, 1.314, 1.271, 1.234, 1.208, 1.189, 1.177, 1.171, 1.169, 1.173, 1.182, 1.198, 1.221, 1.254, 1.292, 1.338, 1.387, 1.452, 1.519, 1.609, 1.701, 1.836, 1.971,
+ 2.363, 2.183, 2.003, 1.873, 1.746, 1.648, 1.555, 1.477, 1.401, 1.342, 1.289, 1.247, 1.212, 1.187, 1.168, 1.156, 1.149, 1.148, 1.152, 1.16, 1.177, 1.198, 1.231, 1.267, 1.312, 1.36, 1.425, 1.492, 1.58, 1.671, 1.802, 1.932,
+ 2.314, 2.14, 1.965, 1.839, 1.716, 1.622, 1.532, 1.454, 1.38, 1.322, 1.27, 1.229, 1.195, 1.169, 1.149, 1.137, 1.129, 1.128, 1.132, 1.142, 1.158, 1.18, 1.21, 1.245, 1.289, 1.336, 1.401, 1.469, 1.557, 1.649, 1.776, 1.903,
+ 2.264, 2.096, 1.927, 1.805, 1.687, 1.596, 1.509, 1.432, 1.358, 1.301, 1.251, 1.211, 1.177, 1.151, 1.131, 1.117, 1.109, 1.108, 1.113, 1.123, 1.14, 1.161, 1.19, 1.222, 1.265, 1.313, 1.378, 1.445, 1.534, 1.626, 1.75, 1.874,
+ 2.225, 2.061, 1.897, 1.778, 1.663, 1.574, 1.489, 1.414, 1.341, 1.285, 1.235, 1.196, 1.163, 1.136, 1.115, 1.1, 1.091, 1.089, 1.095, 1.106, 1.124, 1.145, 1.174, 1.205, 1.248, 1.294, 1.359, 1.427, 1.516, 1.606, 1.728, 1.849,
+ 2.193, 2.033, 1.872, 1.756, 1.642, 1.556, 1.473, 1.399, 1.327, 1.272, 1.224, 1.185, 1.15, 1.123, 1.1, 1.084, 1.074, 1.072, 1.078, 1.09, 1.11, 1.133, 1.161, 1.193, 1.234, 1.28, 1.345, 1.413, 1.501, 1.59, 1.709, 1.828,
+ 2.161, 2.004, 1.848, 1.734, 1.622, 1.537, 1.457, 1.384, 1.313, 1.26, 1.212, 1.173, 1.138, 1.11, 1.085, 1.068, 1.057, 1.055, 1.062, 1.075, 1.096, 1.12, 1.148, 1.18, 1.221, 1.266, 1.331, 1.399, 1.486, 1.574, 1.69, 1.807,
+ 2.14, 1.986, 1.832, 1.719, 1.609, 1.525, 1.445, 1.373, 1.304, 1.251, 1.204, 1.165, 1.129, 1.1, 1.074, 1.055, 1.043, 1.041, 1.049, 1.063, 1.086, 1.11, 1.14, 1.172, 1.212, 1.258, 1.323, 1.39, 1.477, 1.566, 1.679, 1.792,
+ 2.123, 1.971, 1.819, 1.707, 1.598, 1.514, 1.434, 1.364, 1.296, 1.243, 1.197, 1.158, 1.122, 1.091, 1.064, 1.044, 1.031, 1.027, 1.036, 1.052, 1.076, 1.102, 1.132, 1.165, 1.206, 1.251, 1.316, 1.383, 1.471, 1.56, 1.67, 1.78,
+ 2.106, 1.956, 1.806, 1.695, 1.587, 1.504, 1.424, 1.354, 1.288, 1.236, 1.19, 1.15, 1.114, 1.083, 1.055, 1.033, 1.018, 1.014, 1.024, 1.04, 1.066, 1.094, 1.124, 1.158, 1.199, 1.245, 1.309, 1.376, 1.465, 1.555, 1.661, 1.767,
+ 2.104, 1.955, 1.805, 1.694, 1.586, 1.502, 1.422, 1.352, 1.285, 1.234, 1.188, 1.149, 1.113, 1.081, 1.053, 1.031, 1.014, 1.011, 1.021, 1.038, 1.064, 1.091, 1.122, 1.156, 1.198, 1.244, 1.308, 1.376, 1.465, 1.555, 1.66, 1.766,
+ 2.104, 1.955, 1.806, 1.695, 1.586, 1.502, 1.421, 1.351, 1.284, 1.232, 1.187, 1.148, 1.112, 1.08, 1.051, 1.029, 1.012, 1.008, 1.02, 1.036, 1.062, 1.089, 1.12, 1.155, 1.197, 1.244, 1.308, 1.375, 1.465, 1.555, 1.661, 1.766,
+ 2.105, 1.956, 1.807, 1.696, 1.587, 1.502, 1.42, 1.35, 1.282, 1.231, 1.186, 1.148, 1.112, 1.08, 1.051, 1.028, 1.011, 1.007, 1.019, 1.036, 1.061, 1.088, 1.119, 1.154, 1.197, 1.244, 1.308, 1.376, 1.466, 1.557, 1.662, 1.767,
+ 2.121, 1.97, 1.818, 1.705, 1.595, 1.508, 1.424, 1.353, 1.286, 1.236, 1.191, 1.153, 1.118, 1.087, 1.059, 1.038, 1.022, 1.018, 1.028, 1.044, 1.067, 1.093, 1.124, 1.158, 1.201, 1.248, 1.314, 1.383, 1.474, 1.567, 1.672, 1.777,
+ 2.137, 1.983, 1.829, 1.715, 1.603, 1.514, 1.428, 1.357, 1.291, 1.24, 1.196, 1.158, 1.123, 1.094, 1.068, 1.047, 1.033, 1.029, 1.038, 1.052, 1.074, 1.098, 1.128, 1.162, 1.205, 1.253, 1.32, 1.39, 1.483, 1.577, 1.682, 1.788,
+ 2.154, 1.998, 1.843, 1.726, 1.613, 1.522, 1.435, 1.364, 1.297, 1.246, 1.202, 1.164, 1.131, 1.102, 1.078, 1.059, 1.045, 1.041, 1.048, 1.061, 1.082, 1.105, 1.134, 1.167, 1.211, 1.259, 1.327, 1.399, 1.494, 1.588, 1.694, 1.8,
+ 2.176, 2.019, 1.862, 1.744, 1.628, 1.537, 1.449, 1.377, 1.309, 1.258, 1.213, 1.176, 1.143, 1.116, 1.092, 1.074, 1.061, 1.057, 1.063, 1.075, 1.094, 1.117, 1.146, 1.178, 1.222, 1.27, 1.34, 1.414, 1.509, 1.604, 1.711, 1.818,
+ 2.199, 2.04, 1.881, 1.761, 1.644, 1.552, 1.464, 1.391, 1.321, 1.269, 1.223, 1.187, 1.155, 1.129, 1.106, 1.09, 1.078, 1.074, 1.078, 1.088, 1.107, 1.128, 1.157, 1.189, 1.233, 1.281, 1.353, 1.428, 1.524, 1.62, 1.728, 1.836,
+ 2.228, 2.066, 1.904, 1.782, 1.662, 1.57, 1.482, 1.408, 1.337, 1.284, 1.237, 1.201, 1.17, 1.145, 1.123, 1.107, 1.096, 1.092, 1.095, 1.104, 1.121, 1.142, 1.17, 1.203, 1.247, 1.297, 1.37, 1.446, 1.542, 1.639, 1.75, 1.86,
+ 2.267, 2.099, 1.932, 1.807, 1.684, 1.592, 1.504, 1.428, 1.356, 1.302, 1.255, 1.219, 1.189, 1.164, 1.141, 1.125, 1.115, 1.111, 1.114, 1.123, 1.138, 1.158, 1.186, 1.22, 1.266, 1.318, 1.391, 1.467, 1.563, 1.661, 1.776, 1.891,
+ 2.305, 2.132, 1.96, 1.832, 1.707, 1.614, 1.526, 1.449, 1.375, 1.32, 1.272, 1.237, 1.208, 1.182, 1.16, 1.144, 1.135, 1.131, 1.134, 1.141, 1.155, 1.174, 1.203, 1.236, 1.285, 1.338, 1.412, 1.489, 1.585, 1.682, 1.802, 1.922,
+ 2.351, 2.173, 1.996, 1.864, 1.736, 1.641, 1.552, 1.474, 1.4, 1.344, 1.294, 1.258, 1.228, 1.203, 1.181, 1.165, 1.156, 1.152, 1.155, 1.162, 1.176, 1.195, 1.224, 1.259, 1.309, 1.365, 1.439, 1.516, 1.613, 1.711, 1.835, 1.96,
+ 2.4, 2.218, 2.036, 1.901, 1.768, 1.671, 1.58, 1.502, 1.428, 1.37, 1.319, 1.281, 1.249, 1.224, 1.203, 1.188, 1.178, 1.174, 1.177, 1.184, 1.197, 1.217, 1.248, 1.285, 1.337, 1.394, 1.469, 1.547, 1.644, 1.743, 1.873, 2.002,
+ 2.45, 2.264, 2.077, 1.938, 1.801, 1.702, 1.608, 1.53, 1.456, 1.397, 1.344, 1.304, 1.271, 1.245, 1.224, 1.21, 1.2, 1.196, 1.199, 1.206, 1.219, 1.239, 1.272, 1.311, 1.365, 1.424, 1.5, 1.578, 1.676, 1.776, 1.91, 2.044,
+ 2.513, 2.318, 2.124, 1.984, 1.848, 1.747, 1.652, 1.572, 1.496, 1.436, 1.383, 1.341, 1.303, 1.274, 1.253, 1.238, 1.228, 1.225, 1.228, 1.235, 1.248, 1.269, 1.303, 1.343, 1.4, 1.46, 1.537, 1.617, 1.718, 1.82, 1.962, 2.103,
+ 2.579, 2.376, 2.172, 2.032, 1.897, 1.796, 1.7, 1.617, 1.538, 1.479, 1.426, 1.38, 1.337, 1.306, 1.283, 1.267, 1.258, 1.254, 1.257, 1.265, 1.279, 1.3, 1.336, 1.377, 1.435, 1.497, 1.576, 1.658, 1.761, 1.867, 2.016, 2.165,
+ 2.645, 2.433, 2.22, 2.08, 1.946, 1.844, 1.747, 1.663, 1.581, 1.521, 1.468, 1.419, 1.371, 1.337, 1.313, 1.296, 1.287, 1.284, 1.287, 1.295, 1.309, 1.331, 1.368, 1.411, 1.471, 1.535, 1.615, 1.699, 1.805, 1.914, 2.071, 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/pisp/data/imx296.json b/src/ipa/rpi/pisp/data/imx296.json
new file mode 100644
index 00000000..d9dde898
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx296.json
@@ -0,0 +1,1194 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 4724,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 860,
+ "reference_Y": 14551
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.751
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 226,
+ "slope": 0.01032
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2875.0, 0.4699, 0.3209,
+ 3610.0, 0.4089, 0.4265,
+ 4640.0, 0.3281, 0.5417,
+ 5912.0, 0.2992, 0.5771,
+ 7630.0, 0.2285, 0.6524
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01783,
+ "transverse_neg": 0.02154
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.084, 2.084, 2.085, 2.085, 2.085, 2.087, 2.088, 2.087, 2.086, 2.082, 2.082, 2.084, 2.086, 2.088, 2.088, 2.088, 2.087, 2.088, 2.088, 2.091, 2.092, 2.093, 2.093, 2.093, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.091, 2.088,
+ 2.086, 2.086, 2.087, 2.088, 2.089, 2.089, 2.091, 2.089, 2.087, 2.086, 2.087, 2.088, 2.091, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.093, 2.093, 2.094, 2.095, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.093,
+ 2.087, 2.087, 2.088, 2.091, 2.091, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.096,
+ 2.089, 2.088, 2.089, 2.091, 2.091, 2.092, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.092, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.095, 2.096, 2.096, 2.097, 2.099, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.099, 2.098, 2.097,
+ 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.094, 2.095, 2.096, 2.097, 2.098, 2.098, 2.098, 2.101, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.089, 2.091, 2.092, 2.092, 2.092, 2.092, 2.094, 2.096, 2.096, 2.097, 2.098, 2.099, 2.099, 2.099, 2.099, 2.099, 2.097,
+ 2.093, 2.094, 2.094, 2.094, 2.095, 2.093, 2.092, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.096, 2.096, 2.097, 2.098, 2.098, 2.101, 2.101, 2.099, 2.099, 2.099,
+ 2.094, 2.094, 2.094, 2.095, 2.095, 2.095, 2.091, 2.089, 2.091, 2.089, 2.089, 2.089, 2.091, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.093, 2.095, 2.096, 2.097, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.099,
+ 2.095, 2.094, 2.094, 2.095, 2.096, 2.095, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.093, 2.094, 2.096, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.101, 2.099,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.095, 2.092, 2.089, 2.089, 2.088, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.093, 2.093, 2.095, 2.096, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.101,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.092, 2.092, 2.094, 2.094, 2.094, 2.096, 2.096, 2.098, 2.099, 2.102, 2.103, 2.103, 2.102, 2.102,
+ 2.095, 2.095, 2.095, 2.096, 2.096, 2.094, 2.093, 2.091, 2.091, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.093, 2.094, 2.094, 2.095, 2.096, 2.097, 2.098, 2.099, 2.103, 2.103, 2.103, 2.101, 2.101,
+ 2.095, 2.096, 2.096, 2.097, 2.096, 2.095, 2.093, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.096, 2.097, 2.099, 2.101, 2.103, 2.103, 2.103, 2.101, 2.099,
+ 2.096, 2.096, 2.097, 2.096, 2.097, 2.096, 2.094, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.099, 2.101, 2.103, 2.103, 2.102, 2.101, 2.101,
+ 2.096, 2.096, 2.097, 2.097, 2.097, 2.096, 2.094, 2.093, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.097, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101,
+ 2.097, 2.096, 2.097, 2.097, 2.097, 2.097, 2.095, 2.093, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.092, 2.092, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.101,
+ 2.098, 2.097, 2.096, 2.097, 2.097, 2.097, 2.095, 2.094, 2.094, 2.094, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.095, 2.095, 2.094, 2.093, 2.095, 2.096, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.102,
+ 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.095, 2.094, 2.095, 2.093, 2.093, 2.092, 2.092, 2.092, 2.094, 2.094, 2.096, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.102, 2.101, 2.102,
+ 2.098, 2.097, 2.097, 2.098, 2.097, 2.096, 2.095, 2.095, 2.095, 2.094, 2.093, 2.093, 2.094, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.095, 2.097, 2.097, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.104, 2.103, 2.102, 2.101,
+ 2.099, 2.098, 2.098, 2.098, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.094, 2.094, 2.094, 2.096, 2.097, 2.097, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.101, 2.101, 2.104, 2.105, 2.105, 2.103, 2.102,
+ 2.101, 2.099, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.098, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.103, 2.104, 2.105, 2.105, 2.104, 2.103,
+ 2.102, 2.102, 2.099, 2.098, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.104, 2.105, 2.106, 2.106, 2.106, 2.104, 2.104, 2.104,
+ 2.102, 2.101, 2.099, 2.099, 2.099, 2.101, 2.101, 2.101, 2.099, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.099, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.103, 2.105, 2.106, 2.106, 2.106, 2.106, 2.105, 2.104, 2.104,
+ 2.099, 2.099, 2.099, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.098, 2.097, 2.098, 2.098, 2.099, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.105, 2.105, 2.106, 2.106, 2.104, 2.104, 2.103,
+ 2.096, 2.097, 2.097, 2.097, 2.097, 2.099, 2.099, 2.099, 2.099, 2.097, 2.097, 2.098, 2.098, 2.099, 2.098, 2.097, 2.097, 2.099, 2.101, 2.101, 2.101, 2.101, 2.101, 2.103, 2.105, 2.105, 2.105, 2.104, 2.104, 2.103, 2.101, 2.101,
+ 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.098, 2.099, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.097, 2.097, 2.096, 2.098, 2.098, 2.099, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.104, 2.104, 2.103, 2.101, 2.099, 2.098,
+ 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.097, 2.098, 2.097, 2.097, 2.096, 2.096, 2.098, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.103, 2.104, 2.104, 2.102, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.096, 2.098, 2.098, 2.098, 2.098, 2.097, 2.098, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.099, 2.099, 2.099, 2.101, 2.102, 2.103, 2.104, 2.104, 2.104, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.097, 2.099, 2.099, 2.099, 2.099, 2.099, 2.099, 2.098, 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.098, 2.097, 2.099, 2.101, 2.099, 2.099, 2.099, 2.102, 2.102, 2.104, 2.105, 2.105, 2.102, 2.099, 2.098
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.431, 3.437, 3.439, 3.439, 3.436, 3.438, 3.441, 3.441, 3.441, 3.441, 3.442, 3.443, 3.443, 3.444, 3.446, 3.448, 3.451, 3.451, 3.452, 3.451, 3.449, 3.449, 3.452, 3.453, 3.454, 3.454, 3.453, 3.456, 3.456, 3.456, 3.451, 3.448,
+ 3.445, 3.446, 3.445, 3.449, 3.453, 3.451, 3.451, 3.446, 3.447, 3.446, 3.447, 3.451, 3.453, 3.455, 3.454, 3.453, 3.453, 3.454, 3.455, 3.456, 3.457, 3.459, 3.461, 3.462, 3.463, 3.463, 3.465, 3.466, 3.467, 3.465, 3.459, 3.457,
+ 3.449, 3.449, 3.449, 3.454, 3.455, 3.454, 3.453, 3.451, 3.451, 3.448, 3.451, 3.451, 3.455, 3.456, 3.457, 3.456, 3.456, 3.458, 3.457, 3.459, 3.459, 3.461, 3.464, 3.467, 3.467, 3.466, 3.468, 3.469, 3.471, 3.468, 3.465, 3.462,
+ 3.451, 3.448, 3.451, 3.453, 3.457, 3.455, 3.454, 3.449, 3.449, 3.448, 3.449, 3.449, 3.455, 3.455, 3.456, 3.455, 3.454, 3.455, 3.455, 3.457, 3.458, 3.458, 3.461, 3.464, 3.466, 3.468, 3.469, 3.469, 3.469, 3.468, 3.465, 3.463,
+ 3.449, 3.449, 3.451, 3.453, 3.456, 3.455, 3.452, 3.449, 3.448, 3.447, 3.446, 3.448, 3.451, 3.452, 3.454, 3.455, 3.455, 3.454, 3.457, 3.458, 3.458, 3.459, 3.461, 3.464, 3.464, 3.466, 3.467, 3.469, 3.469, 3.467, 3.463, 3.459,
+ 3.449, 3.451, 3.452, 3.454, 3.455, 3.454, 3.452, 3.449, 3.447, 3.447, 3.446, 3.449, 3.449, 3.451, 3.452, 3.452, 3.452, 3.452, 3.454, 3.455, 3.457, 3.459, 3.461, 3.464, 3.464, 3.466, 3.465, 3.468, 3.468, 3.469, 3.465, 3.462,
+ 3.451, 3.451, 3.452, 3.453, 3.453, 3.453, 3.451, 3.449, 3.449, 3.447, 3.446, 3.447, 3.448, 3.451, 3.451, 3.451, 3.453, 3.452, 3.452, 3.452, 3.457, 3.458, 3.461, 3.463, 3.464, 3.465, 3.464, 3.466, 3.468, 3.469, 3.466, 3.463,
+ 3.451, 3.451, 3.451, 3.454, 3.453, 3.453, 3.451, 3.448, 3.448, 3.444, 3.444, 3.444, 3.448, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.452, 3.454, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.467, 3.468, 3.469, 3.466, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.454, 3.453, 3.449, 3.448, 3.447, 3.447, 3.444, 3.446, 3.446, 3.446, 3.446, 3.447, 3.449, 3.449, 3.451, 3.452, 3.455, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.468, 3.469, 3.468, 3.465, 3.462,
+ 3.453, 3.452, 3.454, 3.456, 3.455, 3.453, 3.449, 3.447, 3.446, 3.446, 3.445, 3.448, 3.447, 3.446, 3.445, 3.446, 3.448, 3.448, 3.449, 3.453, 3.455, 3.457, 3.459, 3.461, 3.464, 3.466, 3.467, 3.468, 3.468, 3.467, 3.465, 3.463,
+ 3.453, 3.453, 3.454, 3.456, 3.456, 3.451, 3.448, 3.447, 3.447, 3.446, 3.445, 3.446, 3.446, 3.446, 3.446, 3.446, 3.448, 3.448, 3.449, 3.452, 3.454, 3.456, 3.459, 3.459, 3.461, 3.465, 3.466, 3.468, 3.468, 3.468, 3.467, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.456, 3.452, 3.448, 3.446, 3.446, 3.444, 3.446, 3.445, 3.446, 3.446, 3.447, 3.448, 3.449, 3.449, 3.449, 3.452, 3.453, 3.454, 3.458, 3.458, 3.461, 3.461, 3.464, 3.469, 3.469, 3.468, 3.466, 3.466,
+ 3.452, 3.452, 3.453, 3.454, 3.454, 3.453, 3.447, 3.446, 3.444, 3.444, 3.444, 3.444, 3.445, 3.446, 3.448, 3.451, 3.452, 3.453, 3.451, 3.453, 3.453, 3.455, 3.458, 3.459, 3.461, 3.462, 3.463, 3.468, 3.471, 3.469, 3.467, 3.467,
+ 3.454, 3.455, 3.457, 3.458, 3.458, 3.455, 3.449, 3.446, 3.445, 3.445, 3.445, 3.445, 3.447, 3.447, 3.448, 3.451, 3.452, 3.453, 3.452, 3.452, 3.452, 3.454, 3.457, 3.459, 3.459, 3.462, 3.464, 3.468, 3.469, 3.467, 3.465, 3.465,
+ 3.457, 3.455, 3.455, 3.459, 3.458, 3.454, 3.451, 3.448, 3.445, 3.445, 3.445, 3.446, 3.448, 3.449, 3.451, 3.452, 3.451, 3.453, 3.452, 3.452, 3.453, 3.457, 3.457, 3.461, 3.461, 3.463, 3.465, 3.468, 3.471, 3.468, 3.465, 3.463,
+ 3.458, 3.456, 3.456, 3.459, 3.457, 3.454, 3.452, 3.449, 3.447, 3.445, 3.446, 3.447, 3.447, 3.448, 3.449, 3.448, 3.449, 3.451, 3.451, 3.451, 3.451, 3.455, 3.456, 3.458, 3.462, 3.463, 3.464, 3.465, 3.467, 3.466, 3.464, 3.462,
+ 3.457, 3.456, 3.455, 3.457, 3.457, 3.454, 3.449, 3.447, 3.445, 3.445, 3.446, 3.446, 3.448, 3.446, 3.448, 3.449, 3.449, 3.451, 3.451, 3.451, 3.453, 3.455, 3.457, 3.459, 3.462, 3.464, 3.464, 3.465, 3.467, 3.464, 3.464, 3.463,
+ 3.458, 3.457, 3.455, 3.456, 3.456, 3.456, 3.453, 3.449, 3.447, 3.448, 3.447, 3.447, 3.447, 3.447, 3.447, 3.448, 3.449, 3.451, 3.451, 3.452, 3.453, 3.455, 3.458, 3.459, 3.459, 3.463, 3.464, 3.463, 3.464, 3.463, 3.464, 3.464,
+ 3.457, 3.456, 3.456, 3.456, 3.456, 3.456, 3.455, 3.449, 3.447, 3.448, 3.451, 3.449, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.451, 3.452, 3.453, 3.456, 3.458, 3.459, 3.461, 3.462, 3.464, 3.464, 3.465, 3.464, 3.464, 3.463,
+ 3.457, 3.456, 3.455, 3.455, 3.455, 3.455, 3.453, 3.451, 3.449, 3.448, 3.448, 3.449, 3.449, 3.449, 3.448, 3.449, 3.451, 3.452, 3.452, 3.453, 3.454, 3.457, 3.458, 3.459, 3.462, 3.464, 3.465, 3.464, 3.465, 3.464, 3.463, 3.463,
+ 3.456, 3.456, 3.454, 3.453, 3.454, 3.453, 3.452, 3.451, 3.449, 3.448, 3.448, 3.449, 3.451, 3.451, 3.448, 3.449, 3.451, 3.454, 3.454, 3.454, 3.455, 3.457, 3.458, 3.461, 3.461, 3.462, 3.464, 3.464, 3.466, 3.465, 3.464, 3.464,
+ 3.459, 3.457, 3.456, 3.455, 3.454, 3.453, 3.453, 3.452, 3.452, 3.451, 3.449, 3.449, 3.449, 3.448, 3.447, 3.449, 3.451, 3.454, 3.455, 3.455, 3.456, 3.458, 3.459, 3.461, 3.461, 3.462, 3.463, 3.466, 3.469, 3.465, 3.465, 3.464,
+ 3.463, 3.461, 3.458, 3.458, 3.457, 3.456, 3.456, 3.454, 3.454, 3.452, 3.452, 3.451, 3.451, 3.449, 3.448, 3.448, 3.452, 3.454, 3.456, 3.455, 3.457, 3.458, 3.461, 3.464, 3.462, 3.461, 3.463, 3.466, 3.469, 3.469, 3.467, 3.467,
+ 3.466, 3.462, 3.461, 3.461, 3.459, 3.457, 3.457, 3.457, 3.456, 3.454, 3.455, 3.455, 3.455, 3.451, 3.452, 3.453, 3.454, 3.455, 3.456, 3.456, 3.459, 3.462, 3.463, 3.466, 3.466, 3.467, 3.466, 3.469, 3.471, 3.469, 3.468, 3.466,
+ 3.467, 3.463, 3.463, 3.459, 3.461, 3.459, 3.461, 3.459, 3.458, 3.456, 3.457, 3.456, 3.457, 3.455, 3.456, 3.455, 3.456, 3.457, 3.459, 3.461, 3.461, 3.464, 3.465, 3.468, 3.469, 3.469, 3.469, 3.469, 3.471, 3.468, 3.467, 3.468,
+ 3.467, 3.464, 3.459, 3.459, 3.462, 3.462, 3.462, 3.461, 3.461, 3.462, 3.461, 3.459, 3.461, 3.459, 3.458, 3.457, 3.459, 3.461, 3.462, 3.463, 3.464, 3.466, 3.468, 3.469, 3.471, 3.469, 3.471, 3.472, 3.471, 3.467, 3.466, 3.464,
+ 3.464, 3.462, 3.458, 3.457, 3.458, 3.461, 3.461, 3.461, 3.461, 3.462, 3.462, 3.461, 3.461, 3.459, 3.459, 3.459, 3.461, 3.461, 3.464, 3.465, 3.465, 3.468, 3.468, 3.469, 3.471, 3.469, 3.469, 3.469, 3.469, 3.464, 3.462, 3.459,
+ 3.457, 3.458, 3.455, 3.456, 3.456, 3.457, 3.459, 3.459, 3.459, 3.459, 3.458, 3.456, 3.458, 3.457, 3.458, 3.458, 3.458, 3.459, 3.461, 3.463, 3.465, 3.466, 3.468, 3.469, 3.471, 3.468, 3.466, 3.466, 3.465, 3.461, 3.459, 3.457,
+ 3.456, 3.455, 3.454, 3.454, 3.455, 3.456, 3.458, 3.459, 3.459, 3.456, 3.456, 3.456, 3.455, 3.456, 3.455, 3.455, 3.455, 3.454, 3.457, 3.461, 3.462, 3.464, 3.465, 3.467, 3.467, 3.466, 3.464, 3.464, 3.463, 3.461, 3.457, 3.456,
+ 3.456, 3.454, 3.453, 3.454, 3.454, 3.455, 3.458, 3.459, 3.459, 3.456, 3.455, 3.455, 3.455, 3.451, 3.453, 3.454, 3.454, 3.455, 3.455, 3.458, 3.461, 3.462, 3.461, 3.463, 3.465, 3.464, 3.463, 3.463, 3.462, 3.459, 3.456, 3.451,
+ 3.455, 3.452, 3.452, 3.452, 3.455, 3.457, 3.459, 3.459, 3.459, 3.458, 3.456, 3.456, 3.455, 3.453, 3.453, 3.455, 3.457, 3.457, 3.457, 3.461, 3.461, 3.461, 3.459, 3.462, 3.464, 3.464, 3.464, 3.463, 3.463, 3.459, 3.454, 3.451,
+ 3.452, 3.452, 3.452, 3.453, 3.457, 3.458, 3.458, 3.459, 3.459, 3.458, 3.457, 3.457, 3.455, 3.455, 3.458, 3.459, 3.458, 3.459, 3.459, 3.461, 3.461, 3.461, 3.459, 3.461, 3.463, 3.464, 3.466, 3.463, 3.461, 3.458, 3.453, 3.449
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.403, 3.399, 3.395, 3.391, 3.392, 3.394, 3.401, 3.403, 3.404, 3.404, 3.403, 3.399, 3.398, 3.396, 3.395, 3.396, 3.399, 3.403, 3.404, 3.401, 3.399, 3.398, 3.397, 3.401, 3.401, 3.401, 3.396, 3.394, 3.397, 3.396, 3.388, 3.364,
+ 3.403, 3.399, 3.393, 3.389, 3.391, 3.395, 3.401, 3.404, 3.406, 3.404, 3.403, 3.399, 3.399, 3.397, 3.397, 3.397, 3.401, 3.404, 3.404, 3.402, 3.398, 3.396, 3.397, 3.401, 3.401, 3.401, 3.395, 3.394, 3.396, 3.393, 3.387, 3.364,
+ 3.399, 3.398, 3.391, 3.385, 3.386, 3.395, 3.402, 3.405, 3.405, 3.404, 3.402, 3.399, 3.399, 3.398, 3.398, 3.398, 3.401, 3.404, 3.405, 3.403, 3.399, 3.396, 3.396, 3.398, 3.401, 3.401, 3.398, 3.394, 3.392, 3.389, 3.386, 3.364,
+ 3.398, 3.393, 3.386, 3.382, 3.385, 3.392, 3.399, 3.403, 3.405, 3.404, 3.402, 3.398, 3.398, 3.397, 3.397, 3.398, 3.401, 3.404, 3.405, 3.403, 3.398, 3.394, 3.394, 3.398, 3.401, 3.401, 3.396, 3.392, 3.391, 3.388, 3.383, 3.362,
+ 3.396, 3.391, 3.384, 3.381, 3.384, 3.389, 3.398, 3.402, 3.402, 3.401, 3.399, 3.395, 3.395, 3.395, 3.397, 3.397, 3.401, 3.402, 3.404, 3.403, 3.399, 3.394, 3.393, 3.395, 3.399, 3.399, 3.397, 3.391, 3.388, 3.384, 3.381, 3.363,
+ 3.391, 3.386, 3.382, 3.381, 3.385, 3.389, 3.396, 3.398, 3.399, 3.399, 3.398, 3.395, 3.394, 3.394, 3.395, 3.397, 3.399, 3.401, 3.403, 3.401, 3.398, 3.394, 3.393, 3.393, 3.394, 3.396, 3.395, 3.392, 3.387, 3.382, 3.378, 3.361,
+ 3.389, 3.386, 3.379, 3.379, 3.383, 3.388, 3.394, 3.397, 3.397, 3.397, 3.395, 3.393, 3.393, 3.393, 3.395, 3.395, 3.397, 3.398, 3.401, 3.399, 3.397, 3.395, 3.394, 3.391, 3.393, 3.393, 3.393, 3.389, 3.387, 3.381, 3.374, 3.357,
+ 3.386, 3.383, 3.376, 3.375, 3.381, 3.386, 3.394, 3.396, 3.396, 3.394, 3.392, 3.392, 3.394, 3.394, 3.395, 3.394, 3.396, 3.398, 3.399, 3.397, 3.397, 3.394, 3.393, 3.391, 3.389, 3.391, 3.392, 3.388, 3.386, 3.379, 3.372, 3.355,
+ 3.386, 3.379, 3.373, 3.373, 3.378, 3.384, 3.391, 3.396, 3.395, 3.393, 3.389, 3.391, 3.391, 3.393, 3.394, 3.393, 3.394, 3.396, 3.397, 3.396, 3.393, 3.394, 3.393, 3.392, 3.389, 3.389, 3.389, 3.389, 3.386, 3.378, 3.371, 3.351,
+ 3.379, 3.375, 3.371, 3.371, 3.376, 3.381, 3.388, 3.393, 3.394, 3.391, 3.386, 3.386, 3.388, 3.393, 3.392, 3.392, 3.393, 3.395, 3.394, 3.392, 3.389, 3.391, 3.391, 3.392, 3.389, 3.388, 3.389, 3.389, 3.383, 3.377, 3.369, 3.351,
+ 3.373, 3.371, 3.367, 3.368, 3.373, 3.381, 3.387, 3.389, 3.391, 3.389, 3.385, 3.386, 3.383, 3.389, 3.389, 3.392, 3.392, 3.394, 3.393, 3.389, 3.387, 3.387, 3.388, 3.389, 3.389, 3.388, 3.386, 3.386, 3.382, 3.374, 3.367, 3.345,
+ 3.371, 3.369, 3.365, 3.366, 3.373, 3.379, 3.386, 3.389, 3.391, 3.389, 3.385, 3.384, 3.382, 3.386, 3.387, 3.389, 3.391, 3.392, 3.391, 3.387, 3.385, 3.385, 3.386, 3.388, 3.388, 3.388, 3.386, 3.385, 3.381, 3.373, 3.367, 3.345,
+ 3.367, 3.365, 3.365, 3.366, 3.374, 3.379, 3.384, 3.388, 3.389, 3.387, 3.384, 3.383, 3.383, 3.385, 3.385, 3.386, 3.388, 3.389, 3.388, 3.386, 3.383, 3.382, 3.384, 3.386, 3.387, 3.386, 3.381, 3.381, 3.379, 3.372, 3.364, 3.344,
+ 3.365, 3.363, 3.362, 3.367, 3.375, 3.379, 3.383, 3.384, 3.386, 3.384, 3.381, 3.379, 3.379, 3.383, 3.383, 3.384, 3.385, 3.387, 3.387, 3.385, 3.381, 3.381, 3.382, 3.384, 3.384, 3.385, 3.382, 3.379, 3.374, 3.369, 3.359, 3.343,
+ 3.359, 3.358, 3.361, 3.364, 3.373, 3.381, 3.384, 3.384, 3.385, 3.384, 3.381, 3.377, 3.379, 3.379, 3.382, 3.383, 3.384, 3.386, 3.386, 3.385, 3.381, 3.379, 3.381, 3.382, 3.382, 3.383, 3.379, 3.377, 3.371, 3.364, 3.357, 3.339,
+ 3.357, 3.356, 3.356, 3.362, 3.372, 3.379, 3.384, 3.384, 3.383, 3.381, 3.378, 3.376, 3.377, 3.379, 3.381, 3.382, 3.383, 3.385, 3.385, 3.383, 3.379, 3.379, 3.379, 3.381, 3.381, 3.382, 3.379, 3.372, 3.367, 3.362, 3.354, 3.334,
+ 3.357, 3.354, 3.357, 3.361, 3.372, 3.381, 3.385, 3.385, 3.384, 3.379, 3.376, 3.376, 3.376, 3.379, 3.381, 3.383, 3.383, 3.384, 3.383, 3.379, 3.378, 3.381, 3.379, 3.379, 3.379, 3.379, 3.378, 3.371, 3.363, 3.358, 3.354, 3.332,
+ 3.354, 3.351, 3.354, 3.359, 3.371, 3.379, 3.382, 3.384, 3.381, 3.378, 3.375, 3.374, 3.376, 3.378, 3.381, 3.383, 3.384, 3.382, 3.377, 3.377, 3.376, 3.377, 3.378, 3.378, 3.379, 3.379, 3.376, 3.367, 3.361, 3.357, 3.352, 3.333,
+ 3.352, 3.349, 3.351, 3.357, 3.372, 3.381, 3.383, 3.383, 3.381, 3.376, 3.372, 3.373, 3.375, 3.377, 3.382, 3.384, 3.384, 3.379, 3.376, 3.374, 3.374, 3.375, 3.375, 3.376, 3.377, 3.376, 3.373, 3.366, 3.361, 3.356, 3.347, 3.332,
+ 3.347, 3.346, 3.346, 3.355, 3.371, 3.377, 3.382, 3.381, 3.379, 3.372, 3.371, 3.371, 3.372, 3.375, 3.379, 3.383, 3.384, 3.379, 3.374, 3.373, 3.371, 3.373, 3.374, 3.375, 3.374, 3.374, 3.371, 3.365, 3.359, 3.352, 3.343, 3.331,
+ 3.345, 3.344, 3.345, 3.353, 3.367, 3.374, 3.382, 3.382, 3.376, 3.371, 3.369, 3.368, 3.369, 3.373, 3.377, 3.381, 3.379, 3.376, 3.373, 3.369, 3.368, 3.371, 3.372, 3.373, 3.371, 3.371, 3.369, 3.363, 3.357, 3.349, 3.341, 3.326,
+ 3.343, 3.341, 3.344, 3.351, 3.362, 3.371, 3.376, 3.376, 3.372, 3.369, 3.367, 3.366, 3.367, 3.369, 3.376, 3.378, 3.378, 3.375, 3.371, 3.367, 3.367, 3.368, 3.369, 3.369, 3.369, 3.368, 3.365, 3.361, 3.354, 3.347, 3.338, 3.321,
+ 3.341, 3.339, 3.342, 3.349, 3.359, 3.367, 3.371, 3.372, 3.371, 3.368, 3.366, 3.363, 3.365, 3.368, 3.371, 3.374, 3.376, 3.374, 3.368, 3.365, 3.365, 3.366, 3.368, 3.367, 3.367, 3.363, 3.361, 3.356, 3.352, 3.346, 3.336, 3.317,
+ 3.338, 3.336, 3.338, 3.346, 3.359, 3.364, 3.368, 3.369, 3.367, 3.366, 3.363, 3.362, 3.364, 3.364, 3.367, 3.371, 3.372, 3.369, 3.365, 3.362, 3.362, 3.365, 3.367, 3.367, 3.366, 3.362, 3.357, 3.353, 3.349, 3.342, 3.335, 3.317,
+ 3.334, 3.334, 3.336, 3.346, 3.354, 3.361, 3.365, 3.365, 3.365, 3.362, 3.361, 3.361, 3.362, 3.362, 3.364, 3.366, 3.368, 3.366, 3.361, 3.357, 3.357, 3.359, 3.363, 3.365, 3.363, 3.361, 3.355, 3.351, 3.346, 3.339, 3.336, 3.317,
+ 3.332, 3.332, 3.334, 3.344, 3.354, 3.359, 3.363, 3.365, 3.363, 3.361, 3.359, 3.359, 3.363, 3.363, 3.365, 3.365, 3.367, 3.366, 3.358, 3.356, 3.356, 3.358, 3.362, 3.364, 3.363, 3.359, 3.353, 3.348, 3.345, 3.339, 3.336, 3.315,
+ 3.332, 3.328, 3.331, 3.343, 3.351, 3.357, 3.358, 3.362, 3.361, 3.359, 3.357, 3.357, 3.361, 3.362, 3.364, 3.363, 3.363, 3.359, 3.356, 3.354, 3.354, 3.355, 3.358, 3.359, 3.361, 3.359, 3.351, 3.346, 3.344, 3.339, 3.336, 3.313,
+ 3.324, 3.324, 3.327, 3.334, 3.345, 3.351, 3.354, 3.356, 3.356, 3.354, 3.353, 3.354, 3.357, 3.358, 3.361, 3.358, 3.359, 3.355, 3.352, 3.348, 3.347, 3.351, 3.354, 3.358, 3.359, 3.355, 3.346, 3.343, 3.341, 3.336, 3.331, 3.312,
+ 3.318, 3.319, 3.321, 3.328, 3.337, 3.339, 3.345, 3.348, 3.346, 3.345, 3.347, 3.348, 3.351, 3.354, 3.356, 3.353, 3.354, 3.344, 3.343, 3.343, 3.343, 3.344, 3.347, 3.349, 3.353, 3.346, 3.341, 3.339, 3.331, 3.329, 3.325, 3.311,
+ 3.309, 3.313, 3.317, 3.325, 3.329, 3.332, 3.338, 3.339, 3.341, 3.339, 3.339, 3.342, 3.346, 3.346, 3.351, 3.351, 3.343, 3.338, 3.338, 3.339, 3.339, 3.339, 3.341, 3.341, 3.346, 3.343, 3.339, 3.332, 3.327, 3.326, 3.322, 3.309,
+ 3.305, 3.309, 3.317, 3.325, 3.328, 3.331, 3.334, 3.336, 3.337, 3.336, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.341, 3.336, 3.335, 3.337, 3.339, 3.341, 3.339, 3.339, 3.342, 3.341, 3.337, 3.329, 3.326, 3.325, 3.321, 3.314,
+ 3.302, 3.306, 3.319, 3.325, 3.329, 3.331, 3.334, 3.335, 3.337, 3.337, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.342, 3.336, 3.336, 3.338, 3.339, 3.341, 3.341, 3.341, 3.339, 3.338, 3.336, 3.331, 3.327, 3.324, 3.321, 3.314
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.726, 1.725, 1.723, 1.721, 1.723, 1.724, 1.724, 1.726, 1.727, 1.728, 1.729, 1.728, 1.725, 1.724, 1.726, 1.726, 1.727, 1.729, 1.727, 1.727, 1.724, 1.725, 1.724, 1.726, 1.725, 1.725, 1.724, 1.724, 1.722, 1.721, 1.719, 1.714,
+ 1.726, 1.724, 1.722, 1.721, 1.722, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.727, 1.725, 1.724, 1.724, 1.725, 1.726, 1.725, 1.724, 1.723, 1.722, 1.721, 1.719, 1.714,
+ 1.724, 1.722, 1.719, 1.719, 1.721, 1.723, 1.726, 1.726, 1.727, 1.727, 1.727, 1.725, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.725, 1.724, 1.724, 1.724, 1.726, 1.725, 1.724, 1.722, 1.722, 1.721, 1.719, 1.712,
+ 1.723, 1.721, 1.719, 1.719, 1.719, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.726, 1.728, 1.729, 1.728, 1.725, 1.723, 1.723, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.718, 1.711,
+ 1.722, 1.719, 1.719, 1.718, 1.719, 1.722, 1.725, 1.726, 1.726, 1.727, 1.727, 1.726, 1.725, 1.726, 1.726, 1.726, 1.727, 1.727, 1.728, 1.727, 1.726, 1.725, 1.724, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.715, 1.711,
+ 1.721, 1.717, 1.717, 1.716, 1.719, 1.722, 1.724, 1.726, 1.726, 1.727, 1.726, 1.726, 1.726, 1.726, 1.726, 1.727, 1.727, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.722, 1.721, 1.718, 1.715, 1.707,
+ 1.718, 1.717, 1.716, 1.716, 1.718, 1.721, 1.725, 1.726, 1.726, 1.726, 1.725, 1.725, 1.725, 1.725, 1.726, 1.727, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.718, 1.715, 1.709,
+ 1.718, 1.716, 1.716, 1.715, 1.717, 1.721, 1.724, 1.725, 1.726, 1.725, 1.725, 1.724, 1.724, 1.725, 1.726, 1.726, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.723, 1.723, 1.723, 1.722, 1.722, 1.719, 1.718, 1.714, 1.709,
+ 1.718, 1.716, 1.715, 1.715, 1.717, 1.721, 1.723, 1.725, 1.726, 1.725, 1.724, 1.723, 1.724, 1.725, 1.725, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.723, 1.722, 1.722, 1.721, 1.717, 1.714, 1.707,
+ 1.717, 1.716, 1.714, 1.714, 1.716, 1.721, 1.723, 1.725, 1.725, 1.725, 1.723, 1.723, 1.724, 1.726, 1.726, 1.726, 1.726, 1.725, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.706,
+ 1.715, 1.714, 1.714, 1.714, 1.716, 1.719, 1.722, 1.724, 1.725, 1.725, 1.723, 1.723, 1.724, 1.725, 1.725, 1.725, 1.726, 1.725, 1.725, 1.725, 1.724, 1.724, 1.724, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.713, 1.705,
+ 1.714, 1.714, 1.713, 1.714, 1.717, 1.719, 1.722, 1.724, 1.724, 1.724, 1.723, 1.722, 1.723, 1.724, 1.724, 1.724, 1.726, 1.725, 1.726, 1.725, 1.723, 1.723, 1.724, 1.724, 1.724, 1.723, 1.721, 1.719, 1.717, 1.715, 1.713, 1.706,
+ 1.712, 1.712, 1.712, 1.713, 1.718, 1.719, 1.721, 1.723, 1.724, 1.724, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.725, 1.725, 1.725, 1.725, 1.723, 1.722, 1.724, 1.723, 1.723, 1.722, 1.721, 1.719, 1.717, 1.714, 1.711, 1.706,
+ 1.712, 1.711, 1.711, 1.713, 1.717, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.724, 1.725, 1.725, 1.724, 1.723, 1.722, 1.722, 1.722, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.711, 1.706,
+ 1.711, 1.709, 1.711, 1.713, 1.716, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.721, 1.722, 1.724, 1.724, 1.724, 1.723, 1.724, 1.724, 1.724, 1.722, 1.722, 1.722, 1.722, 1.722, 1.721, 1.719, 1.718, 1.714, 1.712, 1.709, 1.702,
+ 1.709, 1.709, 1.709, 1.712, 1.717, 1.719, 1.721, 1.723, 1.723, 1.723, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.724, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.716, 1.713, 1.711, 1.709, 1.701,
+ 1.708, 1.707, 1.709, 1.712, 1.716, 1.719, 1.722, 1.723, 1.723, 1.723, 1.721, 1.721, 1.721, 1.722, 1.723, 1.723, 1.723, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.714, 1.712, 1.709, 1.708, 1.702,
+ 1.707, 1.707, 1.708, 1.711, 1.716, 1.721, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.722, 1.722, 1.723, 1.723, 1.723, 1.722, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.721, 1.717, 1.714, 1.711, 1.709, 1.707, 1.702,
+ 1.706, 1.706, 1.707, 1.711, 1.714, 1.719, 1.722, 1.722, 1.722, 1.721, 1.719, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.719, 1.719, 1.721, 1.721, 1.719, 1.719, 1.716, 1.713, 1.711, 1.709, 1.706, 1.701,
+ 1.705, 1.704, 1.706, 1.709, 1.713, 1.718, 1.721, 1.722, 1.721, 1.719, 1.718, 1.719, 1.721, 1.722, 1.723, 1.724, 1.724, 1.721, 1.721, 1.721, 1.719, 1.719, 1.719, 1.719, 1.719, 1.717, 1.715, 1.713, 1.711, 1.707, 1.704, 1.699,
+ 1.703, 1.703, 1.704, 1.709, 1.712, 1.717, 1.719, 1.721, 1.719, 1.718, 1.717, 1.718, 1.719, 1.721, 1.722, 1.723, 1.723, 1.722, 1.719, 1.719, 1.718, 1.719, 1.719, 1.718, 1.717, 1.716, 1.714, 1.712, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.703, 1.704, 1.708, 1.712, 1.715, 1.718, 1.719, 1.719, 1.717, 1.717, 1.717, 1.717, 1.718, 1.721, 1.722, 1.722, 1.721, 1.719, 1.718, 1.717, 1.718, 1.718, 1.717, 1.716, 1.714, 1.714, 1.711, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.702, 1.703, 1.706, 1.709, 1.715, 1.717, 1.718, 1.717, 1.717, 1.716, 1.716, 1.717, 1.717, 1.719, 1.721, 1.721, 1.721, 1.719, 1.717, 1.716, 1.717, 1.717, 1.716, 1.714, 1.713, 1.712, 1.711, 1.708, 1.706, 1.702, 1.696,
+ 1.701, 1.701, 1.702, 1.706, 1.709, 1.714, 1.716, 1.717, 1.716, 1.716, 1.716, 1.715, 1.716, 1.716, 1.717, 1.718, 1.719, 1.719, 1.716, 1.715, 1.715, 1.715, 1.715, 1.715, 1.714, 1.713, 1.711, 1.709, 1.708, 1.704, 1.701, 1.695,
+ 1.699, 1.699, 1.702, 1.706, 1.708, 1.712, 1.714, 1.715, 1.715, 1.715, 1.714, 1.715, 1.714, 1.715, 1.716, 1.716, 1.716, 1.716, 1.714, 1.713, 1.713, 1.714, 1.715, 1.714, 1.714, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.695,
+ 1.698, 1.699, 1.701, 1.705, 1.708, 1.711, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.715, 1.715, 1.716, 1.716, 1.715, 1.713, 1.713, 1.713, 1.714, 1.714, 1.714, 1.713, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.696,
+ 1.698, 1.699, 1.701, 1.705, 1.707, 1.711, 1.712, 1.713, 1.713, 1.713, 1.713, 1.714, 1.714, 1.715, 1.715, 1.716, 1.715, 1.714, 1.713, 1.712, 1.712, 1.712, 1.713, 1.713, 1.713, 1.711, 1.709, 1.707, 1.705, 1.703, 1.701, 1.696,
+ 1.698, 1.697, 1.699, 1.702, 1.705, 1.707, 1.711, 1.711, 1.711, 1.711, 1.711, 1.712, 1.712, 1.713, 1.714, 1.714, 1.713, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.708, 1.706, 1.704, 1.703, 1.699, 1.696,
+ 1.694, 1.695, 1.697, 1.699, 1.702, 1.705, 1.706, 1.707, 1.707, 1.708, 1.708, 1.708, 1.709, 1.711, 1.711, 1.711, 1.708, 1.708, 1.708, 1.707, 1.707, 1.707, 1.708, 1.708, 1.709, 1.708, 1.706, 1.703, 1.702, 1.701, 1.698, 1.696,
+ 1.692, 1.692, 1.695, 1.698, 1.699, 1.701, 1.704, 1.704, 1.704, 1.704, 1.705, 1.706, 1.707, 1.709, 1.709, 1.707, 1.706, 1.704, 1.704, 1.705, 1.705, 1.706, 1.706, 1.706, 1.706, 1.706, 1.703, 1.702, 1.701, 1.699, 1.696, 1.694,
+ 1.691, 1.692, 1.695, 1.697, 1.699, 1.699, 1.702, 1.703, 1.703, 1.702, 1.703, 1.704, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.704, 1.705, 1.705, 1.705, 1.705, 1.704, 1.703, 1.701, 1.699, 1.698, 1.696, 1.695,
+ 1.689, 1.691, 1.696, 1.698, 1.699, 1.699, 1.701, 1.702, 1.702, 1.702, 1.703, 1.703, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.703, 1.704, 1.704, 1.705, 1.704, 1.704, 1.702, 1.701, 1.698, 1.698, 1.696, 1.696
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.425, 1.393, 1.341, 1.295, 1.258, 1.226, 1.201, 1.181, 1.162, 1.146, 1.133, 1.123, 1.115, 1.111, 1.107, 1.106, 1.106, 1.107, 1.108, 1.111, 1.114, 1.122, 1.133, 1.148, 1.164, 1.184, 1.208, 1.236, 1.271, 1.309, 1.359, 1.381,
+ 1.397, 1.367, 1.317, 1.274, 1.237, 1.207, 1.183, 1.163, 1.146, 1.133, 1.123, 1.114, 1.107, 1.101, 1.098, 1.096, 1.096, 1.096, 1.097, 1.102, 1.106, 1.112, 1.122, 1.133, 1.148, 1.166, 1.187, 1.215, 1.249, 1.288, 1.335, 1.359,
+ 1.374, 1.341, 1.292, 1.251, 1.215, 1.186, 1.166, 1.146, 1.131, 1.117, 1.108, 1.099, 1.091, 1.088, 1.084, 1.082, 1.081, 1.082, 1.084, 1.088, 1.093, 1.098, 1.107, 1.118, 1.133, 1.149, 1.169, 1.195, 1.228, 1.267, 1.313, 1.335,
+ 1.352, 1.318, 1.271, 1.231, 1.196, 1.169, 1.149, 1.131, 1.115, 1.103, 1.093, 1.086, 1.079, 1.074, 1.071, 1.069, 1.069, 1.069, 1.071, 1.076, 1.079, 1.085, 1.094, 1.102, 1.117, 1.133, 1.152, 1.176, 1.208, 1.246, 1.289, 1.313,
+ 1.333, 1.298, 1.253, 1.212, 1.179, 1.153, 1.134, 1.116, 1.102, 1.089, 1.079, 1.072, 1.066, 1.062, 1.059, 1.058, 1.057, 1.057, 1.059, 1.064, 1.068, 1.072, 1.081, 1.091, 1.102, 1.119, 1.137, 1.161, 1.191, 1.227, 1.271, 1.293,
+ 1.317, 1.281, 1.235, 1.196, 1.165, 1.139, 1.119, 1.104, 1.089, 1.078, 1.068, 1.062, 1.055, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.053, 1.056, 1.061, 1.069, 1.079, 1.091, 1.105, 1.126, 1.147, 1.177, 1.212, 1.253, 1.278,
+ 1.301, 1.265, 1.221, 1.181, 1.151, 1.127, 1.108, 1.091, 1.078, 1.068, 1.059, 1.051, 1.045, 1.041, 1.038, 1.037, 1.036, 1.037, 1.038, 1.042, 1.046, 1.051, 1.059, 1.069, 1.081, 1.096, 1.113, 1.135, 1.164, 1.198, 1.238, 1.264,
+ 1.286, 1.251, 1.207, 1.169, 1.141, 1.116, 1.098, 1.081, 1.068, 1.058, 1.049, 1.042, 1.037, 1.033, 1.031, 1.029, 1.028, 1.028, 1.029, 1.033, 1.037, 1.043, 1.051, 1.059, 1.071, 1.086, 1.104, 1.124, 1.152, 1.185, 1.225, 1.252,
+ 1.275, 1.239, 1.196, 1.161, 1.132, 1.107, 1.089, 1.073, 1.059, 1.049, 1.041, 1.035, 1.028, 1.024, 1.023, 1.021, 1.021, 1.021, 1.022, 1.024, 1.029, 1.036, 1.043, 1.051, 1.063, 1.078, 1.095, 1.115, 1.143, 1.175, 1.214, 1.243,
+ 1.267, 1.227, 1.187, 1.152, 1.122, 1.101, 1.081, 1.067, 1.054, 1.042, 1.035, 1.028, 1.023, 1.018, 1.015, 1.014, 1.014, 1.014, 1.016, 1.019, 1.024, 1.029, 1.036, 1.045, 1.056, 1.071, 1.088, 1.107, 1.134, 1.167, 1.204, 1.234,
+ 1.261, 1.219, 1.179, 1.145, 1.116, 1.095, 1.076, 1.061, 1.047, 1.037, 1.031, 1.023, 1.018, 1.014, 1.011, 1.009, 1.009, 1.009, 1.011, 1.013, 1.018, 1.024, 1.031, 1.039, 1.049, 1.065, 1.083, 1.102, 1.128, 1.161, 1.196, 1.228,
+ 1.256, 1.213, 1.173, 1.139, 1.111, 1.091, 1.071, 1.056, 1.043, 1.033, 1.026, 1.019, 1.014, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.009, 1.013, 1.018, 1.026, 1.035, 1.046, 1.061, 1.078, 1.097, 1.123, 1.154, 1.191, 1.222,
+ 1.251, 1.208, 1.169, 1.137, 1.108, 1.088, 1.069, 1.053, 1.039, 1.029, 1.023, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.002, 1.003, 1.006, 1.009, 1.015, 1.022, 1.032, 1.044, 1.057, 1.076, 1.094, 1.119, 1.149, 1.186, 1.218,
+ 1.249, 1.205, 1.167, 1.133, 1.107, 1.085, 1.067, 1.052, 1.038, 1.029, 1.021, 1.013, 1.008, 1.004, 1.003, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.013, 1.021, 1.031, 1.042, 1.055, 1.073, 1.093, 1.116, 1.147, 1.182, 1.218,
+ 1.249, 1.204, 1.165, 1.132, 1.106, 1.085, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.007, 1.003, 1.002, 1.001, 1.001, 1.001, 1.001, 1.004, 1.007, 1.013, 1.021, 1.029, 1.042, 1.055, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.249, 1.204, 1.165, 1.132, 1.107, 1.086, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.008, 1.004, 1.002, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.014, 1.021, 1.029, 1.042, 1.056, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.251, 1.205, 1.166, 1.133, 1.108, 1.087, 1.068, 1.052, 1.039, 1.031, 1.021, 1.014, 1.009, 1.006, 1.003, 1.002, 1.001, 1.001, 1.003, 1.006, 1.009, 1.014, 1.022, 1.031, 1.043, 1.056, 1.073, 1.093, 1.116, 1.145, 1.182, 1.218,
+ 1.252, 1.208, 1.168, 1.137, 1.111, 1.089, 1.071, 1.055, 1.043, 1.033, 1.023, 1.016, 1.012, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.008, 1.012, 1.017, 1.024, 1.034, 1.045, 1.059, 1.075, 1.095, 1.119, 1.149, 1.185, 1.218,
+ 1.256, 1.213, 1.173, 1.142, 1.115, 1.093, 1.075, 1.059, 1.047, 1.036, 1.027, 1.021, 1.016, 1.012, 1.011, 1.009, 1.008, 1.008, 1.009, 1.012, 1.016, 1.021, 1.028, 1.038, 1.049, 1.064, 1.081, 1.099, 1.126, 1.155, 1.192, 1.223,
+ 1.261, 1.221, 1.179, 1.148, 1.121, 1.099, 1.081, 1.065, 1.052, 1.042, 1.032, 1.026, 1.021, 1.017, 1.015, 1.014, 1.014, 1.013, 1.013, 1.016, 1.021, 1.026, 1.033, 1.043, 1.054, 1.068, 1.085, 1.106, 1.132, 1.161, 1.199, 1.228,
+ 1.267, 1.228, 1.188, 1.155, 1.128, 1.105, 1.086, 1.071, 1.059, 1.047, 1.038, 1.031, 1.027, 1.022, 1.021, 1.019, 1.019, 1.019, 1.019, 1.022, 1.026, 1.032, 1.038, 1.049, 1.061, 1.075, 1.092, 1.112, 1.138, 1.169, 1.207, 1.236,
+ 1.278, 1.241, 1.199, 1.164, 1.137, 1.114, 1.094, 1.078, 1.066, 1.055, 1.046, 1.038, 1.032, 1.029, 1.027, 1.027, 1.027, 1.027, 1.027, 1.029, 1.032, 1.038, 1.047, 1.056, 1.067, 1.083, 1.099, 1.121, 1.146, 1.178, 1.217, 1.244,
+ 1.291, 1.252, 1.211, 1.175, 1.147, 1.124, 1.103, 1.088, 1.075, 1.063, 1.054, 1.046, 1.041, 1.036, 1.035, 1.035, 1.035, 1.035, 1.036, 1.038, 1.041, 1.047, 1.055, 1.065, 1.075, 1.092, 1.111, 1.132, 1.157, 1.189, 1.231, 1.255,
+ 1.303, 1.265, 1.222, 1.187, 1.158, 1.133, 1.112, 1.097, 1.083, 1.072, 1.063, 1.054, 1.048, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.046, 1.049, 1.055, 1.065, 1.074, 1.086, 1.102, 1.119, 1.144, 1.171, 1.203, 1.243, 1.268,
+ 1.317, 1.282, 1.236, 1.201, 1.171, 1.146, 1.125, 1.109, 1.095, 1.083, 1.072, 1.064, 1.058, 1.054, 1.052, 1.051, 1.051, 1.053, 1.054, 1.057, 1.061, 1.065, 1.074, 1.086, 1.099, 1.113, 1.133, 1.156, 1.183, 1.217, 1.259, 1.282,
+ 1.335, 1.301, 1.254, 1.218, 1.186, 1.159, 1.138, 1.121, 1.108, 1.095, 1.085, 1.076, 1.069, 1.066, 1.065, 1.063, 1.062, 1.063, 1.065, 1.068, 1.073, 1.078, 1.087, 1.098, 1.113, 1.126, 1.146, 1.171, 1.199, 1.235, 1.277, 1.299,
+ 1.356, 1.321, 1.274, 1.235, 1.202, 1.175, 1.153, 1.137, 1.121, 1.108, 1.097, 1.089, 1.084, 1.081, 1.077, 1.075, 1.075, 1.075, 1.077, 1.081, 1.086, 1.091, 1.099, 1.113, 1.126, 1.144, 1.162, 1.187, 1.218, 1.255, 1.297, 1.321,
+ 1.376, 1.344, 1.296, 1.257, 1.223, 1.194, 1.171, 1.153, 1.137, 1.124, 1.112, 1.104, 1.099, 1.095, 1.093, 1.091, 1.089, 1.091, 1.092, 1.095, 1.101, 1.108, 1.116, 1.128, 1.144, 1.161, 1.181, 1.206, 1.237, 1.275, 1.321, 1.347,
+ 1.403, 1.369, 1.319, 1.279, 1.244, 1.214, 1.191, 1.171, 1.154, 1.139, 1.129, 1.121, 1.115, 1.111, 1.109, 1.106, 1.105, 1.105, 1.108, 1.112, 1.117, 1.124, 1.135, 1.147, 1.162, 1.181, 1.203, 1.228, 1.262, 1.301, 1.347, 1.377,
+ 1.429, 1.398, 1.348, 1.306, 1.269, 1.237, 1.214, 1.191, 1.173, 1.158, 1.146, 1.138, 1.132, 1.128, 1.125, 1.123, 1.122, 1.123, 1.125, 1.129, 1.136, 1.142, 1.154, 1.166, 1.182, 1.203, 1.226, 1.253, 1.288, 1.329, 1.377, 1.406,
+ 1.465, 1.429, 1.377, 1.335, 1.295, 1.262, 1.236, 1.214, 1.194, 1.179, 1.167, 1.157, 1.151, 1.146, 1.144, 1.142, 1.142, 1.142, 1.144, 1.149, 1.154, 1.163, 1.174, 1.187, 1.205, 1.226, 1.251, 1.279, 1.315, 1.357, 1.406, 1.437,
+ 1.493, 1.465, 1.409, 1.364, 1.323, 1.289, 1.261, 1.235, 1.214, 1.194, 1.179, 1.171, 1.166, 1.163, 1.161, 1.161, 1.161, 1.161, 1.162, 1.164, 1.168, 1.175, 1.187, 1.205, 1.225, 1.251, 1.276, 1.306, 1.344, 1.387, 1.437, 1.455
+ ],
+ "sigma": 0.0007,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.95054, -0.57435, -0.37619,
+ -0.46945, 1.86661, -0.39716,
+ 0.07977, -1.14072, 2.06095
+ ]
+ },
+ {
+ "ct": 2800,
+ "ccm":
+ [
+ 1.94104, -0.60261, -0.33844,
+ -0.43162, 1.85422, -0.42261,
+ 0.03799, -0.95022, 1.91222
+ ]
+ },
+ {
+ "ct": 2900,
+ "ccm":
+ [
+ 1.91828, -0.59569, -0.32258,
+ -0.51902, 2.09091, -0.57189,
+ -0.03324, -0.73462, 1.76785
+ ]
+ },
+ {
+ "ct": 3620,
+ "ccm":
+ [
+ 1.97199, -0.66403, -0.30797,
+ -0.46411, 2.02612, -0.56201,
+ -0.07764, -0.61178, 1.68942
+ ]
+ },
+ {
+ "ct": 4560,
+ "ccm":
+ [
+ 2.15256, -0.84787, -0.30469,
+ -0.48422, 2.28962, -0.80541,
+ -0.15113, -0.53014, 1.68127
+ ]
+ },
+ {
+ "ct": 5600,
+ "ccm":
+ [
+ 2.04576, -0.74771, -0.29805,
+ -0.36332, 1.98993, -0.62662,
+ -0.09328, -0.46543, 1.55871
+ ]
+ },
+ {
+ "ct": 7400,
+ "ccm":
+ [
+ 2.37532, -0.83069, -0.54462,
+ -0.48279, 2.84309, -1.36031,
+ -0.21178, -0.66532, 1.87709
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.06,
+ "strength": 0.5,
+ "limit": 0.5
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx296_16mm.json b/src/ipa/rpi/pisp/data/imx296_16mm.json
new file mode 100644
index 00000000..87443745
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx296_16mm.json
@@ -0,0 +1,1247 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 4724,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 860,
+ "reference_Y": 14551
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.751
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 226,
+ "slope": 0.01032
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2875.0, 0.4699, 0.3209,
+ 3610.0, 0.4089, 0.4265,
+ 4640.0, 0.3281, 0.5417,
+ 5912.0, 0.2992, 0.5771,
+ 7630.0, 0.2285, 0.6524
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01783,
+ "transverse_neg": 0.02154
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.084, 2.084, 2.085, 2.085, 2.085, 2.087, 2.088, 2.087, 2.086, 2.082, 2.082, 2.084, 2.086, 2.088, 2.088, 2.088, 2.087, 2.088, 2.088, 2.091, 2.092, 2.093, 2.093, 2.093, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.091, 2.088,
+ 2.086, 2.086, 2.087, 2.088, 2.089, 2.089, 2.091, 2.089, 2.087, 2.086, 2.087, 2.088, 2.091, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.093, 2.093, 2.094, 2.095, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.093,
+ 2.087, 2.087, 2.088, 2.091, 2.091, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.096,
+ 2.089, 2.088, 2.089, 2.091, 2.091, 2.092, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.092, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.095, 2.096, 2.096, 2.097, 2.099, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.099, 2.098, 2.097,
+ 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.094, 2.095, 2.096, 2.097, 2.098, 2.098, 2.098, 2.101, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.089, 2.091, 2.092, 2.092, 2.092, 2.092, 2.094, 2.096, 2.096, 2.097, 2.098, 2.099, 2.099, 2.099, 2.099, 2.099, 2.097,
+ 2.093, 2.094, 2.094, 2.094, 2.095, 2.093, 2.092, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.096, 2.096, 2.097, 2.098, 2.098, 2.101, 2.101, 2.099, 2.099, 2.099,
+ 2.094, 2.094, 2.094, 2.095, 2.095, 2.095, 2.091, 2.089, 2.091, 2.089, 2.089, 2.089, 2.091, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.093, 2.095, 2.096, 2.097, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.099,
+ 2.095, 2.094, 2.094, 2.095, 2.096, 2.095, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.093, 2.094, 2.096, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.101, 2.099,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.095, 2.092, 2.089, 2.089, 2.088, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.093, 2.093, 2.095, 2.096, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.101,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.092, 2.092, 2.094, 2.094, 2.094, 2.096, 2.096, 2.098, 2.099, 2.102, 2.103, 2.103, 2.102, 2.102,
+ 2.095, 2.095, 2.095, 2.096, 2.096, 2.094, 2.093, 2.091, 2.091, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.093, 2.094, 2.094, 2.095, 2.096, 2.097, 2.098, 2.099, 2.103, 2.103, 2.103, 2.101, 2.101,
+ 2.095, 2.096, 2.096, 2.097, 2.096, 2.095, 2.093, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.096, 2.097, 2.099, 2.101, 2.103, 2.103, 2.103, 2.101, 2.099,
+ 2.096, 2.096, 2.097, 2.096, 2.097, 2.096, 2.094, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.099, 2.101, 2.103, 2.103, 2.102, 2.101, 2.101,
+ 2.096, 2.096, 2.097, 2.097, 2.097, 2.096, 2.094, 2.093, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.097, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101,
+ 2.097, 2.096, 2.097, 2.097, 2.097, 2.097, 2.095, 2.093, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.092, 2.092, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.101,
+ 2.098, 2.097, 2.096, 2.097, 2.097, 2.097, 2.095, 2.094, 2.094, 2.094, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.095, 2.095, 2.094, 2.093, 2.095, 2.096, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.102,
+ 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.095, 2.094, 2.095, 2.093, 2.093, 2.092, 2.092, 2.092, 2.094, 2.094, 2.096, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.102, 2.101, 2.102,
+ 2.098, 2.097, 2.097, 2.098, 2.097, 2.096, 2.095, 2.095, 2.095, 2.094, 2.093, 2.093, 2.094, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.095, 2.097, 2.097, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.104, 2.103, 2.102, 2.101,
+ 2.099, 2.098, 2.098, 2.098, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.094, 2.094, 2.094, 2.096, 2.097, 2.097, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.101, 2.101, 2.104, 2.105, 2.105, 2.103, 2.102,
+ 2.101, 2.099, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.098, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.103, 2.104, 2.105, 2.105, 2.104, 2.103,
+ 2.102, 2.102, 2.099, 2.098, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.104, 2.105, 2.106, 2.106, 2.106, 2.104, 2.104, 2.104,
+ 2.102, 2.101, 2.099, 2.099, 2.099, 2.101, 2.101, 2.101, 2.099, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.099, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.103, 2.105, 2.106, 2.106, 2.106, 2.106, 2.105, 2.104, 2.104,
+ 2.099, 2.099, 2.099, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.098, 2.097, 2.098, 2.098, 2.099, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.105, 2.105, 2.106, 2.106, 2.104, 2.104, 2.103,
+ 2.096, 2.097, 2.097, 2.097, 2.097, 2.099, 2.099, 2.099, 2.099, 2.097, 2.097, 2.098, 2.098, 2.099, 2.098, 2.097, 2.097, 2.099, 2.101, 2.101, 2.101, 2.101, 2.101, 2.103, 2.105, 2.105, 2.105, 2.104, 2.104, 2.103, 2.101, 2.101,
+ 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.098, 2.099, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.097, 2.097, 2.096, 2.098, 2.098, 2.099, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.104, 2.104, 2.103, 2.101, 2.099, 2.098,
+ 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.097, 2.098, 2.097, 2.097, 2.096, 2.096, 2.098, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.103, 2.104, 2.104, 2.102, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.096, 2.098, 2.098, 2.098, 2.098, 2.097, 2.098, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.099, 2.099, 2.099, 2.101, 2.102, 2.103, 2.104, 2.104, 2.104, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.097, 2.099, 2.099, 2.099, 2.099, 2.099, 2.099, 2.098, 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.098, 2.097, 2.099, 2.101, 2.099, 2.099, 2.099, 2.102, 2.102, 2.104, 2.105, 2.105, 2.102, 2.099, 2.098
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.431, 3.437, 3.439, 3.439, 3.436, 3.438, 3.441, 3.441, 3.441, 3.441, 3.442, 3.443, 3.443, 3.444, 3.446, 3.448, 3.451, 3.451, 3.452, 3.451, 3.449, 3.449, 3.452, 3.453, 3.454, 3.454, 3.453, 3.456, 3.456, 3.456, 3.451, 3.448,
+ 3.445, 3.446, 3.445, 3.449, 3.453, 3.451, 3.451, 3.446, 3.447, 3.446, 3.447, 3.451, 3.453, 3.455, 3.454, 3.453, 3.453, 3.454, 3.455, 3.456, 3.457, 3.459, 3.461, 3.462, 3.463, 3.463, 3.465, 3.466, 3.467, 3.465, 3.459, 3.457,
+ 3.449, 3.449, 3.449, 3.454, 3.455, 3.454, 3.453, 3.451, 3.451, 3.448, 3.451, 3.451, 3.455, 3.456, 3.457, 3.456, 3.456, 3.458, 3.457, 3.459, 3.459, 3.461, 3.464, 3.467, 3.467, 3.466, 3.468, 3.469, 3.471, 3.468, 3.465, 3.462,
+ 3.451, 3.448, 3.451, 3.453, 3.457, 3.455, 3.454, 3.449, 3.449, 3.448, 3.449, 3.449, 3.455, 3.455, 3.456, 3.455, 3.454, 3.455, 3.455, 3.457, 3.458, 3.458, 3.461, 3.464, 3.466, 3.468, 3.469, 3.469, 3.469, 3.468, 3.465, 3.463,
+ 3.449, 3.449, 3.451, 3.453, 3.456, 3.455, 3.452, 3.449, 3.448, 3.447, 3.446, 3.448, 3.451, 3.452, 3.454, 3.455, 3.455, 3.454, 3.457, 3.458, 3.458, 3.459, 3.461, 3.464, 3.464, 3.466, 3.467, 3.469, 3.469, 3.467, 3.463, 3.459,
+ 3.449, 3.451, 3.452, 3.454, 3.455, 3.454, 3.452, 3.449, 3.447, 3.447, 3.446, 3.449, 3.449, 3.451, 3.452, 3.452, 3.452, 3.452, 3.454, 3.455, 3.457, 3.459, 3.461, 3.464, 3.464, 3.466, 3.465, 3.468, 3.468, 3.469, 3.465, 3.462,
+ 3.451, 3.451, 3.452, 3.453, 3.453, 3.453, 3.451, 3.449, 3.449, 3.447, 3.446, 3.447, 3.448, 3.451, 3.451, 3.451, 3.453, 3.452, 3.452, 3.452, 3.457, 3.458, 3.461, 3.463, 3.464, 3.465, 3.464, 3.466, 3.468, 3.469, 3.466, 3.463,
+ 3.451, 3.451, 3.451, 3.454, 3.453, 3.453, 3.451, 3.448, 3.448, 3.444, 3.444, 3.444, 3.448, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.452, 3.454, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.467, 3.468, 3.469, 3.466, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.454, 3.453, 3.449, 3.448, 3.447, 3.447, 3.444, 3.446, 3.446, 3.446, 3.446, 3.447, 3.449, 3.449, 3.451, 3.452, 3.455, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.468, 3.469, 3.468, 3.465, 3.462,
+ 3.453, 3.452, 3.454, 3.456, 3.455, 3.453, 3.449, 3.447, 3.446, 3.446, 3.445, 3.448, 3.447, 3.446, 3.445, 3.446, 3.448, 3.448, 3.449, 3.453, 3.455, 3.457, 3.459, 3.461, 3.464, 3.466, 3.467, 3.468, 3.468, 3.467, 3.465, 3.463,
+ 3.453, 3.453, 3.454, 3.456, 3.456, 3.451, 3.448, 3.447, 3.447, 3.446, 3.445, 3.446, 3.446, 3.446, 3.446, 3.446, 3.448, 3.448, 3.449, 3.452, 3.454, 3.456, 3.459, 3.459, 3.461, 3.465, 3.466, 3.468, 3.468, 3.468, 3.467, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.456, 3.452, 3.448, 3.446, 3.446, 3.444, 3.446, 3.445, 3.446, 3.446, 3.447, 3.448, 3.449, 3.449, 3.449, 3.452, 3.453, 3.454, 3.458, 3.458, 3.461, 3.461, 3.464, 3.469, 3.469, 3.468, 3.466, 3.466,
+ 3.452, 3.452, 3.453, 3.454, 3.454, 3.453, 3.447, 3.446, 3.444, 3.444, 3.444, 3.444, 3.445, 3.446, 3.448, 3.451, 3.452, 3.453, 3.451, 3.453, 3.453, 3.455, 3.458, 3.459, 3.461, 3.462, 3.463, 3.468, 3.471, 3.469, 3.467, 3.467,
+ 3.454, 3.455, 3.457, 3.458, 3.458, 3.455, 3.449, 3.446, 3.445, 3.445, 3.445, 3.445, 3.447, 3.447, 3.448, 3.451, 3.452, 3.453, 3.452, 3.452, 3.452, 3.454, 3.457, 3.459, 3.459, 3.462, 3.464, 3.468, 3.469, 3.467, 3.465, 3.465,
+ 3.457, 3.455, 3.455, 3.459, 3.458, 3.454, 3.451, 3.448, 3.445, 3.445, 3.445, 3.446, 3.448, 3.449, 3.451, 3.452, 3.451, 3.453, 3.452, 3.452, 3.453, 3.457, 3.457, 3.461, 3.461, 3.463, 3.465, 3.468, 3.471, 3.468, 3.465, 3.463,
+ 3.458, 3.456, 3.456, 3.459, 3.457, 3.454, 3.452, 3.449, 3.447, 3.445, 3.446, 3.447, 3.447, 3.448, 3.449, 3.448, 3.449, 3.451, 3.451, 3.451, 3.451, 3.455, 3.456, 3.458, 3.462, 3.463, 3.464, 3.465, 3.467, 3.466, 3.464, 3.462,
+ 3.457, 3.456, 3.455, 3.457, 3.457, 3.454, 3.449, 3.447, 3.445, 3.445, 3.446, 3.446, 3.448, 3.446, 3.448, 3.449, 3.449, 3.451, 3.451, 3.451, 3.453, 3.455, 3.457, 3.459, 3.462, 3.464, 3.464, 3.465, 3.467, 3.464, 3.464, 3.463,
+ 3.458, 3.457, 3.455, 3.456, 3.456, 3.456, 3.453, 3.449, 3.447, 3.448, 3.447, 3.447, 3.447, 3.447, 3.447, 3.448, 3.449, 3.451, 3.451, 3.452, 3.453, 3.455, 3.458, 3.459, 3.459, 3.463, 3.464, 3.463, 3.464, 3.463, 3.464, 3.464,
+ 3.457, 3.456, 3.456, 3.456, 3.456, 3.456, 3.455, 3.449, 3.447, 3.448, 3.451, 3.449, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.451, 3.452, 3.453, 3.456, 3.458, 3.459, 3.461, 3.462, 3.464, 3.464, 3.465, 3.464, 3.464, 3.463,
+ 3.457, 3.456, 3.455, 3.455, 3.455, 3.455, 3.453, 3.451, 3.449, 3.448, 3.448, 3.449, 3.449, 3.449, 3.448, 3.449, 3.451, 3.452, 3.452, 3.453, 3.454, 3.457, 3.458, 3.459, 3.462, 3.464, 3.465, 3.464, 3.465, 3.464, 3.463, 3.463,
+ 3.456, 3.456, 3.454, 3.453, 3.454, 3.453, 3.452, 3.451, 3.449, 3.448, 3.448, 3.449, 3.451, 3.451, 3.448, 3.449, 3.451, 3.454, 3.454, 3.454, 3.455, 3.457, 3.458, 3.461, 3.461, 3.462, 3.464, 3.464, 3.466, 3.465, 3.464, 3.464,
+ 3.459, 3.457, 3.456, 3.455, 3.454, 3.453, 3.453, 3.452, 3.452, 3.451, 3.449, 3.449, 3.449, 3.448, 3.447, 3.449, 3.451, 3.454, 3.455, 3.455, 3.456, 3.458, 3.459, 3.461, 3.461, 3.462, 3.463, 3.466, 3.469, 3.465, 3.465, 3.464,
+ 3.463, 3.461, 3.458, 3.458, 3.457, 3.456, 3.456, 3.454, 3.454, 3.452, 3.452, 3.451, 3.451, 3.449, 3.448, 3.448, 3.452, 3.454, 3.456, 3.455, 3.457, 3.458, 3.461, 3.464, 3.462, 3.461, 3.463, 3.466, 3.469, 3.469, 3.467, 3.467,
+ 3.466, 3.462, 3.461, 3.461, 3.459, 3.457, 3.457, 3.457, 3.456, 3.454, 3.455, 3.455, 3.455, 3.451, 3.452, 3.453, 3.454, 3.455, 3.456, 3.456, 3.459, 3.462, 3.463, 3.466, 3.466, 3.467, 3.466, 3.469, 3.471, 3.469, 3.468, 3.466,
+ 3.467, 3.463, 3.463, 3.459, 3.461, 3.459, 3.461, 3.459, 3.458, 3.456, 3.457, 3.456, 3.457, 3.455, 3.456, 3.455, 3.456, 3.457, 3.459, 3.461, 3.461, 3.464, 3.465, 3.468, 3.469, 3.469, 3.469, 3.469, 3.471, 3.468, 3.467, 3.468,
+ 3.467, 3.464, 3.459, 3.459, 3.462, 3.462, 3.462, 3.461, 3.461, 3.462, 3.461, 3.459, 3.461, 3.459, 3.458, 3.457, 3.459, 3.461, 3.462, 3.463, 3.464, 3.466, 3.468, 3.469, 3.471, 3.469, 3.471, 3.472, 3.471, 3.467, 3.466, 3.464,
+ 3.464, 3.462, 3.458, 3.457, 3.458, 3.461, 3.461, 3.461, 3.461, 3.462, 3.462, 3.461, 3.461, 3.459, 3.459, 3.459, 3.461, 3.461, 3.464, 3.465, 3.465, 3.468, 3.468, 3.469, 3.471, 3.469, 3.469, 3.469, 3.469, 3.464, 3.462, 3.459,
+ 3.457, 3.458, 3.455, 3.456, 3.456, 3.457, 3.459, 3.459, 3.459, 3.459, 3.458, 3.456, 3.458, 3.457, 3.458, 3.458, 3.458, 3.459, 3.461, 3.463, 3.465, 3.466, 3.468, 3.469, 3.471, 3.468, 3.466, 3.466, 3.465, 3.461, 3.459, 3.457,
+ 3.456, 3.455, 3.454, 3.454, 3.455, 3.456, 3.458, 3.459, 3.459, 3.456, 3.456, 3.456, 3.455, 3.456, 3.455, 3.455, 3.455, 3.454, 3.457, 3.461, 3.462, 3.464, 3.465, 3.467, 3.467, 3.466, 3.464, 3.464, 3.463, 3.461, 3.457, 3.456,
+ 3.456, 3.454, 3.453, 3.454, 3.454, 3.455, 3.458, 3.459, 3.459, 3.456, 3.455, 3.455, 3.455, 3.451, 3.453, 3.454, 3.454, 3.455, 3.455, 3.458, 3.461, 3.462, 3.461, 3.463, 3.465, 3.464, 3.463, 3.463, 3.462, 3.459, 3.456, 3.451,
+ 3.455, 3.452, 3.452, 3.452, 3.455, 3.457, 3.459, 3.459, 3.459, 3.458, 3.456, 3.456, 3.455, 3.453, 3.453, 3.455, 3.457, 3.457, 3.457, 3.461, 3.461, 3.461, 3.459, 3.462, 3.464, 3.464, 3.464, 3.463, 3.463, 3.459, 3.454, 3.451,
+ 3.452, 3.452, 3.452, 3.453, 3.457, 3.458, 3.458, 3.459, 3.459, 3.458, 3.457, 3.457, 3.455, 3.455, 3.458, 3.459, 3.458, 3.459, 3.459, 3.461, 3.461, 3.461, 3.459, 3.461, 3.463, 3.464, 3.466, 3.463, 3.461, 3.458, 3.453, 3.449
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.403, 3.399, 3.395, 3.391, 3.392, 3.394, 3.401, 3.403, 3.404, 3.404, 3.403, 3.399, 3.398, 3.396, 3.395, 3.396, 3.399, 3.403, 3.404, 3.401, 3.399, 3.398, 3.397, 3.401, 3.401, 3.401, 3.396, 3.394, 3.397, 3.396, 3.388, 3.364,
+ 3.403, 3.399, 3.393, 3.389, 3.391, 3.395, 3.401, 3.404, 3.406, 3.404, 3.403, 3.399, 3.399, 3.397, 3.397, 3.397, 3.401, 3.404, 3.404, 3.402, 3.398, 3.396, 3.397, 3.401, 3.401, 3.401, 3.395, 3.394, 3.396, 3.393, 3.387, 3.364,
+ 3.399, 3.398, 3.391, 3.385, 3.386, 3.395, 3.402, 3.405, 3.405, 3.404, 3.402, 3.399, 3.399, 3.398, 3.398, 3.398, 3.401, 3.404, 3.405, 3.403, 3.399, 3.396, 3.396, 3.398, 3.401, 3.401, 3.398, 3.394, 3.392, 3.389, 3.386, 3.364,
+ 3.398, 3.393, 3.386, 3.382, 3.385, 3.392, 3.399, 3.403, 3.405, 3.404, 3.402, 3.398, 3.398, 3.397, 3.397, 3.398, 3.401, 3.404, 3.405, 3.403, 3.398, 3.394, 3.394, 3.398, 3.401, 3.401, 3.396, 3.392, 3.391, 3.388, 3.383, 3.362,
+ 3.396, 3.391, 3.384, 3.381, 3.384, 3.389, 3.398, 3.402, 3.402, 3.401, 3.399, 3.395, 3.395, 3.395, 3.397, 3.397, 3.401, 3.402, 3.404, 3.403, 3.399, 3.394, 3.393, 3.395, 3.399, 3.399, 3.397, 3.391, 3.388, 3.384, 3.381, 3.363,
+ 3.391, 3.386, 3.382, 3.381, 3.385, 3.389, 3.396, 3.398, 3.399, 3.399, 3.398, 3.395, 3.394, 3.394, 3.395, 3.397, 3.399, 3.401, 3.403, 3.401, 3.398, 3.394, 3.393, 3.393, 3.394, 3.396, 3.395, 3.392, 3.387, 3.382, 3.378, 3.361,
+ 3.389, 3.386, 3.379, 3.379, 3.383, 3.388, 3.394, 3.397, 3.397, 3.397, 3.395, 3.393, 3.393, 3.393, 3.395, 3.395, 3.397, 3.398, 3.401, 3.399, 3.397, 3.395, 3.394, 3.391, 3.393, 3.393, 3.393, 3.389, 3.387, 3.381, 3.374, 3.357,
+ 3.386, 3.383, 3.376, 3.375, 3.381, 3.386, 3.394, 3.396, 3.396, 3.394, 3.392, 3.392, 3.394, 3.394, 3.395, 3.394, 3.396, 3.398, 3.399, 3.397, 3.397, 3.394, 3.393, 3.391, 3.389, 3.391, 3.392, 3.388, 3.386, 3.379, 3.372, 3.355,
+ 3.386, 3.379, 3.373, 3.373, 3.378, 3.384, 3.391, 3.396, 3.395, 3.393, 3.389, 3.391, 3.391, 3.393, 3.394, 3.393, 3.394, 3.396, 3.397, 3.396, 3.393, 3.394, 3.393, 3.392, 3.389, 3.389, 3.389, 3.389, 3.386, 3.378, 3.371, 3.351,
+ 3.379, 3.375, 3.371, 3.371, 3.376, 3.381, 3.388, 3.393, 3.394, 3.391, 3.386, 3.386, 3.388, 3.393, 3.392, 3.392, 3.393, 3.395, 3.394, 3.392, 3.389, 3.391, 3.391, 3.392, 3.389, 3.388, 3.389, 3.389, 3.383, 3.377, 3.369, 3.351,
+ 3.373, 3.371, 3.367, 3.368, 3.373, 3.381, 3.387, 3.389, 3.391, 3.389, 3.385, 3.386, 3.383, 3.389, 3.389, 3.392, 3.392, 3.394, 3.393, 3.389, 3.387, 3.387, 3.388, 3.389, 3.389, 3.388, 3.386, 3.386, 3.382, 3.374, 3.367, 3.345,
+ 3.371, 3.369, 3.365, 3.366, 3.373, 3.379, 3.386, 3.389, 3.391, 3.389, 3.385, 3.384, 3.382, 3.386, 3.387, 3.389, 3.391, 3.392, 3.391, 3.387, 3.385, 3.385, 3.386, 3.388, 3.388, 3.388, 3.386, 3.385, 3.381, 3.373, 3.367, 3.345,
+ 3.367, 3.365, 3.365, 3.366, 3.374, 3.379, 3.384, 3.388, 3.389, 3.387, 3.384, 3.383, 3.383, 3.385, 3.385, 3.386, 3.388, 3.389, 3.388, 3.386, 3.383, 3.382, 3.384, 3.386, 3.387, 3.386, 3.381, 3.381, 3.379, 3.372, 3.364, 3.344,
+ 3.365, 3.363, 3.362, 3.367, 3.375, 3.379, 3.383, 3.384, 3.386, 3.384, 3.381, 3.379, 3.379, 3.383, 3.383, 3.384, 3.385, 3.387, 3.387, 3.385, 3.381, 3.381, 3.382, 3.384, 3.384, 3.385, 3.382, 3.379, 3.374, 3.369, 3.359, 3.343,
+ 3.359, 3.358, 3.361, 3.364, 3.373, 3.381, 3.384, 3.384, 3.385, 3.384, 3.381, 3.377, 3.379, 3.379, 3.382, 3.383, 3.384, 3.386, 3.386, 3.385, 3.381, 3.379, 3.381, 3.382, 3.382, 3.383, 3.379, 3.377, 3.371, 3.364, 3.357, 3.339,
+ 3.357, 3.356, 3.356, 3.362, 3.372, 3.379, 3.384, 3.384, 3.383, 3.381, 3.378, 3.376, 3.377, 3.379, 3.381, 3.382, 3.383, 3.385, 3.385, 3.383, 3.379, 3.379, 3.379, 3.381, 3.381, 3.382, 3.379, 3.372, 3.367, 3.362, 3.354, 3.334,
+ 3.357, 3.354, 3.357, 3.361, 3.372, 3.381, 3.385, 3.385, 3.384, 3.379, 3.376, 3.376, 3.376, 3.379, 3.381, 3.383, 3.383, 3.384, 3.383, 3.379, 3.378, 3.381, 3.379, 3.379, 3.379, 3.379, 3.378, 3.371, 3.363, 3.358, 3.354, 3.332,
+ 3.354, 3.351, 3.354, 3.359, 3.371, 3.379, 3.382, 3.384, 3.381, 3.378, 3.375, 3.374, 3.376, 3.378, 3.381, 3.383, 3.384, 3.382, 3.377, 3.377, 3.376, 3.377, 3.378, 3.378, 3.379, 3.379, 3.376, 3.367, 3.361, 3.357, 3.352, 3.333,
+ 3.352, 3.349, 3.351, 3.357, 3.372, 3.381, 3.383, 3.383, 3.381, 3.376, 3.372, 3.373, 3.375, 3.377, 3.382, 3.384, 3.384, 3.379, 3.376, 3.374, 3.374, 3.375, 3.375, 3.376, 3.377, 3.376, 3.373, 3.366, 3.361, 3.356, 3.347, 3.332,
+ 3.347, 3.346, 3.346, 3.355, 3.371, 3.377, 3.382, 3.381, 3.379, 3.372, 3.371, 3.371, 3.372, 3.375, 3.379, 3.383, 3.384, 3.379, 3.374, 3.373, 3.371, 3.373, 3.374, 3.375, 3.374, 3.374, 3.371, 3.365, 3.359, 3.352, 3.343, 3.331,
+ 3.345, 3.344, 3.345, 3.353, 3.367, 3.374, 3.382, 3.382, 3.376, 3.371, 3.369, 3.368, 3.369, 3.373, 3.377, 3.381, 3.379, 3.376, 3.373, 3.369, 3.368, 3.371, 3.372, 3.373, 3.371, 3.371, 3.369, 3.363, 3.357, 3.349, 3.341, 3.326,
+ 3.343, 3.341, 3.344, 3.351, 3.362, 3.371, 3.376, 3.376, 3.372, 3.369, 3.367, 3.366, 3.367, 3.369, 3.376, 3.378, 3.378, 3.375, 3.371, 3.367, 3.367, 3.368, 3.369, 3.369, 3.369, 3.368, 3.365, 3.361, 3.354, 3.347, 3.338, 3.321,
+ 3.341, 3.339, 3.342, 3.349, 3.359, 3.367, 3.371, 3.372, 3.371, 3.368, 3.366, 3.363, 3.365, 3.368, 3.371, 3.374, 3.376, 3.374, 3.368, 3.365, 3.365, 3.366, 3.368, 3.367, 3.367, 3.363, 3.361, 3.356, 3.352, 3.346, 3.336, 3.317,
+ 3.338, 3.336, 3.338, 3.346, 3.359, 3.364, 3.368, 3.369, 3.367, 3.366, 3.363, 3.362, 3.364, 3.364, 3.367, 3.371, 3.372, 3.369, 3.365, 3.362, 3.362, 3.365, 3.367, 3.367, 3.366, 3.362, 3.357, 3.353, 3.349, 3.342, 3.335, 3.317,
+ 3.334, 3.334, 3.336, 3.346, 3.354, 3.361, 3.365, 3.365, 3.365, 3.362, 3.361, 3.361, 3.362, 3.362, 3.364, 3.366, 3.368, 3.366, 3.361, 3.357, 3.357, 3.359, 3.363, 3.365, 3.363, 3.361, 3.355, 3.351, 3.346, 3.339, 3.336, 3.317,
+ 3.332, 3.332, 3.334, 3.344, 3.354, 3.359, 3.363, 3.365, 3.363, 3.361, 3.359, 3.359, 3.363, 3.363, 3.365, 3.365, 3.367, 3.366, 3.358, 3.356, 3.356, 3.358, 3.362, 3.364, 3.363, 3.359, 3.353, 3.348, 3.345, 3.339, 3.336, 3.315,
+ 3.332, 3.328, 3.331, 3.343, 3.351, 3.357, 3.358, 3.362, 3.361, 3.359, 3.357, 3.357, 3.361, 3.362, 3.364, 3.363, 3.363, 3.359, 3.356, 3.354, 3.354, 3.355, 3.358, 3.359, 3.361, 3.359, 3.351, 3.346, 3.344, 3.339, 3.336, 3.313,
+ 3.324, 3.324, 3.327, 3.334, 3.345, 3.351, 3.354, 3.356, 3.356, 3.354, 3.353, 3.354, 3.357, 3.358, 3.361, 3.358, 3.359, 3.355, 3.352, 3.348, 3.347, 3.351, 3.354, 3.358, 3.359, 3.355, 3.346, 3.343, 3.341, 3.336, 3.331, 3.312,
+ 3.318, 3.319, 3.321, 3.328, 3.337, 3.339, 3.345, 3.348, 3.346, 3.345, 3.347, 3.348, 3.351, 3.354, 3.356, 3.353, 3.354, 3.344, 3.343, 3.343, 3.343, 3.344, 3.347, 3.349, 3.353, 3.346, 3.341, 3.339, 3.331, 3.329, 3.325, 3.311,
+ 3.309, 3.313, 3.317, 3.325, 3.329, 3.332, 3.338, 3.339, 3.341, 3.339, 3.339, 3.342, 3.346, 3.346, 3.351, 3.351, 3.343, 3.338, 3.338, 3.339, 3.339, 3.339, 3.341, 3.341, 3.346, 3.343, 3.339, 3.332, 3.327, 3.326, 3.322, 3.309,
+ 3.305, 3.309, 3.317, 3.325, 3.328, 3.331, 3.334, 3.336, 3.337, 3.336, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.341, 3.336, 3.335, 3.337, 3.339, 3.341, 3.339, 3.339, 3.342, 3.341, 3.337, 3.329, 3.326, 3.325, 3.321, 3.314,
+ 3.302, 3.306, 3.319, 3.325, 3.329, 3.331, 3.334, 3.335, 3.337, 3.337, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.342, 3.336, 3.336, 3.338, 3.339, 3.341, 3.341, 3.341, 3.339, 3.338, 3.336, 3.331, 3.327, 3.324, 3.321, 3.314
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.726, 1.725, 1.723, 1.721, 1.723, 1.724, 1.724, 1.726, 1.727, 1.728, 1.729, 1.728, 1.725, 1.724, 1.726, 1.726, 1.727, 1.729, 1.727, 1.727, 1.724, 1.725, 1.724, 1.726, 1.725, 1.725, 1.724, 1.724, 1.722, 1.721, 1.719, 1.714,
+ 1.726, 1.724, 1.722, 1.721, 1.722, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.727, 1.725, 1.724, 1.724, 1.725, 1.726, 1.725, 1.724, 1.723, 1.722, 1.721, 1.719, 1.714,
+ 1.724, 1.722, 1.719, 1.719, 1.721, 1.723, 1.726, 1.726, 1.727, 1.727, 1.727, 1.725, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.725, 1.724, 1.724, 1.724, 1.726, 1.725, 1.724, 1.722, 1.722, 1.721, 1.719, 1.712,
+ 1.723, 1.721, 1.719, 1.719, 1.719, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.726, 1.728, 1.729, 1.728, 1.725, 1.723, 1.723, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.718, 1.711,
+ 1.722, 1.719, 1.719, 1.718, 1.719, 1.722, 1.725, 1.726, 1.726, 1.727, 1.727, 1.726, 1.725, 1.726, 1.726, 1.726, 1.727, 1.727, 1.728, 1.727, 1.726, 1.725, 1.724, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.715, 1.711,
+ 1.721, 1.717, 1.717, 1.716, 1.719, 1.722, 1.724, 1.726, 1.726, 1.727, 1.726, 1.726, 1.726, 1.726, 1.726, 1.727, 1.727, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.722, 1.721, 1.718, 1.715, 1.707,
+ 1.718, 1.717, 1.716, 1.716, 1.718, 1.721, 1.725, 1.726, 1.726, 1.726, 1.725, 1.725, 1.725, 1.725, 1.726, 1.727, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.718, 1.715, 1.709,
+ 1.718, 1.716, 1.716, 1.715, 1.717, 1.721, 1.724, 1.725, 1.726, 1.725, 1.725, 1.724, 1.724, 1.725, 1.726, 1.726, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.723, 1.723, 1.723, 1.722, 1.722, 1.719, 1.718, 1.714, 1.709,
+ 1.718, 1.716, 1.715, 1.715, 1.717, 1.721, 1.723, 1.725, 1.726, 1.725, 1.724, 1.723, 1.724, 1.725, 1.725, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.723, 1.722, 1.722, 1.721, 1.717, 1.714, 1.707,
+ 1.717, 1.716, 1.714, 1.714, 1.716, 1.721, 1.723, 1.725, 1.725, 1.725, 1.723, 1.723, 1.724, 1.726, 1.726, 1.726, 1.726, 1.725, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.706,
+ 1.715, 1.714, 1.714, 1.714, 1.716, 1.719, 1.722, 1.724, 1.725, 1.725, 1.723, 1.723, 1.724, 1.725, 1.725, 1.725, 1.726, 1.725, 1.725, 1.725, 1.724, 1.724, 1.724, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.713, 1.705,
+ 1.714, 1.714, 1.713, 1.714, 1.717, 1.719, 1.722, 1.724, 1.724, 1.724, 1.723, 1.722, 1.723, 1.724, 1.724, 1.724, 1.726, 1.725, 1.726, 1.725, 1.723, 1.723, 1.724, 1.724, 1.724, 1.723, 1.721, 1.719, 1.717, 1.715, 1.713, 1.706,
+ 1.712, 1.712, 1.712, 1.713, 1.718, 1.719, 1.721, 1.723, 1.724, 1.724, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.725, 1.725, 1.725, 1.725, 1.723, 1.722, 1.724, 1.723, 1.723, 1.722, 1.721, 1.719, 1.717, 1.714, 1.711, 1.706,
+ 1.712, 1.711, 1.711, 1.713, 1.717, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.724, 1.725, 1.725, 1.724, 1.723, 1.722, 1.722, 1.722, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.711, 1.706,
+ 1.711, 1.709, 1.711, 1.713, 1.716, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.721, 1.722, 1.724, 1.724, 1.724, 1.723, 1.724, 1.724, 1.724, 1.722, 1.722, 1.722, 1.722, 1.722, 1.721, 1.719, 1.718, 1.714, 1.712, 1.709, 1.702,
+ 1.709, 1.709, 1.709, 1.712, 1.717, 1.719, 1.721, 1.723, 1.723, 1.723, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.724, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.716, 1.713, 1.711, 1.709, 1.701,
+ 1.708, 1.707, 1.709, 1.712, 1.716, 1.719, 1.722, 1.723, 1.723, 1.723, 1.721, 1.721, 1.721, 1.722, 1.723, 1.723, 1.723, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.714, 1.712, 1.709, 1.708, 1.702,
+ 1.707, 1.707, 1.708, 1.711, 1.716, 1.721, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.722, 1.722, 1.723, 1.723, 1.723, 1.722, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.721, 1.717, 1.714, 1.711, 1.709, 1.707, 1.702,
+ 1.706, 1.706, 1.707, 1.711, 1.714, 1.719, 1.722, 1.722, 1.722, 1.721, 1.719, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.719, 1.719, 1.721, 1.721, 1.719, 1.719, 1.716, 1.713, 1.711, 1.709, 1.706, 1.701,
+ 1.705, 1.704, 1.706, 1.709, 1.713, 1.718, 1.721, 1.722, 1.721, 1.719, 1.718, 1.719, 1.721, 1.722, 1.723, 1.724, 1.724, 1.721, 1.721, 1.721, 1.719, 1.719, 1.719, 1.719, 1.719, 1.717, 1.715, 1.713, 1.711, 1.707, 1.704, 1.699,
+ 1.703, 1.703, 1.704, 1.709, 1.712, 1.717, 1.719, 1.721, 1.719, 1.718, 1.717, 1.718, 1.719, 1.721, 1.722, 1.723, 1.723, 1.722, 1.719, 1.719, 1.718, 1.719, 1.719, 1.718, 1.717, 1.716, 1.714, 1.712, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.703, 1.704, 1.708, 1.712, 1.715, 1.718, 1.719, 1.719, 1.717, 1.717, 1.717, 1.717, 1.718, 1.721, 1.722, 1.722, 1.721, 1.719, 1.718, 1.717, 1.718, 1.718, 1.717, 1.716, 1.714, 1.714, 1.711, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.702, 1.703, 1.706, 1.709, 1.715, 1.717, 1.718, 1.717, 1.717, 1.716, 1.716, 1.717, 1.717, 1.719, 1.721, 1.721, 1.721, 1.719, 1.717, 1.716, 1.717, 1.717, 1.716, 1.714, 1.713, 1.712, 1.711, 1.708, 1.706, 1.702, 1.696,
+ 1.701, 1.701, 1.702, 1.706, 1.709, 1.714, 1.716, 1.717, 1.716, 1.716, 1.716, 1.715, 1.716, 1.716, 1.717, 1.718, 1.719, 1.719, 1.716, 1.715, 1.715, 1.715, 1.715, 1.715, 1.714, 1.713, 1.711, 1.709, 1.708, 1.704, 1.701, 1.695,
+ 1.699, 1.699, 1.702, 1.706, 1.708, 1.712, 1.714, 1.715, 1.715, 1.715, 1.714, 1.715, 1.714, 1.715, 1.716, 1.716, 1.716, 1.716, 1.714, 1.713, 1.713, 1.714, 1.715, 1.714, 1.714, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.695,
+ 1.698, 1.699, 1.701, 1.705, 1.708, 1.711, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.715, 1.715, 1.716, 1.716, 1.715, 1.713, 1.713, 1.713, 1.714, 1.714, 1.714, 1.713, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.696,
+ 1.698, 1.699, 1.701, 1.705, 1.707, 1.711, 1.712, 1.713, 1.713, 1.713, 1.713, 1.714, 1.714, 1.715, 1.715, 1.716, 1.715, 1.714, 1.713, 1.712, 1.712, 1.712, 1.713, 1.713, 1.713, 1.711, 1.709, 1.707, 1.705, 1.703, 1.701, 1.696,
+ 1.698, 1.697, 1.699, 1.702, 1.705, 1.707, 1.711, 1.711, 1.711, 1.711, 1.711, 1.712, 1.712, 1.713, 1.714, 1.714, 1.713, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.708, 1.706, 1.704, 1.703, 1.699, 1.696,
+ 1.694, 1.695, 1.697, 1.699, 1.702, 1.705, 1.706, 1.707, 1.707, 1.708, 1.708, 1.708, 1.709, 1.711, 1.711, 1.711, 1.708, 1.708, 1.708, 1.707, 1.707, 1.707, 1.708, 1.708, 1.709, 1.708, 1.706, 1.703, 1.702, 1.701, 1.698, 1.696,
+ 1.692, 1.692, 1.695, 1.698, 1.699, 1.701, 1.704, 1.704, 1.704, 1.704, 1.705, 1.706, 1.707, 1.709, 1.709, 1.707, 1.706, 1.704, 1.704, 1.705, 1.705, 1.706, 1.706, 1.706, 1.706, 1.706, 1.703, 1.702, 1.701, 1.699, 1.696, 1.694,
+ 1.691, 1.692, 1.695, 1.697, 1.699, 1.699, 1.702, 1.703, 1.703, 1.702, 1.703, 1.704, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.704, 1.705, 1.705, 1.705, 1.705, 1.704, 1.703, 1.701, 1.699, 1.698, 1.696, 1.695,
+ 1.689, 1.691, 1.696, 1.698, 1.699, 1.699, 1.701, 1.702, 1.702, 1.702, 1.703, 1.703, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.703, 1.704, 1.704, 1.705, 1.704, 1.704, 1.702, 1.701, 1.698, 1.698, 1.696, 1.696
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.425, 1.393, 1.341, 1.295, 1.258, 1.226, 1.201, 1.181, 1.162, 1.146, 1.133, 1.123, 1.115, 1.111, 1.107, 1.106, 1.106, 1.107, 1.108, 1.111, 1.114, 1.122, 1.133, 1.148, 1.164, 1.184, 1.208, 1.236, 1.271, 1.309, 1.359, 1.381,
+ 1.397, 1.367, 1.317, 1.274, 1.237, 1.207, 1.183, 1.163, 1.146, 1.133, 1.123, 1.114, 1.107, 1.101, 1.098, 1.096, 1.096, 1.096, 1.097, 1.102, 1.106, 1.112, 1.122, 1.133, 1.148, 1.166, 1.187, 1.215, 1.249, 1.288, 1.335, 1.359,
+ 1.374, 1.341, 1.292, 1.251, 1.215, 1.186, 1.166, 1.146, 1.131, 1.117, 1.108, 1.099, 1.091, 1.088, 1.084, 1.082, 1.081, 1.082, 1.084, 1.088, 1.093, 1.098, 1.107, 1.118, 1.133, 1.149, 1.169, 1.195, 1.228, 1.267, 1.313, 1.335,
+ 1.352, 1.318, 1.271, 1.231, 1.196, 1.169, 1.149, 1.131, 1.115, 1.103, 1.093, 1.086, 1.079, 1.074, 1.071, 1.069, 1.069, 1.069, 1.071, 1.076, 1.079, 1.085, 1.094, 1.102, 1.117, 1.133, 1.152, 1.176, 1.208, 1.246, 1.289, 1.313,
+ 1.333, 1.298, 1.253, 1.212, 1.179, 1.153, 1.134, 1.116, 1.102, 1.089, 1.079, 1.072, 1.066, 1.062, 1.059, 1.058, 1.057, 1.057, 1.059, 1.064, 1.068, 1.072, 1.081, 1.091, 1.102, 1.119, 1.137, 1.161, 1.191, 1.227, 1.271, 1.293,
+ 1.317, 1.281, 1.235, 1.196, 1.165, 1.139, 1.119, 1.104, 1.089, 1.078, 1.068, 1.062, 1.055, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.053, 1.056, 1.061, 1.069, 1.079, 1.091, 1.105, 1.126, 1.147, 1.177, 1.212, 1.253, 1.278,
+ 1.301, 1.265, 1.221, 1.181, 1.151, 1.127, 1.108, 1.091, 1.078, 1.068, 1.059, 1.051, 1.045, 1.041, 1.038, 1.037, 1.036, 1.037, 1.038, 1.042, 1.046, 1.051, 1.059, 1.069, 1.081, 1.096, 1.113, 1.135, 1.164, 1.198, 1.238, 1.264,
+ 1.286, 1.251, 1.207, 1.169, 1.141, 1.116, 1.098, 1.081, 1.068, 1.058, 1.049, 1.042, 1.037, 1.033, 1.031, 1.029, 1.028, 1.028, 1.029, 1.033, 1.037, 1.043, 1.051, 1.059, 1.071, 1.086, 1.104, 1.124, 1.152, 1.185, 1.225, 1.252,
+ 1.275, 1.239, 1.196, 1.161, 1.132, 1.107, 1.089, 1.073, 1.059, 1.049, 1.041, 1.035, 1.028, 1.024, 1.023, 1.021, 1.021, 1.021, 1.022, 1.024, 1.029, 1.036, 1.043, 1.051, 1.063, 1.078, 1.095, 1.115, 1.143, 1.175, 1.214, 1.243,
+ 1.267, 1.227, 1.187, 1.152, 1.122, 1.101, 1.081, 1.067, 1.054, 1.042, 1.035, 1.028, 1.023, 1.018, 1.015, 1.014, 1.014, 1.014, 1.016, 1.019, 1.024, 1.029, 1.036, 1.045, 1.056, 1.071, 1.088, 1.107, 1.134, 1.167, 1.204, 1.234,
+ 1.261, 1.219, 1.179, 1.145, 1.116, 1.095, 1.076, 1.061, 1.047, 1.037, 1.031, 1.023, 1.018, 1.014, 1.011, 1.009, 1.009, 1.009, 1.011, 1.013, 1.018, 1.024, 1.031, 1.039, 1.049, 1.065, 1.083, 1.102, 1.128, 1.161, 1.196, 1.228,
+ 1.256, 1.213, 1.173, 1.139, 1.111, 1.091, 1.071, 1.056, 1.043, 1.033, 1.026, 1.019, 1.014, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.009, 1.013, 1.018, 1.026, 1.035, 1.046, 1.061, 1.078, 1.097, 1.123, 1.154, 1.191, 1.222,
+ 1.251, 1.208, 1.169, 1.137, 1.108, 1.088, 1.069, 1.053, 1.039, 1.029, 1.023, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.002, 1.003, 1.006, 1.009, 1.015, 1.022, 1.032, 1.044, 1.057, 1.076, 1.094, 1.119, 1.149, 1.186, 1.218,
+ 1.249, 1.205, 1.167, 1.133, 1.107, 1.085, 1.067, 1.052, 1.038, 1.029, 1.021, 1.013, 1.008, 1.004, 1.003, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.013, 1.021, 1.031, 1.042, 1.055, 1.073, 1.093, 1.116, 1.147, 1.182, 1.218,
+ 1.249, 1.204, 1.165, 1.132, 1.106, 1.085, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.007, 1.003, 1.002, 1.001, 1.001, 1.001, 1.001, 1.004, 1.007, 1.013, 1.021, 1.029, 1.042, 1.055, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.249, 1.204, 1.165, 1.132, 1.107, 1.086, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.008, 1.004, 1.002, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.014, 1.021, 1.029, 1.042, 1.056, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.251, 1.205, 1.166, 1.133, 1.108, 1.087, 1.068, 1.052, 1.039, 1.031, 1.021, 1.014, 1.009, 1.006, 1.003, 1.002, 1.001, 1.001, 1.003, 1.006, 1.009, 1.014, 1.022, 1.031, 1.043, 1.056, 1.073, 1.093, 1.116, 1.145, 1.182, 1.218,
+ 1.252, 1.208, 1.168, 1.137, 1.111, 1.089, 1.071, 1.055, 1.043, 1.033, 1.023, 1.016, 1.012, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.008, 1.012, 1.017, 1.024, 1.034, 1.045, 1.059, 1.075, 1.095, 1.119, 1.149, 1.185, 1.218,
+ 1.256, 1.213, 1.173, 1.142, 1.115, 1.093, 1.075, 1.059, 1.047, 1.036, 1.027, 1.021, 1.016, 1.012, 1.011, 1.009, 1.008, 1.008, 1.009, 1.012, 1.016, 1.021, 1.028, 1.038, 1.049, 1.064, 1.081, 1.099, 1.126, 1.155, 1.192, 1.223,
+ 1.261, 1.221, 1.179, 1.148, 1.121, 1.099, 1.081, 1.065, 1.052, 1.042, 1.032, 1.026, 1.021, 1.017, 1.015, 1.014, 1.014, 1.013, 1.013, 1.016, 1.021, 1.026, 1.033, 1.043, 1.054, 1.068, 1.085, 1.106, 1.132, 1.161, 1.199, 1.228,
+ 1.267, 1.228, 1.188, 1.155, 1.128, 1.105, 1.086, 1.071, 1.059, 1.047, 1.038, 1.031, 1.027, 1.022, 1.021, 1.019, 1.019, 1.019, 1.019, 1.022, 1.026, 1.032, 1.038, 1.049, 1.061, 1.075, 1.092, 1.112, 1.138, 1.169, 1.207, 1.236,
+ 1.278, 1.241, 1.199, 1.164, 1.137, 1.114, 1.094, 1.078, 1.066, 1.055, 1.046, 1.038, 1.032, 1.029, 1.027, 1.027, 1.027, 1.027, 1.027, 1.029, 1.032, 1.038, 1.047, 1.056, 1.067, 1.083, 1.099, 1.121, 1.146, 1.178, 1.217, 1.244,
+ 1.291, 1.252, 1.211, 1.175, 1.147, 1.124, 1.103, 1.088, 1.075, 1.063, 1.054, 1.046, 1.041, 1.036, 1.035, 1.035, 1.035, 1.035, 1.036, 1.038, 1.041, 1.047, 1.055, 1.065, 1.075, 1.092, 1.111, 1.132, 1.157, 1.189, 1.231, 1.255,
+ 1.303, 1.265, 1.222, 1.187, 1.158, 1.133, 1.112, 1.097, 1.083, 1.072, 1.063, 1.054, 1.048, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.046, 1.049, 1.055, 1.065, 1.074, 1.086, 1.102, 1.119, 1.144, 1.171, 1.203, 1.243, 1.268,
+ 1.317, 1.282, 1.236, 1.201, 1.171, 1.146, 1.125, 1.109, 1.095, 1.083, 1.072, 1.064, 1.058, 1.054, 1.052, 1.051, 1.051, 1.053, 1.054, 1.057, 1.061, 1.065, 1.074, 1.086, 1.099, 1.113, 1.133, 1.156, 1.183, 1.217, 1.259, 1.282,
+ 1.335, 1.301, 1.254, 1.218, 1.186, 1.159, 1.138, 1.121, 1.108, 1.095, 1.085, 1.076, 1.069, 1.066, 1.065, 1.063, 1.062, 1.063, 1.065, 1.068, 1.073, 1.078, 1.087, 1.098, 1.113, 1.126, 1.146, 1.171, 1.199, 1.235, 1.277, 1.299,
+ 1.356, 1.321, 1.274, 1.235, 1.202, 1.175, 1.153, 1.137, 1.121, 1.108, 1.097, 1.089, 1.084, 1.081, 1.077, 1.075, 1.075, 1.075, 1.077, 1.081, 1.086, 1.091, 1.099, 1.113, 1.126, 1.144, 1.162, 1.187, 1.218, 1.255, 1.297, 1.321,
+ 1.376, 1.344, 1.296, 1.257, 1.223, 1.194, 1.171, 1.153, 1.137, 1.124, 1.112, 1.104, 1.099, 1.095, 1.093, 1.091, 1.089, 1.091, 1.092, 1.095, 1.101, 1.108, 1.116, 1.128, 1.144, 1.161, 1.181, 1.206, 1.237, 1.275, 1.321, 1.347,
+ 1.403, 1.369, 1.319, 1.279, 1.244, 1.214, 1.191, 1.171, 1.154, 1.139, 1.129, 1.121, 1.115, 1.111, 1.109, 1.106, 1.105, 1.105, 1.108, 1.112, 1.117, 1.124, 1.135, 1.147, 1.162, 1.181, 1.203, 1.228, 1.262, 1.301, 1.347, 1.377,
+ 1.429, 1.398, 1.348, 1.306, 1.269, 1.237, 1.214, 1.191, 1.173, 1.158, 1.146, 1.138, 1.132, 1.128, 1.125, 1.123, 1.122, 1.123, 1.125, 1.129, 1.136, 1.142, 1.154, 1.166, 1.182, 1.203, 1.226, 1.253, 1.288, 1.329, 1.377, 1.406,
+ 1.465, 1.429, 1.377, 1.335, 1.295, 1.262, 1.236, 1.214, 1.194, 1.179, 1.167, 1.157, 1.151, 1.146, 1.144, 1.142, 1.142, 1.142, 1.144, 1.149, 1.154, 1.163, 1.174, 1.187, 1.205, 1.226, 1.251, 1.279, 1.315, 1.357, 1.406, 1.437,
+ 1.493, 1.465, 1.409, 1.364, 1.323, 1.289, 1.261, 1.235, 1.214, 1.194, 1.179, 1.171, 1.166, 1.163, 1.161, 1.161, 1.161, 1.161, 1.162, 1.164, 1.168, 1.175, 1.187, 1.205, 1.225, 1.251, 1.276, 1.306, 1.344, 1.387, 1.437, 1.455
+ ],
+ "sigma": 0.0007,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.95054, -0.57435, -0.37619,
+ -0.46945, 1.86661, -0.39716,
+ 0.07977, -1.14072, 2.06095
+ ]
+ },
+ {
+ "ct": 2800,
+ "ccm":
+ [
+ 1.94104, -0.60261, -0.33844,
+ -0.43162, 1.85422, -0.42261,
+ 0.03799, -0.95022, 1.91222
+ ]
+ },
+ {
+ "ct": 2900,
+ "ccm":
+ [
+ 1.91828, -0.59569, -0.32258,
+ -0.51902, 2.09091, -0.57189,
+ -0.03324, -0.73462, 1.76785
+ ]
+ },
+ {
+ "ct": 3620,
+ "ccm":
+ [
+ 1.97199, -0.66403, -0.30797,
+ -0.46411, 2.02612, -0.56201,
+ -0.07764, -0.61178, 1.68942
+ ]
+ },
+ {
+ "ct": 4560,
+ "ccm":
+ [
+ 2.15256, -0.84787, -0.30469,
+ -0.48422, 2.28962, -0.80541,
+ -0.15113, -0.53014, 1.68127
+ ]
+ },
+ {
+ "ct": 5600,
+ "ccm":
+ [
+ 2.04576, -0.74771, -0.29805,
+ -0.36332, 1.98993, -0.62662,
+ -0.09328, -0.46543, 1.55871
+ ]
+ },
+ {
+ "ct": 7400,
+ "ccm":
+ [
+ 2.37532, -0.83069, -0.54462,
+ -0.48279, 2.84309, -1.36031,
+ -0.21178, -0.66532, 1.87709
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.06,
+ "strength": 0.5,
+ "limit": 0.5
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "lut_rx":
+ [
+ -0.15, -0.12, -0.08, -0.03, 0.02, 0.06, 0.11, 0.14, 0.22,
+ -0.15, -0.12, -0.08, -0.04, 0.01, 0.05, 0.1, 0.14, 0.21,
+ -0.15, -0.12, -0.08, -0.04, 0.01, 0.06, 0.1, 0.14, 0.21,
+ -0.14, -0.11, -0.08, -0.04, 0.01, 0.05, 0.1, 0.13, 0.2,
+ -0.13, -0.11, -0.08, -0.03, 0.01, 0.05, 0.09, 0.13, 0.2,
+ -0.14, -0.11, -0.07, -0.03, 0.01, 0.06, 0.09, 0.14, 0.21,
+ -0.14, -0.11, -0.07, -0.03, 0.01, 0.05, 0.09, 0.13, 0.21,
+ -0.14, -0.11, -0.07, -0.03, 0.01, 0.05, 0.09, 0.13, 0.2,
+ -0.14, -0.1, -0.07, -0.03, 0.01, 0.06, 0.09, 0.13, 0.2
+ ],
+ "lut_ry":
+ [
+ -0.13, -0.13, -0.12, -0.13, -0.13, -0.14, -0.14, -0.13, -0.13,
+ -0.1, -0.1, -0.1, -0.1, -0.11, -0.11, -0.11, -0.11, -0.1,
+ -0.08, -0.08, -0.09, -0.09, -0.1, -0.09, -0.09, -0.1, -0.09,
+ -0.07, -0.06, -0.06, -0.07, -0.07, -0.07, -0.07, -0.07, -0.09,
+ -0.04, -0.03, -0.04, -0.04, -0.04, -0.04, -0.05, -0.04, -0.06,
+ -0.02, -0.01, -0.01, -0.02, -0.02, -0.02, -0.02, -0.02, -0.03,
+ -0.0, 0.01, 0.0, -0.0, -0.01, -0.01, -0.0, 0.0, -0.0,
+ 0.02, 0.02, 0.02, 0.01, 0.01, 0.01, 0.01, 0.02, 0.02,
+ 0.04, 0.05, 0.04, 0.03, 0.03, 0.03, 0.03, 0.04, 0.04
+ ],
+ "lut_bx":
+ [
+ -0.35, -0.28, -0.22, -0.13, -0.05, 0.02, 0.1, 0.16, 0.28,
+ -0.32, -0.25, -0.19, -0.12, -0.05, 0.02, 0.09, 0.16, 0.28,
+ -0.32, -0.26, -0.19, -0.12, -0.05, 0.02, 0.09, 0.15, 0.28,
+ -0.32, -0.25, -0.19, -0.11, -0.05, 0.02, 0.09, 0.16, 0.28,
+ -0.3, -0.25, -0.19, -0.11, -0.04, 0.02, 0.09, 0.16, 0.28,
+ -0.3, -0.25, -0.18, -0.11, -0.05, 0.02, 0.09, 0.15, 0.28,
+ -0.3, -0.25, -0.19, -0.11, -0.05, 0.02, 0.09, 0.15, 0.27,
+ -0.3, -0.24, -0.17, -0.11, -0.04, 0.02, 0.09, 0.15, 0.27,
+ -0.27, -0.21, -0.15, -0.09, -0.03, 0.03, 0.09, 0.15, 0.27
+ ],
+ "lut_by":
+ [
+ -0.23, -0.22, -0.22, -0.21, -0.21, -0.21, -0.21, -0.21, -0.23,
+ -0.19, -0.17, -0.17, -0.17, -0.17, -0.17, -0.17, -0.17, -0.19,
+ -0.16, -0.13, -0.13, -0.13, -0.12, -0.13, -0.12, -0.13, -0.15,
+ -0.11, -0.08, -0.08, -0.08, -0.07, -0.08, -0.08, -0.08, -0.1,
+ -0.07, -0.04, -0.04, -0.04, -0.03, -0.03, -0.04, -0.04, -0.07,
+ -0.02, 0.01, 0.01, 0.01, 0.02, 0.02, 0.01, 0.01, -0.02,
+ 0.03, 0.07, 0.07, 0.07, 0.07, 0.07, 0.06, 0.06, 0.05,
+ 0.09, 0.1, 0.1, 0.1, 0.12, 0.12, 0.11, 0.11, 0.09,
+ 0.13, 0.13, 0.13, 0.14, 0.18, 0.2, 0.19, 0.18, 0.16
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx296_6mm.json b/src/ipa/rpi/pisp/data/imx296_6mm.json
new file mode 100644
index 00000000..abbcaa83
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx296_6mm.json
@@ -0,0 +1,1247 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 4724,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 860,
+ "reference_Y": 14551
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.751
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 226,
+ "slope": 0.01032
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2875.0, 0.4699, 0.3209,
+ 3610.0, 0.4089, 0.4265,
+ 4640.0, 0.3281, 0.5417,
+ 5912.0, 0.2992, 0.5771,
+ 7630.0, 0.2285, 0.6524
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01783,
+ "transverse_neg": 0.02154
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.084, 2.084, 2.085, 2.085, 2.085, 2.087, 2.088, 2.087, 2.086, 2.082, 2.082, 2.084, 2.086, 2.088, 2.088, 2.088, 2.087, 2.088, 2.088, 2.091, 2.092, 2.093, 2.093, 2.093, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.091, 2.088,
+ 2.086, 2.086, 2.087, 2.088, 2.089, 2.089, 2.091, 2.089, 2.087, 2.086, 2.087, 2.088, 2.091, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.093, 2.093, 2.094, 2.095, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.093,
+ 2.087, 2.087, 2.088, 2.091, 2.091, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.096,
+ 2.089, 2.088, 2.089, 2.091, 2.091, 2.092, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.092, 2.092, 2.092, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.096, 2.096, 2.096, 2.096, 2.097, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.091, 2.091, 2.089, 2.088, 2.088, 2.089, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.094, 2.095, 2.095, 2.096, 2.096, 2.097, 2.099, 2.098, 2.097, 2.097, 2.097,
+ 2.091, 2.091, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.099, 2.098, 2.097,
+ 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.095, 2.096, 2.096, 2.097, 2.097, 2.099, 2.099, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.094, 2.095, 2.096, 2.097, 2.098, 2.098, 2.098, 2.101, 2.101, 2.099, 2.098,
+ 2.092, 2.092, 2.093, 2.093, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.089, 2.089, 2.091, 2.092, 2.092, 2.092, 2.092, 2.094, 2.096, 2.096, 2.097, 2.098, 2.099, 2.099, 2.099, 2.099, 2.099, 2.097,
+ 2.093, 2.094, 2.094, 2.094, 2.095, 2.093, 2.092, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.094, 2.096, 2.096, 2.097, 2.098, 2.098, 2.101, 2.101, 2.099, 2.099, 2.099,
+ 2.094, 2.094, 2.094, 2.095, 2.095, 2.095, 2.091, 2.089, 2.091, 2.089, 2.089, 2.089, 2.091, 2.091, 2.089, 2.091, 2.091, 2.091, 2.092, 2.092, 2.093, 2.093, 2.095, 2.096, 2.097, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.099,
+ 2.095, 2.094, 2.094, 2.095, 2.096, 2.095, 2.091, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.093, 2.094, 2.096, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.101, 2.099,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.095, 2.092, 2.089, 2.089, 2.088, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.093, 2.093, 2.095, 2.096, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.101,
+ 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.092, 2.091, 2.089, 2.089, 2.089, 2.089, 2.091, 2.091, 2.091, 2.093, 2.093, 2.093, 2.092, 2.092, 2.094, 2.094, 2.094, 2.096, 2.096, 2.098, 2.099, 2.102, 2.103, 2.103, 2.102, 2.102,
+ 2.095, 2.095, 2.095, 2.096, 2.096, 2.094, 2.093, 2.091, 2.091, 2.089, 2.089, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.092, 2.093, 2.094, 2.094, 2.095, 2.096, 2.097, 2.098, 2.099, 2.103, 2.103, 2.103, 2.101, 2.101,
+ 2.095, 2.096, 2.096, 2.097, 2.096, 2.095, 2.093, 2.092, 2.091, 2.091, 2.091, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.096, 2.097, 2.099, 2.101, 2.103, 2.103, 2.103, 2.101, 2.099,
+ 2.096, 2.096, 2.097, 2.096, 2.097, 2.096, 2.094, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.099, 2.101, 2.103, 2.103, 2.102, 2.101, 2.101,
+ 2.096, 2.096, 2.097, 2.097, 2.097, 2.096, 2.094, 2.093, 2.092, 2.092, 2.091, 2.091, 2.092, 2.092, 2.092, 2.093, 2.093, 2.094, 2.093, 2.093, 2.094, 2.095, 2.096, 2.097, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101,
+ 2.097, 2.096, 2.097, 2.097, 2.097, 2.097, 2.095, 2.093, 2.093, 2.093, 2.093, 2.092, 2.091, 2.091, 2.092, 2.092, 2.093, 2.094, 2.093, 2.093, 2.093, 2.095, 2.096, 2.097, 2.099, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.101,
+ 2.098, 2.097, 2.096, 2.097, 2.097, 2.097, 2.095, 2.094, 2.094, 2.094, 2.092, 2.092, 2.092, 2.092, 2.092, 2.092, 2.094, 2.095, 2.095, 2.094, 2.093, 2.095, 2.096, 2.099, 2.101, 2.101, 2.102, 2.102, 2.102, 2.101, 2.101, 2.102,
+ 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.095, 2.094, 2.095, 2.093, 2.093, 2.092, 2.092, 2.092, 2.094, 2.094, 2.096, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.099, 2.099, 2.101, 2.102, 2.103, 2.102, 2.102, 2.101, 2.102,
+ 2.098, 2.097, 2.097, 2.098, 2.097, 2.096, 2.095, 2.095, 2.095, 2.094, 2.093, 2.093, 2.094, 2.094, 2.094, 2.095, 2.096, 2.096, 2.096, 2.095, 2.097, 2.097, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.104, 2.103, 2.102, 2.101,
+ 2.099, 2.098, 2.098, 2.098, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.094, 2.094, 2.094, 2.094, 2.096, 2.097, 2.097, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.101, 2.101, 2.104, 2.105, 2.105, 2.103, 2.102,
+ 2.101, 2.099, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.096, 2.096, 2.095, 2.095, 2.095, 2.095, 2.095, 2.096, 2.098, 2.098, 2.097, 2.097, 2.098, 2.099, 2.101, 2.101, 2.102, 2.103, 2.104, 2.105, 2.105, 2.104, 2.103,
+ 2.102, 2.102, 2.099, 2.098, 2.099, 2.099, 2.099, 2.098, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.104, 2.105, 2.106, 2.106, 2.106, 2.104, 2.104, 2.104,
+ 2.102, 2.101, 2.099, 2.099, 2.099, 2.101, 2.101, 2.101, 2.099, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.098, 2.099, 2.099, 2.099, 2.099, 2.101, 2.101, 2.102, 2.103, 2.105, 2.106, 2.106, 2.106, 2.106, 2.105, 2.104, 2.104,
+ 2.099, 2.099, 2.099, 2.098, 2.098, 2.099, 2.101, 2.101, 2.099, 2.098, 2.097, 2.098, 2.098, 2.099, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.105, 2.105, 2.106, 2.106, 2.104, 2.104, 2.103,
+ 2.096, 2.097, 2.097, 2.097, 2.097, 2.099, 2.099, 2.099, 2.099, 2.097, 2.097, 2.098, 2.098, 2.099, 2.098, 2.097, 2.097, 2.099, 2.101, 2.101, 2.101, 2.101, 2.101, 2.103, 2.105, 2.105, 2.105, 2.104, 2.104, 2.103, 2.101, 2.101,
+ 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.098, 2.099, 2.097, 2.096, 2.096, 2.097, 2.098, 2.098, 2.097, 2.097, 2.096, 2.098, 2.098, 2.099, 2.101, 2.101, 2.101, 2.102, 2.104, 2.105, 2.104, 2.104, 2.103, 2.101, 2.099, 2.098,
+ 2.096, 2.096, 2.096, 2.096, 2.097, 2.097, 2.097, 2.097, 2.097, 2.097, 2.096, 2.097, 2.098, 2.097, 2.097, 2.096, 2.096, 2.098, 2.098, 2.098, 2.099, 2.099, 2.101, 2.101, 2.103, 2.103, 2.104, 2.104, 2.102, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.096, 2.098, 2.098, 2.098, 2.098, 2.097, 2.098, 2.097, 2.097, 2.097, 2.097, 2.096, 2.096, 2.096, 2.097, 2.097, 2.098, 2.099, 2.099, 2.099, 2.101, 2.102, 2.103, 2.104, 2.104, 2.104, 2.101, 2.099, 2.098,
+ 2.097, 2.096, 2.095, 2.097, 2.099, 2.099, 2.099, 2.099, 2.099, 2.099, 2.098, 2.098, 2.097, 2.096, 2.096, 2.097, 2.097, 2.098, 2.097, 2.099, 2.101, 2.099, 2.099, 2.099, 2.102, 2.102, 2.104, 2.105, 2.105, 2.102, 2.099, 2.098
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.431, 3.437, 3.439, 3.439, 3.436, 3.438, 3.441, 3.441, 3.441, 3.441, 3.442, 3.443, 3.443, 3.444, 3.446, 3.448, 3.451, 3.451, 3.452, 3.451, 3.449, 3.449, 3.452, 3.453, 3.454, 3.454, 3.453, 3.456, 3.456, 3.456, 3.451, 3.448,
+ 3.445, 3.446, 3.445, 3.449, 3.453, 3.451, 3.451, 3.446, 3.447, 3.446, 3.447, 3.451, 3.453, 3.455, 3.454, 3.453, 3.453, 3.454, 3.455, 3.456, 3.457, 3.459, 3.461, 3.462, 3.463, 3.463, 3.465, 3.466, 3.467, 3.465, 3.459, 3.457,
+ 3.449, 3.449, 3.449, 3.454, 3.455, 3.454, 3.453, 3.451, 3.451, 3.448, 3.451, 3.451, 3.455, 3.456, 3.457, 3.456, 3.456, 3.458, 3.457, 3.459, 3.459, 3.461, 3.464, 3.467, 3.467, 3.466, 3.468, 3.469, 3.471, 3.468, 3.465, 3.462,
+ 3.451, 3.448, 3.451, 3.453, 3.457, 3.455, 3.454, 3.449, 3.449, 3.448, 3.449, 3.449, 3.455, 3.455, 3.456, 3.455, 3.454, 3.455, 3.455, 3.457, 3.458, 3.458, 3.461, 3.464, 3.466, 3.468, 3.469, 3.469, 3.469, 3.468, 3.465, 3.463,
+ 3.449, 3.449, 3.451, 3.453, 3.456, 3.455, 3.452, 3.449, 3.448, 3.447, 3.446, 3.448, 3.451, 3.452, 3.454, 3.455, 3.455, 3.454, 3.457, 3.458, 3.458, 3.459, 3.461, 3.464, 3.464, 3.466, 3.467, 3.469, 3.469, 3.467, 3.463, 3.459,
+ 3.449, 3.451, 3.452, 3.454, 3.455, 3.454, 3.452, 3.449, 3.447, 3.447, 3.446, 3.449, 3.449, 3.451, 3.452, 3.452, 3.452, 3.452, 3.454, 3.455, 3.457, 3.459, 3.461, 3.464, 3.464, 3.466, 3.465, 3.468, 3.468, 3.469, 3.465, 3.462,
+ 3.451, 3.451, 3.452, 3.453, 3.453, 3.453, 3.451, 3.449, 3.449, 3.447, 3.446, 3.447, 3.448, 3.451, 3.451, 3.451, 3.453, 3.452, 3.452, 3.452, 3.457, 3.458, 3.461, 3.463, 3.464, 3.465, 3.464, 3.466, 3.468, 3.469, 3.466, 3.463,
+ 3.451, 3.451, 3.451, 3.454, 3.453, 3.453, 3.451, 3.448, 3.448, 3.444, 3.444, 3.444, 3.448, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.452, 3.454, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.467, 3.468, 3.469, 3.466, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.454, 3.453, 3.449, 3.448, 3.447, 3.447, 3.444, 3.446, 3.446, 3.446, 3.446, 3.447, 3.449, 3.449, 3.451, 3.452, 3.455, 3.457, 3.461, 3.462, 3.464, 3.466, 3.466, 3.468, 3.469, 3.468, 3.465, 3.462,
+ 3.453, 3.452, 3.454, 3.456, 3.455, 3.453, 3.449, 3.447, 3.446, 3.446, 3.445, 3.448, 3.447, 3.446, 3.445, 3.446, 3.448, 3.448, 3.449, 3.453, 3.455, 3.457, 3.459, 3.461, 3.464, 3.466, 3.467, 3.468, 3.468, 3.467, 3.465, 3.463,
+ 3.453, 3.453, 3.454, 3.456, 3.456, 3.451, 3.448, 3.447, 3.447, 3.446, 3.445, 3.446, 3.446, 3.446, 3.446, 3.446, 3.448, 3.448, 3.449, 3.452, 3.454, 3.456, 3.459, 3.459, 3.461, 3.465, 3.466, 3.468, 3.468, 3.468, 3.467, 3.465,
+ 3.451, 3.451, 3.452, 3.455, 3.456, 3.452, 3.448, 3.446, 3.446, 3.444, 3.446, 3.445, 3.446, 3.446, 3.447, 3.448, 3.449, 3.449, 3.449, 3.452, 3.453, 3.454, 3.458, 3.458, 3.461, 3.461, 3.464, 3.469, 3.469, 3.468, 3.466, 3.466,
+ 3.452, 3.452, 3.453, 3.454, 3.454, 3.453, 3.447, 3.446, 3.444, 3.444, 3.444, 3.444, 3.445, 3.446, 3.448, 3.451, 3.452, 3.453, 3.451, 3.453, 3.453, 3.455, 3.458, 3.459, 3.461, 3.462, 3.463, 3.468, 3.471, 3.469, 3.467, 3.467,
+ 3.454, 3.455, 3.457, 3.458, 3.458, 3.455, 3.449, 3.446, 3.445, 3.445, 3.445, 3.445, 3.447, 3.447, 3.448, 3.451, 3.452, 3.453, 3.452, 3.452, 3.452, 3.454, 3.457, 3.459, 3.459, 3.462, 3.464, 3.468, 3.469, 3.467, 3.465, 3.465,
+ 3.457, 3.455, 3.455, 3.459, 3.458, 3.454, 3.451, 3.448, 3.445, 3.445, 3.445, 3.446, 3.448, 3.449, 3.451, 3.452, 3.451, 3.453, 3.452, 3.452, 3.453, 3.457, 3.457, 3.461, 3.461, 3.463, 3.465, 3.468, 3.471, 3.468, 3.465, 3.463,
+ 3.458, 3.456, 3.456, 3.459, 3.457, 3.454, 3.452, 3.449, 3.447, 3.445, 3.446, 3.447, 3.447, 3.448, 3.449, 3.448, 3.449, 3.451, 3.451, 3.451, 3.451, 3.455, 3.456, 3.458, 3.462, 3.463, 3.464, 3.465, 3.467, 3.466, 3.464, 3.462,
+ 3.457, 3.456, 3.455, 3.457, 3.457, 3.454, 3.449, 3.447, 3.445, 3.445, 3.446, 3.446, 3.448, 3.446, 3.448, 3.449, 3.449, 3.451, 3.451, 3.451, 3.453, 3.455, 3.457, 3.459, 3.462, 3.464, 3.464, 3.465, 3.467, 3.464, 3.464, 3.463,
+ 3.458, 3.457, 3.455, 3.456, 3.456, 3.456, 3.453, 3.449, 3.447, 3.448, 3.447, 3.447, 3.447, 3.447, 3.447, 3.448, 3.449, 3.451, 3.451, 3.452, 3.453, 3.455, 3.458, 3.459, 3.459, 3.463, 3.464, 3.463, 3.464, 3.463, 3.464, 3.464,
+ 3.457, 3.456, 3.456, 3.456, 3.456, 3.456, 3.455, 3.449, 3.447, 3.448, 3.451, 3.449, 3.449, 3.449, 3.448, 3.449, 3.449, 3.451, 3.451, 3.452, 3.453, 3.456, 3.458, 3.459, 3.461, 3.462, 3.464, 3.464, 3.465, 3.464, 3.464, 3.463,
+ 3.457, 3.456, 3.455, 3.455, 3.455, 3.455, 3.453, 3.451, 3.449, 3.448, 3.448, 3.449, 3.449, 3.449, 3.448, 3.449, 3.451, 3.452, 3.452, 3.453, 3.454, 3.457, 3.458, 3.459, 3.462, 3.464, 3.465, 3.464, 3.465, 3.464, 3.463, 3.463,
+ 3.456, 3.456, 3.454, 3.453, 3.454, 3.453, 3.452, 3.451, 3.449, 3.448, 3.448, 3.449, 3.451, 3.451, 3.448, 3.449, 3.451, 3.454, 3.454, 3.454, 3.455, 3.457, 3.458, 3.461, 3.461, 3.462, 3.464, 3.464, 3.466, 3.465, 3.464, 3.464,
+ 3.459, 3.457, 3.456, 3.455, 3.454, 3.453, 3.453, 3.452, 3.452, 3.451, 3.449, 3.449, 3.449, 3.448, 3.447, 3.449, 3.451, 3.454, 3.455, 3.455, 3.456, 3.458, 3.459, 3.461, 3.461, 3.462, 3.463, 3.466, 3.469, 3.465, 3.465, 3.464,
+ 3.463, 3.461, 3.458, 3.458, 3.457, 3.456, 3.456, 3.454, 3.454, 3.452, 3.452, 3.451, 3.451, 3.449, 3.448, 3.448, 3.452, 3.454, 3.456, 3.455, 3.457, 3.458, 3.461, 3.464, 3.462, 3.461, 3.463, 3.466, 3.469, 3.469, 3.467, 3.467,
+ 3.466, 3.462, 3.461, 3.461, 3.459, 3.457, 3.457, 3.457, 3.456, 3.454, 3.455, 3.455, 3.455, 3.451, 3.452, 3.453, 3.454, 3.455, 3.456, 3.456, 3.459, 3.462, 3.463, 3.466, 3.466, 3.467, 3.466, 3.469, 3.471, 3.469, 3.468, 3.466,
+ 3.467, 3.463, 3.463, 3.459, 3.461, 3.459, 3.461, 3.459, 3.458, 3.456, 3.457, 3.456, 3.457, 3.455, 3.456, 3.455, 3.456, 3.457, 3.459, 3.461, 3.461, 3.464, 3.465, 3.468, 3.469, 3.469, 3.469, 3.469, 3.471, 3.468, 3.467, 3.468,
+ 3.467, 3.464, 3.459, 3.459, 3.462, 3.462, 3.462, 3.461, 3.461, 3.462, 3.461, 3.459, 3.461, 3.459, 3.458, 3.457, 3.459, 3.461, 3.462, 3.463, 3.464, 3.466, 3.468, 3.469, 3.471, 3.469, 3.471, 3.472, 3.471, 3.467, 3.466, 3.464,
+ 3.464, 3.462, 3.458, 3.457, 3.458, 3.461, 3.461, 3.461, 3.461, 3.462, 3.462, 3.461, 3.461, 3.459, 3.459, 3.459, 3.461, 3.461, 3.464, 3.465, 3.465, 3.468, 3.468, 3.469, 3.471, 3.469, 3.469, 3.469, 3.469, 3.464, 3.462, 3.459,
+ 3.457, 3.458, 3.455, 3.456, 3.456, 3.457, 3.459, 3.459, 3.459, 3.459, 3.458, 3.456, 3.458, 3.457, 3.458, 3.458, 3.458, 3.459, 3.461, 3.463, 3.465, 3.466, 3.468, 3.469, 3.471, 3.468, 3.466, 3.466, 3.465, 3.461, 3.459, 3.457,
+ 3.456, 3.455, 3.454, 3.454, 3.455, 3.456, 3.458, 3.459, 3.459, 3.456, 3.456, 3.456, 3.455, 3.456, 3.455, 3.455, 3.455, 3.454, 3.457, 3.461, 3.462, 3.464, 3.465, 3.467, 3.467, 3.466, 3.464, 3.464, 3.463, 3.461, 3.457, 3.456,
+ 3.456, 3.454, 3.453, 3.454, 3.454, 3.455, 3.458, 3.459, 3.459, 3.456, 3.455, 3.455, 3.455, 3.451, 3.453, 3.454, 3.454, 3.455, 3.455, 3.458, 3.461, 3.462, 3.461, 3.463, 3.465, 3.464, 3.463, 3.463, 3.462, 3.459, 3.456, 3.451,
+ 3.455, 3.452, 3.452, 3.452, 3.455, 3.457, 3.459, 3.459, 3.459, 3.458, 3.456, 3.456, 3.455, 3.453, 3.453, 3.455, 3.457, 3.457, 3.457, 3.461, 3.461, 3.461, 3.459, 3.462, 3.464, 3.464, 3.464, 3.463, 3.463, 3.459, 3.454, 3.451,
+ 3.452, 3.452, 3.452, 3.453, 3.457, 3.458, 3.458, 3.459, 3.459, 3.458, 3.457, 3.457, 3.455, 3.455, 3.458, 3.459, 3.458, 3.459, 3.459, 3.461, 3.461, 3.461, 3.459, 3.461, 3.463, 3.464, 3.466, 3.463, 3.461, 3.458, 3.453, 3.449
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.403, 3.399, 3.395, 3.391, 3.392, 3.394, 3.401, 3.403, 3.404, 3.404, 3.403, 3.399, 3.398, 3.396, 3.395, 3.396, 3.399, 3.403, 3.404, 3.401, 3.399, 3.398, 3.397, 3.401, 3.401, 3.401, 3.396, 3.394, 3.397, 3.396, 3.388, 3.364,
+ 3.403, 3.399, 3.393, 3.389, 3.391, 3.395, 3.401, 3.404, 3.406, 3.404, 3.403, 3.399, 3.399, 3.397, 3.397, 3.397, 3.401, 3.404, 3.404, 3.402, 3.398, 3.396, 3.397, 3.401, 3.401, 3.401, 3.395, 3.394, 3.396, 3.393, 3.387, 3.364,
+ 3.399, 3.398, 3.391, 3.385, 3.386, 3.395, 3.402, 3.405, 3.405, 3.404, 3.402, 3.399, 3.399, 3.398, 3.398, 3.398, 3.401, 3.404, 3.405, 3.403, 3.399, 3.396, 3.396, 3.398, 3.401, 3.401, 3.398, 3.394, 3.392, 3.389, 3.386, 3.364,
+ 3.398, 3.393, 3.386, 3.382, 3.385, 3.392, 3.399, 3.403, 3.405, 3.404, 3.402, 3.398, 3.398, 3.397, 3.397, 3.398, 3.401, 3.404, 3.405, 3.403, 3.398, 3.394, 3.394, 3.398, 3.401, 3.401, 3.396, 3.392, 3.391, 3.388, 3.383, 3.362,
+ 3.396, 3.391, 3.384, 3.381, 3.384, 3.389, 3.398, 3.402, 3.402, 3.401, 3.399, 3.395, 3.395, 3.395, 3.397, 3.397, 3.401, 3.402, 3.404, 3.403, 3.399, 3.394, 3.393, 3.395, 3.399, 3.399, 3.397, 3.391, 3.388, 3.384, 3.381, 3.363,
+ 3.391, 3.386, 3.382, 3.381, 3.385, 3.389, 3.396, 3.398, 3.399, 3.399, 3.398, 3.395, 3.394, 3.394, 3.395, 3.397, 3.399, 3.401, 3.403, 3.401, 3.398, 3.394, 3.393, 3.393, 3.394, 3.396, 3.395, 3.392, 3.387, 3.382, 3.378, 3.361,
+ 3.389, 3.386, 3.379, 3.379, 3.383, 3.388, 3.394, 3.397, 3.397, 3.397, 3.395, 3.393, 3.393, 3.393, 3.395, 3.395, 3.397, 3.398, 3.401, 3.399, 3.397, 3.395, 3.394, 3.391, 3.393, 3.393, 3.393, 3.389, 3.387, 3.381, 3.374, 3.357,
+ 3.386, 3.383, 3.376, 3.375, 3.381, 3.386, 3.394, 3.396, 3.396, 3.394, 3.392, 3.392, 3.394, 3.394, 3.395, 3.394, 3.396, 3.398, 3.399, 3.397, 3.397, 3.394, 3.393, 3.391, 3.389, 3.391, 3.392, 3.388, 3.386, 3.379, 3.372, 3.355,
+ 3.386, 3.379, 3.373, 3.373, 3.378, 3.384, 3.391, 3.396, 3.395, 3.393, 3.389, 3.391, 3.391, 3.393, 3.394, 3.393, 3.394, 3.396, 3.397, 3.396, 3.393, 3.394, 3.393, 3.392, 3.389, 3.389, 3.389, 3.389, 3.386, 3.378, 3.371, 3.351,
+ 3.379, 3.375, 3.371, 3.371, 3.376, 3.381, 3.388, 3.393, 3.394, 3.391, 3.386, 3.386, 3.388, 3.393, 3.392, 3.392, 3.393, 3.395, 3.394, 3.392, 3.389, 3.391, 3.391, 3.392, 3.389, 3.388, 3.389, 3.389, 3.383, 3.377, 3.369, 3.351,
+ 3.373, 3.371, 3.367, 3.368, 3.373, 3.381, 3.387, 3.389, 3.391, 3.389, 3.385, 3.386, 3.383, 3.389, 3.389, 3.392, 3.392, 3.394, 3.393, 3.389, 3.387, 3.387, 3.388, 3.389, 3.389, 3.388, 3.386, 3.386, 3.382, 3.374, 3.367, 3.345,
+ 3.371, 3.369, 3.365, 3.366, 3.373, 3.379, 3.386, 3.389, 3.391, 3.389, 3.385, 3.384, 3.382, 3.386, 3.387, 3.389, 3.391, 3.392, 3.391, 3.387, 3.385, 3.385, 3.386, 3.388, 3.388, 3.388, 3.386, 3.385, 3.381, 3.373, 3.367, 3.345,
+ 3.367, 3.365, 3.365, 3.366, 3.374, 3.379, 3.384, 3.388, 3.389, 3.387, 3.384, 3.383, 3.383, 3.385, 3.385, 3.386, 3.388, 3.389, 3.388, 3.386, 3.383, 3.382, 3.384, 3.386, 3.387, 3.386, 3.381, 3.381, 3.379, 3.372, 3.364, 3.344,
+ 3.365, 3.363, 3.362, 3.367, 3.375, 3.379, 3.383, 3.384, 3.386, 3.384, 3.381, 3.379, 3.379, 3.383, 3.383, 3.384, 3.385, 3.387, 3.387, 3.385, 3.381, 3.381, 3.382, 3.384, 3.384, 3.385, 3.382, 3.379, 3.374, 3.369, 3.359, 3.343,
+ 3.359, 3.358, 3.361, 3.364, 3.373, 3.381, 3.384, 3.384, 3.385, 3.384, 3.381, 3.377, 3.379, 3.379, 3.382, 3.383, 3.384, 3.386, 3.386, 3.385, 3.381, 3.379, 3.381, 3.382, 3.382, 3.383, 3.379, 3.377, 3.371, 3.364, 3.357, 3.339,
+ 3.357, 3.356, 3.356, 3.362, 3.372, 3.379, 3.384, 3.384, 3.383, 3.381, 3.378, 3.376, 3.377, 3.379, 3.381, 3.382, 3.383, 3.385, 3.385, 3.383, 3.379, 3.379, 3.379, 3.381, 3.381, 3.382, 3.379, 3.372, 3.367, 3.362, 3.354, 3.334,
+ 3.357, 3.354, 3.357, 3.361, 3.372, 3.381, 3.385, 3.385, 3.384, 3.379, 3.376, 3.376, 3.376, 3.379, 3.381, 3.383, 3.383, 3.384, 3.383, 3.379, 3.378, 3.381, 3.379, 3.379, 3.379, 3.379, 3.378, 3.371, 3.363, 3.358, 3.354, 3.332,
+ 3.354, 3.351, 3.354, 3.359, 3.371, 3.379, 3.382, 3.384, 3.381, 3.378, 3.375, 3.374, 3.376, 3.378, 3.381, 3.383, 3.384, 3.382, 3.377, 3.377, 3.376, 3.377, 3.378, 3.378, 3.379, 3.379, 3.376, 3.367, 3.361, 3.357, 3.352, 3.333,
+ 3.352, 3.349, 3.351, 3.357, 3.372, 3.381, 3.383, 3.383, 3.381, 3.376, 3.372, 3.373, 3.375, 3.377, 3.382, 3.384, 3.384, 3.379, 3.376, 3.374, 3.374, 3.375, 3.375, 3.376, 3.377, 3.376, 3.373, 3.366, 3.361, 3.356, 3.347, 3.332,
+ 3.347, 3.346, 3.346, 3.355, 3.371, 3.377, 3.382, 3.381, 3.379, 3.372, 3.371, 3.371, 3.372, 3.375, 3.379, 3.383, 3.384, 3.379, 3.374, 3.373, 3.371, 3.373, 3.374, 3.375, 3.374, 3.374, 3.371, 3.365, 3.359, 3.352, 3.343, 3.331,
+ 3.345, 3.344, 3.345, 3.353, 3.367, 3.374, 3.382, 3.382, 3.376, 3.371, 3.369, 3.368, 3.369, 3.373, 3.377, 3.381, 3.379, 3.376, 3.373, 3.369, 3.368, 3.371, 3.372, 3.373, 3.371, 3.371, 3.369, 3.363, 3.357, 3.349, 3.341, 3.326,
+ 3.343, 3.341, 3.344, 3.351, 3.362, 3.371, 3.376, 3.376, 3.372, 3.369, 3.367, 3.366, 3.367, 3.369, 3.376, 3.378, 3.378, 3.375, 3.371, 3.367, 3.367, 3.368, 3.369, 3.369, 3.369, 3.368, 3.365, 3.361, 3.354, 3.347, 3.338, 3.321,
+ 3.341, 3.339, 3.342, 3.349, 3.359, 3.367, 3.371, 3.372, 3.371, 3.368, 3.366, 3.363, 3.365, 3.368, 3.371, 3.374, 3.376, 3.374, 3.368, 3.365, 3.365, 3.366, 3.368, 3.367, 3.367, 3.363, 3.361, 3.356, 3.352, 3.346, 3.336, 3.317,
+ 3.338, 3.336, 3.338, 3.346, 3.359, 3.364, 3.368, 3.369, 3.367, 3.366, 3.363, 3.362, 3.364, 3.364, 3.367, 3.371, 3.372, 3.369, 3.365, 3.362, 3.362, 3.365, 3.367, 3.367, 3.366, 3.362, 3.357, 3.353, 3.349, 3.342, 3.335, 3.317,
+ 3.334, 3.334, 3.336, 3.346, 3.354, 3.361, 3.365, 3.365, 3.365, 3.362, 3.361, 3.361, 3.362, 3.362, 3.364, 3.366, 3.368, 3.366, 3.361, 3.357, 3.357, 3.359, 3.363, 3.365, 3.363, 3.361, 3.355, 3.351, 3.346, 3.339, 3.336, 3.317,
+ 3.332, 3.332, 3.334, 3.344, 3.354, 3.359, 3.363, 3.365, 3.363, 3.361, 3.359, 3.359, 3.363, 3.363, 3.365, 3.365, 3.367, 3.366, 3.358, 3.356, 3.356, 3.358, 3.362, 3.364, 3.363, 3.359, 3.353, 3.348, 3.345, 3.339, 3.336, 3.315,
+ 3.332, 3.328, 3.331, 3.343, 3.351, 3.357, 3.358, 3.362, 3.361, 3.359, 3.357, 3.357, 3.361, 3.362, 3.364, 3.363, 3.363, 3.359, 3.356, 3.354, 3.354, 3.355, 3.358, 3.359, 3.361, 3.359, 3.351, 3.346, 3.344, 3.339, 3.336, 3.313,
+ 3.324, 3.324, 3.327, 3.334, 3.345, 3.351, 3.354, 3.356, 3.356, 3.354, 3.353, 3.354, 3.357, 3.358, 3.361, 3.358, 3.359, 3.355, 3.352, 3.348, 3.347, 3.351, 3.354, 3.358, 3.359, 3.355, 3.346, 3.343, 3.341, 3.336, 3.331, 3.312,
+ 3.318, 3.319, 3.321, 3.328, 3.337, 3.339, 3.345, 3.348, 3.346, 3.345, 3.347, 3.348, 3.351, 3.354, 3.356, 3.353, 3.354, 3.344, 3.343, 3.343, 3.343, 3.344, 3.347, 3.349, 3.353, 3.346, 3.341, 3.339, 3.331, 3.329, 3.325, 3.311,
+ 3.309, 3.313, 3.317, 3.325, 3.329, 3.332, 3.338, 3.339, 3.341, 3.339, 3.339, 3.342, 3.346, 3.346, 3.351, 3.351, 3.343, 3.338, 3.338, 3.339, 3.339, 3.339, 3.341, 3.341, 3.346, 3.343, 3.339, 3.332, 3.327, 3.326, 3.322, 3.309,
+ 3.305, 3.309, 3.317, 3.325, 3.328, 3.331, 3.334, 3.336, 3.337, 3.336, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.341, 3.336, 3.335, 3.337, 3.339, 3.341, 3.339, 3.339, 3.342, 3.341, 3.337, 3.329, 3.326, 3.325, 3.321, 3.314,
+ 3.302, 3.306, 3.319, 3.325, 3.329, 3.331, 3.334, 3.335, 3.337, 3.337, 3.339, 3.341, 3.344, 3.346, 3.348, 3.347, 3.342, 3.336, 3.336, 3.338, 3.339, 3.341, 3.341, 3.341, 3.339, 3.338, 3.336, 3.331, 3.327, 3.324, 3.321, 3.314
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.726, 1.725, 1.723, 1.721, 1.723, 1.724, 1.724, 1.726, 1.727, 1.728, 1.729, 1.728, 1.725, 1.724, 1.726, 1.726, 1.727, 1.729, 1.727, 1.727, 1.724, 1.725, 1.724, 1.726, 1.725, 1.725, 1.724, 1.724, 1.722, 1.721, 1.719, 1.714,
+ 1.726, 1.724, 1.722, 1.721, 1.722, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.727, 1.725, 1.724, 1.724, 1.725, 1.726, 1.725, 1.724, 1.723, 1.722, 1.721, 1.719, 1.714,
+ 1.724, 1.722, 1.719, 1.719, 1.721, 1.723, 1.726, 1.726, 1.727, 1.727, 1.727, 1.725, 1.726, 1.725, 1.725, 1.725, 1.726, 1.727, 1.728, 1.728, 1.725, 1.724, 1.724, 1.724, 1.726, 1.725, 1.724, 1.722, 1.722, 1.721, 1.719, 1.712,
+ 1.723, 1.721, 1.719, 1.719, 1.719, 1.723, 1.725, 1.726, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.726, 1.726, 1.728, 1.729, 1.728, 1.725, 1.723, 1.723, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.718, 1.711,
+ 1.722, 1.719, 1.719, 1.718, 1.719, 1.722, 1.725, 1.726, 1.726, 1.727, 1.727, 1.726, 1.725, 1.726, 1.726, 1.726, 1.727, 1.727, 1.728, 1.727, 1.726, 1.725, 1.724, 1.725, 1.726, 1.725, 1.724, 1.722, 1.721, 1.719, 1.715, 1.711,
+ 1.721, 1.717, 1.717, 1.716, 1.719, 1.722, 1.724, 1.726, 1.726, 1.727, 1.726, 1.726, 1.726, 1.726, 1.726, 1.727, 1.727, 1.727, 1.727, 1.727, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.722, 1.721, 1.718, 1.715, 1.707,
+ 1.718, 1.717, 1.716, 1.716, 1.718, 1.721, 1.725, 1.726, 1.726, 1.726, 1.725, 1.725, 1.725, 1.725, 1.726, 1.727, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.718, 1.715, 1.709,
+ 1.718, 1.716, 1.716, 1.715, 1.717, 1.721, 1.724, 1.725, 1.726, 1.725, 1.725, 1.724, 1.724, 1.725, 1.726, 1.726, 1.727, 1.727, 1.727, 1.726, 1.726, 1.726, 1.725, 1.723, 1.723, 1.723, 1.722, 1.722, 1.719, 1.718, 1.714, 1.709,
+ 1.718, 1.716, 1.715, 1.715, 1.717, 1.721, 1.723, 1.725, 1.726, 1.725, 1.724, 1.723, 1.724, 1.725, 1.725, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.726, 1.725, 1.724, 1.724, 1.723, 1.722, 1.722, 1.721, 1.717, 1.714, 1.707,
+ 1.717, 1.716, 1.714, 1.714, 1.716, 1.721, 1.723, 1.725, 1.725, 1.725, 1.723, 1.723, 1.724, 1.726, 1.726, 1.726, 1.726, 1.725, 1.726, 1.725, 1.725, 1.725, 1.725, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.706,
+ 1.715, 1.714, 1.714, 1.714, 1.716, 1.719, 1.722, 1.724, 1.725, 1.725, 1.723, 1.723, 1.724, 1.725, 1.725, 1.725, 1.726, 1.725, 1.725, 1.725, 1.724, 1.724, 1.724, 1.725, 1.724, 1.723, 1.722, 1.721, 1.718, 1.716, 1.713, 1.705,
+ 1.714, 1.714, 1.713, 1.714, 1.717, 1.719, 1.722, 1.724, 1.724, 1.724, 1.723, 1.722, 1.723, 1.724, 1.724, 1.724, 1.726, 1.725, 1.726, 1.725, 1.723, 1.723, 1.724, 1.724, 1.724, 1.723, 1.721, 1.719, 1.717, 1.715, 1.713, 1.706,
+ 1.712, 1.712, 1.712, 1.713, 1.718, 1.719, 1.721, 1.723, 1.724, 1.724, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.725, 1.725, 1.725, 1.725, 1.723, 1.722, 1.724, 1.723, 1.723, 1.722, 1.721, 1.719, 1.717, 1.714, 1.711, 1.706,
+ 1.712, 1.711, 1.711, 1.713, 1.717, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.722, 1.723, 1.724, 1.724, 1.724, 1.724, 1.725, 1.725, 1.724, 1.723, 1.722, 1.722, 1.722, 1.723, 1.722, 1.721, 1.718, 1.716, 1.714, 1.711, 1.706,
+ 1.711, 1.709, 1.711, 1.713, 1.716, 1.719, 1.722, 1.724, 1.724, 1.723, 1.722, 1.721, 1.722, 1.724, 1.724, 1.724, 1.723, 1.724, 1.724, 1.724, 1.722, 1.722, 1.722, 1.722, 1.722, 1.721, 1.719, 1.718, 1.714, 1.712, 1.709, 1.702,
+ 1.709, 1.709, 1.709, 1.712, 1.717, 1.719, 1.721, 1.723, 1.723, 1.723, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.724, 1.724, 1.724, 1.724, 1.723, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.716, 1.713, 1.711, 1.709, 1.701,
+ 1.708, 1.707, 1.709, 1.712, 1.716, 1.719, 1.722, 1.723, 1.723, 1.723, 1.721, 1.721, 1.721, 1.722, 1.723, 1.723, 1.723, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.719, 1.714, 1.712, 1.709, 1.708, 1.702,
+ 1.707, 1.707, 1.708, 1.711, 1.716, 1.721, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.722, 1.722, 1.723, 1.723, 1.723, 1.722, 1.722, 1.722, 1.722, 1.721, 1.721, 1.721, 1.721, 1.721, 1.717, 1.714, 1.711, 1.709, 1.707, 1.702,
+ 1.706, 1.706, 1.707, 1.711, 1.714, 1.719, 1.722, 1.722, 1.722, 1.721, 1.719, 1.721, 1.721, 1.722, 1.723, 1.724, 1.723, 1.722, 1.722, 1.721, 1.719, 1.719, 1.721, 1.721, 1.719, 1.719, 1.716, 1.713, 1.711, 1.709, 1.706, 1.701,
+ 1.705, 1.704, 1.706, 1.709, 1.713, 1.718, 1.721, 1.722, 1.721, 1.719, 1.718, 1.719, 1.721, 1.722, 1.723, 1.724, 1.724, 1.721, 1.721, 1.721, 1.719, 1.719, 1.719, 1.719, 1.719, 1.717, 1.715, 1.713, 1.711, 1.707, 1.704, 1.699,
+ 1.703, 1.703, 1.704, 1.709, 1.712, 1.717, 1.719, 1.721, 1.719, 1.718, 1.717, 1.718, 1.719, 1.721, 1.722, 1.723, 1.723, 1.722, 1.719, 1.719, 1.718, 1.719, 1.719, 1.718, 1.717, 1.716, 1.714, 1.712, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.703, 1.704, 1.708, 1.712, 1.715, 1.718, 1.719, 1.719, 1.717, 1.717, 1.717, 1.717, 1.718, 1.721, 1.722, 1.722, 1.721, 1.719, 1.718, 1.717, 1.718, 1.718, 1.717, 1.716, 1.714, 1.714, 1.711, 1.709, 1.706, 1.703, 1.697,
+ 1.702, 1.702, 1.703, 1.706, 1.709, 1.715, 1.717, 1.718, 1.717, 1.717, 1.716, 1.716, 1.717, 1.717, 1.719, 1.721, 1.721, 1.721, 1.719, 1.717, 1.716, 1.717, 1.717, 1.716, 1.714, 1.713, 1.712, 1.711, 1.708, 1.706, 1.702, 1.696,
+ 1.701, 1.701, 1.702, 1.706, 1.709, 1.714, 1.716, 1.717, 1.716, 1.716, 1.716, 1.715, 1.716, 1.716, 1.717, 1.718, 1.719, 1.719, 1.716, 1.715, 1.715, 1.715, 1.715, 1.715, 1.714, 1.713, 1.711, 1.709, 1.708, 1.704, 1.701, 1.695,
+ 1.699, 1.699, 1.702, 1.706, 1.708, 1.712, 1.714, 1.715, 1.715, 1.715, 1.714, 1.715, 1.714, 1.715, 1.716, 1.716, 1.716, 1.716, 1.714, 1.713, 1.713, 1.714, 1.715, 1.714, 1.714, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.695,
+ 1.698, 1.699, 1.701, 1.705, 1.708, 1.711, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.714, 1.715, 1.715, 1.716, 1.716, 1.715, 1.713, 1.713, 1.713, 1.714, 1.714, 1.714, 1.713, 1.712, 1.709, 1.707, 1.706, 1.703, 1.701, 1.696,
+ 1.698, 1.699, 1.701, 1.705, 1.707, 1.711, 1.712, 1.713, 1.713, 1.713, 1.713, 1.714, 1.714, 1.715, 1.715, 1.716, 1.715, 1.714, 1.713, 1.712, 1.712, 1.712, 1.713, 1.713, 1.713, 1.711, 1.709, 1.707, 1.705, 1.703, 1.701, 1.696,
+ 1.698, 1.697, 1.699, 1.702, 1.705, 1.707, 1.711, 1.711, 1.711, 1.711, 1.711, 1.712, 1.712, 1.713, 1.714, 1.714, 1.713, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.711, 1.708, 1.706, 1.704, 1.703, 1.699, 1.696,
+ 1.694, 1.695, 1.697, 1.699, 1.702, 1.705, 1.706, 1.707, 1.707, 1.708, 1.708, 1.708, 1.709, 1.711, 1.711, 1.711, 1.708, 1.708, 1.708, 1.707, 1.707, 1.707, 1.708, 1.708, 1.709, 1.708, 1.706, 1.703, 1.702, 1.701, 1.698, 1.696,
+ 1.692, 1.692, 1.695, 1.698, 1.699, 1.701, 1.704, 1.704, 1.704, 1.704, 1.705, 1.706, 1.707, 1.709, 1.709, 1.707, 1.706, 1.704, 1.704, 1.705, 1.705, 1.706, 1.706, 1.706, 1.706, 1.706, 1.703, 1.702, 1.701, 1.699, 1.696, 1.694,
+ 1.691, 1.692, 1.695, 1.697, 1.699, 1.699, 1.702, 1.703, 1.703, 1.702, 1.703, 1.704, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.704, 1.705, 1.705, 1.705, 1.705, 1.704, 1.703, 1.701, 1.699, 1.698, 1.696, 1.695,
+ 1.689, 1.691, 1.696, 1.698, 1.699, 1.699, 1.701, 1.702, 1.702, 1.702, 1.703, 1.703, 1.706, 1.707, 1.708, 1.706, 1.705, 1.703, 1.703, 1.703, 1.703, 1.704, 1.704, 1.705, 1.704, 1.704, 1.702, 1.701, 1.698, 1.698, 1.696, 1.696
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.425, 1.393, 1.341, 1.295, 1.258, 1.226, 1.201, 1.181, 1.162, 1.146, 1.133, 1.123, 1.115, 1.111, 1.107, 1.106, 1.106, 1.107, 1.108, 1.111, 1.114, 1.122, 1.133, 1.148, 1.164, 1.184, 1.208, 1.236, 1.271, 1.309, 1.359, 1.381,
+ 1.397, 1.367, 1.317, 1.274, 1.237, 1.207, 1.183, 1.163, 1.146, 1.133, 1.123, 1.114, 1.107, 1.101, 1.098, 1.096, 1.096, 1.096, 1.097, 1.102, 1.106, 1.112, 1.122, 1.133, 1.148, 1.166, 1.187, 1.215, 1.249, 1.288, 1.335, 1.359,
+ 1.374, 1.341, 1.292, 1.251, 1.215, 1.186, 1.166, 1.146, 1.131, 1.117, 1.108, 1.099, 1.091, 1.088, 1.084, 1.082, 1.081, 1.082, 1.084, 1.088, 1.093, 1.098, 1.107, 1.118, 1.133, 1.149, 1.169, 1.195, 1.228, 1.267, 1.313, 1.335,
+ 1.352, 1.318, 1.271, 1.231, 1.196, 1.169, 1.149, 1.131, 1.115, 1.103, 1.093, 1.086, 1.079, 1.074, 1.071, 1.069, 1.069, 1.069, 1.071, 1.076, 1.079, 1.085, 1.094, 1.102, 1.117, 1.133, 1.152, 1.176, 1.208, 1.246, 1.289, 1.313,
+ 1.333, 1.298, 1.253, 1.212, 1.179, 1.153, 1.134, 1.116, 1.102, 1.089, 1.079, 1.072, 1.066, 1.062, 1.059, 1.058, 1.057, 1.057, 1.059, 1.064, 1.068, 1.072, 1.081, 1.091, 1.102, 1.119, 1.137, 1.161, 1.191, 1.227, 1.271, 1.293,
+ 1.317, 1.281, 1.235, 1.196, 1.165, 1.139, 1.119, 1.104, 1.089, 1.078, 1.068, 1.062, 1.055, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.053, 1.056, 1.061, 1.069, 1.079, 1.091, 1.105, 1.126, 1.147, 1.177, 1.212, 1.253, 1.278,
+ 1.301, 1.265, 1.221, 1.181, 1.151, 1.127, 1.108, 1.091, 1.078, 1.068, 1.059, 1.051, 1.045, 1.041, 1.038, 1.037, 1.036, 1.037, 1.038, 1.042, 1.046, 1.051, 1.059, 1.069, 1.081, 1.096, 1.113, 1.135, 1.164, 1.198, 1.238, 1.264,
+ 1.286, 1.251, 1.207, 1.169, 1.141, 1.116, 1.098, 1.081, 1.068, 1.058, 1.049, 1.042, 1.037, 1.033, 1.031, 1.029, 1.028, 1.028, 1.029, 1.033, 1.037, 1.043, 1.051, 1.059, 1.071, 1.086, 1.104, 1.124, 1.152, 1.185, 1.225, 1.252,
+ 1.275, 1.239, 1.196, 1.161, 1.132, 1.107, 1.089, 1.073, 1.059, 1.049, 1.041, 1.035, 1.028, 1.024, 1.023, 1.021, 1.021, 1.021, 1.022, 1.024, 1.029, 1.036, 1.043, 1.051, 1.063, 1.078, 1.095, 1.115, 1.143, 1.175, 1.214, 1.243,
+ 1.267, 1.227, 1.187, 1.152, 1.122, 1.101, 1.081, 1.067, 1.054, 1.042, 1.035, 1.028, 1.023, 1.018, 1.015, 1.014, 1.014, 1.014, 1.016, 1.019, 1.024, 1.029, 1.036, 1.045, 1.056, 1.071, 1.088, 1.107, 1.134, 1.167, 1.204, 1.234,
+ 1.261, 1.219, 1.179, 1.145, 1.116, 1.095, 1.076, 1.061, 1.047, 1.037, 1.031, 1.023, 1.018, 1.014, 1.011, 1.009, 1.009, 1.009, 1.011, 1.013, 1.018, 1.024, 1.031, 1.039, 1.049, 1.065, 1.083, 1.102, 1.128, 1.161, 1.196, 1.228,
+ 1.256, 1.213, 1.173, 1.139, 1.111, 1.091, 1.071, 1.056, 1.043, 1.033, 1.026, 1.019, 1.014, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.009, 1.013, 1.018, 1.026, 1.035, 1.046, 1.061, 1.078, 1.097, 1.123, 1.154, 1.191, 1.222,
+ 1.251, 1.208, 1.169, 1.137, 1.108, 1.088, 1.069, 1.053, 1.039, 1.029, 1.023, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.002, 1.003, 1.006, 1.009, 1.015, 1.022, 1.032, 1.044, 1.057, 1.076, 1.094, 1.119, 1.149, 1.186, 1.218,
+ 1.249, 1.205, 1.167, 1.133, 1.107, 1.085, 1.067, 1.052, 1.038, 1.029, 1.021, 1.013, 1.008, 1.004, 1.003, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.013, 1.021, 1.031, 1.042, 1.055, 1.073, 1.093, 1.116, 1.147, 1.182, 1.218,
+ 1.249, 1.204, 1.165, 1.132, 1.106, 1.085, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.007, 1.003, 1.002, 1.001, 1.001, 1.001, 1.001, 1.004, 1.007, 1.013, 1.021, 1.029, 1.042, 1.055, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.249, 1.204, 1.165, 1.132, 1.107, 1.086, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.008, 1.004, 1.002, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.014, 1.021, 1.029, 1.042, 1.056, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.251, 1.205, 1.166, 1.133, 1.108, 1.087, 1.068, 1.052, 1.039, 1.031, 1.021, 1.014, 1.009, 1.006, 1.003, 1.002, 1.001, 1.001, 1.003, 1.006, 1.009, 1.014, 1.022, 1.031, 1.043, 1.056, 1.073, 1.093, 1.116, 1.145, 1.182, 1.218,
+ 1.252, 1.208, 1.168, 1.137, 1.111, 1.089, 1.071, 1.055, 1.043, 1.033, 1.023, 1.016, 1.012, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.008, 1.012, 1.017, 1.024, 1.034, 1.045, 1.059, 1.075, 1.095, 1.119, 1.149, 1.185, 1.218,
+ 1.256, 1.213, 1.173, 1.142, 1.115, 1.093, 1.075, 1.059, 1.047, 1.036, 1.027, 1.021, 1.016, 1.012, 1.011, 1.009, 1.008, 1.008, 1.009, 1.012, 1.016, 1.021, 1.028, 1.038, 1.049, 1.064, 1.081, 1.099, 1.126, 1.155, 1.192, 1.223,
+ 1.261, 1.221, 1.179, 1.148, 1.121, 1.099, 1.081, 1.065, 1.052, 1.042, 1.032, 1.026, 1.021, 1.017, 1.015, 1.014, 1.014, 1.013, 1.013, 1.016, 1.021, 1.026, 1.033, 1.043, 1.054, 1.068, 1.085, 1.106, 1.132, 1.161, 1.199, 1.228,
+ 1.267, 1.228, 1.188, 1.155, 1.128, 1.105, 1.086, 1.071, 1.059, 1.047, 1.038, 1.031, 1.027, 1.022, 1.021, 1.019, 1.019, 1.019, 1.019, 1.022, 1.026, 1.032, 1.038, 1.049, 1.061, 1.075, 1.092, 1.112, 1.138, 1.169, 1.207, 1.236,
+ 1.278, 1.241, 1.199, 1.164, 1.137, 1.114, 1.094, 1.078, 1.066, 1.055, 1.046, 1.038, 1.032, 1.029, 1.027, 1.027, 1.027, 1.027, 1.027, 1.029, 1.032, 1.038, 1.047, 1.056, 1.067, 1.083, 1.099, 1.121, 1.146, 1.178, 1.217, 1.244,
+ 1.291, 1.252, 1.211, 1.175, 1.147, 1.124, 1.103, 1.088, 1.075, 1.063, 1.054, 1.046, 1.041, 1.036, 1.035, 1.035, 1.035, 1.035, 1.036, 1.038, 1.041, 1.047, 1.055, 1.065, 1.075, 1.092, 1.111, 1.132, 1.157, 1.189, 1.231, 1.255,
+ 1.303, 1.265, 1.222, 1.187, 1.158, 1.133, 1.112, 1.097, 1.083, 1.072, 1.063, 1.054, 1.048, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.046, 1.049, 1.055, 1.065, 1.074, 1.086, 1.102, 1.119, 1.144, 1.171, 1.203, 1.243, 1.268,
+ 1.317, 1.282, 1.236, 1.201, 1.171, 1.146, 1.125, 1.109, 1.095, 1.083, 1.072, 1.064, 1.058, 1.054, 1.052, 1.051, 1.051, 1.053, 1.054, 1.057, 1.061, 1.065, 1.074, 1.086, 1.099, 1.113, 1.133, 1.156, 1.183, 1.217, 1.259, 1.282,
+ 1.335, 1.301, 1.254, 1.218, 1.186, 1.159, 1.138, 1.121, 1.108, 1.095, 1.085, 1.076, 1.069, 1.066, 1.065, 1.063, 1.062, 1.063, 1.065, 1.068, 1.073, 1.078, 1.087, 1.098, 1.113, 1.126, 1.146, 1.171, 1.199, 1.235, 1.277, 1.299,
+ 1.356, 1.321, 1.274, 1.235, 1.202, 1.175, 1.153, 1.137, 1.121, 1.108, 1.097, 1.089, 1.084, 1.081, 1.077, 1.075, 1.075, 1.075, 1.077, 1.081, 1.086, 1.091, 1.099, 1.113, 1.126, 1.144, 1.162, 1.187, 1.218, 1.255, 1.297, 1.321,
+ 1.376, 1.344, 1.296, 1.257, 1.223, 1.194, 1.171, 1.153, 1.137, 1.124, 1.112, 1.104, 1.099, 1.095, 1.093, 1.091, 1.089, 1.091, 1.092, 1.095, 1.101, 1.108, 1.116, 1.128, 1.144, 1.161, 1.181, 1.206, 1.237, 1.275, 1.321, 1.347,
+ 1.403, 1.369, 1.319, 1.279, 1.244, 1.214, 1.191, 1.171, 1.154, 1.139, 1.129, 1.121, 1.115, 1.111, 1.109, 1.106, 1.105, 1.105, 1.108, 1.112, 1.117, 1.124, 1.135, 1.147, 1.162, 1.181, 1.203, 1.228, 1.262, 1.301, 1.347, 1.377,
+ 1.429, 1.398, 1.348, 1.306, 1.269, 1.237, 1.214, 1.191, 1.173, 1.158, 1.146, 1.138, 1.132, 1.128, 1.125, 1.123, 1.122, 1.123, 1.125, 1.129, 1.136, 1.142, 1.154, 1.166, 1.182, 1.203, 1.226, 1.253, 1.288, 1.329, 1.377, 1.406,
+ 1.465, 1.429, 1.377, 1.335, 1.295, 1.262, 1.236, 1.214, 1.194, 1.179, 1.167, 1.157, 1.151, 1.146, 1.144, 1.142, 1.142, 1.142, 1.144, 1.149, 1.154, 1.163, 1.174, 1.187, 1.205, 1.226, 1.251, 1.279, 1.315, 1.357, 1.406, 1.437,
+ 1.493, 1.465, 1.409, 1.364, 1.323, 1.289, 1.261, 1.235, 1.214, 1.194, 1.179, 1.171, 1.166, 1.163, 1.161, 1.161, 1.161, 1.161, 1.162, 1.164, 1.168, 1.175, 1.187, 1.205, 1.225, 1.251, 1.276, 1.306, 1.344, 1.387, 1.437, 1.455
+ ],
+ "sigma": 0.0007,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.95054, -0.57435, -0.37619,
+ -0.46945, 1.86661, -0.39716,
+ 0.07977, -1.14072, 2.06095
+ ]
+ },
+ {
+ "ct": 2800,
+ "ccm":
+ [
+ 1.94104, -0.60261, -0.33844,
+ -0.43162, 1.85422, -0.42261,
+ 0.03799, -0.95022, 1.91222
+ ]
+ },
+ {
+ "ct": 2900,
+ "ccm":
+ [
+ 1.91828, -0.59569, -0.32258,
+ -0.51902, 2.09091, -0.57189,
+ -0.03324, -0.73462, 1.76785
+ ]
+ },
+ {
+ "ct": 3620,
+ "ccm":
+ [
+ 1.97199, -0.66403, -0.30797,
+ -0.46411, 2.02612, -0.56201,
+ -0.07764, -0.61178, 1.68942
+ ]
+ },
+ {
+ "ct": 4560,
+ "ccm":
+ [
+ 2.15256, -0.84787, -0.30469,
+ -0.48422, 2.28962, -0.80541,
+ -0.15113, -0.53014, 1.68127
+ ]
+ },
+ {
+ "ct": 5600,
+ "ccm":
+ [
+ 2.04576, -0.74771, -0.29805,
+ -0.36332, 1.98993, -0.62662,
+ -0.09328, -0.46543, 1.55871
+ ]
+ },
+ {
+ "ct": 7400,
+ "ccm":
+ [
+ 2.37532, -0.83069, -0.54462,
+ -0.48279, 2.84309, -1.36031,
+ -0.21178, -0.66532, 1.87709
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.06,
+ "strength": 0.5,
+ "limit": 0.5
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "lut_rx":
+ [
+ -0.28, -0.22, -0.16, -0.09, -0.02, 0.04, 0.11, 0.17, 0.29,
+ -0.28, -0.22, -0.16, -0.09, -0.02, 0.04, 0.11, 0.18, 0.3,
+ -0.28, -0.22, -0.16, -0.09, -0.02, 0.05, 0.11, 0.18, 0.31,
+ -0.28, -0.22, -0.16, -0.09, -0.02, 0.05, 0.12, 0.18, 0.31,
+ -0.27, -0.22, -0.16, -0.09, -0.02, 0.05, 0.12, 0.19, 0.31,
+ -0.27, -0.21, -0.15, -0.08, -0.02, 0.05, 0.12, 0.18, 0.31,
+ -0.27, -0.21, -0.15, -0.08, -0.02, 0.05, 0.11, 0.18, 0.3,
+ -0.25, -0.2, -0.15, -0.09, -0.02, 0.05, 0.11, 0.17, 0.29,
+ -0.24, -0.19, -0.14, -0.08, -0.02, 0.04, 0.11, 0.17, 0.29
+ ],
+ "lut_ry":
+ [
+ -0.19, -0.18, -0.19, -0.19, -0.19, -0.18, -0.19, -0.19, -0.2,
+ -0.14, -0.14, -0.15, -0.16, -0.16, -0.16, -0.16, -0.16, -0.17,
+ -0.11, -0.1, -0.11, -0.12, -0.12, -0.12, -0.12, -0.12, -0.14,
+ -0.06, -0.05, -0.05, -0.06, -0.07, -0.07, -0.06, -0.06, -0.08,
+ -0.01, 0.0, -0.01, -0.01, -0.01, -0.01, -0.01, -0.01, -0.02,
+ 0.04, 0.05, 0.04, 0.03, 0.03, 0.03, 0.03, 0.04, 0.03,
+ 0.07, 0.08, 0.07, 0.07, 0.07, 0.07, 0.08, 0.08, 0.07,
+ 0.1, 0.11, 0.1, 0.1, 0.1, 0.1, 0.1, 0.11, 0.1,
+ 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.15, 0.15, 0.14
+ ],
+ "lut_bx":
+ [
+ -0.21, -0.17, -0.13, -0.06, 0.01, 0.07, 0.13, 0.18, 0.27,
+ -0.21, -0.17, -0.13, -0.06, 0.01, 0.08, 0.14, 0.2, 0.28,
+ -0.22, -0.18, -0.13, -0.06, 0.01, 0.08, 0.15, 0.21, 0.3,
+ -0.22, -0.18, -0.13, -0.06, 0.01, 0.08, 0.15, 0.21, 0.31,
+ -0.21, -0.17, -0.13, -0.07, 0.01, 0.08, 0.15, 0.2, 0.31,
+ -0.2, -0.16, -0.12, -0.06, 0.0, 0.07, 0.14, 0.18, 0.28,
+ -0.19, -0.15, -0.11, -0.06, 0.01, 0.07, 0.13, 0.18, 0.26,
+ -0.17, -0.14, -0.1, -0.05, 0.01, 0.07, 0.12, 0.16, 0.25,
+ -0.15, -0.12, -0.08, -0.04, 0.01, 0.07, 0.1, 0.13, 0.22
+ ],
+ "lut_by":
+ [
+ -0.15, -0.15, -0.17, -0.18, -0.18, -0.18, -0.17, -0.16, -0.14,
+ -0.12, -0.12, -0.13, -0.14, -0.14, -0.14, -0.13, -0.12, -0.11,
+ -0.09, -0.08, -0.09, -0.1, -0.1, -0.09, -0.09, -0.08, -0.09,
+ -0.06, -0.04, -0.04, -0.05, -0.04, -0.04, -0.04, -0.04, -0.06,
+ -0.02, 0.01, 0.01, 0.02, 0.02, 0.02, 0.02, 0.01, -0.02,
+ 0.02, 0.05, 0.07, 0.08, 0.09, 0.09, 0.08, 0.06, 0.02,
+ 0.05, 0.08, 0.1, 0.12, 0.13, 0.13, 0.12, 0.1, 0.06,
+ 0.07, 0.09, 0.11, 0.14, 0.16, 0.16, 0.14, 0.12, 0.07,
+ 0.09, 0.11, 0.14, 0.17, 0.19, 0.19, 0.18, 0.15, 0.1
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx296_mono.json b/src/ipa/rpi/pisp/data/imx296_mono.json
new file mode 100644
index 00000000..153f86a0
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx296_mono.json
@@ -0,0 +1,960 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 4724,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 860,
+ "reference_Y": 14551
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.751
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 226,
+ "slope": 0.01032
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.425, 1.393, 1.341, 1.295, 1.258, 1.226, 1.201, 1.181, 1.162, 1.146, 1.133, 1.123, 1.115, 1.111, 1.107, 1.106, 1.106, 1.107, 1.108, 1.111, 1.114, 1.122, 1.133, 1.148, 1.164, 1.184, 1.208, 1.236, 1.271, 1.309, 1.359, 1.381,
+ 1.397, 1.367, 1.317, 1.274, 1.237, 1.207, 1.183, 1.163, 1.146, 1.133, 1.123, 1.114, 1.107, 1.101, 1.098, 1.096, 1.096, 1.096, 1.097, 1.102, 1.106, 1.112, 1.122, 1.133, 1.148, 1.166, 1.187, 1.215, 1.249, 1.288, 1.335, 1.359,
+ 1.374, 1.341, 1.292, 1.251, 1.215, 1.186, 1.166, 1.146, 1.131, 1.117, 1.108, 1.099, 1.091, 1.088, 1.084, 1.082, 1.081, 1.082, 1.084, 1.088, 1.093, 1.098, 1.107, 1.118, 1.133, 1.149, 1.169, 1.195, 1.228, 1.267, 1.313, 1.335,
+ 1.352, 1.318, 1.271, 1.231, 1.196, 1.169, 1.149, 1.131, 1.115, 1.103, 1.093, 1.086, 1.079, 1.074, 1.071, 1.069, 1.069, 1.069, 1.071, 1.076, 1.079, 1.085, 1.094, 1.102, 1.117, 1.133, 1.152, 1.176, 1.208, 1.246, 1.289, 1.313,
+ 1.333, 1.298, 1.253, 1.212, 1.179, 1.153, 1.134, 1.116, 1.102, 1.089, 1.079, 1.072, 1.066, 1.062, 1.059, 1.058, 1.057, 1.057, 1.059, 1.064, 1.068, 1.072, 1.081, 1.091, 1.102, 1.119, 1.137, 1.161, 1.191, 1.227, 1.271, 1.293,
+ 1.317, 1.281, 1.235, 1.196, 1.165, 1.139, 1.119, 1.104, 1.089, 1.078, 1.068, 1.062, 1.055, 1.051, 1.048, 1.047, 1.047, 1.047, 1.048, 1.053, 1.056, 1.061, 1.069, 1.079, 1.091, 1.105, 1.126, 1.147, 1.177, 1.212, 1.253, 1.278,
+ 1.301, 1.265, 1.221, 1.181, 1.151, 1.127, 1.108, 1.091, 1.078, 1.068, 1.059, 1.051, 1.045, 1.041, 1.038, 1.037, 1.036, 1.037, 1.038, 1.042, 1.046, 1.051, 1.059, 1.069, 1.081, 1.096, 1.113, 1.135, 1.164, 1.198, 1.238, 1.264,
+ 1.286, 1.251, 1.207, 1.169, 1.141, 1.116, 1.098, 1.081, 1.068, 1.058, 1.049, 1.042, 1.037, 1.033, 1.031, 1.029, 1.028, 1.028, 1.029, 1.033, 1.037, 1.043, 1.051, 1.059, 1.071, 1.086, 1.104, 1.124, 1.152, 1.185, 1.225, 1.252,
+ 1.275, 1.239, 1.196, 1.161, 1.132, 1.107, 1.089, 1.073, 1.059, 1.049, 1.041, 1.035, 1.028, 1.024, 1.023, 1.021, 1.021, 1.021, 1.022, 1.024, 1.029, 1.036, 1.043, 1.051, 1.063, 1.078, 1.095, 1.115, 1.143, 1.175, 1.214, 1.243,
+ 1.267, 1.227, 1.187, 1.152, 1.122, 1.101, 1.081, 1.067, 1.054, 1.042, 1.035, 1.028, 1.023, 1.018, 1.015, 1.014, 1.014, 1.014, 1.016, 1.019, 1.024, 1.029, 1.036, 1.045, 1.056, 1.071, 1.088, 1.107, 1.134, 1.167, 1.204, 1.234,
+ 1.261, 1.219, 1.179, 1.145, 1.116, 1.095, 1.076, 1.061, 1.047, 1.037, 1.031, 1.023, 1.018, 1.014, 1.011, 1.009, 1.009, 1.009, 1.011, 1.013, 1.018, 1.024, 1.031, 1.039, 1.049, 1.065, 1.083, 1.102, 1.128, 1.161, 1.196, 1.228,
+ 1.256, 1.213, 1.173, 1.139, 1.111, 1.091, 1.071, 1.056, 1.043, 1.033, 1.026, 1.019, 1.014, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.009, 1.013, 1.018, 1.026, 1.035, 1.046, 1.061, 1.078, 1.097, 1.123, 1.154, 1.191, 1.222,
+ 1.251, 1.208, 1.169, 1.137, 1.108, 1.088, 1.069, 1.053, 1.039, 1.029, 1.023, 1.015, 1.011, 1.006, 1.004, 1.003, 1.001, 1.002, 1.003, 1.006, 1.009, 1.015, 1.022, 1.032, 1.044, 1.057, 1.076, 1.094, 1.119, 1.149, 1.186, 1.218,
+ 1.249, 1.205, 1.167, 1.133, 1.107, 1.085, 1.067, 1.052, 1.038, 1.029, 1.021, 1.013, 1.008, 1.004, 1.003, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.013, 1.021, 1.031, 1.042, 1.055, 1.073, 1.093, 1.116, 1.147, 1.182, 1.218,
+ 1.249, 1.204, 1.165, 1.132, 1.106, 1.085, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.007, 1.003, 1.002, 1.001, 1.001, 1.001, 1.001, 1.004, 1.007, 1.013, 1.021, 1.029, 1.042, 1.055, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.249, 1.204, 1.165, 1.132, 1.107, 1.086, 1.067, 1.051, 1.038, 1.029, 1.019, 1.013, 1.008, 1.004, 1.002, 1.001, 1.001, 1.001, 1.002, 1.004, 1.007, 1.014, 1.021, 1.029, 1.042, 1.056, 1.072, 1.091, 1.115, 1.145, 1.181, 1.217,
+ 1.251, 1.205, 1.166, 1.133, 1.108, 1.087, 1.068, 1.052, 1.039, 1.031, 1.021, 1.014, 1.009, 1.006, 1.003, 1.002, 1.001, 1.001, 1.003, 1.006, 1.009, 1.014, 1.022, 1.031, 1.043, 1.056, 1.073, 1.093, 1.116, 1.145, 1.182, 1.218,
+ 1.252, 1.208, 1.168, 1.137, 1.111, 1.089, 1.071, 1.055, 1.043, 1.033, 1.023, 1.016, 1.012, 1.009, 1.006, 1.005, 1.004, 1.004, 1.006, 1.008, 1.012, 1.017, 1.024, 1.034, 1.045, 1.059, 1.075, 1.095, 1.119, 1.149, 1.185, 1.218,
+ 1.256, 1.213, 1.173, 1.142, 1.115, 1.093, 1.075, 1.059, 1.047, 1.036, 1.027, 1.021, 1.016, 1.012, 1.011, 1.009, 1.008, 1.008, 1.009, 1.012, 1.016, 1.021, 1.028, 1.038, 1.049, 1.064, 1.081, 1.099, 1.126, 1.155, 1.192, 1.223,
+ 1.261, 1.221, 1.179, 1.148, 1.121, 1.099, 1.081, 1.065, 1.052, 1.042, 1.032, 1.026, 1.021, 1.017, 1.015, 1.014, 1.014, 1.013, 1.013, 1.016, 1.021, 1.026, 1.033, 1.043, 1.054, 1.068, 1.085, 1.106, 1.132, 1.161, 1.199, 1.228,
+ 1.267, 1.228, 1.188, 1.155, 1.128, 1.105, 1.086, 1.071, 1.059, 1.047, 1.038, 1.031, 1.027, 1.022, 1.021, 1.019, 1.019, 1.019, 1.019, 1.022, 1.026, 1.032, 1.038, 1.049, 1.061, 1.075, 1.092, 1.112, 1.138, 1.169, 1.207, 1.236,
+ 1.278, 1.241, 1.199, 1.164, 1.137, 1.114, 1.094, 1.078, 1.066, 1.055, 1.046, 1.038, 1.032, 1.029, 1.027, 1.027, 1.027, 1.027, 1.027, 1.029, 1.032, 1.038, 1.047, 1.056, 1.067, 1.083, 1.099, 1.121, 1.146, 1.178, 1.217, 1.244,
+ 1.291, 1.252, 1.211, 1.175, 1.147, 1.124, 1.103, 1.088, 1.075, 1.063, 1.054, 1.046, 1.041, 1.036, 1.035, 1.035, 1.035, 1.035, 1.036, 1.038, 1.041, 1.047, 1.055, 1.065, 1.075, 1.092, 1.111, 1.132, 1.157, 1.189, 1.231, 1.255,
+ 1.303, 1.265, 1.222, 1.187, 1.158, 1.133, 1.112, 1.097, 1.083, 1.072, 1.063, 1.054, 1.048, 1.043, 1.043, 1.043, 1.043, 1.043, 1.043, 1.046, 1.049, 1.055, 1.065, 1.074, 1.086, 1.102, 1.119, 1.144, 1.171, 1.203, 1.243, 1.268,
+ 1.317, 1.282, 1.236, 1.201, 1.171, 1.146, 1.125, 1.109, 1.095, 1.083, 1.072, 1.064, 1.058, 1.054, 1.052, 1.051, 1.051, 1.053, 1.054, 1.057, 1.061, 1.065, 1.074, 1.086, 1.099, 1.113, 1.133, 1.156, 1.183, 1.217, 1.259, 1.282,
+ 1.335, 1.301, 1.254, 1.218, 1.186, 1.159, 1.138, 1.121, 1.108, 1.095, 1.085, 1.076, 1.069, 1.066, 1.065, 1.063, 1.062, 1.063, 1.065, 1.068, 1.073, 1.078, 1.087, 1.098, 1.113, 1.126, 1.146, 1.171, 1.199, 1.235, 1.277, 1.299,
+ 1.356, 1.321, 1.274, 1.235, 1.202, 1.175, 1.153, 1.137, 1.121, 1.108, 1.097, 1.089, 1.084, 1.081, 1.077, 1.075, 1.075, 1.075, 1.077, 1.081, 1.086, 1.091, 1.099, 1.113, 1.126, 1.144, 1.162, 1.187, 1.218, 1.255, 1.297, 1.321,
+ 1.376, 1.344, 1.296, 1.257, 1.223, 1.194, 1.171, 1.153, 1.137, 1.124, 1.112, 1.104, 1.099, 1.095, 1.093, 1.091, 1.089, 1.091, 1.092, 1.095, 1.101, 1.108, 1.116, 1.128, 1.144, 1.161, 1.181, 1.206, 1.237, 1.275, 1.321, 1.347,
+ 1.403, 1.369, 1.319, 1.279, 1.244, 1.214, 1.191, 1.171, 1.154, 1.139, 1.129, 1.121, 1.115, 1.111, 1.109, 1.106, 1.105, 1.105, 1.108, 1.112, 1.117, 1.124, 1.135, 1.147, 1.162, 1.181, 1.203, 1.228, 1.262, 1.301, 1.347, 1.377,
+ 1.429, 1.398, 1.348, 1.306, 1.269, 1.237, 1.214, 1.191, 1.173, 1.158, 1.146, 1.138, 1.132, 1.128, 1.125, 1.123, 1.122, 1.123, 1.125, 1.129, 1.136, 1.142, 1.154, 1.166, 1.182, 1.203, 1.226, 1.253, 1.288, 1.329, 1.377, 1.406,
+ 1.465, 1.429, 1.377, 1.335, 1.295, 1.262, 1.236, 1.214, 1.194, 1.179, 1.167, 1.157, 1.151, 1.146, 1.144, 1.142, 1.142, 1.142, 1.144, 1.149, 1.154, 1.163, 1.174, 1.187, 1.205, 1.226, 1.251, 1.279, 1.315, 1.357, 1.406, 1.437,
+ 1.493, 1.465, 1.409, 1.364, 1.323, 1.289, 1.261, 1.235, 1.214, 1.194, 1.179, 1.171, 1.166, 1.163, 1.161, 1.161, 1.161, 1.161, 1.162, 1.164, 1.168, 1.175, 1.187, 1.205, 1.225, 1.251, 1.276, 1.306, 1.344, 1.387, 1.437, 1.455
+ ],
+ "sigma": 0.0007,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.06,
+ "strength": 0.5,
+ "limit": 0.5
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": [ 0.0, 2.5, 0.01, 2.5, 0.06, 1.0, 1.0, 1.0 ],
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": [ 0.0, 2.5, 0.01, 2.5, 0.06, 1.0, 1.0, 1.0 ],
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx378.json b/src/ipa/rpi/pisp/data/imx378.json
new file mode 100644
index 00000000..ac760f79
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx378.json
@@ -0,0 +1,634 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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
+ ]
+ }
+ ]
+ },
+ "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.603, 1.601, 1.597, 1.594, 1.588, 1.582, 1.576, 1.57, 1.566, 1.562, 1.561, 1.561, 1.561, 1.561, 1.561, 1.561, 1.562, 1.565, 1.57, 1.577, 1.584, 1.591, 1.598, 1.604, 1.61, 1.617, 1.623, 1.627, 1.632, 1.634, 1.636,
+ 1.603, 1.601, 1.599, 1.595, 1.591, 1.585, 1.579, 1.572, 1.565, 1.561, 1.557, 1.555, 1.555, 1.555, 1.555, 1.555, 1.556, 1.558, 1.561, 1.566, 1.573, 1.581, 1.587, 1.594, 1.6, 1.607, 1.613, 1.62, 1.625, 1.63, 1.632, 1.635,
+ 1.602, 1.599, 1.596, 1.592, 1.589, 1.582, 1.575, 1.568, 1.561, 1.556, 1.552, 1.55, 1.549, 1.548, 1.548, 1.549, 1.55, 1.553, 1.556, 1.562, 1.57, 1.577, 1.583, 1.589, 1.596, 1.603, 1.61, 1.617, 1.622, 1.627, 1.63, 1.633,
+ 1.601, 1.597, 1.594, 1.59, 1.586, 1.579, 1.571, 1.564, 1.556, 1.551, 1.546, 1.544, 1.542, 1.541, 1.541, 1.542, 1.544, 1.547, 1.551, 1.557, 1.565, 1.573, 1.579, 1.585, 1.592, 1.6, 1.607, 1.614, 1.62, 1.625, 1.628, 1.632,
+ 1.6, 1.596, 1.591, 1.586, 1.58, 1.573, 1.566, 1.559, 1.551, 1.546, 1.54, 1.537, 1.534, 1.533, 1.533, 1.534, 1.536, 1.539, 1.544, 1.551, 1.559, 1.567, 1.574, 1.581, 1.589, 1.596, 1.604, 1.612, 1.618, 1.624, 1.627, 1.631,
+ 1.599, 1.594, 1.588, 1.582, 1.575, 1.568, 1.56, 1.553, 1.547, 1.54, 1.534, 1.529, 1.525, 1.524, 1.524, 1.525, 1.527, 1.531, 1.537, 1.545, 1.553, 1.561, 1.569, 1.577, 1.585, 1.593, 1.601, 1.609, 1.616, 1.623, 1.626, 1.63,
+ 1.599, 1.592, 1.586, 1.578, 1.571, 1.563, 1.555, 1.548, 1.542, 1.535, 1.528, 1.522, 1.517, 1.515, 1.515, 1.516, 1.519, 1.523, 1.531, 1.539, 1.547, 1.556, 1.564, 1.573, 1.581, 1.59, 1.599, 1.607, 1.615, 1.622, 1.625, 1.629,
+ 1.598, 1.591, 1.583, 1.575, 1.567, 1.559, 1.55, 1.543, 1.537, 1.53, 1.523, 1.516, 1.509, 1.506, 1.506, 1.507, 1.51, 1.516, 1.525, 1.533, 1.541, 1.55, 1.56, 1.57, 1.579, 1.587, 1.596, 1.605, 1.613, 1.621, 1.625, 1.629,
+ 1.597, 1.589, 1.581, 1.572, 1.564, 1.555, 1.546, 1.539, 1.532, 1.525, 1.517, 1.509, 1.5, 1.497, 1.497, 1.499, 1.501, 1.508, 1.519, 1.528, 1.535, 1.544, 1.556, 1.567, 1.576, 1.585, 1.594, 1.603, 1.612, 1.62, 1.624, 1.629,
+ 1.597, 1.588, 1.579, 1.57, 1.561, 1.552, 1.543, 1.535, 1.527, 1.519, 1.511, 1.503, 1.494, 1.491, 1.49, 1.492, 1.496, 1.502, 1.512, 1.521, 1.529, 1.539, 1.552, 1.564, 1.574, 1.583, 1.592, 1.602, 1.611, 1.619, 1.624, 1.629,
+ 1.597, 1.588, 1.579, 1.569, 1.56, 1.55, 1.54, 1.531, 1.522, 1.513, 1.504, 1.497, 1.489, 1.486, 1.486, 1.488, 1.493, 1.498, 1.506, 1.514, 1.523, 1.535, 1.548, 1.561, 1.572, 1.582, 1.591, 1.601, 1.61, 1.619, 1.624, 1.629,
+ 1.597, 1.587, 1.578, 1.568, 1.559, 1.548, 1.538, 1.527, 1.516, 1.507, 1.498, 1.491, 1.485, 1.482, 1.481, 1.484, 1.489, 1.495, 1.499, 1.506, 1.518, 1.53, 1.545, 1.559, 1.57, 1.58, 1.59, 1.6, 1.61, 1.619, 1.624, 1.629,
+ 1.597, 1.587, 1.578, 1.568, 1.558, 1.548, 1.537, 1.526, 1.514, 1.504, 1.494, 1.487, 1.482, 1.479, 1.479, 1.481, 1.486, 1.491, 1.496, 1.503, 1.515, 1.529, 1.544, 1.558, 1.569, 1.58, 1.59, 1.6, 1.61, 1.619, 1.624, 1.629,
+ 1.597, 1.587, 1.578, 1.568, 1.558, 1.548, 1.537, 1.525, 1.513, 1.502, 1.491, 1.484, 1.48, 1.478, 1.477, 1.479, 1.483, 1.488, 1.494, 1.502, 1.515, 1.528, 1.543, 1.557, 1.569, 1.58, 1.59, 1.6, 1.61, 1.619, 1.624, 1.629,
+ 1.597, 1.587, 1.578, 1.568, 1.558, 1.547, 1.536, 1.524, 1.511, 1.499, 1.487, 1.481, 1.478, 1.476, 1.476, 1.477, 1.48, 1.485, 1.492, 1.501, 1.514, 1.527, 1.543, 1.557, 1.569, 1.58, 1.59, 1.6, 1.61, 1.619, 1.624, 1.629,
+ 1.597, 1.587, 1.578, 1.568, 1.558, 1.547, 1.536, 1.524, 1.511, 1.499, 1.487, 1.481, 1.479, 1.477, 1.477, 1.478, 1.48, 1.484, 1.492, 1.501, 1.514, 1.527, 1.543, 1.557, 1.569, 1.58, 1.591, 1.601, 1.61, 1.619, 1.624, 1.63,
+ 1.597, 1.588, 1.578, 1.568, 1.558, 1.547, 1.536, 1.524, 1.511, 1.499, 1.487, 1.482, 1.48, 1.479, 1.478, 1.479, 1.48, 1.484, 1.492, 1.501, 1.514, 1.527, 1.543, 1.557, 1.569, 1.581, 1.591, 1.602, 1.611, 1.619, 1.625, 1.63,
+ 1.597, 1.588, 1.579, 1.569, 1.559, 1.548, 1.536, 1.524, 1.512, 1.5, 1.488, 1.483, 1.482, 1.48, 1.48, 1.48, 1.481, 1.485, 1.492, 1.501, 1.514, 1.527, 1.543, 1.557, 1.57, 1.581, 1.592, 1.602, 1.611, 1.62, 1.625, 1.631,
+ 1.597, 1.588, 1.58, 1.57, 1.56, 1.549, 1.538, 1.526, 1.515, 1.504, 1.494, 1.488, 1.484, 1.481, 1.48, 1.482, 1.485, 1.49, 1.497, 1.506, 1.518, 1.531, 1.546, 1.56, 1.572, 1.583, 1.594, 1.604, 1.613, 1.621, 1.626, 1.631,
+ 1.597, 1.589, 1.581, 1.571, 1.562, 1.551, 1.54, 1.529, 1.518, 1.509, 1.501, 1.493, 1.486, 1.482, 1.481, 1.483, 1.489, 1.496, 1.503, 1.511, 1.522, 1.534, 1.548, 1.562, 1.574, 1.586, 1.596, 1.607, 1.615, 1.622, 1.627, 1.632,
+ 1.597, 1.59, 1.582, 1.573, 1.564, 1.553, 1.543, 1.532, 1.522, 1.514, 1.507, 1.499, 1.489, 1.485, 1.484, 1.487, 1.494, 1.501, 1.508, 1.517, 1.527, 1.538, 1.552, 1.565, 1.577, 1.588, 1.599, 1.609, 1.616, 1.624, 1.628, 1.632,
+ 1.599, 1.592, 1.585, 1.576, 1.566, 1.557, 1.548, 1.538, 1.529, 1.521, 1.513, 1.504, 1.496, 1.492, 1.492, 1.494, 1.5, 1.507, 1.515, 1.524, 1.534, 1.545, 1.557, 1.569, 1.581, 1.592, 1.602, 1.611, 1.619, 1.626, 1.63, 1.634,
+ 1.6, 1.594, 1.588, 1.579, 1.569, 1.561, 1.553, 1.545, 1.537, 1.528, 1.518, 1.51, 1.503, 1.5, 1.5, 1.502, 1.506, 1.512, 1.522, 1.531, 1.542, 1.552, 1.563, 1.574, 1.585, 1.596, 1.605, 1.614, 1.621, 1.628, 1.632, 1.635,
+ 1.602, 1.597, 1.591, 1.582, 1.573, 1.565, 1.558, 1.551, 1.543, 1.534, 1.524, 1.517, 1.511, 1.509, 1.509, 1.511, 1.514, 1.52, 1.529, 1.539, 1.549, 1.559, 1.569, 1.579, 1.589, 1.6, 1.608, 1.617, 1.624, 1.631, 1.634, 1.637,
+ 1.605, 1.6, 1.596, 1.587, 1.579, 1.571, 1.563, 1.556, 1.549, 1.541, 1.533, 1.527, 1.522, 1.52, 1.52, 1.522, 1.525, 1.53, 1.538, 1.546, 1.557, 1.566, 1.576, 1.585, 1.595, 1.604, 1.612, 1.621, 1.627, 1.633, 1.637, 1.641,
+ 1.608, 1.604, 1.6, 1.592, 1.585, 1.577, 1.569, 1.561, 1.554, 1.547, 1.541, 1.536, 1.533, 1.531, 1.531, 1.533, 1.536, 1.54, 1.546, 1.554, 1.564, 1.574, 1.583, 1.592, 1.6, 1.608, 1.616, 1.624, 1.63, 1.636, 1.64, 1.644,
+ 1.611, 1.607, 1.604, 1.597, 1.59, 1.582, 1.574, 1.567, 1.56, 1.554, 1.549, 1.545, 1.543, 1.542, 1.542, 1.543, 1.546, 1.55, 1.556, 1.562, 1.571, 1.58, 1.589, 1.598, 1.605, 1.613, 1.621, 1.628, 1.634, 1.639, 1.643, 1.647,
+ 1.614, 1.61, 1.607, 1.601, 1.595, 1.588, 1.58, 1.574, 1.567, 1.562, 1.557, 1.554, 1.553, 1.552, 1.552, 1.554, 1.557, 1.561, 1.565, 1.571, 1.579, 1.587, 1.595, 1.603, 1.61, 1.618, 1.625, 1.633, 1.638, 1.642, 1.646, 1.65,
+ 1.616, 1.614, 1.611, 1.606, 1.601, 1.594, 1.586, 1.58, 1.574, 1.569, 1.565, 1.563, 1.562, 1.562, 1.562, 1.564, 1.567, 1.571, 1.575, 1.58, 1.586, 1.593, 1.601, 1.609, 1.616, 1.623, 1.63, 1.637, 1.641, 1.646, 1.65, 1.653,
+ 1.618, 1.615, 1.613, 1.609, 1.604, 1.598, 1.592, 1.586, 1.58, 1.575, 1.572, 1.571, 1.57, 1.57, 1.57, 1.572, 1.574, 1.577, 1.581, 1.585, 1.592, 1.599, 1.606, 1.614, 1.621, 1.628, 1.634, 1.64, 1.644, 1.649, 1.651, 1.654,
+ 1.618, 1.617, 1.615, 1.612, 1.608, 1.603, 1.597, 1.591, 1.585, 1.581, 1.579, 1.578, 1.578, 1.578, 1.578, 1.579, 1.581, 1.583, 1.586, 1.59, 1.597, 1.604, 1.612, 1.619, 1.626, 1.633, 1.638, 1.643, 1.647, 1.651, 1.653, 1.655,
+ 1.619, 1.618, 1.617, 1.614, 1.611, 1.607, 1.602, 1.596, 1.59, 1.587, 1.585, 1.585, 1.585, 1.585, 1.585, 1.586, 1.587, 1.589, 1.591, 1.595, 1.602, 1.609, 1.617, 1.624, 1.631, 1.638, 1.642, 1.646, 1.65, 1.654, 1.655, 1.655
+ ]
+ },
+ {
+ "ct": 5500,
+ "table":
+ [
+ 2.664, 2.661, 2.658, 2.652, 2.646, 2.638, 2.631, 2.619, 2.605, 2.602, 2.602, 2.602, 2.602, 2.603, 2.605, 2.609, 2.614, 2.619, 2.625, 2.632, 2.642, 2.654, 2.667, 2.68, 2.69, 2.701, 2.712, 2.723, 2.73, 2.736, 2.742, 2.747,
+ 2.662, 2.659, 2.656, 2.649, 2.64, 2.631, 2.622, 2.61, 2.596, 2.593, 2.592, 2.593, 2.593, 2.595, 2.599, 2.604, 2.61, 2.616, 2.621, 2.628, 2.636, 2.646, 2.659, 2.671, 2.682, 2.694, 2.706, 2.718, 2.726, 2.733, 2.739, 2.745,
+ 2.66, 2.657, 2.655, 2.645, 2.635, 2.625, 2.614, 2.601, 2.587, 2.583, 2.583, 2.583, 2.584, 2.588, 2.593, 2.599, 2.606, 2.612, 2.618, 2.624, 2.63, 2.639, 2.65, 2.662, 2.674, 2.687, 2.7, 2.713, 2.721, 2.73, 2.736, 2.743,
+ 2.657, 2.655, 2.652, 2.641, 2.629, 2.617, 2.605, 2.592, 2.579, 2.575, 2.574, 2.574, 2.575, 2.58, 2.587, 2.593, 2.6, 2.607, 2.613, 2.619, 2.625, 2.632, 2.643, 2.654, 2.667, 2.68, 2.694, 2.708, 2.717, 2.727, 2.734, 2.741,
+ 2.654, 2.649, 2.644, 2.633, 2.621, 2.608, 2.595, 2.584, 2.573, 2.569, 2.566, 2.566, 2.566, 2.57, 2.575, 2.582, 2.59, 2.598, 2.607, 2.615, 2.621, 2.628, 2.639, 2.65, 2.664, 2.677, 2.691, 2.705, 2.715, 2.725, 2.733, 2.741,
+ 2.651, 2.644, 2.636, 2.624, 2.612, 2.599, 2.585, 2.576, 2.568, 2.563, 2.559, 2.557, 2.558, 2.56, 2.563, 2.57, 2.579, 2.589, 2.6, 2.61, 2.617, 2.625, 2.635, 2.647, 2.66, 2.674, 2.688, 2.701, 2.712, 2.723, 2.732, 2.741,
+ 2.648, 2.639, 2.629, 2.617, 2.604, 2.59, 2.577, 2.569, 2.563, 2.557, 2.551, 2.549, 2.549, 2.55, 2.552, 2.558, 2.568, 2.58, 2.593, 2.604, 2.612, 2.621, 2.632, 2.644, 2.658, 2.671, 2.685, 2.699, 2.71, 2.722, 2.731, 2.741,
+ 2.646, 2.635, 2.623, 2.61, 2.596, 2.584, 2.572, 2.565, 2.559, 2.552, 2.544, 2.541, 2.54, 2.541, 2.542, 2.548, 2.559, 2.57, 2.583, 2.595, 2.605, 2.616, 2.63, 2.643, 2.656, 2.67, 2.684, 2.698, 2.71, 2.722, 2.731, 2.741,
+ 2.644, 2.63, 2.617, 2.603, 2.588, 2.578, 2.567, 2.561, 2.555, 2.547, 2.538, 2.533, 2.532, 2.531, 2.532, 2.538, 2.549, 2.561, 2.574, 2.586, 2.598, 2.612, 2.627, 2.642, 2.655, 2.668, 2.682, 2.696, 2.709, 2.722, 2.731, 2.741,
+ 2.643, 2.628, 2.613, 2.598, 2.583, 2.573, 2.564, 2.557, 2.55, 2.541, 2.531, 2.526, 2.524, 2.523, 2.524, 2.53, 2.541, 2.552, 2.565, 2.578, 2.593, 2.608, 2.625, 2.641, 2.654, 2.668, 2.682, 2.696, 2.709, 2.722, 2.731, 2.741,
+ 2.643, 2.627, 2.61, 2.595, 2.581, 2.571, 2.562, 2.553, 2.543, 2.534, 2.526, 2.52, 2.516, 2.516, 2.519, 2.525, 2.533, 2.543, 2.556, 2.57, 2.588, 2.606, 2.623, 2.64, 2.654, 2.668, 2.682, 2.696, 2.709, 2.722, 2.731, 2.741,
+ 2.643, 2.625, 2.608, 2.593, 2.578, 2.569, 2.56, 2.549, 2.536, 2.528, 2.52, 2.514, 2.508, 2.509, 2.515, 2.52, 2.526, 2.535, 2.546, 2.562, 2.583, 2.603, 2.622, 2.639, 2.653, 2.668, 2.682, 2.696, 2.709, 2.722, 2.731, 2.741,
+ 2.643, 2.625, 2.607, 2.592, 2.577, 2.568, 2.56, 2.548, 2.534, 2.524, 2.516, 2.51, 2.505, 2.506, 2.51, 2.516, 2.522, 2.531, 2.544, 2.56, 2.581, 2.601, 2.621, 2.639, 2.654, 2.669, 2.683, 2.696, 2.71, 2.723, 2.732, 2.742,
+ 2.643, 2.625, 2.607, 2.592, 2.577, 2.568, 2.56, 2.548, 2.534, 2.523, 2.512, 2.506, 2.503, 2.504, 2.506, 2.512, 2.52, 2.53, 2.543, 2.559, 2.58, 2.6, 2.62, 2.639, 2.655, 2.67, 2.684, 2.697, 2.711, 2.725, 2.734, 2.743,
+ 2.643, 2.625, 2.607, 2.592, 2.577, 2.568, 2.56, 2.548, 2.534, 2.521, 2.508, 2.503, 2.502, 2.501, 2.502, 2.508, 2.517, 2.529, 2.543, 2.559, 2.579, 2.599, 2.62, 2.639, 2.656, 2.671, 2.685, 2.698, 2.713, 2.727, 2.736, 2.744,
+ 2.645, 2.627, 2.609, 2.593, 2.578, 2.569, 2.56, 2.548, 2.535, 2.522, 2.51, 2.504, 2.502, 2.502, 2.504, 2.509, 2.518, 2.529, 2.543, 2.559, 2.579, 2.599, 2.621, 2.642, 2.658, 2.673, 2.686, 2.7, 2.715, 2.729, 2.738, 2.747,
+ 2.646, 2.628, 2.611, 2.595, 2.58, 2.569, 2.56, 2.548, 2.535, 2.523, 2.512, 2.506, 2.503, 2.504, 2.507, 2.512, 2.518, 2.529, 2.543, 2.559, 2.579, 2.6, 2.623, 2.644, 2.659, 2.674, 2.688, 2.702, 2.716, 2.73, 2.74, 2.749,
+ 2.648, 2.63, 2.612, 2.597, 2.581, 2.571, 2.56, 2.548, 2.536, 2.525, 2.515, 2.509, 2.504, 2.505, 2.509, 2.514, 2.52, 2.53, 2.544, 2.56, 2.58, 2.6, 2.625, 2.646, 2.661, 2.676, 2.69, 2.704, 2.718, 2.732, 2.742, 2.752,
+ 2.648, 2.632, 2.615, 2.6, 2.585, 2.575, 2.565, 2.555, 2.544, 2.533, 2.523, 2.516, 2.511, 2.511, 2.514, 2.52, 2.529, 2.539, 2.551, 2.566, 2.585, 2.605, 2.628, 2.649, 2.664, 2.68, 2.694, 2.709, 2.723, 2.736, 2.745, 2.754,
+ 2.648, 2.633, 2.618, 2.603, 2.588, 2.579, 2.569, 2.561, 2.552, 2.542, 2.53, 2.523, 2.517, 2.516, 2.518, 2.526, 2.538, 2.549, 2.558, 2.571, 2.589, 2.609, 2.631, 2.651, 2.667, 2.683, 2.699, 2.713, 2.727, 2.74, 2.748, 2.756,
+ 2.649, 2.635, 2.622, 2.607, 2.593, 2.584, 2.575, 2.567, 2.559, 2.549, 2.538, 2.531, 2.525, 2.523, 2.525, 2.533, 2.546, 2.558, 2.566, 2.579, 2.597, 2.615, 2.635, 2.655, 2.671, 2.688, 2.703, 2.718, 2.732, 2.745, 2.752, 2.76,
+ 2.653, 2.64, 2.628, 2.615, 2.602, 2.591, 2.581, 2.573, 2.565, 2.556, 2.546, 2.54, 2.537, 2.536, 2.537, 2.543, 2.553, 2.565, 2.577, 2.592, 2.609, 2.627, 2.645, 2.662, 2.678, 2.693, 2.708, 2.723, 2.737, 2.75, 2.758, 2.766,
+ 2.657, 2.646, 2.634, 2.622, 2.61, 2.599, 2.588, 2.579, 2.57, 2.562, 2.554, 2.55, 2.549, 2.548, 2.548, 2.553, 2.561, 2.572, 2.588, 2.605, 2.622, 2.639, 2.655, 2.67, 2.685, 2.699, 2.714, 2.728, 2.742, 2.756, 2.764, 2.773,
+ 2.662, 2.652, 2.642, 2.63, 2.618, 2.607, 2.595, 2.586, 2.578, 2.571, 2.565, 2.563, 2.562, 2.562, 2.563, 2.567, 2.573, 2.584, 2.601, 2.618, 2.635, 2.651, 2.665, 2.679, 2.692, 2.706, 2.72, 2.735, 2.748, 2.761, 2.77, 2.778,
+ 2.669, 2.661, 2.652, 2.64, 2.627, 2.615, 2.604, 2.596, 2.589, 2.584, 2.58, 2.578, 2.578, 2.579, 2.581, 2.585, 2.591, 2.601, 2.615, 2.63, 2.646, 2.661, 2.675, 2.688, 2.701, 2.714, 2.729, 2.744, 2.756, 2.768, 2.775, 2.782,
+ 2.676, 2.669, 2.662, 2.649, 2.636, 2.624, 2.612, 2.605, 2.6, 2.596, 2.594, 2.593, 2.593, 2.595, 2.598, 2.603, 2.609, 2.618, 2.629, 2.642, 2.657, 2.672, 2.685, 2.698, 2.71, 2.723, 2.738, 2.752, 2.763, 2.774, 2.78, 2.786,
+ 2.683, 2.676, 2.67, 2.658, 2.646, 2.635, 2.623, 2.616, 2.611, 2.609, 2.608, 2.608, 2.608, 2.611, 2.614, 2.619, 2.625, 2.633, 2.644, 2.655, 2.668, 2.681, 2.694, 2.707, 2.719, 2.732, 2.747, 2.76, 2.77, 2.779, 2.785, 2.791,
+ 2.688, 2.682, 2.676, 2.667, 2.658, 2.646, 2.635, 2.628, 2.623, 2.621, 2.621, 2.621, 2.623, 2.625, 2.629, 2.634, 2.64, 2.648, 2.658, 2.669, 2.679, 2.691, 2.703, 2.716, 2.729, 2.742, 2.755, 2.768, 2.776, 2.783, 2.79, 2.796,
+ 2.694, 2.689, 2.683, 2.676, 2.67, 2.658, 2.646, 2.64, 2.635, 2.634, 2.634, 2.635, 2.637, 2.64, 2.644, 2.649, 2.655, 2.663, 2.673, 2.682, 2.691, 2.7, 2.712, 2.725, 2.738, 2.752, 2.764, 2.775, 2.782, 2.788, 2.795, 2.802,
+ 2.697, 2.693, 2.689, 2.682, 2.676, 2.666, 2.655, 2.65, 2.645, 2.644, 2.644, 2.645, 2.647, 2.649, 2.652, 2.657, 2.663, 2.671, 2.68, 2.69, 2.699, 2.709, 2.722, 2.735, 2.747, 2.76, 2.77, 2.78, 2.787, 2.793, 2.798, 2.804,
+ 2.7, 2.697, 2.694, 2.687, 2.68, 2.672, 2.664, 2.659, 2.655, 2.654, 2.654, 2.655, 2.655, 2.657, 2.66, 2.664, 2.671, 2.678, 2.687, 2.696, 2.707, 2.718, 2.732, 2.744, 2.756, 2.767, 2.776, 2.785, 2.791, 2.798, 2.801, 2.804,
+ 2.702, 2.701, 2.699, 2.692, 2.685, 2.679, 2.672, 2.668, 2.665, 2.664, 2.664, 2.664, 2.664, 2.665, 2.667, 2.671, 2.678, 2.685, 2.693, 2.703, 2.715, 2.728, 2.741, 2.754, 2.764, 2.774, 2.782, 2.789, 2.796, 2.803, 2.804, 2.805
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 2800,
+ "table":
+ [
+ 2.876, 2.872, 2.868, 2.866, 2.863, 2.858, 2.852, 2.849, 2.847, 2.846, 2.846, 2.846, 2.847, 2.848, 2.85, 2.851, 2.851, 2.852, 2.855, 2.859, 2.864, 2.868, 2.872, 2.877, 2.884, 2.89, 2.895, 2.9, 2.907, 2.913, 2.92, 2.926,
+ 2.871, 2.869, 2.866, 2.863, 2.861, 2.856, 2.85, 2.848, 2.846, 2.846, 2.846, 2.847, 2.847, 2.848, 2.85, 2.852, 2.853, 2.854, 2.856, 2.86, 2.866, 2.871, 2.875, 2.879, 2.884, 2.889, 2.894, 2.899, 2.905, 2.912, 2.917, 2.923,
+ 2.867, 2.865, 2.863, 2.861, 2.858, 2.854, 2.848, 2.847, 2.846, 2.846, 2.847, 2.847, 2.848, 2.849, 2.85, 2.852, 2.854, 2.856, 2.858, 2.861, 2.868, 2.874, 2.877, 2.881, 2.884, 2.888, 2.893, 2.898, 2.904, 2.91, 2.915, 2.92,
+ 2.863, 2.862, 2.861, 2.858, 2.856, 2.851, 2.847, 2.846, 2.846, 2.846, 2.847, 2.848, 2.849, 2.85, 2.852, 2.854, 2.857, 2.859, 2.86, 2.864, 2.871, 2.877, 2.88, 2.883, 2.884, 2.887, 2.892, 2.896, 2.903, 2.909, 2.913, 2.917,
+ 2.862, 2.861, 2.859, 2.856, 2.852, 2.848, 2.845, 2.844, 2.844, 2.846, 2.849, 2.852, 2.855, 2.857, 2.86, 2.863, 2.868, 2.87, 2.871, 2.873, 2.877, 2.88, 2.881, 2.883, 2.885, 2.887, 2.89, 2.894, 2.9, 2.906, 2.911, 2.915,
+ 2.861, 2.859, 2.857, 2.853, 2.849, 2.846, 2.843, 2.842, 2.842, 2.846, 2.851, 2.856, 2.861, 2.865, 2.868, 2.873, 2.878, 2.881, 2.881, 2.882, 2.882, 2.883, 2.883, 2.883, 2.885, 2.886, 2.889, 2.891, 2.897, 2.903, 2.909, 2.914,
+ 2.861, 2.858, 2.856, 2.851, 2.847, 2.844, 2.842, 2.842, 2.843, 2.848, 2.854, 2.861, 2.867, 2.872, 2.876, 2.881, 2.887, 2.89, 2.89, 2.889, 2.888, 2.887, 2.885, 2.884, 2.886, 2.887, 2.888, 2.89, 2.896, 2.901, 2.907, 2.912,
+ 2.86, 2.857, 2.854, 2.85, 2.846, 2.845, 2.844, 2.845, 2.847, 2.852, 2.859, 2.865, 2.872, 2.878, 2.883, 2.887, 2.892, 2.895, 2.895, 2.894, 2.893, 2.892, 2.889, 2.887, 2.888, 2.889, 2.89, 2.892, 2.897, 2.901, 2.906, 2.911,
+ 2.858, 2.855, 2.852, 2.849, 2.846, 2.846, 2.845, 2.848, 2.852, 2.857, 2.863, 2.87, 2.878, 2.884, 2.889, 2.894, 2.898, 2.9, 2.9, 2.899, 2.899, 2.897, 2.893, 2.89, 2.89, 2.89, 2.892, 2.894, 2.897, 2.901, 2.905, 2.91,
+ 2.858, 2.855, 2.851, 2.849, 2.846, 2.846, 2.846, 2.85, 2.856, 2.862, 2.868, 2.875, 2.883, 2.889, 2.894, 2.898, 2.902, 2.904, 2.904, 2.904, 2.903, 2.901, 2.896, 2.893, 2.892, 2.892, 2.894, 2.895, 2.899, 2.902, 2.905, 2.909,
+ 2.858, 2.855, 2.851, 2.849, 2.846, 2.846, 2.846, 2.852, 2.86, 2.867, 2.874, 2.881, 2.887, 2.893, 2.897, 2.901, 2.904, 2.907, 2.908, 2.909, 2.907, 2.905, 2.9, 2.896, 2.894, 2.893, 2.895, 2.897, 2.9, 2.903, 2.906, 2.909,
+ 2.858, 2.855, 2.851, 2.849, 2.846, 2.846, 2.846, 2.854, 2.863, 2.872, 2.88, 2.886, 2.892, 2.896, 2.9, 2.903, 2.907, 2.91, 2.912, 2.913, 2.911, 2.908, 2.904, 2.899, 2.897, 2.895, 2.896, 2.898, 2.901, 2.904, 2.906, 2.909,
+ 2.858, 2.855, 2.851, 2.849, 2.847, 2.847, 2.848, 2.856, 2.866, 2.875, 2.882, 2.889, 2.894, 2.899, 2.902, 2.906, 2.909, 2.912, 2.915, 2.916, 2.914, 2.911, 2.907, 2.903, 2.899, 2.897, 2.898, 2.899, 2.902, 2.904, 2.907, 2.909,
+ 2.858, 2.855, 2.851, 2.85, 2.848, 2.849, 2.85, 2.858, 2.869, 2.877, 2.884, 2.89, 2.896, 2.901, 2.905, 2.908, 2.912, 2.915, 2.918, 2.918, 2.916, 2.913, 2.91, 2.906, 2.902, 2.899, 2.899, 2.899, 2.902, 2.905, 2.907, 2.908,
+ 2.858, 2.855, 2.851, 2.85, 2.849, 2.851, 2.852, 2.861, 2.871, 2.879, 2.886, 2.892, 2.898, 2.903, 2.907, 2.911, 2.915, 2.918, 2.92, 2.921, 2.918, 2.916, 2.913, 2.909, 2.905, 2.901, 2.9, 2.899, 2.902, 2.905, 2.907, 2.908,
+ 2.859, 2.856, 2.853, 2.851, 2.85, 2.852, 2.853, 2.862, 2.871, 2.879, 2.886, 2.892, 2.898, 2.904, 2.908, 2.912, 2.916, 2.918, 2.921, 2.921, 2.919, 2.917, 2.914, 2.91, 2.905, 2.901, 2.9, 2.9, 2.903, 2.906, 2.907, 2.908,
+ 2.86, 2.857, 2.854, 2.853, 2.852, 2.853, 2.854, 2.862, 2.871, 2.879, 2.886, 2.892, 2.898, 2.904, 2.909, 2.913, 2.916, 2.919, 2.921, 2.922, 2.92, 2.918, 2.914, 2.91, 2.905, 2.901, 2.901, 2.901, 2.904, 2.906, 2.907, 2.908,
+ 2.861, 2.858, 2.855, 2.854, 2.853, 2.854, 2.855, 2.862, 2.871, 2.879, 2.886, 2.892, 2.898, 2.904, 2.91, 2.914, 2.917, 2.919, 2.921, 2.922, 2.921, 2.919, 2.914, 2.91, 2.905, 2.901, 2.901, 2.902, 2.904, 2.907, 2.908, 2.908,
+ 2.861, 2.859, 2.857, 2.855, 2.854, 2.854, 2.855, 2.862, 2.871, 2.878, 2.885, 2.891, 2.898, 2.903, 2.908, 2.912, 2.915, 2.918, 2.919, 2.919, 2.918, 2.916, 2.912, 2.909, 2.906, 2.903, 2.903, 2.904, 2.906, 2.907, 2.908, 2.908,
+ 2.862, 2.86, 2.858, 2.856, 2.855, 2.855, 2.856, 2.862, 2.87, 2.877, 2.884, 2.89, 2.897, 2.902, 2.906, 2.91, 2.914, 2.916, 2.918, 2.917, 2.915, 2.913, 2.91, 2.908, 2.906, 2.905, 2.905, 2.906, 2.907, 2.908, 2.908, 2.909,
+ 2.862, 2.861, 2.859, 2.858, 2.856, 2.856, 2.857, 2.863, 2.87, 2.876, 2.883, 2.889, 2.895, 2.9, 2.904, 2.908, 2.912, 2.914, 2.915, 2.915, 2.912, 2.91, 2.908, 2.907, 2.907, 2.907, 2.907, 2.907, 2.908, 2.909, 2.909, 2.909,
+ 2.862, 2.862, 2.861, 2.859, 2.857, 2.858, 2.859, 2.864, 2.87, 2.876, 2.881, 2.886, 2.891, 2.896, 2.901, 2.905, 2.909, 2.911, 2.911, 2.911, 2.909, 2.907, 2.906, 2.906, 2.906, 2.907, 2.908, 2.908, 2.909, 2.91, 2.911, 2.911,
+ 2.863, 2.863, 2.862, 2.86, 2.858, 2.86, 2.862, 2.866, 2.87, 2.875, 2.88, 2.884, 2.887, 2.891, 2.897, 2.902, 2.905, 2.907, 2.907, 2.907, 2.906, 2.905, 2.905, 2.905, 2.906, 2.907, 2.908, 2.909, 2.91, 2.912, 2.912, 2.912,
+ 2.863, 2.863, 2.863, 2.862, 2.86, 2.862, 2.864, 2.867, 2.87, 2.874, 2.878, 2.881, 2.883, 2.888, 2.894, 2.898, 2.901, 2.903, 2.903, 2.903, 2.903, 2.903, 2.903, 2.904, 2.906, 2.907, 2.908, 2.91, 2.912, 2.913, 2.913, 2.914,
+ 2.865, 2.864, 2.864, 2.863, 2.862, 2.864, 2.865, 2.867, 2.869, 2.872, 2.875, 2.878, 2.882, 2.886, 2.89, 2.893, 2.895, 2.897, 2.899, 2.899, 2.899, 2.9, 2.902, 2.903, 2.905, 2.907, 2.909, 2.911, 2.912, 2.914, 2.914, 2.915,
+ 2.866, 2.865, 2.865, 2.865, 2.864, 2.865, 2.866, 2.867, 2.868, 2.87, 2.872, 2.876, 2.88, 2.883, 2.886, 2.888, 2.89, 2.892, 2.894, 2.896, 2.896, 2.897, 2.9, 2.903, 2.905, 2.907, 2.91, 2.913, 2.913, 2.914, 2.915, 2.916,
+ 2.868, 2.868, 2.867, 2.867, 2.866, 2.867, 2.868, 2.868, 2.869, 2.87, 2.871, 2.874, 2.877, 2.879, 2.881, 2.883, 2.885, 2.888, 2.891, 2.893, 2.894, 2.896, 2.898, 2.901, 2.904, 2.907, 2.91, 2.913, 2.914, 2.915, 2.916, 2.918,
+ 2.871, 2.871, 2.871, 2.87, 2.869, 2.869, 2.869, 2.869, 2.87, 2.87, 2.871, 2.872, 2.874, 2.875, 2.875, 2.877, 2.881, 2.885, 2.889, 2.892, 2.893, 2.895, 2.897, 2.899, 2.903, 2.907, 2.91, 2.914, 2.915, 2.916, 2.918, 2.919,
+ 2.874, 2.874, 2.874, 2.873, 2.871, 2.871, 2.871, 2.871, 2.871, 2.871, 2.871, 2.871, 2.87, 2.87, 2.87, 2.872, 2.876, 2.881, 2.886, 2.89, 2.893, 2.894, 2.895, 2.897, 2.902, 2.907, 2.911, 2.914, 2.916, 2.917, 2.919, 2.921,
+ 2.877, 2.877, 2.876, 2.874, 2.873, 2.872, 2.872, 2.872, 2.871, 2.871, 2.871, 2.87, 2.869, 2.869, 2.869, 2.871, 2.874, 2.878, 2.883, 2.887, 2.891, 2.893, 2.894, 2.896, 2.901, 2.907, 2.911, 2.914, 2.916, 2.918, 2.919, 2.921,
+ 2.88, 2.879, 2.878, 2.876, 2.874, 2.874, 2.873, 2.872, 2.871, 2.871, 2.871, 2.87, 2.869, 2.869, 2.869, 2.87, 2.871, 2.874, 2.879, 2.884, 2.889, 2.892, 2.894, 2.896, 2.901, 2.906, 2.91, 2.914, 2.916, 2.918, 2.92, 2.921,
+ 2.882, 2.881, 2.879, 2.878, 2.876, 2.875, 2.874, 2.873, 2.871, 2.871, 2.871, 2.87, 2.869, 2.869, 2.869, 2.869, 2.869, 2.871, 2.875, 2.881, 2.887, 2.891, 2.893, 2.895, 2.901, 2.906, 2.91, 2.914, 2.917, 2.919, 2.92, 2.921
+ ]
+ },
+ {
+ "ct": 5500,
+ "table":
+ [
+ 1.488, 1.488, 1.488, 1.488, 1.488, 1.488, 1.488, 1.489, 1.491, 1.491, 1.492, 1.492, 1.492, 1.492, 1.491, 1.491, 1.491, 1.491, 1.491, 1.491, 1.492, 1.492, 1.494, 1.495, 1.496, 1.497, 1.498, 1.499, 1.499, 1.499, 1.501, 1.503,
+ 1.486, 1.486, 1.487, 1.487, 1.487, 1.487, 1.488, 1.489, 1.49, 1.491, 1.492, 1.492, 1.492, 1.492, 1.492, 1.491, 1.491, 1.491, 1.491, 1.492, 1.492, 1.493, 1.494, 1.495, 1.495, 1.495, 1.496, 1.496, 1.497, 1.497, 1.498, 1.5,
+ 1.484, 1.485, 1.486, 1.486, 1.486, 1.486, 1.487, 1.488, 1.489, 1.49, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.493, 1.494, 1.494, 1.494, 1.494, 1.493, 1.493, 1.493, 1.494, 1.495, 1.496, 1.497,
+ 1.482, 1.483, 1.485, 1.485, 1.485, 1.486, 1.487, 1.488, 1.489, 1.49, 1.491, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.492, 1.493, 1.493, 1.494, 1.494, 1.494, 1.493, 1.492, 1.491, 1.491, 1.492, 1.493, 1.493, 1.494,
+ 1.482, 1.483, 1.484, 1.484, 1.485, 1.485, 1.486, 1.487, 1.488, 1.49, 1.491, 1.493, 1.493, 1.494, 1.494, 1.495, 1.495, 1.495, 1.495, 1.494, 1.494, 1.494, 1.493, 1.493, 1.492, 1.492, 1.491, 1.491, 1.492, 1.492, 1.492, 1.493,
+ 1.482, 1.482, 1.483, 1.483, 1.484, 1.485, 1.485, 1.486, 1.487, 1.489, 1.491, 1.493, 1.494, 1.496, 1.496, 1.497, 1.497, 1.497, 1.497, 1.496, 1.495, 1.494, 1.493, 1.492, 1.492, 1.491, 1.491, 1.491, 1.491, 1.491, 1.492, 1.492,
+ 1.482, 1.482, 1.482, 1.483, 1.484, 1.484, 1.485, 1.486, 1.487, 1.489, 1.492, 1.494, 1.496, 1.497, 1.498, 1.498, 1.499, 1.499, 1.498, 1.497, 1.496, 1.494, 1.493, 1.492, 1.491, 1.491, 1.491, 1.491, 1.491, 1.491, 1.491, 1.491,
+ 1.482, 1.482, 1.482, 1.482, 1.483, 1.484, 1.485, 1.487, 1.488, 1.491, 1.493, 1.495, 1.496, 1.497, 1.498, 1.499, 1.5, 1.5, 1.499, 1.498, 1.497, 1.495, 1.494, 1.492, 1.492, 1.491, 1.49, 1.49, 1.49, 1.49, 1.49, 1.49,
+ 1.481, 1.481, 1.481, 1.482, 1.482, 1.483, 1.485, 1.487, 1.49, 1.492, 1.495, 1.496, 1.497, 1.498, 1.499, 1.499, 1.5, 1.5, 1.499, 1.499, 1.498, 1.497, 1.494, 1.493, 1.492, 1.491, 1.49, 1.488, 1.488, 1.488, 1.488, 1.488,
+ 1.481, 1.481, 1.481, 1.481, 1.482, 1.483, 1.484, 1.487, 1.49, 1.493, 1.495, 1.497, 1.498, 1.498, 1.499, 1.5, 1.5, 1.501, 1.5, 1.499, 1.498, 1.497, 1.495, 1.492, 1.491, 1.49, 1.489, 1.487, 1.487, 1.487, 1.487, 1.487,
+ 1.481, 1.481, 1.481, 1.481, 1.481, 1.482, 1.484, 1.487, 1.49, 1.493, 1.496, 1.497, 1.498, 1.498, 1.499, 1.5, 1.5, 1.501, 1.5, 1.499, 1.498, 1.497, 1.494, 1.492, 1.491, 1.489, 1.488, 1.486, 1.486, 1.485, 1.485, 1.485,
+ 1.481, 1.481, 1.481, 1.481, 1.481, 1.482, 1.483, 1.486, 1.49, 1.493, 1.496, 1.497, 1.498, 1.498, 1.499, 1.5, 1.5, 1.501, 1.5, 1.499, 1.498, 1.497, 1.494, 1.492, 1.49, 1.488, 1.487, 1.485, 1.484, 1.483, 1.483, 1.483,
+ 1.48, 1.48, 1.48, 1.481, 1.481, 1.482, 1.483, 1.486, 1.489, 1.493, 1.496, 1.497, 1.497, 1.498, 1.499, 1.499, 1.5, 1.5, 1.499, 1.499, 1.498, 1.496, 1.494, 1.491, 1.489, 1.487, 1.485, 1.484, 1.483, 1.483, 1.483, 1.482,
+ 1.48, 1.48, 1.48, 1.48, 1.481, 1.482, 1.482, 1.485, 1.489, 1.492, 1.495, 1.496, 1.497, 1.498, 1.498, 1.499, 1.499, 1.5, 1.499, 1.498, 1.497, 1.495, 1.493, 1.491, 1.488, 1.486, 1.484, 1.483, 1.483, 1.482, 1.482, 1.482,
+ 1.479, 1.479, 1.479, 1.48, 1.481, 1.481, 1.482, 1.485, 1.488, 1.491, 1.494, 1.496, 1.497, 1.497, 1.498, 1.498, 1.499, 1.499, 1.499, 1.498, 1.496, 1.495, 1.493, 1.491, 1.488, 1.485, 1.483, 1.482, 1.482, 1.482, 1.482, 1.481,
+ 1.479, 1.479, 1.479, 1.48, 1.48, 1.481, 1.482, 1.485, 1.488, 1.491, 1.494, 1.495, 1.496, 1.497, 1.497, 1.498, 1.498, 1.498, 1.498, 1.497, 1.496, 1.494, 1.492, 1.49, 1.487, 1.484, 1.483, 1.482, 1.481, 1.481, 1.48, 1.48,
+ 1.479, 1.479, 1.479, 1.479, 1.48, 1.48, 1.481, 1.484, 1.488, 1.491, 1.493, 1.495, 1.496, 1.497, 1.497, 1.497, 1.498, 1.497, 1.497, 1.497, 1.496, 1.494, 1.492, 1.489, 1.486, 1.483, 1.482, 1.481, 1.481, 1.48, 1.479, 1.478,
+ 1.479, 1.479, 1.479, 1.479, 1.479, 1.48, 1.481, 1.484, 1.488, 1.491, 1.493, 1.495, 1.496, 1.496, 1.497, 1.497, 1.497, 1.497, 1.496, 1.496, 1.495, 1.494, 1.491, 1.488, 1.485, 1.482, 1.481, 1.481, 1.48, 1.479, 1.478, 1.477,
+ 1.479, 1.479, 1.479, 1.479, 1.479, 1.48, 1.481, 1.484, 1.487, 1.49, 1.492, 1.494, 1.495, 1.496, 1.496, 1.497, 1.497, 1.496, 1.496, 1.495, 1.494, 1.493, 1.49, 1.487, 1.484, 1.482, 1.481, 1.48, 1.479, 1.479, 1.478, 1.477,
+ 1.478, 1.478, 1.478, 1.479, 1.479, 1.48, 1.481, 1.484, 1.487, 1.489, 1.491, 1.493, 1.494, 1.495, 1.496, 1.496, 1.496, 1.496, 1.496, 1.495, 1.494, 1.492, 1.489, 1.487, 1.484, 1.482, 1.481, 1.479, 1.479, 1.478, 1.477, 1.476,
+ 1.478, 1.478, 1.478, 1.478, 1.479, 1.48, 1.481, 1.483, 1.486, 1.488, 1.49, 1.492, 1.493, 1.494, 1.495, 1.496, 1.496, 1.495, 1.495, 1.494, 1.493, 1.491, 1.488, 1.486, 1.484, 1.482, 1.48, 1.479, 1.478, 1.478, 1.477, 1.476,
+ 1.478, 1.478, 1.478, 1.478, 1.479, 1.48, 1.481, 1.483, 1.486, 1.488, 1.489, 1.491, 1.492, 1.493, 1.494, 1.495, 1.495, 1.494, 1.494, 1.493, 1.491, 1.489, 1.487, 1.485, 1.483, 1.481, 1.48, 1.479, 1.478, 1.477, 1.477, 1.476,
+ 1.478, 1.478, 1.478, 1.478, 1.479, 1.48, 1.482, 1.484, 1.486, 1.487, 1.488, 1.49, 1.491, 1.492, 1.493, 1.494, 1.494, 1.493, 1.493, 1.492, 1.489, 1.487, 1.486, 1.484, 1.483, 1.481, 1.48, 1.479, 1.478, 1.476, 1.476, 1.476,
+ 1.478, 1.478, 1.478, 1.479, 1.479, 1.481, 1.482, 1.484, 1.485, 1.486, 1.487, 1.489, 1.49, 1.491, 1.492, 1.492, 1.492, 1.492, 1.491, 1.49, 1.488, 1.486, 1.485, 1.483, 1.482, 1.481, 1.48, 1.479, 1.477, 1.476, 1.476, 1.476,
+ 1.477, 1.478, 1.478, 1.479, 1.48, 1.481, 1.482, 1.484, 1.485, 1.486, 1.487, 1.488, 1.489, 1.49, 1.49, 1.49, 1.49, 1.49, 1.49, 1.489, 1.487, 1.485, 1.484, 1.483, 1.482, 1.481, 1.48, 1.479, 1.477, 1.476, 1.476, 1.476,
+ 1.477, 1.478, 1.479, 1.48, 1.481, 1.482, 1.483, 1.484, 1.485, 1.486, 1.486, 1.487, 1.488, 1.488, 1.489, 1.488, 1.488, 1.488, 1.488, 1.487, 1.485, 1.484, 1.484, 1.483, 1.482, 1.481, 1.48, 1.479, 1.477, 1.476, 1.476, 1.476,
+ 1.477, 1.478, 1.479, 1.48, 1.481, 1.482, 1.483, 1.484, 1.485, 1.485, 1.486, 1.486, 1.487, 1.487, 1.487, 1.486, 1.486, 1.486, 1.486, 1.486, 1.485, 1.484, 1.483, 1.483, 1.482, 1.481, 1.48, 1.479, 1.477, 1.476, 1.476, 1.476,
+ 1.477, 1.478, 1.479, 1.48, 1.481, 1.482, 1.483, 1.484, 1.484, 1.485, 1.485, 1.486, 1.486, 1.485, 1.485, 1.484, 1.484, 1.484, 1.485, 1.485, 1.484, 1.483, 1.483, 1.482, 1.482, 1.481, 1.48, 1.479, 1.478, 1.477, 1.476, 1.476,
+ 1.477, 1.478, 1.479, 1.48, 1.482, 1.482, 1.483, 1.483, 1.484, 1.484, 1.485, 1.485, 1.484, 1.484, 1.483, 1.482, 1.482, 1.483, 1.484, 1.484, 1.483, 1.483, 1.482, 1.482, 1.481, 1.481, 1.48, 1.479, 1.478, 1.477, 1.476, 1.476,
+ 1.477, 1.478, 1.479, 1.48, 1.482, 1.482, 1.483, 1.483, 1.484, 1.484, 1.485, 1.484, 1.484, 1.483, 1.482, 1.482, 1.482, 1.482, 1.483, 1.483, 1.483, 1.483, 1.482, 1.482, 1.481, 1.48, 1.48, 1.479, 1.478, 1.478, 1.477, 1.477,
+ 1.477, 1.478, 1.479, 1.48, 1.482, 1.482, 1.483, 1.483, 1.484, 1.484, 1.484, 1.484, 1.483, 1.483, 1.482, 1.482, 1.482, 1.482, 1.483, 1.483, 1.482, 1.482, 1.482, 1.481, 1.48, 1.48, 1.479, 1.479, 1.479, 1.478, 1.478, 1.478,
+ 1.477, 1.478, 1.479, 1.48, 1.482, 1.482, 1.483, 1.483, 1.484, 1.484, 1.484, 1.483, 1.482, 1.482, 1.482, 1.482, 1.482, 1.482, 1.482, 1.482, 1.482, 1.482, 1.481, 1.481, 1.48, 1.479, 1.479, 1.479, 1.479, 1.479, 1.479, 1.479
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.764, 2.711, 2.658, 2.504, 2.342, 2.204, 2.07, 1.937, 1.803, 1.706, 1.622, 1.582, 1.565, 1.558, 1.558, 1.558, 1.558, 1.56, 1.565, 1.586, 1.631, 1.7, 1.818, 1.941, 2.081, 2.222, 2.37, 2.522, 2.711, 2.893, 2.968, 3.043,
+ 2.725, 2.642, 2.56, 2.405, 2.246, 2.115, 1.989, 1.86, 1.732, 1.642, 1.567, 1.527, 1.504, 1.493, 1.488, 1.486, 1.486, 1.492, 1.503, 1.528, 1.574, 1.64, 1.746, 1.86, 1.995, 2.131, 2.274, 2.423, 2.608, 2.788, 2.888, 2.988,
+ 2.686, 2.574, 2.462, 2.307, 2.149, 2.026, 1.908, 1.784, 1.66, 1.578, 1.511, 1.471, 1.443, 1.427, 1.419, 1.415, 1.415, 1.423, 1.44, 1.47, 1.516, 1.579, 1.674, 1.779, 1.909, 2.04, 2.179, 2.323, 2.504, 2.682, 2.808, 2.933,
+ 2.636, 2.502, 2.368, 2.212, 2.055, 1.937, 1.825, 1.709, 1.592, 1.517, 1.457, 1.416, 1.385, 1.365, 1.353, 1.347, 1.347, 1.358, 1.38, 1.413, 1.46, 1.52, 1.605, 1.7, 1.824, 1.949, 2.085, 2.227, 2.405, 2.581, 2.725, 2.87,
+ 2.537, 2.413, 2.289, 2.133, 1.974, 1.853, 1.737, 1.637, 1.54, 1.468, 1.406, 1.366, 1.336, 1.317, 1.306, 1.301, 1.301, 1.311, 1.332, 1.362, 1.407, 1.464, 1.545, 1.633, 1.743, 1.858, 1.999, 2.144, 2.323, 2.5, 2.635, 2.771,
+ 2.439, 2.325, 2.211, 2.053, 1.892, 1.768, 1.649, 1.564, 1.488, 1.42, 1.355, 1.315, 1.288, 1.27, 1.259, 1.254, 1.254, 1.263, 1.283, 1.312, 1.355, 1.409, 1.485, 1.567, 1.662, 1.767, 1.912, 2.062, 2.242, 2.419, 2.545, 2.672,
+ 2.362, 2.251, 2.14, 1.982, 1.82, 1.695, 1.576, 1.501, 1.437, 1.375, 1.313, 1.273, 1.243, 1.225, 1.214, 1.209, 1.209, 1.218, 1.238, 1.267, 1.31, 1.361, 1.43, 1.504, 1.591, 1.689, 1.835, 1.986, 2.167, 2.345, 2.469, 2.594,
+ 2.323, 2.202, 2.081, 1.924, 1.764, 1.643, 1.529, 1.454, 1.389, 1.335, 1.286, 1.244, 1.206, 1.184, 1.172, 1.166, 1.166, 1.177, 1.201, 1.233, 1.278, 1.327, 1.385, 1.45, 1.537, 1.634, 1.776, 1.924, 2.105, 2.283, 2.418, 2.552,
+ 2.285, 2.154, 2.023, 1.866, 1.708, 1.592, 1.481, 1.406, 1.341, 1.296, 1.258, 1.215, 1.169, 1.143, 1.13, 1.124, 1.124, 1.137, 1.164, 1.199, 1.246, 1.292, 1.339, 1.397, 1.483, 1.58, 1.717, 1.861, 2.043, 2.222, 2.366, 2.51,
+ 2.258, 2.116, 1.975, 1.82, 1.665, 1.552, 1.446, 1.372, 1.306, 1.262, 1.227, 1.185, 1.141, 1.113, 1.097, 1.089, 1.089, 1.103, 1.134, 1.17, 1.214, 1.259, 1.303, 1.358, 1.443, 1.537, 1.671, 1.81, 1.992, 2.171, 2.325, 2.479,
+ 2.24, 2.088, 1.936, 1.784, 1.631, 1.522, 1.42, 1.347, 1.282, 1.234, 1.192, 1.156, 1.121, 1.093, 1.07, 1.06, 1.06, 1.076, 1.11, 1.146, 1.184, 1.225, 1.274, 1.332, 1.414, 1.505, 1.634, 1.77, 1.951, 2.13, 2.294, 2.457,
+ 2.223, 2.06, 1.898, 1.747, 1.597, 1.492, 1.394, 1.323, 1.258, 1.206, 1.158, 1.126, 1.101, 1.074, 1.044, 1.03, 1.031, 1.049, 1.087, 1.122, 1.153, 1.192, 1.245, 1.306, 1.385, 1.473, 1.598, 1.73, 1.91, 2.089, 2.262, 2.435,
+ 2.218, 2.047, 1.876, 1.727, 1.579, 1.476, 1.38, 1.309, 1.245, 1.19, 1.138, 1.104, 1.078, 1.053, 1.028, 1.018, 1.019, 1.035, 1.065, 1.097, 1.131, 1.172, 1.228, 1.29, 1.367, 1.453, 1.575, 1.705, 1.884, 2.062, 2.245, 2.428,
+ 2.218, 2.039, 1.861, 1.712, 1.566, 1.465, 1.37, 1.299, 1.234, 1.178, 1.124, 1.085, 1.054, 1.032, 1.017, 1.012, 1.015, 1.026, 1.045, 1.072, 1.112, 1.158, 1.216, 1.279, 1.354, 1.438, 1.559, 1.686, 1.863, 2.041, 2.234, 2.427,
+ 2.218, 2.032, 1.846, 1.698, 1.553, 1.453, 1.36, 1.29, 1.224, 1.166, 1.109, 1.066, 1.03, 1.012, 1.006, 1.005, 1.011, 1.017, 1.024, 1.047, 1.093, 1.143, 1.203, 1.267, 1.341, 1.424, 1.542, 1.667, 1.842, 2.02, 2.223, 2.426,
+ 2.218, 2.031, 1.844, 1.697, 1.552, 1.452, 1.36, 1.289, 1.223, 1.164, 1.108, 1.064, 1.027, 1.009, 1.004, 1.004, 1.009, 1.015, 1.022, 1.045, 1.091, 1.142, 1.202, 1.266, 1.34, 1.423, 1.54, 1.665, 1.841, 2.018, 2.222, 2.426,
+ 2.218, 2.031, 1.844, 1.697, 1.552, 1.452, 1.36, 1.289, 1.223, 1.164, 1.108, 1.064, 1.027, 1.009, 1.004, 1.004, 1.008, 1.014, 1.021, 1.045, 1.091, 1.142, 1.202, 1.266, 1.34, 1.423, 1.54, 1.665, 1.841, 2.018, 2.222, 2.426,
+ 2.218, 2.032, 1.846, 1.699, 1.554, 1.454, 1.361, 1.29, 1.225, 1.166, 1.11, 1.066, 1.028, 1.01, 1.004, 1.004, 1.007, 1.014, 1.023, 1.047, 1.093, 1.143, 1.203, 1.267, 1.341, 1.424, 1.542, 1.667, 1.842, 2.02, 2.223, 2.426,
+ 2.22, 2.044, 1.869, 1.719, 1.572, 1.471, 1.376, 1.305, 1.24, 1.183, 1.129, 1.089, 1.054, 1.031, 1.014, 1.009, 1.013, 1.026, 1.048, 1.077, 1.115, 1.159, 1.218, 1.28, 1.356, 1.44, 1.56, 1.687, 1.863, 2.041, 2.234, 2.427,
+ 2.222, 2.056, 1.891, 1.74, 1.591, 1.487, 1.391, 1.319, 1.254, 1.2, 1.149, 1.111, 1.08, 1.052, 1.025, 1.015, 1.019, 1.038, 1.073, 1.106, 1.136, 1.175, 1.232, 1.294, 1.371, 1.457, 1.578, 1.706, 1.884, 2.062, 2.245, 2.427,
+ 2.231, 2.074, 1.918, 1.766, 1.614, 1.508, 1.41, 1.338, 1.273, 1.22, 1.173, 1.136, 1.106, 1.074, 1.041, 1.028, 1.033, 1.055, 1.097, 1.134, 1.16, 1.196, 1.251, 1.311, 1.39, 1.477, 1.601, 1.732, 1.911, 2.089, 2.262, 2.434,
+ 2.255, 2.108, 1.961, 1.807, 1.652, 1.542, 1.439, 1.365, 1.299, 1.25, 1.207, 1.167, 1.13, 1.099, 1.074, 1.063, 1.066, 1.084, 1.119, 1.155, 1.192, 1.233, 1.281, 1.338, 1.419, 1.509, 1.637, 1.772, 1.953, 2.132, 2.294, 2.456,
+ 2.279, 2.142, 2.004, 1.848, 1.69, 1.576, 1.468, 1.393, 1.326, 1.279, 1.241, 1.198, 1.153, 1.125, 1.107, 1.099, 1.1, 1.113, 1.141, 1.177, 1.223, 1.269, 1.312, 1.366, 1.449, 1.541, 1.673, 1.812, 1.995, 2.175, 2.327, 2.479,
+ 2.317, 2.187, 2.057, 1.898, 1.737, 1.619, 1.508, 1.431, 1.363, 1.314, 1.273, 1.229, 1.183, 1.157, 1.143, 1.137, 1.137, 1.148, 1.171, 1.205, 1.255, 1.304, 1.35, 1.406, 1.49, 1.584, 1.72, 1.863, 2.047, 2.228, 2.37, 2.512,
+ 2.37, 2.247, 2.123, 1.961, 1.797, 1.675, 1.559, 1.481, 1.413, 1.356, 1.303, 1.259, 1.22, 1.196, 1.184, 1.178, 1.178, 1.188, 1.21, 1.242, 1.287, 1.337, 1.396, 1.462, 1.545, 1.64, 1.781, 1.928, 2.112, 2.292, 2.425, 2.559,
+ 2.424, 2.306, 2.188, 2.024, 1.857, 1.731, 1.611, 1.532, 1.462, 1.397, 1.334, 1.29, 1.256, 1.236, 1.224, 1.219, 1.219, 1.229, 1.249, 1.278, 1.32, 1.371, 1.443, 1.518, 1.6, 1.695, 1.842, 1.994, 2.177, 2.356, 2.48, 2.605,
+ 2.513, 2.39, 2.266, 2.102, 1.935, 1.808, 1.687, 1.597, 1.516, 1.446, 1.379, 1.335, 1.302, 1.282, 1.271, 1.266, 1.266, 1.276, 1.296, 1.325, 1.366, 1.42, 1.497, 1.58, 1.674, 1.779, 1.923, 2.073, 2.255, 2.434, 2.563, 2.693,
+ 2.621, 2.486, 2.352, 2.189, 2.024, 1.897, 1.775, 1.672, 1.572, 1.498, 1.433, 1.388, 1.353, 1.333, 1.322, 1.316, 1.316, 1.326, 1.348, 1.378, 1.421, 1.476, 1.556, 1.645, 1.759, 1.877, 2.016, 2.159, 2.341, 2.52, 2.662, 2.804,
+ 2.728, 2.583, 2.438, 2.276, 2.113, 1.986, 1.864, 1.746, 1.628, 1.55, 1.487, 1.441, 1.405, 1.383, 1.372, 1.367, 1.367, 1.377, 1.399, 1.431, 1.476, 1.533, 1.615, 1.71, 1.843, 1.976, 2.108, 2.246, 2.427, 2.606, 2.76, 2.915,
+ 2.781, 2.661, 2.54, 2.378, 2.213, 2.08, 1.952, 1.826, 1.699, 1.613, 1.542, 1.497, 1.465, 1.447, 1.439, 1.436, 1.436, 1.444, 1.461, 1.489, 1.533, 1.594, 1.686, 1.791, 1.93, 2.069, 2.206, 2.349, 2.533, 2.714, 2.845, 2.977,
+ 2.822, 2.734, 2.646, 2.483, 2.316, 2.176, 2.04, 1.907, 1.774, 1.678, 1.597, 1.553, 1.527, 1.515, 1.511, 1.509, 1.509, 1.514, 1.525, 1.549, 1.591, 1.655, 1.76, 1.875, 2.018, 2.161, 2.305, 2.454, 2.643, 2.827, 2.927, 3.027,
+ 2.862, 2.807, 2.752, 2.589, 2.418, 2.271, 2.128, 1.988, 1.848, 1.744, 1.652, 1.608, 1.59, 1.582, 1.582, 1.582, 1.582, 1.584, 1.589, 1.608, 1.65, 1.716, 1.833, 1.958, 2.105, 2.253, 2.404, 2.56, 2.754, 2.94, 3.009, 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/pisp/data/imx415.json b/src/ipa/rpi/pisp/data/imx415.json
new file mode 100755
index 00000000..824a5371
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx415.json
@@ -0,0 +1,1159 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 19230,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 1198,
+ "reference_Y": 14876
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 17,
+ "reference_slope": 3.439
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 193,
+ "slope": 0.00902
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2698.0, 0.7746, 0.2063,
+ 2930.0, 0.7579, 0.2155,
+ 3643.0, 0.6412, 0.2905,
+ 4605.0, 0.5038, 0.4099,
+ 5658.0, 0.4541, 0.4634
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01128,
+ "transverse_neg": 0.01437
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.027, 1.027, 1.022, 1.018, 1.014, 1.012, 1.009, 1.009, 1.007, 1.006, 1.004, 1.002, 1.002, 1.001, 1.001, 1.001, 1.001, 1.002, 1.003, 1.004, 1.006, 1.008, 1.009, 1.011, 1.014, 1.018, 1.019, 1.026, 1.029, 1.033, 1.039, 1.039,
+ 1.039, 1.034, 1.031, 1.028, 1.027, 1.025, 1.023, 1.021, 1.019, 1.019, 1.017, 1.016, 1.016, 1.016, 1.016, 1.016, 1.016, 1.016, 1.017, 1.018, 1.019, 1.021, 1.024, 1.025, 1.027, 1.029, 1.032, 1.035, 1.037, 1.042, 1.046, 1.049,
+ 1.041, 1.038, 1.033, 1.029, 1.027, 1.026, 1.024, 1.023, 1.021, 1.019, 1.018, 1.017, 1.016, 1.016, 1.016, 1.017, 1.017, 1.017, 1.018, 1.019, 1.021, 1.022, 1.024, 1.026, 1.029, 1.031, 1.034, 1.037, 1.042, 1.044, 1.049, 1.052,
+ 1.041, 1.037, 1.032, 1.028, 1.027, 1.025, 1.023, 1.022, 1.021, 1.019, 1.018, 1.017, 1.016, 1.016, 1.016, 1.016, 1.016, 1.017, 1.018, 1.019, 1.021, 1.022, 1.024, 1.026, 1.028, 1.031, 1.033, 1.037, 1.039, 1.044, 1.047, 1.051,
+ 1.039, 1.036, 1.031, 1.027, 1.026, 1.024, 1.022, 1.021, 1.019, 1.018, 1.017, 1.016, 1.015, 1.015, 1.015, 1.015, 1.015, 1.016, 1.017, 1.018, 1.019, 1.021, 1.023, 1.025, 1.028, 1.031, 1.033, 1.036, 1.039, 1.043, 1.046, 1.049,
+ 1.037, 1.034, 1.029, 1.026, 1.024, 1.023, 1.021, 1.019, 1.018, 1.017, 1.016, 1.015, 1.015, 1.013, 1.014, 1.014, 1.014, 1.014, 1.016, 1.017, 1.019, 1.019, 1.022, 1.025, 1.027, 1.029, 1.033, 1.036, 1.039, 1.042, 1.045, 1.049,
+ 1.036, 1.031, 1.029, 1.025, 1.023, 1.021, 1.019, 1.018, 1.017, 1.016, 1.015, 1.014, 1.013, 1.013, 1.012, 1.013, 1.013, 1.014, 1.015, 1.016, 1.018, 1.019, 1.021, 1.023, 1.026, 1.029, 1.032, 1.035, 1.039, 1.041, 1.044, 1.048,
+ 1.034, 1.031, 1.028, 1.024, 1.022, 1.021, 1.018, 1.017, 1.016, 1.015, 1.014, 1.013, 1.012, 1.011, 1.011, 1.012, 1.012, 1.013, 1.014, 1.015, 1.017, 1.018, 1.021, 1.022, 1.025, 1.028, 1.032, 1.035, 1.038, 1.041, 1.044, 1.048,
+ 1.032, 1.031, 1.027, 1.023, 1.021, 1.019, 1.017, 1.016, 1.015, 1.014, 1.013, 1.012, 1.011, 1.011, 1.011, 1.011, 1.012, 1.012, 1.013, 1.014, 1.016, 1.018, 1.019, 1.021, 1.024, 1.027, 1.031, 1.033, 1.037, 1.041, 1.043, 1.048,
+ 1.031, 1.029, 1.025, 1.023, 1.021, 1.019, 1.017, 1.015, 1.014, 1.013, 1.012, 1.011, 1.011, 1.009, 1.009, 1.009, 1.011, 1.012, 1.012, 1.013, 1.015, 1.017, 1.019, 1.021, 1.023, 1.027, 1.031, 1.033, 1.036, 1.041, 1.043, 1.048,
+ 1.031, 1.029, 1.025, 1.023, 1.021, 1.018, 1.016, 1.015, 1.014, 1.012, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.011, 1.011, 1.012, 1.014, 1.015, 1.017, 1.019, 1.021, 1.023, 1.027, 1.031, 1.032, 1.036, 1.041, 1.043, 1.047,
+ 1.029, 1.027, 1.025, 1.022, 1.021, 1.018, 1.015, 1.014, 1.013, 1.012, 1.012, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.012, 1.014, 1.015, 1.017, 1.019, 1.021, 1.024, 1.028, 1.031, 1.033, 1.036, 1.041, 1.045, 1.048,
+ 1.029, 1.027, 1.024, 1.022, 1.021, 1.018, 1.016, 1.014, 1.013, 1.012, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.013, 1.014, 1.015, 1.016, 1.018, 1.021, 1.024, 1.027, 1.031, 1.033, 1.037, 1.041, 1.045, 1.047,
+ 1.029, 1.027, 1.024, 1.022, 1.021, 1.018, 1.016, 1.014, 1.013, 1.012, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.013, 1.013, 1.015, 1.016, 1.018, 1.021, 1.024, 1.027, 1.031, 1.034, 1.037, 1.041, 1.045, 1.048,
+ 1.029, 1.027, 1.024, 1.021, 1.021, 1.018, 1.016, 1.014, 1.013, 1.012, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.012, 1.013, 1.015, 1.016, 1.018, 1.019, 1.023, 1.027, 1.031, 1.033, 1.037, 1.039, 1.044, 1.049,
+ 1.029, 1.027, 1.023, 1.022, 1.021, 1.019, 1.016, 1.014, 1.013, 1.012, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.012, 1.013, 1.015, 1.016, 1.018, 1.021, 1.023, 1.028, 1.031, 1.034, 1.037, 1.039, 1.044, 1.049,
+ 1.029, 1.027, 1.024, 1.022, 1.021, 1.019, 1.017, 1.015, 1.013, 1.012, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.012, 1.014, 1.015, 1.017, 1.018, 1.021, 1.024, 1.028, 1.031, 1.034, 1.038, 1.041, 1.045, 1.049,
+ 1.031, 1.027, 1.025, 1.023, 1.022, 1.019, 1.018, 1.015, 1.014, 1.013, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.011, 1.011, 1.013, 1.014, 1.016, 1.017, 1.019, 1.021, 1.023, 1.028, 1.031, 1.034, 1.038, 1.041, 1.046, 1.051,
+ 1.031, 1.027, 1.025, 1.023, 1.022, 1.021, 1.018, 1.016, 1.014, 1.013, 1.012, 1.011, 1.011, 1.009, 1.011, 1.011, 1.011, 1.012, 1.013, 1.015, 1.016, 1.018, 1.019, 1.021, 1.024, 1.028, 1.032, 1.035, 1.039, 1.041, 1.046, 1.051,
+ 1.031, 1.028, 1.025, 1.023, 1.023, 1.019, 1.018, 1.016, 1.014, 1.014, 1.013, 1.012, 1.011, 1.011, 1.011, 1.011, 1.011, 1.012, 1.013, 1.015, 1.017, 1.018, 1.021, 1.022, 1.025, 1.028, 1.032, 1.035, 1.039, 1.043, 1.047, 1.051,
+ 1.031, 1.028, 1.025, 1.024, 1.023, 1.021, 1.018, 1.017, 1.015, 1.014, 1.013, 1.012, 1.011, 1.011, 1.011, 1.012, 1.012, 1.012, 1.014, 1.015, 1.018, 1.019, 1.021, 1.023, 1.026, 1.029, 1.033, 1.037, 1.041, 1.044, 1.048, 1.052,
+ 1.031, 1.028, 1.025, 1.024, 1.023, 1.022, 1.019, 1.017, 1.015, 1.015, 1.013, 1.012, 1.012, 1.012, 1.012, 1.013, 1.013, 1.014, 1.014, 1.017, 1.019, 1.019, 1.022, 1.024, 1.027, 1.031, 1.034, 1.038, 1.041, 1.046, 1.051, 1.055,
+ 1.031, 1.029, 1.026, 1.025, 1.023, 1.022, 1.019, 1.017, 1.016, 1.015, 1.014, 1.013, 1.012, 1.012, 1.013, 1.013, 1.014, 1.015, 1.016, 1.018, 1.019, 1.021, 1.023, 1.025, 1.028, 1.032, 1.035, 1.038, 1.042, 1.047, 1.052, 1.056,
+ 1.033, 1.031, 1.027, 1.025, 1.024, 1.023, 1.021, 1.019, 1.017, 1.016, 1.015, 1.014, 1.014, 1.013, 1.014, 1.014, 1.015, 1.016, 1.017, 1.019, 1.021, 1.022, 1.025, 1.027, 1.029, 1.033, 1.036, 1.039, 1.044, 1.048, 1.053, 1.056,
+ 1.036, 1.032, 1.029, 1.027, 1.026, 1.025, 1.022, 1.021, 1.019, 1.018, 1.017, 1.016, 1.015, 1.014, 1.015, 1.016, 1.017, 1.017, 1.018, 1.021, 1.022, 1.024, 1.027, 1.029, 1.031, 1.035, 1.038, 1.041, 1.046, 1.049, 1.054, 1.058,
+ 1.038, 1.035, 1.031, 1.029, 1.028, 1.026, 1.025, 1.022, 1.021, 1.019, 1.019, 1.018, 1.017, 1.016, 1.016, 1.017, 1.018, 1.019, 1.021, 1.022, 1.024, 1.025, 1.028, 1.031, 1.034, 1.036, 1.041, 1.043, 1.049, 1.052, 1.057, 1.059,
+ 1.041, 1.037, 1.034, 1.031, 1.029, 1.028, 1.027, 1.024, 1.022, 1.021, 1.021, 1.019, 1.019, 1.018, 1.018, 1.019, 1.019, 1.021, 1.022, 1.024, 1.025, 1.028, 1.031, 1.033, 1.035, 1.038, 1.041, 1.045, 1.051, 1.055, 1.059, 1.063,
+ 1.043, 1.039, 1.036, 1.033, 1.031, 1.029, 1.028, 1.027, 1.024, 1.023, 1.022, 1.021, 1.021, 1.021, 1.021, 1.021, 1.021, 1.022, 1.024, 1.026, 1.028, 1.029, 1.032, 1.035, 1.037, 1.039, 1.044, 1.049, 1.054, 1.058, 1.063, 1.066,
+ 1.045, 1.043, 1.038, 1.034, 1.032, 1.031, 1.029, 1.029, 1.027, 1.026, 1.024, 1.023, 1.022, 1.021, 1.021, 1.022, 1.022, 1.024, 1.026, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.042, 1.047, 1.049, 1.056, 1.061, 1.064, 1.067,
+ 1.048, 1.043, 1.039, 1.035, 1.033, 1.031, 1.031, 1.029, 1.028, 1.027, 1.026, 1.026, 1.024, 1.023, 1.023, 1.023, 1.024, 1.025, 1.027, 1.029, 1.031, 1.033, 1.035, 1.038, 1.039, 1.044, 1.047, 1.053, 1.058, 1.063, 1.066, 1.071,
+ 1.049, 1.045, 1.041, 1.038, 1.034, 1.032, 1.031, 1.031, 1.029, 1.028, 1.027, 1.027, 1.026, 1.024, 1.024, 1.024, 1.025, 1.027, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.042, 1.046, 1.051, 1.055, 1.061, 1.064, 1.069, 1.072,
+ 1.051, 1.049, 1.044, 1.041, 1.036, 1.033, 1.032, 1.031, 1.031, 1.029, 1.028, 1.028, 1.027, 1.027, 1.025, 1.025, 1.026, 1.028, 1.029, 1.031, 1.033, 1.034, 1.038, 1.039, 1.043, 1.048, 1.053, 1.058, 1.062, 1.064, 1.071, 1.074
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.028, 1.028, 1.022, 1.014, 1.008, 1.006, 1.006, 1.005, 1.004, 1.003, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.002, 1.003, 1.004, 1.006, 1.008, 1.009, 1.011, 1.015, 1.018, 1.021, 1.028, 1.034, 1.039, 1.044, 1.044,
+ 1.038, 1.031, 1.025, 1.023, 1.022, 1.021, 1.021, 1.021, 1.019, 1.019, 1.017, 1.016, 1.015, 1.015, 1.015, 1.016, 1.017, 1.018, 1.019, 1.019, 1.021, 1.023, 1.024, 1.027, 1.028, 1.031, 1.034, 1.038, 1.042, 1.047, 1.054, 1.058,
+ 1.043, 1.038, 1.031, 1.024, 1.023, 1.022, 1.021, 1.021, 1.021, 1.019, 1.018, 1.017, 1.016, 1.016, 1.017, 1.017, 1.018, 1.018, 1.019, 1.021, 1.022, 1.024, 1.026, 1.028, 1.031, 1.033, 1.037, 1.041, 1.047, 1.053, 1.058, 1.062,
+ 1.043, 1.037, 1.029, 1.024, 1.022, 1.021, 1.021, 1.019, 1.019, 1.019, 1.017, 1.016, 1.016, 1.016, 1.016, 1.017, 1.018, 1.018, 1.019, 1.021, 1.022, 1.024, 1.026, 1.029, 1.031, 1.034, 1.038, 1.042, 1.046, 1.052, 1.058, 1.062,
+ 1.041, 1.036, 1.029, 1.024, 1.022, 1.021, 1.019, 1.019, 1.018, 1.017, 1.016, 1.015, 1.015, 1.015, 1.016, 1.016, 1.016, 1.017, 1.018, 1.021, 1.022, 1.024, 1.026, 1.028, 1.031, 1.033, 1.037, 1.042, 1.047, 1.051, 1.058, 1.062,
+ 1.039, 1.034, 1.028, 1.023, 1.021, 1.019, 1.018, 1.018, 1.017, 1.016, 1.015, 1.015, 1.014, 1.014, 1.014, 1.015, 1.016, 1.016, 1.017, 1.019, 1.022, 1.024, 1.025, 1.028, 1.031, 1.033, 1.037, 1.041, 1.047, 1.051, 1.058, 1.062,
+ 1.039, 1.033, 1.027, 1.022, 1.019, 1.018, 1.017, 1.017, 1.016, 1.015, 1.014, 1.014, 1.013, 1.013, 1.013, 1.013, 1.015, 1.015, 1.016, 1.019, 1.021, 1.023, 1.025, 1.027, 1.031, 1.033, 1.037, 1.041, 1.048, 1.051, 1.057, 1.062,
+ 1.038, 1.032, 1.026, 1.021, 1.018, 1.017, 1.016, 1.015, 1.014, 1.014, 1.013, 1.012, 1.012, 1.012, 1.012, 1.012, 1.013, 1.014, 1.016, 1.017, 1.019, 1.022, 1.023, 1.026, 1.029, 1.033, 1.037, 1.041, 1.048, 1.052, 1.058, 1.063,
+ 1.037, 1.031, 1.026, 1.021, 1.018, 1.016, 1.015, 1.014, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.011, 1.011, 1.012, 1.014, 1.015, 1.016, 1.018, 1.021, 1.023, 1.026, 1.028, 1.032, 1.037, 1.042, 1.048, 1.053, 1.059, 1.066,
+ 1.037, 1.029, 1.025, 1.021, 1.018, 1.016, 1.014, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.009, 1.011, 1.011, 1.012, 1.013, 1.014, 1.016, 1.018, 1.021, 1.023, 1.026, 1.028, 1.033, 1.037, 1.042, 1.049, 1.053, 1.061, 1.066,
+ 1.035, 1.029, 1.024, 1.019, 1.017, 1.015, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.009, 1.009, 1.009, 1.011, 1.012, 1.013, 1.014, 1.016, 1.018, 1.021, 1.023, 1.026, 1.028, 1.034, 1.037, 1.042, 1.049, 1.054, 1.061, 1.067,
+ 1.034, 1.029, 1.023, 1.019, 1.017, 1.014, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.009, 1.009, 1.009, 1.011, 1.011, 1.012, 1.014, 1.016, 1.019, 1.021, 1.024, 1.027, 1.029, 1.034, 1.039, 1.042, 1.049, 1.054, 1.062, 1.067,
+ 1.034, 1.029, 1.023, 1.019, 1.017, 1.014, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.009, 1.009, 1.009, 1.009, 1.011, 1.012, 1.015, 1.016, 1.018, 1.021, 1.024, 1.027, 1.029, 1.034, 1.038, 1.043, 1.049, 1.055, 1.062, 1.067,
+ 1.035, 1.029, 1.023, 1.019, 1.018, 1.015, 1.013, 1.013, 1.012, 1.012, 1.011, 1.011, 1.011, 1.009, 1.009, 1.011, 1.012, 1.013, 1.015, 1.017, 1.019, 1.021, 1.024, 1.028, 1.031, 1.034, 1.039, 1.043, 1.051, 1.056, 1.064, 1.069,
+ 1.035, 1.031, 1.023, 1.019, 1.018, 1.017, 1.014, 1.013, 1.012, 1.012, 1.012, 1.011, 1.009, 1.009, 1.009, 1.011, 1.012, 1.013, 1.015, 1.017, 1.019, 1.021, 1.024, 1.027, 1.031, 1.035, 1.041, 1.044, 1.051, 1.057, 1.064, 1.071,
+ 1.036, 1.032, 1.023, 1.021, 1.019, 1.017, 1.015, 1.014, 1.013, 1.013, 1.012, 1.011, 1.011, 1.011, 1.011, 1.011, 1.012, 1.013, 1.016, 1.017, 1.019, 1.021, 1.023, 1.027, 1.031, 1.036, 1.041, 1.045, 1.051, 1.057, 1.066, 1.072,
+ 1.037, 1.032, 1.025, 1.021, 1.019, 1.018, 1.017, 1.014, 1.013, 1.013, 1.013, 1.012, 1.011, 1.011, 1.011, 1.012, 1.013, 1.014, 1.016, 1.017, 1.019, 1.022, 1.024, 1.027, 1.031, 1.036, 1.041, 1.045, 1.051, 1.057, 1.066, 1.073,
+ 1.038, 1.032, 1.025, 1.023, 1.021, 1.019, 1.017, 1.015, 1.014, 1.014, 1.013, 1.013, 1.012, 1.011, 1.012, 1.012, 1.014, 1.014, 1.016, 1.019, 1.021, 1.022, 1.025, 1.027, 1.031, 1.036, 1.041, 1.047, 1.052, 1.058, 1.066, 1.074,
+ 1.039, 1.033, 1.026, 1.024, 1.022, 1.021, 1.018, 1.016, 1.015, 1.015, 1.014, 1.014, 1.013, 1.013, 1.013, 1.014, 1.014, 1.015, 1.017, 1.021, 1.022, 1.023, 1.026, 1.028, 1.032, 1.036, 1.041, 1.047, 1.053, 1.059, 1.067, 1.076,
+ 1.041, 1.034, 1.026, 1.025, 1.023, 1.021, 1.019, 1.017, 1.016, 1.015, 1.015, 1.014, 1.014, 1.014, 1.014, 1.014, 1.015, 1.016, 1.018, 1.021, 1.023, 1.026, 1.027, 1.029, 1.033, 1.038, 1.041, 1.048, 1.054, 1.061, 1.069, 1.077,
+ 1.041, 1.034, 1.027, 1.025, 1.024, 1.022, 1.021, 1.018, 1.016, 1.016, 1.015, 1.015, 1.014, 1.015, 1.015, 1.016, 1.017, 1.017, 1.021, 1.021, 1.024, 1.027, 1.029, 1.032, 1.034, 1.039, 1.043, 1.049, 1.056, 1.062, 1.071, 1.077,
+ 1.041, 1.034, 1.028, 1.025, 1.024, 1.022, 1.021, 1.019, 1.018, 1.017, 1.017, 1.016, 1.016, 1.016, 1.016, 1.017, 1.018, 1.019, 1.021, 1.023, 1.026, 1.029, 1.031, 1.034, 1.036, 1.041, 1.046, 1.051, 1.058, 1.063, 1.071, 1.078,
+ 1.042, 1.034, 1.029, 1.026, 1.025, 1.024, 1.021, 1.019, 1.019, 1.018, 1.018, 1.017, 1.017, 1.017, 1.018, 1.019, 1.019, 1.021, 1.022, 1.024, 1.027, 1.029, 1.033, 1.035, 1.038, 1.042, 1.047, 1.054, 1.061, 1.067, 1.074, 1.079,
+ 1.043, 1.036, 1.031, 1.027, 1.026, 1.025, 1.023, 1.022, 1.021, 1.021, 1.019, 1.019, 1.019, 1.018, 1.019, 1.021, 1.021, 1.022, 1.024, 1.026, 1.029, 1.033, 1.035, 1.037, 1.041, 1.044, 1.049, 1.056, 1.062, 1.067, 1.077, 1.081,
+ 1.045, 1.038, 1.034, 1.029, 1.028, 1.026, 1.025, 1.023, 1.023, 1.022, 1.021, 1.021, 1.019, 1.019, 1.021, 1.022, 1.023, 1.024, 1.026, 1.028, 1.032, 1.035, 1.037, 1.039, 1.042, 1.047, 1.052, 1.057, 1.064, 1.069, 1.078, 1.083,
+ 1.047, 1.041, 1.036, 1.032, 1.029, 1.028, 1.028, 1.026, 1.026, 1.025, 1.024, 1.023, 1.022, 1.022, 1.023, 1.023, 1.026, 1.027, 1.028, 1.031, 1.035, 1.037, 1.039, 1.043, 1.046, 1.051, 1.055, 1.062, 1.067, 1.074, 1.082, 1.085,
+ 1.049, 1.043, 1.038, 1.035, 1.032, 1.031, 1.029, 1.028, 1.027, 1.027, 1.027, 1.026, 1.026, 1.026, 1.026, 1.027, 1.028, 1.029, 1.032, 1.034, 1.037, 1.039, 1.042, 1.046, 1.048, 1.053, 1.059, 1.064, 1.071, 1.078, 1.085, 1.088,
+ 1.052, 1.048, 1.041, 1.037, 1.035, 1.033, 1.033, 1.032, 1.031, 1.031, 1.029, 1.028, 1.028, 1.028, 1.029, 1.029, 1.031, 1.032, 1.035, 1.036, 1.039, 1.042, 1.046, 1.048, 1.052, 1.055, 1.061, 1.067, 1.075, 1.082, 1.088, 1.091,
+ 1.056, 1.051, 1.043, 1.038, 1.037, 1.035, 1.034, 1.034, 1.034, 1.033, 1.032, 1.031, 1.029, 1.031, 1.031, 1.031, 1.033, 1.035, 1.036, 1.039, 1.042, 1.045, 1.048, 1.051, 1.054, 1.059, 1.065, 1.069, 1.081, 1.085, 1.092, 1.097,
+ 1.059, 1.053, 1.047, 1.041, 1.038, 1.036, 1.035, 1.035, 1.035, 1.035, 1.034, 1.033, 1.032, 1.032, 1.032, 1.033, 1.035, 1.037, 1.039, 1.042, 1.043, 1.047, 1.049, 1.053, 1.056, 1.061, 1.067, 1.072, 1.083, 1.088, 1.095, 1.101,
+ 1.062, 1.056, 1.048, 1.043, 1.039, 1.037, 1.037, 1.037, 1.037, 1.037, 1.036, 1.035, 1.034, 1.034, 1.035, 1.036, 1.038, 1.039, 1.041, 1.043, 1.046, 1.049, 1.051, 1.054, 1.059, 1.065, 1.071, 1.076, 1.085, 1.091, 1.097, 1.105,
+ 1.064, 1.062, 1.053, 1.046, 1.041, 1.039, 1.038, 1.038, 1.039, 1.039, 1.038, 1.037, 1.038, 1.036, 1.036, 1.038, 1.039, 1.041, 1.042, 1.044, 1.048, 1.049, 1.053, 1.057, 1.061, 1.065, 1.073, 1.081, 1.087, 1.093, 1.101, 1.106
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.006, 1.006, 1.001, 1.001, 1.007, 1.014, 1.021, 1.026, 1.029, 1.031, 1.035, 1.038, 1.042, 1.043, 1.043, 1.045, 1.045, 1.046, 1.046, 1.048, 1.048, 1.048, 1.047, 1.046, 1.045, 1.044, 1.043, 1.042, 1.044, 1.053, 1.065, 1.071,
+ 1.026, 1.019, 1.018, 1.018, 1.021, 1.025, 1.033, 1.039, 1.046, 1.051, 1.053, 1.056, 1.059, 1.061, 1.063, 1.065, 1.067, 1.068, 1.069, 1.069, 1.069, 1.069, 1.068, 1.067, 1.066, 1.065, 1.062, 1.062, 1.063, 1.065, 1.072, 1.079,
+ 1.029, 1.026, 1.023, 1.023, 1.026, 1.033, 1.039, 1.046, 1.051, 1.054, 1.056, 1.059, 1.061, 1.064, 1.066, 1.068, 1.071, 1.071, 1.072, 1.073, 1.074, 1.074, 1.072, 1.071, 1.069, 1.068, 1.067, 1.065, 1.066, 1.069, 1.078, 1.089,
+ 1.032, 1.029, 1.026, 1.026, 1.029, 1.037, 1.042, 1.049, 1.053, 1.055, 1.059, 1.061, 1.062, 1.065, 1.067, 1.069, 1.072, 1.074, 1.075, 1.076, 1.077, 1.078, 1.078, 1.076, 1.074, 1.072, 1.069, 1.068, 1.068, 1.071, 1.079, 1.089,
+ 1.035, 1.032, 1.029, 1.029, 1.033, 1.039, 1.046, 1.052, 1.055, 1.057, 1.061, 1.062, 1.064, 1.066, 1.068, 1.071, 1.075, 1.075, 1.077, 1.079, 1.081, 1.082, 1.082, 1.081, 1.077, 1.075, 1.073, 1.071, 1.069, 1.072, 1.081, 1.092,
+ 1.035, 1.033, 1.029, 1.029, 1.035, 1.039, 1.047, 1.053, 1.056, 1.059, 1.062, 1.064, 1.065, 1.067, 1.069, 1.072, 1.075, 1.077, 1.079, 1.081, 1.082, 1.084, 1.084, 1.083, 1.081, 1.077, 1.075, 1.074, 1.073, 1.075, 1.081, 1.093,
+ 1.033, 1.032, 1.031, 1.032, 1.036, 1.041, 1.049, 1.053, 1.058, 1.059, 1.062, 1.064, 1.066, 1.068, 1.071, 1.072, 1.074, 1.077, 1.079, 1.082, 1.084, 1.085, 1.086, 1.085, 1.084, 1.081, 1.077, 1.076, 1.075, 1.077, 1.082, 1.093,
+ 1.034, 1.033, 1.032, 1.034, 1.038, 1.043, 1.048, 1.054, 1.058, 1.061, 1.063, 1.065, 1.067, 1.069, 1.071, 1.073, 1.074, 1.077, 1.079, 1.082, 1.084, 1.087, 1.087, 1.087, 1.086, 1.083, 1.081, 1.079, 1.078, 1.079, 1.084, 1.095,
+ 1.034, 1.033, 1.033, 1.034, 1.039, 1.044, 1.049, 1.053, 1.058, 1.061, 1.063, 1.065, 1.067, 1.069, 1.071, 1.073, 1.074, 1.076, 1.079, 1.083, 1.086, 1.088, 1.089, 1.089, 1.087, 1.085, 1.083, 1.081, 1.081, 1.082, 1.088, 1.096,
+ 1.035, 1.035, 1.034, 1.035, 1.039, 1.045, 1.049, 1.053, 1.058, 1.061, 1.062, 1.065, 1.067, 1.069, 1.071, 1.072, 1.074, 1.076, 1.079, 1.083, 1.087, 1.089, 1.091, 1.089, 1.089, 1.086, 1.084, 1.083, 1.082, 1.084, 1.089, 1.098,
+ 1.036, 1.034, 1.034, 1.036, 1.039, 1.046, 1.051, 1.054, 1.058, 1.061, 1.063, 1.065, 1.067, 1.069, 1.071, 1.073, 1.075, 1.077, 1.079, 1.085, 1.089, 1.092, 1.092, 1.091, 1.091, 1.087, 1.085, 1.083, 1.083, 1.085, 1.089, 1.099,
+ 1.039, 1.035, 1.034, 1.036, 1.041, 1.047, 1.049, 1.054, 1.059, 1.062, 1.063, 1.065, 1.067, 1.069, 1.072, 1.074, 1.076, 1.079, 1.081, 1.086, 1.091, 1.093, 1.093, 1.092, 1.091, 1.089, 1.087, 1.085, 1.085, 1.086, 1.091, 1.101,
+ 1.039, 1.037, 1.035, 1.035, 1.042, 1.047, 1.049, 1.055, 1.059, 1.063, 1.065, 1.066, 1.067, 1.069, 1.074, 1.076, 1.078, 1.079, 1.083, 1.086, 1.091, 1.094, 1.095, 1.095, 1.092, 1.091, 1.089, 1.087, 1.086, 1.087, 1.091, 1.103,
+ 1.041, 1.038, 1.036, 1.036, 1.044, 1.049, 1.052, 1.055, 1.059, 1.064, 1.066, 1.067, 1.069, 1.072, 1.074, 1.077, 1.079, 1.081, 1.083, 1.087, 1.091, 1.095, 1.097, 1.097, 1.095, 1.092, 1.091, 1.088, 1.087, 1.089, 1.094, 1.104,
+ 1.044, 1.039, 1.039, 1.039, 1.046, 1.049, 1.053, 1.056, 1.061, 1.065, 1.067, 1.068, 1.071, 1.074, 1.076, 1.078, 1.079, 1.081, 1.083, 1.087, 1.092, 1.096, 1.098, 1.099, 1.097, 1.095, 1.093, 1.091, 1.088, 1.089, 1.097, 1.106,
+ 1.046, 1.043, 1.039, 1.041, 1.046, 1.052, 1.053, 1.056, 1.061, 1.065, 1.068, 1.069, 1.072, 1.075, 1.077, 1.079, 1.079, 1.081, 1.083, 1.088, 1.092, 1.097, 1.099, 1.099, 1.098, 1.096, 1.094, 1.092, 1.089, 1.091, 1.099, 1.107,
+ 1.048, 1.045, 1.042, 1.042, 1.046, 1.052, 1.055, 1.058, 1.061, 1.065, 1.069, 1.071, 1.073, 1.076, 1.078, 1.079, 1.082, 1.083, 1.085, 1.089, 1.093, 1.098, 1.099, 1.099, 1.098, 1.097, 1.095, 1.092, 1.091, 1.092, 1.101, 1.109,
+ 1.049, 1.046, 1.043, 1.044, 1.047, 1.053, 1.055, 1.059, 1.063, 1.065, 1.069, 1.071, 1.073, 1.077, 1.079, 1.082, 1.083, 1.085, 1.086, 1.091, 1.094, 1.098, 1.101, 1.101, 1.098, 1.097, 1.095, 1.093, 1.093, 1.095, 1.102, 1.111,
+ 1.054, 1.048, 1.046, 1.046, 1.047, 1.054, 1.058, 1.062, 1.064, 1.066, 1.071, 1.073, 1.075, 1.078, 1.082, 1.084, 1.085, 1.086, 1.089, 1.092, 1.096, 1.099, 1.101, 1.102, 1.101, 1.099, 1.096, 1.094, 1.094, 1.097, 1.104, 1.115,
+ 1.053, 1.049, 1.047, 1.046, 1.049, 1.056, 1.061, 1.065, 1.066, 1.069, 1.072, 1.075, 1.077, 1.081, 1.083, 1.084, 1.086, 1.088, 1.091, 1.094, 1.098, 1.101, 1.103, 1.103, 1.103, 1.101, 1.099, 1.096, 1.096, 1.099, 1.106, 1.117,
+ 1.053, 1.051, 1.047, 1.047, 1.051, 1.058, 1.065, 1.066, 1.069, 1.071, 1.074, 1.076, 1.078, 1.082, 1.084, 1.085, 1.088, 1.091, 1.094, 1.097, 1.101, 1.102, 1.104, 1.104, 1.103, 1.102, 1.101, 1.098, 1.098, 1.099, 1.108, 1.118,
+ 1.053, 1.051, 1.048, 1.048, 1.051, 1.058, 1.065, 1.068, 1.071, 1.073, 1.075, 1.076, 1.079, 1.083, 1.084, 1.087, 1.089, 1.092, 1.096, 1.099, 1.102, 1.104, 1.105, 1.106, 1.105, 1.104, 1.101, 1.099, 1.099, 1.103, 1.109, 1.118,
+ 1.055, 1.051, 1.049, 1.048, 1.051, 1.057, 1.065, 1.069, 1.071, 1.074, 1.076, 1.078, 1.081, 1.084, 1.087, 1.088, 1.092, 1.095, 1.098, 1.101, 1.104, 1.106, 1.107, 1.108, 1.107, 1.106, 1.104, 1.103, 1.103, 1.106, 1.113, 1.121,
+ 1.058, 1.051, 1.049, 1.049, 1.051, 1.057, 1.064, 1.069, 1.073, 1.076, 1.078, 1.081, 1.084, 1.086, 1.088, 1.092, 1.095, 1.098, 1.099, 1.103, 1.106, 1.108, 1.111, 1.111, 1.111, 1.109, 1.107, 1.106, 1.106, 1.111, 1.115, 1.125,
+ 1.061, 1.053, 1.051, 1.051, 1.053, 1.059, 1.065, 1.071, 1.074, 1.078, 1.081, 1.084, 1.086, 1.088, 1.092, 1.096, 1.098, 1.099, 1.102, 1.105, 1.107, 1.109, 1.111, 1.112, 1.113, 1.112, 1.109, 1.109, 1.109, 1.112, 1.121, 1.134,
+ 1.064, 1.055, 1.052, 1.052, 1.054, 1.061, 1.065, 1.072, 1.077, 1.081, 1.084, 1.086, 1.088, 1.091, 1.096, 1.097, 1.099, 1.102, 1.104, 1.107, 1.109, 1.111, 1.112, 1.113, 1.114, 1.114, 1.114, 1.113, 1.112, 1.116, 1.125, 1.137,
+ 1.064, 1.059, 1.054, 1.054, 1.057, 1.062, 1.067, 1.073, 1.079, 1.081, 1.085, 1.087, 1.089, 1.093, 1.097, 1.099, 1.103, 1.104, 1.106, 1.109, 1.111, 1.113, 1.113, 1.114, 1.114, 1.116, 1.116, 1.116, 1.117, 1.122, 1.133, 1.143,
+ 1.069, 1.062, 1.057, 1.057, 1.059, 1.063, 1.068, 1.074, 1.079, 1.082, 1.086, 1.089, 1.091, 1.095, 1.098, 1.102, 1.104, 1.106, 1.109, 1.111, 1.113, 1.114, 1.114, 1.115, 1.116, 1.118, 1.119, 1.121, 1.123, 1.129, 1.142, 1.151,
+ 1.071, 1.065, 1.059, 1.058, 1.061, 1.065, 1.069, 1.074, 1.079, 1.083, 1.087, 1.091, 1.092, 1.095, 1.099, 1.103, 1.105, 1.108, 1.109, 1.111, 1.113, 1.114, 1.115, 1.116, 1.118, 1.119, 1.122, 1.124, 1.129, 1.134, 1.148, 1.161,
+ 1.075, 1.066, 1.059, 1.059, 1.061, 1.065, 1.069, 1.073, 1.081, 1.085, 1.089, 1.092, 1.094, 1.097, 1.101, 1.104, 1.106, 1.109, 1.111, 1.112, 1.113, 1.115, 1.117, 1.118, 1.119, 1.121, 1.124, 1.127, 1.132, 1.138, 1.156, 1.171,
+ 1.076, 1.069, 1.061, 1.059, 1.061, 1.066, 1.069, 1.073, 1.081, 1.085, 1.089, 1.094, 1.095, 1.098, 1.102, 1.105, 1.108, 1.109, 1.111, 1.112, 1.115, 1.116, 1.118, 1.119, 1.121, 1.123, 1.126, 1.129, 1.135, 1.144, 1.161, 1.173,
+ 1.082, 1.075, 1.064, 1.059, 1.061, 1.066, 1.069, 1.074, 1.079, 1.087, 1.092, 1.094, 1.096, 1.099, 1.103, 1.105, 1.108, 1.109, 1.112, 1.114, 1.115, 1.117, 1.119, 1.119, 1.121, 1.124, 1.129, 1.131, 1.137, 1.148, 1.165, 1.182
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.002, 1.002, 1.001, 1.002, 1.004, 1.007, 1.009, 1.013, 1.014, 1.015, 1.017, 1.019, 1.021, 1.021, 1.022, 1.022, 1.022, 1.022, 1.022, 1.022, 1.022, 1.022, 1.022, 1.021, 1.019, 1.018, 1.017, 1.014, 1.015, 1.015, 1.015, 1.014,
+ 1.021, 1.018, 1.018, 1.017, 1.019, 1.021, 1.024, 1.026, 1.029, 1.031, 1.033, 1.035, 1.037, 1.038, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.041, 1.039, 1.038, 1.037, 1.036, 1.034, 1.033, 1.032, 1.033, 1.033, 1.034,
+ 1.022, 1.021, 1.019, 1.019, 1.021, 1.024, 1.026, 1.028, 1.032, 1.033, 1.035, 1.037, 1.038, 1.039, 1.041, 1.041, 1.042, 1.042, 1.042, 1.043, 1.042, 1.042, 1.042, 1.041, 1.039, 1.038, 1.037, 1.035, 1.034, 1.034, 1.035, 1.037,
+ 1.023, 1.022, 1.021, 1.021, 1.023, 1.026, 1.028, 1.031, 1.033, 1.035, 1.037, 1.038, 1.039, 1.041, 1.042, 1.043, 1.044, 1.044, 1.045, 1.044, 1.044, 1.044, 1.044, 1.043, 1.041, 1.039, 1.039, 1.036, 1.035, 1.035, 1.035, 1.038,
+ 1.024, 1.023, 1.023, 1.023, 1.025, 1.027, 1.029, 1.033, 1.034, 1.037, 1.039, 1.039, 1.041, 1.042, 1.044, 1.045, 1.045, 1.045, 1.046, 1.047, 1.047, 1.047, 1.046, 1.045, 1.043, 1.041, 1.039, 1.037, 1.035, 1.035, 1.037, 1.039,
+ 1.025, 1.024, 1.024, 1.025, 1.027, 1.028, 1.031, 1.033, 1.036, 1.038, 1.039, 1.041, 1.043, 1.044, 1.046, 1.046, 1.046, 1.047, 1.048, 1.049, 1.049, 1.049, 1.048, 1.047, 1.046, 1.043, 1.041, 1.038, 1.036, 1.036, 1.037, 1.041,
+ 1.024, 1.025, 1.025, 1.026, 1.027, 1.029, 1.031, 1.033, 1.036, 1.039, 1.041, 1.043, 1.044, 1.045, 1.047, 1.048, 1.048, 1.049, 1.049, 1.051, 1.051, 1.049, 1.049, 1.049, 1.047, 1.045, 1.042, 1.039, 1.038, 1.037, 1.038, 1.039,
+ 1.025, 1.025, 1.026, 1.027, 1.028, 1.029, 1.031, 1.033, 1.037, 1.039, 1.042, 1.043, 1.045, 1.046, 1.048, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.051, 1.051, 1.049, 1.048, 1.046, 1.043, 1.041, 1.038, 1.037, 1.038, 1.041,
+ 1.024, 1.025, 1.027, 1.027, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.042, 1.044, 1.045, 1.047, 1.048, 1.049, 1.049, 1.051, 1.051, 1.051, 1.052, 1.052, 1.052, 1.049, 1.048, 1.046, 1.044, 1.042, 1.041, 1.038, 1.038, 1.041,
+ 1.024, 1.025, 1.027, 1.028, 1.029, 1.031, 1.032, 1.034, 1.038, 1.041, 1.043, 1.045, 1.046, 1.047, 1.049, 1.049, 1.049, 1.051, 1.051, 1.052, 1.054, 1.053, 1.053, 1.051, 1.049, 1.047, 1.044, 1.042, 1.041, 1.039, 1.039, 1.042,
+ 1.025, 1.025, 1.027, 1.028, 1.029, 1.031, 1.032, 1.035, 1.039, 1.041, 1.044, 1.045, 1.046, 1.048, 1.049, 1.051, 1.051, 1.051, 1.052, 1.054, 1.054, 1.055, 1.053, 1.052, 1.049, 1.047, 1.044, 1.042, 1.041, 1.039, 1.041, 1.042,
+ 1.026, 1.026, 1.026, 1.028, 1.029, 1.031, 1.033, 1.036, 1.039, 1.042, 1.044, 1.045, 1.047, 1.048, 1.051, 1.051, 1.052, 1.052, 1.053, 1.054, 1.055, 1.055, 1.054, 1.052, 1.051, 1.049, 1.045, 1.042, 1.039, 1.039, 1.041, 1.042,
+ 1.027, 1.026, 1.026, 1.028, 1.029, 1.032, 1.033, 1.036, 1.039, 1.042, 1.045, 1.046, 1.047, 1.049, 1.052, 1.053, 1.053, 1.054, 1.054, 1.054, 1.055, 1.055, 1.055, 1.053, 1.051, 1.049, 1.046, 1.043, 1.039, 1.039, 1.041, 1.043,
+ 1.027, 1.027, 1.027, 1.028, 1.029, 1.032, 1.034, 1.036, 1.039, 1.043, 1.045, 1.047, 1.048, 1.051, 1.052, 1.053, 1.054, 1.054, 1.054, 1.055, 1.055, 1.056, 1.056, 1.055, 1.052, 1.051, 1.047, 1.044, 1.041, 1.039, 1.041, 1.043,
+ 1.028, 1.028, 1.028, 1.028, 1.029, 1.032, 1.033, 1.036, 1.038, 1.044, 1.046, 1.048, 1.049, 1.052, 1.053, 1.053, 1.054, 1.054, 1.055, 1.055, 1.056, 1.056, 1.057, 1.056, 1.053, 1.051, 1.048, 1.044, 1.042, 1.041, 1.042, 1.043,
+ 1.028, 1.028, 1.028, 1.028, 1.031, 1.032, 1.033, 1.035, 1.038, 1.043, 1.046, 1.048, 1.051, 1.053, 1.053, 1.054, 1.055, 1.055, 1.055, 1.055, 1.056, 1.057, 1.058, 1.056, 1.054, 1.052, 1.049, 1.045, 1.043, 1.042, 1.042, 1.044,
+ 1.029, 1.028, 1.027, 1.028, 1.031, 1.032, 1.033, 1.036, 1.038, 1.043, 1.046, 1.049, 1.051, 1.053, 1.054, 1.055, 1.055, 1.055, 1.055, 1.055, 1.056, 1.057, 1.057, 1.056, 1.053, 1.052, 1.049, 1.045, 1.042, 1.042, 1.043, 1.045,
+ 1.029, 1.028, 1.028, 1.029, 1.031, 1.033, 1.034, 1.036, 1.039, 1.042, 1.047, 1.048, 1.051, 1.053, 1.055, 1.056, 1.056, 1.055, 1.055, 1.055, 1.057, 1.057, 1.057, 1.057, 1.055, 1.052, 1.049, 1.046, 1.043, 1.043, 1.044, 1.045,
+ 1.031, 1.029, 1.028, 1.029, 1.031, 1.033, 1.035, 1.037, 1.039, 1.042, 1.046, 1.048, 1.051, 1.052, 1.055, 1.056, 1.056, 1.056, 1.056, 1.056, 1.057, 1.057, 1.057, 1.057, 1.055, 1.053, 1.049, 1.046, 1.044, 1.043, 1.044, 1.047,
+ 1.031, 1.029, 1.029, 1.029, 1.031, 1.033, 1.036, 1.037, 1.039, 1.042, 1.046, 1.048, 1.051, 1.052, 1.054, 1.055, 1.056, 1.056, 1.056, 1.057, 1.058, 1.058, 1.058, 1.057, 1.056, 1.054, 1.051, 1.047, 1.045, 1.044, 1.045, 1.047,
+ 1.031, 1.029, 1.029, 1.029, 1.031, 1.033, 1.036, 1.037, 1.039, 1.042, 1.045, 1.047, 1.051, 1.052, 1.054, 1.055, 1.056, 1.056, 1.057, 1.057, 1.058, 1.057, 1.058, 1.056, 1.055, 1.053, 1.051, 1.047, 1.045, 1.044, 1.044, 1.047,
+ 1.031, 1.029, 1.029, 1.031, 1.031, 1.032, 1.035, 1.038, 1.039, 1.042, 1.045, 1.048, 1.051, 1.052, 1.053, 1.055, 1.056, 1.056, 1.057, 1.057, 1.058, 1.058, 1.057, 1.056, 1.055, 1.053, 1.051, 1.048, 1.045, 1.044, 1.045, 1.048,
+ 1.031, 1.029, 1.029, 1.029, 1.031, 1.032, 1.034, 1.039, 1.041, 1.042, 1.045, 1.047, 1.049, 1.052, 1.053, 1.055, 1.055, 1.056, 1.057, 1.057, 1.058, 1.058, 1.057, 1.056, 1.055, 1.054, 1.051, 1.048, 1.047, 1.045, 1.046, 1.048,
+ 1.031, 1.029, 1.028, 1.029, 1.031, 1.032, 1.034, 1.037, 1.041, 1.043, 1.045, 1.047, 1.049, 1.052, 1.054, 1.055, 1.056, 1.056, 1.057, 1.057, 1.058, 1.058, 1.058, 1.057, 1.056, 1.054, 1.051, 1.049, 1.047, 1.046, 1.047, 1.048,
+ 1.029, 1.029, 1.028, 1.029, 1.031, 1.032, 1.035, 1.037, 1.039, 1.042, 1.045, 1.048, 1.049, 1.052, 1.054, 1.055, 1.055, 1.056, 1.056, 1.057, 1.058, 1.058, 1.058, 1.057, 1.056, 1.055, 1.052, 1.049, 1.047, 1.047, 1.047, 1.048,
+ 1.031, 1.029, 1.028, 1.029, 1.031, 1.032, 1.035, 1.037, 1.039, 1.042, 1.045, 1.047, 1.049, 1.052, 1.054, 1.055, 1.056, 1.056, 1.056, 1.057, 1.058, 1.058, 1.058, 1.058, 1.057, 1.055, 1.054, 1.051, 1.049, 1.048, 1.049, 1.051,
+ 1.031, 1.029, 1.029, 1.029, 1.031, 1.032, 1.035, 1.037, 1.041, 1.042, 1.045, 1.047, 1.049, 1.051, 1.053, 1.055, 1.056, 1.056, 1.056, 1.057, 1.057, 1.058, 1.058, 1.057, 1.056, 1.055, 1.054, 1.052, 1.051, 1.049, 1.051, 1.051,
+ 1.032, 1.031, 1.029, 1.029, 1.031, 1.032, 1.035, 1.037, 1.041, 1.042, 1.044, 1.046, 1.048, 1.051, 1.053, 1.054, 1.055, 1.056, 1.056, 1.056, 1.056, 1.056, 1.057, 1.056, 1.056, 1.055, 1.054, 1.052, 1.052, 1.051, 1.052, 1.054,
+ 1.034, 1.031, 1.031, 1.029, 1.031, 1.032, 1.034, 1.036, 1.041, 1.042, 1.043, 1.045, 1.047, 1.049, 1.052, 1.053, 1.055, 1.055, 1.056, 1.056, 1.056, 1.056, 1.056, 1.056, 1.056, 1.055, 1.054, 1.053, 1.053, 1.053, 1.053, 1.054,
+ 1.034, 1.033, 1.029, 1.029, 1.031, 1.032, 1.034, 1.036, 1.039, 1.042, 1.044, 1.045, 1.047, 1.049, 1.051, 1.052, 1.054, 1.055, 1.055, 1.055, 1.056, 1.056, 1.056, 1.056, 1.055, 1.055, 1.055, 1.055, 1.054, 1.054, 1.055, 1.056,
+ 1.034, 1.033, 1.029, 1.029, 1.031, 1.032, 1.033, 1.036, 1.039, 1.042, 1.044, 1.046, 1.047, 1.049, 1.051, 1.053, 1.054, 1.055, 1.055, 1.056, 1.056, 1.056, 1.056, 1.056, 1.055, 1.055, 1.054, 1.055, 1.055, 1.054, 1.055, 1.057,
+ 1.034, 1.032, 1.031, 1.029, 1.029, 1.032, 1.034, 1.037, 1.039, 1.042, 1.045, 1.046, 1.047, 1.049, 1.052, 1.054, 1.054, 1.055, 1.056, 1.056, 1.056, 1.056, 1.056, 1.055, 1.054, 1.054, 1.054, 1.055, 1.054, 1.055, 1.056, 1.057
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.367, 2.251, 2.061, 1.909, 1.781, 1.674, 1.582, 1.506, 1.441, 1.382, 1.329, 1.287, 1.266, 1.256, 1.255, 1.255, 1.255, 1.259, 1.273, 1.309, 1.355, 1.411, 1.469, 1.541, 1.625, 1.723, 1.838, 1.975, 2.139, 2.338, 2.589, 2.717,
+ 2.305, 2.152, 1.974, 1.831, 1.706, 1.604, 1.518, 1.443, 1.387, 1.343, 1.296, 1.255, 1.231, 1.213, 1.203, 1.203, 1.206, 1.221, 1.244, 1.279, 1.325, 1.364, 1.411, 1.481, 1.559, 1.653, 1.763, 1.895, 2.051, 2.238, 2.473, 2.641,
+ 2.249, 2.099, 1.925, 1.784, 1.665, 1.564, 1.479, 1.407, 1.347, 1.296, 1.255, 1.225, 1.199, 1.182, 1.172, 1.172, 1.178, 1.191, 1.215, 1.244, 1.279, 1.325, 1.378, 1.445, 1.522, 1.614, 1.719, 1.847, 1.998, 2.181, 2.406, 2.573,
+ 2.201, 2.048, 1.881, 1.743, 1.628, 1.529, 1.446, 1.376, 1.316, 1.266, 1.225, 1.198, 1.171, 1.152, 1.143, 1.143, 1.153, 1.165, 1.191, 1.215, 1.252, 1.297, 1.351, 1.413, 1.489, 1.577, 1.682, 1.804, 1.951, 2.129, 2.347, 2.513,
+ 2.156, 2.004, 1.842, 1.706, 1.594, 1.497, 1.416, 1.346, 1.287, 1.238, 1.199, 1.171, 1.143, 1.125, 1.117, 1.117, 1.127, 1.139, 1.165, 1.191, 1.227, 1.272, 1.324, 1.387, 1.459, 1.545, 1.646, 1.766, 1.909, 2.081, 2.294, 2.461,
+ 2.116, 1.963, 1.807, 1.674, 1.562, 1.467, 1.388, 1.321, 1.262, 1.214, 1.174, 1.143, 1.119, 1.099, 1.092, 1.092, 1.103, 1.116, 1.139, 1.167, 1.205, 1.249, 1.301, 1.362, 1.432, 1.515, 1.614, 1.731, 1.869, 2.039, 2.246, 2.413,
+ 2.082, 1.929, 1.774, 1.644, 1.535, 1.442, 1.364, 1.298, 1.241, 1.192, 1.152, 1.119, 1.099, 1.078, 1.069, 1.069, 1.078, 1.098, 1.116, 1.146, 1.184, 1.229, 1.281, 1.341, 1.409, 1.489, 1.585, 1.699, 1.834, 2.001, 2.205, 2.374,
+ 2.054, 1.898, 1.745, 1.619, 1.513, 1.422, 1.344, 1.277, 1.221, 1.172, 1.132, 1.099, 1.078, 1.059, 1.051, 1.051, 1.058, 1.078, 1.098, 1.128, 1.167, 1.212, 1.264, 1.323, 1.389, 1.467, 1.562, 1.674, 1.805, 1.969, 2.171, 2.339,
+ 2.031, 1.872, 1.722, 1.598, 1.493, 1.403, 1.327, 1.261, 1.203, 1.156, 1.114, 1.082, 1.059, 1.044, 1.034, 1.034, 1.044, 1.058, 1.081, 1.113, 1.151, 1.198, 1.249, 1.308, 1.373, 1.449, 1.543, 1.655, 1.799, 1.942, 2.141, 2.312,
+ 2.011, 1.849, 1.704, 1.581, 1.476, 1.387, 1.311, 1.245, 1.189, 1.141, 1.101, 1.068, 1.044, 1.031, 1.019, 1.019, 1.032, 1.044, 1.068, 1.099, 1.138, 1.184, 1.238, 1.295, 1.359, 1.435, 1.526, 1.639, 1.782, 1.924, 2.115, 2.287,
+ 1.996, 1.833, 1.687, 1.565, 1.462, 1.374, 1.299, 1.233, 1.177, 1.128, 1.087, 1.055, 1.031, 1.019, 1.008, 1.009, 1.019, 1.033, 1.057, 1.089, 1.128, 1.174, 1.227, 1.283, 1.347, 1.422, 1.512, 1.621, 1.764, 1.905, 2.091, 2.269,
+ 1.985, 1.819, 1.675, 1.553, 1.451, 1.363, 1.289, 1.223, 1.167, 1.119, 1.078, 1.046, 1.022, 1.007, 1.003, 1.007, 1.009, 1.024, 1.049, 1.081, 1.119, 1.166, 1.218, 1.275, 1.339, 1.413, 1.502, 1.606, 1.742, 1.885, 2.075, 2.258,
+ 1.979, 1.809, 1.666, 1.546, 1.442, 1.356, 1.281, 1.215, 1.159, 1.112, 1.071, 1.039, 1.015, 1.001, 1.001, 1.003, 1.009, 1.019, 1.043, 1.075, 1.114, 1.159, 1.212, 1.268, 1.332, 1.407, 1.496, 1.599, 1.722, 1.875, 2.064, 2.249,
+ 1.976, 1.803, 1.661, 1.541, 1.437, 1.351, 1.276, 1.211, 1.156, 1.107, 1.067, 1.035, 1.011, 1.001, 1.001, 1.003, 1.009, 1.018, 1.039, 1.071, 1.111, 1.156, 1.207, 1.265, 1.329, 1.404, 1.492, 1.594, 1.717, 1.869, 2.059, 2.247,
+ 1.976, 1.801, 1.659, 1.538, 1.436, 1.348, 1.274, 1.209, 1.153, 1.106, 1.066, 1.034, 1.011, 1.001, 1.001, 1.004, 1.011, 1.018, 1.039, 1.071, 1.109, 1.155, 1.207, 1.264, 1.329, 1.404, 1.492, 1.593, 1.716, 1.868, 2.059, 2.247,
+ 1.976, 1.801, 1.659, 1.538, 1.436, 1.348, 1.274, 1.209, 1.153, 1.106, 1.066, 1.034, 1.011, 1.003, 1.003, 1.005, 1.011, 1.019, 1.041, 1.071, 1.109, 1.155, 1.207, 1.264, 1.329, 1.404, 1.493, 1.594, 1.716, 1.869, 2.059, 2.247,
+ 1.989, 1.809, 1.663, 1.542, 1.439, 1.351, 1.277, 1.212, 1.156, 1.108, 1.069, 1.038, 1.014, 1.006, 1.005, 1.008, 1.014, 1.023, 1.044, 1.075, 1.113, 1.158, 1.209, 1.267, 1.333, 1.408, 1.497, 1.599, 1.722, 1.875, 2.066, 2.251,
+ 1.997, 1.818, 1.669, 1.547, 1.444, 1.356, 1.282, 1.215, 1.161, 1.113, 1.075, 1.043, 1.019, 1.009, 1.008, 1.011, 1.016, 1.027, 1.049, 1.079, 1.117, 1.163, 1.215, 1.273, 1.338, 1.414, 1.503, 1.607, 1.731, 1.884, 2.073, 2.258,
+ 2.003, 1.827, 1.681, 1.558, 1.453, 1.365, 1.289, 1.225, 1.169, 1.122, 1.083, 1.051, 1.028, 1.018, 1.013, 1.016, 1.021, 1.035, 1.057, 1.087, 1.126, 1.171, 1.224, 1.281, 1.347, 1.424, 1.514, 1.617, 1.743, 1.896, 2.087, 2.268,
+ 2.011, 1.843, 1.696, 1.571, 1.465, 1.377, 1.302, 1.236, 1.179, 1.132, 1.093, 1.062, 1.039, 1.028, 1.021, 1.021, 1.032, 1.044, 1.067, 1.098, 1.136, 1.181, 1.233, 1.291, 1.359, 1.436, 1.526, 1.632, 1.758, 1.913, 2.104, 2.283,
+ 2.021, 1.863, 1.715, 1.588, 1.481, 1.392, 1.316, 1.249, 1.193, 1.145, 1.106, 1.075, 1.053, 1.039, 1.032, 1.032, 1.044, 1.057, 1.081, 1.111, 1.149, 1.194, 1.245, 1.304, 1.372, 1.452, 1.541, 1.649, 1.777, 1.932, 2.126, 2.299,
+ 2.042, 1.887, 1.736, 1.608, 1.501, 1.409, 1.333, 1.266, 1.209, 1.162, 1.123, 1.091, 1.071, 1.053, 1.049, 1.049, 1.057, 1.075, 1.095, 1.125, 1.163, 1.209, 1.259, 1.319, 1.389, 1.469, 1.561, 1.671, 1.799, 1.957, 2.156, 2.324,
+ 2.072, 1.916, 1.762, 1.631, 1.522, 1.429, 1.352, 1.285, 1.228, 1.179, 1.141, 1.109, 1.091, 1.071, 1.067, 1.067, 1.075, 1.095, 1.112, 1.143, 1.181, 1.227, 1.276, 1.338, 1.408, 1.489, 1.583, 1.694, 1.827, 1.987, 2.187, 2.354,
+ 2.104, 1.949, 1.791, 1.659, 1.548, 1.453, 1.375, 1.308, 1.248, 1.201, 1.162, 1.132, 1.109, 1.092, 1.088, 1.088, 1.096, 1.112, 1.134, 1.164, 1.202, 1.247, 1.297, 1.359, 1.432, 1.515, 1.611, 1.724, 1.858, 2.022, 2.225, 2.391,
+ 2.138, 1.985, 1.824, 1.689, 1.577, 1.482, 1.401, 1.332, 1.274, 1.225, 1.185, 1.158, 1.132, 1.116, 1.112, 1.112, 1.119, 1.134, 1.161, 1.186, 1.225, 1.271, 1.319, 1.384, 1.458, 1.542, 1.641, 1.757, 1.896, 2.064, 2.271, 2.436,
+ 2.179, 2.028, 1.862, 1.724, 1.611, 1.512, 1.429, 1.361, 1.301, 1.251, 1.212, 1.185, 1.158, 1.143, 1.138, 1.138, 1.145, 1.161, 1.186, 1.212, 1.251, 1.295, 1.346, 1.411, 1.487, 1.574, 1.675, 1.793, 1.936, 2.109, 2.321, 2.488,
+ 2.229, 2.075, 1.904, 1.764, 1.646, 1.547, 1.462, 1.391, 1.331, 1.281, 1.241, 1.212, 1.186, 1.172, 1.167, 1.167, 1.174, 1.189, 1.212, 1.242, 1.278, 1.323, 1.375, 1.443, 1.521, 1.609, 1.713, 1.836, 1.982, 2.159, 2.379, 2.546,
+ 2.283, 2.131, 1.954, 1.808, 1.687, 1.584, 1.499, 1.425, 1.364, 1.313, 1.276, 1.241, 1.218, 1.202, 1.197, 1.197, 1.205, 1.221, 1.242, 1.276, 1.308, 1.354, 1.408, 1.478, 1.558, 1.649, 1.756, 1.884, 2.034, 2.216, 2.446, 2.612,
+ 2.346, 2.191, 2.011, 1.859, 1.733, 1.628, 1.539, 1.465, 1.401, 1.349, 1.313, 1.276, 1.253, 1.237, 1.232, 1.232, 1.239, 1.254, 1.276, 1.308, 1.344, 1.389, 1.447, 1.517, 1.598, 1.695, 1.805, 1.937, 2.094, 2.282, 2.521, 2.688,
+ 2.417, 2.263, 2.071, 1.915, 1.785, 1.676, 1.584, 1.508, 1.443, 1.391, 1.349, 1.314, 1.291, 1.275, 1.269, 1.269, 1.276, 1.291, 1.313, 1.344, 1.384, 1.429, 1.489, 1.562, 1.646, 1.743, 1.859, 1.996, 2.157, 2.355, 2.607, 2.772,
+ 2.497, 2.338, 2.139, 1.976, 1.842, 1.729, 1.635, 1.555, 1.488, 1.433, 1.389, 1.355, 1.331, 1.316, 1.309, 1.309, 1.316, 1.331, 1.353, 1.383, 1.424, 1.473, 1.535, 1.609, 1.696, 1.798, 1.918, 2.061, 2.228, 2.436, 2.699, 2.868,
+ 2.585, 2.391, 2.186, 2.019, 1.881, 1.765, 1.669, 1.588, 1.519, 1.464, 1.421, 1.385, 1.355, 1.336, 1.335, 1.335, 1.336, 1.353, 1.382, 1.413, 1.454, 1.504, 1.567, 1.644, 1.733, 1.836, 1.958, 2.106, 2.278, 2.496, 2.765, 2.979
+ ],
+ "sigma": 0.00094,
+ "sigma_Cb": 0.00164
+ }
+ },
+ {
+ "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": 2698,
+ "ccm":
+ [
+ 1.57242, -0.32752, -0.24489,
+ -0.61284, 1.70498, -0.09215,
+ -0.43411, 0.48072, 0.95338
+ ]
+ },
+ {
+ "ct": 2930,
+ "ccm":
+ [
+ 1.69569, -0.52958, -0.16612,
+ -0.67108, 1.78452, -0.11343,
+ -0.41759, 0.54607, 0.87152
+ ]
+ },
+ {
+ "ct": 3643,
+ "ccm":
+ [
+ 1.72576, -0.72422, -0.00153,
+ -0.45949, 1.40534, 0.05415,
+ -0.14492, -0.79825, 1.94317
+ ]
+ },
+ {
+ "ct": 4605,
+ "ccm":
+ [
+ 1.49944, -0.41939, -0.08005,
+ -0.39248, 1.69456, -0.30208,
+ 0.01559, -0.88541, 1.86981
+ ]
+ },
+ {
+ "ct": 5658,
+ "ccm":
+ [
+ 1.38807, -0.23217, -0.15589,
+ -0.37464, 1.70299, -0.32835,
+ -0.01316, -0.72039, 1.73355
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.cac": { }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "night": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+}
diff --git a/src/ipa/rpi/pisp/data/imx462.json b/src/ipa/rpi/pisp/data/imx462.json
new file mode 100644
index 00000000..20ca1a66
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx462.json
@@ -0,0 +1,342 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "description": "This is an interim tuning only. Please consider doing a more formal tuning for your application.",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ },
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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 ]
+ },
+ "short":
+ {
+ "shutter": [ 10, 5000, 10000, 20000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.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.604, 2.365, 2.2, 2.039, 1.916, 1.799, 1.707, 1.622, 1.552, 1.487, 1.435, 1.389, 1.356, 1.332, 1.317, 1.31, 1.308, 1.313, 1.324, 1.344, 1.37, 1.41, 1.454, 1.508, 1.567, 1.641, 1.719, 1.82, 1.925, 2.073, 2.221,
+ 2.749, 2.521, 2.294, 2.134, 1.979, 1.861, 1.749, 1.661, 1.578, 1.511, 1.448, 1.398, 1.354, 1.322, 1.3, 1.285, 1.278, 1.277, 1.281, 1.292, 1.311, 1.336, 1.374, 1.416, 1.469, 1.526, 1.596, 1.671, 1.77, 1.872, 2.019, 2.166,
+ 2.654, 2.438, 2.223, 2.069, 1.919, 1.807, 1.7, 1.614, 1.534, 1.469, 1.409, 1.361, 1.318, 1.288, 1.267, 1.254, 1.247, 1.245, 1.25, 1.259, 1.277, 1.302, 1.338, 1.379, 1.43, 1.485, 1.552, 1.623, 1.719, 1.819, 1.965, 2.112,
+ 2.563, 2.359, 2.155, 2.007, 1.863, 1.755, 1.653, 1.571, 1.493, 1.43, 1.372, 1.325, 1.284, 1.256, 1.236, 1.223, 1.217, 1.216, 1.219, 1.229, 1.246, 1.269, 1.305, 1.344, 1.393, 1.446, 1.51, 1.578, 1.672, 1.77, 1.914, 2.059,
+ 2.494, 2.299, 2.103, 1.961, 1.822, 1.718, 1.619, 1.538, 1.461, 1.399, 1.343, 1.298, 1.259, 1.232, 1.213, 1.2, 1.194, 1.193, 1.196, 1.205, 1.222, 1.245, 1.279, 1.318, 1.365, 1.416, 1.481, 1.549, 1.641, 1.735, 1.875, 2.015,
+ 2.425, 2.238, 2.05, 1.914, 1.782, 1.681, 1.585, 1.505, 1.429, 1.369, 1.314, 1.271, 1.234, 1.208, 1.189, 1.177, 1.171, 1.169, 1.173, 1.182, 1.198, 1.221, 1.254, 1.292, 1.338, 1.387, 1.452, 1.519, 1.609, 1.701, 1.836, 1.971,
+ 2.363, 2.183, 2.003, 1.873, 1.746, 1.648, 1.555, 1.477, 1.401, 1.342, 1.289, 1.247, 1.212, 1.187, 1.168, 1.156, 1.149, 1.148, 1.152, 1.16, 1.177, 1.198, 1.231, 1.267, 1.312, 1.36, 1.425, 1.492, 1.58, 1.671, 1.802, 1.932,
+ 2.314, 2.14, 1.965, 1.839, 1.716, 1.622, 1.532, 1.454, 1.38, 1.322, 1.27, 1.229, 1.195, 1.169, 1.149, 1.137, 1.129, 1.128, 1.132, 1.142, 1.158, 1.18, 1.21, 1.245, 1.289, 1.336, 1.401, 1.469, 1.557, 1.649, 1.776, 1.903,
+ 2.264, 2.096, 1.927, 1.805, 1.687, 1.596, 1.509, 1.432, 1.358, 1.301, 1.251, 1.211, 1.177, 1.151, 1.131, 1.117, 1.109, 1.108, 1.113, 1.123, 1.14, 1.161, 1.19, 1.222, 1.265, 1.313, 1.378, 1.445, 1.534, 1.626, 1.75, 1.874,
+ 2.225, 2.061, 1.897, 1.778, 1.663, 1.574, 1.489, 1.414, 1.341, 1.285, 1.235, 1.196, 1.163, 1.136, 1.115, 1.1, 1.091, 1.089, 1.095, 1.106, 1.124, 1.145, 1.174, 1.205, 1.248, 1.294, 1.359, 1.427, 1.516, 1.606, 1.728, 1.849,
+ 2.193, 2.033, 1.872, 1.756, 1.642, 1.556, 1.473, 1.399, 1.327, 1.272, 1.224, 1.185, 1.15, 1.123, 1.1, 1.084, 1.074, 1.072, 1.078, 1.09, 1.11, 1.133, 1.161, 1.193, 1.234, 1.28, 1.345, 1.413, 1.501, 1.59, 1.709, 1.828,
+ 2.161, 2.004, 1.848, 1.734, 1.622, 1.537, 1.457, 1.384, 1.313, 1.26, 1.212, 1.173, 1.138, 1.11, 1.085, 1.068, 1.057, 1.055, 1.062, 1.075, 1.096, 1.12, 1.148, 1.18, 1.221, 1.266, 1.331, 1.399, 1.486, 1.574, 1.69, 1.807,
+ 2.14, 1.986, 1.832, 1.719, 1.609, 1.525, 1.445, 1.373, 1.304, 1.251, 1.204, 1.165, 1.129, 1.1, 1.074, 1.055, 1.043, 1.041, 1.049, 1.063, 1.086, 1.11, 1.14, 1.172, 1.212, 1.258, 1.323, 1.39, 1.477, 1.566, 1.679, 1.792,
+ 2.123, 1.971, 1.819, 1.707, 1.598, 1.514, 1.434, 1.364, 1.296, 1.243, 1.197, 1.158, 1.122, 1.091, 1.064, 1.044, 1.031, 1.027, 1.036, 1.052, 1.076, 1.102, 1.132, 1.165, 1.206, 1.251, 1.316, 1.383, 1.471, 1.56, 1.67, 1.78,
+ 2.106, 1.956, 1.806, 1.695, 1.587, 1.504, 1.424, 1.354, 1.288, 1.236, 1.19, 1.15, 1.114, 1.083, 1.055, 1.033, 1.018, 1.014, 1.024, 1.04, 1.066, 1.094, 1.124, 1.158, 1.199, 1.245, 1.309, 1.376, 1.465, 1.555, 1.661, 1.767,
+ 2.104, 1.955, 1.805, 1.694, 1.586, 1.502, 1.422, 1.352, 1.285, 1.234, 1.188, 1.149, 1.113, 1.081, 1.053, 1.031, 1.014, 1.011, 1.021, 1.038, 1.064, 1.091, 1.122, 1.156, 1.198, 1.244, 1.308, 1.376, 1.465, 1.555, 1.66, 1.766,
+ 2.104, 1.955, 1.806, 1.695, 1.586, 1.502, 1.421, 1.351, 1.284, 1.232, 1.187, 1.148, 1.112, 1.08, 1.051, 1.029, 1.012, 1.008, 1.02, 1.036, 1.062, 1.089, 1.12, 1.155, 1.197, 1.244, 1.308, 1.375, 1.465, 1.555, 1.661, 1.766,
+ 2.105, 1.956, 1.807, 1.696, 1.587, 1.502, 1.42, 1.35, 1.282, 1.231, 1.186, 1.148, 1.112, 1.08, 1.051, 1.028, 1.011, 1.007, 1.019, 1.036, 1.061, 1.088, 1.119, 1.154, 1.197, 1.244, 1.308, 1.376, 1.466, 1.557, 1.662, 1.767,
+ 2.121, 1.97, 1.818, 1.705, 1.595, 1.508, 1.424, 1.353, 1.286, 1.236, 1.191, 1.153, 1.118, 1.087, 1.059, 1.038, 1.022, 1.018, 1.028, 1.044, 1.067, 1.093, 1.124, 1.158, 1.201, 1.248, 1.314, 1.383, 1.474, 1.567, 1.672, 1.777,
+ 2.137, 1.983, 1.829, 1.715, 1.603, 1.514, 1.428, 1.357, 1.291, 1.24, 1.196, 1.158, 1.123, 1.094, 1.068, 1.047, 1.033, 1.029, 1.038, 1.052, 1.074, 1.098, 1.128, 1.162, 1.205, 1.253, 1.32, 1.39, 1.483, 1.577, 1.682, 1.788,
+ 2.154, 1.998, 1.843, 1.726, 1.613, 1.522, 1.435, 1.364, 1.297, 1.246, 1.202, 1.164, 1.131, 1.102, 1.078, 1.059, 1.045, 1.041, 1.048, 1.061, 1.082, 1.105, 1.134, 1.167, 1.211, 1.259, 1.327, 1.399, 1.494, 1.588, 1.694, 1.8,
+ 2.176, 2.019, 1.862, 1.744, 1.628, 1.537, 1.449, 1.377, 1.309, 1.258, 1.213, 1.176, 1.143, 1.116, 1.092, 1.074, 1.061, 1.057, 1.063, 1.075, 1.094, 1.117, 1.146, 1.178, 1.222, 1.27, 1.34, 1.414, 1.509, 1.604, 1.711, 1.818,
+ 2.199, 2.04, 1.881, 1.761, 1.644, 1.552, 1.464, 1.391, 1.321, 1.269, 1.223, 1.187, 1.155, 1.129, 1.106, 1.09, 1.078, 1.074, 1.078, 1.088, 1.107, 1.128, 1.157, 1.189, 1.233, 1.281, 1.353, 1.428, 1.524, 1.62, 1.728, 1.836,
+ 2.228, 2.066, 1.904, 1.782, 1.662, 1.57, 1.482, 1.408, 1.337, 1.284, 1.237, 1.201, 1.17, 1.145, 1.123, 1.107, 1.096, 1.092, 1.095, 1.104, 1.121, 1.142, 1.17, 1.203, 1.247, 1.297, 1.37, 1.446, 1.542, 1.639, 1.75, 1.86,
+ 2.267, 2.099, 1.932, 1.807, 1.684, 1.592, 1.504, 1.428, 1.356, 1.302, 1.255, 1.219, 1.189, 1.164, 1.141, 1.125, 1.115, 1.111, 1.114, 1.123, 1.138, 1.158, 1.186, 1.22, 1.266, 1.318, 1.391, 1.467, 1.563, 1.661, 1.776, 1.891,
+ 2.305, 2.132, 1.96, 1.832, 1.707, 1.614, 1.526, 1.449, 1.375, 1.32, 1.272, 1.237, 1.208, 1.182, 1.16, 1.144, 1.135, 1.131, 1.134, 1.141, 1.155, 1.174, 1.203, 1.236, 1.285, 1.338, 1.412, 1.489, 1.585, 1.682, 1.802, 1.922,
+ 2.351, 2.173, 1.996, 1.864, 1.736, 1.641, 1.552, 1.474, 1.4, 1.344, 1.294, 1.258, 1.228, 1.203, 1.181, 1.165, 1.156, 1.152, 1.155, 1.162, 1.176, 1.195, 1.224, 1.259, 1.309, 1.365, 1.439, 1.516, 1.613, 1.711, 1.835, 1.96,
+ 2.4, 2.218, 2.036, 1.901, 1.768, 1.671, 1.58, 1.502, 1.428, 1.37, 1.319, 1.281, 1.249, 1.224, 1.203, 1.188, 1.178, 1.174, 1.177, 1.184, 1.197, 1.217, 1.248, 1.285, 1.337, 1.394, 1.469, 1.547, 1.644, 1.743, 1.873, 2.002,
+ 2.45, 2.264, 2.077, 1.938, 1.801, 1.702, 1.608, 1.53, 1.456, 1.397, 1.344, 1.304, 1.271, 1.245, 1.224, 1.21, 1.2, 1.196, 1.199, 1.206, 1.219, 1.239, 1.272, 1.311, 1.365, 1.424, 1.5, 1.578, 1.676, 1.776, 1.91, 2.044,
+ 2.513, 2.318, 2.124, 1.984, 1.848, 1.747, 1.652, 1.572, 1.496, 1.436, 1.383, 1.341, 1.303, 1.274, 1.253, 1.238, 1.228, 1.225, 1.228, 1.235, 1.248, 1.269, 1.303, 1.343, 1.4, 1.46, 1.537, 1.617, 1.718, 1.82, 1.962, 2.103,
+ 2.579, 2.376, 2.172, 2.032, 1.897, 1.796, 1.7, 1.617, 1.538, 1.479, 1.426, 1.38, 1.337, 1.306, 1.283, 1.267, 1.258, 1.254, 1.257, 1.265, 1.279, 1.3, 1.336, 1.377, 1.435, 1.497, 1.576, 1.658, 1.761, 1.867, 2.016, 2.165,
+ 2.645, 2.433, 2.22, 2.08, 1.946, 1.844, 1.747, 1.663, 1.581, 1.521, 1.468, 1.419, 1.371, 1.337, 1.313, 1.296, 1.287, 1.284, 1.287, 1.295, 1.309, 1.331, 1.368, 1.411, 1.471, 1.535, 1.615, 1.699, 1.805, 1.914, 2.071, 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/pisp/data/imx477.json b/src/ipa/rpi/pisp/data/imx477.json
new file mode 100644
index 00000000..2fe04c21
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx477.json
@@ -0,0 +1,1186 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 12000,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 740,
+ "reference_Y": 15051
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.809
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 204,
+ "slope": 0.0061
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2850.0, 0.4307, 0.3957,
+ 2960.0, 0.4159, 0.4313,
+ 3580.0, 0.3771, 0.5176,
+ 4559.0, 0.3031, 0.6573,
+ 5881.0, 0.2809, 0.6942,
+ 7600.0, 0.2263, 0.7762
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.02634,
+ "transverse_neg": 0.02255
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.359, 2.354, 2.351, 2.351, 2.343, 2.337, 2.331, 2.325, 2.323, 2.321, 2.317, 2.315, 2.313, 2.313, 2.311, 2.312, 2.312, 2.313, 2.315, 2.315, 2.316, 2.317, 2.319, 2.323, 2.326, 2.329, 2.332, 2.332, 2.335, 2.337, 2.352, 2.363,
+ 2.352, 2.351, 2.349, 2.346, 2.342, 2.334, 2.328, 2.324, 2.321, 2.317, 2.315, 2.314, 2.312, 2.311, 2.311, 2.311, 2.311, 2.311, 2.312, 2.314, 2.315, 2.316, 2.317, 2.319, 2.324, 2.326, 2.328, 2.329, 2.331, 2.337, 2.348, 2.355,
+ 2.346, 2.346, 2.345, 2.344, 2.338, 2.329, 2.325, 2.319, 2.316, 2.314, 2.311, 2.309, 2.308, 2.306, 2.304, 2.304, 2.305, 2.307, 2.308, 2.309, 2.311, 2.311, 2.313, 2.316, 2.319, 2.322, 2.325, 2.326, 2.328, 2.335, 2.343, 2.349,
+ 2.342, 2.342, 2.341, 2.338, 2.332, 2.326, 2.319, 2.316, 2.312, 2.309, 2.308, 2.305, 2.303, 2.302, 2.301, 2.301, 2.302, 2.303, 2.304, 2.305, 2.305, 2.307, 2.311, 2.313, 2.315, 2.319, 2.321, 2.325, 2.328, 2.333, 2.338, 2.348,
+ 2.337, 2.337, 2.337, 2.336, 2.331, 2.322, 2.317, 2.312, 2.309, 2.307, 2.304, 2.302, 2.299, 2.299, 2.298, 2.298, 2.299, 2.299, 2.301, 2.302, 2.302, 2.304, 2.305, 2.309, 2.314, 2.316, 2.321, 2.324, 2.326, 2.329, 2.335, 2.343,
+ 2.335, 2.334, 2.333, 2.333, 2.326, 2.318, 2.313, 2.309, 2.306, 2.302, 2.299, 2.297, 2.297, 2.296, 2.295, 2.295, 2.294, 2.295, 2.296, 2.298, 2.298, 2.301, 2.303, 2.305, 2.311, 2.315, 2.319, 2.323, 2.325, 2.329, 2.333, 2.339,
+ 2.329, 2.331, 2.329, 2.329, 2.325, 2.315, 2.309, 2.306, 2.302, 2.299, 2.297, 2.295, 2.293, 2.292, 2.291, 2.291, 2.291, 2.291, 2.293, 2.294, 2.296, 2.298, 2.301, 2.304, 2.307, 2.313, 2.317, 2.319, 2.323, 2.327, 2.331, 2.339,
+ 2.329, 2.328, 2.328, 2.328, 2.321, 2.313, 2.307, 2.303, 2.299, 2.295, 2.294, 2.292, 2.289, 2.289, 2.288, 2.288, 2.288, 2.289, 2.289, 2.292, 2.294, 2.295, 2.297, 2.301, 2.306, 2.311, 2.315, 2.318, 2.319, 2.323, 2.329, 2.335,
+ 2.326, 2.327, 2.325, 2.325, 2.319, 2.311, 2.305, 2.299, 2.296, 2.293, 2.291, 2.289, 2.288, 2.287, 2.285, 2.285, 2.286, 2.288, 2.288, 2.289, 2.291, 2.294, 2.295, 2.298, 2.304, 2.308, 2.313, 2.315, 2.317, 2.319, 2.327, 2.335,
+ 2.325, 2.325, 2.323, 2.323, 2.317, 2.309, 2.303, 2.298, 2.294, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.284, 2.284, 2.285, 2.287, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.305, 2.309, 2.313, 2.315, 2.317, 2.325, 2.334,
+ 2.322, 2.324, 2.322, 2.322, 2.316, 2.306, 2.301, 2.296, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.283, 2.283, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.293, 2.296, 2.301, 2.304, 2.308, 2.311, 2.312, 2.315, 2.323, 2.333,
+ 2.321, 2.323, 2.322, 2.322, 2.314, 2.306, 2.299, 2.294, 2.291, 2.288, 2.286, 2.285, 2.284, 2.282, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.306, 2.308, 2.311, 2.312, 2.322, 2.332,
+ 2.319, 2.321, 2.321, 2.321, 2.314, 2.305, 2.297, 2.293, 2.289, 2.287, 2.285, 2.284, 2.283, 2.281, 2.281, 2.281, 2.282, 2.283, 2.283, 2.285, 2.287, 2.289, 2.291, 2.292, 2.297, 2.301, 2.305, 2.307, 2.309, 2.312, 2.321, 2.333,
+ 2.319, 2.321, 2.319, 2.319, 2.314, 2.303, 2.296, 2.293, 2.289, 2.286, 2.285, 2.283, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.296, 2.301, 2.305, 2.307, 2.308, 2.312, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.296, 2.291, 2.289, 2.286, 2.284, 2.282, 2.281, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.287, 2.288, 2.291, 2.295, 2.299, 2.304, 2.306, 2.307, 2.311, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.287, 2.285, 2.282, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.284, 2.285, 2.286, 2.288, 2.291, 2.295, 2.299, 2.303, 2.306, 2.307, 2.312, 2.321, 2.331,
+ 2.318, 2.319, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.286, 2.285, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.285, 2.286, 2.287, 2.291, 2.294, 2.298, 2.303, 2.306, 2.307, 2.311, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.302, 2.297, 2.292, 2.289, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.287, 2.289, 2.294, 2.297, 2.303, 2.305, 2.308, 2.313, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.303, 2.299, 2.293, 2.291, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.288, 2.291, 2.294, 2.298, 2.304, 2.306, 2.308, 2.312, 2.322, 2.331,
+ 2.319, 2.321, 2.321, 2.321, 2.315, 2.305, 2.301, 2.295, 2.292, 2.289, 2.286, 2.285, 2.283, 2.282, 2.282, 2.282, 2.284, 2.283, 2.284, 2.284, 2.285, 2.287, 2.288, 2.291, 2.294, 2.299, 2.304, 2.306, 2.309, 2.313, 2.322, 2.334,
+ 2.321, 2.322, 2.322, 2.322, 2.317, 2.307, 2.301, 2.296, 2.292, 2.291, 2.288, 2.286, 2.285, 2.284, 2.283, 2.284, 2.285, 2.284, 2.285, 2.285, 2.287, 2.288, 2.289, 2.293, 2.297, 2.301, 2.305, 2.308, 2.311, 2.314, 2.323, 2.335,
+ 2.322, 2.324, 2.324, 2.324, 2.319, 2.309, 2.303, 2.297, 2.295, 2.292, 2.291, 2.288, 2.286, 2.286, 2.285, 2.286, 2.286, 2.286, 2.287, 2.288, 2.289, 2.289, 2.291, 2.294, 2.299, 2.302, 2.307, 2.311, 2.312, 2.316, 2.325, 2.335,
+ 2.324, 2.326, 2.325, 2.326, 2.321, 2.311, 2.305, 2.301, 2.297, 2.295, 2.293, 2.291, 2.289, 2.289, 2.288, 2.288, 2.287, 2.288, 2.289, 2.291, 2.292, 2.292, 2.295, 2.299, 2.301, 2.304, 2.309, 2.312, 2.315, 2.319, 2.327, 2.337,
+ 2.329, 2.329, 2.328, 2.328, 2.323, 2.315, 2.308, 2.304, 2.301, 2.298, 2.296, 2.294, 2.291, 2.291, 2.289, 2.291, 2.291, 2.291, 2.292, 2.293, 2.294, 2.295, 2.297, 2.299, 2.303, 2.308, 2.312, 2.315, 2.318, 2.321, 2.329, 2.339,
+ 2.329, 2.331, 2.332, 2.332, 2.326, 2.318, 2.311, 2.306, 2.304, 2.301, 2.299, 2.297, 2.295, 2.293, 2.292, 2.292, 2.292, 2.293, 2.294, 2.294, 2.296, 2.297, 2.299, 2.302, 2.306, 2.311, 2.315, 2.318, 2.319, 2.324, 2.332, 2.342,
+ 2.331, 2.333, 2.334, 2.334, 2.328, 2.321, 2.313, 2.308, 2.305, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.295, 2.294, 2.296, 2.296, 2.297, 2.298, 2.299, 2.302, 2.305, 2.308, 2.314, 2.317, 2.321, 2.323, 2.327, 2.334, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.309, 2.306, 2.304, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.309, 2.315, 2.319, 2.321, 2.324, 2.328, 2.337, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.311, 2.306, 2.304, 2.303, 2.302, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.311, 2.314, 2.319, 2.323, 2.325, 2.329, 2.339, 2.348,
+ 2.329, 2.329, 2.329, 2.331, 2.326, 2.319, 2.312, 2.309, 2.304, 2.303, 2.302, 2.301, 2.298, 2.295, 2.294, 2.294, 2.295, 2.295, 2.296, 2.297, 2.299, 2.301, 2.302, 2.304, 2.308, 2.313, 2.319, 2.322, 2.325, 2.329, 2.339, 2.351,
+ 2.329, 2.329, 2.329, 2.329, 2.326, 2.317, 2.311, 2.308, 2.303, 2.302, 2.301, 2.298, 2.296, 2.295, 2.294, 2.294, 2.294, 2.294, 2.296, 2.297, 2.298, 2.299, 2.301, 2.304, 2.307, 2.312, 2.318, 2.322, 2.326, 2.331, 2.341, 2.355,
+ 2.339, 2.332, 2.331, 2.331, 2.327, 2.323, 2.316, 2.309, 2.306, 2.302, 2.301, 2.299, 2.297, 2.296, 2.295, 2.294, 2.294, 2.296, 2.297, 2.297, 2.299, 2.301, 2.303, 2.306, 2.308, 2.317, 2.322, 2.325, 2.329, 2.341, 2.353, 2.361,
+ 2.347, 2.347, 2.345, 2.343, 2.338, 2.332, 2.326, 2.322, 2.321, 2.318, 2.316, 2.315, 2.313, 2.312, 2.311, 2.311, 2.311, 2.311, 2.312, 2.315, 2.317, 2.318, 2.319, 2.323, 2.324, 2.329, 2.334, 2.337, 2.344, 2.347, 2.361, 2.364
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.869, 3.852, 3.844, 3.842, 3.836, 3.821, 3.807, 3.796, 3.789, 3.784, 3.778, 3.775, 3.769, 3.768, 3.765, 3.765, 3.767, 3.769, 3.772, 3.774, 3.773, 3.775, 3.779, 3.787, 3.793, 3.801, 3.806, 3.804, 3.813, 3.819, 3.855, 3.879,
+ 3.854, 3.844, 3.837, 3.836, 3.824, 3.811, 3.797, 3.789, 3.784, 3.777, 3.774, 3.769, 3.764, 3.758, 3.757, 3.758, 3.758, 3.761, 3.763, 3.764, 3.765, 3.766, 3.772, 3.778, 3.787, 3.792, 3.794, 3.798, 3.802, 3.815, 3.839, 3.873,
+ 3.838, 3.831, 3.826, 3.823, 3.813, 3.799, 3.787, 3.781, 3.773, 3.768, 3.763, 3.759, 3.753, 3.749, 3.745, 3.745, 3.745, 3.752, 3.754, 3.757, 3.757, 3.759, 3.763, 3.769, 3.773, 3.781, 3.786, 3.792, 3.798, 3.811, 3.831, 3.861,
+ 3.833, 3.822, 3.817, 3.816, 3.804, 3.788, 3.779, 3.772, 3.766, 3.759, 3.755, 3.749, 3.744, 3.741, 3.738, 3.739, 3.739, 3.741, 3.743, 3.747, 3.749, 3.751, 3.756, 3.764, 3.769, 3.776, 3.783, 3.789, 3.798, 3.809, 3.821, 3.855,
+ 3.824, 3.818, 3.808, 3.808, 3.797, 3.781, 3.772, 3.764, 3.757, 3.752, 3.747, 3.743, 3.737, 3.735, 3.733, 3.733, 3.733, 3.735, 3.737, 3.738, 3.741, 3.746, 3.749, 3.755, 3.766, 3.771, 3.781, 3.789, 3.794, 3.806, 3.818, 3.849,
+ 3.815, 3.808, 3.799, 3.801, 3.787, 3.775, 3.767, 3.757, 3.751, 3.745, 3.738, 3.734, 3.732, 3.727, 3.725, 3.723, 3.722, 3.722, 3.726, 3.729, 3.734, 3.738, 3.744, 3.749, 3.759, 3.769, 3.781, 3.788, 3.792, 3.799, 3.811, 3.841,
+ 3.804, 3.799, 3.793, 3.793, 3.783, 3.771, 3.759, 3.751, 3.744, 3.735, 3.732, 3.727, 3.723, 3.721, 3.719, 3.716, 3.716, 3.716, 3.718, 3.722, 3.727, 3.731, 3.737, 3.746, 3.756, 3.767, 3.776, 3.782, 3.788, 3.795, 3.808, 3.831,
+ 3.802, 3.797, 3.787, 3.787, 3.779, 3.762, 3.753, 3.744, 3.734, 3.727, 3.725, 3.721, 3.716, 3.714, 3.709, 3.709, 3.711, 3.711, 3.712, 3.717, 3.722, 3.725, 3.731, 3.739, 3.752, 3.762, 3.772, 3.778, 3.779, 3.789, 3.798, 3.826,
+ 3.791, 3.789, 3.784, 3.784, 3.775, 3.759, 3.746, 3.735, 3.729, 3.724, 3.718, 3.714, 3.712, 3.707, 3.704, 3.704, 3.706, 3.708, 3.709, 3.711, 3.716, 3.722, 3.726, 3.735, 3.746, 3.754, 3.767, 3.774, 3.777, 3.781, 3.794, 3.824,
+ 3.789, 3.784, 3.779, 3.781, 3.771, 3.753, 3.741, 3.732, 3.725, 3.719, 3.715, 3.711, 3.707, 3.704, 3.701, 3.701, 3.702, 3.704, 3.708, 3.709, 3.713, 3.718, 3.724, 3.731, 3.742, 3.749, 3.761, 3.768, 3.772, 3.778, 3.791, 3.822,
+ 3.789, 3.781, 3.777, 3.777, 3.764, 3.749, 3.739, 3.729, 3.722, 3.718, 3.711, 3.708, 3.705, 3.701, 3.699, 3.699, 3.699, 3.701, 3.705, 3.707, 3.711, 3.715, 3.721, 3.727, 3.738, 3.746, 3.757, 3.763, 3.765, 3.773, 3.788, 3.821,
+ 3.785, 3.779, 3.774, 3.774, 3.764, 3.747, 3.736, 3.726, 3.719, 3.711, 3.709, 3.706, 3.701, 3.698, 3.696, 3.695, 3.695, 3.698, 3.702, 3.704, 3.707, 3.712, 3.718, 3.725, 3.734, 3.741, 3.753, 3.756, 3.759, 3.764, 3.784, 3.818,
+ 3.779, 3.776, 3.773, 3.773, 3.759, 3.744, 3.733, 3.724, 3.714, 3.709, 3.706, 3.704, 3.699, 3.696, 3.694, 3.694, 3.694, 3.697, 3.701, 3.703, 3.706, 3.709, 3.714, 3.721, 3.731, 3.737, 3.749, 3.753, 3.758, 3.762, 3.783, 3.819,
+ 3.779, 3.776, 3.769, 3.769, 3.757, 3.741, 3.729, 3.721, 3.712, 3.708, 3.705, 3.701, 3.697, 3.695, 3.694, 3.694, 3.695, 3.696, 3.698, 3.702, 3.705, 3.709, 3.712, 3.717, 3.728, 3.736, 3.749, 3.752, 3.756, 3.761, 3.781, 3.815,
+ 3.779, 3.773, 3.768, 3.768, 3.756, 3.738, 3.731, 3.719, 3.711, 3.707, 3.703, 3.698, 3.695, 3.694, 3.694, 3.695, 3.695, 3.695, 3.696, 3.702, 3.705, 3.708, 3.712, 3.717, 3.728, 3.736, 3.747, 3.751, 3.754, 3.761, 3.781, 3.815,
+ 3.782, 3.773, 3.767, 3.767, 3.755, 3.738, 3.728, 3.721, 3.711, 3.707, 3.701, 3.698, 3.695, 3.693, 3.694, 3.696, 3.695, 3.695, 3.695, 3.701, 3.703, 3.706, 3.711, 3.715, 3.726, 3.735, 3.745, 3.751, 3.754, 3.763, 3.779, 3.815,
+ 3.781, 3.771, 3.767, 3.767, 3.754, 3.739, 3.726, 3.721, 3.712, 3.706, 3.701, 3.698, 3.695, 3.693, 3.693, 3.695, 3.695, 3.695, 3.696, 3.698, 3.703, 3.705, 3.709, 3.715, 3.725, 3.734, 3.745, 3.751, 3.755, 3.762, 3.783, 3.818,
+ 3.781, 3.774, 3.767, 3.767, 3.755, 3.741, 3.729, 3.722, 3.712, 3.708, 3.701, 3.699, 3.695, 3.693, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.725, 3.732, 3.746, 3.751, 3.756, 3.763, 3.783, 3.821,
+ 3.781, 3.774, 3.769, 3.769, 3.756, 3.741, 3.731, 3.724, 3.713, 3.711, 3.707, 3.699, 3.697, 3.694, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.724, 3.734, 3.747, 3.751, 3.756, 3.765, 3.784, 3.821,
+ 3.784, 3.776, 3.773, 3.773, 3.759, 3.742, 3.733, 3.726, 3.719, 3.711, 3.709, 3.703, 3.698, 3.695, 3.694, 3.695, 3.697, 3.696, 3.698, 3.699, 3.703, 3.706, 3.711, 3.714, 3.727, 3.735, 3.746, 3.751, 3.757, 3.766, 3.787, 3.822,
+ 3.786, 3.783, 3.774, 3.774, 3.766, 3.747, 3.737, 3.727, 3.722, 3.716, 3.711, 3.706, 3.702, 3.698, 3.697, 3.698, 3.699, 3.699, 3.701, 3.703, 3.706, 3.711, 3.713, 3.719, 3.731, 3.739, 3.748, 3.753, 3.761, 3.769, 3.789, 3.826,
+ 3.786, 3.784, 3.779, 3.779, 3.769, 3.751, 3.742, 3.732, 3.725, 3.719, 3.715, 3.711, 3.706, 3.704, 3.701, 3.701, 3.702, 3.702, 3.705, 3.707, 3.712, 3.714, 3.717, 3.724, 3.733, 3.743, 3.749, 3.758, 3.764, 3.769, 3.791, 3.826,
+ 3.793, 3.787, 3.782, 3.782, 3.774, 3.756, 3.747, 3.737, 3.729, 3.725, 3.719, 3.715, 3.712, 3.708, 3.707, 3.706, 3.707, 3.708, 3.709, 3.713, 3.714, 3.717, 3.723, 3.729, 3.736, 3.747, 3.757, 3.764, 3.768, 3.774, 3.794, 3.829,
+ 3.794, 3.791, 3.786, 3.786, 3.779, 3.762, 3.751, 3.742, 3.735, 3.729, 3.725, 3.719, 3.716, 3.711, 3.709, 3.709, 3.709, 3.711, 3.716, 3.717, 3.721, 3.723, 3.726, 3.732, 3.741, 3.752, 3.761, 3.767, 3.773, 3.779, 3.801, 3.829,
+ 3.802, 3.798, 3.793, 3.793, 3.779, 3.766, 3.754, 3.746, 3.741, 3.736, 3.731, 3.726, 3.719, 3.717, 3.716, 3.715, 3.716, 3.717, 3.719, 3.721, 3.724, 3.726, 3.731, 3.737, 3.744, 3.756, 3.766, 3.772, 3.776, 3.784, 3.807, 3.839,
+ 3.805, 3.799, 3.795, 3.795, 3.784, 3.767, 3.757, 3.749, 3.744, 3.739, 3.736, 3.731, 3.726, 3.722, 3.719, 3.719, 3.719, 3.721, 3.723, 3.725, 3.727, 3.732, 3.738, 3.742, 3.751, 3.761, 3.771, 3.775, 3.782, 3.789, 3.811, 3.841,
+ 3.804, 3.801, 3.799, 3.799, 3.787, 3.772, 3.761, 3.752, 3.746, 3.742, 3.739, 3.735, 3.729, 3.726, 3.723, 3.724, 3.725, 3.726, 3.727, 3.728, 3.732, 3.736, 3.739, 3.745, 3.754, 3.765, 3.775, 3.779, 3.785, 3.795, 3.816, 3.844,
+ 3.801, 3.799, 3.796, 3.796, 3.787, 3.773, 3.761, 3.753, 3.746, 3.743, 3.739, 3.735, 3.731, 3.726, 3.725, 3.725, 3.725, 3.726, 3.727, 3.729, 3.733, 3.736, 3.741, 3.745, 3.755, 3.766, 3.776, 3.783, 3.786, 3.797, 3.819, 3.851,
+ 3.799, 3.795, 3.788, 3.788, 3.783, 3.772, 3.759, 3.749, 3.744, 3.738, 3.735, 3.733, 3.726, 3.724, 3.722, 3.722, 3.723, 3.724, 3.725, 3.727, 3.729, 3.733, 3.736, 3.742, 3.754, 3.762, 3.772, 3.779, 3.784, 3.796, 3.821, 3.859,
+ 3.799, 3.789, 3.787, 3.788, 3.779, 3.766, 3.755, 3.749, 3.742, 3.736, 3.733, 3.727, 3.723, 3.722, 3.721, 3.719, 3.719, 3.721, 3.725, 3.726, 3.728, 3.732, 3.734, 3.741, 3.747, 3.758, 3.771, 3.778, 3.785, 3.796, 3.825, 3.862,
+ 3.824, 3.799, 3.789, 3.789, 3.788, 3.777, 3.761, 3.751, 3.743, 3.739, 3.736, 3.728, 3.726, 3.725, 3.721, 3.719, 3.721, 3.723, 3.727, 3.728, 3.729, 3.733, 3.737, 3.744, 3.755, 3.769, 3.776, 3.784, 3.793, 3.819, 3.863, 3.877,
+ 3.833, 3.833, 3.833, 3.842, 3.825, 3.815, 3.807, 3.799, 3.792, 3.788, 3.785, 3.782, 3.778, 3.777, 3.773, 3.772, 3.772, 3.774, 3.778, 3.779, 3.779, 3.785, 3.792, 3.798, 3.803, 3.811, 3.822, 3.834, 3.843, 3.846, 3.877, 3.886
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.616, 2.616, 2.618, 2.621, 2.619, 2.618, 2.615, 2.615, 2.613, 2.611, 2.609, 2.609, 2.609, 2.611, 2.611, 2.611, 2.611, 2.609, 2.608, 2.608, 2.611, 2.613, 2.613, 2.614, 2.614, 2.615, 2.615, 2.622, 2.624, 2.621, 2.624, 2.641,
+ 2.616, 2.618, 2.621, 2.623, 2.623, 2.619, 2.618, 2.616, 2.616, 2.613, 2.611, 2.611, 2.611, 2.611, 2.612, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.612, 2.613, 2.612, 2.613, 2.615, 2.617, 2.621, 2.621, 2.619, 2.621, 2.641,
+ 2.621, 2.624, 2.627, 2.627, 2.625, 2.623, 2.621, 2.619, 2.618, 2.618, 2.618, 2.617, 2.616, 2.616, 2.615, 2.613, 2.612, 2.613, 2.613, 2.614, 2.614, 2.613, 2.614, 2.613, 2.614, 2.617, 2.619, 2.621, 2.621, 2.619, 2.623, 2.643,
+ 2.626, 2.627, 2.628, 2.629, 2.628, 2.625, 2.622, 2.621, 2.621, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.616, 2.616, 2.616, 2.618, 2.618, 2.617, 2.617, 2.618, 2.619, 2.621, 2.623, 2.624, 2.626, 2.625, 2.624, 2.625, 2.654,
+ 2.627, 2.628, 2.628, 2.628, 2.626, 2.623, 2.622, 2.622, 2.622, 2.622, 2.621, 2.621, 2.619, 2.617, 2.617, 2.616, 2.617, 2.617, 2.618, 2.619, 2.618, 2.618, 2.618, 2.621, 2.622, 2.624, 2.626, 2.627, 2.627, 2.626, 2.628, 2.655,
+ 2.625, 2.626, 2.627, 2.626, 2.625, 2.623, 2.622, 2.621, 2.622, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.616, 2.616, 2.616, 2.616, 2.616, 2.617, 2.618, 2.619, 2.621, 2.622, 2.624, 2.626, 2.628, 2.628, 2.629, 2.629, 2.655,
+ 2.626, 2.625, 2.626, 2.625, 2.625, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.616, 2.614, 2.613, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.618, 2.619, 2.621, 2.623, 2.624, 2.627, 2.629, 2.631, 2.629, 2.631, 2.651,
+ 2.625, 2.625, 2.625, 2.624, 2.623, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.614, 2.613, 2.612, 2.611, 2.611, 2.612, 2.612, 2.613, 2.616, 2.618, 2.619, 2.622, 2.624, 2.626, 2.628, 2.631, 2.631, 2.631, 2.631, 2.651,
+ 2.625, 2.625, 2.624, 2.623, 2.622, 2.622, 2.622, 2.622, 2.622, 2.621, 2.617, 2.615, 2.613, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.613, 2.615, 2.618, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.624, 2.624, 2.622, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.618, 2.616, 2.614, 2.612, 2.611, 2.609, 2.609, 2.608, 2.609, 2.611, 2.611, 2.615, 2.617, 2.619, 2.621, 2.625, 2.628, 2.631, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.622, 2.623, 2.622, 2.622, 2.621, 2.619, 2.619, 2.619, 2.618, 2.616, 2.614, 2.613, 2.611, 2.609, 2.608, 2.606, 2.607, 2.607, 2.609, 2.611, 2.615, 2.617, 2.619, 2.622, 2.626, 2.629, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.622, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.614, 2.613, 2.611, 2.611, 2.607, 2.606, 2.605, 2.604, 2.605, 2.607, 2.609, 2.613, 2.616, 2.619, 2.622, 2.627, 2.631, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.613, 2.609, 2.607, 2.604, 2.602, 2.601, 2.602, 2.603, 2.605, 2.609, 2.612, 2.616, 2.619, 2.624, 2.628, 2.631, 2.632, 2.633, 2.629, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.615, 2.614, 2.612, 2.608, 2.603, 2.601, 2.598, 2.597, 2.599, 2.602, 2.605, 2.608, 2.611, 2.615, 2.622, 2.625, 2.629, 2.631, 2.631, 2.633, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.616, 2.614, 2.611, 2.606, 2.601, 2.598, 2.595, 2.595, 2.597, 2.601, 2.604, 2.608, 2.612, 2.615, 2.623, 2.627, 2.629, 2.631, 2.631, 2.632, 2.631, 2.628, 2.628, 2.651,
+ 2.622, 2.623, 2.624, 2.624, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.609, 2.606, 2.601, 2.596, 2.594, 2.594, 2.596, 2.599, 2.603, 2.609, 2.613, 2.617, 2.623, 2.627, 2.629, 2.631, 2.632, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.623, 2.625, 2.625, 2.624, 2.621, 2.621, 2.619, 2.617, 2.616, 2.613, 2.608, 2.605, 2.601, 2.595, 2.593, 2.593, 2.595, 2.598, 2.604, 2.609, 2.615, 2.619, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.632, 2.629, 2.631, 2.651,
+ 2.624, 2.626, 2.626, 2.623, 2.621, 2.619, 2.618, 2.617, 2.615, 2.612, 2.608, 2.605, 2.601, 2.597, 2.595, 2.595, 2.596, 2.598, 2.605, 2.609, 2.616, 2.621, 2.626, 2.627, 2.629, 2.631, 2.633, 2.633, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.618, 2.617, 2.614, 2.612, 2.609, 2.606, 2.602, 2.599, 2.598, 2.597, 2.598, 2.602, 2.607, 2.612, 2.619, 2.621, 2.626, 2.628, 2.629, 2.632, 2.633, 2.634, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.621, 2.618, 2.617, 2.614, 2.612, 2.611, 2.608, 2.604, 2.602, 2.599, 2.599, 2.603, 2.606, 2.611, 2.616, 2.621, 2.624, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.634, 2.633, 2.633, 2.656,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.611, 2.611, 2.607, 2.604, 2.604, 2.604, 2.606, 2.609, 2.613, 2.619, 2.622, 2.625, 2.628, 2.631, 2.632, 2.633, 2.633, 2.636, 2.636, 2.634, 2.634, 2.658,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.619, 2.618, 2.616, 2.614, 2.613, 2.612, 2.611, 2.609, 2.608, 2.607, 2.608, 2.609, 2.613, 2.617, 2.621, 2.623, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.635, 2.636, 2.636, 2.636, 2.661,
+ 2.623, 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.616, 2.615, 2.614, 2.613, 2.612, 2.612, 2.611, 2.611, 2.611, 2.614, 2.615, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.633, 2.635, 2.635, 2.637, 2.637, 2.636, 2.637, 2.661,
+ 2.623, 2.624, 2.625, 2.626, 2.624, 2.621, 2.619, 2.617, 2.616, 2.615, 2.615, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.619, 2.621, 2.623, 2.626, 2.628, 2.631, 2.632, 2.634, 2.635, 2.636, 2.637, 2.638, 2.637, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.627, 2.626, 2.623, 2.619, 2.619, 2.618, 2.618, 2.618, 2.617, 2.617, 2.616, 2.616, 2.616, 2.619, 2.622, 2.623, 2.625, 2.628, 2.628, 2.631, 2.632, 2.634, 2.636, 2.638, 2.639, 2.639, 2.638, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.628, 2.626, 2.623, 2.621, 2.619, 2.619, 2.619, 2.619, 2.619, 2.619, 2.618, 2.618, 2.619, 2.623, 2.624, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.635, 2.638, 2.639, 2.639, 2.639, 2.636, 2.636, 2.662,
+ 2.625, 2.627, 2.628, 2.628, 2.626, 2.624, 2.623, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.624, 2.624, 2.625, 2.627, 2.628, 2.631, 2.631, 2.632, 2.634, 2.636, 2.639, 2.639, 2.641, 2.639, 2.635, 2.635, 2.663,
+ 2.625, 2.626, 2.628, 2.628, 2.627, 2.625, 2.624, 2.623, 2.623, 2.622, 2.623, 2.624, 2.624, 2.625, 2.625, 2.625, 2.625, 2.626, 2.627, 2.629, 2.631, 2.632, 2.633, 2.635, 2.638, 2.641, 2.642, 2.643, 2.642, 2.636, 2.636, 2.665,
+ 2.624, 2.626, 2.628, 2.628, 2.628, 2.626, 2.624, 2.624, 2.623, 2.623, 2.623, 2.625, 2.627, 2.627, 2.626, 2.626, 2.626, 2.627, 2.628, 2.629, 2.632, 2.633, 2.635, 2.637, 2.639, 2.642, 2.644, 2.644, 2.642, 2.638, 2.638, 2.665,
+ 2.623, 2.625, 2.626, 2.627, 2.626, 2.626, 2.624, 2.623, 2.623, 2.623, 2.623, 2.623, 2.626, 2.627, 2.626, 2.626, 2.626, 2.626, 2.628, 2.628, 2.629, 2.631, 2.634, 2.636, 2.639, 2.642, 2.644, 2.643, 2.641, 2.637, 2.638, 2.659,
+ 2.623, 2.627, 2.627, 2.627, 2.627, 2.628, 2.627, 2.624, 2.624, 2.623, 2.624, 2.624, 2.628, 2.628, 2.627, 2.628, 2.628, 2.628, 2.629, 2.629, 2.631, 2.635, 2.637, 2.639, 2.641, 2.643, 2.646, 2.645, 2.643, 2.641, 2.654, 2.659,
+ 2.642, 2.641, 2.643, 2.645, 2.645, 2.644, 2.644, 2.643, 2.643, 2.642, 2.642, 2.642, 2.643, 2.644, 2.644, 2.644, 2.646, 2.646, 2.647, 2.649, 2.651, 2.652, 2.654, 2.656, 2.658, 2.661, 2.661, 2.661, 2.659, 2.654, 2.659, 2.659
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.391, 1.394, 1.395, 1.396, 1.398, 1.398, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.398, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.399, 1.397, 1.399, 1.402,
+ 1.393, 1.395, 1.396, 1.398, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.401, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.399, 1.401, 1.401, 1.399, 1.398, 1.399, 1.402,
+ 1.398, 1.401, 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.401, 1.401, 1.399, 1.399, 1.401, 1.401, 1.401, 1.401, 1.399, 1.401, 1.406,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.402, 1.403, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.405, 1.404, 1.404, 1.404, 1.404, 1.405, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.406, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.408, 1.408, 1.407, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.403, 1.402, 1.402, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.408, 1.407, 1.405, 1.405, 1.414,
+ 1.402, 1.402, 1.402, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.403, 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.407, 1.409, 1.409, 1.409, 1.409, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.403, 1.404, 1.405, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.408, 1.405, 1.405, 1.413,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.402, 1.401, 1.399, 1.398, 1.398, 1.399, 1.399, 1.401, 1.403, 1.404, 1.405, 1.407, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.403, 1.404, 1.405, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.399, 1.401, 1.403, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.398, 1.401, 1.403, 1.406, 1.407, 1.409, 1.409, 1.411, 1.409, 1.409, 1.409, 1.408, 1.407, 1.407, 1.413,
+ 1.403, 1.404, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.404, 1.404, 1.403, 1.402, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.402, 1.404, 1.406, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.414,
+ 1.403, 1.403, 1.404, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.402, 1.401, 1.399, 1.398, 1.398, 1.398, 1.401, 1.403, 1.404, 1.408, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.402, 1.404, 1.406, 1.407, 1.408, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.409, 1.408, 1.408, 1.415,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.406, 1.408, 1.408, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.405, 1.406, 1.407, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.407, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.416,
+ 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.406, 1.407, 1.407, 1.407, 1.408, 1.409, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.417,
+ 1.402, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.415,
+ 1.402, 1.402, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.405, 1.406, 1.406, 1.406, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.412, 1.412, 1.413, 1.413, 1.411, 1.408, 1.411, 1.413,
+ 1.406, 1.406, 1.408, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.414, 1.414, 1.414, 1.414, 1.415, 1.415, 1.415, 1.415, 1.416, 1.416, 1.416, 1.417, 1.418, 1.418, 1.417, 1.417, 1.414, 1.411, 1.413, 1.413
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.554, 1.522, 1.466, 1.422, 1.385, 1.351, 1.322, 1.294, 1.269, 1.246, 1.228, 1.214, 1.207, 1.202, 1.199, 1.199, 1.199, 1.199, 1.202, 1.207, 1.218, 1.235, 1.255, 1.279, 1.305, 1.333, 1.365, 1.402, 1.447, 1.508, 1.602, 1.638,
+ 1.522, 1.478, 1.431, 1.391, 1.355, 1.323, 1.298, 1.271, 1.247, 1.228, 1.212, 1.199, 1.187, 1.179, 1.173, 1.172, 1.172, 1.174, 1.179, 1.189, 1.201, 1.216, 1.235, 1.256, 1.282, 1.308, 1.335, 1.368, 1.411, 1.461, 1.535, 1.602,
+ 1.479, 1.449, 1.407, 1.367, 1.332, 1.301, 1.271, 1.247, 1.226, 1.208, 1.191, 1.178, 1.166, 1.158, 1.153, 1.151, 1.151, 1.153, 1.159, 1.168, 1.179, 1.194, 1.212, 1.234, 1.256, 1.282, 1.311, 1.343, 1.382, 1.427, 1.489, 1.535,
+ 1.454, 1.423, 1.383, 1.345, 1.309, 1.278, 1.249, 1.226, 1.206, 1.187, 1.171, 1.158, 1.146, 1.138, 1.132, 1.129, 1.129, 1.133, 1.139, 1.147, 1.159, 1.173, 1.191, 1.212, 1.234, 1.261, 1.288, 1.321, 1.357, 1.401, 1.455, 1.489,
+ 1.433, 1.401, 1.362, 1.325, 1.289, 1.258, 1.231, 1.206, 1.187, 1.169, 1.153, 1.138, 1.129, 1.121, 1.115, 1.112, 1.112, 1.114, 1.121, 1.129, 1.141, 1.155, 1.172, 1.191, 1.214, 1.241, 1.269, 1.301, 1.337, 1.377, 1.428, 1.457,
+ 1.415, 1.382, 1.343, 1.306, 1.273, 1.241, 1.213, 1.189, 1.169, 1.153, 1.137, 1.123, 1.112, 1.105, 1.097, 1.095, 1.095, 1.098, 1.103, 1.112, 1.124, 1.139, 1.155, 1.173, 1.197, 1.222, 1.252, 1.282, 1.317, 1.356, 1.405, 1.434,
+ 1.398, 1.363, 1.325, 1.289, 1.256, 1.224, 1.198, 1.175, 1.155, 1.137, 1.123, 1.108, 1.097, 1.089, 1.083, 1.079, 1.079, 1.083, 1.088, 1.097, 1.109, 1.124, 1.139, 1.158, 1.181, 1.206, 1.234, 1.266, 1.299, 1.339, 1.384, 1.415,
+ 1.382, 1.347, 1.309, 1.274, 1.242, 1.211, 1.185, 1.162, 1.142, 1.124, 1.108, 1.095, 1.083, 1.075, 1.069, 1.066, 1.066, 1.068, 1.074, 1.083, 1.096, 1.109, 1.125, 1.145, 1.166, 1.191, 1.219, 1.251, 1.285, 1.324, 1.367, 1.399,
+ 1.369, 1.334, 1.296, 1.261, 1.228, 1.199, 1.173, 1.151, 1.131, 1.112, 1.095, 1.083, 1.071, 1.062, 1.056, 1.053, 1.053, 1.055, 1.061, 1.069, 1.083, 1.096, 1.112, 1.132, 1.153, 1.178, 1.206, 1.237, 1.271, 1.309, 1.353, 1.385,
+ 1.359, 1.321, 1.284, 1.251, 1.217, 1.189, 1.164, 1.141, 1.121, 1.102, 1.086, 1.071, 1.061, 1.049, 1.045, 1.042, 1.042, 1.043, 1.051, 1.061, 1.069, 1.085, 1.101, 1.121, 1.143, 1.167, 1.195, 1.225, 1.259, 1.298, 1.341, 1.375,
+ 1.351, 1.312, 1.275, 1.241, 1.209, 1.181, 1.155, 1.133, 1.112, 1.092, 1.076, 1.061, 1.049, 1.041, 1.034, 1.032, 1.032, 1.035, 1.041, 1.051, 1.061, 1.075, 1.092, 1.112, 1.133, 1.158, 1.185, 1.216, 1.249, 1.288, 1.331, 1.364,
+ 1.344, 1.303, 1.267, 1.233, 1.201, 1.173, 1.147, 1.124, 1.104, 1.085, 1.067, 1.053, 1.041, 1.033, 1.024, 1.022, 1.022, 1.025, 1.034, 1.041, 1.053, 1.066, 1.083, 1.103, 1.126, 1.149, 1.177, 1.207, 1.241, 1.279, 1.321, 1.357,
+ 1.339, 1.297, 1.261, 1.226, 1.194, 1.166, 1.142, 1.119, 1.098, 1.078, 1.061, 1.046, 1.034, 1.024, 1.017, 1.014, 1.014, 1.017, 1.025, 1.034, 1.046, 1.059, 1.077, 1.096, 1.118, 1.143, 1.169, 1.201, 1.235, 1.273, 1.314, 1.352,
+ 1.337, 1.293, 1.256, 1.223, 1.191, 1.163, 1.136, 1.114, 1.093, 1.074, 1.056, 1.041, 1.027, 1.017, 1.012, 1.006, 1.006, 1.013, 1.017, 1.028, 1.041, 1.055, 1.072, 1.092, 1.114, 1.138, 1.165, 1.195, 1.229, 1.268, 1.309, 1.348,
+ 1.337, 1.291, 1.253, 1.219, 1.187, 1.159, 1.133, 1.109, 1.089, 1.071, 1.053, 1.037, 1.023, 1.012, 1.006, 1.002, 1.003, 1.006, 1.013, 1.023, 1.038, 1.052, 1.069, 1.089, 1.111, 1.135, 1.161, 1.192, 1.226, 1.264, 1.306, 1.348,
+ 1.337, 1.291, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.035, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.341, 1.292, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.034, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.348, 1.298, 1.255, 1.219, 1.188, 1.159, 1.134, 1.111, 1.088, 1.069, 1.051, 1.035, 1.021, 1.009, 1.003, 1.001, 1.002, 1.004, 1.011, 1.022, 1.036, 1.053, 1.071, 1.089, 1.111, 1.135, 1.162, 1.191, 1.226, 1.264, 1.306, 1.347,
+ 1.354, 1.306, 1.258, 1.222, 1.191, 1.162, 1.135, 1.113, 1.092, 1.073, 1.054, 1.038, 1.024, 1.014, 1.008, 1.003, 1.004, 1.008, 1.014, 1.026, 1.039, 1.056, 1.073, 1.093, 1.115, 1.139, 1.165, 1.195, 1.229, 1.267, 1.309, 1.349,
+ 1.358, 1.312, 1.263, 1.227, 1.195, 1.167, 1.141, 1.117, 1.097, 1.078, 1.061, 1.043, 1.029, 1.021, 1.014, 1.008, 1.008, 1.014, 1.021, 1.032, 1.045, 1.061, 1.078, 1.097, 1.119, 1.144, 1.169, 1.201, 1.234, 1.272, 1.315, 1.353,
+ 1.364, 1.319, 1.269, 1.234, 1.201, 1.174, 1.148, 1.124, 1.103, 1.084, 1.067, 1.052, 1.038, 1.029, 1.021, 1.016, 1.016, 1.021, 1.029, 1.038, 1.051, 1.067, 1.084, 1.103, 1.126, 1.151, 1.176, 1.207, 1.241, 1.279, 1.321, 1.358,
+ 1.371, 1.326, 1.277, 1.242, 1.209, 1.181, 1.155, 1.132, 1.111, 1.092, 1.075, 1.061, 1.049, 1.038, 1.029, 1.027, 1.027, 1.029, 1.038, 1.047, 1.061, 1.075, 1.092, 1.111, 1.133, 1.157, 1.185, 1.213, 1.247, 1.286, 1.329, 1.365,
+ 1.379, 1.334, 1.287, 1.251, 1.219, 1.191, 1.164, 1.141, 1.119, 1.101, 1.085, 1.071, 1.061, 1.049, 1.041, 1.038, 1.038, 1.041, 1.047, 1.059, 1.071, 1.084, 1.101, 1.119, 1.141, 1.165, 1.193, 1.223, 1.257, 1.295, 1.338, 1.374,
+ 1.389, 1.343, 1.298, 1.262, 1.231, 1.201, 1.174, 1.151, 1.131, 1.111, 1.095, 1.083, 1.071, 1.061, 1.054, 1.051, 1.051, 1.054, 1.059, 1.071, 1.081, 1.094, 1.111, 1.129, 1.152, 1.176, 1.203, 1.235, 1.269, 1.307, 1.351, 1.384,
+ 1.401, 1.351, 1.311, 1.274, 1.242, 1.214, 1.187, 1.164, 1.142, 1.124, 1.108, 1.095, 1.083, 1.074, 1.068, 1.066, 1.066, 1.068, 1.073, 1.081, 1.094, 1.108, 1.123, 1.141, 1.164, 1.188, 1.215, 1.247, 1.281, 1.321, 1.364, 1.396,
+ 1.412, 1.366, 1.327, 1.289, 1.257, 1.227, 1.201, 1.176, 1.156, 1.137, 1.122, 1.108, 1.096, 1.088, 1.083, 1.081, 1.081, 1.082, 1.087, 1.095, 1.108, 1.122, 1.136, 1.154, 1.177, 1.201, 1.229, 1.261, 1.296, 1.337, 1.382, 1.409,
+ 1.421, 1.383, 1.343, 1.306, 1.273, 1.243, 1.216, 1.192, 1.169, 1.152, 1.137, 1.122, 1.111, 1.103, 1.098, 1.095, 1.095, 1.097, 1.102, 1.111, 1.123, 1.136, 1.152, 1.169, 1.191, 1.217, 1.246, 1.278, 1.314, 1.354, 1.399, 1.429,
+ 1.434, 1.402, 1.362, 1.324, 1.291, 1.261, 1.232, 1.208, 1.187, 1.168, 1.152, 1.138, 1.127, 1.119, 1.114, 1.112, 1.112, 1.115, 1.121, 1.128, 1.139, 1.152, 1.169, 1.186, 1.209, 1.234, 1.262, 1.295, 1.332, 1.372, 1.419, 1.451,
+ 1.453, 1.422, 1.382, 1.344, 1.309, 1.278, 1.249, 1.226, 1.204, 1.187, 1.168, 1.155, 1.144, 1.135, 1.131, 1.131, 1.131, 1.133, 1.138, 1.146, 1.157, 1.171, 1.186, 1.206, 1.227, 1.252, 1.281, 1.314, 1.351, 1.393, 1.442, 1.473,
+ 1.475, 1.446, 1.404, 1.366, 1.329, 1.298, 1.269, 1.245, 1.224, 1.204, 1.188, 1.174, 1.163, 1.154, 1.149, 1.148, 1.148, 1.152, 1.156, 1.164, 1.176, 1.189, 1.206, 1.226, 1.247, 1.274, 1.303, 1.336, 1.374, 1.417, 1.471, 1.505,
+ 1.503, 1.472, 1.428, 1.389, 1.353, 1.321, 1.291, 1.266, 1.245, 1.224, 1.207, 1.192, 1.183, 1.174, 1.169, 1.167, 1.168, 1.169, 1.175, 1.183, 1.195, 1.209, 1.226, 1.247, 1.267, 1.294, 1.325, 1.359, 1.397, 1.445, 1.505, 1.548,
+ 1.534, 1.503, 1.455, 1.413, 1.378, 1.344, 1.315, 1.289, 1.265, 1.243, 1.224, 1.207, 1.196, 1.192, 1.189, 1.189, 1.189, 1.189, 1.192, 1.198, 1.209, 1.226, 1.244, 1.266, 1.291, 1.318, 1.349, 1.383, 1.425, 1.475, 1.548, 1.591
+ ],
+ "sigma": 0.00095,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.97469, -0.71439, -0.26031,
+ -0.43521, 2.09769, -0.66248,
+ -0.04826, -0.84642, 1.89468
+ ]
+ },
+ {
+ "ct": 2960,
+ "ccm":
+ [
+ 2.12952, -0.91185, -0.21768,
+ -0.38018, 1.90789, -0.52771,
+ 0.03988, -1.10079, 2.06092
+ ]
+ },
+ {
+ "ct": 3580,
+ "ccm":
+ [
+ 2.03422, -0.80048, -0.23374,
+ -0.39089, 1.97221, -0.58132,
+ -0.08969, -0.61439, 1.70408
+ ]
+ },
+ {
+ "ct": 4559,
+ "ccm":
+ [
+ 2.15423, -0.98143, -0.17279,
+ -0.38131, 2.14763, -0.76632,
+ -0.10069, -0.54383, 1.64452
+ ]
+ },
+ {
+ "ct": 5881,
+ "ccm":
+ [
+ 2.18464, -0.95493, -0.22971,
+ -0.36826, 2.00298, -0.63471,
+ -0.15219, -0.38055, 1.53274
+ ]
+ },
+ {
+ "ct": 7600,
+ "ccm":
+ [
+ 2.30687, -0.97295, -0.33392,
+ -0.30872, 2.32779, -1.01908,
+ -0.17761, -0.55891, 1.73651
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx477_16mm.json b/src/ipa/rpi/pisp/data/imx477_16mm.json
new file mode 100644
index 00000000..f4e65c92
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx477_16mm.json
@@ -0,0 +1,1240 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 12000,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 740,
+ "reference_Y": 15051
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.809
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 204,
+ "slope": 0.0061
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2850.0, 0.4307, 0.3957,
+ 2960.0, 0.4159, 0.4313,
+ 3580.0, 0.3771, 0.5176,
+ 4559.0, 0.3031, 0.6573,
+ 5881.0, 0.2809, 0.6942,
+ 7600.0, 0.2263, 0.7762
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.02634,
+ "transverse_neg": 0.02255
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.359, 2.354, 2.351, 2.351, 2.343, 2.337, 2.331, 2.325, 2.323, 2.321, 2.317, 2.315, 2.313, 2.313, 2.311, 2.312, 2.312, 2.313, 2.315, 2.315, 2.316, 2.317, 2.319, 2.323, 2.326, 2.329, 2.332, 2.332, 2.335, 2.337, 2.352, 2.363,
+ 2.352, 2.351, 2.349, 2.346, 2.342, 2.334, 2.328, 2.324, 2.321, 2.317, 2.315, 2.314, 2.312, 2.311, 2.311, 2.311, 2.311, 2.311, 2.312, 2.314, 2.315, 2.316, 2.317, 2.319, 2.324, 2.326, 2.328, 2.329, 2.331, 2.337, 2.348, 2.355,
+ 2.346, 2.346, 2.345, 2.344, 2.338, 2.329, 2.325, 2.319, 2.316, 2.314, 2.311, 2.309, 2.308, 2.306, 2.304, 2.304, 2.305, 2.307, 2.308, 2.309, 2.311, 2.311, 2.313, 2.316, 2.319, 2.322, 2.325, 2.326, 2.328, 2.335, 2.343, 2.349,
+ 2.342, 2.342, 2.341, 2.338, 2.332, 2.326, 2.319, 2.316, 2.312, 2.309, 2.308, 2.305, 2.303, 2.302, 2.301, 2.301, 2.302, 2.303, 2.304, 2.305, 2.305, 2.307, 2.311, 2.313, 2.315, 2.319, 2.321, 2.325, 2.328, 2.333, 2.338, 2.348,
+ 2.337, 2.337, 2.337, 2.336, 2.331, 2.322, 2.317, 2.312, 2.309, 2.307, 2.304, 2.302, 2.299, 2.299, 2.298, 2.298, 2.299, 2.299, 2.301, 2.302, 2.302, 2.304, 2.305, 2.309, 2.314, 2.316, 2.321, 2.324, 2.326, 2.329, 2.335, 2.343,
+ 2.335, 2.334, 2.333, 2.333, 2.326, 2.318, 2.313, 2.309, 2.306, 2.302, 2.299, 2.297, 2.297, 2.296, 2.295, 2.295, 2.294, 2.295, 2.296, 2.298, 2.298, 2.301, 2.303, 2.305, 2.311, 2.315, 2.319, 2.323, 2.325, 2.329, 2.333, 2.339,
+ 2.329, 2.331, 2.329, 2.329, 2.325, 2.315, 2.309, 2.306, 2.302, 2.299, 2.297, 2.295, 2.293, 2.292, 2.291, 2.291, 2.291, 2.291, 2.293, 2.294, 2.296, 2.298, 2.301, 2.304, 2.307, 2.313, 2.317, 2.319, 2.323, 2.327, 2.331, 2.339,
+ 2.329, 2.328, 2.328, 2.328, 2.321, 2.313, 2.307, 2.303, 2.299, 2.295, 2.294, 2.292, 2.289, 2.289, 2.288, 2.288, 2.288, 2.289, 2.289, 2.292, 2.294, 2.295, 2.297, 2.301, 2.306, 2.311, 2.315, 2.318, 2.319, 2.323, 2.329, 2.335,
+ 2.326, 2.327, 2.325, 2.325, 2.319, 2.311, 2.305, 2.299, 2.296, 2.293, 2.291, 2.289, 2.288, 2.287, 2.285, 2.285, 2.286, 2.288, 2.288, 2.289, 2.291, 2.294, 2.295, 2.298, 2.304, 2.308, 2.313, 2.315, 2.317, 2.319, 2.327, 2.335,
+ 2.325, 2.325, 2.323, 2.323, 2.317, 2.309, 2.303, 2.298, 2.294, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.284, 2.284, 2.285, 2.287, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.305, 2.309, 2.313, 2.315, 2.317, 2.325, 2.334,
+ 2.322, 2.324, 2.322, 2.322, 2.316, 2.306, 2.301, 2.296, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.283, 2.283, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.293, 2.296, 2.301, 2.304, 2.308, 2.311, 2.312, 2.315, 2.323, 2.333,
+ 2.321, 2.323, 2.322, 2.322, 2.314, 2.306, 2.299, 2.294, 2.291, 2.288, 2.286, 2.285, 2.284, 2.282, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.306, 2.308, 2.311, 2.312, 2.322, 2.332,
+ 2.319, 2.321, 2.321, 2.321, 2.314, 2.305, 2.297, 2.293, 2.289, 2.287, 2.285, 2.284, 2.283, 2.281, 2.281, 2.281, 2.282, 2.283, 2.283, 2.285, 2.287, 2.289, 2.291, 2.292, 2.297, 2.301, 2.305, 2.307, 2.309, 2.312, 2.321, 2.333,
+ 2.319, 2.321, 2.319, 2.319, 2.314, 2.303, 2.296, 2.293, 2.289, 2.286, 2.285, 2.283, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.296, 2.301, 2.305, 2.307, 2.308, 2.312, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.296, 2.291, 2.289, 2.286, 2.284, 2.282, 2.281, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.287, 2.288, 2.291, 2.295, 2.299, 2.304, 2.306, 2.307, 2.311, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.287, 2.285, 2.282, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.284, 2.285, 2.286, 2.288, 2.291, 2.295, 2.299, 2.303, 2.306, 2.307, 2.312, 2.321, 2.331,
+ 2.318, 2.319, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.286, 2.285, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.285, 2.286, 2.287, 2.291, 2.294, 2.298, 2.303, 2.306, 2.307, 2.311, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.302, 2.297, 2.292, 2.289, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.287, 2.289, 2.294, 2.297, 2.303, 2.305, 2.308, 2.313, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.303, 2.299, 2.293, 2.291, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.288, 2.291, 2.294, 2.298, 2.304, 2.306, 2.308, 2.312, 2.322, 2.331,
+ 2.319, 2.321, 2.321, 2.321, 2.315, 2.305, 2.301, 2.295, 2.292, 2.289, 2.286, 2.285, 2.283, 2.282, 2.282, 2.282, 2.284, 2.283, 2.284, 2.284, 2.285, 2.287, 2.288, 2.291, 2.294, 2.299, 2.304, 2.306, 2.309, 2.313, 2.322, 2.334,
+ 2.321, 2.322, 2.322, 2.322, 2.317, 2.307, 2.301, 2.296, 2.292, 2.291, 2.288, 2.286, 2.285, 2.284, 2.283, 2.284, 2.285, 2.284, 2.285, 2.285, 2.287, 2.288, 2.289, 2.293, 2.297, 2.301, 2.305, 2.308, 2.311, 2.314, 2.323, 2.335,
+ 2.322, 2.324, 2.324, 2.324, 2.319, 2.309, 2.303, 2.297, 2.295, 2.292, 2.291, 2.288, 2.286, 2.286, 2.285, 2.286, 2.286, 2.286, 2.287, 2.288, 2.289, 2.289, 2.291, 2.294, 2.299, 2.302, 2.307, 2.311, 2.312, 2.316, 2.325, 2.335,
+ 2.324, 2.326, 2.325, 2.326, 2.321, 2.311, 2.305, 2.301, 2.297, 2.295, 2.293, 2.291, 2.289, 2.289, 2.288, 2.288, 2.287, 2.288, 2.289, 2.291, 2.292, 2.292, 2.295, 2.299, 2.301, 2.304, 2.309, 2.312, 2.315, 2.319, 2.327, 2.337,
+ 2.329, 2.329, 2.328, 2.328, 2.323, 2.315, 2.308, 2.304, 2.301, 2.298, 2.296, 2.294, 2.291, 2.291, 2.289, 2.291, 2.291, 2.291, 2.292, 2.293, 2.294, 2.295, 2.297, 2.299, 2.303, 2.308, 2.312, 2.315, 2.318, 2.321, 2.329, 2.339,
+ 2.329, 2.331, 2.332, 2.332, 2.326, 2.318, 2.311, 2.306, 2.304, 2.301, 2.299, 2.297, 2.295, 2.293, 2.292, 2.292, 2.292, 2.293, 2.294, 2.294, 2.296, 2.297, 2.299, 2.302, 2.306, 2.311, 2.315, 2.318, 2.319, 2.324, 2.332, 2.342,
+ 2.331, 2.333, 2.334, 2.334, 2.328, 2.321, 2.313, 2.308, 2.305, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.295, 2.294, 2.296, 2.296, 2.297, 2.298, 2.299, 2.302, 2.305, 2.308, 2.314, 2.317, 2.321, 2.323, 2.327, 2.334, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.309, 2.306, 2.304, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.309, 2.315, 2.319, 2.321, 2.324, 2.328, 2.337, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.311, 2.306, 2.304, 2.303, 2.302, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.311, 2.314, 2.319, 2.323, 2.325, 2.329, 2.339, 2.348,
+ 2.329, 2.329, 2.329, 2.331, 2.326, 2.319, 2.312, 2.309, 2.304, 2.303, 2.302, 2.301, 2.298, 2.295, 2.294, 2.294, 2.295, 2.295, 2.296, 2.297, 2.299, 2.301, 2.302, 2.304, 2.308, 2.313, 2.319, 2.322, 2.325, 2.329, 2.339, 2.351,
+ 2.329, 2.329, 2.329, 2.329, 2.326, 2.317, 2.311, 2.308, 2.303, 2.302, 2.301, 2.298, 2.296, 2.295, 2.294, 2.294, 2.294, 2.294, 2.296, 2.297, 2.298, 2.299, 2.301, 2.304, 2.307, 2.312, 2.318, 2.322, 2.326, 2.331, 2.341, 2.355,
+ 2.339, 2.332, 2.331, 2.331, 2.327, 2.323, 2.316, 2.309, 2.306, 2.302, 2.301, 2.299, 2.297, 2.296, 2.295, 2.294, 2.294, 2.296, 2.297, 2.297, 2.299, 2.301, 2.303, 2.306, 2.308, 2.317, 2.322, 2.325, 2.329, 2.341, 2.353, 2.361,
+ 2.347, 2.347, 2.345, 2.343, 2.338, 2.332, 2.326, 2.322, 2.321, 2.318, 2.316, 2.315, 2.313, 2.312, 2.311, 2.311, 2.311, 2.311, 2.312, 2.315, 2.317, 2.318, 2.319, 2.323, 2.324, 2.329, 2.334, 2.337, 2.344, 2.347, 2.361, 2.364
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.869, 3.852, 3.844, 3.842, 3.836, 3.821, 3.807, 3.796, 3.789, 3.784, 3.778, 3.775, 3.769, 3.768, 3.765, 3.765, 3.767, 3.769, 3.772, 3.774, 3.773, 3.775, 3.779, 3.787, 3.793, 3.801, 3.806, 3.804, 3.813, 3.819, 3.855, 3.879,
+ 3.854, 3.844, 3.837, 3.836, 3.824, 3.811, 3.797, 3.789, 3.784, 3.777, 3.774, 3.769, 3.764, 3.758, 3.757, 3.758, 3.758, 3.761, 3.763, 3.764, 3.765, 3.766, 3.772, 3.778, 3.787, 3.792, 3.794, 3.798, 3.802, 3.815, 3.839, 3.873,
+ 3.838, 3.831, 3.826, 3.823, 3.813, 3.799, 3.787, 3.781, 3.773, 3.768, 3.763, 3.759, 3.753, 3.749, 3.745, 3.745, 3.745, 3.752, 3.754, 3.757, 3.757, 3.759, 3.763, 3.769, 3.773, 3.781, 3.786, 3.792, 3.798, 3.811, 3.831, 3.861,
+ 3.833, 3.822, 3.817, 3.816, 3.804, 3.788, 3.779, 3.772, 3.766, 3.759, 3.755, 3.749, 3.744, 3.741, 3.738, 3.739, 3.739, 3.741, 3.743, 3.747, 3.749, 3.751, 3.756, 3.764, 3.769, 3.776, 3.783, 3.789, 3.798, 3.809, 3.821, 3.855,
+ 3.824, 3.818, 3.808, 3.808, 3.797, 3.781, 3.772, 3.764, 3.757, 3.752, 3.747, 3.743, 3.737, 3.735, 3.733, 3.733, 3.733, 3.735, 3.737, 3.738, 3.741, 3.746, 3.749, 3.755, 3.766, 3.771, 3.781, 3.789, 3.794, 3.806, 3.818, 3.849,
+ 3.815, 3.808, 3.799, 3.801, 3.787, 3.775, 3.767, 3.757, 3.751, 3.745, 3.738, 3.734, 3.732, 3.727, 3.725, 3.723, 3.722, 3.722, 3.726, 3.729, 3.734, 3.738, 3.744, 3.749, 3.759, 3.769, 3.781, 3.788, 3.792, 3.799, 3.811, 3.841,
+ 3.804, 3.799, 3.793, 3.793, 3.783, 3.771, 3.759, 3.751, 3.744, 3.735, 3.732, 3.727, 3.723, 3.721, 3.719, 3.716, 3.716, 3.716, 3.718, 3.722, 3.727, 3.731, 3.737, 3.746, 3.756, 3.767, 3.776, 3.782, 3.788, 3.795, 3.808, 3.831,
+ 3.802, 3.797, 3.787, 3.787, 3.779, 3.762, 3.753, 3.744, 3.734, 3.727, 3.725, 3.721, 3.716, 3.714, 3.709, 3.709, 3.711, 3.711, 3.712, 3.717, 3.722, 3.725, 3.731, 3.739, 3.752, 3.762, 3.772, 3.778, 3.779, 3.789, 3.798, 3.826,
+ 3.791, 3.789, 3.784, 3.784, 3.775, 3.759, 3.746, 3.735, 3.729, 3.724, 3.718, 3.714, 3.712, 3.707, 3.704, 3.704, 3.706, 3.708, 3.709, 3.711, 3.716, 3.722, 3.726, 3.735, 3.746, 3.754, 3.767, 3.774, 3.777, 3.781, 3.794, 3.824,
+ 3.789, 3.784, 3.779, 3.781, 3.771, 3.753, 3.741, 3.732, 3.725, 3.719, 3.715, 3.711, 3.707, 3.704, 3.701, 3.701, 3.702, 3.704, 3.708, 3.709, 3.713, 3.718, 3.724, 3.731, 3.742, 3.749, 3.761, 3.768, 3.772, 3.778, 3.791, 3.822,
+ 3.789, 3.781, 3.777, 3.777, 3.764, 3.749, 3.739, 3.729, 3.722, 3.718, 3.711, 3.708, 3.705, 3.701, 3.699, 3.699, 3.699, 3.701, 3.705, 3.707, 3.711, 3.715, 3.721, 3.727, 3.738, 3.746, 3.757, 3.763, 3.765, 3.773, 3.788, 3.821,
+ 3.785, 3.779, 3.774, 3.774, 3.764, 3.747, 3.736, 3.726, 3.719, 3.711, 3.709, 3.706, 3.701, 3.698, 3.696, 3.695, 3.695, 3.698, 3.702, 3.704, 3.707, 3.712, 3.718, 3.725, 3.734, 3.741, 3.753, 3.756, 3.759, 3.764, 3.784, 3.818,
+ 3.779, 3.776, 3.773, 3.773, 3.759, 3.744, 3.733, 3.724, 3.714, 3.709, 3.706, 3.704, 3.699, 3.696, 3.694, 3.694, 3.694, 3.697, 3.701, 3.703, 3.706, 3.709, 3.714, 3.721, 3.731, 3.737, 3.749, 3.753, 3.758, 3.762, 3.783, 3.819,
+ 3.779, 3.776, 3.769, 3.769, 3.757, 3.741, 3.729, 3.721, 3.712, 3.708, 3.705, 3.701, 3.697, 3.695, 3.694, 3.694, 3.695, 3.696, 3.698, 3.702, 3.705, 3.709, 3.712, 3.717, 3.728, 3.736, 3.749, 3.752, 3.756, 3.761, 3.781, 3.815,
+ 3.779, 3.773, 3.768, 3.768, 3.756, 3.738, 3.731, 3.719, 3.711, 3.707, 3.703, 3.698, 3.695, 3.694, 3.694, 3.695, 3.695, 3.695, 3.696, 3.702, 3.705, 3.708, 3.712, 3.717, 3.728, 3.736, 3.747, 3.751, 3.754, 3.761, 3.781, 3.815,
+ 3.782, 3.773, 3.767, 3.767, 3.755, 3.738, 3.728, 3.721, 3.711, 3.707, 3.701, 3.698, 3.695, 3.693, 3.694, 3.696, 3.695, 3.695, 3.695, 3.701, 3.703, 3.706, 3.711, 3.715, 3.726, 3.735, 3.745, 3.751, 3.754, 3.763, 3.779, 3.815,
+ 3.781, 3.771, 3.767, 3.767, 3.754, 3.739, 3.726, 3.721, 3.712, 3.706, 3.701, 3.698, 3.695, 3.693, 3.693, 3.695, 3.695, 3.695, 3.696, 3.698, 3.703, 3.705, 3.709, 3.715, 3.725, 3.734, 3.745, 3.751, 3.755, 3.762, 3.783, 3.818,
+ 3.781, 3.774, 3.767, 3.767, 3.755, 3.741, 3.729, 3.722, 3.712, 3.708, 3.701, 3.699, 3.695, 3.693, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.725, 3.732, 3.746, 3.751, 3.756, 3.763, 3.783, 3.821,
+ 3.781, 3.774, 3.769, 3.769, 3.756, 3.741, 3.731, 3.724, 3.713, 3.711, 3.707, 3.699, 3.697, 3.694, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.724, 3.734, 3.747, 3.751, 3.756, 3.765, 3.784, 3.821,
+ 3.784, 3.776, 3.773, 3.773, 3.759, 3.742, 3.733, 3.726, 3.719, 3.711, 3.709, 3.703, 3.698, 3.695, 3.694, 3.695, 3.697, 3.696, 3.698, 3.699, 3.703, 3.706, 3.711, 3.714, 3.727, 3.735, 3.746, 3.751, 3.757, 3.766, 3.787, 3.822,
+ 3.786, 3.783, 3.774, 3.774, 3.766, 3.747, 3.737, 3.727, 3.722, 3.716, 3.711, 3.706, 3.702, 3.698, 3.697, 3.698, 3.699, 3.699, 3.701, 3.703, 3.706, 3.711, 3.713, 3.719, 3.731, 3.739, 3.748, 3.753, 3.761, 3.769, 3.789, 3.826,
+ 3.786, 3.784, 3.779, 3.779, 3.769, 3.751, 3.742, 3.732, 3.725, 3.719, 3.715, 3.711, 3.706, 3.704, 3.701, 3.701, 3.702, 3.702, 3.705, 3.707, 3.712, 3.714, 3.717, 3.724, 3.733, 3.743, 3.749, 3.758, 3.764, 3.769, 3.791, 3.826,
+ 3.793, 3.787, 3.782, 3.782, 3.774, 3.756, 3.747, 3.737, 3.729, 3.725, 3.719, 3.715, 3.712, 3.708, 3.707, 3.706, 3.707, 3.708, 3.709, 3.713, 3.714, 3.717, 3.723, 3.729, 3.736, 3.747, 3.757, 3.764, 3.768, 3.774, 3.794, 3.829,
+ 3.794, 3.791, 3.786, 3.786, 3.779, 3.762, 3.751, 3.742, 3.735, 3.729, 3.725, 3.719, 3.716, 3.711, 3.709, 3.709, 3.709, 3.711, 3.716, 3.717, 3.721, 3.723, 3.726, 3.732, 3.741, 3.752, 3.761, 3.767, 3.773, 3.779, 3.801, 3.829,
+ 3.802, 3.798, 3.793, 3.793, 3.779, 3.766, 3.754, 3.746, 3.741, 3.736, 3.731, 3.726, 3.719, 3.717, 3.716, 3.715, 3.716, 3.717, 3.719, 3.721, 3.724, 3.726, 3.731, 3.737, 3.744, 3.756, 3.766, 3.772, 3.776, 3.784, 3.807, 3.839,
+ 3.805, 3.799, 3.795, 3.795, 3.784, 3.767, 3.757, 3.749, 3.744, 3.739, 3.736, 3.731, 3.726, 3.722, 3.719, 3.719, 3.719, 3.721, 3.723, 3.725, 3.727, 3.732, 3.738, 3.742, 3.751, 3.761, 3.771, 3.775, 3.782, 3.789, 3.811, 3.841,
+ 3.804, 3.801, 3.799, 3.799, 3.787, 3.772, 3.761, 3.752, 3.746, 3.742, 3.739, 3.735, 3.729, 3.726, 3.723, 3.724, 3.725, 3.726, 3.727, 3.728, 3.732, 3.736, 3.739, 3.745, 3.754, 3.765, 3.775, 3.779, 3.785, 3.795, 3.816, 3.844,
+ 3.801, 3.799, 3.796, 3.796, 3.787, 3.773, 3.761, 3.753, 3.746, 3.743, 3.739, 3.735, 3.731, 3.726, 3.725, 3.725, 3.725, 3.726, 3.727, 3.729, 3.733, 3.736, 3.741, 3.745, 3.755, 3.766, 3.776, 3.783, 3.786, 3.797, 3.819, 3.851,
+ 3.799, 3.795, 3.788, 3.788, 3.783, 3.772, 3.759, 3.749, 3.744, 3.738, 3.735, 3.733, 3.726, 3.724, 3.722, 3.722, 3.723, 3.724, 3.725, 3.727, 3.729, 3.733, 3.736, 3.742, 3.754, 3.762, 3.772, 3.779, 3.784, 3.796, 3.821, 3.859,
+ 3.799, 3.789, 3.787, 3.788, 3.779, 3.766, 3.755, 3.749, 3.742, 3.736, 3.733, 3.727, 3.723, 3.722, 3.721, 3.719, 3.719, 3.721, 3.725, 3.726, 3.728, 3.732, 3.734, 3.741, 3.747, 3.758, 3.771, 3.778, 3.785, 3.796, 3.825, 3.862,
+ 3.824, 3.799, 3.789, 3.789, 3.788, 3.777, 3.761, 3.751, 3.743, 3.739, 3.736, 3.728, 3.726, 3.725, 3.721, 3.719, 3.721, 3.723, 3.727, 3.728, 3.729, 3.733, 3.737, 3.744, 3.755, 3.769, 3.776, 3.784, 3.793, 3.819, 3.863, 3.877,
+ 3.833, 3.833, 3.833, 3.842, 3.825, 3.815, 3.807, 3.799, 3.792, 3.788, 3.785, 3.782, 3.778, 3.777, 3.773, 3.772, 3.772, 3.774, 3.778, 3.779, 3.779, 3.785, 3.792, 3.798, 3.803, 3.811, 3.822, 3.834, 3.843, 3.846, 3.877, 3.886
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.616, 2.616, 2.618, 2.621, 2.619, 2.618, 2.615, 2.615, 2.613, 2.611, 2.609, 2.609, 2.609, 2.611, 2.611, 2.611, 2.611, 2.609, 2.608, 2.608, 2.611, 2.613, 2.613, 2.614, 2.614, 2.615, 2.615, 2.622, 2.624, 2.621, 2.624, 2.641,
+ 2.616, 2.618, 2.621, 2.623, 2.623, 2.619, 2.618, 2.616, 2.616, 2.613, 2.611, 2.611, 2.611, 2.611, 2.612, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.612, 2.613, 2.612, 2.613, 2.615, 2.617, 2.621, 2.621, 2.619, 2.621, 2.641,
+ 2.621, 2.624, 2.627, 2.627, 2.625, 2.623, 2.621, 2.619, 2.618, 2.618, 2.618, 2.617, 2.616, 2.616, 2.615, 2.613, 2.612, 2.613, 2.613, 2.614, 2.614, 2.613, 2.614, 2.613, 2.614, 2.617, 2.619, 2.621, 2.621, 2.619, 2.623, 2.643,
+ 2.626, 2.627, 2.628, 2.629, 2.628, 2.625, 2.622, 2.621, 2.621, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.616, 2.616, 2.616, 2.618, 2.618, 2.617, 2.617, 2.618, 2.619, 2.621, 2.623, 2.624, 2.626, 2.625, 2.624, 2.625, 2.654,
+ 2.627, 2.628, 2.628, 2.628, 2.626, 2.623, 2.622, 2.622, 2.622, 2.622, 2.621, 2.621, 2.619, 2.617, 2.617, 2.616, 2.617, 2.617, 2.618, 2.619, 2.618, 2.618, 2.618, 2.621, 2.622, 2.624, 2.626, 2.627, 2.627, 2.626, 2.628, 2.655,
+ 2.625, 2.626, 2.627, 2.626, 2.625, 2.623, 2.622, 2.621, 2.622, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.616, 2.616, 2.616, 2.616, 2.616, 2.617, 2.618, 2.619, 2.621, 2.622, 2.624, 2.626, 2.628, 2.628, 2.629, 2.629, 2.655,
+ 2.626, 2.625, 2.626, 2.625, 2.625, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.616, 2.614, 2.613, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.618, 2.619, 2.621, 2.623, 2.624, 2.627, 2.629, 2.631, 2.629, 2.631, 2.651,
+ 2.625, 2.625, 2.625, 2.624, 2.623, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.614, 2.613, 2.612, 2.611, 2.611, 2.612, 2.612, 2.613, 2.616, 2.618, 2.619, 2.622, 2.624, 2.626, 2.628, 2.631, 2.631, 2.631, 2.631, 2.651,
+ 2.625, 2.625, 2.624, 2.623, 2.622, 2.622, 2.622, 2.622, 2.622, 2.621, 2.617, 2.615, 2.613, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.613, 2.615, 2.618, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.624, 2.624, 2.622, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.618, 2.616, 2.614, 2.612, 2.611, 2.609, 2.609, 2.608, 2.609, 2.611, 2.611, 2.615, 2.617, 2.619, 2.621, 2.625, 2.628, 2.631, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.622, 2.623, 2.622, 2.622, 2.621, 2.619, 2.619, 2.619, 2.618, 2.616, 2.614, 2.613, 2.611, 2.609, 2.608, 2.606, 2.607, 2.607, 2.609, 2.611, 2.615, 2.617, 2.619, 2.622, 2.626, 2.629, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.622, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.614, 2.613, 2.611, 2.611, 2.607, 2.606, 2.605, 2.604, 2.605, 2.607, 2.609, 2.613, 2.616, 2.619, 2.622, 2.627, 2.631, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.613, 2.609, 2.607, 2.604, 2.602, 2.601, 2.602, 2.603, 2.605, 2.609, 2.612, 2.616, 2.619, 2.624, 2.628, 2.631, 2.632, 2.633, 2.629, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.615, 2.614, 2.612, 2.608, 2.603, 2.601, 2.598, 2.597, 2.599, 2.602, 2.605, 2.608, 2.611, 2.615, 2.622, 2.625, 2.629, 2.631, 2.631, 2.633, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.616, 2.614, 2.611, 2.606, 2.601, 2.598, 2.595, 2.595, 2.597, 2.601, 2.604, 2.608, 2.612, 2.615, 2.623, 2.627, 2.629, 2.631, 2.631, 2.632, 2.631, 2.628, 2.628, 2.651,
+ 2.622, 2.623, 2.624, 2.624, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.609, 2.606, 2.601, 2.596, 2.594, 2.594, 2.596, 2.599, 2.603, 2.609, 2.613, 2.617, 2.623, 2.627, 2.629, 2.631, 2.632, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.623, 2.625, 2.625, 2.624, 2.621, 2.621, 2.619, 2.617, 2.616, 2.613, 2.608, 2.605, 2.601, 2.595, 2.593, 2.593, 2.595, 2.598, 2.604, 2.609, 2.615, 2.619, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.632, 2.629, 2.631, 2.651,
+ 2.624, 2.626, 2.626, 2.623, 2.621, 2.619, 2.618, 2.617, 2.615, 2.612, 2.608, 2.605, 2.601, 2.597, 2.595, 2.595, 2.596, 2.598, 2.605, 2.609, 2.616, 2.621, 2.626, 2.627, 2.629, 2.631, 2.633, 2.633, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.618, 2.617, 2.614, 2.612, 2.609, 2.606, 2.602, 2.599, 2.598, 2.597, 2.598, 2.602, 2.607, 2.612, 2.619, 2.621, 2.626, 2.628, 2.629, 2.632, 2.633, 2.634, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.621, 2.618, 2.617, 2.614, 2.612, 2.611, 2.608, 2.604, 2.602, 2.599, 2.599, 2.603, 2.606, 2.611, 2.616, 2.621, 2.624, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.634, 2.633, 2.633, 2.656,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.611, 2.611, 2.607, 2.604, 2.604, 2.604, 2.606, 2.609, 2.613, 2.619, 2.622, 2.625, 2.628, 2.631, 2.632, 2.633, 2.633, 2.636, 2.636, 2.634, 2.634, 2.658,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.619, 2.618, 2.616, 2.614, 2.613, 2.612, 2.611, 2.609, 2.608, 2.607, 2.608, 2.609, 2.613, 2.617, 2.621, 2.623, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.635, 2.636, 2.636, 2.636, 2.661,
+ 2.623, 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.616, 2.615, 2.614, 2.613, 2.612, 2.612, 2.611, 2.611, 2.611, 2.614, 2.615, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.633, 2.635, 2.635, 2.637, 2.637, 2.636, 2.637, 2.661,
+ 2.623, 2.624, 2.625, 2.626, 2.624, 2.621, 2.619, 2.617, 2.616, 2.615, 2.615, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.619, 2.621, 2.623, 2.626, 2.628, 2.631, 2.632, 2.634, 2.635, 2.636, 2.637, 2.638, 2.637, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.627, 2.626, 2.623, 2.619, 2.619, 2.618, 2.618, 2.618, 2.617, 2.617, 2.616, 2.616, 2.616, 2.619, 2.622, 2.623, 2.625, 2.628, 2.628, 2.631, 2.632, 2.634, 2.636, 2.638, 2.639, 2.639, 2.638, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.628, 2.626, 2.623, 2.621, 2.619, 2.619, 2.619, 2.619, 2.619, 2.619, 2.618, 2.618, 2.619, 2.623, 2.624, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.635, 2.638, 2.639, 2.639, 2.639, 2.636, 2.636, 2.662,
+ 2.625, 2.627, 2.628, 2.628, 2.626, 2.624, 2.623, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.624, 2.624, 2.625, 2.627, 2.628, 2.631, 2.631, 2.632, 2.634, 2.636, 2.639, 2.639, 2.641, 2.639, 2.635, 2.635, 2.663,
+ 2.625, 2.626, 2.628, 2.628, 2.627, 2.625, 2.624, 2.623, 2.623, 2.622, 2.623, 2.624, 2.624, 2.625, 2.625, 2.625, 2.625, 2.626, 2.627, 2.629, 2.631, 2.632, 2.633, 2.635, 2.638, 2.641, 2.642, 2.643, 2.642, 2.636, 2.636, 2.665,
+ 2.624, 2.626, 2.628, 2.628, 2.628, 2.626, 2.624, 2.624, 2.623, 2.623, 2.623, 2.625, 2.627, 2.627, 2.626, 2.626, 2.626, 2.627, 2.628, 2.629, 2.632, 2.633, 2.635, 2.637, 2.639, 2.642, 2.644, 2.644, 2.642, 2.638, 2.638, 2.665,
+ 2.623, 2.625, 2.626, 2.627, 2.626, 2.626, 2.624, 2.623, 2.623, 2.623, 2.623, 2.623, 2.626, 2.627, 2.626, 2.626, 2.626, 2.626, 2.628, 2.628, 2.629, 2.631, 2.634, 2.636, 2.639, 2.642, 2.644, 2.643, 2.641, 2.637, 2.638, 2.659,
+ 2.623, 2.627, 2.627, 2.627, 2.627, 2.628, 2.627, 2.624, 2.624, 2.623, 2.624, 2.624, 2.628, 2.628, 2.627, 2.628, 2.628, 2.628, 2.629, 2.629, 2.631, 2.635, 2.637, 2.639, 2.641, 2.643, 2.646, 2.645, 2.643, 2.641, 2.654, 2.659,
+ 2.642, 2.641, 2.643, 2.645, 2.645, 2.644, 2.644, 2.643, 2.643, 2.642, 2.642, 2.642, 2.643, 2.644, 2.644, 2.644, 2.646, 2.646, 2.647, 2.649, 2.651, 2.652, 2.654, 2.656, 2.658, 2.661, 2.661, 2.661, 2.659, 2.654, 2.659, 2.659
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.391, 1.394, 1.395, 1.396, 1.398, 1.398, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.398, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.399, 1.397, 1.399, 1.402,
+ 1.393, 1.395, 1.396, 1.398, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.401, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.399, 1.401, 1.401, 1.399, 1.398, 1.399, 1.402,
+ 1.398, 1.401, 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.401, 1.401, 1.399, 1.399, 1.401, 1.401, 1.401, 1.401, 1.399, 1.401, 1.406,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.402, 1.403, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.405, 1.404, 1.404, 1.404, 1.404, 1.405, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.406, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.408, 1.408, 1.407, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.403, 1.402, 1.402, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.408, 1.407, 1.405, 1.405, 1.414,
+ 1.402, 1.402, 1.402, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.403, 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.407, 1.409, 1.409, 1.409, 1.409, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.403, 1.404, 1.405, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.408, 1.405, 1.405, 1.413,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.402, 1.401, 1.399, 1.398, 1.398, 1.399, 1.399, 1.401, 1.403, 1.404, 1.405, 1.407, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.403, 1.404, 1.405, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.399, 1.401, 1.403, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.398, 1.401, 1.403, 1.406, 1.407, 1.409, 1.409, 1.411, 1.409, 1.409, 1.409, 1.408, 1.407, 1.407, 1.413,
+ 1.403, 1.404, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.404, 1.404, 1.403, 1.402, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.402, 1.404, 1.406, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.414,
+ 1.403, 1.403, 1.404, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.402, 1.401, 1.399, 1.398, 1.398, 1.398, 1.401, 1.403, 1.404, 1.408, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.402, 1.404, 1.406, 1.407, 1.408, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.409, 1.408, 1.408, 1.415,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.406, 1.408, 1.408, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.405, 1.406, 1.407, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.407, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.416,
+ 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.406, 1.407, 1.407, 1.407, 1.408, 1.409, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.417,
+ 1.402, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.415,
+ 1.402, 1.402, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.405, 1.406, 1.406, 1.406, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.412, 1.412, 1.413, 1.413, 1.411, 1.408, 1.411, 1.413,
+ 1.406, 1.406, 1.408, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.414, 1.414, 1.414, 1.414, 1.415, 1.415, 1.415, 1.415, 1.416, 1.416, 1.416, 1.417, 1.418, 1.418, 1.417, 1.417, 1.414, 1.411, 1.413, 1.413
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.554, 1.522, 1.466, 1.422, 1.385, 1.351, 1.322, 1.294, 1.269, 1.246, 1.228, 1.214, 1.207, 1.202, 1.199, 1.199, 1.199, 1.199, 1.202, 1.207, 1.218, 1.235, 1.255, 1.279, 1.305, 1.333, 1.365, 1.402, 1.447, 1.508, 1.602, 1.638,
+ 1.522, 1.478, 1.431, 1.391, 1.355, 1.323, 1.298, 1.271, 1.247, 1.228, 1.212, 1.199, 1.187, 1.179, 1.173, 1.172, 1.172, 1.174, 1.179, 1.189, 1.201, 1.216, 1.235, 1.256, 1.282, 1.308, 1.335, 1.368, 1.411, 1.461, 1.535, 1.602,
+ 1.479, 1.449, 1.407, 1.367, 1.332, 1.301, 1.271, 1.247, 1.226, 1.208, 1.191, 1.178, 1.166, 1.158, 1.153, 1.151, 1.151, 1.153, 1.159, 1.168, 1.179, 1.194, 1.212, 1.234, 1.256, 1.282, 1.311, 1.343, 1.382, 1.427, 1.489, 1.535,
+ 1.454, 1.423, 1.383, 1.345, 1.309, 1.278, 1.249, 1.226, 1.206, 1.187, 1.171, 1.158, 1.146, 1.138, 1.132, 1.129, 1.129, 1.133, 1.139, 1.147, 1.159, 1.173, 1.191, 1.212, 1.234, 1.261, 1.288, 1.321, 1.357, 1.401, 1.455, 1.489,
+ 1.433, 1.401, 1.362, 1.325, 1.289, 1.258, 1.231, 1.206, 1.187, 1.169, 1.153, 1.138, 1.129, 1.121, 1.115, 1.112, 1.112, 1.114, 1.121, 1.129, 1.141, 1.155, 1.172, 1.191, 1.214, 1.241, 1.269, 1.301, 1.337, 1.377, 1.428, 1.457,
+ 1.415, 1.382, 1.343, 1.306, 1.273, 1.241, 1.213, 1.189, 1.169, 1.153, 1.137, 1.123, 1.112, 1.105, 1.097, 1.095, 1.095, 1.098, 1.103, 1.112, 1.124, 1.139, 1.155, 1.173, 1.197, 1.222, 1.252, 1.282, 1.317, 1.356, 1.405, 1.434,
+ 1.398, 1.363, 1.325, 1.289, 1.256, 1.224, 1.198, 1.175, 1.155, 1.137, 1.123, 1.108, 1.097, 1.089, 1.083, 1.079, 1.079, 1.083, 1.088, 1.097, 1.109, 1.124, 1.139, 1.158, 1.181, 1.206, 1.234, 1.266, 1.299, 1.339, 1.384, 1.415,
+ 1.382, 1.347, 1.309, 1.274, 1.242, 1.211, 1.185, 1.162, 1.142, 1.124, 1.108, 1.095, 1.083, 1.075, 1.069, 1.066, 1.066, 1.068, 1.074, 1.083, 1.096, 1.109, 1.125, 1.145, 1.166, 1.191, 1.219, 1.251, 1.285, 1.324, 1.367, 1.399,
+ 1.369, 1.334, 1.296, 1.261, 1.228, 1.199, 1.173, 1.151, 1.131, 1.112, 1.095, 1.083, 1.071, 1.062, 1.056, 1.053, 1.053, 1.055, 1.061, 1.069, 1.083, 1.096, 1.112, 1.132, 1.153, 1.178, 1.206, 1.237, 1.271, 1.309, 1.353, 1.385,
+ 1.359, 1.321, 1.284, 1.251, 1.217, 1.189, 1.164, 1.141, 1.121, 1.102, 1.086, 1.071, 1.061, 1.049, 1.045, 1.042, 1.042, 1.043, 1.051, 1.061, 1.069, 1.085, 1.101, 1.121, 1.143, 1.167, 1.195, 1.225, 1.259, 1.298, 1.341, 1.375,
+ 1.351, 1.312, 1.275, 1.241, 1.209, 1.181, 1.155, 1.133, 1.112, 1.092, 1.076, 1.061, 1.049, 1.041, 1.034, 1.032, 1.032, 1.035, 1.041, 1.051, 1.061, 1.075, 1.092, 1.112, 1.133, 1.158, 1.185, 1.216, 1.249, 1.288, 1.331, 1.364,
+ 1.344, 1.303, 1.267, 1.233, 1.201, 1.173, 1.147, 1.124, 1.104, 1.085, 1.067, 1.053, 1.041, 1.033, 1.024, 1.022, 1.022, 1.025, 1.034, 1.041, 1.053, 1.066, 1.083, 1.103, 1.126, 1.149, 1.177, 1.207, 1.241, 1.279, 1.321, 1.357,
+ 1.339, 1.297, 1.261, 1.226, 1.194, 1.166, 1.142, 1.119, 1.098, 1.078, 1.061, 1.046, 1.034, 1.024, 1.017, 1.014, 1.014, 1.017, 1.025, 1.034, 1.046, 1.059, 1.077, 1.096, 1.118, 1.143, 1.169, 1.201, 1.235, 1.273, 1.314, 1.352,
+ 1.337, 1.293, 1.256, 1.223, 1.191, 1.163, 1.136, 1.114, 1.093, 1.074, 1.056, 1.041, 1.027, 1.017, 1.012, 1.006, 1.006, 1.013, 1.017, 1.028, 1.041, 1.055, 1.072, 1.092, 1.114, 1.138, 1.165, 1.195, 1.229, 1.268, 1.309, 1.348,
+ 1.337, 1.291, 1.253, 1.219, 1.187, 1.159, 1.133, 1.109, 1.089, 1.071, 1.053, 1.037, 1.023, 1.012, 1.006, 1.002, 1.003, 1.006, 1.013, 1.023, 1.038, 1.052, 1.069, 1.089, 1.111, 1.135, 1.161, 1.192, 1.226, 1.264, 1.306, 1.348,
+ 1.337, 1.291, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.035, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.341, 1.292, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.034, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.348, 1.298, 1.255, 1.219, 1.188, 1.159, 1.134, 1.111, 1.088, 1.069, 1.051, 1.035, 1.021, 1.009, 1.003, 1.001, 1.002, 1.004, 1.011, 1.022, 1.036, 1.053, 1.071, 1.089, 1.111, 1.135, 1.162, 1.191, 1.226, 1.264, 1.306, 1.347,
+ 1.354, 1.306, 1.258, 1.222, 1.191, 1.162, 1.135, 1.113, 1.092, 1.073, 1.054, 1.038, 1.024, 1.014, 1.008, 1.003, 1.004, 1.008, 1.014, 1.026, 1.039, 1.056, 1.073, 1.093, 1.115, 1.139, 1.165, 1.195, 1.229, 1.267, 1.309, 1.349,
+ 1.358, 1.312, 1.263, 1.227, 1.195, 1.167, 1.141, 1.117, 1.097, 1.078, 1.061, 1.043, 1.029, 1.021, 1.014, 1.008, 1.008, 1.014, 1.021, 1.032, 1.045, 1.061, 1.078, 1.097, 1.119, 1.144, 1.169, 1.201, 1.234, 1.272, 1.315, 1.353,
+ 1.364, 1.319, 1.269, 1.234, 1.201, 1.174, 1.148, 1.124, 1.103, 1.084, 1.067, 1.052, 1.038, 1.029, 1.021, 1.016, 1.016, 1.021, 1.029, 1.038, 1.051, 1.067, 1.084, 1.103, 1.126, 1.151, 1.176, 1.207, 1.241, 1.279, 1.321, 1.358,
+ 1.371, 1.326, 1.277, 1.242, 1.209, 1.181, 1.155, 1.132, 1.111, 1.092, 1.075, 1.061, 1.049, 1.038, 1.029, 1.027, 1.027, 1.029, 1.038, 1.047, 1.061, 1.075, 1.092, 1.111, 1.133, 1.157, 1.185, 1.213, 1.247, 1.286, 1.329, 1.365,
+ 1.379, 1.334, 1.287, 1.251, 1.219, 1.191, 1.164, 1.141, 1.119, 1.101, 1.085, 1.071, 1.061, 1.049, 1.041, 1.038, 1.038, 1.041, 1.047, 1.059, 1.071, 1.084, 1.101, 1.119, 1.141, 1.165, 1.193, 1.223, 1.257, 1.295, 1.338, 1.374,
+ 1.389, 1.343, 1.298, 1.262, 1.231, 1.201, 1.174, 1.151, 1.131, 1.111, 1.095, 1.083, 1.071, 1.061, 1.054, 1.051, 1.051, 1.054, 1.059, 1.071, 1.081, 1.094, 1.111, 1.129, 1.152, 1.176, 1.203, 1.235, 1.269, 1.307, 1.351, 1.384,
+ 1.401, 1.351, 1.311, 1.274, 1.242, 1.214, 1.187, 1.164, 1.142, 1.124, 1.108, 1.095, 1.083, 1.074, 1.068, 1.066, 1.066, 1.068, 1.073, 1.081, 1.094, 1.108, 1.123, 1.141, 1.164, 1.188, 1.215, 1.247, 1.281, 1.321, 1.364, 1.396,
+ 1.412, 1.366, 1.327, 1.289, 1.257, 1.227, 1.201, 1.176, 1.156, 1.137, 1.122, 1.108, 1.096, 1.088, 1.083, 1.081, 1.081, 1.082, 1.087, 1.095, 1.108, 1.122, 1.136, 1.154, 1.177, 1.201, 1.229, 1.261, 1.296, 1.337, 1.382, 1.409,
+ 1.421, 1.383, 1.343, 1.306, 1.273, 1.243, 1.216, 1.192, 1.169, 1.152, 1.137, 1.122, 1.111, 1.103, 1.098, 1.095, 1.095, 1.097, 1.102, 1.111, 1.123, 1.136, 1.152, 1.169, 1.191, 1.217, 1.246, 1.278, 1.314, 1.354, 1.399, 1.429,
+ 1.434, 1.402, 1.362, 1.324, 1.291, 1.261, 1.232, 1.208, 1.187, 1.168, 1.152, 1.138, 1.127, 1.119, 1.114, 1.112, 1.112, 1.115, 1.121, 1.128, 1.139, 1.152, 1.169, 1.186, 1.209, 1.234, 1.262, 1.295, 1.332, 1.372, 1.419, 1.451,
+ 1.453, 1.422, 1.382, 1.344, 1.309, 1.278, 1.249, 1.226, 1.204, 1.187, 1.168, 1.155, 1.144, 1.135, 1.131, 1.131, 1.131, 1.133, 1.138, 1.146, 1.157, 1.171, 1.186, 1.206, 1.227, 1.252, 1.281, 1.314, 1.351, 1.393, 1.442, 1.473,
+ 1.475, 1.446, 1.404, 1.366, 1.329, 1.298, 1.269, 1.245, 1.224, 1.204, 1.188, 1.174, 1.163, 1.154, 1.149, 1.148, 1.148, 1.152, 1.156, 1.164, 1.176, 1.189, 1.206, 1.226, 1.247, 1.274, 1.303, 1.336, 1.374, 1.417, 1.471, 1.505,
+ 1.503, 1.472, 1.428, 1.389, 1.353, 1.321, 1.291, 1.266, 1.245, 1.224, 1.207, 1.192, 1.183, 1.174, 1.169, 1.167, 1.168, 1.169, 1.175, 1.183, 1.195, 1.209, 1.226, 1.247, 1.267, 1.294, 1.325, 1.359, 1.397, 1.445, 1.505, 1.548,
+ 1.534, 1.503, 1.455, 1.413, 1.378, 1.344, 1.315, 1.289, 1.265, 1.243, 1.224, 1.207, 1.196, 1.192, 1.189, 1.189, 1.189, 1.189, 1.192, 1.198, 1.209, 1.226, 1.244, 1.266, 1.291, 1.318, 1.349, 1.383, 1.425, 1.475, 1.548, 1.591
+ ],
+ "sigma": 0.00095,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.97469, -0.71439, -0.26031,
+ -0.43521, 2.09769, -0.66248,
+ -0.04826, -0.84642, 1.89468
+ ]
+ },
+ {
+ "ct": 2960,
+ "ccm":
+ [
+ 2.12952, -0.91185, -0.21768,
+ -0.38018, 1.90789, -0.52771,
+ 0.03988, -1.10079, 2.06092
+ ]
+ },
+ {
+ "ct": 3580,
+ "ccm":
+ [
+ 2.03422, -0.80048, -0.23374,
+ -0.39089, 1.97221, -0.58132,
+ -0.08969, -0.61439, 1.70408
+ ]
+ },
+ {
+ "ct": 4559,
+ "ccm":
+ [
+ 2.15423, -0.98143, -0.17279,
+ -0.38131, 2.14763, -0.76632,
+ -0.10069, -0.54383, 1.64452
+ ]
+ },
+ {
+ "ct": 5881,
+ "ccm":
+ [
+ 2.18464, -0.95493, -0.22971,
+ -0.36826, 2.00298, -0.63471,
+ -0.15219, -0.38055, 1.53274
+ ]
+ },
+ {
+ "ct": 7600,
+ "ccm":
+ [
+ 2.30687, -0.97295, -0.33392,
+ -0.30872, 2.32779, -1.01908,
+ -0.17761, -0.55891, 1.73651
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "strength": 1.0,
+ "lut_rx":
+ [
+ -0.34, -0.26, -0.18, -0.1, -0.02, 0.06, 0.13, 0.22, 0.37,
+ -0.36, -0.28, -0.19, -0.1, -0.02, 0.06, 0.13, 0.21, 0.36,
+ -0.37, -0.29, -0.19, -0.1, -0.02, 0.06, 0.13, 0.21, 0.36,
+ -0.38, -0.27, -0.18, -0.09, -0.01, 0.07, 0.14, 0.22, 0.36,
+ -0.36, -0.27, -0.18, -0.09, -0.01, 0.07, 0.15, 0.23, 0.38,
+ -0.35, -0.27, -0.18, -0.09, -0.01, 0.08, 0.15, 0.23, 0.39,
+ -0.34, -0.26, -0.18, -0.1, -0.01, 0.07, 0.15, 0.22, 0.39,
+ -0.33, -0.24, -0.17, -0.09, -0.01, 0.08, 0.15, 0.22, 0.37,
+ -0.3, -0.21, -0.14, -0.07, 0.0, 0.09, 0.16, 0.22, 0.36
+ ],
+ "lut_ry":
+ [
+ -0.22, -0.23, -0.26, -0.27, -0.27, -0.26, -0.24, -0.23, -0.22,
+ -0.18, -0.19, -0.21, -0.21, -0.22, -0.21, -0.19, -0.18, -0.16,
+ -0.15, -0.14, -0.16, -0.17, -0.18, -0.17, -0.16, -0.14, -0.11,
+ -0.09, -0.07, -0.08, -0.09, -0.1, -0.09, -0.08, -0.06, -0.06,
+ -0.02, 0.0, -0.01, -0.02, -0.03, -0.03, -0.01, 0.01, 0.01,
+ 0.05, 0.06, 0.04, 0.03, 0.03, 0.03, 0.04, 0.06, 0.07,
+ 0.09, 0.1, 0.09, 0.08, 0.07, 0.07, 0.09, 0.1, 0.12,
+ 0.14, 0.16, 0.15, 0.15, 0.15, 0.14, 0.15, 0.16, 0.16,
+ 0.24, 0.26, 0.25, 0.25, 0.26, 0.26, 0.26, 0.26, 0.25
+ ],
+ "lut_bx":
+ [
+ -0.03, -0.02, -0.0, 0.0, 0.02, 0.03, 0.04, 0.06, 0.11,
+ -0.03, -0.01, -0.0, -0.0, 0.01, 0.02, 0.04, 0.06, 0.11,
+ -0.03, -0.01, 0.0, 0.01, 0.01, 0.02, 0.04, 0.06, 0.1,
+ -0.03, -0.01, 0.0, 0.01, 0.01, 0.02, 0.04, 0.07, 0.12,
+ -0.03, -0.01, -0.0, 0.01, 0.01, 0.02, 0.04, 0.07, 0.13,
+ -0.03, -0.01, 0.0, 0.01, 0.02, 0.02, 0.04, 0.07, 0.14,
+ -0.03, -0.01, 0.0, 0.01, 0.02, 0.02, 0.04, 0.08, 0.15,
+ -0.03, -0.01, 0.0, 0.0, 0.01, 0.02, 0.04, 0.08, 0.16,
+ -0.02, -0.0, 0.0, -0.0, -0.01, 0.0, 0.03, 0.08, 0.16
+ ],
+ "lut_by":
+ [
+ -0.1, -0.07, -0.05, -0.04, -0.04, -0.04, -0.05, -0.08, -0.13,
+ -0.08, -0.06, -0.05, -0.04, -0.04, -0.04, -0.04, -0.06, -0.11,
+ -0.07, -0.04, -0.03, -0.03, -0.02, -0.02, -0.03, -0.04, -0.08,
+ -0.06, -0.04, -0.02, -0.02, -0.02, -0.02, -0.02, -0.04, -0.06,
+ -0.04, -0.01, 0.0, 0.02, 0.02, 0.01, 0.0, -0.01, -0.06,
+ -0.02, 0.01, 0.02, 0.03, 0.04, 0.04, 0.03, 0.01, -0.04,
+ -0.0, 0.02, 0.04, 0.05, 0.05, 0.05, 0.05, 0.03, -0.02,
+ 0.0, 0.02, 0.04, 0.05, 0.06, 0.05, 0.04, 0.04, 0.01,
+ -0.0, 0.02, 0.03, 0.04, 0.05, 0.04, 0.02, 0.03, 0.01
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": [ 0.0, 2.5, 0.01, 2.5, 0.06, 1.0, 1.0, 1.0 ],
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": [ 0.0, 2.5, 0.01, 2.5, 0.06, 1.0, 1.0, 1.0 ],
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx477_6mm.json b/src/ipa/rpi/pisp/data/imx477_6mm.json
new file mode 100644
index 00000000..27268c23
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx477_6mm.json
@@ -0,0 +1,1240 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 12000,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 740,
+ "reference_Y": 15051
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.809
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 204,
+ "slope": 0.0061
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2850.0, 0.4307, 0.3957,
+ 2960.0, 0.4159, 0.4313,
+ 3580.0, 0.3771, 0.5176,
+ 4559.0, 0.3031, 0.6573,
+ 5881.0, 0.2809, 0.6942,
+ 7600.0, 0.2263, 0.7762
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.02634,
+ "transverse_neg": 0.02255
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.359, 2.354, 2.351, 2.351, 2.343, 2.337, 2.331, 2.325, 2.323, 2.321, 2.317, 2.315, 2.313, 2.313, 2.311, 2.312, 2.312, 2.313, 2.315, 2.315, 2.316, 2.317, 2.319, 2.323, 2.326, 2.329, 2.332, 2.332, 2.335, 2.337, 2.352, 2.363,
+ 2.352, 2.351, 2.349, 2.346, 2.342, 2.334, 2.328, 2.324, 2.321, 2.317, 2.315, 2.314, 2.312, 2.311, 2.311, 2.311, 2.311, 2.311, 2.312, 2.314, 2.315, 2.316, 2.317, 2.319, 2.324, 2.326, 2.328, 2.329, 2.331, 2.337, 2.348, 2.355,
+ 2.346, 2.346, 2.345, 2.344, 2.338, 2.329, 2.325, 2.319, 2.316, 2.314, 2.311, 2.309, 2.308, 2.306, 2.304, 2.304, 2.305, 2.307, 2.308, 2.309, 2.311, 2.311, 2.313, 2.316, 2.319, 2.322, 2.325, 2.326, 2.328, 2.335, 2.343, 2.349,
+ 2.342, 2.342, 2.341, 2.338, 2.332, 2.326, 2.319, 2.316, 2.312, 2.309, 2.308, 2.305, 2.303, 2.302, 2.301, 2.301, 2.302, 2.303, 2.304, 2.305, 2.305, 2.307, 2.311, 2.313, 2.315, 2.319, 2.321, 2.325, 2.328, 2.333, 2.338, 2.348,
+ 2.337, 2.337, 2.337, 2.336, 2.331, 2.322, 2.317, 2.312, 2.309, 2.307, 2.304, 2.302, 2.299, 2.299, 2.298, 2.298, 2.299, 2.299, 2.301, 2.302, 2.302, 2.304, 2.305, 2.309, 2.314, 2.316, 2.321, 2.324, 2.326, 2.329, 2.335, 2.343,
+ 2.335, 2.334, 2.333, 2.333, 2.326, 2.318, 2.313, 2.309, 2.306, 2.302, 2.299, 2.297, 2.297, 2.296, 2.295, 2.295, 2.294, 2.295, 2.296, 2.298, 2.298, 2.301, 2.303, 2.305, 2.311, 2.315, 2.319, 2.323, 2.325, 2.329, 2.333, 2.339,
+ 2.329, 2.331, 2.329, 2.329, 2.325, 2.315, 2.309, 2.306, 2.302, 2.299, 2.297, 2.295, 2.293, 2.292, 2.291, 2.291, 2.291, 2.291, 2.293, 2.294, 2.296, 2.298, 2.301, 2.304, 2.307, 2.313, 2.317, 2.319, 2.323, 2.327, 2.331, 2.339,
+ 2.329, 2.328, 2.328, 2.328, 2.321, 2.313, 2.307, 2.303, 2.299, 2.295, 2.294, 2.292, 2.289, 2.289, 2.288, 2.288, 2.288, 2.289, 2.289, 2.292, 2.294, 2.295, 2.297, 2.301, 2.306, 2.311, 2.315, 2.318, 2.319, 2.323, 2.329, 2.335,
+ 2.326, 2.327, 2.325, 2.325, 2.319, 2.311, 2.305, 2.299, 2.296, 2.293, 2.291, 2.289, 2.288, 2.287, 2.285, 2.285, 2.286, 2.288, 2.288, 2.289, 2.291, 2.294, 2.295, 2.298, 2.304, 2.308, 2.313, 2.315, 2.317, 2.319, 2.327, 2.335,
+ 2.325, 2.325, 2.323, 2.323, 2.317, 2.309, 2.303, 2.298, 2.294, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.284, 2.284, 2.285, 2.287, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.305, 2.309, 2.313, 2.315, 2.317, 2.325, 2.334,
+ 2.322, 2.324, 2.322, 2.322, 2.316, 2.306, 2.301, 2.296, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.283, 2.283, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.293, 2.296, 2.301, 2.304, 2.308, 2.311, 2.312, 2.315, 2.323, 2.333,
+ 2.321, 2.323, 2.322, 2.322, 2.314, 2.306, 2.299, 2.294, 2.291, 2.288, 2.286, 2.285, 2.284, 2.282, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.306, 2.308, 2.311, 2.312, 2.322, 2.332,
+ 2.319, 2.321, 2.321, 2.321, 2.314, 2.305, 2.297, 2.293, 2.289, 2.287, 2.285, 2.284, 2.283, 2.281, 2.281, 2.281, 2.282, 2.283, 2.283, 2.285, 2.287, 2.289, 2.291, 2.292, 2.297, 2.301, 2.305, 2.307, 2.309, 2.312, 2.321, 2.333,
+ 2.319, 2.321, 2.319, 2.319, 2.314, 2.303, 2.296, 2.293, 2.289, 2.286, 2.285, 2.283, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.296, 2.301, 2.305, 2.307, 2.308, 2.312, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.296, 2.291, 2.289, 2.286, 2.284, 2.282, 2.281, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.287, 2.288, 2.291, 2.295, 2.299, 2.304, 2.306, 2.307, 2.311, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.287, 2.285, 2.282, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.284, 2.285, 2.286, 2.288, 2.291, 2.295, 2.299, 2.303, 2.306, 2.307, 2.312, 2.321, 2.331,
+ 2.318, 2.319, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.286, 2.285, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.285, 2.286, 2.287, 2.291, 2.294, 2.298, 2.303, 2.306, 2.307, 2.311, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.302, 2.297, 2.292, 2.289, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.287, 2.289, 2.294, 2.297, 2.303, 2.305, 2.308, 2.313, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.303, 2.299, 2.293, 2.291, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.288, 2.291, 2.294, 2.298, 2.304, 2.306, 2.308, 2.312, 2.322, 2.331,
+ 2.319, 2.321, 2.321, 2.321, 2.315, 2.305, 2.301, 2.295, 2.292, 2.289, 2.286, 2.285, 2.283, 2.282, 2.282, 2.282, 2.284, 2.283, 2.284, 2.284, 2.285, 2.287, 2.288, 2.291, 2.294, 2.299, 2.304, 2.306, 2.309, 2.313, 2.322, 2.334,
+ 2.321, 2.322, 2.322, 2.322, 2.317, 2.307, 2.301, 2.296, 2.292, 2.291, 2.288, 2.286, 2.285, 2.284, 2.283, 2.284, 2.285, 2.284, 2.285, 2.285, 2.287, 2.288, 2.289, 2.293, 2.297, 2.301, 2.305, 2.308, 2.311, 2.314, 2.323, 2.335,
+ 2.322, 2.324, 2.324, 2.324, 2.319, 2.309, 2.303, 2.297, 2.295, 2.292, 2.291, 2.288, 2.286, 2.286, 2.285, 2.286, 2.286, 2.286, 2.287, 2.288, 2.289, 2.289, 2.291, 2.294, 2.299, 2.302, 2.307, 2.311, 2.312, 2.316, 2.325, 2.335,
+ 2.324, 2.326, 2.325, 2.326, 2.321, 2.311, 2.305, 2.301, 2.297, 2.295, 2.293, 2.291, 2.289, 2.289, 2.288, 2.288, 2.287, 2.288, 2.289, 2.291, 2.292, 2.292, 2.295, 2.299, 2.301, 2.304, 2.309, 2.312, 2.315, 2.319, 2.327, 2.337,
+ 2.329, 2.329, 2.328, 2.328, 2.323, 2.315, 2.308, 2.304, 2.301, 2.298, 2.296, 2.294, 2.291, 2.291, 2.289, 2.291, 2.291, 2.291, 2.292, 2.293, 2.294, 2.295, 2.297, 2.299, 2.303, 2.308, 2.312, 2.315, 2.318, 2.321, 2.329, 2.339,
+ 2.329, 2.331, 2.332, 2.332, 2.326, 2.318, 2.311, 2.306, 2.304, 2.301, 2.299, 2.297, 2.295, 2.293, 2.292, 2.292, 2.292, 2.293, 2.294, 2.294, 2.296, 2.297, 2.299, 2.302, 2.306, 2.311, 2.315, 2.318, 2.319, 2.324, 2.332, 2.342,
+ 2.331, 2.333, 2.334, 2.334, 2.328, 2.321, 2.313, 2.308, 2.305, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.295, 2.294, 2.296, 2.296, 2.297, 2.298, 2.299, 2.302, 2.305, 2.308, 2.314, 2.317, 2.321, 2.323, 2.327, 2.334, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.309, 2.306, 2.304, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.309, 2.315, 2.319, 2.321, 2.324, 2.328, 2.337, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.311, 2.306, 2.304, 2.303, 2.302, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.311, 2.314, 2.319, 2.323, 2.325, 2.329, 2.339, 2.348,
+ 2.329, 2.329, 2.329, 2.331, 2.326, 2.319, 2.312, 2.309, 2.304, 2.303, 2.302, 2.301, 2.298, 2.295, 2.294, 2.294, 2.295, 2.295, 2.296, 2.297, 2.299, 2.301, 2.302, 2.304, 2.308, 2.313, 2.319, 2.322, 2.325, 2.329, 2.339, 2.351,
+ 2.329, 2.329, 2.329, 2.329, 2.326, 2.317, 2.311, 2.308, 2.303, 2.302, 2.301, 2.298, 2.296, 2.295, 2.294, 2.294, 2.294, 2.294, 2.296, 2.297, 2.298, 2.299, 2.301, 2.304, 2.307, 2.312, 2.318, 2.322, 2.326, 2.331, 2.341, 2.355,
+ 2.339, 2.332, 2.331, 2.331, 2.327, 2.323, 2.316, 2.309, 2.306, 2.302, 2.301, 2.299, 2.297, 2.296, 2.295, 2.294, 2.294, 2.296, 2.297, 2.297, 2.299, 2.301, 2.303, 2.306, 2.308, 2.317, 2.322, 2.325, 2.329, 2.341, 2.353, 2.361,
+ 2.347, 2.347, 2.345, 2.343, 2.338, 2.332, 2.326, 2.322, 2.321, 2.318, 2.316, 2.315, 2.313, 2.312, 2.311, 2.311, 2.311, 2.311, 2.312, 2.315, 2.317, 2.318, 2.319, 2.323, 2.324, 2.329, 2.334, 2.337, 2.344, 2.347, 2.361, 2.364
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.869, 3.852, 3.844, 3.842, 3.836, 3.821, 3.807, 3.796, 3.789, 3.784, 3.778, 3.775, 3.769, 3.768, 3.765, 3.765, 3.767, 3.769, 3.772, 3.774, 3.773, 3.775, 3.779, 3.787, 3.793, 3.801, 3.806, 3.804, 3.813, 3.819, 3.855, 3.879,
+ 3.854, 3.844, 3.837, 3.836, 3.824, 3.811, 3.797, 3.789, 3.784, 3.777, 3.774, 3.769, 3.764, 3.758, 3.757, 3.758, 3.758, 3.761, 3.763, 3.764, 3.765, 3.766, 3.772, 3.778, 3.787, 3.792, 3.794, 3.798, 3.802, 3.815, 3.839, 3.873,
+ 3.838, 3.831, 3.826, 3.823, 3.813, 3.799, 3.787, 3.781, 3.773, 3.768, 3.763, 3.759, 3.753, 3.749, 3.745, 3.745, 3.745, 3.752, 3.754, 3.757, 3.757, 3.759, 3.763, 3.769, 3.773, 3.781, 3.786, 3.792, 3.798, 3.811, 3.831, 3.861,
+ 3.833, 3.822, 3.817, 3.816, 3.804, 3.788, 3.779, 3.772, 3.766, 3.759, 3.755, 3.749, 3.744, 3.741, 3.738, 3.739, 3.739, 3.741, 3.743, 3.747, 3.749, 3.751, 3.756, 3.764, 3.769, 3.776, 3.783, 3.789, 3.798, 3.809, 3.821, 3.855,
+ 3.824, 3.818, 3.808, 3.808, 3.797, 3.781, 3.772, 3.764, 3.757, 3.752, 3.747, 3.743, 3.737, 3.735, 3.733, 3.733, 3.733, 3.735, 3.737, 3.738, 3.741, 3.746, 3.749, 3.755, 3.766, 3.771, 3.781, 3.789, 3.794, 3.806, 3.818, 3.849,
+ 3.815, 3.808, 3.799, 3.801, 3.787, 3.775, 3.767, 3.757, 3.751, 3.745, 3.738, 3.734, 3.732, 3.727, 3.725, 3.723, 3.722, 3.722, 3.726, 3.729, 3.734, 3.738, 3.744, 3.749, 3.759, 3.769, 3.781, 3.788, 3.792, 3.799, 3.811, 3.841,
+ 3.804, 3.799, 3.793, 3.793, 3.783, 3.771, 3.759, 3.751, 3.744, 3.735, 3.732, 3.727, 3.723, 3.721, 3.719, 3.716, 3.716, 3.716, 3.718, 3.722, 3.727, 3.731, 3.737, 3.746, 3.756, 3.767, 3.776, 3.782, 3.788, 3.795, 3.808, 3.831,
+ 3.802, 3.797, 3.787, 3.787, 3.779, 3.762, 3.753, 3.744, 3.734, 3.727, 3.725, 3.721, 3.716, 3.714, 3.709, 3.709, 3.711, 3.711, 3.712, 3.717, 3.722, 3.725, 3.731, 3.739, 3.752, 3.762, 3.772, 3.778, 3.779, 3.789, 3.798, 3.826,
+ 3.791, 3.789, 3.784, 3.784, 3.775, 3.759, 3.746, 3.735, 3.729, 3.724, 3.718, 3.714, 3.712, 3.707, 3.704, 3.704, 3.706, 3.708, 3.709, 3.711, 3.716, 3.722, 3.726, 3.735, 3.746, 3.754, 3.767, 3.774, 3.777, 3.781, 3.794, 3.824,
+ 3.789, 3.784, 3.779, 3.781, 3.771, 3.753, 3.741, 3.732, 3.725, 3.719, 3.715, 3.711, 3.707, 3.704, 3.701, 3.701, 3.702, 3.704, 3.708, 3.709, 3.713, 3.718, 3.724, 3.731, 3.742, 3.749, 3.761, 3.768, 3.772, 3.778, 3.791, 3.822,
+ 3.789, 3.781, 3.777, 3.777, 3.764, 3.749, 3.739, 3.729, 3.722, 3.718, 3.711, 3.708, 3.705, 3.701, 3.699, 3.699, 3.699, 3.701, 3.705, 3.707, 3.711, 3.715, 3.721, 3.727, 3.738, 3.746, 3.757, 3.763, 3.765, 3.773, 3.788, 3.821,
+ 3.785, 3.779, 3.774, 3.774, 3.764, 3.747, 3.736, 3.726, 3.719, 3.711, 3.709, 3.706, 3.701, 3.698, 3.696, 3.695, 3.695, 3.698, 3.702, 3.704, 3.707, 3.712, 3.718, 3.725, 3.734, 3.741, 3.753, 3.756, 3.759, 3.764, 3.784, 3.818,
+ 3.779, 3.776, 3.773, 3.773, 3.759, 3.744, 3.733, 3.724, 3.714, 3.709, 3.706, 3.704, 3.699, 3.696, 3.694, 3.694, 3.694, 3.697, 3.701, 3.703, 3.706, 3.709, 3.714, 3.721, 3.731, 3.737, 3.749, 3.753, 3.758, 3.762, 3.783, 3.819,
+ 3.779, 3.776, 3.769, 3.769, 3.757, 3.741, 3.729, 3.721, 3.712, 3.708, 3.705, 3.701, 3.697, 3.695, 3.694, 3.694, 3.695, 3.696, 3.698, 3.702, 3.705, 3.709, 3.712, 3.717, 3.728, 3.736, 3.749, 3.752, 3.756, 3.761, 3.781, 3.815,
+ 3.779, 3.773, 3.768, 3.768, 3.756, 3.738, 3.731, 3.719, 3.711, 3.707, 3.703, 3.698, 3.695, 3.694, 3.694, 3.695, 3.695, 3.695, 3.696, 3.702, 3.705, 3.708, 3.712, 3.717, 3.728, 3.736, 3.747, 3.751, 3.754, 3.761, 3.781, 3.815,
+ 3.782, 3.773, 3.767, 3.767, 3.755, 3.738, 3.728, 3.721, 3.711, 3.707, 3.701, 3.698, 3.695, 3.693, 3.694, 3.696, 3.695, 3.695, 3.695, 3.701, 3.703, 3.706, 3.711, 3.715, 3.726, 3.735, 3.745, 3.751, 3.754, 3.763, 3.779, 3.815,
+ 3.781, 3.771, 3.767, 3.767, 3.754, 3.739, 3.726, 3.721, 3.712, 3.706, 3.701, 3.698, 3.695, 3.693, 3.693, 3.695, 3.695, 3.695, 3.696, 3.698, 3.703, 3.705, 3.709, 3.715, 3.725, 3.734, 3.745, 3.751, 3.755, 3.762, 3.783, 3.818,
+ 3.781, 3.774, 3.767, 3.767, 3.755, 3.741, 3.729, 3.722, 3.712, 3.708, 3.701, 3.699, 3.695, 3.693, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.725, 3.732, 3.746, 3.751, 3.756, 3.763, 3.783, 3.821,
+ 3.781, 3.774, 3.769, 3.769, 3.756, 3.741, 3.731, 3.724, 3.713, 3.711, 3.707, 3.699, 3.697, 3.694, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.724, 3.734, 3.747, 3.751, 3.756, 3.765, 3.784, 3.821,
+ 3.784, 3.776, 3.773, 3.773, 3.759, 3.742, 3.733, 3.726, 3.719, 3.711, 3.709, 3.703, 3.698, 3.695, 3.694, 3.695, 3.697, 3.696, 3.698, 3.699, 3.703, 3.706, 3.711, 3.714, 3.727, 3.735, 3.746, 3.751, 3.757, 3.766, 3.787, 3.822,
+ 3.786, 3.783, 3.774, 3.774, 3.766, 3.747, 3.737, 3.727, 3.722, 3.716, 3.711, 3.706, 3.702, 3.698, 3.697, 3.698, 3.699, 3.699, 3.701, 3.703, 3.706, 3.711, 3.713, 3.719, 3.731, 3.739, 3.748, 3.753, 3.761, 3.769, 3.789, 3.826,
+ 3.786, 3.784, 3.779, 3.779, 3.769, 3.751, 3.742, 3.732, 3.725, 3.719, 3.715, 3.711, 3.706, 3.704, 3.701, 3.701, 3.702, 3.702, 3.705, 3.707, 3.712, 3.714, 3.717, 3.724, 3.733, 3.743, 3.749, 3.758, 3.764, 3.769, 3.791, 3.826,
+ 3.793, 3.787, 3.782, 3.782, 3.774, 3.756, 3.747, 3.737, 3.729, 3.725, 3.719, 3.715, 3.712, 3.708, 3.707, 3.706, 3.707, 3.708, 3.709, 3.713, 3.714, 3.717, 3.723, 3.729, 3.736, 3.747, 3.757, 3.764, 3.768, 3.774, 3.794, 3.829,
+ 3.794, 3.791, 3.786, 3.786, 3.779, 3.762, 3.751, 3.742, 3.735, 3.729, 3.725, 3.719, 3.716, 3.711, 3.709, 3.709, 3.709, 3.711, 3.716, 3.717, 3.721, 3.723, 3.726, 3.732, 3.741, 3.752, 3.761, 3.767, 3.773, 3.779, 3.801, 3.829,
+ 3.802, 3.798, 3.793, 3.793, 3.779, 3.766, 3.754, 3.746, 3.741, 3.736, 3.731, 3.726, 3.719, 3.717, 3.716, 3.715, 3.716, 3.717, 3.719, 3.721, 3.724, 3.726, 3.731, 3.737, 3.744, 3.756, 3.766, 3.772, 3.776, 3.784, 3.807, 3.839,
+ 3.805, 3.799, 3.795, 3.795, 3.784, 3.767, 3.757, 3.749, 3.744, 3.739, 3.736, 3.731, 3.726, 3.722, 3.719, 3.719, 3.719, 3.721, 3.723, 3.725, 3.727, 3.732, 3.738, 3.742, 3.751, 3.761, 3.771, 3.775, 3.782, 3.789, 3.811, 3.841,
+ 3.804, 3.801, 3.799, 3.799, 3.787, 3.772, 3.761, 3.752, 3.746, 3.742, 3.739, 3.735, 3.729, 3.726, 3.723, 3.724, 3.725, 3.726, 3.727, 3.728, 3.732, 3.736, 3.739, 3.745, 3.754, 3.765, 3.775, 3.779, 3.785, 3.795, 3.816, 3.844,
+ 3.801, 3.799, 3.796, 3.796, 3.787, 3.773, 3.761, 3.753, 3.746, 3.743, 3.739, 3.735, 3.731, 3.726, 3.725, 3.725, 3.725, 3.726, 3.727, 3.729, 3.733, 3.736, 3.741, 3.745, 3.755, 3.766, 3.776, 3.783, 3.786, 3.797, 3.819, 3.851,
+ 3.799, 3.795, 3.788, 3.788, 3.783, 3.772, 3.759, 3.749, 3.744, 3.738, 3.735, 3.733, 3.726, 3.724, 3.722, 3.722, 3.723, 3.724, 3.725, 3.727, 3.729, 3.733, 3.736, 3.742, 3.754, 3.762, 3.772, 3.779, 3.784, 3.796, 3.821, 3.859,
+ 3.799, 3.789, 3.787, 3.788, 3.779, 3.766, 3.755, 3.749, 3.742, 3.736, 3.733, 3.727, 3.723, 3.722, 3.721, 3.719, 3.719, 3.721, 3.725, 3.726, 3.728, 3.732, 3.734, 3.741, 3.747, 3.758, 3.771, 3.778, 3.785, 3.796, 3.825, 3.862,
+ 3.824, 3.799, 3.789, 3.789, 3.788, 3.777, 3.761, 3.751, 3.743, 3.739, 3.736, 3.728, 3.726, 3.725, 3.721, 3.719, 3.721, 3.723, 3.727, 3.728, 3.729, 3.733, 3.737, 3.744, 3.755, 3.769, 3.776, 3.784, 3.793, 3.819, 3.863, 3.877,
+ 3.833, 3.833, 3.833, 3.842, 3.825, 3.815, 3.807, 3.799, 3.792, 3.788, 3.785, 3.782, 3.778, 3.777, 3.773, 3.772, 3.772, 3.774, 3.778, 3.779, 3.779, 3.785, 3.792, 3.798, 3.803, 3.811, 3.822, 3.834, 3.843, 3.846, 3.877, 3.886
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.616, 2.616, 2.618, 2.621, 2.619, 2.618, 2.615, 2.615, 2.613, 2.611, 2.609, 2.609, 2.609, 2.611, 2.611, 2.611, 2.611, 2.609, 2.608, 2.608, 2.611, 2.613, 2.613, 2.614, 2.614, 2.615, 2.615, 2.622, 2.624, 2.621, 2.624, 2.641,
+ 2.616, 2.618, 2.621, 2.623, 2.623, 2.619, 2.618, 2.616, 2.616, 2.613, 2.611, 2.611, 2.611, 2.611, 2.612, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.612, 2.613, 2.612, 2.613, 2.615, 2.617, 2.621, 2.621, 2.619, 2.621, 2.641,
+ 2.621, 2.624, 2.627, 2.627, 2.625, 2.623, 2.621, 2.619, 2.618, 2.618, 2.618, 2.617, 2.616, 2.616, 2.615, 2.613, 2.612, 2.613, 2.613, 2.614, 2.614, 2.613, 2.614, 2.613, 2.614, 2.617, 2.619, 2.621, 2.621, 2.619, 2.623, 2.643,
+ 2.626, 2.627, 2.628, 2.629, 2.628, 2.625, 2.622, 2.621, 2.621, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.616, 2.616, 2.616, 2.618, 2.618, 2.617, 2.617, 2.618, 2.619, 2.621, 2.623, 2.624, 2.626, 2.625, 2.624, 2.625, 2.654,
+ 2.627, 2.628, 2.628, 2.628, 2.626, 2.623, 2.622, 2.622, 2.622, 2.622, 2.621, 2.621, 2.619, 2.617, 2.617, 2.616, 2.617, 2.617, 2.618, 2.619, 2.618, 2.618, 2.618, 2.621, 2.622, 2.624, 2.626, 2.627, 2.627, 2.626, 2.628, 2.655,
+ 2.625, 2.626, 2.627, 2.626, 2.625, 2.623, 2.622, 2.621, 2.622, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.616, 2.616, 2.616, 2.616, 2.616, 2.617, 2.618, 2.619, 2.621, 2.622, 2.624, 2.626, 2.628, 2.628, 2.629, 2.629, 2.655,
+ 2.626, 2.625, 2.626, 2.625, 2.625, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.616, 2.614, 2.613, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.618, 2.619, 2.621, 2.623, 2.624, 2.627, 2.629, 2.631, 2.629, 2.631, 2.651,
+ 2.625, 2.625, 2.625, 2.624, 2.623, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.614, 2.613, 2.612, 2.611, 2.611, 2.612, 2.612, 2.613, 2.616, 2.618, 2.619, 2.622, 2.624, 2.626, 2.628, 2.631, 2.631, 2.631, 2.631, 2.651,
+ 2.625, 2.625, 2.624, 2.623, 2.622, 2.622, 2.622, 2.622, 2.622, 2.621, 2.617, 2.615, 2.613, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.613, 2.615, 2.618, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.624, 2.624, 2.622, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.618, 2.616, 2.614, 2.612, 2.611, 2.609, 2.609, 2.608, 2.609, 2.611, 2.611, 2.615, 2.617, 2.619, 2.621, 2.625, 2.628, 2.631, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.622, 2.623, 2.622, 2.622, 2.621, 2.619, 2.619, 2.619, 2.618, 2.616, 2.614, 2.613, 2.611, 2.609, 2.608, 2.606, 2.607, 2.607, 2.609, 2.611, 2.615, 2.617, 2.619, 2.622, 2.626, 2.629, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.622, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.614, 2.613, 2.611, 2.611, 2.607, 2.606, 2.605, 2.604, 2.605, 2.607, 2.609, 2.613, 2.616, 2.619, 2.622, 2.627, 2.631, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.613, 2.609, 2.607, 2.604, 2.602, 2.601, 2.602, 2.603, 2.605, 2.609, 2.612, 2.616, 2.619, 2.624, 2.628, 2.631, 2.632, 2.633, 2.629, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.615, 2.614, 2.612, 2.608, 2.603, 2.601, 2.598, 2.597, 2.599, 2.602, 2.605, 2.608, 2.611, 2.615, 2.622, 2.625, 2.629, 2.631, 2.631, 2.633, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.616, 2.614, 2.611, 2.606, 2.601, 2.598, 2.595, 2.595, 2.597, 2.601, 2.604, 2.608, 2.612, 2.615, 2.623, 2.627, 2.629, 2.631, 2.631, 2.632, 2.631, 2.628, 2.628, 2.651,
+ 2.622, 2.623, 2.624, 2.624, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.609, 2.606, 2.601, 2.596, 2.594, 2.594, 2.596, 2.599, 2.603, 2.609, 2.613, 2.617, 2.623, 2.627, 2.629, 2.631, 2.632, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.623, 2.625, 2.625, 2.624, 2.621, 2.621, 2.619, 2.617, 2.616, 2.613, 2.608, 2.605, 2.601, 2.595, 2.593, 2.593, 2.595, 2.598, 2.604, 2.609, 2.615, 2.619, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.632, 2.629, 2.631, 2.651,
+ 2.624, 2.626, 2.626, 2.623, 2.621, 2.619, 2.618, 2.617, 2.615, 2.612, 2.608, 2.605, 2.601, 2.597, 2.595, 2.595, 2.596, 2.598, 2.605, 2.609, 2.616, 2.621, 2.626, 2.627, 2.629, 2.631, 2.633, 2.633, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.618, 2.617, 2.614, 2.612, 2.609, 2.606, 2.602, 2.599, 2.598, 2.597, 2.598, 2.602, 2.607, 2.612, 2.619, 2.621, 2.626, 2.628, 2.629, 2.632, 2.633, 2.634, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.621, 2.618, 2.617, 2.614, 2.612, 2.611, 2.608, 2.604, 2.602, 2.599, 2.599, 2.603, 2.606, 2.611, 2.616, 2.621, 2.624, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.634, 2.633, 2.633, 2.656,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.611, 2.611, 2.607, 2.604, 2.604, 2.604, 2.606, 2.609, 2.613, 2.619, 2.622, 2.625, 2.628, 2.631, 2.632, 2.633, 2.633, 2.636, 2.636, 2.634, 2.634, 2.658,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.619, 2.618, 2.616, 2.614, 2.613, 2.612, 2.611, 2.609, 2.608, 2.607, 2.608, 2.609, 2.613, 2.617, 2.621, 2.623, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.635, 2.636, 2.636, 2.636, 2.661,
+ 2.623, 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.616, 2.615, 2.614, 2.613, 2.612, 2.612, 2.611, 2.611, 2.611, 2.614, 2.615, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.633, 2.635, 2.635, 2.637, 2.637, 2.636, 2.637, 2.661,
+ 2.623, 2.624, 2.625, 2.626, 2.624, 2.621, 2.619, 2.617, 2.616, 2.615, 2.615, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.619, 2.621, 2.623, 2.626, 2.628, 2.631, 2.632, 2.634, 2.635, 2.636, 2.637, 2.638, 2.637, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.627, 2.626, 2.623, 2.619, 2.619, 2.618, 2.618, 2.618, 2.617, 2.617, 2.616, 2.616, 2.616, 2.619, 2.622, 2.623, 2.625, 2.628, 2.628, 2.631, 2.632, 2.634, 2.636, 2.638, 2.639, 2.639, 2.638, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.628, 2.626, 2.623, 2.621, 2.619, 2.619, 2.619, 2.619, 2.619, 2.619, 2.618, 2.618, 2.619, 2.623, 2.624, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.635, 2.638, 2.639, 2.639, 2.639, 2.636, 2.636, 2.662,
+ 2.625, 2.627, 2.628, 2.628, 2.626, 2.624, 2.623, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.624, 2.624, 2.625, 2.627, 2.628, 2.631, 2.631, 2.632, 2.634, 2.636, 2.639, 2.639, 2.641, 2.639, 2.635, 2.635, 2.663,
+ 2.625, 2.626, 2.628, 2.628, 2.627, 2.625, 2.624, 2.623, 2.623, 2.622, 2.623, 2.624, 2.624, 2.625, 2.625, 2.625, 2.625, 2.626, 2.627, 2.629, 2.631, 2.632, 2.633, 2.635, 2.638, 2.641, 2.642, 2.643, 2.642, 2.636, 2.636, 2.665,
+ 2.624, 2.626, 2.628, 2.628, 2.628, 2.626, 2.624, 2.624, 2.623, 2.623, 2.623, 2.625, 2.627, 2.627, 2.626, 2.626, 2.626, 2.627, 2.628, 2.629, 2.632, 2.633, 2.635, 2.637, 2.639, 2.642, 2.644, 2.644, 2.642, 2.638, 2.638, 2.665,
+ 2.623, 2.625, 2.626, 2.627, 2.626, 2.626, 2.624, 2.623, 2.623, 2.623, 2.623, 2.623, 2.626, 2.627, 2.626, 2.626, 2.626, 2.626, 2.628, 2.628, 2.629, 2.631, 2.634, 2.636, 2.639, 2.642, 2.644, 2.643, 2.641, 2.637, 2.638, 2.659,
+ 2.623, 2.627, 2.627, 2.627, 2.627, 2.628, 2.627, 2.624, 2.624, 2.623, 2.624, 2.624, 2.628, 2.628, 2.627, 2.628, 2.628, 2.628, 2.629, 2.629, 2.631, 2.635, 2.637, 2.639, 2.641, 2.643, 2.646, 2.645, 2.643, 2.641, 2.654, 2.659,
+ 2.642, 2.641, 2.643, 2.645, 2.645, 2.644, 2.644, 2.643, 2.643, 2.642, 2.642, 2.642, 2.643, 2.644, 2.644, 2.644, 2.646, 2.646, 2.647, 2.649, 2.651, 2.652, 2.654, 2.656, 2.658, 2.661, 2.661, 2.661, 2.659, 2.654, 2.659, 2.659
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.391, 1.394, 1.395, 1.396, 1.398, 1.398, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.398, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.399, 1.397, 1.399, 1.402,
+ 1.393, 1.395, 1.396, 1.398, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.401, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.399, 1.401, 1.401, 1.399, 1.398, 1.399, 1.402,
+ 1.398, 1.401, 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.401, 1.401, 1.399, 1.399, 1.401, 1.401, 1.401, 1.401, 1.399, 1.401, 1.406,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.402, 1.403, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.405, 1.404, 1.404, 1.404, 1.404, 1.405, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.406, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.408, 1.408, 1.407, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.403, 1.402, 1.402, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.408, 1.407, 1.405, 1.405, 1.414,
+ 1.402, 1.402, 1.402, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.403, 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.407, 1.409, 1.409, 1.409, 1.409, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.403, 1.404, 1.405, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.408, 1.405, 1.405, 1.413,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.402, 1.401, 1.399, 1.398, 1.398, 1.399, 1.399, 1.401, 1.403, 1.404, 1.405, 1.407, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.403, 1.404, 1.405, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.399, 1.401, 1.403, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.398, 1.401, 1.403, 1.406, 1.407, 1.409, 1.409, 1.411, 1.409, 1.409, 1.409, 1.408, 1.407, 1.407, 1.413,
+ 1.403, 1.404, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.404, 1.404, 1.403, 1.402, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.402, 1.404, 1.406, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.414,
+ 1.403, 1.403, 1.404, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.402, 1.401, 1.399, 1.398, 1.398, 1.398, 1.401, 1.403, 1.404, 1.408, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.402, 1.404, 1.406, 1.407, 1.408, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.409, 1.408, 1.408, 1.415,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.406, 1.408, 1.408, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.405, 1.406, 1.407, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.407, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.416,
+ 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.406, 1.407, 1.407, 1.407, 1.408, 1.409, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.417,
+ 1.402, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.415,
+ 1.402, 1.402, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.405, 1.406, 1.406, 1.406, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.412, 1.412, 1.413, 1.413, 1.411, 1.408, 1.411, 1.413,
+ 1.406, 1.406, 1.408, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.414, 1.414, 1.414, 1.414, 1.415, 1.415, 1.415, 1.415, 1.416, 1.416, 1.416, 1.417, 1.418, 1.418, 1.417, 1.417, 1.414, 1.411, 1.413, 1.413
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.554, 1.522, 1.466, 1.422, 1.385, 1.351, 1.322, 1.294, 1.269, 1.246, 1.228, 1.214, 1.207, 1.202, 1.199, 1.199, 1.199, 1.199, 1.202, 1.207, 1.218, 1.235, 1.255, 1.279, 1.305, 1.333, 1.365, 1.402, 1.447, 1.508, 1.602, 1.638,
+ 1.522, 1.478, 1.431, 1.391, 1.355, 1.323, 1.298, 1.271, 1.247, 1.228, 1.212, 1.199, 1.187, 1.179, 1.173, 1.172, 1.172, 1.174, 1.179, 1.189, 1.201, 1.216, 1.235, 1.256, 1.282, 1.308, 1.335, 1.368, 1.411, 1.461, 1.535, 1.602,
+ 1.479, 1.449, 1.407, 1.367, 1.332, 1.301, 1.271, 1.247, 1.226, 1.208, 1.191, 1.178, 1.166, 1.158, 1.153, 1.151, 1.151, 1.153, 1.159, 1.168, 1.179, 1.194, 1.212, 1.234, 1.256, 1.282, 1.311, 1.343, 1.382, 1.427, 1.489, 1.535,
+ 1.454, 1.423, 1.383, 1.345, 1.309, 1.278, 1.249, 1.226, 1.206, 1.187, 1.171, 1.158, 1.146, 1.138, 1.132, 1.129, 1.129, 1.133, 1.139, 1.147, 1.159, 1.173, 1.191, 1.212, 1.234, 1.261, 1.288, 1.321, 1.357, 1.401, 1.455, 1.489,
+ 1.433, 1.401, 1.362, 1.325, 1.289, 1.258, 1.231, 1.206, 1.187, 1.169, 1.153, 1.138, 1.129, 1.121, 1.115, 1.112, 1.112, 1.114, 1.121, 1.129, 1.141, 1.155, 1.172, 1.191, 1.214, 1.241, 1.269, 1.301, 1.337, 1.377, 1.428, 1.457,
+ 1.415, 1.382, 1.343, 1.306, 1.273, 1.241, 1.213, 1.189, 1.169, 1.153, 1.137, 1.123, 1.112, 1.105, 1.097, 1.095, 1.095, 1.098, 1.103, 1.112, 1.124, 1.139, 1.155, 1.173, 1.197, 1.222, 1.252, 1.282, 1.317, 1.356, 1.405, 1.434,
+ 1.398, 1.363, 1.325, 1.289, 1.256, 1.224, 1.198, 1.175, 1.155, 1.137, 1.123, 1.108, 1.097, 1.089, 1.083, 1.079, 1.079, 1.083, 1.088, 1.097, 1.109, 1.124, 1.139, 1.158, 1.181, 1.206, 1.234, 1.266, 1.299, 1.339, 1.384, 1.415,
+ 1.382, 1.347, 1.309, 1.274, 1.242, 1.211, 1.185, 1.162, 1.142, 1.124, 1.108, 1.095, 1.083, 1.075, 1.069, 1.066, 1.066, 1.068, 1.074, 1.083, 1.096, 1.109, 1.125, 1.145, 1.166, 1.191, 1.219, 1.251, 1.285, 1.324, 1.367, 1.399,
+ 1.369, 1.334, 1.296, 1.261, 1.228, 1.199, 1.173, 1.151, 1.131, 1.112, 1.095, 1.083, 1.071, 1.062, 1.056, 1.053, 1.053, 1.055, 1.061, 1.069, 1.083, 1.096, 1.112, 1.132, 1.153, 1.178, 1.206, 1.237, 1.271, 1.309, 1.353, 1.385,
+ 1.359, 1.321, 1.284, 1.251, 1.217, 1.189, 1.164, 1.141, 1.121, 1.102, 1.086, 1.071, 1.061, 1.049, 1.045, 1.042, 1.042, 1.043, 1.051, 1.061, 1.069, 1.085, 1.101, 1.121, 1.143, 1.167, 1.195, 1.225, 1.259, 1.298, 1.341, 1.375,
+ 1.351, 1.312, 1.275, 1.241, 1.209, 1.181, 1.155, 1.133, 1.112, 1.092, 1.076, 1.061, 1.049, 1.041, 1.034, 1.032, 1.032, 1.035, 1.041, 1.051, 1.061, 1.075, 1.092, 1.112, 1.133, 1.158, 1.185, 1.216, 1.249, 1.288, 1.331, 1.364,
+ 1.344, 1.303, 1.267, 1.233, 1.201, 1.173, 1.147, 1.124, 1.104, 1.085, 1.067, 1.053, 1.041, 1.033, 1.024, 1.022, 1.022, 1.025, 1.034, 1.041, 1.053, 1.066, 1.083, 1.103, 1.126, 1.149, 1.177, 1.207, 1.241, 1.279, 1.321, 1.357,
+ 1.339, 1.297, 1.261, 1.226, 1.194, 1.166, 1.142, 1.119, 1.098, 1.078, 1.061, 1.046, 1.034, 1.024, 1.017, 1.014, 1.014, 1.017, 1.025, 1.034, 1.046, 1.059, 1.077, 1.096, 1.118, 1.143, 1.169, 1.201, 1.235, 1.273, 1.314, 1.352,
+ 1.337, 1.293, 1.256, 1.223, 1.191, 1.163, 1.136, 1.114, 1.093, 1.074, 1.056, 1.041, 1.027, 1.017, 1.012, 1.006, 1.006, 1.013, 1.017, 1.028, 1.041, 1.055, 1.072, 1.092, 1.114, 1.138, 1.165, 1.195, 1.229, 1.268, 1.309, 1.348,
+ 1.337, 1.291, 1.253, 1.219, 1.187, 1.159, 1.133, 1.109, 1.089, 1.071, 1.053, 1.037, 1.023, 1.012, 1.006, 1.002, 1.003, 1.006, 1.013, 1.023, 1.038, 1.052, 1.069, 1.089, 1.111, 1.135, 1.161, 1.192, 1.226, 1.264, 1.306, 1.348,
+ 1.337, 1.291, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.035, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.341, 1.292, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.034, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.348, 1.298, 1.255, 1.219, 1.188, 1.159, 1.134, 1.111, 1.088, 1.069, 1.051, 1.035, 1.021, 1.009, 1.003, 1.001, 1.002, 1.004, 1.011, 1.022, 1.036, 1.053, 1.071, 1.089, 1.111, 1.135, 1.162, 1.191, 1.226, 1.264, 1.306, 1.347,
+ 1.354, 1.306, 1.258, 1.222, 1.191, 1.162, 1.135, 1.113, 1.092, 1.073, 1.054, 1.038, 1.024, 1.014, 1.008, 1.003, 1.004, 1.008, 1.014, 1.026, 1.039, 1.056, 1.073, 1.093, 1.115, 1.139, 1.165, 1.195, 1.229, 1.267, 1.309, 1.349,
+ 1.358, 1.312, 1.263, 1.227, 1.195, 1.167, 1.141, 1.117, 1.097, 1.078, 1.061, 1.043, 1.029, 1.021, 1.014, 1.008, 1.008, 1.014, 1.021, 1.032, 1.045, 1.061, 1.078, 1.097, 1.119, 1.144, 1.169, 1.201, 1.234, 1.272, 1.315, 1.353,
+ 1.364, 1.319, 1.269, 1.234, 1.201, 1.174, 1.148, 1.124, 1.103, 1.084, 1.067, 1.052, 1.038, 1.029, 1.021, 1.016, 1.016, 1.021, 1.029, 1.038, 1.051, 1.067, 1.084, 1.103, 1.126, 1.151, 1.176, 1.207, 1.241, 1.279, 1.321, 1.358,
+ 1.371, 1.326, 1.277, 1.242, 1.209, 1.181, 1.155, 1.132, 1.111, 1.092, 1.075, 1.061, 1.049, 1.038, 1.029, 1.027, 1.027, 1.029, 1.038, 1.047, 1.061, 1.075, 1.092, 1.111, 1.133, 1.157, 1.185, 1.213, 1.247, 1.286, 1.329, 1.365,
+ 1.379, 1.334, 1.287, 1.251, 1.219, 1.191, 1.164, 1.141, 1.119, 1.101, 1.085, 1.071, 1.061, 1.049, 1.041, 1.038, 1.038, 1.041, 1.047, 1.059, 1.071, 1.084, 1.101, 1.119, 1.141, 1.165, 1.193, 1.223, 1.257, 1.295, 1.338, 1.374,
+ 1.389, 1.343, 1.298, 1.262, 1.231, 1.201, 1.174, 1.151, 1.131, 1.111, 1.095, 1.083, 1.071, 1.061, 1.054, 1.051, 1.051, 1.054, 1.059, 1.071, 1.081, 1.094, 1.111, 1.129, 1.152, 1.176, 1.203, 1.235, 1.269, 1.307, 1.351, 1.384,
+ 1.401, 1.351, 1.311, 1.274, 1.242, 1.214, 1.187, 1.164, 1.142, 1.124, 1.108, 1.095, 1.083, 1.074, 1.068, 1.066, 1.066, 1.068, 1.073, 1.081, 1.094, 1.108, 1.123, 1.141, 1.164, 1.188, 1.215, 1.247, 1.281, 1.321, 1.364, 1.396,
+ 1.412, 1.366, 1.327, 1.289, 1.257, 1.227, 1.201, 1.176, 1.156, 1.137, 1.122, 1.108, 1.096, 1.088, 1.083, 1.081, 1.081, 1.082, 1.087, 1.095, 1.108, 1.122, 1.136, 1.154, 1.177, 1.201, 1.229, 1.261, 1.296, 1.337, 1.382, 1.409,
+ 1.421, 1.383, 1.343, 1.306, 1.273, 1.243, 1.216, 1.192, 1.169, 1.152, 1.137, 1.122, 1.111, 1.103, 1.098, 1.095, 1.095, 1.097, 1.102, 1.111, 1.123, 1.136, 1.152, 1.169, 1.191, 1.217, 1.246, 1.278, 1.314, 1.354, 1.399, 1.429,
+ 1.434, 1.402, 1.362, 1.324, 1.291, 1.261, 1.232, 1.208, 1.187, 1.168, 1.152, 1.138, 1.127, 1.119, 1.114, 1.112, 1.112, 1.115, 1.121, 1.128, 1.139, 1.152, 1.169, 1.186, 1.209, 1.234, 1.262, 1.295, 1.332, 1.372, 1.419, 1.451,
+ 1.453, 1.422, 1.382, 1.344, 1.309, 1.278, 1.249, 1.226, 1.204, 1.187, 1.168, 1.155, 1.144, 1.135, 1.131, 1.131, 1.131, 1.133, 1.138, 1.146, 1.157, 1.171, 1.186, 1.206, 1.227, 1.252, 1.281, 1.314, 1.351, 1.393, 1.442, 1.473,
+ 1.475, 1.446, 1.404, 1.366, 1.329, 1.298, 1.269, 1.245, 1.224, 1.204, 1.188, 1.174, 1.163, 1.154, 1.149, 1.148, 1.148, 1.152, 1.156, 1.164, 1.176, 1.189, 1.206, 1.226, 1.247, 1.274, 1.303, 1.336, 1.374, 1.417, 1.471, 1.505,
+ 1.503, 1.472, 1.428, 1.389, 1.353, 1.321, 1.291, 1.266, 1.245, 1.224, 1.207, 1.192, 1.183, 1.174, 1.169, 1.167, 1.168, 1.169, 1.175, 1.183, 1.195, 1.209, 1.226, 1.247, 1.267, 1.294, 1.325, 1.359, 1.397, 1.445, 1.505, 1.548,
+ 1.534, 1.503, 1.455, 1.413, 1.378, 1.344, 1.315, 1.289, 1.265, 1.243, 1.224, 1.207, 1.196, 1.192, 1.189, 1.189, 1.189, 1.189, 1.192, 1.198, 1.209, 1.226, 1.244, 1.266, 1.291, 1.318, 1.349, 1.383, 1.425, 1.475, 1.548, 1.591
+ ],
+ "sigma": 0.00095,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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.97469, -0.71439, -0.26031,
+ -0.43521, 2.09769, -0.66248,
+ -0.04826, -0.84642, 1.89468
+ ]
+ },
+ {
+ "ct": 2960,
+ "ccm":
+ [
+ 2.12952, -0.91185, -0.21768,
+ -0.38018, 1.90789, -0.52771,
+ 0.03988, -1.10079, 2.06092
+ ]
+ },
+ {
+ "ct": 3580,
+ "ccm":
+ [
+ 2.03422, -0.80048, -0.23374,
+ -0.39089, 1.97221, -0.58132,
+ -0.08969, -0.61439, 1.70408
+ ]
+ },
+ {
+ "ct": 4559,
+ "ccm":
+ [
+ 2.15423, -0.98143, -0.17279,
+ -0.38131, 2.14763, -0.76632,
+ -0.10069, -0.54383, 1.64452
+ ]
+ },
+ {
+ "ct": 5881,
+ "ccm":
+ [
+ 2.18464, -0.95493, -0.22971,
+ -0.36826, 2.00298, -0.63471,
+ -0.15219, -0.38055, 1.53274
+ ]
+ },
+ {
+ "ct": 7600,
+ "ccm":
+ [
+ 2.30687, -0.97295, -0.33392,
+ -0.30872, 2.32779, -1.01908,
+ -0.17761, -0.55891, 1.73651
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "strength": 1.0,
+ "lut_rx":
+ [
+ -0.46, -0.31, -0.17, -0.05, 0.04, 0.14, 0.28, 0.36, 0.58,
+ -0.43, -0.28, -0.16, -0.05, 0.05, 0.14, 0.24, 0.36, 0.58,
+ -0.42, -0.27, -0.15, -0.05, 0.05, 0.14, 0.23, 0.34, 0.58,
+ -0.39, -0.25, -0.14, -0.04, 0.05, 0.15, 0.24, 0.35, 0.56,
+ -0.35, -0.24, -0.14, -0.04, 0.05, 0.14, 0.25, 0.35, 0.56,
+ -0.33, -0.22, -0.13, -0.04, 0.04, 0.13, 0.24, 0.33, 0.54,
+ -0.33, -0.22, -0.13, -0.04, 0.04, 0.13, 0.23, 0.31, 0.5,
+ -0.32, -0.23, -0.13, -0.04, 0.04, 0.12, 0.21, 0.29, 0.46,
+ -0.33, -0.24, -0.15, -0.05, 0.03, 0.11, 0.19, 0.26, 0.43
+ ],
+ "lut_ry":
+ [
+ -0.39, -0.34, -0.31, -0.29, -0.27, -0.29, -0.32, -0.31, -0.32,
+ -0.29, -0.24, -0.22, -0.21, -0.22, -0.21, -0.21, -0.22, -0.23,
+ -0.22, -0.17, -0.17, -0.17, -0.17, -0.17, -0.16, -0.16, -0.18,
+ -0.13, -0.09, -0.09, -0.1, -0.1, -0.1, -0.09, -0.08, -0.11,
+ -0.04, -0.02, -0.03, -0.03, -0.03, -0.03, -0.02, -0.01, -0.02,
+ 0.03, 0.05, 0.03, 0.03, 0.02, 0.03, 0.04, 0.06, 0.05,
+ 0.11, 0.11, 0.09, 0.08, 0.09, 0.09, 0.1, 0.11, 0.11,
+ 0.17, 0.16, 0.14, 0.13, 0.13, 0.13, 0.14, 0.15, 0.15,
+ 0.24, 0.23, 0.21, 0.19, 0.18, 0.2, 0.2, 0.2, 0.21
+ ],
+ "lut_bx":
+ [
+ -0.33, -0.22, -0.13, -0.05, 0.01, 0.09, 0.19, 0.25, 0.39,
+ -0.3, -0.19, -0.12, -0.04, 0.01, 0.06, 0.15, 0.23, 0.38,
+ -0.28, -0.18, -0.1, -0.04, 0.01, 0.06, 0.14, 0.22, 0.38,
+ -0.25, -0.16, -0.09, -0.04, 0.01, 0.06, 0.14, 0.23, 0.39,
+ -0.22, -0.15, -0.09, -0.03, 0.01, 0.06, 0.14, 0.23, 0.42,
+ -0.21, -0.14, -0.09, -0.04, 0.0, 0.06, 0.13, 0.21, 0.39,
+ -0.21, -0.14, -0.08, -0.04, 0.0, 0.06, 0.13, 0.2, 0.34,
+ -0.21, -0.14, -0.08, -0.04, 0.0, 0.06, 0.12, 0.19, 0.31,
+ -0.21, -0.15, -0.08, -0.03, 0.0, 0.06, 0.12, 0.17, 0.29
+ ],
+ "lut_by":
+ [
+ -0.3, -0.24, -0.21, -0.19, -0.19, -0.19, -0.23, -0.23, -0.27,
+ -0.23, -0.17, -0.14, -0.12, -0.12, -0.13, -0.15, -0.17, -0.21,
+ -0.17, -0.11, -0.09, -0.08, -0.08, -0.08, -0.1, -0.11, -0.16,
+ -0.09, -0.05, -0.04, -0.03, -0.03, -0.03, -0.04, -0.06, -0.11,
+ -0.03, -0.01, 0.0, 0.01, 0.01, 0.01, 0.0, -0.0, -0.05,
+ 0.02, 0.04, 0.04, 0.04, 0.05, 0.05, 0.05, 0.05, 0.01,
+ 0.07, 0.08, 0.09, 0.09, 0.1, 0.1, 0.1, 0.09, 0.06,
+ 0.11, 0.12, 0.11, 0.12, 0.12, 0.13, 0.13, 0.13, 0.1,
+ 0.16, 0.17, 0.16, 0.16, 0.16, 0.17, 0.17, 0.17, 0.15
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx477_noir.json b/src/ipa/rpi/pisp/data/imx477_noir.json
new file mode 100644
index 00000000..defc4f4d
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx477_noir.json
@@ -0,0 +1,1148 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 12000,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 740,
+ "reference_Y": 15051
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.809
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 204,
+ "slope": 0.0061
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.359, 2.354, 2.351, 2.351, 2.343, 2.337, 2.331, 2.325, 2.323, 2.321, 2.317, 2.315, 2.313, 2.313, 2.311, 2.312, 2.312, 2.313, 2.315, 2.315, 2.316, 2.317, 2.319, 2.323, 2.326, 2.329, 2.332, 2.332, 2.335, 2.337, 2.352, 2.363,
+ 2.352, 2.351, 2.349, 2.346, 2.342, 2.334, 2.328, 2.324, 2.321, 2.317, 2.315, 2.314, 2.312, 2.311, 2.311, 2.311, 2.311, 2.311, 2.312, 2.314, 2.315, 2.316, 2.317, 2.319, 2.324, 2.326, 2.328, 2.329, 2.331, 2.337, 2.348, 2.355,
+ 2.346, 2.346, 2.345, 2.344, 2.338, 2.329, 2.325, 2.319, 2.316, 2.314, 2.311, 2.309, 2.308, 2.306, 2.304, 2.304, 2.305, 2.307, 2.308, 2.309, 2.311, 2.311, 2.313, 2.316, 2.319, 2.322, 2.325, 2.326, 2.328, 2.335, 2.343, 2.349,
+ 2.342, 2.342, 2.341, 2.338, 2.332, 2.326, 2.319, 2.316, 2.312, 2.309, 2.308, 2.305, 2.303, 2.302, 2.301, 2.301, 2.302, 2.303, 2.304, 2.305, 2.305, 2.307, 2.311, 2.313, 2.315, 2.319, 2.321, 2.325, 2.328, 2.333, 2.338, 2.348,
+ 2.337, 2.337, 2.337, 2.336, 2.331, 2.322, 2.317, 2.312, 2.309, 2.307, 2.304, 2.302, 2.299, 2.299, 2.298, 2.298, 2.299, 2.299, 2.301, 2.302, 2.302, 2.304, 2.305, 2.309, 2.314, 2.316, 2.321, 2.324, 2.326, 2.329, 2.335, 2.343,
+ 2.335, 2.334, 2.333, 2.333, 2.326, 2.318, 2.313, 2.309, 2.306, 2.302, 2.299, 2.297, 2.297, 2.296, 2.295, 2.295, 2.294, 2.295, 2.296, 2.298, 2.298, 2.301, 2.303, 2.305, 2.311, 2.315, 2.319, 2.323, 2.325, 2.329, 2.333, 2.339,
+ 2.329, 2.331, 2.329, 2.329, 2.325, 2.315, 2.309, 2.306, 2.302, 2.299, 2.297, 2.295, 2.293, 2.292, 2.291, 2.291, 2.291, 2.291, 2.293, 2.294, 2.296, 2.298, 2.301, 2.304, 2.307, 2.313, 2.317, 2.319, 2.323, 2.327, 2.331, 2.339,
+ 2.329, 2.328, 2.328, 2.328, 2.321, 2.313, 2.307, 2.303, 2.299, 2.295, 2.294, 2.292, 2.289, 2.289, 2.288, 2.288, 2.288, 2.289, 2.289, 2.292, 2.294, 2.295, 2.297, 2.301, 2.306, 2.311, 2.315, 2.318, 2.319, 2.323, 2.329, 2.335,
+ 2.326, 2.327, 2.325, 2.325, 2.319, 2.311, 2.305, 2.299, 2.296, 2.293, 2.291, 2.289, 2.288, 2.287, 2.285, 2.285, 2.286, 2.288, 2.288, 2.289, 2.291, 2.294, 2.295, 2.298, 2.304, 2.308, 2.313, 2.315, 2.317, 2.319, 2.327, 2.335,
+ 2.325, 2.325, 2.323, 2.323, 2.317, 2.309, 2.303, 2.298, 2.294, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.284, 2.284, 2.285, 2.287, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.305, 2.309, 2.313, 2.315, 2.317, 2.325, 2.334,
+ 2.322, 2.324, 2.322, 2.322, 2.316, 2.306, 2.301, 2.296, 2.292, 2.289, 2.287, 2.286, 2.285, 2.284, 2.283, 2.283, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.293, 2.296, 2.301, 2.304, 2.308, 2.311, 2.312, 2.315, 2.323, 2.333,
+ 2.321, 2.323, 2.322, 2.322, 2.314, 2.306, 2.299, 2.294, 2.291, 2.288, 2.286, 2.285, 2.284, 2.282, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.289, 2.291, 2.291, 2.294, 2.297, 2.302, 2.306, 2.308, 2.311, 2.312, 2.322, 2.332,
+ 2.319, 2.321, 2.321, 2.321, 2.314, 2.305, 2.297, 2.293, 2.289, 2.287, 2.285, 2.284, 2.283, 2.281, 2.281, 2.281, 2.282, 2.283, 2.283, 2.285, 2.287, 2.289, 2.291, 2.292, 2.297, 2.301, 2.305, 2.307, 2.309, 2.312, 2.321, 2.333,
+ 2.319, 2.321, 2.319, 2.319, 2.314, 2.303, 2.296, 2.293, 2.289, 2.286, 2.285, 2.283, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.288, 2.289, 2.291, 2.296, 2.301, 2.305, 2.307, 2.308, 2.312, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.296, 2.291, 2.289, 2.286, 2.284, 2.282, 2.281, 2.281, 2.281, 2.281, 2.282, 2.282, 2.283, 2.284, 2.286, 2.287, 2.288, 2.291, 2.295, 2.299, 2.304, 2.306, 2.307, 2.311, 2.321, 2.332,
+ 2.319, 2.321, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.287, 2.285, 2.282, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.284, 2.285, 2.286, 2.288, 2.291, 2.295, 2.299, 2.303, 2.306, 2.307, 2.312, 2.321, 2.331,
+ 2.318, 2.319, 2.319, 2.319, 2.313, 2.303, 2.297, 2.292, 2.289, 2.286, 2.285, 2.282, 2.281, 2.281, 2.281, 2.282, 2.282, 2.282, 2.282, 2.283, 2.285, 2.286, 2.287, 2.291, 2.294, 2.298, 2.303, 2.306, 2.307, 2.311, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.302, 2.297, 2.292, 2.289, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.287, 2.289, 2.294, 2.297, 2.303, 2.305, 2.308, 2.313, 2.321, 2.331,
+ 2.319, 2.319, 2.319, 2.319, 2.313, 2.303, 2.299, 2.293, 2.291, 2.287, 2.285, 2.283, 2.282, 2.281, 2.281, 2.282, 2.283, 2.283, 2.283, 2.283, 2.285, 2.286, 2.288, 2.291, 2.294, 2.298, 2.304, 2.306, 2.308, 2.312, 2.322, 2.331,
+ 2.319, 2.321, 2.321, 2.321, 2.315, 2.305, 2.301, 2.295, 2.292, 2.289, 2.286, 2.285, 2.283, 2.282, 2.282, 2.282, 2.284, 2.283, 2.284, 2.284, 2.285, 2.287, 2.288, 2.291, 2.294, 2.299, 2.304, 2.306, 2.309, 2.313, 2.322, 2.334,
+ 2.321, 2.322, 2.322, 2.322, 2.317, 2.307, 2.301, 2.296, 2.292, 2.291, 2.288, 2.286, 2.285, 2.284, 2.283, 2.284, 2.285, 2.284, 2.285, 2.285, 2.287, 2.288, 2.289, 2.293, 2.297, 2.301, 2.305, 2.308, 2.311, 2.314, 2.323, 2.335,
+ 2.322, 2.324, 2.324, 2.324, 2.319, 2.309, 2.303, 2.297, 2.295, 2.292, 2.291, 2.288, 2.286, 2.286, 2.285, 2.286, 2.286, 2.286, 2.287, 2.288, 2.289, 2.289, 2.291, 2.294, 2.299, 2.302, 2.307, 2.311, 2.312, 2.316, 2.325, 2.335,
+ 2.324, 2.326, 2.325, 2.326, 2.321, 2.311, 2.305, 2.301, 2.297, 2.295, 2.293, 2.291, 2.289, 2.289, 2.288, 2.288, 2.287, 2.288, 2.289, 2.291, 2.292, 2.292, 2.295, 2.299, 2.301, 2.304, 2.309, 2.312, 2.315, 2.319, 2.327, 2.337,
+ 2.329, 2.329, 2.328, 2.328, 2.323, 2.315, 2.308, 2.304, 2.301, 2.298, 2.296, 2.294, 2.291, 2.291, 2.289, 2.291, 2.291, 2.291, 2.292, 2.293, 2.294, 2.295, 2.297, 2.299, 2.303, 2.308, 2.312, 2.315, 2.318, 2.321, 2.329, 2.339,
+ 2.329, 2.331, 2.332, 2.332, 2.326, 2.318, 2.311, 2.306, 2.304, 2.301, 2.299, 2.297, 2.295, 2.293, 2.292, 2.292, 2.292, 2.293, 2.294, 2.294, 2.296, 2.297, 2.299, 2.302, 2.306, 2.311, 2.315, 2.318, 2.319, 2.324, 2.332, 2.342,
+ 2.331, 2.333, 2.334, 2.334, 2.328, 2.321, 2.313, 2.308, 2.305, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.295, 2.294, 2.296, 2.296, 2.297, 2.298, 2.299, 2.302, 2.305, 2.308, 2.314, 2.317, 2.321, 2.323, 2.327, 2.334, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.309, 2.306, 2.304, 2.303, 2.301, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.309, 2.315, 2.319, 2.321, 2.324, 2.328, 2.337, 2.346,
+ 2.331, 2.332, 2.334, 2.334, 2.329, 2.321, 2.314, 2.311, 2.306, 2.304, 2.303, 2.302, 2.299, 2.297, 2.295, 2.295, 2.296, 2.297, 2.298, 2.298, 2.299, 2.301, 2.303, 2.306, 2.311, 2.314, 2.319, 2.323, 2.325, 2.329, 2.339, 2.348,
+ 2.329, 2.329, 2.329, 2.331, 2.326, 2.319, 2.312, 2.309, 2.304, 2.303, 2.302, 2.301, 2.298, 2.295, 2.294, 2.294, 2.295, 2.295, 2.296, 2.297, 2.299, 2.301, 2.302, 2.304, 2.308, 2.313, 2.319, 2.322, 2.325, 2.329, 2.339, 2.351,
+ 2.329, 2.329, 2.329, 2.329, 2.326, 2.317, 2.311, 2.308, 2.303, 2.302, 2.301, 2.298, 2.296, 2.295, 2.294, 2.294, 2.294, 2.294, 2.296, 2.297, 2.298, 2.299, 2.301, 2.304, 2.307, 2.312, 2.318, 2.322, 2.326, 2.331, 2.341, 2.355,
+ 2.339, 2.332, 2.331, 2.331, 2.327, 2.323, 2.316, 2.309, 2.306, 2.302, 2.301, 2.299, 2.297, 2.296, 2.295, 2.294, 2.294, 2.296, 2.297, 2.297, 2.299, 2.301, 2.303, 2.306, 2.308, 2.317, 2.322, 2.325, 2.329, 2.341, 2.353, 2.361,
+ 2.347, 2.347, 2.345, 2.343, 2.338, 2.332, 2.326, 2.322, 2.321, 2.318, 2.316, 2.315, 2.313, 2.312, 2.311, 2.311, 2.311, 2.311, 2.312, 2.315, 2.317, 2.318, 2.319, 2.323, 2.324, 2.329, 2.334, 2.337, 2.344, 2.347, 2.361, 2.364
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 3.869, 3.852, 3.844, 3.842, 3.836, 3.821, 3.807, 3.796, 3.789, 3.784, 3.778, 3.775, 3.769, 3.768, 3.765, 3.765, 3.767, 3.769, 3.772, 3.774, 3.773, 3.775, 3.779, 3.787, 3.793, 3.801, 3.806, 3.804, 3.813, 3.819, 3.855, 3.879,
+ 3.854, 3.844, 3.837, 3.836, 3.824, 3.811, 3.797, 3.789, 3.784, 3.777, 3.774, 3.769, 3.764, 3.758, 3.757, 3.758, 3.758, 3.761, 3.763, 3.764, 3.765, 3.766, 3.772, 3.778, 3.787, 3.792, 3.794, 3.798, 3.802, 3.815, 3.839, 3.873,
+ 3.838, 3.831, 3.826, 3.823, 3.813, 3.799, 3.787, 3.781, 3.773, 3.768, 3.763, 3.759, 3.753, 3.749, 3.745, 3.745, 3.745, 3.752, 3.754, 3.757, 3.757, 3.759, 3.763, 3.769, 3.773, 3.781, 3.786, 3.792, 3.798, 3.811, 3.831, 3.861,
+ 3.833, 3.822, 3.817, 3.816, 3.804, 3.788, 3.779, 3.772, 3.766, 3.759, 3.755, 3.749, 3.744, 3.741, 3.738, 3.739, 3.739, 3.741, 3.743, 3.747, 3.749, 3.751, 3.756, 3.764, 3.769, 3.776, 3.783, 3.789, 3.798, 3.809, 3.821, 3.855,
+ 3.824, 3.818, 3.808, 3.808, 3.797, 3.781, 3.772, 3.764, 3.757, 3.752, 3.747, 3.743, 3.737, 3.735, 3.733, 3.733, 3.733, 3.735, 3.737, 3.738, 3.741, 3.746, 3.749, 3.755, 3.766, 3.771, 3.781, 3.789, 3.794, 3.806, 3.818, 3.849,
+ 3.815, 3.808, 3.799, 3.801, 3.787, 3.775, 3.767, 3.757, 3.751, 3.745, 3.738, 3.734, 3.732, 3.727, 3.725, 3.723, 3.722, 3.722, 3.726, 3.729, 3.734, 3.738, 3.744, 3.749, 3.759, 3.769, 3.781, 3.788, 3.792, 3.799, 3.811, 3.841,
+ 3.804, 3.799, 3.793, 3.793, 3.783, 3.771, 3.759, 3.751, 3.744, 3.735, 3.732, 3.727, 3.723, 3.721, 3.719, 3.716, 3.716, 3.716, 3.718, 3.722, 3.727, 3.731, 3.737, 3.746, 3.756, 3.767, 3.776, 3.782, 3.788, 3.795, 3.808, 3.831,
+ 3.802, 3.797, 3.787, 3.787, 3.779, 3.762, 3.753, 3.744, 3.734, 3.727, 3.725, 3.721, 3.716, 3.714, 3.709, 3.709, 3.711, 3.711, 3.712, 3.717, 3.722, 3.725, 3.731, 3.739, 3.752, 3.762, 3.772, 3.778, 3.779, 3.789, 3.798, 3.826,
+ 3.791, 3.789, 3.784, 3.784, 3.775, 3.759, 3.746, 3.735, 3.729, 3.724, 3.718, 3.714, 3.712, 3.707, 3.704, 3.704, 3.706, 3.708, 3.709, 3.711, 3.716, 3.722, 3.726, 3.735, 3.746, 3.754, 3.767, 3.774, 3.777, 3.781, 3.794, 3.824,
+ 3.789, 3.784, 3.779, 3.781, 3.771, 3.753, 3.741, 3.732, 3.725, 3.719, 3.715, 3.711, 3.707, 3.704, 3.701, 3.701, 3.702, 3.704, 3.708, 3.709, 3.713, 3.718, 3.724, 3.731, 3.742, 3.749, 3.761, 3.768, 3.772, 3.778, 3.791, 3.822,
+ 3.789, 3.781, 3.777, 3.777, 3.764, 3.749, 3.739, 3.729, 3.722, 3.718, 3.711, 3.708, 3.705, 3.701, 3.699, 3.699, 3.699, 3.701, 3.705, 3.707, 3.711, 3.715, 3.721, 3.727, 3.738, 3.746, 3.757, 3.763, 3.765, 3.773, 3.788, 3.821,
+ 3.785, 3.779, 3.774, 3.774, 3.764, 3.747, 3.736, 3.726, 3.719, 3.711, 3.709, 3.706, 3.701, 3.698, 3.696, 3.695, 3.695, 3.698, 3.702, 3.704, 3.707, 3.712, 3.718, 3.725, 3.734, 3.741, 3.753, 3.756, 3.759, 3.764, 3.784, 3.818,
+ 3.779, 3.776, 3.773, 3.773, 3.759, 3.744, 3.733, 3.724, 3.714, 3.709, 3.706, 3.704, 3.699, 3.696, 3.694, 3.694, 3.694, 3.697, 3.701, 3.703, 3.706, 3.709, 3.714, 3.721, 3.731, 3.737, 3.749, 3.753, 3.758, 3.762, 3.783, 3.819,
+ 3.779, 3.776, 3.769, 3.769, 3.757, 3.741, 3.729, 3.721, 3.712, 3.708, 3.705, 3.701, 3.697, 3.695, 3.694, 3.694, 3.695, 3.696, 3.698, 3.702, 3.705, 3.709, 3.712, 3.717, 3.728, 3.736, 3.749, 3.752, 3.756, 3.761, 3.781, 3.815,
+ 3.779, 3.773, 3.768, 3.768, 3.756, 3.738, 3.731, 3.719, 3.711, 3.707, 3.703, 3.698, 3.695, 3.694, 3.694, 3.695, 3.695, 3.695, 3.696, 3.702, 3.705, 3.708, 3.712, 3.717, 3.728, 3.736, 3.747, 3.751, 3.754, 3.761, 3.781, 3.815,
+ 3.782, 3.773, 3.767, 3.767, 3.755, 3.738, 3.728, 3.721, 3.711, 3.707, 3.701, 3.698, 3.695, 3.693, 3.694, 3.696, 3.695, 3.695, 3.695, 3.701, 3.703, 3.706, 3.711, 3.715, 3.726, 3.735, 3.745, 3.751, 3.754, 3.763, 3.779, 3.815,
+ 3.781, 3.771, 3.767, 3.767, 3.754, 3.739, 3.726, 3.721, 3.712, 3.706, 3.701, 3.698, 3.695, 3.693, 3.693, 3.695, 3.695, 3.695, 3.696, 3.698, 3.703, 3.705, 3.709, 3.715, 3.725, 3.734, 3.745, 3.751, 3.755, 3.762, 3.783, 3.818,
+ 3.781, 3.774, 3.767, 3.767, 3.755, 3.741, 3.729, 3.722, 3.712, 3.708, 3.701, 3.699, 3.695, 3.693, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.725, 3.732, 3.746, 3.751, 3.756, 3.763, 3.783, 3.821,
+ 3.781, 3.774, 3.769, 3.769, 3.756, 3.741, 3.731, 3.724, 3.713, 3.711, 3.707, 3.699, 3.697, 3.694, 3.693, 3.694, 3.695, 3.695, 3.697, 3.698, 3.702, 3.704, 3.709, 3.713, 3.724, 3.734, 3.747, 3.751, 3.756, 3.765, 3.784, 3.821,
+ 3.784, 3.776, 3.773, 3.773, 3.759, 3.742, 3.733, 3.726, 3.719, 3.711, 3.709, 3.703, 3.698, 3.695, 3.694, 3.695, 3.697, 3.696, 3.698, 3.699, 3.703, 3.706, 3.711, 3.714, 3.727, 3.735, 3.746, 3.751, 3.757, 3.766, 3.787, 3.822,
+ 3.786, 3.783, 3.774, 3.774, 3.766, 3.747, 3.737, 3.727, 3.722, 3.716, 3.711, 3.706, 3.702, 3.698, 3.697, 3.698, 3.699, 3.699, 3.701, 3.703, 3.706, 3.711, 3.713, 3.719, 3.731, 3.739, 3.748, 3.753, 3.761, 3.769, 3.789, 3.826,
+ 3.786, 3.784, 3.779, 3.779, 3.769, 3.751, 3.742, 3.732, 3.725, 3.719, 3.715, 3.711, 3.706, 3.704, 3.701, 3.701, 3.702, 3.702, 3.705, 3.707, 3.712, 3.714, 3.717, 3.724, 3.733, 3.743, 3.749, 3.758, 3.764, 3.769, 3.791, 3.826,
+ 3.793, 3.787, 3.782, 3.782, 3.774, 3.756, 3.747, 3.737, 3.729, 3.725, 3.719, 3.715, 3.712, 3.708, 3.707, 3.706, 3.707, 3.708, 3.709, 3.713, 3.714, 3.717, 3.723, 3.729, 3.736, 3.747, 3.757, 3.764, 3.768, 3.774, 3.794, 3.829,
+ 3.794, 3.791, 3.786, 3.786, 3.779, 3.762, 3.751, 3.742, 3.735, 3.729, 3.725, 3.719, 3.716, 3.711, 3.709, 3.709, 3.709, 3.711, 3.716, 3.717, 3.721, 3.723, 3.726, 3.732, 3.741, 3.752, 3.761, 3.767, 3.773, 3.779, 3.801, 3.829,
+ 3.802, 3.798, 3.793, 3.793, 3.779, 3.766, 3.754, 3.746, 3.741, 3.736, 3.731, 3.726, 3.719, 3.717, 3.716, 3.715, 3.716, 3.717, 3.719, 3.721, 3.724, 3.726, 3.731, 3.737, 3.744, 3.756, 3.766, 3.772, 3.776, 3.784, 3.807, 3.839,
+ 3.805, 3.799, 3.795, 3.795, 3.784, 3.767, 3.757, 3.749, 3.744, 3.739, 3.736, 3.731, 3.726, 3.722, 3.719, 3.719, 3.719, 3.721, 3.723, 3.725, 3.727, 3.732, 3.738, 3.742, 3.751, 3.761, 3.771, 3.775, 3.782, 3.789, 3.811, 3.841,
+ 3.804, 3.801, 3.799, 3.799, 3.787, 3.772, 3.761, 3.752, 3.746, 3.742, 3.739, 3.735, 3.729, 3.726, 3.723, 3.724, 3.725, 3.726, 3.727, 3.728, 3.732, 3.736, 3.739, 3.745, 3.754, 3.765, 3.775, 3.779, 3.785, 3.795, 3.816, 3.844,
+ 3.801, 3.799, 3.796, 3.796, 3.787, 3.773, 3.761, 3.753, 3.746, 3.743, 3.739, 3.735, 3.731, 3.726, 3.725, 3.725, 3.725, 3.726, 3.727, 3.729, 3.733, 3.736, 3.741, 3.745, 3.755, 3.766, 3.776, 3.783, 3.786, 3.797, 3.819, 3.851,
+ 3.799, 3.795, 3.788, 3.788, 3.783, 3.772, 3.759, 3.749, 3.744, 3.738, 3.735, 3.733, 3.726, 3.724, 3.722, 3.722, 3.723, 3.724, 3.725, 3.727, 3.729, 3.733, 3.736, 3.742, 3.754, 3.762, 3.772, 3.779, 3.784, 3.796, 3.821, 3.859,
+ 3.799, 3.789, 3.787, 3.788, 3.779, 3.766, 3.755, 3.749, 3.742, 3.736, 3.733, 3.727, 3.723, 3.722, 3.721, 3.719, 3.719, 3.721, 3.725, 3.726, 3.728, 3.732, 3.734, 3.741, 3.747, 3.758, 3.771, 3.778, 3.785, 3.796, 3.825, 3.862,
+ 3.824, 3.799, 3.789, 3.789, 3.788, 3.777, 3.761, 3.751, 3.743, 3.739, 3.736, 3.728, 3.726, 3.725, 3.721, 3.719, 3.721, 3.723, 3.727, 3.728, 3.729, 3.733, 3.737, 3.744, 3.755, 3.769, 3.776, 3.784, 3.793, 3.819, 3.863, 3.877,
+ 3.833, 3.833, 3.833, 3.842, 3.825, 3.815, 3.807, 3.799, 3.792, 3.788, 3.785, 3.782, 3.778, 3.777, 3.773, 3.772, 3.772, 3.774, 3.778, 3.779, 3.779, 3.785, 3.792, 3.798, 3.803, 3.811, 3.822, 3.834, 3.843, 3.846, 3.877, 3.886
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.616, 2.616, 2.618, 2.621, 2.619, 2.618, 2.615, 2.615, 2.613, 2.611, 2.609, 2.609, 2.609, 2.611, 2.611, 2.611, 2.611, 2.609, 2.608, 2.608, 2.611, 2.613, 2.613, 2.614, 2.614, 2.615, 2.615, 2.622, 2.624, 2.621, 2.624, 2.641,
+ 2.616, 2.618, 2.621, 2.623, 2.623, 2.619, 2.618, 2.616, 2.616, 2.613, 2.611, 2.611, 2.611, 2.611, 2.612, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.612, 2.613, 2.612, 2.613, 2.615, 2.617, 2.621, 2.621, 2.619, 2.621, 2.641,
+ 2.621, 2.624, 2.627, 2.627, 2.625, 2.623, 2.621, 2.619, 2.618, 2.618, 2.618, 2.617, 2.616, 2.616, 2.615, 2.613, 2.612, 2.613, 2.613, 2.614, 2.614, 2.613, 2.614, 2.613, 2.614, 2.617, 2.619, 2.621, 2.621, 2.619, 2.623, 2.643,
+ 2.626, 2.627, 2.628, 2.629, 2.628, 2.625, 2.622, 2.621, 2.621, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.616, 2.616, 2.616, 2.618, 2.618, 2.617, 2.617, 2.618, 2.619, 2.621, 2.623, 2.624, 2.626, 2.625, 2.624, 2.625, 2.654,
+ 2.627, 2.628, 2.628, 2.628, 2.626, 2.623, 2.622, 2.622, 2.622, 2.622, 2.621, 2.621, 2.619, 2.617, 2.617, 2.616, 2.617, 2.617, 2.618, 2.619, 2.618, 2.618, 2.618, 2.621, 2.622, 2.624, 2.626, 2.627, 2.627, 2.626, 2.628, 2.655,
+ 2.625, 2.626, 2.627, 2.626, 2.625, 2.623, 2.622, 2.621, 2.622, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.616, 2.616, 2.616, 2.616, 2.616, 2.617, 2.618, 2.619, 2.621, 2.622, 2.624, 2.626, 2.628, 2.628, 2.629, 2.629, 2.655,
+ 2.626, 2.625, 2.626, 2.625, 2.625, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.616, 2.614, 2.613, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.618, 2.619, 2.621, 2.623, 2.624, 2.627, 2.629, 2.631, 2.629, 2.631, 2.651,
+ 2.625, 2.625, 2.625, 2.624, 2.623, 2.623, 2.622, 2.622, 2.622, 2.621, 2.619, 2.617, 2.614, 2.613, 2.612, 2.611, 2.611, 2.612, 2.612, 2.613, 2.616, 2.618, 2.619, 2.622, 2.624, 2.626, 2.628, 2.631, 2.631, 2.631, 2.631, 2.651,
+ 2.625, 2.625, 2.624, 2.623, 2.622, 2.622, 2.622, 2.622, 2.622, 2.621, 2.617, 2.615, 2.613, 2.612, 2.611, 2.611, 2.611, 2.611, 2.611, 2.613, 2.615, 2.618, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.624, 2.624, 2.622, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.618, 2.616, 2.614, 2.612, 2.611, 2.609, 2.609, 2.608, 2.609, 2.611, 2.611, 2.615, 2.617, 2.619, 2.621, 2.625, 2.628, 2.631, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.622, 2.623, 2.622, 2.622, 2.621, 2.619, 2.619, 2.619, 2.618, 2.616, 2.614, 2.613, 2.611, 2.609, 2.608, 2.606, 2.607, 2.607, 2.609, 2.611, 2.615, 2.617, 2.619, 2.622, 2.626, 2.629, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.622, 2.622, 2.621, 2.619, 2.619, 2.618, 2.617, 2.614, 2.613, 2.611, 2.611, 2.607, 2.606, 2.605, 2.604, 2.605, 2.607, 2.609, 2.613, 2.616, 2.619, 2.622, 2.627, 2.631, 2.632, 2.632, 2.631, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.621, 2.621, 2.619, 2.617, 2.616, 2.615, 2.613, 2.609, 2.607, 2.604, 2.602, 2.601, 2.602, 2.603, 2.605, 2.609, 2.612, 2.616, 2.619, 2.624, 2.628, 2.631, 2.632, 2.633, 2.629, 2.627, 2.627, 2.651,
+ 2.619, 2.621, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.615, 2.614, 2.612, 2.608, 2.603, 2.601, 2.598, 2.597, 2.599, 2.602, 2.605, 2.608, 2.611, 2.615, 2.622, 2.625, 2.629, 2.631, 2.631, 2.633, 2.631, 2.627, 2.627, 2.651,
+ 2.621, 2.622, 2.623, 2.623, 2.622, 2.621, 2.618, 2.617, 2.616, 2.614, 2.611, 2.606, 2.601, 2.598, 2.595, 2.595, 2.597, 2.601, 2.604, 2.608, 2.612, 2.615, 2.623, 2.627, 2.629, 2.631, 2.631, 2.632, 2.631, 2.628, 2.628, 2.651,
+ 2.622, 2.623, 2.624, 2.624, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.609, 2.606, 2.601, 2.596, 2.594, 2.594, 2.596, 2.599, 2.603, 2.609, 2.613, 2.617, 2.623, 2.627, 2.629, 2.631, 2.632, 2.632, 2.631, 2.629, 2.631, 2.651,
+ 2.623, 2.625, 2.625, 2.624, 2.621, 2.621, 2.619, 2.617, 2.616, 2.613, 2.608, 2.605, 2.601, 2.595, 2.593, 2.593, 2.595, 2.598, 2.604, 2.609, 2.615, 2.619, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.632, 2.629, 2.631, 2.651,
+ 2.624, 2.626, 2.626, 2.623, 2.621, 2.619, 2.618, 2.617, 2.615, 2.612, 2.608, 2.605, 2.601, 2.597, 2.595, 2.595, 2.596, 2.598, 2.605, 2.609, 2.616, 2.621, 2.626, 2.627, 2.629, 2.631, 2.633, 2.633, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.618, 2.617, 2.614, 2.612, 2.609, 2.606, 2.602, 2.599, 2.598, 2.597, 2.598, 2.602, 2.607, 2.612, 2.619, 2.621, 2.626, 2.628, 2.629, 2.632, 2.633, 2.634, 2.633, 2.631, 2.631, 2.655,
+ 2.624, 2.625, 2.625, 2.623, 2.621, 2.621, 2.618, 2.617, 2.614, 2.612, 2.611, 2.608, 2.604, 2.602, 2.599, 2.599, 2.603, 2.606, 2.611, 2.616, 2.621, 2.624, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.634, 2.633, 2.633, 2.656,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.621, 2.619, 2.617, 2.615, 2.613, 2.611, 2.611, 2.607, 2.604, 2.604, 2.604, 2.606, 2.609, 2.613, 2.619, 2.622, 2.625, 2.628, 2.631, 2.632, 2.633, 2.633, 2.636, 2.636, 2.634, 2.634, 2.658,
+ 2.623, 2.624, 2.625, 2.623, 2.622, 2.619, 2.618, 2.616, 2.614, 2.613, 2.612, 2.611, 2.609, 2.608, 2.607, 2.608, 2.609, 2.613, 2.617, 2.621, 2.623, 2.626, 2.629, 2.631, 2.632, 2.633, 2.634, 2.635, 2.636, 2.636, 2.636, 2.661,
+ 2.623, 2.624, 2.625, 2.625, 2.623, 2.621, 2.619, 2.616, 2.615, 2.614, 2.613, 2.612, 2.612, 2.611, 2.611, 2.611, 2.614, 2.615, 2.619, 2.622, 2.625, 2.627, 2.631, 2.632, 2.633, 2.635, 2.635, 2.637, 2.637, 2.636, 2.637, 2.661,
+ 2.623, 2.624, 2.625, 2.626, 2.624, 2.621, 2.619, 2.617, 2.616, 2.615, 2.615, 2.614, 2.614, 2.614, 2.614, 2.614, 2.616, 2.619, 2.621, 2.623, 2.626, 2.628, 2.631, 2.632, 2.634, 2.635, 2.636, 2.637, 2.638, 2.637, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.627, 2.626, 2.623, 2.619, 2.619, 2.618, 2.618, 2.618, 2.617, 2.617, 2.616, 2.616, 2.616, 2.619, 2.622, 2.623, 2.625, 2.628, 2.628, 2.631, 2.632, 2.634, 2.636, 2.638, 2.639, 2.639, 2.638, 2.638, 2.661,
+ 2.625, 2.626, 2.627, 2.628, 2.626, 2.623, 2.621, 2.619, 2.619, 2.619, 2.619, 2.619, 2.619, 2.618, 2.618, 2.619, 2.623, 2.624, 2.625, 2.627, 2.629, 2.629, 2.632, 2.633, 2.635, 2.638, 2.639, 2.639, 2.639, 2.636, 2.636, 2.662,
+ 2.625, 2.627, 2.628, 2.628, 2.626, 2.624, 2.623, 2.622, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.621, 2.624, 2.624, 2.625, 2.627, 2.628, 2.631, 2.631, 2.632, 2.634, 2.636, 2.639, 2.639, 2.641, 2.639, 2.635, 2.635, 2.663,
+ 2.625, 2.626, 2.628, 2.628, 2.627, 2.625, 2.624, 2.623, 2.623, 2.622, 2.623, 2.624, 2.624, 2.625, 2.625, 2.625, 2.625, 2.626, 2.627, 2.629, 2.631, 2.632, 2.633, 2.635, 2.638, 2.641, 2.642, 2.643, 2.642, 2.636, 2.636, 2.665,
+ 2.624, 2.626, 2.628, 2.628, 2.628, 2.626, 2.624, 2.624, 2.623, 2.623, 2.623, 2.625, 2.627, 2.627, 2.626, 2.626, 2.626, 2.627, 2.628, 2.629, 2.632, 2.633, 2.635, 2.637, 2.639, 2.642, 2.644, 2.644, 2.642, 2.638, 2.638, 2.665,
+ 2.623, 2.625, 2.626, 2.627, 2.626, 2.626, 2.624, 2.623, 2.623, 2.623, 2.623, 2.623, 2.626, 2.627, 2.626, 2.626, 2.626, 2.626, 2.628, 2.628, 2.629, 2.631, 2.634, 2.636, 2.639, 2.642, 2.644, 2.643, 2.641, 2.637, 2.638, 2.659,
+ 2.623, 2.627, 2.627, 2.627, 2.627, 2.628, 2.627, 2.624, 2.624, 2.623, 2.624, 2.624, 2.628, 2.628, 2.627, 2.628, 2.628, 2.628, 2.629, 2.629, 2.631, 2.635, 2.637, 2.639, 2.641, 2.643, 2.646, 2.645, 2.643, 2.641, 2.654, 2.659,
+ 2.642, 2.641, 2.643, 2.645, 2.645, 2.644, 2.644, 2.643, 2.643, 2.642, 2.642, 2.642, 2.643, 2.644, 2.644, 2.644, 2.646, 2.646, 2.647, 2.649, 2.651, 2.652, 2.654, 2.656, 2.658, 2.661, 2.661, 2.661, 2.659, 2.654, 2.659, 2.659
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.391, 1.394, 1.395, 1.396, 1.398, 1.398, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.398, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.398, 1.399, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.399, 1.397, 1.399, 1.402,
+ 1.393, 1.395, 1.396, 1.398, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.401, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.399, 1.398, 1.398, 1.399, 1.401, 1.401, 1.399, 1.398, 1.399, 1.402,
+ 1.398, 1.401, 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.401, 1.401, 1.399, 1.399, 1.401, 1.401, 1.401, 1.401, 1.399, 1.401, 1.406,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.402, 1.403, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.405, 1.404, 1.404, 1.404, 1.404, 1.405, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.404, 1.405, 1.412,
+ 1.401, 1.401, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.406, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.405, 1.404, 1.404, 1.404, 1.404, 1.404, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.407, 1.408, 1.408, 1.407, 1.405, 1.405, 1.412,
+ 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.404, 1.405, 1.406, 1.405, 1.405, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.402, 1.402, 1.402, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.403, 1.402, 1.402, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.408, 1.407, 1.405, 1.405, 1.414,
+ 1.402, 1.402, 1.402, 1.402, 1.403, 1.403, 1.405, 1.405, 1.405, 1.405, 1.404, 1.404, 1.403, 1.402, 1.402, 1.401, 1.401, 1.402, 1.403, 1.403, 1.404, 1.405, 1.406, 1.407, 1.409, 1.409, 1.409, 1.409, 1.407, 1.405, 1.405, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.403, 1.404, 1.405, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.408, 1.405, 1.405, 1.413,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.405, 1.404, 1.402, 1.401, 1.399, 1.398, 1.398, 1.399, 1.399, 1.401, 1.403, 1.404, 1.405, 1.407, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.401, 1.398, 1.397, 1.397, 1.398, 1.399, 1.401, 1.403, 1.404, 1.405, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.399, 1.401, 1.403, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.408, 1.406, 1.406, 1.413,
+ 1.403, 1.404, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.404, 1.403, 1.402, 1.399, 1.397, 1.396, 1.396, 1.397, 1.398, 1.401, 1.403, 1.406, 1.407, 1.409, 1.409, 1.411, 1.409, 1.409, 1.409, 1.408, 1.407, 1.407, 1.413,
+ 1.403, 1.404, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.404, 1.404, 1.403, 1.402, 1.399, 1.398, 1.397, 1.397, 1.398, 1.399, 1.402, 1.404, 1.406, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.414,
+ 1.403, 1.403, 1.404, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.402, 1.401, 1.399, 1.398, 1.398, 1.398, 1.401, 1.403, 1.404, 1.408, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.401, 1.401, 1.399, 1.399, 1.401, 1.402, 1.404, 1.407, 1.408, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.407, 1.407, 1.415,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.403, 1.403, 1.403, 1.402, 1.401, 1.401, 1.401, 1.402, 1.404, 1.406, 1.407, 1.408, 1.409, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.409, 1.408, 1.408, 1.415,
+ 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.405, 1.406, 1.408, 1.408, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.409, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.402, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.403, 1.404, 1.404, 1.404, 1.405, 1.406, 1.407, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.416,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.407, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.405, 1.406, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.409, 1.408, 1.408, 1.417,
+ 1.403, 1.403, 1.403, 1.403, 1.404, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.406, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.416,
+ 1.402, 1.403, 1.403, 1.403, 1.404, 1.404, 1.404, 1.404, 1.405, 1.405, 1.406, 1.407, 1.407, 1.407, 1.408, 1.409, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.409, 1.407, 1.407, 1.417,
+ 1.402, 1.403, 1.403, 1.404, 1.404, 1.404, 1.405, 1.405, 1.405, 1.406, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.415,
+ 1.402, 1.402, 1.403, 1.403, 1.404, 1.404, 1.405, 1.405, 1.405, 1.405, 1.406, 1.407, 1.408, 1.408, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.412, 1.411, 1.409, 1.407, 1.407, 1.413,
+ 1.402, 1.402, 1.403, 1.403, 1.405, 1.406, 1.406, 1.406, 1.406, 1.406, 1.407, 1.408, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.412, 1.412, 1.413, 1.413, 1.411, 1.408, 1.411, 1.413,
+ 1.406, 1.406, 1.408, 1.408, 1.409, 1.409, 1.411, 1.411, 1.411, 1.411, 1.411, 1.411, 1.414, 1.414, 1.414, 1.414, 1.415, 1.415, 1.415, 1.415, 1.416, 1.416, 1.416, 1.417, 1.418, 1.418, 1.417, 1.417, 1.414, 1.411, 1.413, 1.413
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.554, 1.522, 1.466, 1.422, 1.385, 1.351, 1.322, 1.294, 1.269, 1.246, 1.228, 1.214, 1.207, 1.202, 1.199, 1.199, 1.199, 1.199, 1.202, 1.207, 1.218, 1.235, 1.255, 1.279, 1.305, 1.333, 1.365, 1.402, 1.447, 1.508, 1.602, 1.638,
+ 1.522, 1.478, 1.431, 1.391, 1.355, 1.323, 1.298, 1.271, 1.247, 1.228, 1.212, 1.199, 1.187, 1.179, 1.173, 1.172, 1.172, 1.174, 1.179, 1.189, 1.201, 1.216, 1.235, 1.256, 1.282, 1.308, 1.335, 1.368, 1.411, 1.461, 1.535, 1.602,
+ 1.479, 1.449, 1.407, 1.367, 1.332, 1.301, 1.271, 1.247, 1.226, 1.208, 1.191, 1.178, 1.166, 1.158, 1.153, 1.151, 1.151, 1.153, 1.159, 1.168, 1.179, 1.194, 1.212, 1.234, 1.256, 1.282, 1.311, 1.343, 1.382, 1.427, 1.489, 1.535,
+ 1.454, 1.423, 1.383, 1.345, 1.309, 1.278, 1.249, 1.226, 1.206, 1.187, 1.171, 1.158, 1.146, 1.138, 1.132, 1.129, 1.129, 1.133, 1.139, 1.147, 1.159, 1.173, 1.191, 1.212, 1.234, 1.261, 1.288, 1.321, 1.357, 1.401, 1.455, 1.489,
+ 1.433, 1.401, 1.362, 1.325, 1.289, 1.258, 1.231, 1.206, 1.187, 1.169, 1.153, 1.138, 1.129, 1.121, 1.115, 1.112, 1.112, 1.114, 1.121, 1.129, 1.141, 1.155, 1.172, 1.191, 1.214, 1.241, 1.269, 1.301, 1.337, 1.377, 1.428, 1.457,
+ 1.415, 1.382, 1.343, 1.306, 1.273, 1.241, 1.213, 1.189, 1.169, 1.153, 1.137, 1.123, 1.112, 1.105, 1.097, 1.095, 1.095, 1.098, 1.103, 1.112, 1.124, 1.139, 1.155, 1.173, 1.197, 1.222, 1.252, 1.282, 1.317, 1.356, 1.405, 1.434,
+ 1.398, 1.363, 1.325, 1.289, 1.256, 1.224, 1.198, 1.175, 1.155, 1.137, 1.123, 1.108, 1.097, 1.089, 1.083, 1.079, 1.079, 1.083, 1.088, 1.097, 1.109, 1.124, 1.139, 1.158, 1.181, 1.206, 1.234, 1.266, 1.299, 1.339, 1.384, 1.415,
+ 1.382, 1.347, 1.309, 1.274, 1.242, 1.211, 1.185, 1.162, 1.142, 1.124, 1.108, 1.095, 1.083, 1.075, 1.069, 1.066, 1.066, 1.068, 1.074, 1.083, 1.096, 1.109, 1.125, 1.145, 1.166, 1.191, 1.219, 1.251, 1.285, 1.324, 1.367, 1.399,
+ 1.369, 1.334, 1.296, 1.261, 1.228, 1.199, 1.173, 1.151, 1.131, 1.112, 1.095, 1.083, 1.071, 1.062, 1.056, 1.053, 1.053, 1.055, 1.061, 1.069, 1.083, 1.096, 1.112, 1.132, 1.153, 1.178, 1.206, 1.237, 1.271, 1.309, 1.353, 1.385,
+ 1.359, 1.321, 1.284, 1.251, 1.217, 1.189, 1.164, 1.141, 1.121, 1.102, 1.086, 1.071, 1.061, 1.049, 1.045, 1.042, 1.042, 1.043, 1.051, 1.061, 1.069, 1.085, 1.101, 1.121, 1.143, 1.167, 1.195, 1.225, 1.259, 1.298, 1.341, 1.375,
+ 1.351, 1.312, 1.275, 1.241, 1.209, 1.181, 1.155, 1.133, 1.112, 1.092, 1.076, 1.061, 1.049, 1.041, 1.034, 1.032, 1.032, 1.035, 1.041, 1.051, 1.061, 1.075, 1.092, 1.112, 1.133, 1.158, 1.185, 1.216, 1.249, 1.288, 1.331, 1.364,
+ 1.344, 1.303, 1.267, 1.233, 1.201, 1.173, 1.147, 1.124, 1.104, 1.085, 1.067, 1.053, 1.041, 1.033, 1.024, 1.022, 1.022, 1.025, 1.034, 1.041, 1.053, 1.066, 1.083, 1.103, 1.126, 1.149, 1.177, 1.207, 1.241, 1.279, 1.321, 1.357,
+ 1.339, 1.297, 1.261, 1.226, 1.194, 1.166, 1.142, 1.119, 1.098, 1.078, 1.061, 1.046, 1.034, 1.024, 1.017, 1.014, 1.014, 1.017, 1.025, 1.034, 1.046, 1.059, 1.077, 1.096, 1.118, 1.143, 1.169, 1.201, 1.235, 1.273, 1.314, 1.352,
+ 1.337, 1.293, 1.256, 1.223, 1.191, 1.163, 1.136, 1.114, 1.093, 1.074, 1.056, 1.041, 1.027, 1.017, 1.012, 1.006, 1.006, 1.013, 1.017, 1.028, 1.041, 1.055, 1.072, 1.092, 1.114, 1.138, 1.165, 1.195, 1.229, 1.268, 1.309, 1.348,
+ 1.337, 1.291, 1.253, 1.219, 1.187, 1.159, 1.133, 1.109, 1.089, 1.071, 1.053, 1.037, 1.023, 1.012, 1.006, 1.002, 1.003, 1.006, 1.013, 1.023, 1.038, 1.052, 1.069, 1.089, 1.111, 1.135, 1.161, 1.192, 1.226, 1.264, 1.306, 1.348,
+ 1.337, 1.291, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.035, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.341, 1.292, 1.253, 1.218, 1.186, 1.157, 1.132, 1.109, 1.088, 1.068, 1.049, 1.034, 1.021, 1.009, 1.001, 1.001, 1.001, 1.003, 1.011, 1.021, 1.035, 1.051, 1.069, 1.087, 1.109, 1.133, 1.161, 1.189, 1.224, 1.262, 1.304, 1.347,
+ 1.348, 1.298, 1.255, 1.219, 1.188, 1.159, 1.134, 1.111, 1.088, 1.069, 1.051, 1.035, 1.021, 1.009, 1.003, 1.001, 1.002, 1.004, 1.011, 1.022, 1.036, 1.053, 1.071, 1.089, 1.111, 1.135, 1.162, 1.191, 1.226, 1.264, 1.306, 1.347,
+ 1.354, 1.306, 1.258, 1.222, 1.191, 1.162, 1.135, 1.113, 1.092, 1.073, 1.054, 1.038, 1.024, 1.014, 1.008, 1.003, 1.004, 1.008, 1.014, 1.026, 1.039, 1.056, 1.073, 1.093, 1.115, 1.139, 1.165, 1.195, 1.229, 1.267, 1.309, 1.349,
+ 1.358, 1.312, 1.263, 1.227, 1.195, 1.167, 1.141, 1.117, 1.097, 1.078, 1.061, 1.043, 1.029, 1.021, 1.014, 1.008, 1.008, 1.014, 1.021, 1.032, 1.045, 1.061, 1.078, 1.097, 1.119, 1.144, 1.169, 1.201, 1.234, 1.272, 1.315, 1.353,
+ 1.364, 1.319, 1.269, 1.234, 1.201, 1.174, 1.148, 1.124, 1.103, 1.084, 1.067, 1.052, 1.038, 1.029, 1.021, 1.016, 1.016, 1.021, 1.029, 1.038, 1.051, 1.067, 1.084, 1.103, 1.126, 1.151, 1.176, 1.207, 1.241, 1.279, 1.321, 1.358,
+ 1.371, 1.326, 1.277, 1.242, 1.209, 1.181, 1.155, 1.132, 1.111, 1.092, 1.075, 1.061, 1.049, 1.038, 1.029, 1.027, 1.027, 1.029, 1.038, 1.047, 1.061, 1.075, 1.092, 1.111, 1.133, 1.157, 1.185, 1.213, 1.247, 1.286, 1.329, 1.365,
+ 1.379, 1.334, 1.287, 1.251, 1.219, 1.191, 1.164, 1.141, 1.119, 1.101, 1.085, 1.071, 1.061, 1.049, 1.041, 1.038, 1.038, 1.041, 1.047, 1.059, 1.071, 1.084, 1.101, 1.119, 1.141, 1.165, 1.193, 1.223, 1.257, 1.295, 1.338, 1.374,
+ 1.389, 1.343, 1.298, 1.262, 1.231, 1.201, 1.174, 1.151, 1.131, 1.111, 1.095, 1.083, 1.071, 1.061, 1.054, 1.051, 1.051, 1.054, 1.059, 1.071, 1.081, 1.094, 1.111, 1.129, 1.152, 1.176, 1.203, 1.235, 1.269, 1.307, 1.351, 1.384,
+ 1.401, 1.351, 1.311, 1.274, 1.242, 1.214, 1.187, 1.164, 1.142, 1.124, 1.108, 1.095, 1.083, 1.074, 1.068, 1.066, 1.066, 1.068, 1.073, 1.081, 1.094, 1.108, 1.123, 1.141, 1.164, 1.188, 1.215, 1.247, 1.281, 1.321, 1.364, 1.396,
+ 1.412, 1.366, 1.327, 1.289, 1.257, 1.227, 1.201, 1.176, 1.156, 1.137, 1.122, 1.108, 1.096, 1.088, 1.083, 1.081, 1.081, 1.082, 1.087, 1.095, 1.108, 1.122, 1.136, 1.154, 1.177, 1.201, 1.229, 1.261, 1.296, 1.337, 1.382, 1.409,
+ 1.421, 1.383, 1.343, 1.306, 1.273, 1.243, 1.216, 1.192, 1.169, 1.152, 1.137, 1.122, 1.111, 1.103, 1.098, 1.095, 1.095, 1.097, 1.102, 1.111, 1.123, 1.136, 1.152, 1.169, 1.191, 1.217, 1.246, 1.278, 1.314, 1.354, 1.399, 1.429,
+ 1.434, 1.402, 1.362, 1.324, 1.291, 1.261, 1.232, 1.208, 1.187, 1.168, 1.152, 1.138, 1.127, 1.119, 1.114, 1.112, 1.112, 1.115, 1.121, 1.128, 1.139, 1.152, 1.169, 1.186, 1.209, 1.234, 1.262, 1.295, 1.332, 1.372, 1.419, 1.451,
+ 1.453, 1.422, 1.382, 1.344, 1.309, 1.278, 1.249, 1.226, 1.204, 1.187, 1.168, 1.155, 1.144, 1.135, 1.131, 1.131, 1.131, 1.133, 1.138, 1.146, 1.157, 1.171, 1.186, 1.206, 1.227, 1.252, 1.281, 1.314, 1.351, 1.393, 1.442, 1.473,
+ 1.475, 1.446, 1.404, 1.366, 1.329, 1.298, 1.269, 1.245, 1.224, 1.204, 1.188, 1.174, 1.163, 1.154, 1.149, 1.148, 1.148, 1.152, 1.156, 1.164, 1.176, 1.189, 1.206, 1.226, 1.247, 1.274, 1.303, 1.336, 1.374, 1.417, 1.471, 1.505,
+ 1.503, 1.472, 1.428, 1.389, 1.353, 1.321, 1.291, 1.266, 1.245, 1.224, 1.207, 1.192, 1.183, 1.174, 1.169, 1.167, 1.168, 1.169, 1.175, 1.183, 1.195, 1.209, 1.226, 1.247, 1.267, 1.294, 1.325, 1.359, 1.397, 1.445, 1.505, 1.548,
+ 1.534, 1.503, 1.455, 1.413, 1.378, 1.344, 1.315, 1.289, 1.265, 1.243, 1.224, 1.207, 1.196, 1.192, 1.189, 1.189, 1.189, 1.189, 1.192, 1.198, 1.209, 1.226, 1.244, 1.266, 1.291, 1.318, 1.349, 1.383, 1.425, 1.475, 1.548, 1.591
+ ],
+ "sigma": 0.00095,
+ "sigma_Cb": 0.00098
+ }
+ },
+ {
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx477_scientific.json b/src/ipa/rpi/pisp/data/imx477_scientific.json
new file mode 100644
index 00000000..4ec5a15b
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx477_scientific.json
@@ -0,0 +1,546 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 12000,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 740,
+ "reference_Y": 15051
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.809
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 204,
+ "slope": 0.0061
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 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":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 33333 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.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
+ ]
+ }
+ ]
+ },
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx519.json b/src/ipa/rpi/pisp/data/imx519.json
new file mode 100644
index 00000000..9bc4d9a3
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx519.json
@@ -0,0 +1,634 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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
+ ]
+ }
+ ]
+ },
+ "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.524, 1.521, 1.515, 1.509, 1.502, 1.494, 1.486, 1.478, 1.469, 1.458, 1.451, 1.445, 1.442, 1.441, 1.441, 1.441, 1.441, 1.441, 1.442, 1.446, 1.451, 1.46, 1.469, 1.477, 1.484, 1.489, 1.495, 1.499, 1.503, 1.504, 1.504,
+ 1.526, 1.522, 1.518, 1.512, 1.505, 1.497, 1.489, 1.481, 1.473, 1.462, 1.451, 1.443, 1.436, 1.432, 1.431, 1.43, 1.43, 1.43, 1.431, 1.434, 1.438, 1.444, 1.454, 1.463, 1.471, 1.479, 1.485, 1.491, 1.496, 1.5, 1.502, 1.504,
+ 1.526, 1.521, 1.516, 1.508, 1.501, 1.492, 1.483, 1.475, 1.467, 1.456, 1.444, 1.435, 1.428, 1.423, 1.42, 1.418, 1.418, 1.419, 1.422, 1.425, 1.431, 1.438, 1.447, 1.457, 1.466, 1.474, 1.482, 1.488, 1.493, 1.498, 1.5, 1.503,
+ 1.524, 1.519, 1.513, 1.505, 1.496, 1.487, 1.478, 1.469, 1.461, 1.45, 1.437, 1.427, 1.419, 1.413, 1.409, 1.407, 1.407, 1.408, 1.412, 1.417, 1.423, 1.431, 1.441, 1.45, 1.46, 1.469, 1.477, 1.485, 1.49, 1.495, 1.499, 1.502,
+ 1.522, 1.516, 1.51, 1.502, 1.493, 1.483, 1.472, 1.462, 1.452, 1.441, 1.429, 1.419, 1.409, 1.402, 1.397, 1.395, 1.395, 1.397, 1.401, 1.406, 1.414, 1.422, 1.433, 1.443, 1.453, 1.462, 1.471, 1.48, 1.486, 1.492, 1.496, 1.5,
+ 1.519, 1.513, 1.508, 1.499, 1.489, 1.478, 1.467, 1.455, 1.443, 1.432, 1.421, 1.41, 1.399, 1.391, 1.386, 1.383, 1.383, 1.386, 1.39, 1.396, 1.405, 1.414, 1.425, 1.436, 1.446, 1.456, 1.466, 1.475, 1.483, 1.49, 1.493, 1.497,
+ 1.516, 1.511, 1.505, 1.495, 1.485, 1.473, 1.461, 1.448, 1.435, 1.423, 1.412, 1.401, 1.389, 1.381, 1.375, 1.372, 1.372, 1.374, 1.379, 1.386, 1.396, 1.406, 1.418, 1.429, 1.439, 1.449, 1.46, 1.47, 1.479, 1.487, 1.491, 1.494,
+ 1.515, 1.508, 1.502, 1.491, 1.48, 1.467, 1.454, 1.441, 1.427, 1.415, 1.404, 1.392, 1.38, 1.371, 1.364, 1.361, 1.361, 1.363, 1.369, 1.377, 1.388, 1.398, 1.409, 1.42, 1.431, 1.443, 1.454, 1.465, 1.475, 1.484, 1.488, 1.492,
+ 1.513, 1.505, 1.498, 1.487, 1.475, 1.461, 1.448, 1.434, 1.419, 1.407, 1.396, 1.383, 1.37, 1.361, 1.353, 1.349, 1.349, 1.352, 1.359, 1.367, 1.379, 1.391, 1.401, 1.412, 1.424, 1.436, 1.448, 1.46, 1.471, 1.481, 1.485, 1.49,
+ 1.511, 1.503, 1.495, 1.483, 1.47, 1.456, 1.442, 1.427, 1.412, 1.399, 1.387, 1.375, 1.362, 1.352, 1.344, 1.34, 1.34, 1.343, 1.35, 1.36, 1.371, 1.383, 1.393, 1.405, 1.418, 1.431, 1.443, 1.455, 1.467, 1.478, 1.483, 1.488,
+ 1.51, 1.501, 1.492, 1.479, 1.466, 1.451, 1.436, 1.421, 1.406, 1.392, 1.378, 1.366, 1.355, 1.346, 1.336, 1.332, 1.332, 1.335, 1.344, 1.353, 1.363, 1.374, 1.385, 1.398, 1.412, 1.425, 1.439, 1.452, 1.463, 1.475, 1.481, 1.486,
+ 1.508, 1.499, 1.489, 1.475, 1.461, 1.446, 1.43, 1.414, 1.399, 1.384, 1.369, 1.358, 1.348, 1.339, 1.329, 1.324, 1.323, 1.328, 1.338, 1.347, 1.355, 1.365, 1.378, 1.391, 1.406, 1.42, 1.434, 1.448, 1.46, 1.472, 1.478, 1.484,
+ 1.508, 1.497, 1.487, 1.473, 1.459, 1.443, 1.426, 1.41, 1.394, 1.379, 1.363, 1.351, 1.342, 1.333, 1.325, 1.321, 1.32, 1.324, 1.332, 1.34, 1.349, 1.359, 1.372, 1.386, 1.401, 1.416, 1.43, 1.445, 1.457, 1.47, 1.477, 1.484,
+ 1.507, 1.496, 1.485, 1.471, 1.457, 1.441, 1.424, 1.407, 1.391, 1.375, 1.359, 1.346, 1.335, 1.327, 1.322, 1.319, 1.319, 1.321, 1.326, 1.333, 1.343, 1.354, 1.368, 1.382, 1.397, 1.412, 1.427, 1.442, 1.455, 1.468, 1.476, 1.483,
+ 1.507, 1.495, 1.483, 1.469, 1.455, 1.439, 1.422, 1.404, 1.387, 1.371, 1.355, 1.341, 1.328, 1.321, 1.319, 1.318, 1.318, 1.319, 1.321, 1.326, 1.338, 1.35, 1.363, 1.378, 1.393, 1.408, 1.424, 1.439, 1.453, 1.466, 1.474, 1.483,
+ 1.507, 1.495, 1.483, 1.469, 1.455, 1.438, 1.421, 1.404, 1.387, 1.37, 1.354, 1.34, 1.327, 1.32, 1.318, 1.316, 1.316, 1.317, 1.32, 1.326, 1.337, 1.35, 1.363, 1.377, 1.393, 1.408, 1.424, 1.439, 1.452, 1.466, 1.474, 1.483,
+ 1.507, 1.495, 1.483, 1.469, 1.455, 1.438, 1.421, 1.404, 1.387, 1.37, 1.354, 1.34, 1.327, 1.32, 1.316, 1.315, 1.315, 1.316, 1.319, 1.326, 1.337, 1.35, 1.363, 1.377, 1.393, 1.408, 1.424, 1.439, 1.452, 1.466, 1.474, 1.483,
+ 1.507, 1.495, 1.483, 1.469, 1.455, 1.438, 1.422, 1.404, 1.387, 1.37, 1.355, 1.341, 1.328, 1.32, 1.315, 1.313, 1.313, 1.315, 1.319, 1.326, 1.338, 1.35, 1.363, 1.377, 1.393, 1.408, 1.424, 1.439, 1.452, 1.466, 1.474, 1.483,
+ 1.507, 1.495, 1.484, 1.47, 1.456, 1.439, 1.423, 1.406, 1.389, 1.372, 1.357, 1.343, 1.331, 1.323, 1.318, 1.316, 1.316, 1.318, 1.323, 1.33, 1.34, 1.352, 1.366, 1.38, 1.395, 1.41, 1.425, 1.44, 1.453, 1.466, 1.475, 1.483,
+ 1.507, 1.496, 1.485, 1.471, 1.456, 1.44, 1.424, 1.407, 1.39, 1.374, 1.359, 1.346, 1.335, 1.326, 1.32, 1.318, 1.319, 1.321, 1.327, 1.334, 1.343, 1.354, 1.368, 1.383, 1.398, 1.412, 1.427, 1.442, 1.455, 1.467, 1.475, 1.483,
+ 1.507, 1.497, 1.486, 1.472, 1.458, 1.442, 1.426, 1.41, 1.393, 1.377, 1.362, 1.35, 1.339, 1.331, 1.324, 1.321, 1.322, 1.325, 1.331, 1.338, 1.347, 1.357, 1.372, 1.386, 1.4, 1.415, 1.429, 1.443, 1.456, 1.468, 1.475, 1.483,
+ 1.507, 1.497, 1.487, 1.474, 1.46, 1.445, 1.43, 1.414, 1.398, 1.382, 1.368, 1.356, 1.347, 1.338, 1.329, 1.326, 1.326, 1.33, 1.337, 1.345, 1.353, 1.364, 1.377, 1.39, 1.405, 1.419, 1.432, 1.446, 1.458, 1.469, 1.476, 1.483,
+ 1.508, 1.498, 1.489, 1.476, 1.463, 1.448, 1.433, 1.418, 1.402, 1.388, 1.373, 1.363, 1.354, 1.345, 1.335, 1.33, 1.331, 1.334, 1.343, 1.351, 1.36, 1.37, 1.382, 1.394, 1.409, 1.422, 1.435, 1.448, 1.46, 1.471, 1.477, 1.484,
+ 1.508, 1.499, 1.49, 1.478, 1.466, 1.452, 1.437, 1.422, 1.407, 1.394, 1.381, 1.37, 1.361, 1.352, 1.342, 1.338, 1.338, 1.341, 1.349, 1.358, 1.367, 1.377, 1.388, 1.4, 1.413, 1.427, 1.439, 1.451, 1.462, 1.472, 1.478, 1.484,
+ 1.51, 1.501, 1.492, 1.481, 1.469, 1.455, 1.441, 1.427, 1.413, 1.4, 1.389, 1.378, 1.368, 1.359, 1.351, 1.347, 1.347, 1.35, 1.357, 1.364, 1.375, 1.385, 1.395, 1.406, 1.419, 1.432, 1.443, 1.455, 1.465, 1.474, 1.48, 1.486,
+ 1.511, 1.502, 1.494, 1.483, 1.472, 1.459, 1.445, 1.431, 1.418, 1.407, 1.398, 1.387, 1.375, 1.366, 1.36, 1.357, 1.357, 1.359, 1.364, 1.371, 1.382, 1.393, 1.402, 1.412, 1.424, 1.436, 1.447, 1.458, 1.467, 1.477, 1.482, 1.487,
+ 1.511, 1.503, 1.495, 1.485, 1.475, 1.462, 1.449, 1.436, 1.424, 1.414, 1.405, 1.394, 1.383, 1.375, 1.37, 1.368, 1.368, 1.369, 1.373, 1.38, 1.39, 1.4, 1.409, 1.418, 1.43, 1.441, 1.451, 1.461, 1.469, 1.478, 1.482, 1.487,
+ 1.511, 1.503, 1.496, 1.486, 1.477, 1.465, 1.453, 1.442, 1.431, 1.421, 1.412, 1.402, 1.392, 1.385, 1.381, 1.378, 1.378, 1.38, 1.383, 1.389, 1.398, 1.406, 1.415, 1.424, 1.436, 1.446, 1.455, 1.464, 1.471, 1.478, 1.483, 1.487,
+ 1.511, 1.504, 1.496, 1.488, 1.479, 1.468, 1.457, 1.447, 1.437, 1.428, 1.418, 1.409, 1.401, 1.395, 1.391, 1.389, 1.389, 1.39, 1.393, 1.398, 1.405, 1.413, 1.421, 1.431, 1.441, 1.452, 1.459, 1.466, 1.473, 1.479, 1.483, 1.487,
+ 1.511, 1.504, 1.496, 1.488, 1.479, 1.469, 1.459, 1.45, 1.442, 1.433, 1.424, 1.416, 1.408, 1.403, 1.4, 1.399, 1.399, 1.4, 1.402, 1.406, 1.412, 1.419, 1.427, 1.436, 1.446, 1.455, 1.462, 1.468, 1.474, 1.48, 1.484, 1.487,
+ 1.511, 1.503, 1.496, 1.488, 1.479, 1.47, 1.461, 1.453, 1.446, 1.438, 1.429, 1.422, 1.415, 1.411, 1.41, 1.409, 1.409, 1.41, 1.411, 1.413, 1.419, 1.425, 1.433, 1.441, 1.45, 1.457, 1.464, 1.47, 1.476, 1.481, 1.484, 1.487,
+ 1.511, 1.503, 1.496, 1.487, 1.479, 1.471, 1.464, 1.457, 1.45, 1.442, 1.435, 1.428, 1.422, 1.419, 1.419, 1.419, 1.419, 1.419, 1.419, 1.421, 1.426, 1.432, 1.439, 1.447, 1.454, 1.46, 1.466, 1.472, 1.477, 1.482, 1.485, 1.487
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 2.581, 2.577, 2.573, 2.566, 2.559, 2.55, 2.541, 2.529, 2.517, 2.504, 2.491, 2.482, 2.476, 2.472, 2.471, 2.471, 2.471, 2.471, 2.471, 2.473, 2.476, 2.482, 2.492, 2.501, 2.51, 2.518, 2.526, 2.533, 2.538, 2.543, 2.544, 2.544,
+ 2.579, 2.574, 2.568, 2.56, 2.552, 2.543, 2.534, 2.522, 2.509, 2.496, 2.481, 2.471, 2.463, 2.458, 2.455, 2.453, 2.453, 2.454, 2.455, 2.458, 2.464, 2.472, 2.483, 2.494, 2.503, 2.512, 2.52, 2.528, 2.534, 2.54, 2.542, 2.544,
+ 2.577, 2.57, 2.564, 2.555, 2.546, 2.537, 2.528, 2.515, 2.501, 2.487, 2.471, 2.46, 2.45, 2.443, 2.438, 2.436, 2.436, 2.437, 2.44, 2.444, 2.452, 2.462, 2.474, 2.486, 2.496, 2.506, 2.515, 2.524, 2.53, 2.537, 2.54, 2.543,
+ 2.574, 2.566, 2.559, 2.549, 2.539, 2.53, 2.521, 2.507, 2.493, 2.477, 2.461, 2.448, 2.437, 2.428, 2.421, 2.418, 2.418, 2.42, 2.424, 2.43, 2.44, 2.451, 2.465, 2.478, 2.489, 2.499, 2.509, 2.519, 2.526, 2.533, 2.538, 2.542,
+ 2.569, 2.562, 2.555, 2.544, 2.533, 2.522, 2.511, 2.496, 2.481, 2.465, 2.449, 2.435, 2.422, 2.413, 2.405, 2.402, 2.402, 2.404, 2.409, 2.416, 2.426, 2.438, 2.453, 2.466, 2.479, 2.491, 2.502, 2.512, 2.52, 2.528, 2.533, 2.538,
+ 2.564, 2.558, 2.552, 2.539, 2.527, 2.514, 2.5, 2.485, 2.469, 2.453, 2.436, 2.422, 2.408, 2.398, 2.389, 2.385, 2.385, 2.388, 2.393, 2.401, 2.413, 2.425, 2.44, 2.455, 2.469, 2.483, 2.494, 2.505, 2.514, 2.523, 2.529, 2.534,
+ 2.56, 2.553, 2.547, 2.534, 2.52, 2.505, 2.49, 2.474, 2.457, 2.441, 2.424, 2.409, 2.393, 2.382, 2.373, 2.369, 2.369, 2.371, 2.378, 2.386, 2.399, 2.412, 2.428, 2.444, 2.459, 2.473, 2.486, 2.497, 2.508, 2.518, 2.524, 2.53,
+ 2.557, 2.549, 2.541, 2.527, 2.512, 2.495, 2.479, 2.462, 2.445, 2.429, 2.413, 2.396, 2.379, 2.366, 2.356, 2.351, 2.351, 2.354, 2.362, 2.372, 2.385, 2.399, 2.416, 2.433, 2.448, 2.463, 2.476, 2.489, 2.501, 2.513, 2.519, 2.526,
+ 2.553, 2.544, 2.535, 2.519, 2.504, 2.486, 2.468, 2.45, 2.433, 2.417, 2.401, 2.383, 2.364, 2.349, 2.338, 2.333, 2.333, 2.337, 2.346, 2.357, 2.371, 2.386, 2.404, 2.421, 2.437, 2.452, 2.467, 2.481, 2.495, 2.508, 2.514, 2.521,
+ 2.55, 2.54, 2.529, 2.513, 2.497, 2.478, 2.458, 2.44, 2.422, 2.405, 2.388, 2.37, 2.352, 2.336, 2.323, 2.317, 2.317, 2.322, 2.332, 2.344, 2.358, 2.374, 2.392, 2.409, 2.426, 2.442, 2.458, 2.474, 2.489, 2.502, 2.509, 2.516,
+ 2.547, 2.536, 2.525, 2.507, 2.49, 2.47, 2.45, 2.43, 2.411, 2.393, 2.374, 2.357, 2.342, 2.326, 2.31, 2.303, 2.302, 2.308, 2.32, 2.333, 2.348, 2.363, 2.379, 2.396, 2.414, 2.433, 2.45, 2.468, 2.482, 2.497, 2.504, 2.512,
+ 2.544, 2.532, 2.52, 2.502, 2.483, 2.463, 2.442, 2.421, 2.4, 2.38, 2.36, 2.344, 2.332, 2.316, 2.297, 2.288, 2.288, 2.294, 2.308, 2.322, 2.337, 2.352, 2.367, 2.383, 2.403, 2.423, 2.442, 2.461, 2.476, 2.491, 2.499, 2.507,
+ 2.542, 2.53, 2.517, 2.498, 2.479, 2.458, 2.436, 2.414, 2.393, 2.371, 2.35, 2.334, 2.319, 2.305, 2.29, 2.283, 2.282, 2.287, 2.298, 2.311, 2.326, 2.341, 2.358, 2.375, 2.396, 2.417, 2.436, 2.456, 2.472, 2.487, 2.496, 2.505,
+ 2.542, 2.528, 2.515, 2.495, 2.476, 2.453, 2.431, 2.408, 2.386, 2.364, 2.342, 2.323, 2.306, 2.294, 2.284, 2.28, 2.28, 2.284, 2.291, 2.3, 2.315, 2.331, 2.35, 2.369, 2.39, 2.411, 2.431, 2.451, 2.467, 2.484, 2.494, 2.505,
+ 2.541, 2.527, 2.512, 2.492, 2.472, 2.449, 2.426, 2.403, 2.379, 2.356, 2.334, 2.313, 2.293, 2.283, 2.279, 2.278, 2.279, 2.28, 2.283, 2.29, 2.304, 2.321, 2.342, 2.363, 2.384, 2.406, 2.426, 2.446, 2.463, 2.48, 2.492, 2.504,
+ 2.541, 2.526, 2.512, 2.492, 2.472, 2.449, 2.426, 2.402, 2.378, 2.356, 2.333, 2.312, 2.292, 2.281, 2.276, 2.274, 2.275, 2.277, 2.28, 2.288, 2.303, 2.32, 2.341, 2.363, 2.384, 2.405, 2.425, 2.445, 2.463, 2.48, 2.492, 2.504,
+ 2.541, 2.526, 2.512, 2.492, 2.472, 2.449, 2.426, 2.402, 2.378, 2.356, 2.333, 2.312, 2.292, 2.28, 2.273, 2.27, 2.271, 2.273, 2.279, 2.288, 2.303, 2.32, 2.341, 2.363, 2.384, 2.405, 2.425, 2.445, 2.463, 2.48, 2.492, 2.504,
+ 2.541, 2.526, 2.512, 2.492, 2.472, 2.449, 2.426, 2.402, 2.379, 2.356, 2.334, 2.313, 2.293, 2.28, 2.271, 2.267, 2.267, 2.271, 2.278, 2.288, 2.303, 2.32, 2.342, 2.363, 2.384, 2.405, 2.426, 2.445, 2.463, 2.48, 2.492, 2.504,
+ 2.541, 2.527, 2.512, 2.493, 2.473, 2.45, 2.427, 2.404, 2.382, 2.36, 2.338, 2.318, 2.299, 2.285, 2.276, 2.271, 2.272, 2.276, 2.284, 2.294, 2.308, 2.324, 2.345, 2.365, 2.386, 2.407, 2.427, 2.447, 2.464, 2.481, 2.492, 2.504,
+ 2.541, 2.527, 2.513, 2.493, 2.474, 2.451, 2.429, 2.406, 2.385, 2.363, 2.342, 2.323, 2.305, 2.291, 2.28, 2.275, 2.276, 2.28, 2.29, 2.301, 2.313, 2.328, 2.348, 2.368, 2.389, 2.409, 2.429, 2.448, 2.465, 2.481, 2.493, 2.504,
+ 2.541, 2.527, 2.514, 2.495, 2.476, 2.454, 2.431, 2.41, 2.389, 2.368, 2.347, 2.329, 2.312, 2.298, 2.286, 2.281, 2.281, 2.286, 2.297, 2.308, 2.319, 2.333, 2.352, 2.372, 2.392, 2.412, 2.432, 2.45, 2.467, 2.482, 2.493, 2.504,
+ 2.542, 2.529, 2.516, 2.498, 2.479, 2.458, 2.437, 2.416, 2.395, 2.376, 2.356, 2.339, 2.324, 2.31, 2.295, 2.289, 2.289, 2.294, 2.305, 2.317, 2.329, 2.344, 2.361, 2.379, 2.398, 2.418, 2.436, 2.454, 2.47, 2.485, 2.495, 2.505,
+ 2.543, 2.531, 2.518, 2.501, 2.483, 2.463, 2.442, 2.422, 2.402, 2.383, 2.364, 2.349, 2.336, 2.321, 2.305, 2.297, 2.297, 2.303, 2.313, 2.325, 2.339, 2.354, 2.37, 2.386, 2.405, 2.423, 2.441, 2.459, 2.473, 2.487, 2.496, 2.506,
+ 2.545, 2.533, 2.521, 2.504, 2.488, 2.468, 2.448, 2.429, 2.41, 2.392, 2.375, 2.36, 2.347, 2.332, 2.316, 2.309, 2.309, 2.313, 2.323, 2.335, 2.35, 2.365, 2.379, 2.394, 2.412, 2.43, 2.447, 2.463, 2.477, 2.49, 2.498, 2.506,
+ 2.547, 2.535, 2.524, 2.508, 2.492, 2.474, 2.456, 2.438, 2.419, 2.403, 2.388, 2.372, 2.357, 2.343, 2.33, 2.324, 2.324, 2.328, 2.335, 2.346, 2.361, 2.375, 2.389, 2.403, 2.42, 2.437, 2.453, 2.468, 2.481, 2.493, 2.5, 2.508,
+ 2.548, 2.538, 2.527, 2.512, 2.497, 2.481, 2.464, 2.446, 2.428, 2.414, 2.401, 2.384, 2.367, 2.354, 2.344, 2.339, 2.339, 2.342, 2.348, 2.357, 2.371, 2.386, 2.399, 2.413, 2.429, 2.444, 2.459, 2.473, 2.485, 2.496, 2.502, 2.509,
+ 2.55, 2.539, 2.529, 2.515, 2.501, 2.486, 2.47, 2.454, 2.438, 2.425, 2.411, 2.396, 2.379, 2.367, 2.359, 2.355, 2.355, 2.357, 2.361, 2.369, 2.382, 2.396, 2.409, 2.423, 2.437, 2.451, 2.464, 2.476, 2.487, 2.498, 2.504, 2.509,
+ 2.551, 2.541, 2.531, 2.518, 2.505, 2.49, 2.476, 2.463, 2.449, 2.435, 2.421, 2.406, 2.392, 2.381, 2.374, 2.371, 2.371, 2.372, 2.376, 2.382, 2.393, 2.406, 2.42, 2.434, 2.446, 2.458, 2.469, 2.479, 2.489, 2.499, 2.504, 2.51,
+ 2.552, 2.542, 2.532, 2.52, 2.508, 2.495, 2.482, 2.471, 2.46, 2.446, 2.43, 2.417, 2.404, 2.396, 2.389, 2.386, 2.386, 2.387, 2.39, 2.395, 2.404, 2.415, 2.431, 2.445, 2.455, 2.465, 2.473, 2.482, 2.491, 2.499, 2.505, 2.511,
+ 2.552, 2.543, 2.533, 2.521, 2.509, 2.497, 2.486, 2.476, 2.466, 2.453, 2.439, 2.427, 2.415, 2.407, 2.403, 2.401, 2.401, 2.401, 2.403, 2.407, 2.415, 2.425, 2.439, 2.452, 2.462, 2.471, 2.478, 2.485, 2.493, 2.501, 2.506, 2.511,
+ 2.553, 2.543, 2.533, 2.521, 2.509, 2.499, 2.488, 2.48, 2.471, 2.46, 2.448, 2.436, 2.424, 2.418, 2.416, 2.415, 2.415, 2.415, 2.416, 2.419, 2.425, 2.434, 2.447, 2.459, 2.468, 2.477, 2.483, 2.489, 2.496, 2.503, 2.507, 2.511,
+ 2.553, 2.543, 2.534, 2.522, 2.51, 2.5, 2.491, 2.484, 2.477, 2.468, 2.457, 2.446, 2.434, 2.429, 2.429, 2.429, 2.429, 2.429, 2.429, 2.431, 2.436, 2.443, 2.454, 2.465, 2.474, 2.482, 2.487, 2.493, 2.499, 2.504, 2.508, 2.511
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.132, 3.129, 3.126, 3.121, 3.117, 3.111, 3.104, 3.101, 3.098, 3.095, 3.092, 3.09, 3.088, 3.087, 3.086, 3.087, 3.087, 3.089, 3.09, 3.091, 3.092, 3.094, 3.098, 3.103, 3.109, 3.114, 3.118, 3.122, 3.132, 3.141, 3.143, 3.144,
+ 3.138, 3.133, 3.128, 3.124, 3.119, 3.113, 3.106, 3.102, 3.099, 3.096, 3.094, 3.091, 3.089, 3.088, 3.087, 3.088, 3.089, 3.09, 3.091, 3.092, 3.094, 3.097, 3.101, 3.105, 3.11, 3.115, 3.12, 3.125, 3.134, 3.142, 3.145, 3.147,
+ 3.144, 3.137, 3.131, 3.126, 3.122, 3.115, 3.108, 3.104, 3.101, 3.098, 3.095, 3.093, 3.091, 3.089, 3.089, 3.089, 3.09, 3.09, 3.091, 3.093, 3.096, 3.1, 3.103, 3.107, 3.111, 3.116, 3.122, 3.128, 3.136, 3.143, 3.147, 3.15,
+ 3.15, 3.142, 3.134, 3.129, 3.124, 3.117, 3.11, 3.106, 3.102, 3.1, 3.097, 3.095, 3.093, 3.091, 3.09, 3.09, 3.091, 3.092, 3.092, 3.094, 3.099, 3.102, 3.105, 3.109, 3.113, 3.118, 3.124, 3.13, 3.138, 3.145, 3.149, 3.153,
+ 3.154, 3.147, 3.14, 3.133, 3.126, 3.12, 3.115, 3.11, 3.105, 3.102, 3.1, 3.098, 3.096, 3.095, 3.094, 3.094, 3.095, 3.096, 3.096, 3.098, 3.101, 3.105, 3.108, 3.112, 3.116, 3.121, 3.126, 3.132, 3.14, 3.148, 3.152, 3.156,
+ 3.158, 3.152, 3.146, 3.137, 3.129, 3.124, 3.119, 3.114, 3.108, 3.105, 3.102, 3.101, 3.099, 3.099, 3.098, 3.098, 3.099, 3.099, 3.1, 3.102, 3.104, 3.107, 3.111, 3.115, 3.119, 3.124, 3.129, 3.134, 3.143, 3.151, 3.154, 3.158,
+ 3.163, 3.157, 3.151, 3.142, 3.132, 3.127, 3.123, 3.117, 3.112, 3.108, 3.106, 3.105, 3.104, 3.103, 3.103, 3.103, 3.103, 3.104, 3.105, 3.106, 3.108, 3.11, 3.114, 3.118, 3.123, 3.127, 3.132, 3.137, 3.146, 3.154, 3.157, 3.161,
+ 3.168, 3.162, 3.155, 3.146, 3.137, 3.131, 3.126, 3.121, 3.117, 3.114, 3.112, 3.111, 3.109, 3.109, 3.109, 3.109, 3.109, 3.11, 3.11, 3.111, 3.113, 3.115, 3.118, 3.122, 3.126, 3.13, 3.135, 3.141, 3.149, 3.156, 3.16, 3.165,
+ 3.174, 3.167, 3.16, 3.151, 3.143, 3.136, 3.129, 3.125, 3.122, 3.12, 3.119, 3.117, 3.115, 3.115, 3.115, 3.115, 3.115, 3.116, 3.116, 3.117, 3.119, 3.12, 3.122, 3.125, 3.129, 3.134, 3.139, 3.145, 3.152, 3.158, 3.164, 3.169,
+ 3.177, 3.171, 3.164, 3.156, 3.148, 3.14, 3.133, 3.13, 3.128, 3.127, 3.126, 3.124, 3.122, 3.122, 3.122, 3.122, 3.122, 3.123, 3.123, 3.124, 3.125, 3.126, 3.127, 3.129, 3.133, 3.138, 3.144, 3.15, 3.156, 3.162, 3.167, 3.173,
+ 3.18, 3.175, 3.17, 3.161, 3.152, 3.145, 3.139, 3.136, 3.135, 3.134, 3.133, 3.132, 3.13, 3.13, 3.13, 3.131, 3.131, 3.131, 3.131, 3.131, 3.132, 3.133, 3.133, 3.135, 3.138, 3.142, 3.149, 3.155, 3.16, 3.166, 3.171, 3.175,
+ 3.182, 3.179, 3.175, 3.166, 3.157, 3.15, 3.144, 3.142, 3.141, 3.141, 3.141, 3.14, 3.138, 3.137, 3.138, 3.139, 3.139, 3.139, 3.139, 3.139, 3.139, 3.139, 3.139, 3.14, 3.143, 3.146, 3.153, 3.16, 3.165, 3.17, 3.174, 3.178,
+ 3.185, 3.181, 3.178, 3.169, 3.16, 3.154, 3.148, 3.147, 3.146, 3.146, 3.147, 3.146, 3.145, 3.145, 3.146, 3.147, 3.147, 3.147, 3.147, 3.147, 3.147, 3.147, 3.146, 3.147, 3.149, 3.152, 3.158, 3.164, 3.169, 3.173, 3.177, 3.181,
+ 3.187, 3.184, 3.18, 3.172, 3.163, 3.158, 3.153, 3.152, 3.151, 3.151, 3.151, 3.151, 3.151, 3.152, 3.154, 3.154, 3.154, 3.154, 3.154, 3.154, 3.154, 3.154, 3.154, 3.154, 3.155, 3.157, 3.162, 3.167, 3.171, 3.176, 3.18, 3.184,
+ 3.189, 3.186, 3.183, 3.175, 3.166, 3.161, 3.157, 3.156, 3.156, 3.156, 3.156, 3.157, 3.158, 3.159, 3.161, 3.162, 3.162, 3.162, 3.162, 3.162, 3.162, 3.162, 3.161, 3.161, 3.162, 3.163, 3.166, 3.169, 3.174, 3.179, 3.183, 3.187,
+ 3.192, 3.189, 3.185, 3.177, 3.168, 3.164, 3.16, 3.159, 3.159, 3.159, 3.16, 3.161, 3.162, 3.164, 3.165, 3.166, 3.166, 3.166, 3.166, 3.166, 3.166, 3.165, 3.164, 3.164, 3.164, 3.166, 3.168, 3.172, 3.177, 3.182, 3.185, 3.188,
+ 3.196, 3.192, 3.187, 3.179, 3.17, 3.166, 3.163, 3.162, 3.162, 3.162, 3.163, 3.165, 3.166, 3.168, 3.169, 3.17, 3.17, 3.17, 3.169, 3.169, 3.169, 3.168, 3.167, 3.167, 3.167, 3.168, 3.171, 3.174, 3.179, 3.185, 3.186, 3.188,
+ 3.199, 3.194, 3.19, 3.181, 3.172, 3.169, 3.166, 3.165, 3.164, 3.165, 3.167, 3.168, 3.17, 3.172, 3.173, 3.173, 3.173, 3.173, 3.172, 3.172, 3.171, 3.171, 3.17, 3.169, 3.169, 3.17, 3.173, 3.176, 3.182, 3.187, 3.188, 3.189,
+ 3.202, 3.197, 3.192, 3.183, 3.175, 3.171, 3.168, 3.166, 3.165, 3.165, 3.167, 3.168, 3.17, 3.172, 3.173, 3.173, 3.173, 3.173, 3.172, 3.172, 3.171, 3.171, 3.17, 3.17, 3.17, 3.171, 3.174, 3.177, 3.183, 3.189, 3.19, 3.191,
+ 3.204, 3.199, 3.195, 3.186, 3.177, 3.173, 3.17, 3.168, 3.165, 3.166, 3.167, 3.168, 3.17, 3.172, 3.173, 3.173, 3.173, 3.173, 3.172, 3.172, 3.171, 3.171, 3.171, 3.171, 3.171, 3.172, 3.175, 3.177, 3.184, 3.191, 3.192, 3.193,
+ 3.206, 3.201, 3.196, 3.188, 3.178, 3.175, 3.172, 3.169, 3.166, 3.165, 3.166, 3.168, 3.169, 3.17, 3.171, 3.172, 3.172, 3.172, 3.171, 3.171, 3.171, 3.171, 3.171, 3.171, 3.172, 3.173, 3.176, 3.178, 3.185, 3.192, 3.193, 3.194,
+ 3.207, 3.202, 3.197, 3.188, 3.179, 3.175, 3.172, 3.169, 3.165, 3.164, 3.164, 3.165, 3.165, 3.166, 3.167, 3.168, 3.168, 3.168, 3.168, 3.169, 3.169, 3.169, 3.17, 3.171, 3.172, 3.174, 3.176, 3.18, 3.186, 3.193, 3.194, 3.196,
+ 3.208, 3.203, 3.197, 3.188, 3.179, 3.175, 3.172, 3.168, 3.165, 3.163, 3.162, 3.162, 3.161, 3.162, 3.163, 3.164, 3.164, 3.164, 3.165, 3.166, 3.167, 3.168, 3.17, 3.171, 3.172, 3.174, 3.177, 3.181, 3.187, 3.193, 3.195, 3.197,
+ 3.208, 3.203, 3.197, 3.188, 3.179, 3.174, 3.171, 3.168, 3.164, 3.162, 3.161, 3.16, 3.159, 3.159, 3.159, 3.16, 3.161, 3.162, 3.163, 3.164, 3.166, 3.167, 3.169, 3.171, 3.172, 3.174, 3.178, 3.182, 3.188, 3.194, 3.196, 3.198,
+ 3.206, 3.201, 3.196, 3.187, 3.178, 3.173, 3.169, 3.166, 3.163, 3.161, 3.159, 3.158, 3.157, 3.157, 3.157, 3.158, 3.16, 3.161, 3.162, 3.163, 3.164, 3.166, 3.168, 3.17, 3.172, 3.174, 3.178, 3.182, 3.189, 3.196, 3.197, 3.199,
+ 3.205, 3.2, 3.195, 3.186, 3.177, 3.172, 3.167, 3.164, 3.162, 3.16, 3.157, 3.156, 3.155, 3.155, 3.155, 3.156, 3.158, 3.16, 3.161, 3.162, 3.163, 3.165, 3.167, 3.169, 3.171, 3.174, 3.178, 3.183, 3.19, 3.197, 3.198, 3.199,
+ 3.203, 3.198, 3.194, 3.185, 3.177, 3.172, 3.167, 3.164, 3.162, 3.159, 3.157, 3.155, 3.154, 3.154, 3.154, 3.155, 3.157, 3.159, 3.16, 3.162, 3.163, 3.165, 3.167, 3.169, 3.171, 3.174, 3.179, 3.184, 3.191, 3.198, 3.198, 3.199,
+ 3.201, 3.197, 3.193, 3.185, 3.177, 3.172, 3.168, 3.165, 3.162, 3.159, 3.157, 3.156, 3.154, 3.153, 3.153, 3.154, 3.156, 3.158, 3.16, 3.162, 3.163, 3.165, 3.167, 3.169, 3.171, 3.174, 3.18, 3.185, 3.191, 3.197, 3.198, 3.199,
+ 3.199, 3.195, 3.191, 3.184, 3.177, 3.173, 3.169, 3.166, 3.162, 3.16, 3.158, 3.156, 3.154, 3.153, 3.153, 3.154, 3.155, 3.157, 3.16, 3.162, 3.163, 3.165, 3.167, 3.169, 3.171, 3.174, 3.18, 3.186, 3.191, 3.196, 3.198, 3.199,
+ 3.199, 3.195, 3.19, 3.184, 3.178, 3.174, 3.171, 3.167, 3.163, 3.16, 3.158, 3.156, 3.154, 3.153, 3.153, 3.154, 3.155, 3.157, 3.159, 3.161, 3.163, 3.166, 3.167, 3.17, 3.172, 3.175, 3.181, 3.186, 3.191, 3.195, 3.197, 3.199,
+ 3.199, 3.194, 3.189, 3.184, 3.179, 3.175, 3.172, 3.168, 3.165, 3.161, 3.158, 3.156, 3.154, 3.153, 3.153, 3.154, 3.155, 3.157, 3.159, 3.161, 3.164, 3.167, 3.169, 3.171, 3.173, 3.176, 3.181, 3.186, 3.19, 3.194, 3.196, 3.198,
+ 3.199, 3.194, 3.188, 3.184, 3.18, 3.176, 3.174, 3.17, 3.166, 3.162, 3.158, 3.156, 3.154, 3.153, 3.154, 3.155, 3.155, 3.157, 3.158, 3.161, 3.164, 3.168, 3.17, 3.172, 3.174, 3.177, 3.181, 3.186, 3.189, 3.193, 3.196, 3.198
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 1.579, 1.579, 1.579, 1.578, 1.577, 1.576, 1.574, 1.574, 1.573, 1.572, 1.571, 1.571, 1.571, 1.571, 1.571, 1.571, 1.571, 1.571, 1.57, 1.569, 1.569, 1.569, 1.57, 1.571, 1.572, 1.572, 1.573, 1.574, 1.576, 1.577, 1.578, 1.578,
+ 1.581, 1.58, 1.579, 1.578, 1.577, 1.576, 1.575, 1.574, 1.573, 1.572, 1.572, 1.571, 1.571, 1.571, 1.571, 1.571, 1.571, 1.571, 1.57, 1.57, 1.57, 1.57, 1.571, 1.571, 1.572, 1.573, 1.574, 1.575, 1.576, 1.577, 1.578, 1.578,
+ 1.583, 1.581, 1.579, 1.578, 1.578, 1.576, 1.575, 1.574, 1.573, 1.573, 1.572, 1.571, 1.571, 1.571, 1.572, 1.572, 1.572, 1.571, 1.571, 1.57, 1.57, 1.571, 1.571, 1.572, 1.572, 1.573, 1.574, 1.576, 1.577, 1.578, 1.578, 1.579,
+ 1.584, 1.582, 1.579, 1.579, 1.578, 1.577, 1.575, 1.574, 1.573, 1.573, 1.572, 1.572, 1.571, 1.571, 1.572, 1.572, 1.572, 1.572, 1.571, 1.571, 1.571, 1.571, 1.572, 1.572, 1.573, 1.573, 1.575, 1.576, 1.577, 1.578, 1.579, 1.579,
+ 1.585, 1.583, 1.581, 1.58, 1.579, 1.578, 1.576, 1.575, 1.574, 1.573, 1.573, 1.572, 1.572, 1.572, 1.573, 1.573, 1.573, 1.573, 1.573, 1.572, 1.572, 1.572, 1.572, 1.573, 1.574, 1.575, 1.576, 1.577, 1.578, 1.579, 1.58, 1.58,
+ 1.586, 1.585, 1.583, 1.581, 1.579, 1.578, 1.577, 1.576, 1.575, 1.574, 1.573, 1.573, 1.573, 1.573, 1.574, 1.574, 1.574, 1.574, 1.574, 1.573, 1.573, 1.573, 1.573, 1.574, 1.575, 1.576, 1.577, 1.578, 1.579, 1.58, 1.58, 1.581,
+ 1.588, 1.586, 1.584, 1.582, 1.58, 1.579, 1.578, 1.577, 1.576, 1.575, 1.574, 1.574, 1.574, 1.574, 1.575, 1.576, 1.576, 1.576, 1.575, 1.575, 1.574, 1.574, 1.574, 1.575, 1.576, 1.576, 1.577, 1.579, 1.58, 1.582, 1.582, 1.582,
+ 1.589, 1.587, 1.586, 1.584, 1.582, 1.58, 1.579, 1.578, 1.577, 1.576, 1.576, 1.576, 1.576, 1.576, 1.577, 1.578, 1.578, 1.578, 1.578, 1.577, 1.576, 1.575, 1.575, 1.576, 1.576, 1.577, 1.578, 1.58, 1.581, 1.583, 1.583, 1.583,
+ 1.59, 1.588, 1.587, 1.585, 1.583, 1.581, 1.579, 1.578, 1.578, 1.578, 1.578, 1.578, 1.578, 1.579, 1.58, 1.58, 1.58, 1.58, 1.58, 1.579, 1.578, 1.577, 1.577, 1.577, 1.577, 1.578, 1.579, 1.581, 1.583, 1.584, 1.585, 1.585,
+ 1.592, 1.59, 1.588, 1.586, 1.585, 1.583, 1.581, 1.58, 1.579, 1.58, 1.58, 1.58, 1.581, 1.581, 1.582, 1.582, 1.582, 1.582, 1.582, 1.582, 1.58, 1.579, 1.579, 1.578, 1.579, 1.579, 1.581, 1.582, 1.584, 1.586, 1.586, 1.587,
+ 1.593, 1.591, 1.589, 1.588, 1.586, 1.584, 1.583, 1.582, 1.582, 1.582, 1.583, 1.583, 1.583, 1.584, 1.584, 1.584, 1.585, 1.585, 1.585, 1.584, 1.583, 1.582, 1.581, 1.581, 1.581, 1.582, 1.583, 1.584, 1.586, 1.587, 1.587, 1.588,
+ 1.595, 1.593, 1.591, 1.589, 1.587, 1.586, 1.585, 1.584, 1.584, 1.585, 1.585, 1.586, 1.586, 1.586, 1.586, 1.587, 1.587, 1.587, 1.587, 1.587, 1.585, 1.584, 1.584, 1.583, 1.583, 1.584, 1.585, 1.586, 1.587, 1.589, 1.589, 1.589,
+ 1.596, 1.594, 1.592, 1.59, 1.588, 1.587, 1.586, 1.586, 1.586, 1.587, 1.588, 1.588, 1.589, 1.589, 1.589, 1.59, 1.59, 1.59, 1.59, 1.59, 1.588, 1.587, 1.587, 1.587, 1.586, 1.586, 1.587, 1.588, 1.589, 1.59, 1.59, 1.59,
+ 1.596, 1.595, 1.594, 1.592, 1.59, 1.589, 1.588, 1.588, 1.589, 1.589, 1.59, 1.591, 1.592, 1.592, 1.592, 1.593, 1.593, 1.594, 1.594, 1.593, 1.592, 1.591, 1.59, 1.59, 1.589, 1.589, 1.589, 1.59, 1.591, 1.591, 1.591, 1.591,
+ 1.597, 1.596, 1.595, 1.593, 1.591, 1.59, 1.589, 1.59, 1.591, 1.592, 1.592, 1.593, 1.594, 1.595, 1.595, 1.596, 1.596, 1.597, 1.597, 1.596, 1.595, 1.595, 1.594, 1.593, 1.592, 1.592, 1.592, 1.592, 1.592, 1.593, 1.593, 1.593,
+ 1.598, 1.597, 1.596, 1.594, 1.592, 1.591, 1.59, 1.591, 1.591, 1.592, 1.593, 1.594, 1.596, 1.596, 1.597, 1.597, 1.598, 1.599, 1.598, 1.598, 1.597, 1.596, 1.595, 1.594, 1.594, 1.593, 1.593, 1.593, 1.593, 1.594, 1.594, 1.594,
+ 1.6, 1.598, 1.596, 1.595, 1.593, 1.592, 1.591, 1.592, 1.592, 1.593, 1.594, 1.595, 1.597, 1.597, 1.598, 1.599, 1.6, 1.6, 1.6, 1.599, 1.598, 1.597, 1.596, 1.595, 1.595, 1.594, 1.594, 1.595, 1.595, 1.594, 1.594, 1.594,
+ 1.601, 1.599, 1.597, 1.595, 1.593, 1.593, 1.592, 1.592, 1.593, 1.594, 1.595, 1.596, 1.597, 1.598, 1.599, 1.6, 1.601, 1.602, 1.601, 1.6, 1.599, 1.598, 1.597, 1.596, 1.595, 1.595, 1.596, 1.596, 1.596, 1.595, 1.595, 1.595,
+ 1.601, 1.599, 1.598, 1.596, 1.594, 1.593, 1.592, 1.593, 1.593, 1.594, 1.595, 1.596, 1.597, 1.598, 1.599, 1.6, 1.601, 1.602, 1.601, 1.6, 1.599, 1.598, 1.597, 1.596, 1.596, 1.596, 1.596, 1.596, 1.596, 1.596, 1.596, 1.596,
+ 1.601, 1.6, 1.599, 1.596, 1.594, 1.593, 1.593, 1.593, 1.593, 1.594, 1.595, 1.596, 1.597, 1.598, 1.599, 1.6, 1.601, 1.602, 1.601, 1.6, 1.599, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.596, 1.596, 1.596,
+ 1.601, 1.6, 1.599, 1.597, 1.594, 1.594, 1.593, 1.593, 1.593, 1.594, 1.594, 1.596, 1.597, 1.598, 1.599, 1.6, 1.601, 1.601, 1.601, 1.6, 1.599, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597,
+ 1.601, 1.6, 1.599, 1.597, 1.594, 1.594, 1.593, 1.593, 1.593, 1.593, 1.594, 1.595, 1.596, 1.597, 1.598, 1.599, 1.599, 1.6, 1.6, 1.599, 1.599, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597,
+ 1.602, 1.6, 1.599, 1.597, 1.594, 1.594, 1.593, 1.593, 1.592, 1.593, 1.593, 1.594, 1.595, 1.596, 1.597, 1.598, 1.598, 1.598, 1.598, 1.598, 1.598, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.598,
+ 1.602, 1.6, 1.599, 1.597, 1.594, 1.594, 1.593, 1.592, 1.592, 1.592, 1.593, 1.593, 1.594, 1.595, 1.596, 1.597, 1.597, 1.597, 1.598, 1.598, 1.598, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.598, 1.598,
+ 1.6, 1.599, 1.599, 1.596, 1.594, 1.593, 1.593, 1.592, 1.592, 1.592, 1.592, 1.592, 1.593, 1.594, 1.595, 1.596, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.598, 1.599, 1.599, 1.599,
+ 1.599, 1.599, 1.598, 1.596, 1.594, 1.593, 1.592, 1.592, 1.591, 1.591, 1.591, 1.592, 1.592, 1.593, 1.595, 1.595, 1.596, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.599, 1.598, 1.598, 1.596, 1.594, 1.593, 1.592, 1.592, 1.591, 1.591, 1.591, 1.591, 1.592, 1.593, 1.594, 1.595, 1.596, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.598, 1.598, 1.597, 1.596, 1.594, 1.593, 1.593, 1.592, 1.592, 1.592, 1.592, 1.592, 1.592, 1.593, 1.594, 1.595, 1.595, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.598, 1.597, 1.596, 1.595, 1.594, 1.594, 1.593, 1.593, 1.592, 1.592, 1.592, 1.592, 1.592, 1.593, 1.594, 1.594, 1.595, 1.596, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.598, 1.597, 1.596, 1.595, 1.594, 1.594, 1.593, 1.593, 1.592, 1.592, 1.592, 1.592, 1.592, 1.593, 1.594, 1.594, 1.595, 1.595, 1.596, 1.597, 1.597, 1.597, 1.597, 1.597, 1.598, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.597, 1.596, 1.595, 1.595, 1.594, 1.594, 1.594, 1.593, 1.593, 1.592, 1.592, 1.592, 1.593, 1.593, 1.594, 1.595, 1.595, 1.595, 1.596, 1.597, 1.597, 1.598, 1.598, 1.598, 1.598, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599, 1.599,
+ 1.597, 1.596, 1.595, 1.595, 1.594, 1.594, 1.594, 1.594, 1.593, 1.593, 1.592, 1.592, 1.593, 1.594, 1.595, 1.595, 1.595, 1.595, 1.596, 1.597, 1.598, 1.598, 1.598, 1.598, 1.598, 1.598, 1.599, 1.599, 1.599, 1.599, 1.599, 1.599
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.887, 2.823, 2.758, 2.586, 2.405, 2.265, 2.132, 2.01, 1.891, 1.795, 1.707, 1.661, 1.635, 1.624, 1.623, 1.623, 1.623, 1.623, 1.624, 1.633, 1.654, 1.698, 1.785, 1.88, 1.998, 2.118, 2.249, 2.385, 2.56, 2.727, 2.782, 2.838,
+ 2.84, 2.745, 2.65, 2.482, 2.308, 2.18, 2.058, 1.941, 1.826, 1.736, 1.656, 1.609, 1.577, 1.56, 1.552, 1.548, 1.548, 1.551, 1.559, 1.574, 1.603, 1.648, 1.726, 1.814, 1.929, 2.045, 2.164, 2.29, 2.457, 2.619, 2.708, 2.797,
+ 2.793, 2.667, 2.542, 2.378, 2.212, 2.094, 1.985, 1.873, 1.761, 1.678, 1.606, 1.557, 1.519, 1.495, 1.48, 1.473, 1.473, 1.48, 1.494, 1.516, 1.551, 1.597, 1.667, 1.748, 1.861, 1.972, 2.08, 2.195, 2.354, 2.511, 2.634, 2.756,
+ 2.734, 2.586, 2.438, 2.279, 2.119, 2.011, 1.91, 1.805, 1.697, 1.62, 1.553, 1.503, 1.461, 1.432, 1.411, 1.401, 1.401, 1.41, 1.43, 1.457, 1.497, 1.545, 1.609, 1.685, 1.792, 1.898, 1.997, 2.103, 2.256, 2.409, 2.556, 2.703,
+ 2.624, 2.49, 2.357, 2.203, 2.048, 1.936, 1.831, 1.736, 1.644, 1.566, 1.495, 1.443, 1.402, 1.372, 1.352, 1.342, 1.342, 1.351, 1.37, 1.397, 1.437, 1.486, 1.556, 1.632, 1.724, 1.818, 1.922, 2.032, 2.181, 2.33, 2.461, 2.593,
+ 2.513, 2.394, 2.275, 2.127, 1.976, 1.861, 1.751, 1.667, 1.59, 1.513, 1.436, 1.383, 1.342, 1.313, 1.292, 1.283, 1.283, 1.291, 1.31, 1.337, 1.376, 1.427, 1.503, 1.579, 1.655, 1.738, 1.846, 1.96, 2.106, 2.25, 2.367, 2.483,
+ 2.427, 2.315, 2.203, 2.058, 1.912, 1.795, 1.683, 1.604, 1.534, 1.461, 1.385, 1.332, 1.289, 1.259, 1.238, 1.228, 1.228, 1.237, 1.255, 1.282, 1.323, 1.376, 1.451, 1.524, 1.592, 1.669, 1.78, 1.895, 2.039, 2.18, 2.288, 2.396,
+ 2.383, 2.265, 2.147, 2.004, 1.859, 1.744, 1.634, 1.552, 1.477, 1.411, 1.349, 1.295, 1.245, 1.213, 1.191, 1.181, 1.181, 1.19, 1.208, 1.238, 1.286, 1.339, 1.401, 1.467, 1.54, 1.62, 1.73, 1.843, 1.984, 2.124, 2.236, 2.348,
+ 2.338, 2.215, 2.091, 1.949, 1.807, 1.694, 1.585, 1.499, 1.42, 1.362, 1.313, 1.259, 1.202, 1.167, 1.145, 1.134, 1.134, 1.143, 1.161, 1.194, 1.248, 1.301, 1.352, 1.41, 1.487, 1.571, 1.679, 1.791, 1.93, 2.067, 2.184, 2.3,
+ 2.307, 2.176, 2.046, 1.906, 1.765, 1.653, 1.545, 1.458, 1.377, 1.321, 1.274, 1.223, 1.17, 1.133, 1.107, 1.095, 1.094, 1.105, 1.127, 1.161, 1.212, 1.262, 1.31, 1.366, 1.446, 1.531, 1.638, 1.749, 1.886, 2.022, 2.145, 2.267,
+ 2.286, 2.147, 2.009, 1.87, 1.732, 1.62, 1.514, 1.427, 1.345, 1.285, 1.232, 1.187, 1.147, 1.11, 1.077, 1.061, 1.06, 1.074, 1.104, 1.137, 1.178, 1.222, 1.273, 1.333, 1.413, 1.499, 1.605, 1.716, 1.851, 1.986, 2.117, 2.247,
+ 2.265, 2.119, 1.973, 1.835, 1.698, 1.588, 1.482, 1.395, 1.313, 1.249, 1.19, 1.152, 1.123, 1.087, 1.046, 1.027, 1.027, 1.043, 1.08, 1.114, 1.143, 1.181, 1.237, 1.3, 1.381, 1.467, 1.573, 1.682, 1.816, 1.95, 2.089, 2.227,
+ 2.258, 2.104, 1.95, 1.813, 1.677, 1.567, 1.462, 1.375, 1.293, 1.227, 1.167, 1.127, 1.096, 1.065, 1.032, 1.016, 1.014, 1.028, 1.058, 1.088, 1.117, 1.157, 1.215, 1.28, 1.361, 1.447, 1.552, 1.661, 1.794, 1.928, 2.075, 2.222,
+ 2.258, 2.095, 1.933, 1.795, 1.66, 1.551, 1.446, 1.36, 1.278, 1.211, 1.15, 1.105, 1.068, 1.042, 1.023, 1.013, 1.011, 1.018, 1.036, 1.06, 1.095, 1.139, 1.199, 1.265, 1.346, 1.432, 1.536, 1.644, 1.777, 1.912, 2.067, 2.222,
+ 2.257, 2.086, 1.915, 1.778, 1.643, 1.535, 1.43, 1.344, 1.262, 1.195, 1.133, 1.083, 1.039, 1.019, 1.014, 1.01, 1.007, 1.008, 1.013, 1.033, 1.073, 1.12, 1.183, 1.25, 1.331, 1.417, 1.52, 1.627, 1.76, 1.895, 2.059, 2.222,
+ 2.257, 2.085, 1.913, 1.776, 1.642, 1.533, 1.429, 1.343, 1.261, 1.194, 1.132, 1.081, 1.036, 1.015, 1.01, 1.007, 1.005, 1.006, 1.009, 1.028, 1.07, 1.119, 1.181, 1.249, 1.33, 1.416, 1.519, 1.626, 1.759, 1.894, 2.058, 2.222,
+ 2.257, 2.085, 1.913, 1.776, 1.642, 1.533, 1.429, 1.343, 1.261, 1.194, 1.132, 1.081, 1.035, 1.013, 1.007, 1.004, 1.003, 1.004, 1.007, 1.026, 1.069, 1.119, 1.181, 1.249, 1.33, 1.416, 1.519, 1.626, 1.759, 1.894, 2.058, 2.222,
+ 2.257, 2.086, 1.915, 1.778, 1.643, 1.535, 1.43, 1.344, 1.262, 1.195, 1.133, 1.082, 1.037, 1.014, 1.005, 1.001, 1.001, 1.003, 1.007, 1.027, 1.07, 1.12, 1.183, 1.25, 1.331, 1.417, 1.52, 1.627, 1.76, 1.896, 2.059, 2.222,
+ 2.257, 2.093, 1.93, 1.793, 1.658, 1.549, 1.444, 1.358, 1.277, 1.21, 1.148, 1.101, 1.062, 1.034, 1.015, 1.005, 1.004, 1.012, 1.029, 1.054, 1.091, 1.136, 1.198, 1.265, 1.346, 1.432, 1.536, 1.644, 1.778, 1.913, 2.068, 2.224,
+ 2.257, 2.101, 1.945, 1.808, 1.673, 1.564, 1.459, 1.373, 1.292, 1.225, 1.163, 1.12, 1.086, 1.055, 1.024, 1.009, 1.007, 1.02, 1.05, 1.08, 1.111, 1.152, 1.213, 1.28, 1.361, 1.447, 1.552, 1.66, 1.795, 1.931, 2.078, 2.225,
+ 2.262, 2.114, 1.965, 1.829, 1.693, 1.583, 1.477, 1.391, 1.311, 1.244, 1.184, 1.142, 1.111, 1.076, 1.038, 1.02, 1.017, 1.034, 1.072, 1.106, 1.135, 1.174, 1.234, 1.299, 1.38, 1.466, 1.572, 1.681, 1.817, 1.953, 2.092, 2.232,
+ 2.28, 2.14, 2.0, 1.862, 1.725, 1.614, 1.507, 1.421, 1.34, 1.276, 1.22, 1.175, 1.136, 1.099, 1.065, 1.048, 1.047, 1.062, 1.094, 1.13, 1.17, 1.215, 1.268, 1.329, 1.411, 1.498, 1.604, 1.714, 1.851, 1.988, 2.12, 2.252,
+ 2.299, 2.166, 2.034, 1.896, 1.757, 1.645, 1.537, 1.45, 1.369, 1.309, 1.256, 1.207, 1.16, 1.123, 1.092, 1.077, 1.077, 1.089, 1.116, 1.153, 1.206, 1.256, 1.303, 1.359, 1.442, 1.529, 1.636, 1.747, 1.886, 2.024, 2.148, 2.273,
+ 2.328, 2.203, 2.078, 1.938, 1.797, 1.684, 1.575, 1.489, 1.409, 1.348, 1.294, 1.242, 1.191, 1.153, 1.126, 1.113, 1.113, 1.124, 1.148, 1.185, 1.241, 1.295, 1.344, 1.402, 1.481, 1.567, 1.675, 1.788, 1.929, 2.068, 2.186, 2.305,
+ 2.369, 2.251, 2.133, 1.991, 1.847, 1.732, 1.621, 1.539, 1.464, 1.397, 1.334, 1.279, 1.228, 1.193, 1.167, 1.155, 1.155, 1.167, 1.191, 1.226, 1.277, 1.331, 1.393, 1.459, 1.533, 1.614, 1.724, 1.838, 1.982, 2.123, 2.237, 2.351,
+ 2.41, 2.299, 2.188, 2.044, 1.897, 1.78, 1.668, 1.589, 1.518, 1.446, 1.374, 1.316, 1.266, 1.232, 1.209, 1.198, 1.198, 1.21, 1.234, 1.267, 1.312, 1.367, 1.443, 1.516, 1.584, 1.661, 1.773, 1.889, 2.035, 2.178, 2.287, 2.396,
+ 2.493, 2.375, 2.258, 2.11, 1.96, 1.845, 1.735, 1.65, 1.572, 1.497, 1.423, 1.365, 1.315, 1.282, 1.26, 1.25, 1.25, 1.261, 1.286, 1.318, 1.362, 1.417, 1.494, 1.57, 1.646, 1.729, 1.838, 1.953, 2.102, 2.248, 2.365, 2.483,
+ 2.599, 2.467, 2.335, 2.183, 2.03, 1.919, 1.814, 1.719, 1.627, 1.549, 1.478, 1.421, 1.372, 1.339, 1.317, 1.306, 1.306, 1.318, 1.343, 1.376, 1.421, 1.475, 1.546, 1.623, 1.715, 1.809, 1.914, 2.025, 2.176, 2.326, 2.459, 2.592,
+ 2.705, 2.559, 2.413, 2.256, 2.099, 1.992, 1.893, 1.788, 1.681, 1.602, 1.532, 1.477, 1.428, 1.395, 1.373, 1.363, 1.363, 1.374, 1.4, 1.433, 1.479, 1.532, 1.598, 1.675, 1.783, 1.89, 1.99, 2.097, 2.251, 2.405, 2.553, 2.702,
+ 2.763, 2.639, 2.514, 2.353, 2.19, 2.075, 1.967, 1.856, 1.744, 1.659, 1.584, 1.529, 1.484, 1.456, 1.44, 1.432, 1.432, 1.441, 1.459, 1.487, 1.53, 1.583, 1.655, 1.738, 1.852, 1.965, 2.073, 2.189, 2.35, 2.508, 2.632, 2.757,
+ 2.81, 2.716, 2.621, 2.456, 2.285, 2.158, 2.039, 1.923, 1.809, 1.718, 1.636, 1.581, 1.54, 1.518, 1.509, 1.505, 1.505, 1.509, 1.519, 1.54, 1.58, 1.633, 1.714, 1.804, 1.92, 2.038, 2.158, 2.286, 2.454, 2.617, 2.708, 2.799,
+ 2.858, 2.793, 2.728, 2.558, 2.38, 2.242, 2.111, 1.991, 1.873, 1.777, 1.688, 1.633, 1.596, 1.58, 1.578, 1.577, 1.577, 1.577, 1.578, 1.593, 1.629, 1.683, 1.772, 1.87, 1.989, 2.111, 2.244, 2.382, 2.558, 2.726, 2.784, 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/pisp/data/imx708.json b/src/ipa/rpi/pisp/data/imx708.json
new file mode 100644
index 00000000..e8d25c21
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx708.json
@@ -0,0 +1,1270 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 20716,
+ "reference_gain": 1.12,
+ "reference_aperture": 1.0,
+ "reference_lux": 810,
+ "reference_Y": 13994
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 1.856
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 221,
+ "slope": 0.00226
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 3.2,
+ "strength": 0.75,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2964.0, 0.7451, 0.3213,
+ 3610.0, 0.6119, 0.4443,
+ 4640.0, 0.5168, 0.5419,
+ 5910.0, 0.4436, 0.6229,
+ 7590.0, 0.3847, 0.6921
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01752,
+ "transverse_neg": 0.01831
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.532, 1.534, 1.535, 1.538, 1.538, 1.533, 1.529, 1.515, 1.506, 1.492, 1.477, 1.465, 1.453, 1.444, 1.437, 1.433, 1.433, 1.435, 1.441, 1.449, 1.461, 1.474, 1.485, 1.499, 1.511, 1.519, 1.525, 1.526, 1.526, 1.523, 1.517, 1.516,
+ 1.532, 1.534, 1.537, 1.538, 1.537, 1.534, 1.525, 1.515, 1.502, 1.486, 1.474, 1.458, 1.449, 1.438, 1.429, 1.427, 1.426, 1.429, 1.436, 1.444, 1.456, 1.468, 1.483, 1.497, 1.509, 1.518, 1.524, 1.526, 1.526, 1.523, 1.521, 1.516,
+ 1.532, 1.534, 1.537, 1.538, 1.536, 1.533, 1.524, 1.512, 1.499, 1.483, 1.468, 1.453, 1.439, 1.429, 1.421, 1.419, 1.419, 1.419, 1.427, 1.438, 1.451, 1.464, 1.479, 1.494, 1.506, 1.516, 1.523, 1.526, 1.526, 1.524, 1.521, 1.518,
+ 1.533, 1.536, 1.537, 1.537, 1.535, 1.532, 1.521, 1.507, 1.491, 1.474, 1.456, 1.441, 1.429, 1.418, 1.409, 1.406, 1.406, 1.408, 1.415, 1.426, 1.439, 1.453, 1.471, 1.485, 1.501, 1.511, 1.522, 1.524, 1.526, 1.525, 1.522, 1.519,
+ 1.537, 1.538, 1.539, 1.538, 1.534, 1.525, 1.513, 1.495, 1.477, 1.459, 1.443, 1.427, 1.413, 1.402, 1.394, 1.391, 1.391, 1.393, 1.399, 1.409, 1.424, 1.439, 1.455, 1.472, 1.489, 1.503, 1.515, 1.523, 1.526, 1.527, 1.525, 1.523,
+ 1.538, 1.539, 1.541, 1.539, 1.531, 1.519, 1.503, 1.484, 1.466, 1.445, 1.427, 1.413, 1.401, 1.386, 1.378, 1.373, 1.373, 1.376, 1.386, 1.398, 1.409, 1.424, 1.441, 1.459, 1.477, 1.495, 1.509, 1.519, 1.526, 1.528, 1.528, 1.526,
+ 1.539, 1.541, 1.541, 1.539, 1.529, 1.516, 1.498, 1.479, 1.456, 1.437, 1.417, 1.401, 1.386, 1.378, 1.369, 1.363, 1.363, 1.367, 1.376, 1.386, 1.399, 1.413, 1.432, 1.451, 1.472, 1.491, 1.507, 1.517, 1.525, 1.527, 1.527, 1.527,
+ 1.539, 1.539, 1.539, 1.538, 1.529, 1.515, 1.497, 1.476, 1.454, 1.433, 1.411, 1.395, 1.381, 1.368, 1.361, 1.356, 1.356, 1.359, 1.367, 1.379, 1.393, 1.409, 1.428, 1.448, 1.471, 1.489, 1.505, 1.516, 1.524, 1.527, 1.527, 1.527,
+ 1.539, 1.539, 1.539, 1.537, 1.528, 1.513, 1.493, 1.471, 1.449, 1.426, 1.406, 1.387, 1.373, 1.361, 1.352, 1.348, 1.348, 1.351, 1.359, 1.372, 1.387, 1.403, 1.422, 1.443, 1.465, 1.484, 1.503, 1.516, 1.525, 1.527, 1.528, 1.526,
+ 1.541, 1.542, 1.539, 1.537, 1.524, 1.506, 1.485, 1.461, 1.438, 1.416, 1.395, 1.377, 1.362, 1.352, 1.344, 1.339, 1.339, 1.342, 1.351, 1.362, 1.376, 1.393, 1.412, 1.434, 1.455, 1.477, 1.495, 1.514, 1.524, 1.528, 1.529, 1.529,
+ 1.543, 1.544, 1.543, 1.534, 1.518, 1.499, 1.476, 1.452, 1.427, 1.405, 1.386, 1.367, 1.354, 1.344, 1.338, 1.329, 1.329, 1.335, 1.342, 1.352, 1.367, 1.382, 1.402, 1.424, 1.445, 1.469, 1.491, 1.507, 1.522, 1.528, 1.529, 1.532,
+ 1.544, 1.544, 1.542, 1.534, 1.518, 1.499, 1.474, 1.449, 1.425, 1.401, 1.379, 1.362, 1.348, 1.338, 1.329, 1.324, 1.325, 1.329, 1.335, 1.347, 1.361, 1.378, 1.397, 1.421, 1.443, 1.467, 1.489, 1.507, 1.521, 1.529, 1.532, 1.533,
+ 1.543, 1.543, 1.541, 1.534, 1.519, 1.499, 1.474, 1.448, 1.424, 1.399, 1.377, 1.359, 1.346, 1.333, 1.324, 1.322, 1.321, 1.324, 1.332, 1.344, 1.359, 1.376, 1.397, 1.419, 1.443, 1.467, 1.489, 1.508, 1.521, 1.528, 1.531, 1.532,
+ 1.543, 1.542, 1.541, 1.533, 1.519, 1.499, 1.474, 1.448, 1.422, 1.399, 1.376, 1.358, 1.344, 1.331, 1.322, 1.319, 1.319, 1.321, 1.331, 1.342, 1.357, 1.375, 1.396, 1.419, 1.443, 1.467, 1.489, 1.508, 1.521, 1.529, 1.531, 1.532,
+ 1.543, 1.542, 1.541, 1.532, 1.518, 1.496, 1.471, 1.445, 1.418, 1.393, 1.373, 1.354, 1.341, 1.329, 1.319, 1.317, 1.316, 1.319, 1.327, 1.338, 1.353, 1.371, 1.392, 1.415, 1.439, 1.465, 1.485, 1.507, 1.519, 1.529, 1.531, 1.531,
+ 1.545, 1.544, 1.542, 1.531, 1.515, 1.493, 1.467, 1.441, 1.414, 1.391, 1.369, 1.351, 1.337, 1.326, 1.318, 1.314, 1.314, 1.317, 1.325, 1.335, 1.351, 1.367, 1.388, 1.411, 1.436, 1.461, 1.483, 1.505, 1.519, 1.531, 1.533, 1.533,
+ 1.545, 1.544, 1.541, 1.531, 1.515, 1.493, 1.467, 1.441, 1.414, 1.391, 1.369, 1.351, 1.337, 1.326, 1.318, 1.314, 1.314, 1.317, 1.325, 1.335, 1.351, 1.367, 1.388, 1.411, 1.436, 1.461, 1.483, 1.505, 1.521, 1.531, 1.534, 1.534,
+ 1.545, 1.544, 1.541, 1.534, 1.519, 1.496, 1.471, 1.446, 1.419, 1.392, 1.372, 1.354, 1.338, 1.328, 1.319, 1.316, 1.315, 1.319, 1.327, 1.338, 1.353, 1.371, 1.392, 1.416, 1.441, 1.465, 1.489, 1.511, 1.522, 1.531, 1.534, 1.535,
+ 1.544, 1.544, 1.542, 1.537, 1.524, 1.501, 1.476, 1.449, 1.424, 1.399, 1.377, 1.359, 1.344, 1.332, 1.324, 1.319, 1.319, 1.323, 1.331, 1.343, 1.358, 1.374, 1.396, 1.419, 1.445, 1.471, 1.493, 1.512, 1.525, 1.532, 1.534, 1.534,
+ 1.545, 1.545, 1.543, 1.538, 1.524, 1.503, 1.479, 1.452, 1.426, 1.402, 1.381, 1.362, 1.348, 1.337, 1.329, 1.324, 1.324, 1.328, 1.335, 1.347, 1.361, 1.379, 1.399, 1.423, 1.447, 1.471, 1.493, 1.513, 1.526, 1.533, 1.534, 1.535,
+ 1.546, 1.546, 1.544, 1.539, 1.525, 1.504, 1.479, 1.453, 1.428, 1.404, 1.383, 1.365, 1.352, 1.339, 1.333, 1.329, 1.329, 1.333, 1.339, 1.349, 1.363, 1.381, 1.402, 1.424, 1.448, 1.472, 1.494, 1.514, 1.526, 1.534, 1.534, 1.534,
+ 1.546, 1.546, 1.544, 1.539, 1.526, 1.505, 1.483, 1.457, 1.432, 1.407, 1.389, 1.371, 1.357, 1.347, 1.339, 1.333, 1.333, 1.339, 1.345, 1.354, 1.368, 1.386, 1.406, 1.428, 1.453, 1.475, 1.496, 1.515, 1.527, 1.535, 1.535, 1.535,
+ 1.545, 1.545, 1.545, 1.541, 1.529, 1.513, 1.491, 1.467, 1.441, 1.418, 1.399, 1.379, 1.366, 1.355, 1.347, 1.341, 1.341, 1.345, 1.354, 1.364, 1.378, 1.395, 1.415, 1.436, 1.459, 1.483, 1.503, 1.519, 1.531, 1.534, 1.535, 1.534,
+ 1.544, 1.545, 1.545, 1.544, 1.535, 1.519, 1.499, 1.476, 1.451, 1.428, 1.409, 1.391, 1.377, 1.366, 1.356, 1.352, 1.352, 1.355, 1.364, 1.374, 1.388, 1.405, 1.426, 1.447, 1.469, 1.492, 1.509, 1.523, 1.532, 1.535, 1.535, 1.533,
+ 1.544, 1.545, 1.546, 1.545, 1.537, 1.523, 1.504, 1.482, 1.458, 1.436, 1.418, 1.401, 1.385, 1.377, 1.367, 1.362, 1.362, 1.365, 1.373, 1.385, 1.398, 1.415, 1.434, 1.455, 1.477, 1.495, 1.514, 1.525, 1.533, 1.536, 1.535, 1.533,
+ 1.545, 1.546, 1.547, 1.545, 1.538, 1.525, 1.508, 1.486, 1.465, 1.444, 1.424, 1.408, 1.394, 1.385, 1.377, 1.371, 1.371, 1.373, 1.384, 1.392, 1.405, 1.421, 1.441, 1.459, 1.481, 1.499, 1.516, 1.528, 1.534, 1.536, 1.536, 1.533,
+ 1.544, 1.546, 1.547, 1.547, 1.541, 1.531, 1.514, 1.494, 1.474, 1.454, 1.434, 1.421, 1.408, 1.394, 1.386, 1.382, 1.382, 1.385, 1.392, 1.405, 1.416, 1.432, 1.449, 1.468, 1.488, 1.505, 1.519, 1.531, 1.536, 1.537, 1.536, 1.533,
+ 1.544, 1.546, 1.548, 1.548, 1.545, 1.536, 1.522, 1.506, 1.486, 1.467, 1.451, 1.434, 1.421, 1.408, 1.401, 1.396, 1.396, 1.399, 1.407, 1.416, 1.431, 1.447, 1.463, 1.481, 1.499, 1.513, 1.526, 1.534, 1.537, 1.537, 1.534, 1.531,
+ 1.543, 1.545, 1.547, 1.549, 1.549, 1.543, 1.531, 1.517, 1.501, 1.483, 1.465, 1.451, 1.438, 1.425, 1.417, 1.412, 1.412, 1.418, 1.423, 1.433, 1.447, 1.462, 1.479, 1.493, 1.511, 1.524, 1.531, 1.536, 1.538, 1.537, 1.533, 1.531,
+ 1.542, 1.545, 1.548, 1.551, 1.551, 1.546, 1.539, 1.524, 1.511, 1.493, 1.479, 1.464, 1.451, 1.442, 1.433, 1.429, 1.429, 1.434, 1.439, 1.449, 1.462, 1.474, 1.491, 1.505, 1.519, 1.529, 1.536, 1.539, 1.539, 1.537, 1.533, 1.531,
+ 1.541, 1.546, 1.549, 1.552, 1.553, 1.551, 1.544, 1.533, 1.521, 1.505, 1.489, 1.477, 1.464, 1.455, 1.447, 1.443, 1.443, 1.446, 1.451, 1.462, 1.472, 1.487, 1.499, 1.514, 1.525, 1.535, 1.541, 1.541, 1.541, 1.539, 1.533, 1.531,
+ 1.541, 1.546, 1.549, 1.553, 1.554, 1.552, 1.546, 1.537, 1.524, 1.512, 1.499, 1.485, 1.474, 1.464, 1.455, 1.451, 1.451, 1.452, 1.461, 1.469, 1.481, 1.495, 1.506, 1.518, 1.529, 1.539, 1.541, 1.542, 1.541, 1.539, 1.533, 1.529
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.586, 2.591, 2.597, 2.601, 2.601, 2.599, 2.592, 2.576, 2.561, 2.541, 2.523, 2.503, 2.486, 2.471, 2.459, 2.452, 2.452, 2.454, 2.462, 2.478, 2.495, 2.512, 2.531, 2.555, 2.568, 2.579, 2.587, 2.588, 2.585, 2.579, 2.573, 2.566,
+ 2.587, 2.592, 2.598, 2.601, 2.601, 2.599, 2.587, 2.574, 2.556, 2.532, 2.512, 2.491, 2.474, 2.462, 2.449, 2.443, 2.439, 2.443, 2.454, 2.464, 2.485, 2.505, 2.525, 2.548, 2.566, 2.578, 2.585, 2.588, 2.586, 2.579, 2.575, 2.567,
+ 2.587, 2.593, 2.598, 2.602, 2.601, 2.597, 2.584, 2.569, 2.551, 2.527, 2.503, 2.482, 2.464, 2.448, 2.434, 2.428, 2.427, 2.431, 2.439, 2.455, 2.474, 2.498, 2.521, 2.541, 2.564, 2.577, 2.585, 2.588, 2.589, 2.581, 2.576, 2.569,
+ 2.593, 2.596, 2.601, 2.603, 2.601, 2.594, 2.583, 2.563, 2.539, 2.514, 2.491, 2.466, 2.445, 2.429, 2.417, 2.409, 2.408, 2.411, 2.421, 2.437, 2.457, 2.481, 2.507, 2.531, 2.555, 2.572, 2.583, 2.588, 2.588, 2.585, 2.579, 2.575,
+ 2.597, 2.599, 2.604, 2.603, 2.599, 2.587, 2.567, 2.548, 2.522, 2.493, 2.467, 2.443, 2.419, 2.406, 2.391, 2.385, 2.385, 2.387, 2.397, 2.413, 2.435, 2.459, 2.486, 2.509, 2.538, 2.559, 2.574, 2.586, 2.588, 2.586, 2.582, 2.579,
+ 2.601, 2.603, 2.606, 2.604, 2.596, 2.578, 2.556, 2.531, 2.501, 2.471, 2.444, 2.419, 2.402, 2.381, 2.365, 2.359, 2.359, 2.361, 2.374, 2.396, 2.413, 2.435, 2.465, 2.493, 2.517, 2.542, 2.562, 2.582, 2.588, 2.587, 2.586, 2.584,
+ 2.601, 2.604, 2.605, 2.604, 2.593, 2.575, 2.547, 2.522, 2.488, 2.458, 2.432, 2.402, 2.381, 2.364, 2.349, 2.338, 2.338, 2.345, 2.359, 2.374, 2.396, 2.423, 2.453, 2.481, 2.511, 2.539, 2.561, 2.581, 2.586, 2.588, 2.588, 2.586,
+ 2.599, 2.602, 2.604, 2.602, 2.592, 2.572, 2.546, 2.516, 2.485, 2.451, 2.422, 2.393, 2.368, 2.349, 2.336, 2.328, 2.328, 2.333, 2.345, 2.365, 2.389, 2.417, 2.447, 2.478, 2.509, 2.537, 2.561, 2.577, 2.585, 2.588, 2.588, 2.587,
+ 2.601, 2.602, 2.604, 2.601, 2.589, 2.569, 2.539, 2.509, 2.473, 2.442, 2.409, 2.379, 2.357, 2.336, 2.323, 2.315, 2.315, 2.322, 2.334, 2.354, 2.377, 2.406, 2.436, 2.469, 2.503, 2.529, 2.558, 2.574, 2.585, 2.588, 2.589, 2.587,
+ 2.601, 2.606, 2.606, 2.601, 2.581, 2.557, 2.525, 2.493, 2.459, 2.426, 2.394, 2.365, 2.339, 2.322, 2.308, 2.301, 2.301, 2.305, 2.322, 2.337, 2.361, 2.389, 2.422, 2.454, 2.485, 2.519, 2.546, 2.568, 2.584, 2.589, 2.589, 2.589,
+ 2.608, 2.608, 2.606, 2.597, 2.576, 2.548, 2.515, 2.481, 2.444, 2.409, 2.376, 2.346, 2.323, 2.308, 2.293, 2.282, 2.281, 2.291, 2.305, 2.322, 2.348, 2.371, 2.403, 2.439, 2.472, 2.508, 2.538, 2.565, 2.582, 2.589, 2.592, 2.593,
+ 2.608, 2.608, 2.605, 2.596, 2.575, 2.547, 2.511, 2.474, 2.435, 2.401, 2.366, 2.339, 2.312, 2.293, 2.281, 2.274, 2.274, 2.281, 2.291, 2.311, 2.334, 2.364, 2.399, 2.433, 2.471, 2.506, 2.538, 2.564, 2.581, 2.591, 2.594, 2.595,
+ 2.605, 2.606, 2.605, 2.595, 2.575, 2.547, 2.511, 2.474, 2.433, 2.397, 2.363, 2.333, 2.309, 2.291, 2.274, 2.267, 2.265, 2.272, 2.284, 2.307, 2.331, 2.361, 2.395, 2.431, 2.469, 2.503, 2.539, 2.567, 2.584, 2.591, 2.595, 2.595,
+ 2.605, 2.606, 2.605, 2.595, 2.575, 2.547, 2.509, 2.473, 2.431, 2.395, 2.361, 2.332, 2.306, 2.285, 2.267, 2.261, 2.262, 2.265, 2.281, 2.302, 2.329, 2.359, 2.395, 2.429, 2.468, 2.503, 2.539, 2.567, 2.583, 2.593, 2.595, 2.595,
+ 2.608, 2.607, 2.606, 2.592, 2.572, 2.543, 2.506, 2.468, 2.426, 2.389, 2.354, 2.327, 2.299, 2.279, 2.262, 2.258, 2.257, 2.262, 2.276, 2.297, 2.321, 2.352, 2.387, 2.425, 2.464, 2.498, 2.532, 2.565, 2.582, 2.592, 2.595, 2.596,
+ 2.611, 2.609, 2.605, 2.592, 2.571, 2.538, 2.499, 2.463, 2.421, 2.384, 2.351, 2.322, 2.295, 2.276, 2.259, 2.254, 2.254, 2.256, 2.273, 2.292, 2.318, 2.347, 2.383, 2.418, 2.456, 2.491, 2.529, 2.562, 2.581, 2.593, 2.597, 2.598,
+ 2.609, 2.609, 2.606, 2.593, 2.571, 2.538, 2.499, 2.463, 2.421, 2.384, 2.351, 2.321, 2.295, 2.276, 2.259, 2.251, 2.251, 2.256, 2.273, 2.292, 2.318, 2.347, 2.383, 2.418, 2.456, 2.491, 2.529, 2.559, 2.582, 2.595, 2.597, 2.599,
+ 2.609, 2.609, 2.607, 2.597, 2.576, 2.543, 2.507, 2.467, 2.427, 2.388, 2.356, 2.323, 2.297, 2.278, 2.262, 2.256, 2.255, 2.262, 2.275, 2.296, 2.321, 2.351, 2.388, 2.425, 2.464, 2.502, 2.534, 2.563, 2.586, 2.595, 2.598, 2.599,
+ 2.609, 2.609, 2.608, 2.601, 2.581, 2.547, 2.513, 2.475, 2.434, 2.398, 2.362, 2.332, 2.307, 2.287, 2.269, 2.263, 2.263, 2.269, 2.281, 2.304, 2.328, 2.358, 2.394, 2.429, 2.469, 2.508, 2.538, 2.568, 2.589, 2.597, 2.598, 2.598,
+ 2.609, 2.611, 2.609, 2.601, 2.583, 2.549, 2.518, 2.478, 2.439, 2.402, 2.367, 2.337, 2.313, 2.293, 2.279, 2.271, 2.269, 2.277, 2.291, 2.311, 2.336, 2.363, 2.399, 2.435, 2.473, 2.509, 2.541, 2.571, 2.591, 2.598, 2.599, 2.599,
+ 2.611, 2.611, 2.609, 2.602, 2.585, 2.551, 2.519, 2.481, 2.442, 2.406, 2.374, 2.342, 2.318, 2.297, 2.287, 2.279, 2.278, 2.287, 2.297, 2.315, 2.339, 2.368, 2.402, 2.438, 2.476, 2.511, 2.545, 2.571, 2.591, 2.599, 2.601, 2.599,
+ 2.611, 2.611, 2.609, 2.604, 2.587, 2.557, 2.521, 2.485, 2.447, 2.412, 2.379, 2.352, 2.328, 2.309, 2.297, 2.288, 2.287, 2.297, 2.308, 2.327, 2.349, 2.377, 2.408, 2.446, 2.481, 2.517, 2.547, 2.573, 2.591, 2.599, 2.601, 2.599,
+ 2.608, 2.609, 2.609, 2.606, 2.592, 2.564, 2.533, 2.498, 2.462, 2.427, 2.394, 2.364, 2.343, 2.326, 2.309, 2.302, 2.302, 2.308, 2.324, 2.341, 2.362, 2.391, 2.425, 2.458, 2.494, 2.526, 2.555, 2.584, 2.593, 2.599, 2.599, 2.599,
+ 2.608, 2.609, 2.609, 2.609, 2.597, 2.574, 2.547, 2.511, 2.475, 2.438, 2.411, 2.381, 2.359, 2.342, 2.327, 2.318, 2.318, 2.325, 2.341, 2.358, 2.377, 2.404, 2.439, 2.469, 2.507, 2.537, 2.564, 2.587, 2.596, 2.598, 2.598, 2.597,
+ 2.609, 2.609, 2.611, 2.609, 2.599, 2.579, 2.551, 2.519, 2.486, 2.453, 2.425, 2.397, 2.375, 2.358, 2.345, 2.336, 2.336, 2.341, 2.355, 2.372, 2.393, 2.419, 2.452, 2.481, 2.516, 2.542, 2.571, 2.591, 2.597, 2.599, 2.598, 2.595,
+ 2.607, 2.611, 2.613, 2.611, 2.605, 2.586, 2.561, 2.529, 2.495, 2.462, 2.435, 2.409, 2.387, 2.374, 2.359, 2.351, 2.351, 2.356, 2.372, 2.385, 2.406, 2.431, 2.462, 2.488, 2.524, 2.551, 2.573, 2.591, 2.598, 2.599, 2.598, 2.596,
+ 2.606, 2.609, 2.613, 2.613, 2.607, 2.591, 2.565, 2.539, 2.507, 2.477, 2.449, 2.425, 2.409, 2.387, 2.376, 2.369, 2.369, 2.374, 2.385, 2.406, 2.422, 2.446, 2.473, 2.502, 2.534, 2.557, 2.578, 2.595, 2.599, 2.601, 2.598, 2.595,
+ 2.606, 2.611, 2.613, 2.614, 2.611, 2.598, 2.581, 2.553, 2.523, 2.496, 2.471, 2.449, 2.425, 2.409, 2.398, 2.391, 2.391, 2.395, 2.408, 2.422, 2.445, 2.468, 2.493, 2.522, 2.549, 2.569, 2.589, 2.601, 2.603, 2.602, 2.596, 2.593,
+ 2.605, 2.609, 2.613, 2.616, 2.614, 2.607, 2.591, 2.571, 2.545, 2.518, 2.494, 2.471, 2.452, 2.435, 2.423, 2.417, 2.417, 2.421, 2.431, 2.449, 2.467, 2.493, 2.516, 2.542, 2.566, 2.585, 2.596, 2.606, 2.605, 2.602, 2.595, 2.593,
+ 2.604, 2.608, 2.616, 2.617, 2.618, 2.613, 2.602, 2.584, 2.559, 2.536, 2.514, 2.493, 2.476, 2.459, 2.445, 2.439, 2.439, 2.445, 2.456, 2.471, 2.493, 2.511, 2.534, 2.559, 2.579, 2.592, 2.607, 2.608, 2.607, 2.604, 2.595, 2.592,
+ 2.603, 2.609, 2.615, 2.619, 2.623, 2.619, 2.608, 2.594, 2.573, 2.551, 2.532, 2.512, 2.493, 2.477, 2.468, 2.462, 2.462, 2.468, 2.476, 2.494, 2.509, 2.528, 2.551, 2.574, 2.589, 2.604, 2.611, 2.611, 2.611, 2.604, 2.598, 2.592,
+ 2.602, 2.607, 2.613, 2.621, 2.624, 2.621, 2.617, 2.601, 2.585, 2.567, 2.544, 2.521, 2.507, 2.493, 2.478, 2.474, 2.475, 2.477, 2.489, 2.505, 2.523, 2.544, 2.563, 2.584, 2.598, 2.609, 2.612, 2.613, 2.613, 2.608, 2.599, 2.591
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.263, 3.263, 3.264, 3.269, 3.274, 3.275, 3.277, 3.281, 3.283, 3.285, 3.289, 3.292, 3.291, 3.291, 3.294, 3.298, 3.302, 3.305, 3.303, 3.301, 3.296, 3.294, 3.293, 3.293, 3.295, 3.295, 3.295, 3.287, 3.285, 3.279, 3.282, 3.285,
+ 3.259, 3.259, 3.262, 3.268, 3.271, 3.273, 3.273, 3.274, 3.277, 3.278, 3.282, 3.285, 3.286, 3.286, 3.288, 3.289, 3.292, 3.291, 3.293, 3.291, 3.288, 3.288, 3.288, 3.288, 3.288, 3.287, 3.287, 3.283, 3.281, 3.276, 3.277, 3.281,
+ 3.259, 3.259, 3.262, 3.269, 3.272, 3.274, 3.273, 3.273, 3.273, 3.276, 3.278, 3.279, 3.281, 3.282, 3.283, 3.283, 3.285, 3.286, 3.286, 3.285, 3.283, 3.282, 3.282, 3.286, 3.285, 3.285, 3.285, 3.282, 3.281, 3.276, 3.275, 3.279,
+ 3.267, 3.266, 3.269, 3.273, 3.274, 3.277, 3.276, 3.275, 3.274, 3.277, 3.277, 3.278, 3.279, 3.279, 3.279, 3.279, 3.281, 3.283, 3.283, 3.281, 3.281, 3.281, 3.282, 3.287, 3.287, 3.288, 3.287, 3.286, 3.283, 3.277, 3.278, 3.281,
+ 3.268, 3.271, 3.274, 3.277, 3.283, 3.286, 3.284, 3.281, 3.278, 3.278, 3.279, 3.279, 3.281, 3.281, 3.281, 3.281, 3.282, 3.283, 3.284, 3.283, 3.282, 3.282, 3.284, 3.288, 3.289, 3.291, 3.291, 3.288, 3.288, 3.283, 3.286, 3.288,
+ 3.272, 3.275, 3.279, 3.283, 3.288, 3.289, 3.289, 3.287, 3.282, 3.284, 3.282, 3.284, 3.284, 3.285, 3.284, 3.285, 3.285, 3.288, 3.287, 3.286, 3.283, 3.283, 3.285, 3.289, 3.292, 3.292, 3.293, 3.292, 3.291, 3.288, 3.289, 3.293,
+ 3.276, 3.278, 3.282, 3.289, 3.293, 3.293, 3.291, 3.289, 3.287, 3.286, 3.285, 3.285, 3.286, 3.286, 3.287, 3.288, 3.289, 3.289, 3.289, 3.288, 3.285, 3.283, 3.286, 3.289, 3.292, 3.294, 3.294, 3.294, 3.293, 3.289, 3.292, 3.293,
+ 3.279, 3.281, 3.286, 3.293, 3.297, 3.298, 3.295, 3.292, 3.288, 3.287, 3.285, 3.287, 3.288, 3.288, 3.289, 3.291, 3.292, 3.293, 3.291, 3.288, 3.286, 3.285, 3.285, 3.291, 3.294, 3.295, 3.297, 3.297, 3.298, 3.293, 3.294, 3.294,
+ 3.281, 3.286, 3.291, 3.298, 3.301, 3.301, 3.299, 3.295, 3.289, 3.288, 3.286, 3.288, 3.289, 3.292, 3.293, 3.292, 3.295, 3.296, 3.295, 3.291, 3.287, 3.286, 3.286, 3.289, 3.295, 3.297, 3.298, 3.301, 3.301, 3.298, 3.297, 3.297,
+ 3.284, 3.289, 3.295, 3.302, 3.303, 3.303, 3.301, 3.298, 3.294, 3.292, 3.289, 3.293, 3.296, 3.297, 3.297, 3.297, 3.297, 3.298, 3.298, 3.296, 3.289, 3.288, 3.287, 3.294, 3.298, 3.301, 3.304, 3.305, 3.304, 3.299, 3.299, 3.302,
+ 3.291, 3.292, 3.299, 3.305, 3.308, 3.305, 3.304, 3.302, 3.298, 3.295, 3.295, 3.298, 3.299, 3.302, 3.303, 3.302, 3.301, 3.301, 3.301, 3.299, 3.296, 3.291, 3.292, 3.297, 3.301, 3.304, 3.306, 3.309, 3.308, 3.302, 3.301, 3.304,
+ 3.292, 3.297, 3.303, 3.309, 3.312, 3.311, 3.308, 3.304, 3.301, 3.299, 3.298, 3.299, 3.303, 3.305, 3.306, 3.305, 3.305, 3.303, 3.303, 3.301, 3.299, 3.294, 3.294, 3.297, 3.302, 3.305, 3.309, 3.311, 3.311, 3.305, 3.305, 3.306,
+ 3.295, 3.298, 3.305, 3.309, 3.313, 3.313, 3.312, 3.307, 3.303, 3.301, 3.299, 3.301, 3.304, 3.307, 3.308, 3.306, 3.306, 3.306, 3.306, 3.302, 3.299, 3.296, 3.295, 3.298, 3.303, 3.306, 3.311, 3.312, 3.312, 3.307, 3.308, 3.309,
+ 3.297, 3.298, 3.303, 3.309, 3.313, 3.313, 3.311, 3.307, 3.303, 3.301, 3.299, 3.299, 3.305, 3.307, 3.307, 3.306, 3.306, 3.306, 3.305, 3.299, 3.297, 3.294, 3.294, 3.298, 3.303, 3.305, 3.311, 3.312, 3.313, 3.308, 3.311, 3.309,
+ 3.297, 3.298, 3.304, 3.309, 3.312, 3.313, 3.311, 3.308, 3.304, 3.302, 3.301, 3.301, 3.306, 3.307, 3.308, 3.306, 3.306, 3.307, 3.306, 3.302, 3.297, 3.294, 3.294, 3.299, 3.305, 3.306, 3.309, 3.312, 3.311, 3.306, 3.308, 3.309,
+ 3.298, 3.299, 3.306, 3.311, 3.315, 3.314, 3.311, 3.308, 3.305, 3.303, 3.303, 3.304, 3.307, 3.309, 3.309, 3.308, 3.308, 3.307, 3.306, 3.302, 3.298, 3.296, 3.296, 3.298, 3.304, 3.306, 3.308, 3.309, 3.314, 3.308, 3.309, 3.308,
+ 3.299, 3.301, 3.307, 3.313, 3.316, 3.316, 3.313, 3.311, 3.307, 3.305, 3.305, 3.307, 3.309, 3.311, 3.312, 3.311, 3.309, 3.309, 3.308, 3.306, 3.301, 3.298, 3.297, 3.301, 3.305, 3.309, 3.309, 3.311, 3.313, 3.306, 3.308, 3.307,
+ 3.301, 3.301, 3.307, 3.314, 3.317, 3.318, 3.314, 3.311, 3.308, 3.306, 3.306, 3.308, 3.311, 3.311, 3.312, 3.311, 3.309, 3.309, 3.309, 3.306, 3.302, 3.298, 3.298, 3.301, 3.305, 3.309, 3.311, 3.311, 3.312, 3.309, 3.308, 3.308,
+ 3.301, 3.302, 3.307, 3.316, 3.319, 3.319, 3.315, 3.312, 3.309, 3.306, 3.307, 3.309, 3.311, 3.312, 3.311, 3.311, 3.309, 3.309, 3.309, 3.306, 3.303, 3.299, 3.298, 3.302, 3.305, 3.308, 3.309, 3.309, 3.309, 3.303, 3.306, 3.307,
+ 3.301, 3.303, 3.308, 3.315, 3.318, 3.318, 3.316, 3.313, 3.311, 3.307, 3.307, 3.308, 3.311, 3.311, 3.311, 3.308, 3.308, 3.307, 3.307, 3.306, 3.303, 3.299, 3.297, 3.301, 3.303, 3.306, 3.309, 3.308, 3.306, 3.303, 3.304, 3.306,
+ 3.302, 3.304, 3.306, 3.316, 3.318, 3.318, 3.317, 3.315, 3.311, 3.308, 3.309, 3.311, 3.311, 3.312, 3.311, 3.307, 3.306, 3.307, 3.308, 3.307, 3.304, 3.299, 3.298, 3.301, 3.303, 3.305, 3.306, 3.305, 3.304, 3.302, 3.303, 3.306,
+ 3.302, 3.304, 3.306, 3.312, 3.316, 3.317, 3.317, 3.313, 3.311, 3.309, 3.309, 3.311, 3.311, 3.312, 3.309, 3.307, 3.306, 3.306, 3.308, 3.307, 3.304, 3.298, 3.297, 3.302, 3.304, 3.305, 3.306, 3.305, 3.304, 3.299, 3.302, 3.303,
+ 3.299, 3.299, 3.306, 3.309, 3.315, 3.316, 3.316, 3.312, 3.309, 3.308, 3.308, 3.309, 3.311, 3.311, 3.307, 3.305, 3.305, 3.305, 3.306, 3.304, 3.299, 3.297, 3.296, 3.301, 3.302, 3.304, 3.303, 3.302, 3.301, 3.298, 3.299, 3.301,
+ 3.295, 3.297, 3.305, 3.309, 3.311, 3.311, 3.311, 3.309, 3.307, 3.306, 3.305, 3.305, 3.305, 3.305, 3.304, 3.301, 3.301, 3.301, 3.302, 3.299, 3.296, 3.295, 3.295, 3.298, 3.301, 3.302, 3.303, 3.301, 3.299, 3.295, 3.297, 3.299,
+ 3.294, 3.296, 3.299, 3.306, 3.308, 3.309, 3.309, 3.307, 3.307, 3.303, 3.302, 3.301, 3.302, 3.303, 3.302, 3.299, 3.298, 3.299, 3.299, 3.298, 3.295, 3.292, 3.292, 3.293, 3.297, 3.299, 3.299, 3.299, 3.297, 3.294, 3.295, 3.299,
+ 3.291, 3.292, 3.296, 3.302, 3.306, 3.306, 3.307, 3.306, 3.305, 3.303, 3.302, 3.301, 3.301, 3.303, 3.301, 3.299, 3.298, 3.298, 3.298, 3.297, 3.295, 3.292, 3.291, 3.291, 3.295, 3.295, 3.297, 3.298, 3.296, 3.293, 3.292, 3.291,
+ 3.293, 3.292, 3.294, 3.301, 3.303, 3.305, 3.308, 3.306, 3.306, 3.304, 3.304, 3.303, 3.303, 3.303, 3.302, 3.301, 3.299, 3.299, 3.299, 3.299, 3.294, 3.291, 3.289, 3.291, 3.293, 3.294, 3.294, 3.294, 3.294, 3.289, 3.291, 3.291,
+ 3.288, 3.289, 3.291, 3.299, 3.303, 3.304, 3.304, 3.304, 3.304, 3.303, 3.303, 3.304, 3.306, 3.305, 3.303, 3.301, 3.301, 3.298, 3.299, 3.298, 3.293, 3.291, 3.289, 3.291, 3.291, 3.292, 3.291, 3.291, 3.291, 3.285, 3.288, 3.291,
+ 3.285, 3.284, 3.287, 3.291, 3.299, 3.299, 3.299, 3.299, 3.299, 3.301, 3.302, 3.303, 3.303, 3.302, 3.299, 3.298, 3.298, 3.298, 3.298, 3.293, 3.288, 3.286, 3.285, 3.288, 3.288, 3.288, 3.287, 3.285, 3.284, 3.279, 3.281, 3.284,
+ 3.281, 3.282, 3.282, 3.286, 3.291, 3.294, 3.294, 3.295, 3.295, 3.299, 3.301, 3.304, 3.305, 3.299, 3.299, 3.297, 3.298, 3.298, 3.297, 3.292, 3.288, 3.285, 3.283, 3.284, 3.284, 3.286, 3.284, 3.282, 3.279, 3.275, 3.275, 3.278,
+ 3.282, 3.282, 3.284, 3.286, 3.291, 3.294, 3.295, 3.295, 3.298, 3.301, 3.304, 3.306, 3.306, 3.304, 3.303, 3.301, 3.302, 3.299, 3.299, 3.295, 3.289, 3.287, 3.284, 3.288, 3.287, 3.287, 3.285, 3.283, 3.282, 3.278, 3.281, 3.286,
+ 3.292, 3.291, 3.292, 3.298, 3.307, 3.309, 3.308, 3.312, 3.313, 3.317, 3.324, 3.327, 3.327, 3.325, 3.326, 3.322, 3.319, 3.317, 3.317, 3.315, 3.312, 3.305, 3.303, 3.301, 3.299, 3.297, 3.299, 3.293, 3.289, 3.285, 3.287, 3.293
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.602, 1.603, 1.605, 1.608, 1.611, 1.612, 1.612, 1.614, 1.615, 1.616, 1.619, 1.621, 1.621, 1.622, 1.622, 1.624, 1.624, 1.626, 1.625, 1.625, 1.623, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.614, 1.612, 1.609, 1.609, 1.611,
+ 1.601, 1.602, 1.605, 1.608, 1.611, 1.612, 1.612, 1.613, 1.614, 1.615, 1.617, 1.619, 1.621, 1.621, 1.621, 1.621, 1.622, 1.624, 1.624, 1.624, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.615, 1.614, 1.612, 1.609, 1.609, 1.609,
+ 1.602, 1.603, 1.605, 1.609, 1.611, 1.613, 1.613, 1.613, 1.613, 1.615, 1.616, 1.618, 1.618, 1.619, 1.619, 1.619, 1.621, 1.621, 1.622, 1.622, 1.619, 1.618, 1.617, 1.617, 1.616, 1.615, 1.615, 1.615, 1.612, 1.609, 1.609, 1.609,
+ 1.604, 1.605, 1.608, 1.612, 1.613, 1.614, 1.614, 1.614, 1.614, 1.614, 1.616, 1.617, 1.618, 1.618, 1.618, 1.619, 1.619, 1.621, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.616, 1.616, 1.616, 1.615, 1.615, 1.612, 1.611, 1.611,
+ 1.606, 1.608, 1.611, 1.614, 1.615, 1.615, 1.616, 1.616, 1.615, 1.615, 1.617, 1.618, 1.619, 1.619, 1.618, 1.619, 1.621, 1.622, 1.623, 1.622, 1.619, 1.619, 1.617, 1.617, 1.617, 1.618, 1.618, 1.617, 1.617, 1.614, 1.613, 1.613,
+ 1.608, 1.611, 1.614, 1.617, 1.618, 1.618, 1.618, 1.618, 1.618, 1.618, 1.619, 1.621, 1.621, 1.621, 1.621, 1.621, 1.622, 1.623, 1.624, 1.623, 1.622, 1.619, 1.619, 1.618, 1.618, 1.618, 1.618, 1.618, 1.618, 1.617, 1.615, 1.614,
+ 1.611, 1.613, 1.616, 1.618, 1.621, 1.621, 1.619, 1.619, 1.619, 1.619, 1.619, 1.621, 1.622, 1.623, 1.623, 1.623, 1.623, 1.624, 1.626, 1.624, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.621, 1.621, 1.619, 1.617, 1.616, 1.616,
+ 1.611, 1.613, 1.617, 1.621, 1.622, 1.622, 1.621, 1.619, 1.619, 1.619, 1.621, 1.622, 1.624, 1.624, 1.624, 1.624, 1.625, 1.626, 1.626, 1.624, 1.623, 1.621, 1.621, 1.619, 1.619, 1.619, 1.621, 1.621, 1.621, 1.619, 1.618, 1.617,
+ 1.613, 1.615, 1.618, 1.621, 1.623, 1.623, 1.622, 1.621, 1.619, 1.619, 1.621, 1.622, 1.625, 1.625, 1.626, 1.626, 1.625, 1.626, 1.626, 1.624, 1.622, 1.621, 1.619, 1.619, 1.619, 1.621, 1.622, 1.622, 1.621, 1.621, 1.619, 1.618,
+ 1.614, 1.617, 1.621, 1.623, 1.624, 1.624, 1.623, 1.621, 1.621, 1.621, 1.622, 1.625, 1.627, 1.627, 1.628, 1.628, 1.628, 1.628, 1.627, 1.626, 1.623, 1.621, 1.621, 1.621, 1.621, 1.623, 1.623, 1.623, 1.623, 1.621, 1.619, 1.619,
+ 1.616, 1.617, 1.622, 1.624, 1.625, 1.625, 1.624, 1.623, 1.622, 1.623, 1.624, 1.627, 1.629, 1.631, 1.631, 1.631, 1.631, 1.631, 1.631, 1.628, 1.626, 1.623, 1.622, 1.622, 1.623, 1.623, 1.624, 1.624, 1.624, 1.622, 1.621, 1.621,
+ 1.617, 1.618, 1.623, 1.625, 1.626, 1.626, 1.625, 1.624, 1.623, 1.624, 1.625, 1.629, 1.631, 1.633, 1.634, 1.634, 1.634, 1.633, 1.633, 1.631, 1.628, 1.624, 1.623, 1.623, 1.623, 1.625, 1.625, 1.625, 1.625, 1.623, 1.622, 1.622,
+ 1.617, 1.619, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.624, 1.625, 1.628, 1.632, 1.634, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.627, 1.624, 1.623, 1.623, 1.623, 1.624, 1.625, 1.626, 1.625, 1.624, 1.623, 1.623,
+ 1.618, 1.619, 1.623, 1.626, 1.627, 1.626, 1.626, 1.625, 1.624, 1.624, 1.625, 1.628, 1.631, 1.634, 1.634, 1.634, 1.634, 1.634, 1.633, 1.631, 1.628, 1.623, 1.622, 1.622, 1.623, 1.624, 1.625, 1.626, 1.626, 1.624, 1.624, 1.623,
+ 1.618, 1.619, 1.623, 1.626, 1.627, 1.627, 1.625, 1.624, 1.624, 1.624, 1.625, 1.628, 1.632, 1.633, 1.634, 1.634, 1.634, 1.633, 1.633, 1.631, 1.627, 1.623, 1.622, 1.622, 1.623, 1.624, 1.624, 1.625, 1.625, 1.624, 1.623, 1.623,
+ 1.619, 1.621, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.624, 1.626, 1.628, 1.632, 1.634, 1.635, 1.634, 1.634, 1.633, 1.633, 1.631, 1.628, 1.625, 1.622, 1.622, 1.622, 1.623, 1.624, 1.625, 1.625, 1.624, 1.623, 1.623,
+ 1.619, 1.621, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.625, 1.627, 1.629, 1.633, 1.635, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.628, 1.625, 1.623, 1.622, 1.622, 1.623, 1.624, 1.624, 1.624, 1.623, 1.623, 1.622,
+ 1.619, 1.621, 1.624, 1.626, 1.628, 1.628, 1.627, 1.626, 1.625, 1.626, 1.627, 1.629, 1.633, 1.635, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.628, 1.625, 1.623, 1.623, 1.623, 1.623, 1.624, 1.624, 1.624, 1.622, 1.622, 1.622,
+ 1.619, 1.621, 1.623, 1.626, 1.628, 1.628, 1.627, 1.626, 1.625, 1.626, 1.627, 1.629, 1.632, 1.634, 1.635, 1.635, 1.634, 1.634, 1.632, 1.631, 1.628, 1.624, 1.622, 1.622, 1.622, 1.623, 1.624, 1.624, 1.624, 1.622, 1.621, 1.621,
+ 1.619, 1.621, 1.623, 1.627, 1.628, 1.628, 1.627, 1.627, 1.626, 1.627, 1.628, 1.629, 1.631, 1.633, 1.634, 1.633, 1.633, 1.632, 1.631, 1.631, 1.627, 1.624, 1.622, 1.622, 1.622, 1.622, 1.623, 1.623, 1.623, 1.621, 1.621, 1.621,
+ 1.621, 1.621, 1.624, 1.627, 1.628, 1.628, 1.627, 1.627, 1.627, 1.627, 1.628, 1.631, 1.632, 1.633, 1.633, 1.632, 1.632, 1.632, 1.631, 1.631, 1.628, 1.625, 1.623, 1.622, 1.622, 1.622, 1.623, 1.623, 1.623, 1.621, 1.621, 1.621,
+ 1.621, 1.621, 1.623, 1.627, 1.628, 1.628, 1.628, 1.627, 1.627, 1.628, 1.628, 1.629, 1.631, 1.632, 1.633, 1.632, 1.631, 1.631, 1.631, 1.629, 1.628, 1.625, 1.624, 1.623, 1.623, 1.623, 1.623, 1.623, 1.623, 1.621, 1.621, 1.619,
+ 1.619, 1.621, 1.623, 1.626, 1.628, 1.629, 1.627, 1.627, 1.627, 1.627, 1.628, 1.629, 1.631, 1.631, 1.631, 1.631, 1.631, 1.629, 1.629, 1.628, 1.626, 1.624, 1.623, 1.623, 1.623, 1.622, 1.623, 1.623, 1.622, 1.621, 1.619, 1.619,
+ 1.618, 1.619, 1.623, 1.625, 1.627, 1.627, 1.627, 1.627, 1.626, 1.627, 1.627, 1.628, 1.628, 1.629, 1.628, 1.628, 1.628, 1.628, 1.628, 1.627, 1.625, 1.623, 1.621, 1.621, 1.621, 1.622, 1.622, 1.622, 1.621, 1.619, 1.618, 1.618,
+ 1.618, 1.619, 1.622, 1.624, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.627, 1.627, 1.627, 1.626, 1.626, 1.626, 1.626, 1.626, 1.624, 1.622, 1.621, 1.621, 1.619, 1.621, 1.621, 1.621, 1.621, 1.618, 1.617, 1.617,
+ 1.616, 1.618, 1.621, 1.623, 1.624, 1.625, 1.625, 1.625, 1.625, 1.625, 1.626, 1.626, 1.627, 1.627, 1.625, 1.624, 1.624, 1.625, 1.626, 1.625, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.621, 1.621, 1.619, 1.616, 1.616, 1.616,
+ 1.615, 1.616, 1.619, 1.621, 1.623, 1.624, 1.625, 1.624, 1.624, 1.625, 1.626, 1.627, 1.627, 1.626, 1.626, 1.625, 1.624, 1.625, 1.625, 1.625, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.619, 1.619, 1.618, 1.616, 1.615, 1.614,
+ 1.614, 1.615, 1.616, 1.621, 1.621, 1.623, 1.624, 1.623, 1.624, 1.624, 1.625, 1.627, 1.627, 1.627, 1.626, 1.625, 1.625, 1.625, 1.625, 1.624, 1.622, 1.621, 1.619, 1.618, 1.617, 1.617, 1.617, 1.617, 1.616, 1.613, 1.612, 1.612,
+ 1.612, 1.612, 1.615, 1.617, 1.621, 1.621, 1.622, 1.622, 1.622, 1.624, 1.625, 1.626, 1.626, 1.626, 1.625, 1.624, 1.624, 1.624, 1.624, 1.623, 1.621, 1.619, 1.618, 1.616, 1.615, 1.615, 1.615, 1.615, 1.613, 1.611, 1.609, 1.609,
+ 1.611, 1.611, 1.612, 1.615, 1.618, 1.619, 1.621, 1.621, 1.622, 1.623, 1.624, 1.626, 1.626, 1.626, 1.625, 1.624, 1.624, 1.624, 1.623, 1.622, 1.621, 1.618, 1.617, 1.615, 1.615, 1.614, 1.614, 1.613, 1.611, 1.609, 1.609, 1.609,
+ 1.611, 1.611, 1.612, 1.615, 1.618, 1.619, 1.621, 1.622, 1.623, 1.625, 1.625, 1.627, 1.627, 1.627, 1.626, 1.626, 1.626, 1.626, 1.624, 1.622, 1.621, 1.618, 1.617, 1.617, 1.616, 1.615, 1.614, 1.613, 1.612, 1.609, 1.609, 1.609,
+ 1.612, 1.612, 1.614, 1.617, 1.619, 1.621, 1.623, 1.624, 1.625, 1.626, 1.627, 1.629, 1.631, 1.629, 1.629, 1.629, 1.628, 1.629, 1.628, 1.626, 1.624, 1.621, 1.621, 1.619, 1.619, 1.618, 1.616, 1.616, 1.613, 1.611, 1.612, 1.612
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.977, 2.794, 2.572, 2.375, 2.218, 2.098, 1.995, 1.903, 1.815, 1.731, 1.647, 1.571, 1.516, 1.493, 1.483, 1.481, 1.481, 1.481, 1.489, 1.511, 1.571, 1.643, 1.729, 1.813, 1.901, 1.993, 2.091, 2.208, 2.364, 2.563, 2.785, 2.971,
+ 2.951, 2.736, 2.512, 2.312, 2.153, 2.031, 1.926, 1.824, 1.736, 1.649, 1.571, 1.506, 1.456, 1.419, 1.396, 1.386, 1.386, 1.392, 1.414, 1.451, 1.505, 1.571, 1.648, 1.733, 1.824, 1.922, 2.025, 2.144, 2.301, 2.499, 2.725, 2.939,
+ 2.883, 2.701, 2.471, 2.266, 2.102, 1.974, 1.861, 1.753, 1.649, 1.571, 1.502, 1.425, 1.361, 1.322, 1.298, 1.286, 1.286, 1.294, 1.317, 1.359, 1.424, 1.501, 1.571, 1.648, 1.751, 1.857, 1.968, 2.095, 2.254, 2.458, 2.688, 2.872,
+ 2.788, 2.632, 2.408, 2.209, 2.056, 1.931, 1.816, 1.704, 1.598, 1.503, 1.425, 1.361, 1.322, 1.298, 1.269, 1.245, 1.243, 1.264, 1.293, 1.317, 1.359, 1.424, 1.501, 1.596, 1.702, 1.812, 1.924, 2.046, 2.197, 2.392, 2.619, 2.777,
+ 2.712, 2.541, 2.327, 2.155, 2.023, 1.908, 1.796, 1.684, 1.578, 1.488, 1.412, 1.351, 1.304, 1.269, 1.245, 1.235, 1.235, 1.243, 1.264, 1.301, 1.349, 1.411, 1.485, 1.577, 1.683, 1.791, 1.902, 2.016, 2.143, 2.312, 2.528, 2.702,
+ 2.678, 2.469, 2.269, 2.117, 1.998, 1.885, 1.773, 1.661, 1.556, 1.469, 1.397, 1.336, 1.277, 1.245, 1.234, 1.226, 1.226, 1.232, 1.244, 1.273, 1.332, 1.392, 1.465, 1.555, 1.659, 1.768, 1.879, 1.991, 2.109, 2.256, 2.454, 2.665,
+ 2.659, 2.433, 2.232, 2.081, 1.957, 1.841, 1.722, 1.606, 1.499, 1.409, 1.337, 1.277, 1.232, 1.198, 1.175, 1.166, 1.166, 1.172, 1.193, 1.228, 1.272, 1.334, 1.408, 1.499, 1.608, 1.717, 1.834, 1.951, 2.073, 2.222, 2.419, 2.648,
+ 2.624, 2.411, 2.204, 2.041, 1.909, 1.784, 1.661, 1.539, 1.431, 1.337, 1.277, 1.219, 1.159, 1.118, 1.096, 1.085, 1.085, 1.092, 1.114, 1.156, 1.219, 1.272, 1.337, 1.429, 1.539, 1.658, 1.779, 1.904, 2.033, 2.193, 2.397, 2.613,
+ 2.564, 2.377, 2.169, 2.012, 1.879, 1.749, 1.623, 1.501, 1.392, 1.299, 1.227, 1.169, 1.125, 1.097, 1.079, 1.063, 1.063, 1.076, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.501, 1.622, 1.746, 1.875, 2.005, 2.161, 2.362, 2.554,
+ 2.515, 2.325, 2.138, 1.997, 1.869, 1.742, 1.617, 1.501, 1.392, 1.299, 1.227, 1.169, 1.125, 1.095, 1.079, 1.063, 1.063, 1.076, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.499, 1.615, 1.741, 1.867, 1.991, 2.132, 2.316, 2.505,
+ 2.498, 2.289, 2.121, 1.988, 1.867, 1.741, 1.616, 1.499, 1.391, 1.299, 1.227, 1.169, 1.125, 1.095, 1.082, 1.065, 1.064, 1.079, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.498, 1.614, 1.738, 1.864, 1.985, 2.116, 2.281, 2.486,
+ 2.498, 2.272, 2.105, 1.971, 1.846, 1.718, 1.592, 1.475, 1.371, 1.279, 1.211, 1.156, 1.112, 1.083, 1.064, 1.055, 1.055, 1.062, 1.081, 1.109, 1.154, 1.212, 1.285, 1.372, 1.473, 1.589, 1.712, 1.843, 1.967, 2.101, 2.263, 2.486,
+ 2.497, 2.267, 2.088, 1.946, 1.813, 1.679, 1.549, 1.431, 1.324, 1.231, 1.159, 1.114, 1.079, 1.035, 1.008, 1.001, 1.001, 1.008, 1.032, 1.076, 1.111, 1.161, 1.235, 1.324, 1.429, 1.547, 1.677, 1.811, 1.941, 2.082, 2.257, 2.484,
+ 2.476, 2.262, 2.077, 1.933, 1.802, 1.671, 1.541, 1.421, 1.317, 1.227, 1.157, 1.101, 1.059, 1.027, 1.004, 1.001, 1.001, 1.004, 1.024, 1.054, 1.098, 1.157, 1.229, 1.317, 1.419, 1.537, 1.667, 1.799, 1.931, 2.071, 2.251, 2.463,
+ 2.455, 2.246, 2.076, 1.933, 1.802, 1.671, 1.541, 1.421, 1.317, 1.227, 1.157, 1.103, 1.064, 1.035, 1.011, 1.003, 1.003, 1.009, 1.032, 1.062, 1.099, 1.157, 1.229, 1.317, 1.419, 1.537, 1.667, 1.799, 1.931, 2.071, 2.236, 2.446,
+ 2.454, 2.239, 2.077, 1.946, 1.817, 1.686, 1.561, 1.444, 1.342, 1.255, 1.189, 1.136, 1.093, 1.059, 1.039, 1.038, 1.038, 1.039, 1.056, 1.091, 1.131, 1.187, 1.258, 1.341, 1.441, 1.556, 1.683, 1.813, 1.939, 2.071, 2.229, 2.445,
+ 2.454, 2.239, 2.079, 1.946, 1.817, 1.686, 1.561, 1.444, 1.342, 1.255, 1.189, 1.136, 1.093, 1.062, 1.039, 1.038, 1.038, 1.039, 1.059, 1.091, 1.131, 1.187, 1.258, 1.341, 1.441, 1.556, 1.683, 1.813, 1.939, 2.071, 2.229, 2.445,
+ 2.458, 2.251, 2.079, 1.941, 1.807, 1.672, 1.543, 1.424, 1.319, 1.231, 1.162, 1.107, 1.065, 1.045, 1.018, 1.003, 1.003, 1.017, 1.044, 1.062, 1.103, 1.159, 1.232, 1.317, 1.419, 1.539, 1.669, 1.802, 1.933, 2.072, 2.239, 2.445,
+ 2.479, 2.265, 2.085, 1.941, 1.807, 1.672, 1.543, 1.424, 1.319, 1.231, 1.162, 1.107, 1.064, 1.031, 1.017, 1.003, 1.003, 1.017, 1.031, 1.059, 1.103, 1.159, 1.232, 1.317, 1.419, 1.539, 1.669, 1.802, 1.933, 2.076, 2.252, 2.468,
+ 2.504, 2.277, 2.099, 1.958, 1.826, 1.695, 1.565, 1.445, 1.338, 1.249, 1.181, 1.129, 1.095, 1.051, 1.027, 1.018, 1.018, 1.028, 1.049, 1.092, 1.127, 1.179, 1.252, 1.339, 1.442, 1.561, 1.691, 1.822, 1.949, 2.089, 2.263, 2.492,
+ 2.509, 2.288, 2.118, 1.982, 1.858, 1.728, 1.604, 1.486, 1.381, 1.293, 1.227, 1.173, 1.127, 1.098, 1.076, 1.067, 1.067, 1.077, 1.097, 1.121, 1.168, 1.225, 1.296, 1.382, 1.483, 1.598, 1.723, 1.852, 1.975, 2.107, 2.274, 2.496,
+ 2.515, 2.312, 2.139, 2.002, 1.877, 1.751, 1.629, 1.512, 1.405, 1.318, 1.248, 1.193, 1.149, 1.118, 1.096, 1.085, 1.085, 1.095, 1.114, 1.145, 1.188, 1.246, 1.319, 1.405, 1.508, 1.623, 1.747, 1.873, 1.995, 2.127, 2.297, 2.501,
+ 2.541, 2.351, 2.161, 2.016, 1.888, 1.762, 1.638, 1.519, 1.411, 1.319, 1.251, 1.197, 1.154, 1.121, 1.099, 1.091, 1.091, 1.099, 1.119, 1.148, 1.192, 1.248, 1.321, 1.411, 1.515, 1.633, 1.758, 1.884, 2.009, 2.149, 2.334, 2.526,
+ 2.588, 2.394, 2.193, 2.036, 1.905, 1.779, 1.656, 1.537, 1.426, 1.329, 1.255, 1.198, 1.161, 1.139, 1.118, 1.096, 1.095, 1.114, 1.138, 1.158, 1.195, 1.256, 1.333, 1.425, 1.533, 1.651, 1.777, 1.902, 2.028, 2.181, 2.378, 2.571,
+ 2.639, 2.431, 2.226, 2.067, 1.937, 1.816, 1.695, 1.577, 1.467, 1.368, 1.298, 1.253, 1.198, 1.161, 1.139, 1.129, 1.129, 1.138, 1.158, 1.195, 1.245, 1.296, 1.374, 1.468, 1.574, 1.692, 1.812, 1.934, 2.059, 2.216, 2.418, 2.626,
+ 2.679, 2.465, 2.261, 2.104, 1.979, 1.862, 1.746, 1.631, 1.522, 1.426, 1.352, 1.297, 1.254, 1.221, 1.201, 1.189, 1.189, 1.198, 1.217, 1.246, 1.293, 1.354, 1.433, 1.526, 1.631, 1.744, 1.859, 1.975, 2.097, 2.252, 2.452, 2.667,
+ 2.711, 2.511, 2.302, 2.141, 2.018, 1.903, 1.791, 1.678, 1.571, 1.475, 1.401, 1.343, 1.297, 1.268, 1.247, 1.236, 1.236, 1.244, 1.263, 1.291, 1.341, 1.403, 1.484, 1.575, 1.679, 1.791, 1.902, 2.012, 2.136, 2.295, 2.501, 2.698,
+ 2.759, 2.582, 2.363, 2.184, 2.049, 1.935, 1.824, 1.714, 1.608, 1.511, 1.431, 1.371, 1.325, 1.295, 1.271, 1.259, 1.259, 1.266, 1.291, 1.318, 1.369, 1.436, 1.517, 1.611, 1.716, 1.825, 1.933, 2.047, 2.179, 2.351, 2.571, 2.748,
+ 2.833, 2.662, 2.433, 2.239, 2.089, 1.968, 1.859, 1.752, 1.646, 1.549, 1.468, 1.411, 1.369, 1.325, 1.296, 1.283, 1.283, 1.292, 1.318, 1.366, 1.411, 1.472, 1.555, 1.651, 1.755, 1.861, 1.969, 2.086, 2.231, 2.422, 2.648, 2.821,
+ 2.909, 2.729, 2.499, 2.298, 2.141, 2.016, 1.907, 1.805, 1.703, 1.611, 1.539, 1.468, 1.411, 1.375, 1.351, 1.339, 1.339, 1.348, 1.372, 1.411, 1.472, 1.543, 1.613, 1.708, 1.807, 1.909, 2.014, 2.135, 2.288, 2.487, 2.716, 2.897,
+ 2.981, 2.789, 2.563, 2.358, 2.197, 2.071, 1.968, 1.868, 1.774, 1.684, 1.607, 1.541, 1.489, 1.453, 1.428, 1.417, 1.417, 1.427, 1.451, 1.489, 1.543, 1.611, 1.686, 1.776, 1.871, 1.966, 2.069, 2.191, 2.349, 2.551, 2.775, 2.964,
+ 3.041, 2.856, 2.629, 2.422, 2.252, 2.127, 2.021, 1.927, 1.834, 1.748, 1.672, 1.604, 1.541, 1.495, 1.483, 1.483, 1.483, 1.483, 1.496, 1.543, 1.608, 1.673, 1.749, 1.835, 1.926, 2.019, 2.122, 2.249, 2.411, 2.614, 2.839, 3.026
+ ],
+ "sigma": 0.00163,
+ "sigma_Cb": 0.0011
+ }
+ },
+ {
+ "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": 2964,
+ "ccm":
+ [
+ 1.72129, -0.45961, -0.26169,
+ -0.30042, 1.56924, -0.26882,
+ 0.15133, -1.13293, 1.98161
+ ]
+ },
+ {
+ "ct": 3610,
+ "ccm":
+ [
+ 1.54474, -0.35082, -0.19391,
+ -0.36989, 1.67926, -0.30936,
+ -0.00524, -0.55197, 1.55722
+ ]
+ },
+ {
+ "ct": 4640,
+ "ccm":
+ [
+ 1.52972, -0.35168, -0.17804,
+ -0.28309, 1.67098, -0.38788,
+ 0.01695, -0.57209, 1.55515
+ ]
+ },
+ {
+ "ct": 5910,
+ "ccm":
+ [
+ 1.56879, -0.42159, -0.14719,
+ -0.27275, 1.59354, -0.32079,
+ -0.02862, -0.40662, 1.43525
+ ]
+ },
+ {
+ "ct": 7590,
+ "ccm":
+ [
+ 1.41424, -0.21092, -0.20332,
+ -0.17646, 1.71734, -0.54087,
+ 0.01297, -0.63111, 1.61814
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "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 ]
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "strength": 1.0,
+ "lut_rx":
+ [
+ -0.21, -0.12, -0.06, -0.04, -0.03, -0.0, 0.02, 0.08, 0.21,
+ -0.2, -0.12, -0.07, -0.05, -0.01, 0.02, 0.02, 0.06, 0.19,
+ -0.18, -0.12, -0.09, -0.07, -0.01, 0.03, 0.03, 0.04, 0.13,
+ -0.15, -0.11, -0.1, -0.09, -0.01, 0.04, 0.04, 0.04, 0.09,
+ -0.15, -0.11, -0.1, -0.09, -0.02, 0.05, 0.05, 0.05, 0.08,
+ -0.16, -0.11, -0.08, -0.07, -0.02, 0.05, 0.06, 0.07, 0.1,
+ -0.18, -0.1, -0.07, -0.05, -0.01, 0.03, 0.05, 0.08, 0.15,
+ -0.21, -0.11, -0.06, -0.04, -0.01, 0.02, 0.04, 0.09, 0.22,
+ -0.23, -0.14, -0.05, -0.03, -0.01, 0.01, 0.03, 0.1, 0.23
+ ],
+ "lut_ry":
+ [
+ -0.13, -0.08, -0.06, -0.08, -0.08, -0.06, -0.04, -0.06, -0.08,
+ -0.09, -0.05, -0.05, -0.09, -0.1, -0.08, -0.05, -0.04, -0.06,
+ -0.04, -0.05, -0.06, -0.1, -0.13, -0.1, -0.06, -0.04, -0.02,
+ -0.03, -0.04, -0.06, -0.09, -0.11, -0.1, -0.06, -0.03, 0.01,
+ -0.01, -0.01, -0.03, -0.03, -0.03, -0.04, -0.03, -0.01, 0.02,
+ 0.03, 0.01, -0.01, 0.0, 0.01, 0.01, -0.0, 0.01, 0.03,
+ 0.05, 0.02, 0.01, 0.02, 0.03, 0.02, 0.01, 0.03, 0.07,
+ 0.08, 0.03, 0.01, 0.01, 0.02, 0.02, 0.02, 0.05, 0.12,
+ 0.11, 0.07, 0.01, 0.0, -0.0, 0.01, 0.03, 0.07, 0.14
+ ],
+ "lut_bx":
+ [
+ 0.27, 0.13, 0.03, -0.01, -0.01, -0.0, -0.04, -0.11, -0.29,
+ 0.23, 0.1, 0.02, -0.01, -0.02, -0.01, -0.03, -0.1, -0.28,
+ 0.22, 0.08, 0.0, -0.01, -0.02, -0.02, -0.02, -0.08, -0.25,
+ 0.2, 0.08, 0.01, 0.0, -0.01, -0.02, -0.01, -0.07, -0.22,
+ 0.19, 0.08, 0.01, 0.0, -0.01, -0.02, -0.02, -0.06, -0.21,
+ 0.2, 0.08, 0.01, 0.0, -0.01, -0.02, -0.02, -0.07, -0.22,
+ 0.21, 0.09, 0.01, -0.01, -0.02, -0.02, -0.03, -0.09, -0.26,
+ 0.21, 0.11, 0.02, -0.01, -0.01, -0.02, -0.04, -0.11, -0.28,
+ 0.23, 0.13, 0.04, -0.01, -0.01, -0.01, -0.06, -0.13, -0.31
+ ],
+ "lut_by":
+ [
+ 0.17, 0.11, 0.07, 0.05, 0.04, 0.05, 0.07, 0.12, 0.19,
+ 0.11, 0.06, 0.04, 0.04, 0.04, 0.03, 0.04, 0.06, 0.13,
+ 0.06, 0.03, 0.02, 0.04, 0.05, 0.04, 0.03, 0.03, 0.06,
+ 0.02, 0.02, 0.03, 0.04, 0.06, 0.05, 0.03, 0.02, 0.01,
+ -0.0, 0.01, 0.03, 0.04, 0.04, 0.04, 0.02, -0.0, -0.03,
+ -0.04, -0.01, 0.02, 0.02, 0.02, 0.02, 0.01, -0.02, -0.09,
+ -0.08, -0.01, 0.04, 0.06, 0.06, 0.05, 0.03, -0.03, -0.14,
+ -0.1, -0.04, 0.04, 0.08, 0.08, 0.06, 0.02, -0.05, -0.18,
+ -0.15, -0.08, 0.02, 0.09, 0.11, 0.08, 0.01, -0.09, -0.22
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx708_noir.json b/src/ipa/rpi/pisp/data/imx708_noir.json
new file mode 100644
index 00000000..e69afb0c
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx708_noir.json
@@ -0,0 +1,1233 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 20716,
+ "reference_gain": 1.12,
+ "reference_aperture": 1.0,
+ "reference_lux": 810,
+ "reference_Y": 13994
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 1.856
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 221,
+ "slope": 0.00226
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.532, 1.534, 1.535, 1.538, 1.538, 1.533, 1.529, 1.515, 1.506, 1.492, 1.477, 1.465, 1.453, 1.444, 1.437, 1.433, 1.433, 1.435, 1.441, 1.449, 1.461, 1.474, 1.485, 1.499, 1.511, 1.519, 1.525, 1.526, 1.526, 1.523, 1.517, 1.516,
+ 1.532, 1.534, 1.537, 1.538, 1.537, 1.534, 1.525, 1.515, 1.502, 1.486, 1.474, 1.458, 1.449, 1.438, 1.429, 1.427, 1.426, 1.429, 1.436, 1.444, 1.456, 1.468, 1.483, 1.497, 1.509, 1.518, 1.524, 1.526, 1.526, 1.523, 1.521, 1.516,
+ 1.532, 1.534, 1.537, 1.538, 1.536, 1.533, 1.524, 1.512, 1.499, 1.483, 1.468, 1.453, 1.439, 1.429, 1.421, 1.419, 1.419, 1.419, 1.427, 1.438, 1.451, 1.464, 1.479, 1.494, 1.506, 1.516, 1.523, 1.526, 1.526, 1.524, 1.521, 1.518,
+ 1.533, 1.536, 1.537, 1.537, 1.535, 1.532, 1.521, 1.507, 1.491, 1.474, 1.456, 1.441, 1.429, 1.418, 1.409, 1.406, 1.406, 1.408, 1.415, 1.426, 1.439, 1.453, 1.471, 1.485, 1.501, 1.511, 1.522, 1.524, 1.526, 1.525, 1.522, 1.519,
+ 1.537, 1.538, 1.539, 1.538, 1.534, 1.525, 1.513, 1.495, 1.477, 1.459, 1.443, 1.427, 1.413, 1.402, 1.394, 1.391, 1.391, 1.393, 1.399, 1.409, 1.424, 1.439, 1.455, 1.472, 1.489, 1.503, 1.515, 1.523, 1.526, 1.527, 1.525, 1.523,
+ 1.538, 1.539, 1.541, 1.539, 1.531, 1.519, 1.503, 1.484, 1.466, 1.445, 1.427, 1.413, 1.401, 1.386, 1.378, 1.373, 1.373, 1.376, 1.386, 1.398, 1.409, 1.424, 1.441, 1.459, 1.477, 1.495, 1.509, 1.519, 1.526, 1.528, 1.528, 1.526,
+ 1.539, 1.541, 1.541, 1.539, 1.529, 1.516, 1.498, 1.479, 1.456, 1.437, 1.417, 1.401, 1.386, 1.378, 1.369, 1.363, 1.363, 1.367, 1.376, 1.386, 1.399, 1.413, 1.432, 1.451, 1.472, 1.491, 1.507, 1.517, 1.525, 1.527, 1.527, 1.527,
+ 1.539, 1.539, 1.539, 1.538, 1.529, 1.515, 1.497, 1.476, 1.454, 1.433, 1.411, 1.395, 1.381, 1.368, 1.361, 1.356, 1.356, 1.359, 1.367, 1.379, 1.393, 1.409, 1.428, 1.448, 1.471, 1.489, 1.505, 1.516, 1.524, 1.527, 1.527, 1.527,
+ 1.539, 1.539, 1.539, 1.537, 1.528, 1.513, 1.493, 1.471, 1.449, 1.426, 1.406, 1.387, 1.373, 1.361, 1.352, 1.348, 1.348, 1.351, 1.359, 1.372, 1.387, 1.403, 1.422, 1.443, 1.465, 1.484, 1.503, 1.516, 1.525, 1.527, 1.528, 1.526,
+ 1.541, 1.542, 1.539, 1.537, 1.524, 1.506, 1.485, 1.461, 1.438, 1.416, 1.395, 1.377, 1.362, 1.352, 1.344, 1.339, 1.339, 1.342, 1.351, 1.362, 1.376, 1.393, 1.412, 1.434, 1.455, 1.477, 1.495, 1.514, 1.524, 1.528, 1.529, 1.529,
+ 1.543, 1.544, 1.543, 1.534, 1.518, 1.499, 1.476, 1.452, 1.427, 1.405, 1.386, 1.367, 1.354, 1.344, 1.338, 1.329, 1.329, 1.335, 1.342, 1.352, 1.367, 1.382, 1.402, 1.424, 1.445, 1.469, 1.491, 1.507, 1.522, 1.528, 1.529, 1.532,
+ 1.544, 1.544, 1.542, 1.534, 1.518, 1.499, 1.474, 1.449, 1.425, 1.401, 1.379, 1.362, 1.348, 1.338, 1.329, 1.324, 1.325, 1.329, 1.335, 1.347, 1.361, 1.378, 1.397, 1.421, 1.443, 1.467, 1.489, 1.507, 1.521, 1.529, 1.532, 1.533,
+ 1.543, 1.543, 1.541, 1.534, 1.519, 1.499, 1.474, 1.448, 1.424, 1.399, 1.377, 1.359, 1.346, 1.333, 1.324, 1.322, 1.321, 1.324, 1.332, 1.344, 1.359, 1.376, 1.397, 1.419, 1.443, 1.467, 1.489, 1.508, 1.521, 1.528, 1.531, 1.532,
+ 1.543, 1.542, 1.541, 1.533, 1.519, 1.499, 1.474, 1.448, 1.422, 1.399, 1.376, 1.358, 1.344, 1.331, 1.322, 1.319, 1.319, 1.321, 1.331, 1.342, 1.357, 1.375, 1.396, 1.419, 1.443, 1.467, 1.489, 1.508, 1.521, 1.529, 1.531, 1.532,
+ 1.543, 1.542, 1.541, 1.532, 1.518, 1.496, 1.471, 1.445, 1.418, 1.393, 1.373, 1.354, 1.341, 1.329, 1.319, 1.317, 1.316, 1.319, 1.327, 1.338, 1.353, 1.371, 1.392, 1.415, 1.439, 1.465, 1.485, 1.507, 1.519, 1.529, 1.531, 1.531,
+ 1.545, 1.544, 1.542, 1.531, 1.515, 1.493, 1.467, 1.441, 1.414, 1.391, 1.369, 1.351, 1.337, 1.326, 1.318, 1.314, 1.314, 1.317, 1.325, 1.335, 1.351, 1.367, 1.388, 1.411, 1.436, 1.461, 1.483, 1.505, 1.519, 1.531, 1.533, 1.533,
+ 1.545, 1.544, 1.541, 1.531, 1.515, 1.493, 1.467, 1.441, 1.414, 1.391, 1.369, 1.351, 1.337, 1.326, 1.318, 1.314, 1.314, 1.317, 1.325, 1.335, 1.351, 1.367, 1.388, 1.411, 1.436, 1.461, 1.483, 1.505, 1.521, 1.531, 1.534, 1.534,
+ 1.545, 1.544, 1.541, 1.534, 1.519, 1.496, 1.471, 1.446, 1.419, 1.392, 1.372, 1.354, 1.338, 1.328, 1.319, 1.316, 1.315, 1.319, 1.327, 1.338, 1.353, 1.371, 1.392, 1.416, 1.441, 1.465, 1.489, 1.511, 1.522, 1.531, 1.534, 1.535,
+ 1.544, 1.544, 1.542, 1.537, 1.524, 1.501, 1.476, 1.449, 1.424, 1.399, 1.377, 1.359, 1.344, 1.332, 1.324, 1.319, 1.319, 1.323, 1.331, 1.343, 1.358, 1.374, 1.396, 1.419, 1.445, 1.471, 1.493, 1.512, 1.525, 1.532, 1.534, 1.534,
+ 1.545, 1.545, 1.543, 1.538, 1.524, 1.503, 1.479, 1.452, 1.426, 1.402, 1.381, 1.362, 1.348, 1.337, 1.329, 1.324, 1.324, 1.328, 1.335, 1.347, 1.361, 1.379, 1.399, 1.423, 1.447, 1.471, 1.493, 1.513, 1.526, 1.533, 1.534, 1.535,
+ 1.546, 1.546, 1.544, 1.539, 1.525, 1.504, 1.479, 1.453, 1.428, 1.404, 1.383, 1.365, 1.352, 1.339, 1.333, 1.329, 1.329, 1.333, 1.339, 1.349, 1.363, 1.381, 1.402, 1.424, 1.448, 1.472, 1.494, 1.514, 1.526, 1.534, 1.534, 1.534,
+ 1.546, 1.546, 1.544, 1.539, 1.526, 1.505, 1.483, 1.457, 1.432, 1.407, 1.389, 1.371, 1.357, 1.347, 1.339, 1.333, 1.333, 1.339, 1.345, 1.354, 1.368, 1.386, 1.406, 1.428, 1.453, 1.475, 1.496, 1.515, 1.527, 1.535, 1.535, 1.535,
+ 1.545, 1.545, 1.545, 1.541, 1.529, 1.513, 1.491, 1.467, 1.441, 1.418, 1.399, 1.379, 1.366, 1.355, 1.347, 1.341, 1.341, 1.345, 1.354, 1.364, 1.378, 1.395, 1.415, 1.436, 1.459, 1.483, 1.503, 1.519, 1.531, 1.534, 1.535, 1.534,
+ 1.544, 1.545, 1.545, 1.544, 1.535, 1.519, 1.499, 1.476, 1.451, 1.428, 1.409, 1.391, 1.377, 1.366, 1.356, 1.352, 1.352, 1.355, 1.364, 1.374, 1.388, 1.405, 1.426, 1.447, 1.469, 1.492, 1.509, 1.523, 1.532, 1.535, 1.535, 1.533,
+ 1.544, 1.545, 1.546, 1.545, 1.537, 1.523, 1.504, 1.482, 1.458, 1.436, 1.418, 1.401, 1.385, 1.377, 1.367, 1.362, 1.362, 1.365, 1.373, 1.385, 1.398, 1.415, 1.434, 1.455, 1.477, 1.495, 1.514, 1.525, 1.533, 1.536, 1.535, 1.533,
+ 1.545, 1.546, 1.547, 1.545, 1.538, 1.525, 1.508, 1.486, 1.465, 1.444, 1.424, 1.408, 1.394, 1.385, 1.377, 1.371, 1.371, 1.373, 1.384, 1.392, 1.405, 1.421, 1.441, 1.459, 1.481, 1.499, 1.516, 1.528, 1.534, 1.536, 1.536, 1.533,
+ 1.544, 1.546, 1.547, 1.547, 1.541, 1.531, 1.514, 1.494, 1.474, 1.454, 1.434, 1.421, 1.408, 1.394, 1.386, 1.382, 1.382, 1.385, 1.392, 1.405, 1.416, 1.432, 1.449, 1.468, 1.488, 1.505, 1.519, 1.531, 1.536, 1.537, 1.536, 1.533,
+ 1.544, 1.546, 1.548, 1.548, 1.545, 1.536, 1.522, 1.506, 1.486, 1.467, 1.451, 1.434, 1.421, 1.408, 1.401, 1.396, 1.396, 1.399, 1.407, 1.416, 1.431, 1.447, 1.463, 1.481, 1.499, 1.513, 1.526, 1.534, 1.537, 1.537, 1.534, 1.531,
+ 1.543, 1.545, 1.547, 1.549, 1.549, 1.543, 1.531, 1.517, 1.501, 1.483, 1.465, 1.451, 1.438, 1.425, 1.417, 1.412, 1.412, 1.418, 1.423, 1.433, 1.447, 1.462, 1.479, 1.493, 1.511, 1.524, 1.531, 1.536, 1.538, 1.537, 1.533, 1.531,
+ 1.542, 1.545, 1.548, 1.551, 1.551, 1.546, 1.539, 1.524, 1.511, 1.493, 1.479, 1.464, 1.451, 1.442, 1.433, 1.429, 1.429, 1.434, 1.439, 1.449, 1.462, 1.474, 1.491, 1.505, 1.519, 1.529, 1.536, 1.539, 1.539, 1.537, 1.533, 1.531,
+ 1.541, 1.546, 1.549, 1.552, 1.553, 1.551, 1.544, 1.533, 1.521, 1.505, 1.489, 1.477, 1.464, 1.455, 1.447, 1.443, 1.443, 1.446, 1.451, 1.462, 1.472, 1.487, 1.499, 1.514, 1.525, 1.535, 1.541, 1.541, 1.541, 1.539, 1.533, 1.531,
+ 1.541, 1.546, 1.549, 1.553, 1.554, 1.552, 1.546, 1.537, 1.524, 1.512, 1.499, 1.485, 1.474, 1.464, 1.455, 1.451, 1.451, 1.452, 1.461, 1.469, 1.481, 1.495, 1.506, 1.518, 1.529, 1.539, 1.541, 1.542, 1.541, 1.539, 1.533, 1.529
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.586, 2.591, 2.597, 2.601, 2.601, 2.599, 2.592, 2.576, 2.561, 2.541, 2.523, 2.503, 2.486, 2.471, 2.459, 2.452, 2.452, 2.454, 2.462, 2.478, 2.495, 2.512, 2.531, 2.555, 2.568, 2.579, 2.587, 2.588, 2.585, 2.579, 2.573, 2.566,
+ 2.587, 2.592, 2.598, 2.601, 2.601, 2.599, 2.587, 2.574, 2.556, 2.532, 2.512, 2.491, 2.474, 2.462, 2.449, 2.443, 2.439, 2.443, 2.454, 2.464, 2.485, 2.505, 2.525, 2.548, 2.566, 2.578, 2.585, 2.588, 2.586, 2.579, 2.575, 2.567,
+ 2.587, 2.593, 2.598, 2.602, 2.601, 2.597, 2.584, 2.569, 2.551, 2.527, 2.503, 2.482, 2.464, 2.448, 2.434, 2.428, 2.427, 2.431, 2.439, 2.455, 2.474, 2.498, 2.521, 2.541, 2.564, 2.577, 2.585, 2.588, 2.589, 2.581, 2.576, 2.569,
+ 2.593, 2.596, 2.601, 2.603, 2.601, 2.594, 2.583, 2.563, 2.539, 2.514, 2.491, 2.466, 2.445, 2.429, 2.417, 2.409, 2.408, 2.411, 2.421, 2.437, 2.457, 2.481, 2.507, 2.531, 2.555, 2.572, 2.583, 2.588, 2.588, 2.585, 2.579, 2.575,
+ 2.597, 2.599, 2.604, 2.603, 2.599, 2.587, 2.567, 2.548, 2.522, 2.493, 2.467, 2.443, 2.419, 2.406, 2.391, 2.385, 2.385, 2.387, 2.397, 2.413, 2.435, 2.459, 2.486, 2.509, 2.538, 2.559, 2.574, 2.586, 2.588, 2.586, 2.582, 2.579,
+ 2.601, 2.603, 2.606, 2.604, 2.596, 2.578, 2.556, 2.531, 2.501, 2.471, 2.444, 2.419, 2.402, 2.381, 2.365, 2.359, 2.359, 2.361, 2.374, 2.396, 2.413, 2.435, 2.465, 2.493, 2.517, 2.542, 2.562, 2.582, 2.588, 2.587, 2.586, 2.584,
+ 2.601, 2.604, 2.605, 2.604, 2.593, 2.575, 2.547, 2.522, 2.488, 2.458, 2.432, 2.402, 2.381, 2.364, 2.349, 2.338, 2.338, 2.345, 2.359, 2.374, 2.396, 2.423, 2.453, 2.481, 2.511, 2.539, 2.561, 2.581, 2.586, 2.588, 2.588, 2.586,
+ 2.599, 2.602, 2.604, 2.602, 2.592, 2.572, 2.546, 2.516, 2.485, 2.451, 2.422, 2.393, 2.368, 2.349, 2.336, 2.328, 2.328, 2.333, 2.345, 2.365, 2.389, 2.417, 2.447, 2.478, 2.509, 2.537, 2.561, 2.577, 2.585, 2.588, 2.588, 2.587,
+ 2.601, 2.602, 2.604, 2.601, 2.589, 2.569, 2.539, 2.509, 2.473, 2.442, 2.409, 2.379, 2.357, 2.336, 2.323, 2.315, 2.315, 2.322, 2.334, 2.354, 2.377, 2.406, 2.436, 2.469, 2.503, 2.529, 2.558, 2.574, 2.585, 2.588, 2.589, 2.587,
+ 2.601, 2.606, 2.606, 2.601, 2.581, 2.557, 2.525, 2.493, 2.459, 2.426, 2.394, 2.365, 2.339, 2.322, 2.308, 2.301, 2.301, 2.305, 2.322, 2.337, 2.361, 2.389, 2.422, 2.454, 2.485, 2.519, 2.546, 2.568, 2.584, 2.589, 2.589, 2.589,
+ 2.608, 2.608, 2.606, 2.597, 2.576, 2.548, 2.515, 2.481, 2.444, 2.409, 2.376, 2.346, 2.323, 2.308, 2.293, 2.282, 2.281, 2.291, 2.305, 2.322, 2.348, 2.371, 2.403, 2.439, 2.472, 2.508, 2.538, 2.565, 2.582, 2.589, 2.592, 2.593,
+ 2.608, 2.608, 2.605, 2.596, 2.575, 2.547, 2.511, 2.474, 2.435, 2.401, 2.366, 2.339, 2.312, 2.293, 2.281, 2.274, 2.274, 2.281, 2.291, 2.311, 2.334, 2.364, 2.399, 2.433, 2.471, 2.506, 2.538, 2.564, 2.581, 2.591, 2.594, 2.595,
+ 2.605, 2.606, 2.605, 2.595, 2.575, 2.547, 2.511, 2.474, 2.433, 2.397, 2.363, 2.333, 2.309, 2.291, 2.274, 2.267, 2.265, 2.272, 2.284, 2.307, 2.331, 2.361, 2.395, 2.431, 2.469, 2.503, 2.539, 2.567, 2.584, 2.591, 2.595, 2.595,
+ 2.605, 2.606, 2.605, 2.595, 2.575, 2.547, 2.509, 2.473, 2.431, 2.395, 2.361, 2.332, 2.306, 2.285, 2.267, 2.261, 2.262, 2.265, 2.281, 2.302, 2.329, 2.359, 2.395, 2.429, 2.468, 2.503, 2.539, 2.567, 2.583, 2.593, 2.595, 2.595,
+ 2.608, 2.607, 2.606, 2.592, 2.572, 2.543, 2.506, 2.468, 2.426, 2.389, 2.354, 2.327, 2.299, 2.279, 2.262, 2.258, 2.257, 2.262, 2.276, 2.297, 2.321, 2.352, 2.387, 2.425, 2.464, 2.498, 2.532, 2.565, 2.582, 2.592, 2.595, 2.596,
+ 2.611, 2.609, 2.605, 2.592, 2.571, 2.538, 2.499, 2.463, 2.421, 2.384, 2.351, 2.322, 2.295, 2.276, 2.259, 2.254, 2.254, 2.256, 2.273, 2.292, 2.318, 2.347, 2.383, 2.418, 2.456, 2.491, 2.529, 2.562, 2.581, 2.593, 2.597, 2.598,
+ 2.609, 2.609, 2.606, 2.593, 2.571, 2.538, 2.499, 2.463, 2.421, 2.384, 2.351, 2.321, 2.295, 2.276, 2.259, 2.251, 2.251, 2.256, 2.273, 2.292, 2.318, 2.347, 2.383, 2.418, 2.456, 2.491, 2.529, 2.559, 2.582, 2.595, 2.597, 2.599,
+ 2.609, 2.609, 2.607, 2.597, 2.576, 2.543, 2.507, 2.467, 2.427, 2.388, 2.356, 2.323, 2.297, 2.278, 2.262, 2.256, 2.255, 2.262, 2.275, 2.296, 2.321, 2.351, 2.388, 2.425, 2.464, 2.502, 2.534, 2.563, 2.586, 2.595, 2.598, 2.599,
+ 2.609, 2.609, 2.608, 2.601, 2.581, 2.547, 2.513, 2.475, 2.434, 2.398, 2.362, 2.332, 2.307, 2.287, 2.269, 2.263, 2.263, 2.269, 2.281, 2.304, 2.328, 2.358, 2.394, 2.429, 2.469, 2.508, 2.538, 2.568, 2.589, 2.597, 2.598, 2.598,
+ 2.609, 2.611, 2.609, 2.601, 2.583, 2.549, 2.518, 2.478, 2.439, 2.402, 2.367, 2.337, 2.313, 2.293, 2.279, 2.271, 2.269, 2.277, 2.291, 2.311, 2.336, 2.363, 2.399, 2.435, 2.473, 2.509, 2.541, 2.571, 2.591, 2.598, 2.599, 2.599,
+ 2.611, 2.611, 2.609, 2.602, 2.585, 2.551, 2.519, 2.481, 2.442, 2.406, 2.374, 2.342, 2.318, 2.297, 2.287, 2.279, 2.278, 2.287, 2.297, 2.315, 2.339, 2.368, 2.402, 2.438, 2.476, 2.511, 2.545, 2.571, 2.591, 2.599, 2.601, 2.599,
+ 2.611, 2.611, 2.609, 2.604, 2.587, 2.557, 2.521, 2.485, 2.447, 2.412, 2.379, 2.352, 2.328, 2.309, 2.297, 2.288, 2.287, 2.297, 2.308, 2.327, 2.349, 2.377, 2.408, 2.446, 2.481, 2.517, 2.547, 2.573, 2.591, 2.599, 2.601, 2.599,
+ 2.608, 2.609, 2.609, 2.606, 2.592, 2.564, 2.533, 2.498, 2.462, 2.427, 2.394, 2.364, 2.343, 2.326, 2.309, 2.302, 2.302, 2.308, 2.324, 2.341, 2.362, 2.391, 2.425, 2.458, 2.494, 2.526, 2.555, 2.584, 2.593, 2.599, 2.599, 2.599,
+ 2.608, 2.609, 2.609, 2.609, 2.597, 2.574, 2.547, 2.511, 2.475, 2.438, 2.411, 2.381, 2.359, 2.342, 2.327, 2.318, 2.318, 2.325, 2.341, 2.358, 2.377, 2.404, 2.439, 2.469, 2.507, 2.537, 2.564, 2.587, 2.596, 2.598, 2.598, 2.597,
+ 2.609, 2.609, 2.611, 2.609, 2.599, 2.579, 2.551, 2.519, 2.486, 2.453, 2.425, 2.397, 2.375, 2.358, 2.345, 2.336, 2.336, 2.341, 2.355, 2.372, 2.393, 2.419, 2.452, 2.481, 2.516, 2.542, 2.571, 2.591, 2.597, 2.599, 2.598, 2.595,
+ 2.607, 2.611, 2.613, 2.611, 2.605, 2.586, 2.561, 2.529, 2.495, 2.462, 2.435, 2.409, 2.387, 2.374, 2.359, 2.351, 2.351, 2.356, 2.372, 2.385, 2.406, 2.431, 2.462, 2.488, 2.524, 2.551, 2.573, 2.591, 2.598, 2.599, 2.598, 2.596,
+ 2.606, 2.609, 2.613, 2.613, 2.607, 2.591, 2.565, 2.539, 2.507, 2.477, 2.449, 2.425, 2.409, 2.387, 2.376, 2.369, 2.369, 2.374, 2.385, 2.406, 2.422, 2.446, 2.473, 2.502, 2.534, 2.557, 2.578, 2.595, 2.599, 2.601, 2.598, 2.595,
+ 2.606, 2.611, 2.613, 2.614, 2.611, 2.598, 2.581, 2.553, 2.523, 2.496, 2.471, 2.449, 2.425, 2.409, 2.398, 2.391, 2.391, 2.395, 2.408, 2.422, 2.445, 2.468, 2.493, 2.522, 2.549, 2.569, 2.589, 2.601, 2.603, 2.602, 2.596, 2.593,
+ 2.605, 2.609, 2.613, 2.616, 2.614, 2.607, 2.591, 2.571, 2.545, 2.518, 2.494, 2.471, 2.452, 2.435, 2.423, 2.417, 2.417, 2.421, 2.431, 2.449, 2.467, 2.493, 2.516, 2.542, 2.566, 2.585, 2.596, 2.606, 2.605, 2.602, 2.595, 2.593,
+ 2.604, 2.608, 2.616, 2.617, 2.618, 2.613, 2.602, 2.584, 2.559, 2.536, 2.514, 2.493, 2.476, 2.459, 2.445, 2.439, 2.439, 2.445, 2.456, 2.471, 2.493, 2.511, 2.534, 2.559, 2.579, 2.592, 2.607, 2.608, 2.607, 2.604, 2.595, 2.592,
+ 2.603, 2.609, 2.615, 2.619, 2.623, 2.619, 2.608, 2.594, 2.573, 2.551, 2.532, 2.512, 2.493, 2.477, 2.468, 2.462, 2.462, 2.468, 2.476, 2.494, 2.509, 2.528, 2.551, 2.574, 2.589, 2.604, 2.611, 2.611, 2.611, 2.604, 2.598, 2.592,
+ 2.602, 2.607, 2.613, 2.621, 2.624, 2.621, 2.617, 2.601, 2.585, 2.567, 2.544, 2.521, 2.507, 2.493, 2.478, 2.474, 2.475, 2.477, 2.489, 2.505, 2.523, 2.544, 2.563, 2.584, 2.598, 2.609, 2.612, 2.613, 2.613, 2.608, 2.599, 2.591
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.263, 3.263, 3.264, 3.269, 3.274, 3.275, 3.277, 3.281, 3.283, 3.285, 3.289, 3.292, 3.291, 3.291, 3.294, 3.298, 3.302, 3.305, 3.303, 3.301, 3.296, 3.294, 3.293, 3.293, 3.295, 3.295, 3.295, 3.287, 3.285, 3.279, 3.282, 3.285,
+ 3.259, 3.259, 3.262, 3.268, 3.271, 3.273, 3.273, 3.274, 3.277, 3.278, 3.282, 3.285, 3.286, 3.286, 3.288, 3.289, 3.292, 3.291, 3.293, 3.291, 3.288, 3.288, 3.288, 3.288, 3.288, 3.287, 3.287, 3.283, 3.281, 3.276, 3.277, 3.281,
+ 3.259, 3.259, 3.262, 3.269, 3.272, 3.274, 3.273, 3.273, 3.273, 3.276, 3.278, 3.279, 3.281, 3.282, 3.283, 3.283, 3.285, 3.286, 3.286, 3.285, 3.283, 3.282, 3.282, 3.286, 3.285, 3.285, 3.285, 3.282, 3.281, 3.276, 3.275, 3.279,
+ 3.267, 3.266, 3.269, 3.273, 3.274, 3.277, 3.276, 3.275, 3.274, 3.277, 3.277, 3.278, 3.279, 3.279, 3.279, 3.279, 3.281, 3.283, 3.283, 3.281, 3.281, 3.281, 3.282, 3.287, 3.287, 3.288, 3.287, 3.286, 3.283, 3.277, 3.278, 3.281,
+ 3.268, 3.271, 3.274, 3.277, 3.283, 3.286, 3.284, 3.281, 3.278, 3.278, 3.279, 3.279, 3.281, 3.281, 3.281, 3.281, 3.282, 3.283, 3.284, 3.283, 3.282, 3.282, 3.284, 3.288, 3.289, 3.291, 3.291, 3.288, 3.288, 3.283, 3.286, 3.288,
+ 3.272, 3.275, 3.279, 3.283, 3.288, 3.289, 3.289, 3.287, 3.282, 3.284, 3.282, 3.284, 3.284, 3.285, 3.284, 3.285, 3.285, 3.288, 3.287, 3.286, 3.283, 3.283, 3.285, 3.289, 3.292, 3.292, 3.293, 3.292, 3.291, 3.288, 3.289, 3.293,
+ 3.276, 3.278, 3.282, 3.289, 3.293, 3.293, 3.291, 3.289, 3.287, 3.286, 3.285, 3.285, 3.286, 3.286, 3.287, 3.288, 3.289, 3.289, 3.289, 3.288, 3.285, 3.283, 3.286, 3.289, 3.292, 3.294, 3.294, 3.294, 3.293, 3.289, 3.292, 3.293,
+ 3.279, 3.281, 3.286, 3.293, 3.297, 3.298, 3.295, 3.292, 3.288, 3.287, 3.285, 3.287, 3.288, 3.288, 3.289, 3.291, 3.292, 3.293, 3.291, 3.288, 3.286, 3.285, 3.285, 3.291, 3.294, 3.295, 3.297, 3.297, 3.298, 3.293, 3.294, 3.294,
+ 3.281, 3.286, 3.291, 3.298, 3.301, 3.301, 3.299, 3.295, 3.289, 3.288, 3.286, 3.288, 3.289, 3.292, 3.293, 3.292, 3.295, 3.296, 3.295, 3.291, 3.287, 3.286, 3.286, 3.289, 3.295, 3.297, 3.298, 3.301, 3.301, 3.298, 3.297, 3.297,
+ 3.284, 3.289, 3.295, 3.302, 3.303, 3.303, 3.301, 3.298, 3.294, 3.292, 3.289, 3.293, 3.296, 3.297, 3.297, 3.297, 3.297, 3.298, 3.298, 3.296, 3.289, 3.288, 3.287, 3.294, 3.298, 3.301, 3.304, 3.305, 3.304, 3.299, 3.299, 3.302,
+ 3.291, 3.292, 3.299, 3.305, 3.308, 3.305, 3.304, 3.302, 3.298, 3.295, 3.295, 3.298, 3.299, 3.302, 3.303, 3.302, 3.301, 3.301, 3.301, 3.299, 3.296, 3.291, 3.292, 3.297, 3.301, 3.304, 3.306, 3.309, 3.308, 3.302, 3.301, 3.304,
+ 3.292, 3.297, 3.303, 3.309, 3.312, 3.311, 3.308, 3.304, 3.301, 3.299, 3.298, 3.299, 3.303, 3.305, 3.306, 3.305, 3.305, 3.303, 3.303, 3.301, 3.299, 3.294, 3.294, 3.297, 3.302, 3.305, 3.309, 3.311, 3.311, 3.305, 3.305, 3.306,
+ 3.295, 3.298, 3.305, 3.309, 3.313, 3.313, 3.312, 3.307, 3.303, 3.301, 3.299, 3.301, 3.304, 3.307, 3.308, 3.306, 3.306, 3.306, 3.306, 3.302, 3.299, 3.296, 3.295, 3.298, 3.303, 3.306, 3.311, 3.312, 3.312, 3.307, 3.308, 3.309,
+ 3.297, 3.298, 3.303, 3.309, 3.313, 3.313, 3.311, 3.307, 3.303, 3.301, 3.299, 3.299, 3.305, 3.307, 3.307, 3.306, 3.306, 3.306, 3.305, 3.299, 3.297, 3.294, 3.294, 3.298, 3.303, 3.305, 3.311, 3.312, 3.313, 3.308, 3.311, 3.309,
+ 3.297, 3.298, 3.304, 3.309, 3.312, 3.313, 3.311, 3.308, 3.304, 3.302, 3.301, 3.301, 3.306, 3.307, 3.308, 3.306, 3.306, 3.307, 3.306, 3.302, 3.297, 3.294, 3.294, 3.299, 3.305, 3.306, 3.309, 3.312, 3.311, 3.306, 3.308, 3.309,
+ 3.298, 3.299, 3.306, 3.311, 3.315, 3.314, 3.311, 3.308, 3.305, 3.303, 3.303, 3.304, 3.307, 3.309, 3.309, 3.308, 3.308, 3.307, 3.306, 3.302, 3.298, 3.296, 3.296, 3.298, 3.304, 3.306, 3.308, 3.309, 3.314, 3.308, 3.309, 3.308,
+ 3.299, 3.301, 3.307, 3.313, 3.316, 3.316, 3.313, 3.311, 3.307, 3.305, 3.305, 3.307, 3.309, 3.311, 3.312, 3.311, 3.309, 3.309, 3.308, 3.306, 3.301, 3.298, 3.297, 3.301, 3.305, 3.309, 3.309, 3.311, 3.313, 3.306, 3.308, 3.307,
+ 3.301, 3.301, 3.307, 3.314, 3.317, 3.318, 3.314, 3.311, 3.308, 3.306, 3.306, 3.308, 3.311, 3.311, 3.312, 3.311, 3.309, 3.309, 3.309, 3.306, 3.302, 3.298, 3.298, 3.301, 3.305, 3.309, 3.311, 3.311, 3.312, 3.309, 3.308, 3.308,
+ 3.301, 3.302, 3.307, 3.316, 3.319, 3.319, 3.315, 3.312, 3.309, 3.306, 3.307, 3.309, 3.311, 3.312, 3.311, 3.311, 3.309, 3.309, 3.309, 3.306, 3.303, 3.299, 3.298, 3.302, 3.305, 3.308, 3.309, 3.309, 3.309, 3.303, 3.306, 3.307,
+ 3.301, 3.303, 3.308, 3.315, 3.318, 3.318, 3.316, 3.313, 3.311, 3.307, 3.307, 3.308, 3.311, 3.311, 3.311, 3.308, 3.308, 3.307, 3.307, 3.306, 3.303, 3.299, 3.297, 3.301, 3.303, 3.306, 3.309, 3.308, 3.306, 3.303, 3.304, 3.306,
+ 3.302, 3.304, 3.306, 3.316, 3.318, 3.318, 3.317, 3.315, 3.311, 3.308, 3.309, 3.311, 3.311, 3.312, 3.311, 3.307, 3.306, 3.307, 3.308, 3.307, 3.304, 3.299, 3.298, 3.301, 3.303, 3.305, 3.306, 3.305, 3.304, 3.302, 3.303, 3.306,
+ 3.302, 3.304, 3.306, 3.312, 3.316, 3.317, 3.317, 3.313, 3.311, 3.309, 3.309, 3.311, 3.311, 3.312, 3.309, 3.307, 3.306, 3.306, 3.308, 3.307, 3.304, 3.298, 3.297, 3.302, 3.304, 3.305, 3.306, 3.305, 3.304, 3.299, 3.302, 3.303,
+ 3.299, 3.299, 3.306, 3.309, 3.315, 3.316, 3.316, 3.312, 3.309, 3.308, 3.308, 3.309, 3.311, 3.311, 3.307, 3.305, 3.305, 3.305, 3.306, 3.304, 3.299, 3.297, 3.296, 3.301, 3.302, 3.304, 3.303, 3.302, 3.301, 3.298, 3.299, 3.301,
+ 3.295, 3.297, 3.305, 3.309, 3.311, 3.311, 3.311, 3.309, 3.307, 3.306, 3.305, 3.305, 3.305, 3.305, 3.304, 3.301, 3.301, 3.301, 3.302, 3.299, 3.296, 3.295, 3.295, 3.298, 3.301, 3.302, 3.303, 3.301, 3.299, 3.295, 3.297, 3.299,
+ 3.294, 3.296, 3.299, 3.306, 3.308, 3.309, 3.309, 3.307, 3.307, 3.303, 3.302, 3.301, 3.302, 3.303, 3.302, 3.299, 3.298, 3.299, 3.299, 3.298, 3.295, 3.292, 3.292, 3.293, 3.297, 3.299, 3.299, 3.299, 3.297, 3.294, 3.295, 3.299,
+ 3.291, 3.292, 3.296, 3.302, 3.306, 3.306, 3.307, 3.306, 3.305, 3.303, 3.302, 3.301, 3.301, 3.303, 3.301, 3.299, 3.298, 3.298, 3.298, 3.297, 3.295, 3.292, 3.291, 3.291, 3.295, 3.295, 3.297, 3.298, 3.296, 3.293, 3.292, 3.291,
+ 3.293, 3.292, 3.294, 3.301, 3.303, 3.305, 3.308, 3.306, 3.306, 3.304, 3.304, 3.303, 3.303, 3.303, 3.302, 3.301, 3.299, 3.299, 3.299, 3.299, 3.294, 3.291, 3.289, 3.291, 3.293, 3.294, 3.294, 3.294, 3.294, 3.289, 3.291, 3.291,
+ 3.288, 3.289, 3.291, 3.299, 3.303, 3.304, 3.304, 3.304, 3.304, 3.303, 3.303, 3.304, 3.306, 3.305, 3.303, 3.301, 3.301, 3.298, 3.299, 3.298, 3.293, 3.291, 3.289, 3.291, 3.291, 3.292, 3.291, 3.291, 3.291, 3.285, 3.288, 3.291,
+ 3.285, 3.284, 3.287, 3.291, 3.299, 3.299, 3.299, 3.299, 3.299, 3.301, 3.302, 3.303, 3.303, 3.302, 3.299, 3.298, 3.298, 3.298, 3.298, 3.293, 3.288, 3.286, 3.285, 3.288, 3.288, 3.288, 3.287, 3.285, 3.284, 3.279, 3.281, 3.284,
+ 3.281, 3.282, 3.282, 3.286, 3.291, 3.294, 3.294, 3.295, 3.295, 3.299, 3.301, 3.304, 3.305, 3.299, 3.299, 3.297, 3.298, 3.298, 3.297, 3.292, 3.288, 3.285, 3.283, 3.284, 3.284, 3.286, 3.284, 3.282, 3.279, 3.275, 3.275, 3.278,
+ 3.282, 3.282, 3.284, 3.286, 3.291, 3.294, 3.295, 3.295, 3.298, 3.301, 3.304, 3.306, 3.306, 3.304, 3.303, 3.301, 3.302, 3.299, 3.299, 3.295, 3.289, 3.287, 3.284, 3.288, 3.287, 3.287, 3.285, 3.283, 3.282, 3.278, 3.281, 3.286,
+ 3.292, 3.291, 3.292, 3.298, 3.307, 3.309, 3.308, 3.312, 3.313, 3.317, 3.324, 3.327, 3.327, 3.325, 3.326, 3.322, 3.319, 3.317, 3.317, 3.315, 3.312, 3.305, 3.303, 3.301, 3.299, 3.297, 3.299, 3.293, 3.289, 3.285, 3.287, 3.293
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.602, 1.603, 1.605, 1.608, 1.611, 1.612, 1.612, 1.614, 1.615, 1.616, 1.619, 1.621, 1.621, 1.622, 1.622, 1.624, 1.624, 1.626, 1.625, 1.625, 1.623, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.614, 1.612, 1.609, 1.609, 1.611,
+ 1.601, 1.602, 1.605, 1.608, 1.611, 1.612, 1.612, 1.613, 1.614, 1.615, 1.617, 1.619, 1.621, 1.621, 1.621, 1.621, 1.622, 1.624, 1.624, 1.624, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.615, 1.614, 1.612, 1.609, 1.609, 1.609,
+ 1.602, 1.603, 1.605, 1.609, 1.611, 1.613, 1.613, 1.613, 1.613, 1.615, 1.616, 1.618, 1.618, 1.619, 1.619, 1.619, 1.621, 1.621, 1.622, 1.622, 1.619, 1.618, 1.617, 1.617, 1.616, 1.615, 1.615, 1.615, 1.612, 1.609, 1.609, 1.609,
+ 1.604, 1.605, 1.608, 1.612, 1.613, 1.614, 1.614, 1.614, 1.614, 1.614, 1.616, 1.617, 1.618, 1.618, 1.618, 1.619, 1.619, 1.621, 1.622, 1.621, 1.619, 1.618, 1.617, 1.616, 1.616, 1.616, 1.616, 1.615, 1.615, 1.612, 1.611, 1.611,
+ 1.606, 1.608, 1.611, 1.614, 1.615, 1.615, 1.616, 1.616, 1.615, 1.615, 1.617, 1.618, 1.619, 1.619, 1.618, 1.619, 1.621, 1.622, 1.623, 1.622, 1.619, 1.619, 1.617, 1.617, 1.617, 1.618, 1.618, 1.617, 1.617, 1.614, 1.613, 1.613,
+ 1.608, 1.611, 1.614, 1.617, 1.618, 1.618, 1.618, 1.618, 1.618, 1.618, 1.619, 1.621, 1.621, 1.621, 1.621, 1.621, 1.622, 1.623, 1.624, 1.623, 1.622, 1.619, 1.619, 1.618, 1.618, 1.618, 1.618, 1.618, 1.618, 1.617, 1.615, 1.614,
+ 1.611, 1.613, 1.616, 1.618, 1.621, 1.621, 1.619, 1.619, 1.619, 1.619, 1.619, 1.621, 1.622, 1.623, 1.623, 1.623, 1.623, 1.624, 1.626, 1.624, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.621, 1.621, 1.619, 1.617, 1.616, 1.616,
+ 1.611, 1.613, 1.617, 1.621, 1.622, 1.622, 1.621, 1.619, 1.619, 1.619, 1.621, 1.622, 1.624, 1.624, 1.624, 1.624, 1.625, 1.626, 1.626, 1.624, 1.623, 1.621, 1.621, 1.619, 1.619, 1.619, 1.621, 1.621, 1.621, 1.619, 1.618, 1.617,
+ 1.613, 1.615, 1.618, 1.621, 1.623, 1.623, 1.622, 1.621, 1.619, 1.619, 1.621, 1.622, 1.625, 1.625, 1.626, 1.626, 1.625, 1.626, 1.626, 1.624, 1.622, 1.621, 1.619, 1.619, 1.619, 1.621, 1.622, 1.622, 1.621, 1.621, 1.619, 1.618,
+ 1.614, 1.617, 1.621, 1.623, 1.624, 1.624, 1.623, 1.621, 1.621, 1.621, 1.622, 1.625, 1.627, 1.627, 1.628, 1.628, 1.628, 1.628, 1.627, 1.626, 1.623, 1.621, 1.621, 1.621, 1.621, 1.623, 1.623, 1.623, 1.623, 1.621, 1.619, 1.619,
+ 1.616, 1.617, 1.622, 1.624, 1.625, 1.625, 1.624, 1.623, 1.622, 1.623, 1.624, 1.627, 1.629, 1.631, 1.631, 1.631, 1.631, 1.631, 1.631, 1.628, 1.626, 1.623, 1.622, 1.622, 1.623, 1.623, 1.624, 1.624, 1.624, 1.622, 1.621, 1.621,
+ 1.617, 1.618, 1.623, 1.625, 1.626, 1.626, 1.625, 1.624, 1.623, 1.624, 1.625, 1.629, 1.631, 1.633, 1.634, 1.634, 1.634, 1.633, 1.633, 1.631, 1.628, 1.624, 1.623, 1.623, 1.623, 1.625, 1.625, 1.625, 1.625, 1.623, 1.622, 1.622,
+ 1.617, 1.619, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.624, 1.625, 1.628, 1.632, 1.634, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.627, 1.624, 1.623, 1.623, 1.623, 1.624, 1.625, 1.626, 1.625, 1.624, 1.623, 1.623,
+ 1.618, 1.619, 1.623, 1.626, 1.627, 1.626, 1.626, 1.625, 1.624, 1.624, 1.625, 1.628, 1.631, 1.634, 1.634, 1.634, 1.634, 1.634, 1.633, 1.631, 1.628, 1.623, 1.622, 1.622, 1.623, 1.624, 1.625, 1.626, 1.626, 1.624, 1.624, 1.623,
+ 1.618, 1.619, 1.623, 1.626, 1.627, 1.627, 1.625, 1.624, 1.624, 1.624, 1.625, 1.628, 1.632, 1.633, 1.634, 1.634, 1.634, 1.633, 1.633, 1.631, 1.627, 1.623, 1.622, 1.622, 1.623, 1.624, 1.624, 1.625, 1.625, 1.624, 1.623, 1.623,
+ 1.619, 1.621, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.624, 1.626, 1.628, 1.632, 1.634, 1.635, 1.634, 1.634, 1.633, 1.633, 1.631, 1.628, 1.625, 1.622, 1.622, 1.622, 1.623, 1.624, 1.625, 1.625, 1.624, 1.623, 1.623,
+ 1.619, 1.621, 1.623, 1.626, 1.627, 1.627, 1.626, 1.625, 1.624, 1.625, 1.627, 1.629, 1.633, 1.635, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.628, 1.625, 1.623, 1.622, 1.622, 1.623, 1.624, 1.624, 1.624, 1.623, 1.623, 1.622,
+ 1.619, 1.621, 1.624, 1.626, 1.628, 1.628, 1.627, 1.626, 1.625, 1.626, 1.627, 1.629, 1.633, 1.635, 1.635, 1.635, 1.635, 1.634, 1.633, 1.631, 1.628, 1.625, 1.623, 1.623, 1.623, 1.623, 1.624, 1.624, 1.624, 1.622, 1.622, 1.622,
+ 1.619, 1.621, 1.623, 1.626, 1.628, 1.628, 1.627, 1.626, 1.625, 1.626, 1.627, 1.629, 1.632, 1.634, 1.635, 1.635, 1.634, 1.634, 1.632, 1.631, 1.628, 1.624, 1.622, 1.622, 1.622, 1.623, 1.624, 1.624, 1.624, 1.622, 1.621, 1.621,
+ 1.619, 1.621, 1.623, 1.627, 1.628, 1.628, 1.627, 1.627, 1.626, 1.627, 1.628, 1.629, 1.631, 1.633, 1.634, 1.633, 1.633, 1.632, 1.631, 1.631, 1.627, 1.624, 1.622, 1.622, 1.622, 1.622, 1.623, 1.623, 1.623, 1.621, 1.621, 1.621,
+ 1.621, 1.621, 1.624, 1.627, 1.628, 1.628, 1.627, 1.627, 1.627, 1.627, 1.628, 1.631, 1.632, 1.633, 1.633, 1.632, 1.632, 1.632, 1.631, 1.631, 1.628, 1.625, 1.623, 1.622, 1.622, 1.622, 1.623, 1.623, 1.623, 1.621, 1.621, 1.621,
+ 1.621, 1.621, 1.623, 1.627, 1.628, 1.628, 1.628, 1.627, 1.627, 1.628, 1.628, 1.629, 1.631, 1.632, 1.633, 1.632, 1.631, 1.631, 1.631, 1.629, 1.628, 1.625, 1.624, 1.623, 1.623, 1.623, 1.623, 1.623, 1.623, 1.621, 1.621, 1.619,
+ 1.619, 1.621, 1.623, 1.626, 1.628, 1.629, 1.627, 1.627, 1.627, 1.627, 1.628, 1.629, 1.631, 1.631, 1.631, 1.631, 1.631, 1.629, 1.629, 1.628, 1.626, 1.624, 1.623, 1.623, 1.623, 1.622, 1.623, 1.623, 1.622, 1.621, 1.619, 1.619,
+ 1.618, 1.619, 1.623, 1.625, 1.627, 1.627, 1.627, 1.627, 1.626, 1.627, 1.627, 1.628, 1.628, 1.629, 1.628, 1.628, 1.628, 1.628, 1.628, 1.627, 1.625, 1.623, 1.621, 1.621, 1.621, 1.622, 1.622, 1.622, 1.621, 1.619, 1.618, 1.618,
+ 1.618, 1.619, 1.622, 1.624, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.626, 1.627, 1.627, 1.627, 1.626, 1.626, 1.626, 1.626, 1.626, 1.624, 1.622, 1.621, 1.621, 1.619, 1.621, 1.621, 1.621, 1.621, 1.618, 1.617, 1.617,
+ 1.616, 1.618, 1.621, 1.623, 1.624, 1.625, 1.625, 1.625, 1.625, 1.625, 1.626, 1.626, 1.627, 1.627, 1.625, 1.624, 1.624, 1.625, 1.626, 1.625, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.621, 1.621, 1.619, 1.616, 1.616, 1.616,
+ 1.615, 1.616, 1.619, 1.621, 1.623, 1.624, 1.625, 1.624, 1.624, 1.625, 1.626, 1.627, 1.627, 1.626, 1.626, 1.625, 1.624, 1.625, 1.625, 1.625, 1.623, 1.621, 1.619, 1.619, 1.619, 1.619, 1.619, 1.619, 1.618, 1.616, 1.615, 1.614,
+ 1.614, 1.615, 1.616, 1.621, 1.621, 1.623, 1.624, 1.623, 1.624, 1.624, 1.625, 1.627, 1.627, 1.627, 1.626, 1.625, 1.625, 1.625, 1.625, 1.624, 1.622, 1.621, 1.619, 1.618, 1.617, 1.617, 1.617, 1.617, 1.616, 1.613, 1.612, 1.612,
+ 1.612, 1.612, 1.615, 1.617, 1.621, 1.621, 1.622, 1.622, 1.622, 1.624, 1.625, 1.626, 1.626, 1.626, 1.625, 1.624, 1.624, 1.624, 1.624, 1.623, 1.621, 1.619, 1.618, 1.616, 1.615, 1.615, 1.615, 1.615, 1.613, 1.611, 1.609, 1.609,
+ 1.611, 1.611, 1.612, 1.615, 1.618, 1.619, 1.621, 1.621, 1.622, 1.623, 1.624, 1.626, 1.626, 1.626, 1.625, 1.624, 1.624, 1.624, 1.623, 1.622, 1.621, 1.618, 1.617, 1.615, 1.615, 1.614, 1.614, 1.613, 1.611, 1.609, 1.609, 1.609,
+ 1.611, 1.611, 1.612, 1.615, 1.618, 1.619, 1.621, 1.622, 1.623, 1.625, 1.625, 1.627, 1.627, 1.627, 1.626, 1.626, 1.626, 1.626, 1.624, 1.622, 1.621, 1.618, 1.617, 1.617, 1.616, 1.615, 1.614, 1.613, 1.612, 1.609, 1.609, 1.609,
+ 1.612, 1.612, 1.614, 1.617, 1.619, 1.621, 1.623, 1.624, 1.625, 1.626, 1.627, 1.629, 1.631, 1.629, 1.629, 1.629, 1.628, 1.629, 1.628, 1.626, 1.624, 1.621, 1.621, 1.619, 1.619, 1.618, 1.616, 1.616, 1.613, 1.611, 1.612, 1.612
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.977, 2.794, 2.572, 2.375, 2.218, 2.098, 1.995, 1.903, 1.815, 1.731, 1.647, 1.571, 1.516, 1.493, 1.483, 1.481, 1.481, 1.481, 1.489, 1.511, 1.571, 1.643, 1.729, 1.813, 1.901, 1.993, 2.091, 2.208, 2.364, 2.563, 2.785, 2.971,
+ 2.951, 2.736, 2.512, 2.312, 2.153, 2.031, 1.926, 1.824, 1.736, 1.649, 1.571, 1.506, 1.456, 1.419, 1.396, 1.386, 1.386, 1.392, 1.414, 1.451, 1.505, 1.571, 1.648, 1.733, 1.824, 1.922, 2.025, 2.144, 2.301, 2.499, 2.725, 2.939,
+ 2.883, 2.701, 2.471, 2.266, 2.102, 1.974, 1.861, 1.753, 1.649, 1.571, 1.502, 1.425, 1.361, 1.322, 1.298, 1.286, 1.286, 1.294, 1.317, 1.359, 1.424, 1.501, 1.571, 1.648, 1.751, 1.857, 1.968, 2.095, 2.254, 2.458, 2.688, 2.872,
+ 2.788, 2.632, 2.408, 2.209, 2.056, 1.931, 1.816, 1.704, 1.598, 1.503, 1.425, 1.361, 1.322, 1.298, 1.269, 1.245, 1.243, 1.264, 1.293, 1.317, 1.359, 1.424, 1.501, 1.596, 1.702, 1.812, 1.924, 2.046, 2.197, 2.392, 2.619, 2.777,
+ 2.712, 2.541, 2.327, 2.155, 2.023, 1.908, 1.796, 1.684, 1.578, 1.488, 1.412, 1.351, 1.304, 1.269, 1.245, 1.235, 1.235, 1.243, 1.264, 1.301, 1.349, 1.411, 1.485, 1.577, 1.683, 1.791, 1.902, 2.016, 2.143, 2.312, 2.528, 2.702,
+ 2.678, 2.469, 2.269, 2.117, 1.998, 1.885, 1.773, 1.661, 1.556, 1.469, 1.397, 1.336, 1.277, 1.245, 1.234, 1.226, 1.226, 1.232, 1.244, 1.273, 1.332, 1.392, 1.465, 1.555, 1.659, 1.768, 1.879, 1.991, 2.109, 2.256, 2.454, 2.665,
+ 2.659, 2.433, 2.232, 2.081, 1.957, 1.841, 1.722, 1.606, 1.499, 1.409, 1.337, 1.277, 1.232, 1.198, 1.175, 1.166, 1.166, 1.172, 1.193, 1.228, 1.272, 1.334, 1.408, 1.499, 1.608, 1.717, 1.834, 1.951, 2.073, 2.222, 2.419, 2.648,
+ 2.624, 2.411, 2.204, 2.041, 1.909, 1.784, 1.661, 1.539, 1.431, 1.337, 1.277, 1.219, 1.159, 1.118, 1.096, 1.085, 1.085, 1.092, 1.114, 1.156, 1.219, 1.272, 1.337, 1.429, 1.539, 1.658, 1.779, 1.904, 2.033, 2.193, 2.397, 2.613,
+ 2.564, 2.377, 2.169, 2.012, 1.879, 1.749, 1.623, 1.501, 1.392, 1.299, 1.227, 1.169, 1.125, 1.097, 1.079, 1.063, 1.063, 1.076, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.501, 1.622, 1.746, 1.875, 2.005, 2.161, 2.362, 2.554,
+ 2.515, 2.325, 2.138, 1.997, 1.869, 1.742, 1.617, 1.501, 1.392, 1.299, 1.227, 1.169, 1.125, 1.095, 1.079, 1.063, 1.063, 1.076, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.499, 1.615, 1.741, 1.867, 1.991, 2.132, 2.316, 2.505,
+ 2.498, 2.289, 2.121, 1.988, 1.867, 1.741, 1.616, 1.499, 1.391, 1.299, 1.227, 1.169, 1.125, 1.095, 1.082, 1.065, 1.064, 1.079, 1.093, 1.124, 1.168, 1.227, 1.302, 1.392, 1.498, 1.614, 1.738, 1.864, 1.985, 2.116, 2.281, 2.486,
+ 2.498, 2.272, 2.105, 1.971, 1.846, 1.718, 1.592, 1.475, 1.371, 1.279, 1.211, 1.156, 1.112, 1.083, 1.064, 1.055, 1.055, 1.062, 1.081, 1.109, 1.154, 1.212, 1.285, 1.372, 1.473, 1.589, 1.712, 1.843, 1.967, 2.101, 2.263, 2.486,
+ 2.497, 2.267, 2.088, 1.946, 1.813, 1.679, 1.549, 1.431, 1.324, 1.231, 1.159, 1.114, 1.079, 1.035, 1.008, 1.001, 1.001, 1.008, 1.032, 1.076, 1.111, 1.161, 1.235, 1.324, 1.429, 1.547, 1.677, 1.811, 1.941, 2.082, 2.257, 2.484,
+ 2.476, 2.262, 2.077, 1.933, 1.802, 1.671, 1.541, 1.421, 1.317, 1.227, 1.157, 1.101, 1.059, 1.027, 1.004, 1.001, 1.001, 1.004, 1.024, 1.054, 1.098, 1.157, 1.229, 1.317, 1.419, 1.537, 1.667, 1.799, 1.931, 2.071, 2.251, 2.463,
+ 2.455, 2.246, 2.076, 1.933, 1.802, 1.671, 1.541, 1.421, 1.317, 1.227, 1.157, 1.103, 1.064, 1.035, 1.011, 1.003, 1.003, 1.009, 1.032, 1.062, 1.099, 1.157, 1.229, 1.317, 1.419, 1.537, 1.667, 1.799, 1.931, 2.071, 2.236, 2.446,
+ 2.454, 2.239, 2.077, 1.946, 1.817, 1.686, 1.561, 1.444, 1.342, 1.255, 1.189, 1.136, 1.093, 1.059, 1.039, 1.038, 1.038, 1.039, 1.056, 1.091, 1.131, 1.187, 1.258, 1.341, 1.441, 1.556, 1.683, 1.813, 1.939, 2.071, 2.229, 2.445,
+ 2.454, 2.239, 2.079, 1.946, 1.817, 1.686, 1.561, 1.444, 1.342, 1.255, 1.189, 1.136, 1.093, 1.062, 1.039, 1.038, 1.038, 1.039, 1.059, 1.091, 1.131, 1.187, 1.258, 1.341, 1.441, 1.556, 1.683, 1.813, 1.939, 2.071, 2.229, 2.445,
+ 2.458, 2.251, 2.079, 1.941, 1.807, 1.672, 1.543, 1.424, 1.319, 1.231, 1.162, 1.107, 1.065, 1.045, 1.018, 1.003, 1.003, 1.017, 1.044, 1.062, 1.103, 1.159, 1.232, 1.317, 1.419, 1.539, 1.669, 1.802, 1.933, 2.072, 2.239, 2.445,
+ 2.479, 2.265, 2.085, 1.941, 1.807, 1.672, 1.543, 1.424, 1.319, 1.231, 1.162, 1.107, 1.064, 1.031, 1.017, 1.003, 1.003, 1.017, 1.031, 1.059, 1.103, 1.159, 1.232, 1.317, 1.419, 1.539, 1.669, 1.802, 1.933, 2.076, 2.252, 2.468,
+ 2.504, 2.277, 2.099, 1.958, 1.826, 1.695, 1.565, 1.445, 1.338, 1.249, 1.181, 1.129, 1.095, 1.051, 1.027, 1.018, 1.018, 1.028, 1.049, 1.092, 1.127, 1.179, 1.252, 1.339, 1.442, 1.561, 1.691, 1.822, 1.949, 2.089, 2.263, 2.492,
+ 2.509, 2.288, 2.118, 1.982, 1.858, 1.728, 1.604, 1.486, 1.381, 1.293, 1.227, 1.173, 1.127, 1.098, 1.076, 1.067, 1.067, 1.077, 1.097, 1.121, 1.168, 1.225, 1.296, 1.382, 1.483, 1.598, 1.723, 1.852, 1.975, 2.107, 2.274, 2.496,
+ 2.515, 2.312, 2.139, 2.002, 1.877, 1.751, 1.629, 1.512, 1.405, 1.318, 1.248, 1.193, 1.149, 1.118, 1.096, 1.085, 1.085, 1.095, 1.114, 1.145, 1.188, 1.246, 1.319, 1.405, 1.508, 1.623, 1.747, 1.873, 1.995, 2.127, 2.297, 2.501,
+ 2.541, 2.351, 2.161, 2.016, 1.888, 1.762, 1.638, 1.519, 1.411, 1.319, 1.251, 1.197, 1.154, 1.121, 1.099, 1.091, 1.091, 1.099, 1.119, 1.148, 1.192, 1.248, 1.321, 1.411, 1.515, 1.633, 1.758, 1.884, 2.009, 2.149, 2.334, 2.526,
+ 2.588, 2.394, 2.193, 2.036, 1.905, 1.779, 1.656, 1.537, 1.426, 1.329, 1.255, 1.198, 1.161, 1.139, 1.118, 1.096, 1.095, 1.114, 1.138, 1.158, 1.195, 1.256, 1.333, 1.425, 1.533, 1.651, 1.777, 1.902, 2.028, 2.181, 2.378, 2.571,
+ 2.639, 2.431, 2.226, 2.067, 1.937, 1.816, 1.695, 1.577, 1.467, 1.368, 1.298, 1.253, 1.198, 1.161, 1.139, 1.129, 1.129, 1.138, 1.158, 1.195, 1.245, 1.296, 1.374, 1.468, 1.574, 1.692, 1.812, 1.934, 2.059, 2.216, 2.418, 2.626,
+ 2.679, 2.465, 2.261, 2.104, 1.979, 1.862, 1.746, 1.631, 1.522, 1.426, 1.352, 1.297, 1.254, 1.221, 1.201, 1.189, 1.189, 1.198, 1.217, 1.246, 1.293, 1.354, 1.433, 1.526, 1.631, 1.744, 1.859, 1.975, 2.097, 2.252, 2.452, 2.667,
+ 2.711, 2.511, 2.302, 2.141, 2.018, 1.903, 1.791, 1.678, 1.571, 1.475, 1.401, 1.343, 1.297, 1.268, 1.247, 1.236, 1.236, 1.244, 1.263, 1.291, 1.341, 1.403, 1.484, 1.575, 1.679, 1.791, 1.902, 2.012, 2.136, 2.295, 2.501, 2.698,
+ 2.759, 2.582, 2.363, 2.184, 2.049, 1.935, 1.824, 1.714, 1.608, 1.511, 1.431, 1.371, 1.325, 1.295, 1.271, 1.259, 1.259, 1.266, 1.291, 1.318, 1.369, 1.436, 1.517, 1.611, 1.716, 1.825, 1.933, 2.047, 2.179, 2.351, 2.571, 2.748,
+ 2.833, 2.662, 2.433, 2.239, 2.089, 1.968, 1.859, 1.752, 1.646, 1.549, 1.468, 1.411, 1.369, 1.325, 1.296, 1.283, 1.283, 1.292, 1.318, 1.366, 1.411, 1.472, 1.555, 1.651, 1.755, 1.861, 1.969, 2.086, 2.231, 2.422, 2.648, 2.821,
+ 2.909, 2.729, 2.499, 2.298, 2.141, 2.016, 1.907, 1.805, 1.703, 1.611, 1.539, 1.468, 1.411, 1.375, 1.351, 1.339, 1.339, 1.348, 1.372, 1.411, 1.472, 1.543, 1.613, 1.708, 1.807, 1.909, 2.014, 2.135, 2.288, 2.487, 2.716, 2.897,
+ 2.981, 2.789, 2.563, 2.358, 2.197, 2.071, 1.968, 1.868, 1.774, 1.684, 1.607, 1.541, 1.489, 1.453, 1.428, 1.417, 1.417, 1.427, 1.451, 1.489, 1.543, 1.611, 1.686, 1.776, 1.871, 1.966, 2.069, 2.191, 2.349, 2.551, 2.775, 2.964,
+ 3.041, 2.856, 2.629, 2.422, 2.252, 2.127, 2.021, 1.927, 1.834, 1.748, 1.672, 1.604, 1.541, 1.495, 1.483, 1.483, 1.483, 1.483, 1.496, 1.543, 1.608, 1.673, 1.749, 1.835, 1.926, 2.019, 2.122, 2.249, 2.411, 2.614, 2.839, 3.026
+ ],
+ "sigma": 0.00163,
+ "sigma_Cb": 0.0011
+ }
+ },
+ {
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "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 ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx708_wide.json b/src/ipa/rpi/pisp/data/imx708_wide.json
new file mode 100644
index 00000000..9fff05d9
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx708_wide.json
@@ -0,0 +1,1293 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 41985,
+ "reference_gain": 1.12,
+ "reference_aperture": 1.0,
+ "reference_lux": 810,
+ "reference_Y": 13859
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.9
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 206,
+ "slope": 0.00324
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2868.0, 0.6419, 0.3613,
+ 3603.0, 0.5374, 0.4787,
+ 4620.0, 0.4482, 0.5813,
+ 5901.0, 0.3883, 0.6514,
+ 7610.0, 0.3279, 0.7232
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.01908,
+ "transverse_neg": 0.01376
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.65,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.717, 1.712, 1.703, 1.692, 1.674, 1.653, 1.638, 1.624, 1.613, 1.601, 1.589, 1.579, 1.575, 1.573, 1.571, 1.571, 1.571, 1.571, 1.572, 1.577, 1.583, 1.593, 1.605, 1.618, 1.636, 1.653, 1.677, 1.699, 1.715, 1.722, 1.731, 1.733,
+ 1.714, 1.706, 1.696, 1.678, 1.658, 1.639, 1.627, 1.614, 1.602, 1.591, 1.579, 1.572, 1.569, 1.566, 1.565, 1.564, 1.564, 1.565, 1.567, 1.571, 1.578, 1.585, 1.595, 1.607, 1.622, 1.641, 1.661, 1.685, 1.706, 1.717, 1.724, 1.732,
+ 1.708, 1.698, 1.688, 1.667, 1.647, 1.629, 1.619, 1.606, 1.593, 1.581, 1.572, 1.565, 1.561, 1.559, 1.559, 1.559, 1.559, 1.561, 1.562, 1.566, 1.571, 1.577, 1.587, 1.598, 1.612, 1.629, 1.649, 1.674, 1.697, 1.713, 1.721, 1.728,
+ 1.706, 1.695, 1.681, 1.655, 1.636, 1.622, 1.613, 1.597, 1.585, 1.572, 1.564, 1.559, 1.558, 1.556, 1.555, 1.555, 1.556, 1.556, 1.558, 1.561, 1.566, 1.571, 1.578, 1.591, 1.605, 1.619, 1.638, 1.662, 1.691, 1.708, 1.719, 1.726,
+ 1.706, 1.692, 1.675, 1.649, 1.629, 1.615, 1.607, 1.592, 1.575, 1.565, 1.559, 1.554, 1.552, 1.551, 1.551, 1.551, 1.551, 1.552, 1.554, 1.557, 1.561, 1.566, 1.573, 1.582, 1.596, 1.611, 1.627, 1.652, 1.681, 1.705, 1.717, 1.724,
+ 1.703, 1.686, 1.664, 1.639, 1.625, 1.612, 1.599, 1.585, 1.569, 1.559, 1.554, 1.549, 1.548, 1.548, 1.546, 1.546, 1.546, 1.547, 1.549, 1.553, 1.557, 1.563, 1.569, 1.576, 1.591, 1.603, 1.621, 1.644, 1.674, 1.698, 1.714, 1.724,
+ 1.702, 1.681, 1.659, 1.635, 1.621, 1.607, 1.594, 1.579, 1.565, 1.554, 1.549, 1.546, 1.544, 1.543, 1.543, 1.542, 1.543, 1.543, 1.544, 1.549, 1.553, 1.558, 1.564, 1.572, 1.584, 1.599, 1.614, 1.639, 1.667, 1.695, 1.712, 1.724,
+ 1.697, 1.678, 1.655, 1.631, 1.616, 1.602, 1.589, 1.575, 1.559, 1.551, 1.545, 1.543, 1.542, 1.542, 1.541, 1.539, 1.539, 1.539, 1.542, 1.544, 1.551, 1.555, 1.562, 1.571, 1.579, 1.594, 1.611, 1.631, 1.661, 1.691, 1.712, 1.724,
+ 1.695, 1.674, 1.651, 1.629, 1.615, 1.599, 1.584, 1.568, 1.554, 1.545, 1.542, 1.541, 1.539, 1.539, 1.538, 1.538, 1.538, 1.539, 1.539, 1.543, 1.548, 1.554, 1.559, 1.568, 1.576, 1.592, 1.608, 1.629, 1.655, 1.689, 1.709, 1.723,
+ 1.691, 1.671, 1.648, 1.627, 1.613, 1.597, 1.581, 1.564, 1.551, 1.543, 1.539, 1.538, 1.538, 1.537, 1.536, 1.535, 1.536, 1.538, 1.539, 1.542, 1.546, 1.551, 1.558, 1.564, 1.575, 1.588, 1.604, 1.627, 1.654, 1.686, 1.709, 1.724,
+ 1.689, 1.667, 1.643, 1.626, 1.612, 1.594, 1.579, 1.559, 1.549, 1.541, 1.536, 1.535, 1.535, 1.535, 1.534, 1.533, 1.534, 1.536, 1.538, 1.541, 1.545, 1.549, 1.555, 1.563, 1.573, 1.585, 1.602, 1.624, 1.651, 1.683, 1.709, 1.725,
+ 1.686, 1.665, 1.641, 1.623, 1.609, 1.594, 1.576, 1.559, 1.546, 1.538, 1.535, 1.534, 1.533, 1.532, 1.531, 1.531, 1.532, 1.534, 1.537, 1.539, 1.544, 1.549, 1.554, 1.562, 1.572, 1.585, 1.601, 1.622, 1.651, 1.682, 1.711, 1.726,
+ 1.686, 1.661, 1.639, 1.623, 1.609, 1.592, 1.574, 1.557, 1.545, 1.537, 1.534, 1.533, 1.532, 1.531, 1.529, 1.528, 1.529, 1.532, 1.537, 1.539, 1.542, 1.548, 1.553, 1.562, 1.571, 1.584, 1.601, 1.621, 1.649, 1.682, 1.711, 1.726,
+ 1.685, 1.661, 1.638, 1.624, 1.609, 1.592, 1.574, 1.557, 1.544, 1.536, 1.533, 1.532, 1.531, 1.529, 1.527, 1.522, 1.526, 1.531, 1.536, 1.539, 1.542, 1.547, 1.553, 1.562, 1.571, 1.583, 1.601, 1.621, 1.648, 1.682, 1.711, 1.726,
+ 1.684, 1.658, 1.638, 1.624, 1.611, 1.592, 1.573, 1.556, 1.543, 1.536, 1.532, 1.531, 1.529, 1.528, 1.522, 1.517, 1.519, 1.527, 1.535, 1.539, 1.541, 1.547, 1.553, 1.562, 1.571, 1.583, 1.601, 1.622, 1.647, 1.681, 1.711, 1.727,
+ 1.681, 1.658, 1.641, 1.624, 1.611, 1.593, 1.573, 1.555, 1.541, 1.535, 1.532, 1.529, 1.529, 1.527, 1.517, 1.506, 1.506, 1.522, 1.534, 1.538, 1.541, 1.546, 1.552, 1.562, 1.569, 1.583, 1.601, 1.622, 1.646, 1.679, 1.709, 1.728,
+ 1.679, 1.656, 1.639, 1.624, 1.611, 1.595, 1.575, 1.556, 1.541, 1.534, 1.531, 1.529, 1.529, 1.527, 1.517, 1.507, 1.507, 1.522, 1.533, 1.538, 1.539, 1.546, 1.552, 1.561, 1.569, 1.584, 1.601, 1.622, 1.647, 1.681, 1.709, 1.726,
+ 1.678, 1.656, 1.638, 1.625, 1.612, 1.597, 1.577, 1.557, 1.542, 1.534, 1.529, 1.529, 1.528, 1.527, 1.522, 1.516, 1.519, 1.525, 1.533, 1.537, 1.539, 1.545, 1.552, 1.561, 1.571, 1.584, 1.601, 1.623, 1.649, 1.681, 1.709, 1.726,
+ 1.679, 1.654, 1.639, 1.626, 1.613, 1.598, 1.578, 1.558, 1.543, 1.534, 1.529, 1.529, 1.529, 1.528, 1.527, 1.522, 1.525, 1.528, 1.533, 1.536, 1.539, 1.546, 1.553, 1.561, 1.571, 1.586, 1.602, 1.623, 1.651, 1.683, 1.712, 1.726,
+ 1.677, 1.655, 1.641, 1.628, 1.615, 1.599, 1.581, 1.562, 1.545, 1.535, 1.531, 1.529, 1.529, 1.528, 1.527, 1.527, 1.528, 1.531, 1.533, 1.536, 1.539, 1.545, 1.552, 1.561, 1.572, 1.588, 1.607, 1.626, 1.654, 1.686, 1.716, 1.729,
+ 1.676, 1.655, 1.642, 1.629, 1.617, 1.602, 1.586, 1.564, 1.546, 1.536, 1.531, 1.529, 1.529, 1.529, 1.529, 1.529, 1.529, 1.532, 1.534, 1.536, 1.539, 1.547, 1.553, 1.563, 1.576, 1.591, 1.609, 1.627, 1.655, 1.688, 1.716, 1.729,
+ 1.676, 1.658, 1.641, 1.631, 1.617, 1.605, 1.588, 1.569, 1.553, 1.539, 1.532, 1.531, 1.529, 1.529, 1.529, 1.529, 1.531, 1.532, 1.534, 1.537, 1.541, 1.547, 1.553, 1.564, 1.578, 1.594, 1.613, 1.632, 1.659, 1.691, 1.717, 1.728,
+ 1.676, 1.658, 1.642, 1.631, 1.619, 1.608, 1.592, 1.575, 1.556, 1.542, 1.533, 1.531, 1.529, 1.529, 1.529, 1.531, 1.531, 1.532, 1.534, 1.537, 1.542, 1.548, 1.556, 1.567, 1.582, 1.598, 1.616, 1.638, 1.661, 1.693, 1.717, 1.729,
+ 1.678, 1.661, 1.644, 1.632, 1.621, 1.611, 1.596, 1.579, 1.561, 1.546, 1.536, 1.532, 1.531, 1.531, 1.531, 1.531, 1.532, 1.533, 1.535, 1.538, 1.544, 1.549, 1.559, 1.569, 1.587, 1.604, 1.618, 1.639, 1.669, 1.697, 1.718, 1.731,
+ 1.679, 1.662, 1.648, 1.635, 1.625, 1.615, 1.602, 1.586, 1.569, 1.552, 1.541, 1.535, 1.532, 1.532, 1.531, 1.532, 1.533, 1.534, 1.537, 1.541, 1.546, 1.552, 1.562, 1.576, 1.592, 1.608, 1.622, 1.647, 1.673, 1.703, 1.721, 1.734,
+ 1.684, 1.664, 1.649, 1.637, 1.627, 1.618, 1.606, 1.593, 1.576, 1.561, 1.547, 1.539, 1.535, 1.533, 1.533, 1.533, 1.534, 1.536, 1.539, 1.543, 1.549, 1.555, 1.568, 1.583, 1.596, 1.612, 1.629, 1.651, 1.681, 1.706, 1.723, 1.734,
+ 1.689, 1.669, 1.649, 1.639, 1.629, 1.621, 1.609, 1.597, 1.585, 1.567, 1.554, 1.546, 1.539, 1.536, 1.535, 1.535, 1.537, 1.538, 1.542, 1.546, 1.553, 1.562, 1.572, 1.589, 1.603, 1.619, 1.635, 1.658, 1.686, 1.708, 1.726, 1.736,
+ 1.692, 1.673, 1.655, 1.644, 1.634, 1.624, 1.614, 1.604, 1.592, 1.577, 1.566, 1.554, 1.546, 1.542, 1.538, 1.538, 1.539, 1.542, 1.546, 1.552, 1.559, 1.568, 1.581, 1.596, 1.609, 1.625, 1.642, 1.664, 1.693, 1.714, 1.727, 1.736,
+ 1.695, 1.679, 1.662, 1.647, 1.638, 1.631, 1.623, 1.612, 1.601, 1.589, 1.577, 1.565, 1.555, 1.549, 1.546, 1.545, 1.546, 1.548, 1.552, 1.559, 1.568, 1.579, 1.593, 1.604, 1.618, 1.632, 1.648, 1.676, 1.701, 1.718, 1.728, 1.739,
+ 1.699, 1.684, 1.667, 1.654, 1.644, 1.635, 1.629, 1.621, 1.609, 1.599, 1.589, 1.578, 1.568, 1.559, 1.556, 1.554, 1.554, 1.557, 1.563, 1.569, 1.578, 1.589, 1.599, 1.612, 1.625, 1.641, 1.661, 1.685, 1.707, 1.722, 1.734, 1.742,
+ 1.703, 1.691, 1.672, 1.658, 1.648, 1.639, 1.634, 1.628, 1.618, 1.606, 1.598, 1.589, 1.579, 1.573, 1.568, 1.567, 1.567, 1.568, 1.571, 1.578, 1.587, 1.597, 1.607, 1.618, 1.632, 1.651, 1.672, 1.694, 1.715, 1.728, 1.737, 1.742,
+ 1.707, 1.691, 1.676, 1.662, 1.651, 1.643, 1.638, 1.631, 1.622, 1.614, 1.604, 1.596, 1.589, 1.579, 1.575, 1.573, 1.573, 1.574, 1.578, 1.586, 1.589, 1.598, 1.609, 1.625, 1.638, 1.657, 1.679, 1.701, 1.719, 1.728, 1.738, 1.742
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.939, 2.935, 2.916, 2.895, 2.856, 2.825, 2.797, 2.777, 2.761, 2.741, 2.726, 2.709, 2.707, 2.704, 2.702, 2.702, 2.703, 2.706, 2.708, 2.709, 2.719, 2.735, 2.753, 2.776, 2.801, 2.832, 2.874, 2.915, 2.939, 2.943, 2.953, 2.961,
+ 2.936, 2.923, 2.901, 2.863, 2.829, 2.801, 2.781, 2.763, 2.743, 2.732, 2.712, 2.701, 2.696, 2.692, 2.691, 2.691, 2.693, 2.694, 2.696, 2.701, 2.709, 2.725, 2.741, 2.758, 2.779, 2.811, 2.838, 2.879, 2.919, 2.939, 2.948, 2.959,
+ 2.929, 2.909, 2.887, 2.847, 2.808, 2.783, 2.765, 2.748, 2.732, 2.713, 2.699, 2.691, 2.687, 2.686, 2.685, 2.685, 2.687, 2.689, 2.691, 2.694, 2.701, 2.709, 2.725, 2.745, 2.763, 2.786, 2.818, 2.863, 2.907, 2.933, 2.941, 2.955,
+ 2.929, 2.903, 2.875, 2.825, 2.791, 2.769, 2.755, 2.737, 2.718, 2.701, 2.688, 2.683, 2.681, 2.679, 2.681, 2.679, 2.681, 2.682, 2.685, 2.689, 2.694, 2.701, 2.711, 2.737, 2.754, 2.772, 2.803, 2.844, 2.894, 2.931, 2.939, 2.953,
+ 2.926, 2.895, 2.862, 2.816, 2.782, 2.759, 2.744, 2.727, 2.709, 2.691, 2.679, 2.673, 2.671, 2.669, 2.669, 2.669, 2.671, 2.674, 2.678, 2.681, 2.685, 2.694, 2.707, 2.725, 2.739, 2.762, 2.786, 2.829, 2.879, 2.919, 2.942, 2.952,
+ 2.919, 2.886, 2.846, 2.797, 2.772, 2.751, 2.737, 2.719, 2.694, 2.679, 2.672, 2.666, 2.664, 2.661, 2.659, 2.658, 2.661, 2.664, 2.669, 2.673, 2.678, 2.685, 2.696, 2.715, 2.728, 2.749, 2.774, 2.808, 2.866, 2.909, 2.936, 2.951,
+ 2.904, 2.877, 2.835, 2.789, 2.763, 2.744, 2.728, 2.712, 2.686, 2.672, 2.664, 2.657, 2.654, 2.654, 2.652, 2.653, 2.654, 2.657, 2.661, 2.666, 2.672, 2.678, 2.688, 2.703, 2.721, 2.742, 2.762, 2.797, 2.851, 2.902, 2.928, 2.949,
+ 2.901, 2.869, 2.825, 2.781, 2.756, 2.738, 2.721, 2.698, 2.679, 2.665, 2.656, 2.652, 2.649, 2.648, 2.648, 2.648, 2.649, 2.651, 2.654, 2.659, 2.667, 2.675, 2.683, 2.699, 2.711, 2.736, 2.754, 2.789, 2.838, 2.896, 2.926, 2.948,
+ 2.899, 2.862, 2.815, 2.774, 2.752, 2.734, 2.717, 2.689, 2.669, 2.658, 2.651, 2.646, 2.645, 2.643, 2.643, 2.644, 2.645, 2.646, 2.649, 2.654, 2.661, 2.669, 2.681, 2.693, 2.707, 2.729, 2.751, 2.782, 2.834, 2.887, 2.924, 2.947,
+ 2.898, 2.853, 2.812, 2.771, 2.751, 2.731, 2.711, 2.686, 2.663, 2.653, 2.646, 2.642, 2.641, 2.642, 2.642, 2.641, 2.641, 2.641, 2.646, 2.651, 2.657, 2.667, 2.678, 2.693, 2.705, 2.728, 2.746, 2.781, 2.829, 2.885, 2.924, 2.951,
+ 2.896, 2.851, 2.807, 2.771, 2.752, 2.729, 2.709, 2.681, 2.661, 2.649, 2.643, 2.641, 2.639, 2.639, 2.638, 2.636, 2.637, 2.638, 2.644, 2.649, 2.657, 2.666, 2.676, 2.688, 2.705, 2.725, 2.745, 2.777, 2.827, 2.884, 2.927, 2.951,
+ 2.891, 2.846, 2.803, 2.771, 2.749, 2.728, 2.706, 2.677, 2.658, 2.647, 2.641, 2.637, 2.637, 2.636, 2.636, 2.633, 2.632, 2.635, 2.643, 2.649, 2.656, 2.665, 2.675, 2.688, 2.704, 2.719, 2.744, 2.776, 2.822, 2.881, 2.927, 2.958,
+ 2.887, 2.841, 2.797, 2.769, 2.749, 2.729, 2.704, 2.674, 2.655, 2.645, 2.638, 2.635, 2.633, 2.632, 2.631, 2.625, 2.627, 2.631, 2.639, 2.649, 2.654, 2.662, 2.673, 2.686, 2.701, 2.718, 2.742, 2.773, 2.822, 2.881, 2.926, 2.958,
+ 2.883, 2.837, 2.796, 2.769, 2.749, 2.729, 2.701, 2.673, 2.653, 2.641, 2.636, 2.632, 2.631, 2.629, 2.623, 2.612, 2.619, 2.627, 2.637, 2.648, 2.652, 2.659, 2.671, 2.688, 2.699, 2.719, 2.742, 2.774, 2.821, 2.882, 2.927, 2.961,
+ 2.881, 2.832, 2.795, 2.769, 2.751, 2.729, 2.701, 2.672, 2.652, 2.639, 2.633, 2.631, 2.628, 2.625, 2.611, 2.599, 2.607, 2.619, 2.635, 2.644, 2.652, 2.659, 2.669, 2.686, 2.698, 2.719, 2.743, 2.775, 2.822, 2.881, 2.926, 2.961,
+ 2.879, 2.829, 2.793, 2.771, 2.751, 2.731, 2.701, 2.672, 2.651, 2.639, 2.632, 2.628, 2.626, 2.621, 2.601, 2.581, 2.581, 2.611, 2.631, 2.642, 2.648, 2.657, 2.669, 2.685, 2.699, 2.721, 2.743, 2.776, 2.819, 2.879, 2.927, 2.961,
+ 2.876, 2.829, 2.796, 2.773, 2.752, 2.731, 2.705, 2.672, 2.651, 2.637, 2.631, 2.627, 2.625, 2.619, 2.601, 2.581, 2.581, 2.611, 2.629, 2.641, 2.647, 2.658, 2.669, 2.685, 2.697, 2.721, 2.746, 2.777, 2.822, 2.881, 2.929, 2.964,
+ 2.874, 2.827, 2.796, 2.775, 2.755, 2.733, 2.708, 2.674, 2.649, 2.635, 2.629, 2.626, 2.624, 2.621, 2.609, 2.601, 2.606, 2.615, 2.629, 2.638, 2.645, 2.657, 2.669, 2.682, 2.699, 2.722, 2.747, 2.778, 2.822, 2.881, 2.931, 2.964,
+ 2.871, 2.827, 2.797, 2.776, 2.761, 2.734, 2.711, 2.679, 2.651, 2.636, 2.628, 2.626, 2.624, 2.621, 2.618, 2.611, 2.614, 2.619, 2.628, 2.639, 2.644, 2.657, 2.668, 2.683, 2.698, 2.723, 2.749, 2.782, 2.824, 2.882, 2.933, 2.965,
+ 2.869, 2.825, 2.797, 2.777, 2.765, 2.741, 2.718, 2.683, 2.655, 2.638, 2.627, 2.625, 2.624, 2.623, 2.621, 2.618, 2.618, 2.624, 2.629, 2.639, 2.644, 2.657, 2.669, 2.684, 2.701, 2.725, 2.755, 2.782, 2.829, 2.887, 2.937, 2.965,
+ 2.871, 2.826, 2.799, 2.776, 2.765, 2.744, 2.723, 2.689, 2.659, 2.639, 2.629, 2.626, 2.626, 2.624, 2.624, 2.622, 2.624, 2.627, 2.632, 2.639, 2.646, 2.657, 2.671, 2.687, 2.706, 2.732, 2.757, 2.789, 2.836, 2.893, 2.941, 2.965,
+ 2.869, 2.831, 2.803, 2.778, 2.766, 2.748, 2.729, 2.697, 2.667, 2.645, 2.632, 2.628, 2.625, 2.625, 2.625, 2.625, 2.627, 2.629, 2.634, 2.638, 2.648, 2.661, 2.673, 2.688, 2.711, 2.741, 2.762, 2.797, 2.843, 2.901, 2.943, 2.964,
+ 2.872, 2.837, 2.802, 2.781, 2.768, 2.753, 2.734, 2.702, 2.674, 2.647, 2.634, 2.629, 2.626, 2.625, 2.625, 2.627, 2.629, 2.632, 2.635, 2.639, 2.649, 2.663, 2.676, 2.694, 2.719, 2.746, 2.771, 2.799, 2.851, 2.905, 2.947, 2.969,
+ 2.871, 2.837, 2.805, 2.786, 2.771, 2.755, 2.739, 2.714, 2.685, 2.655, 2.639, 2.631, 2.626, 2.625, 2.626, 2.628, 2.629, 2.632, 2.634, 2.642, 2.651, 2.663, 2.679, 2.701, 2.726, 2.756, 2.773, 2.809, 2.861, 2.913, 2.949, 2.968,
+ 2.876, 2.841, 2.808, 2.789, 2.775, 2.759, 2.744, 2.719, 2.693, 2.664, 2.648, 2.636, 2.629, 2.627, 2.627, 2.629, 2.631, 2.633, 2.637, 2.645, 2.653, 2.666, 2.682, 2.708, 2.734, 2.759, 2.779, 2.815, 2.868, 2.918, 2.951, 2.971,
+ 2.882, 2.845, 2.816, 2.791, 2.778, 2.766, 2.748, 2.733, 2.707, 2.681, 2.656, 2.643, 2.636, 2.632, 2.631, 2.632, 2.633, 2.637, 2.643, 2.648, 2.659, 2.672, 2.691, 2.719, 2.747, 2.765, 2.791, 2.829, 2.881, 2.931, 2.952, 2.969,
+ 2.889, 2.855, 2.819, 2.799, 2.782, 2.769, 2.755, 2.741, 2.717, 2.691, 2.672, 2.652, 2.643, 2.639, 2.636, 2.636, 2.638, 2.642, 2.646, 2.655, 2.665, 2.682, 2.703, 2.729, 2.752, 2.774, 2.798, 2.839, 2.891, 2.933, 2.959, 2.975,
+ 2.897, 2.862, 2.829, 2.804, 2.789, 2.776, 2.764, 2.749, 2.734, 2.709, 2.689, 2.669, 2.652, 2.644, 2.642, 2.642, 2.644, 2.647, 2.654, 2.664, 2.677, 2.694, 2.714, 2.742, 2.764, 2.782, 2.809, 2.852, 2.899, 2.936, 2.961, 2.976,
+ 2.902, 2.869, 2.841, 2.811, 2.797, 2.785, 2.776, 2.761, 2.748, 2.727, 2.708, 2.689, 2.671, 2.659, 2.655, 2.654, 2.653, 2.656, 2.666, 2.678, 2.693, 2.713, 2.737, 2.756, 2.775, 2.798, 2.825, 2.871, 2.913, 2.944, 2.966, 2.979,
+ 2.911, 2.885, 2.848, 2.821, 2.804, 2.793, 2.784, 2.774, 2.759, 2.747, 2.726, 2.709, 2.692, 2.679, 2.673, 2.672, 2.671, 2.672, 2.681, 2.694, 2.712, 2.729, 2.749, 2.768, 2.789, 2.811, 2.844, 2.886, 2.928, 2.956, 2.971, 2.984,
+ 2.925, 2.893, 2.861, 2.831, 2.813, 2.802, 2.795, 2.783, 2.773, 2.759, 2.744, 2.729, 2.715, 2.701, 2.698, 2.694, 2.693, 2.694, 2.702, 2.714, 2.729, 2.747, 2.761, 2.781, 2.802, 2.828, 2.864, 2.907, 2.942, 2.967, 2.978, 2.989,
+ 2.932, 2.898, 2.871, 2.843, 2.823, 2.811, 2.802, 2.794, 2.779, 2.772, 2.757, 2.742, 2.729, 2.716, 2.705, 2.704, 2.704, 2.707, 2.715, 2.727, 2.737, 2.754, 2.769, 2.788, 2.812, 2.845, 2.878, 2.923, 2.962, 2.973, 2.979, 2.994
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.018, 3.021, 3.026, 3.052, 3.092, 3.143, 3.181, 3.202, 3.209, 3.212, 3.211, 3.209, 3.197, 3.193, 3.185, 3.184, 3.185, 3.187, 3.191, 3.202, 3.211, 3.213, 3.212, 3.203, 3.189, 3.147, 3.099, 3.051, 3.032, 3.031, 3.048, 3.054,
+ 3.019, 3.023, 3.033, 3.066, 3.123, 3.163, 3.196, 3.206, 3.212, 3.212, 3.211, 3.203, 3.193, 3.179, 3.168, 3.159, 3.159, 3.163, 3.174, 3.188, 3.203, 3.208, 3.211, 3.209, 3.195, 3.168, 3.114, 3.064, 3.035, 3.033, 3.044, 3.051,
+ 3.021, 3.028, 3.046, 3.099, 3.156, 3.192, 3.209, 3.215, 3.216, 3.213, 3.203, 3.193, 3.176, 3.159, 3.153, 3.151, 3.149, 3.152, 3.159, 3.171, 3.188, 3.201, 3.209, 3.211, 3.207, 3.189, 3.142, 3.083, 3.042, 3.038, 3.043, 3.046,
+ 3.022, 3.037, 3.065, 3.124, 3.178, 3.206, 3.215, 3.221, 3.218, 3.217, 3.198, 3.179, 3.162, 3.149, 3.138, 3.133, 3.133, 3.136, 3.145, 3.156, 3.174, 3.192, 3.206, 3.215, 3.214, 3.202, 3.159, 3.105, 3.058, 3.042, 3.043, 3.049,
+ 3.024, 3.047, 3.084, 3.151, 3.195, 3.211, 3.219, 3.223, 3.218, 3.208, 3.182, 3.164, 3.149, 3.137, 3.127, 3.119, 3.119, 3.124, 3.134, 3.144, 3.157, 3.178, 3.194, 3.213, 3.215, 3.208, 3.166, 3.124, 3.074, 3.044, 3.044, 3.049,
+ 3.023, 3.058, 3.102, 3.161, 3.201, 3.217, 3.224, 3.223, 3.217, 3.195, 3.174, 3.156, 3.137, 3.125, 3.115, 3.109, 3.109, 3.115, 3.121, 3.131, 3.146, 3.159, 3.186, 3.208, 3.213, 3.211, 3.181, 3.138, 3.084, 3.047, 3.047, 3.049,
+ 3.031, 3.063, 3.126, 3.183, 3.212, 3.224, 3.225, 3.224, 3.216, 3.191, 3.167, 3.143, 3.129, 3.115, 3.105, 3.103, 3.103, 3.107, 3.114, 3.121, 3.131, 3.148, 3.169, 3.199, 3.211, 3.209, 3.186, 3.151, 3.089, 3.051, 3.049, 3.052,
+ 3.033, 3.083, 3.141, 3.201, 3.221, 3.226, 3.226, 3.224, 3.212, 3.187, 3.159, 3.138, 3.119, 3.107, 3.101, 3.098, 3.098, 3.102, 3.107, 3.115, 3.124, 3.138, 3.161, 3.185, 3.207, 3.209, 3.197, 3.162, 3.112, 3.059, 3.056, 3.057,
+ 3.038, 3.092, 3.159, 3.212, 3.225, 3.231, 3.228, 3.224, 3.209, 3.181, 3.152, 3.129, 3.112, 3.103, 3.095, 3.092, 3.093, 3.095, 3.101, 3.108, 3.118, 3.133, 3.152, 3.179, 3.203, 3.209, 3.205, 3.174, 3.124, 3.069, 3.059, 3.058,
+ 3.049, 3.105, 3.176, 3.223, 3.229, 3.231, 3.229, 3.223, 3.206, 3.171, 3.147, 3.125, 3.109, 3.097, 3.091, 3.089, 3.088, 3.091, 3.094, 3.102, 3.111, 3.124, 3.143, 3.169, 3.196, 3.208, 3.207, 3.181, 3.132, 3.079, 3.064, 3.063,
+ 3.055, 3.123, 3.189, 3.226, 3.232, 3.232, 3.229, 3.225, 3.204, 3.169, 3.143, 3.122, 3.108, 3.095, 3.092, 3.089, 3.088, 3.088, 3.092, 3.095, 3.105, 3.117, 3.135, 3.159, 3.191, 3.208, 3.208, 3.189, 3.141, 3.084, 3.064, 3.062,
+ 3.057, 3.127, 3.198, 3.228, 3.233, 3.233, 3.229, 3.225, 3.201, 3.166, 3.139, 3.119, 3.106, 3.096, 3.093, 3.092, 3.088, 3.088, 3.089, 3.093, 3.099, 3.114, 3.129, 3.156, 3.186, 3.208, 3.208, 3.195, 3.143, 3.089, 3.065, 3.064,
+ 3.066, 3.142, 3.209, 3.232, 3.234, 3.233, 3.231, 3.226, 3.198, 3.166, 3.138, 3.117, 3.103, 3.097, 3.095, 3.095, 3.094, 3.089, 3.089, 3.092, 3.097, 3.109, 3.126, 3.155, 3.183, 3.207, 3.207, 3.198, 3.147, 3.091, 3.069, 3.065,
+ 3.072, 3.153, 3.216, 3.231, 3.234, 3.234, 3.229, 3.226, 3.194, 3.165, 3.136, 3.114, 3.101, 3.098, 3.098, 3.104, 3.098, 3.091, 3.088, 3.089, 3.093, 3.103, 3.123, 3.151, 3.181, 3.204, 3.204, 3.197, 3.156, 3.095, 3.069, 3.068,
+ 3.079, 3.159, 3.222, 3.233, 3.236, 3.235, 3.231, 3.226, 3.194, 3.165, 3.133, 3.112, 3.102, 3.099, 3.107, 3.114, 3.111, 3.097, 3.089, 3.089, 3.091, 3.099, 3.121, 3.149, 3.182, 3.202, 3.202, 3.195, 3.156, 3.096, 3.069, 3.068,
+ 3.081, 3.164, 3.226, 3.233, 3.236, 3.235, 3.233, 3.229, 3.199, 3.165, 3.137, 3.113, 3.102, 3.102, 3.111, 3.134, 3.134, 3.103, 3.091, 3.089, 3.092, 3.101, 3.119, 3.147, 3.182, 3.202, 3.202, 3.194, 3.155, 3.095, 3.069, 3.067,
+ 3.085, 3.163, 3.225, 3.236, 3.239, 3.235, 3.234, 3.231, 3.203, 3.169, 3.141, 3.115, 3.103, 3.103, 3.111, 3.134, 3.134, 3.106, 3.092, 3.091, 3.093, 3.103, 3.119, 3.149, 3.185, 3.203, 3.203, 3.193, 3.152, 3.095, 3.068, 3.066,
+ 3.083, 3.168, 3.226, 3.236, 3.241, 3.235, 3.235, 3.231, 3.205, 3.174, 3.144, 3.117, 3.107, 3.103, 3.107, 3.116, 3.109, 3.103, 3.091, 3.091, 3.095, 3.107, 3.123, 3.152, 3.188, 3.204, 3.204, 3.193, 3.151, 3.095, 3.069, 3.066,
+ 3.082, 3.171, 3.228, 3.237, 3.239, 3.235, 3.234, 3.233, 3.217, 3.184, 3.147, 3.119, 3.108, 3.104, 3.103, 3.105, 3.102, 3.095, 3.091, 3.091, 3.097, 3.111, 3.128, 3.157, 3.191, 3.204, 3.204, 3.185, 3.149, 3.094, 3.069, 3.065,
+ 3.086, 3.173, 3.226, 3.237, 3.239, 3.235, 3.234, 3.232, 3.221, 3.185, 3.155, 3.124, 3.112, 3.105, 3.102, 3.099, 3.096, 3.094, 3.092, 3.094, 3.102, 3.114, 3.133, 3.163, 3.197, 3.205, 3.204, 3.183, 3.144, 3.089, 3.068, 3.065,
+ 3.086, 3.166, 3.225, 3.239, 3.239, 3.237, 3.233, 3.231, 3.223, 3.193, 3.165, 3.135, 3.118, 3.108, 3.101, 3.098, 3.095, 3.093, 3.093, 3.099, 3.109, 3.124, 3.145, 3.174, 3.199, 3.204, 3.203, 3.181, 3.132, 3.085, 3.067, 3.062,
+ 3.086, 3.162, 3.224, 3.239, 3.241, 3.236, 3.232, 3.229, 3.224, 3.201, 3.174, 3.147, 3.128, 3.114, 3.103, 3.099, 3.096, 3.095, 3.097, 3.106, 3.116, 3.134, 3.151, 3.182, 3.201, 3.203, 3.201, 3.176, 3.125, 3.078, 3.065, 3.061,
+ 3.077, 3.162, 3.221, 3.239, 3.241, 3.234, 3.229, 3.227, 3.225, 3.207, 3.186, 3.161, 3.137, 3.122, 3.112, 3.102, 3.099, 3.098, 3.106, 3.113, 3.127, 3.139, 3.159, 3.192, 3.204, 3.205, 3.198, 3.167, 3.119, 3.073, 3.062, 3.061,
+ 3.077, 3.161, 3.216, 3.234, 3.236, 3.232, 3.225, 3.225, 3.222, 3.209, 3.194, 3.172, 3.148, 3.132, 3.121, 3.113, 3.107, 3.107, 3.112, 3.124, 3.135, 3.151, 3.175, 3.196, 3.201, 3.201, 3.191, 3.161, 3.114, 3.062, 3.058, 3.057,
+ 3.073, 3.139, 3.201, 3.227, 3.232, 3.227, 3.223, 3.219, 3.216, 3.212, 3.203, 3.181, 3.161, 3.142, 3.129, 3.121, 3.114, 3.114, 3.124, 3.134, 3.145, 3.161, 3.179, 3.196, 3.199, 3.195, 3.182, 3.145, 3.093, 3.052, 3.051, 3.052,
+ 3.066, 3.126, 3.192, 3.218, 3.224, 3.221, 3.218, 3.214, 3.214, 3.209, 3.204, 3.191, 3.174, 3.155, 3.142, 3.129, 3.127, 3.127, 3.136, 3.145, 3.157, 3.175, 3.187, 3.194, 3.196, 3.192, 3.171, 3.134, 3.082, 3.043, 3.042, 3.044,
+ 3.056, 3.114, 3.176, 3.212, 3.219, 3.219, 3.214, 3.209, 3.208, 3.206, 3.203, 3.198, 3.182, 3.171, 3.155, 3.146, 3.144, 3.144, 3.148, 3.156, 3.171, 3.181, 3.188, 3.194, 3.194, 3.187, 3.161, 3.117, 3.066, 3.037, 3.037, 3.044,
+ 3.054, 3.101, 3.162, 3.203, 3.216, 3.215, 3.211, 3.206, 3.203, 3.201, 3.199, 3.197, 3.191, 3.179, 3.171, 3.161, 3.156, 3.156, 3.161, 3.171, 3.179, 3.184, 3.189, 3.192, 3.191, 3.181, 3.142, 3.097, 3.045, 3.032, 3.033, 3.039,
+ 3.041, 3.093, 3.149, 3.194, 3.208, 3.211, 3.208, 3.202, 3.197, 3.197, 3.197, 3.195, 3.191, 3.189, 3.181, 3.176, 3.172, 3.173, 3.178, 3.181, 3.185, 3.187, 3.189, 3.191, 3.189, 3.173, 3.133, 3.085, 3.034, 3.029, 3.031, 3.038,
+ 3.032, 3.079, 3.133, 3.181, 3.197, 3.207, 3.204, 3.198, 3.193, 3.192, 3.189, 3.191, 3.189, 3.187, 3.185, 3.183, 3.183, 3.183, 3.185, 3.188, 3.187, 3.188, 3.189, 3.188, 3.184, 3.164, 3.118, 3.075, 3.031, 3.026, 3.028, 3.039,
+ 3.025, 3.051, 3.099, 3.149, 3.182, 3.193, 3.193, 3.187, 3.181, 3.178, 3.177, 3.177, 3.182, 3.183, 3.183, 3.183, 3.183, 3.184, 3.187, 3.188, 3.186, 3.184, 3.184, 3.181, 3.167, 3.139, 3.098, 3.053, 3.026, 3.024, 3.029, 3.043,
+ 3.016, 3.025, 3.081, 3.122, 3.167, 3.182, 3.185, 3.181, 3.176, 3.171, 3.169, 3.171, 3.174, 3.175, 3.178, 3.178, 3.179, 3.181, 3.185, 3.185, 3.181, 3.179, 3.177, 3.173, 3.151, 3.119, 3.076, 3.031, 3.021, 3.018, 3.024, 3.046
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.503, 1.503, 1.504, 1.515, 1.541, 1.566, 1.587, 1.599, 1.602, 1.603, 1.602, 1.599, 1.595, 1.589, 1.587, 1.586, 1.586, 1.587, 1.589, 1.594, 1.601, 1.604, 1.604, 1.601, 1.589, 1.571, 1.541, 1.517, 1.512, 1.512, 1.522, 1.526,
+ 1.501, 1.502, 1.506, 1.523, 1.557, 1.579, 1.596, 1.603, 1.603, 1.603, 1.601, 1.597, 1.591, 1.582, 1.576, 1.575, 1.574, 1.577, 1.581, 1.588, 1.595, 1.601, 1.603, 1.602, 1.597, 1.578, 1.553, 1.526, 1.512, 1.512, 1.519, 1.526,
+ 1.499, 1.503, 1.512, 1.539, 1.571, 1.593, 1.603, 1.604, 1.604, 1.602, 1.597, 1.591, 1.581, 1.573, 1.568, 1.566, 1.566, 1.568, 1.572, 1.579, 1.587, 1.594, 1.602, 1.603, 1.601, 1.589, 1.566, 1.536, 1.517, 1.516, 1.519, 1.525,
+ 1.499, 1.505, 1.521, 1.553, 1.582, 1.597, 1.604, 1.604, 1.604, 1.601, 1.592, 1.582, 1.573, 1.564, 1.561, 1.558, 1.557, 1.559, 1.564, 1.571, 1.579, 1.588, 1.597, 1.603, 1.603, 1.596, 1.576, 1.545, 1.519, 1.517, 1.518, 1.526,
+ 1.499, 1.509, 1.529, 1.565, 1.591, 1.601, 1.605, 1.604, 1.602, 1.597, 1.586, 1.573, 1.565, 1.558, 1.553, 1.551, 1.551, 1.552, 1.555, 1.563, 1.571, 1.581, 1.592, 1.601, 1.602, 1.599, 1.582, 1.556, 1.528, 1.517, 1.517, 1.526,
+ 1.501, 1.512, 1.539, 1.576, 1.595, 1.603, 1.605, 1.604, 1.601, 1.591, 1.579, 1.567, 1.559, 1.552, 1.548, 1.545, 1.546, 1.548, 1.551, 1.555, 1.563, 1.574, 1.585, 1.598, 1.602, 1.601, 1.589, 1.562, 1.535, 1.519, 1.519, 1.528,
+ 1.501, 1.517, 1.552, 1.587, 1.601, 1.605, 1.605, 1.605, 1.599, 1.588, 1.574, 1.562, 1.553, 1.548, 1.544, 1.543, 1.543, 1.545, 1.547, 1.551, 1.557, 1.567, 1.578, 1.593, 1.601, 1.601, 1.592, 1.571, 1.539, 1.521, 1.521, 1.529,
+ 1.503, 1.524, 1.561, 1.593, 1.605, 1.606, 1.605, 1.603, 1.598, 1.585, 1.569, 1.558, 1.551, 1.545, 1.542, 1.541, 1.541, 1.542, 1.545, 1.547, 1.555, 1.561, 1.573, 1.587, 1.598, 1.601, 1.596, 1.577, 1.546, 1.523, 1.523, 1.529,
+ 1.503, 1.532, 1.568, 1.597, 1.605, 1.606, 1.605, 1.603, 1.596, 1.581, 1.565, 1.555, 1.548, 1.544, 1.541, 1.539, 1.541, 1.541, 1.543, 1.546, 1.549, 1.558, 1.568, 1.583, 1.595, 1.601, 1.599, 1.582, 1.555, 1.525, 1.525, 1.531,
+ 1.508, 1.539, 1.575, 1.601, 1.605, 1.606, 1.605, 1.602, 1.593, 1.577, 1.563, 1.552, 1.546, 1.543, 1.541, 1.539, 1.539, 1.541, 1.542, 1.544, 1.548, 1.553, 1.564, 1.579, 1.592, 1.599, 1.599, 1.585, 1.559, 1.532, 1.531, 1.531,
+ 1.511, 1.544, 1.581, 1.603, 1.606, 1.606, 1.604, 1.603, 1.591, 1.574, 1.561, 1.549, 1.545, 1.542, 1.541, 1.541, 1.541, 1.541, 1.542, 1.543, 1.545, 1.551, 1.561, 1.573, 1.591, 1.599, 1.599, 1.588, 1.563, 1.535, 1.531, 1.531,
+ 1.515, 1.548, 1.589, 1.605, 1.607, 1.607, 1.604, 1.602, 1.591, 1.573, 1.559, 1.549, 1.543, 1.542, 1.541, 1.542, 1.542, 1.542, 1.541, 1.542, 1.543, 1.549, 1.558, 1.571, 1.588, 1.599, 1.599, 1.591, 1.566, 1.537, 1.532, 1.531,
+ 1.517, 1.558, 1.593, 1.606, 1.607, 1.607, 1.605, 1.602, 1.589, 1.572, 1.557, 1.548, 1.543, 1.543, 1.542, 1.544, 1.543, 1.543, 1.541, 1.541, 1.542, 1.546, 1.554, 1.569, 1.585, 1.599, 1.599, 1.593, 1.568, 1.538, 1.533, 1.531,
+ 1.521, 1.563, 1.596, 1.607, 1.608, 1.607, 1.606, 1.603, 1.589, 1.572, 1.557, 1.548, 1.543, 1.543, 1.544, 1.549, 1.546, 1.544, 1.541, 1.541, 1.542, 1.545, 1.553, 1.568, 1.585, 1.598, 1.598, 1.594, 1.571, 1.541, 1.534, 1.531,
+ 1.521, 1.566, 1.599, 1.607, 1.608, 1.607, 1.605, 1.603, 1.591, 1.571, 1.556, 1.547, 1.544, 1.544, 1.551, 1.554, 1.552, 1.546, 1.541, 1.541, 1.541, 1.544, 1.553, 1.567, 1.585, 1.597, 1.598, 1.595, 1.571, 1.541, 1.534, 1.531,
+ 1.523, 1.568, 1.601, 1.607, 1.608, 1.607, 1.606, 1.604, 1.591, 1.572, 1.557, 1.547, 1.545, 1.545, 1.552, 1.566, 1.566, 1.551, 1.542, 1.541, 1.541, 1.544, 1.553, 1.567, 1.586, 1.596, 1.596, 1.593, 1.571, 1.541, 1.533, 1.531,
+ 1.524, 1.569, 1.602, 1.607, 1.608, 1.607, 1.606, 1.604, 1.591, 1.573, 1.559, 1.548, 1.545, 1.546, 1.552, 1.565, 1.565, 1.551, 1.542, 1.541, 1.541, 1.545, 1.553, 1.568, 1.586, 1.597, 1.597, 1.593, 1.571, 1.541, 1.532, 1.532,
+ 1.526, 1.571, 1.602, 1.607, 1.608, 1.606, 1.605, 1.604, 1.593, 1.575, 1.559, 1.549, 1.546, 1.546, 1.549, 1.552, 1.552, 1.546, 1.542, 1.541, 1.542, 1.546, 1.555, 1.569, 1.587, 1.597, 1.597, 1.591, 1.569, 1.539, 1.532, 1.531,
+ 1.526, 1.571, 1.601, 1.608, 1.609, 1.605, 1.605, 1.603, 1.597, 1.579, 1.562, 1.551, 1.546, 1.545, 1.545, 1.549, 1.546, 1.543, 1.542, 1.541, 1.542, 1.547, 1.557, 1.573, 1.588, 1.597, 1.597, 1.589, 1.566, 1.537, 1.531, 1.529,
+ 1.526, 1.569, 1.602, 1.609, 1.609, 1.606, 1.605, 1.604, 1.598, 1.582, 1.567, 1.553, 1.547, 1.545, 1.544, 1.544, 1.544, 1.542, 1.542, 1.542, 1.544, 1.552, 1.559, 1.576, 1.591, 1.597, 1.597, 1.588, 1.563, 1.535, 1.531, 1.529,
+ 1.523, 1.567, 1.601, 1.609, 1.609, 1.606, 1.605, 1.603, 1.599, 1.587, 1.571, 1.558, 1.549, 1.545, 1.544, 1.543, 1.543, 1.542, 1.542, 1.544, 1.548, 1.555, 1.566, 1.581, 1.593, 1.597, 1.597, 1.586, 1.558, 1.534, 1.529, 1.529,
+ 1.523, 1.564, 1.599, 1.609, 1.609, 1.605, 1.604, 1.603, 1.601, 1.592, 1.576, 1.564, 1.553, 1.547, 1.544, 1.543, 1.542, 1.542, 1.544, 1.548, 1.551, 1.561, 1.572, 1.585, 1.594, 1.596, 1.595, 1.581, 1.555, 1.528, 1.527, 1.528,
+ 1.522, 1.561, 1.595, 1.608, 1.608, 1.604, 1.602, 1.601, 1.601, 1.595, 1.582, 1.569, 1.559, 1.552, 1.547, 1.545, 1.543, 1.544, 1.546, 1.551, 1.556, 1.563, 1.576, 1.589, 1.595, 1.596, 1.593, 1.576, 1.551, 1.524, 1.524, 1.528,
+ 1.519, 1.559, 1.591, 1.605, 1.606, 1.603, 1.601, 1.599, 1.601, 1.597, 1.587, 1.576, 1.565, 1.558, 1.552, 1.549, 1.546, 1.547, 1.552, 1.556, 1.561, 1.571, 1.582, 1.593, 1.596, 1.596, 1.591, 1.569, 1.546, 1.521, 1.521, 1.527,
+ 1.516, 1.553, 1.589, 1.602, 1.604, 1.602, 1.599, 1.598, 1.599, 1.598, 1.594, 1.583, 1.572, 1.564, 1.559, 1.553, 1.552, 1.553, 1.556, 1.561, 1.567, 1.578, 1.588, 1.594, 1.596, 1.594, 1.588, 1.567, 1.539, 1.517, 1.517, 1.525,
+ 1.511, 1.548, 1.581, 1.599, 1.602, 1.602, 1.598, 1.597, 1.597, 1.597, 1.595, 1.589, 1.581, 1.571, 1.564, 1.559, 1.559, 1.558, 1.561, 1.567, 1.575, 1.583, 1.591, 1.593, 1.594, 1.591, 1.581, 1.557, 1.529, 1.514, 1.514, 1.521,
+ 1.508, 1.541, 1.576, 1.596, 1.601, 1.601, 1.597, 1.595, 1.594, 1.595, 1.595, 1.592, 1.585, 1.579, 1.571, 1.566, 1.566, 1.566, 1.568, 1.575, 1.582, 1.589, 1.592, 1.593, 1.593, 1.589, 1.575, 1.553, 1.523, 1.511, 1.511, 1.517,
+ 1.505, 1.535, 1.566, 1.591, 1.599, 1.598, 1.596, 1.594, 1.592, 1.592, 1.593, 1.592, 1.589, 1.585, 1.579, 1.575, 1.574, 1.574, 1.577, 1.582, 1.587, 1.591, 1.592, 1.593, 1.592, 1.585, 1.568, 1.541, 1.516, 1.509, 1.509, 1.517,
+ 1.501, 1.528, 1.559, 1.585, 1.595, 1.597, 1.595, 1.593, 1.589, 1.588, 1.591, 1.591, 1.591, 1.589, 1.586, 1.583, 1.582, 1.582, 1.585, 1.588, 1.589, 1.591, 1.592, 1.593, 1.592, 1.582, 1.561, 1.536, 1.512, 1.509, 1.511, 1.517,
+ 1.496, 1.521, 1.549, 1.576, 1.588, 1.594, 1.593, 1.589, 1.586, 1.585, 1.586, 1.588, 1.589, 1.588, 1.588, 1.587, 1.587, 1.587, 1.589, 1.589, 1.591, 1.591, 1.592, 1.592, 1.591, 1.575, 1.555, 1.527, 1.508, 1.507, 1.511, 1.519,
+ 1.495, 1.505, 1.536, 1.563, 1.581, 1.587, 1.588, 1.584, 1.582, 1.578, 1.578, 1.581, 1.583, 1.584, 1.586, 1.587, 1.587, 1.587, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.584, 1.566, 1.544, 1.518, 1.505, 1.505, 1.509, 1.519,
+ 1.493, 1.496, 1.522, 1.547, 1.569, 1.581, 1.582, 1.581, 1.577, 1.575, 1.573, 1.575, 1.579, 1.581, 1.583, 1.584, 1.584, 1.585, 1.587, 1.587, 1.588, 1.588, 1.588, 1.585, 1.573, 1.556, 1.532, 1.511, 1.504, 1.504, 1.508, 1.523
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 4.461, 4.088, 3.793, 3.651, 3.557, 3.439, 3.248, 2.999, 2.751, 2.527, 2.341, 2.191, 2.069, 1.956, 1.907, 1.907, 1.907, 1.908, 1.946, 2.056, 2.179, 2.328, 2.517, 2.747, 2.998, 3.219, 3.359, 3.436, 3.494, 3.621, 3.906, 4.251,
+ 4.297, 3.982, 3.747, 3.634, 3.531, 3.373, 3.136, 2.863, 2.608, 2.386, 2.209, 2.075, 1.957, 1.873, 1.817, 1.789, 1.789, 1.813, 1.865, 1.947, 2.066, 2.198, 2.378, 2.605, 2.872, 3.132, 3.322, 3.431, 3.485, 3.577, 3.802, 4.079,
+ 4.152, 3.905, 3.717, 3.623, 3.499, 3.296, 3.022, 2.735, 2.478, 2.265, 2.094, 1.957, 1.849, 1.763, 1.709, 1.679, 1.679, 1.703, 1.753, 1.837, 1.947, 2.081, 2.253, 2.472, 2.742, 3.032, 3.271, 3.414, 3.479, 3.545, 3.719, 3.937,
+ 4.039, 3.835, 3.688, 3.596, 3.442, 3.196, 2.899, 2.609, 2.356, 2.153, 1.987, 1.849, 1.748, 1.659, 1.605, 1.577, 1.577, 1.599, 1.649, 1.734, 1.837, 1.973, 2.139, 2.348, 2.612, 2.911, 3.192, 3.379, 3.467, 3.516, 3.649, 3.815,
+ 3.952, 3.784, 3.669, 3.562, 3.369, 3.088, 2.778, 2.491, 2.246, 2.049, 1.888, 1.748, 1.657, 1.561, 1.509, 1.481, 1.481, 1.504, 1.552, 1.642, 1.734, 1.869, 2.033, 2.233, 2.489, 2.792, 3.105, 3.331, 3.445, 3.493, 3.591, 3.721,
+ 3.883, 3.741, 3.648, 3.519, 3.287, 2.977, 2.665, 2.382, 2.148, 1.957, 1.796, 1.659, 1.561, 1.474, 1.422, 1.396, 1.396, 1.415, 1.465, 1.552, 1.643, 1.776, 1.936, 2.131, 2.375, 2.678, 3.004, 3.275, 3.416, 3.469, 3.541, 3.643,
+ 3.829, 3.716, 3.617, 3.466, 3.202, 2.876, 2.558, 2.282, 2.059, 1.872, 1.713, 1.577, 1.474, 1.399, 1.345, 1.319, 1.319, 1.338, 1.389, 1.465, 1.559, 1.689, 1.849, 2.042, 2.275, 2.568, 2.903, 3.204, 3.383, 3.446, 3.496, 3.579,
+ 3.793, 3.685, 3.589, 3.411, 3.119, 2.781, 2.466, 2.199, 1.983, 1.798, 1.639, 1.505, 1.399, 1.339, 1.276, 1.253, 1.253, 1.271, 1.327, 1.389, 1.487, 1.612, 1.769, 1.961, 2.189, 2.471, 2.806, 3.133, 3.342, 3.425, 3.459, 3.527,
+ 3.763, 3.666, 3.561, 3.357, 3.042, 2.698, 2.384, 2.129, 1.918, 1.734, 1.575, 1.443, 1.339, 1.276, 1.217, 1.194, 1.194, 1.214, 1.271, 1.327, 1.423, 1.546, 1.702, 1.891, 2.112, 2.386, 2.718, 3.061, 3.301, 3.402, 3.433, 3.486,
+ 3.745, 3.647, 3.529, 3.302, 2.971, 2.627, 2.318, 2.067, 1.859, 1.677, 1.521, 1.389, 1.287, 1.217, 1.171, 1.145, 1.145, 1.165, 1.214, 1.276, 1.369, 1.491, 1.643, 1.831, 2.048, 2.313, 2.644, 2.995, 3.262, 3.381, 3.412, 3.453,
+ 3.731, 3.635, 3.503, 3.249, 2.911, 2.566, 2.259, 2.017, 1.811, 1.629, 1.475, 1.347, 1.246, 1.171, 1.138, 1.103, 1.103, 1.129, 1.165, 1.231, 1.322, 1.443, 1.595, 1.779, 1.993, 2.251, 2.576, 2.936, 3.223, 3.359, 3.392, 3.425,
+ 3.721, 3.625, 3.481, 3.208, 2.861, 2.515, 2.213, 1.976, 1.773, 1.593, 1.439, 1.313, 1.213, 1.138, 1.103, 1.071, 1.071, 1.101, 1.129, 1.194, 1.286, 1.405, 1.555, 1.736, 1.949, 2.202, 2.521, 2.886, 3.189, 3.338, 3.375, 3.406,
+ 3.716, 3.616, 3.458, 3.171, 2.819, 2.472, 2.176, 1.942, 1.741, 1.563, 1.411, 1.285, 1.186, 1.112, 1.071, 1.051, 1.049, 1.069, 1.103, 1.165, 1.256, 1.376, 1.523, 1.702, 1.913, 2.163, 2.477, 2.843, 3.155, 3.318, 3.358, 3.389,
+ 3.712, 3.609, 3.439, 3.142, 2.787, 2.443, 2.147, 1.918, 1.721, 1.541, 1.391, 1.266, 1.167, 1.094, 1.051, 1.035, 1.035, 1.049, 1.085, 1.145, 1.236, 1.355, 1.499, 1.676, 1.886, 2.136, 2.449, 2.814, 3.135, 3.307, 3.351, 3.378,
+ 3.709, 3.604, 3.422, 3.123, 2.768, 2.419, 2.129, 1.903, 1.706, 1.527, 1.377, 1.253, 1.155, 1.083, 1.035, 1.023, 1.023, 1.035, 1.074, 1.134, 1.224, 1.341, 1.484, 1.661, 1.868, 2.117, 2.429, 2.797, 3.122, 3.301, 3.346, 3.374,
+ 3.711, 3.597, 3.412, 3.114, 2.758, 2.409, 2.119, 1.895, 1.701, 1.523, 1.373, 1.251, 1.153, 1.081, 1.033, 1.001, 1.001, 1.032, 1.073, 1.133, 1.222, 1.338, 1.479, 1.655, 1.861, 2.107, 2.418, 2.787, 3.115, 3.297, 3.343, 3.373,
+ 3.713, 3.597, 3.412, 3.113, 2.758, 2.409, 2.119, 1.893, 1.698, 1.523, 1.373, 1.251, 1.153, 1.081, 1.034, 1.011, 1.011, 1.032, 1.074, 1.134, 1.222, 1.338, 1.479, 1.655, 1.861, 2.107, 2.418, 2.787, 3.116, 3.294, 3.341, 3.371,
+ 3.721, 3.599, 3.414, 3.116, 2.763, 2.418, 2.124, 1.895, 1.704, 1.531, 1.382, 1.259, 1.162, 1.091, 1.048, 1.034, 1.032, 1.046, 1.083, 1.145, 1.232, 1.348, 1.491, 1.664, 1.869, 2.115, 2.428, 2.798, 3.123, 3.294, 3.339, 3.372,
+ 3.727, 3.604, 3.421, 3.132, 2.784, 2.438, 2.141, 1.908, 1.716, 1.547, 1.399, 1.276, 1.178, 1.107, 1.069, 1.048, 1.046, 1.067, 1.101, 1.162, 1.249, 1.366, 1.509, 1.684, 1.886, 2.134, 2.449, 2.821, 3.135, 3.299, 3.341, 3.375,
+ 3.739, 3.613, 3.431, 3.154, 2.813, 2.468, 2.166, 1.931, 1.739, 1.569, 1.424, 1.302, 1.203, 1.129, 1.098, 1.069, 1.069, 1.096, 1.123, 1.185, 1.274, 1.391, 1.536, 1.709, 1.914, 2.162, 2.481, 2.851, 3.156, 3.311, 3.342, 3.378,
+ 3.751, 3.626, 3.449, 3.186, 2.855, 2.509, 2.201, 1.961, 1.768, 1.601, 1.454, 1.333, 1.235, 1.159, 1.129, 1.098, 1.098, 1.123, 1.152, 1.216, 1.307, 1.424, 1.569, 1.744, 1.947, 2.202, 2.526, 2.891, 3.182, 3.322, 3.351, 3.387,
+ 3.772, 3.641, 3.473, 3.221, 2.902, 2.559, 2.248, 1.999, 1.804, 1.639, 1.496, 1.373, 1.274, 1.201, 1.159, 1.133, 1.133, 1.152, 1.191, 1.254, 1.347, 1.466, 1.611, 1.785, 1.989, 2.253, 2.582, 2.939, 3.209, 3.334, 3.361, 3.402,
+ 3.797, 3.663, 3.496, 3.263, 2.959, 2.624, 2.308, 2.049, 1.847, 1.684, 1.542, 1.422, 1.321, 1.252, 1.201, 1.175, 1.175, 1.191, 1.239, 1.298, 1.394, 1.516, 1.658, 1.831, 2.041, 2.313, 2.651, 2.998, 3.244, 3.351, 3.375, 3.422,
+ 3.831, 3.686, 3.523, 3.307, 3.023, 2.698, 2.379, 2.112, 1.902, 1.737, 1.596, 1.476, 1.378, 1.315, 1.252, 1.227, 1.227, 1.239, 1.296, 1.355, 1.451, 1.572, 1.715, 1.888, 2.103, 2.386, 2.731, 3.063, 3.279, 3.367, 3.393, 3.456,
+ 3.871, 3.714, 3.551, 3.355, 3.091, 2.781, 2.465, 2.186, 1.965, 1.795, 1.654, 1.538, 1.442, 1.378, 1.318, 1.291, 1.291, 1.304, 1.355, 1.424, 1.515, 1.634, 1.778, 1.952, 2.178, 2.479, 2.821, 3.129, 3.314, 3.381, 3.419, 3.491,
+ 3.925, 3.749, 3.582, 3.401, 3.156, 2.866, 2.559, 2.274, 2.039, 1.859, 1.718, 1.604, 1.513, 1.442, 1.389, 1.363, 1.363, 1.379, 1.424, 1.501, 1.586, 1.702, 1.847, 2.028, 2.269, 2.579, 2.913, 3.193, 3.343, 3.396, 3.447, 3.539,
+ 3.994, 3.794, 3.619, 3.442, 3.231, 2.961, 2.662, 2.375, 2.129, 1.938, 1.789, 1.675, 1.591, 1.513, 1.465, 1.439, 1.439, 1.457, 1.501, 1.582, 1.661, 1.777, 1.925, 2.118, 2.375, 2.691, 3.008, 3.251, 3.371, 3.414, 3.479, 3.598,
+ 4.082, 3.845, 3.656, 3.489, 3.298, 3.053, 2.771, 2.485, 2.232, 2.028, 1.871, 1.751, 1.672, 1.591, 1.544, 1.521, 1.521, 1.539, 1.582, 1.661, 1.741, 1.859, 2.014, 2.224, 2.495, 2.806, 3.098, 3.301, 3.392, 3.431, 3.518, 3.677,
+ 4.196, 3.911, 3.698, 3.534, 3.363, 3.146, 2.881, 2.604, 2.348, 2.132, 1.964, 1.836, 1.751, 1.672, 1.628, 1.606, 1.606, 1.624, 1.665, 1.741, 1.827, 1.951, 2.121, 2.344, 2.624, 2.923, 3.177, 3.336, 3.405, 3.447, 3.567, 3.776,
+ 4.341, 4.002, 3.744, 3.575, 3.415, 3.229, 2.989, 2.729, 2.475, 2.251, 2.071, 1.936, 1.836, 1.759, 1.713, 1.693, 1.693, 1.711, 1.753, 1.827, 1.925, 2.058, 2.243, 2.481, 2.758, 3.027, 3.238, 3.361, 3.409, 3.466, 3.637, 3.896,
+ 4.516, 4.123, 3.804, 3.621, 3.468, 3.308, 3.096, 2.855, 2.609, 2.385, 2.194, 2.045, 1.936, 1.857, 1.807, 1.784, 1.784, 1.803, 1.852, 1.925, 2.033, 2.183, 2.382, 2.623, 2.886, 3.121, 3.284, 3.372, 3.413, 3.494, 3.727, 4.048,
+ 4.716, 4.264, 3.875, 3.674, 3.523, 3.376, 3.189, 2.966, 2.733, 2.511, 2.315, 2.158, 2.039, 1.936, 1.875, 1.872, 1.872, 1.872, 1.925, 2.028, 2.148, 2.308, 2.513, 2.751, 2.994, 3.191, 3.319, 3.384, 3.427, 3.541, 3.838, 4.221
+ ],
+ "sigma": 0.00152,
+ "sigma_Cb": 0.00172
+ }
+ },
+ {
+ "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": 2868,
+ "ccm":
+ [
+ 1.58923, -0.36649, -0.22273,
+ -0.43591, 1.84858, -0.41268,
+ 0.02948, -0.77666, 1.74718
+ ]
+ },
+ {
+ "ct": 2965,
+ "ccm":
+ [
+ 1.73397, -0.42794, -0.30603,
+ -0.36504, 1.72431, -0.35926,
+ 0.12765, -1.10933, 1.98168
+ ]
+ },
+ {
+ "ct": 3603,
+ "ccm":
+ [
+ 1.61787, -0.42704, -0.19084,
+ -0.37819, 1.74588, -0.36769,
+ 0.00961, -0.59807, 1.58847
+ ]
+ },
+ {
+ "ct": 4620,
+ "ccm":
+ [
+ 1.55581, -0.35422, -0.20158,
+ -0.31805, 1.79309, -0.47505,
+ -0.01256, -0.54489, 1.55746
+ ]
+ },
+ {
+ "ct": 5901,
+ "ccm":
+ [
+ 1.64439, -0.48855, -0.15585,
+ -0.29149, 1.67122, -0.37972,
+ -0.03111, -0.44052, 1.47163
+ ]
+ },
+ {
+ "ct": 7610,
+ "ccm":
+ [
+ 1.48667, -0.26072, -0.22595,
+ -0.21815, 1.86724, -0.64909,
+ -0.00985, -0.64485, 1.65471
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "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 ]
+ }
+ },
+ {
+ "rpi.cac":
+ {
+ "strength": 1.0,
+ "lut_rx":
+ [
+ -0.11, -0.11, -0.17, -0.11, -0.0, 0.08, 0.13, 0.1, 0.1,
+ -0.07, -0.17, -0.16, -0.08, -0.02, 0.06, 0.15, 0.15, 0.07,
+ -0.11, -0.21, -0.17, -0.07, -0.02, 0.03, 0.14, 0.17, 0.14,
+ -0.19, -0.22, -0.16, -0.07, -0.01, 0.03, 0.12, 0.19, 0.21,
+ -0.19, -0.23, -0.16, -0.06, -0.01, 0.04, 0.13, 0.19, 0.24,
+ -0.18, -0.22, -0.17, -0.05, -0.01, 0.05, 0.15, 0.2, 0.21,
+ -0.14, -0.19, -0.17, -0.06, 0.0, 0.07, 0.15, 0.18, 0.15,
+ -0.09, -0.14, -0.17, -0.08, 0.0, 0.09, 0.15, 0.14, 0.06,
+ -0.09, -0.08, -0.15, -0.12, -0.0, 0.12, 0.16, 0.07, 0.06
+ ],
+ "lut_ry":
+ [
+ -0.11, -0.11, -0.21, -0.21, -0.19, -0.21, -0.19, -0.11, 0.11,
+ -0.02, -0.1, -0.14, -0.14, -0.13, -0.14, -0.15, -0.11, 0.03,
+ -0.03, -0.09, -0.12, -0.12, -0.12, -0.11, -0.12, -0.1, -0.02,
+ -0.05, -0.07, -0.1, -0.11, -0.11, -0.09, -0.08, -0.07, -0.03,
+ -0.03, -0.02, -0.04, -0.05, -0.05, -0.05, -0.02, -0.01, -0.02,
+ 0.01, 0.03, 0.0, -0.02, -0.02, -0.01, 0.02, 0.03, 0.01,
+ 0.01, 0.06, 0.06, 0.0, -0.01, 0.02, 0.06, 0.06, 0.01,
+ -0.0, 0.08, 0.12, 0.08, 0.05, 0.08, 0.1, 0.08, -0.0,
+ 0.11, 0.09, 0.19, 0.19, 0.15, 0.19, 0.18, 0.12, 0.11
+ ],
+ "lut_bx":
+ [
+ -0.3, -0.28, -0.34, -0.19, -0.01, 0.13, 0.27, 0.21, 0.2,
+ -0.24, -0.38, -0.38, -0.24, -0.02, 0.19, 0.31, 0.34, 0.2,
+ -0.4, -0.47, -0.44, -0.26, -0.03, 0.21, 0.35, 0.39, 0.38,
+ -0.52, -0.49, -0.46, -0.27, -0.02, 0.22, 0.38, 0.46, 0.54,
+ -0.56, -0.51, -0.44, -0.27, -0.02, 0.23, 0.39, 0.47, 0.64,
+ -0.52, -0.49, -0.43, -0.27, -0.02, 0.21, 0.39, 0.45, 0.59,
+ -0.39, -0.41, -0.39, -0.26, -0.02, 0.2, 0.37, 0.39, 0.47,
+ -0.2, -0.34, -0.36, -0.23, -0.03, 0.18, 0.33, 0.28, 0.19,
+ -0.2, -0.21, -0.32, -0.18, -0.04, 0.14, 0.28, 0.17, 0.2
+ ],
+ "lut_by":
+ [
+ -0.25, -0.23, -0.31, -0.36, -0.41, -0.36, -0.32, -0.19, -0.2,
+ -0.09, -0.18, -0.27, -0.32, -0.35, -0.31, -0.23, -0.17, 0.01,
+ -0.13, -0.14, -0.19, -0.2, -0.2, -0.21, -0.17, -0.12, -0.04,
+ -0.1, -0.06, -0.06, -0.07, -0.05, -0.05, -0.05, -0.04, -0.07,
+ -0.03, 0.05, 0.06, 0.07, 0.07, 0.08, 0.06, 0.03, -0.05,
+ 0.03, 0.12, 0.18, 0.19, 0.19, 0.2, 0.17, 0.11, -0.03,
+ 0.04, 0.22, 0.3, 0.37, 0.42, 0.37, 0.3, 0.21, -0.02,
+ 0.05, 0.27, 0.39, 0.5, 0.57, 0.51, 0.41, 0.25, -0.09,
+ 0.25, 0.33, 0.52, 0.65, 0.8, 0.72, 0.56, 0.33, -0.25
+ ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "power_min": 0.7,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "power_min": 0.7,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/imx708_wide_noir.json b/src/ipa/rpi/pisp/data/imx708_wide_noir.json
new file mode 100644
index 00000000..75d1149b
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/imx708_wide_noir.json
@@ -0,0 +1,1148 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 41985,
+ "reference_gain": 1.12,
+ "reference_aperture": 1.0,
+ "reference_lux": 810,
+ "reference_Y": 13859
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.9
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 206,
+ "slope": 0.00324
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.65,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.717, 1.712, 1.703, 1.692, 1.674, 1.653, 1.638, 1.624, 1.613, 1.601, 1.589, 1.579, 1.575, 1.573, 1.571, 1.571, 1.571, 1.571, 1.572, 1.577, 1.583, 1.593, 1.605, 1.618, 1.636, 1.653, 1.677, 1.699, 1.715, 1.722, 1.731, 1.733,
+ 1.714, 1.706, 1.696, 1.678, 1.658, 1.639, 1.627, 1.614, 1.602, 1.591, 1.579, 1.572, 1.569, 1.566, 1.565, 1.564, 1.564, 1.565, 1.567, 1.571, 1.578, 1.585, 1.595, 1.607, 1.622, 1.641, 1.661, 1.685, 1.706, 1.717, 1.724, 1.732,
+ 1.708, 1.698, 1.688, 1.667, 1.647, 1.629, 1.619, 1.606, 1.593, 1.581, 1.572, 1.565, 1.561, 1.559, 1.559, 1.559, 1.559, 1.561, 1.562, 1.566, 1.571, 1.577, 1.587, 1.598, 1.612, 1.629, 1.649, 1.674, 1.697, 1.713, 1.721, 1.728,
+ 1.706, 1.695, 1.681, 1.655, 1.636, 1.622, 1.613, 1.597, 1.585, 1.572, 1.564, 1.559, 1.558, 1.556, 1.555, 1.555, 1.556, 1.556, 1.558, 1.561, 1.566, 1.571, 1.578, 1.591, 1.605, 1.619, 1.638, 1.662, 1.691, 1.708, 1.719, 1.726,
+ 1.706, 1.692, 1.675, 1.649, 1.629, 1.615, 1.607, 1.592, 1.575, 1.565, 1.559, 1.554, 1.552, 1.551, 1.551, 1.551, 1.551, 1.552, 1.554, 1.557, 1.561, 1.566, 1.573, 1.582, 1.596, 1.611, 1.627, 1.652, 1.681, 1.705, 1.717, 1.724,
+ 1.703, 1.686, 1.664, 1.639, 1.625, 1.612, 1.599, 1.585, 1.569, 1.559, 1.554, 1.549, 1.548, 1.548, 1.546, 1.546, 1.546, 1.547, 1.549, 1.553, 1.557, 1.563, 1.569, 1.576, 1.591, 1.603, 1.621, 1.644, 1.674, 1.698, 1.714, 1.724,
+ 1.702, 1.681, 1.659, 1.635, 1.621, 1.607, 1.594, 1.579, 1.565, 1.554, 1.549, 1.546, 1.544, 1.543, 1.543, 1.542, 1.543, 1.543, 1.544, 1.549, 1.553, 1.558, 1.564, 1.572, 1.584, 1.599, 1.614, 1.639, 1.667, 1.695, 1.712, 1.724,
+ 1.697, 1.678, 1.655, 1.631, 1.616, 1.602, 1.589, 1.575, 1.559, 1.551, 1.545, 1.543, 1.542, 1.542, 1.541, 1.539, 1.539, 1.539, 1.542, 1.544, 1.551, 1.555, 1.562, 1.571, 1.579, 1.594, 1.611, 1.631, 1.661, 1.691, 1.712, 1.724,
+ 1.695, 1.674, 1.651, 1.629, 1.615, 1.599, 1.584, 1.568, 1.554, 1.545, 1.542, 1.541, 1.539, 1.539, 1.538, 1.538, 1.538, 1.539, 1.539, 1.543, 1.548, 1.554, 1.559, 1.568, 1.576, 1.592, 1.608, 1.629, 1.655, 1.689, 1.709, 1.723,
+ 1.691, 1.671, 1.648, 1.627, 1.613, 1.597, 1.581, 1.564, 1.551, 1.543, 1.539, 1.538, 1.538, 1.537, 1.536, 1.535, 1.536, 1.538, 1.539, 1.542, 1.546, 1.551, 1.558, 1.564, 1.575, 1.588, 1.604, 1.627, 1.654, 1.686, 1.709, 1.724,
+ 1.689, 1.667, 1.643, 1.626, 1.612, 1.594, 1.579, 1.559, 1.549, 1.541, 1.536, 1.535, 1.535, 1.535, 1.534, 1.533, 1.534, 1.536, 1.538, 1.541, 1.545, 1.549, 1.555, 1.563, 1.573, 1.585, 1.602, 1.624, 1.651, 1.683, 1.709, 1.725,
+ 1.686, 1.665, 1.641, 1.623, 1.609, 1.594, 1.576, 1.559, 1.546, 1.538, 1.535, 1.534, 1.533, 1.532, 1.531, 1.531, 1.532, 1.534, 1.537, 1.539, 1.544, 1.549, 1.554, 1.562, 1.572, 1.585, 1.601, 1.622, 1.651, 1.682, 1.711, 1.726,
+ 1.686, 1.661, 1.639, 1.623, 1.609, 1.592, 1.574, 1.557, 1.545, 1.537, 1.534, 1.533, 1.532, 1.531, 1.529, 1.528, 1.529, 1.532, 1.537, 1.539, 1.542, 1.548, 1.553, 1.562, 1.571, 1.584, 1.601, 1.621, 1.649, 1.682, 1.711, 1.726,
+ 1.685, 1.661, 1.638, 1.624, 1.609, 1.592, 1.574, 1.557, 1.544, 1.536, 1.533, 1.532, 1.531, 1.529, 1.527, 1.522, 1.526, 1.531, 1.536, 1.539, 1.542, 1.547, 1.553, 1.562, 1.571, 1.583, 1.601, 1.621, 1.648, 1.682, 1.711, 1.726,
+ 1.684, 1.658, 1.638, 1.624, 1.611, 1.592, 1.573, 1.556, 1.543, 1.536, 1.532, 1.531, 1.529, 1.528, 1.522, 1.517, 1.519, 1.527, 1.535, 1.539, 1.541, 1.547, 1.553, 1.562, 1.571, 1.583, 1.601, 1.622, 1.647, 1.681, 1.711, 1.727,
+ 1.681, 1.658, 1.641, 1.624, 1.611, 1.593, 1.573, 1.555, 1.541, 1.535, 1.532, 1.529, 1.529, 1.527, 1.517, 1.506, 1.506, 1.522, 1.534, 1.538, 1.541, 1.546, 1.552, 1.562, 1.569, 1.583, 1.601, 1.622, 1.646, 1.679, 1.709, 1.728,
+ 1.679, 1.656, 1.639, 1.624, 1.611, 1.595, 1.575, 1.556, 1.541, 1.534, 1.531, 1.529, 1.529, 1.527, 1.517, 1.507, 1.507, 1.522, 1.533, 1.538, 1.539, 1.546, 1.552, 1.561, 1.569, 1.584, 1.601, 1.622, 1.647, 1.681, 1.709, 1.726,
+ 1.678, 1.656, 1.638, 1.625, 1.612, 1.597, 1.577, 1.557, 1.542, 1.534, 1.529, 1.529, 1.528, 1.527, 1.522, 1.516, 1.519, 1.525, 1.533, 1.537, 1.539, 1.545, 1.552, 1.561, 1.571, 1.584, 1.601, 1.623, 1.649, 1.681, 1.709, 1.726,
+ 1.679, 1.654, 1.639, 1.626, 1.613, 1.598, 1.578, 1.558, 1.543, 1.534, 1.529, 1.529, 1.529, 1.528, 1.527, 1.522, 1.525, 1.528, 1.533, 1.536, 1.539, 1.546, 1.553, 1.561, 1.571, 1.586, 1.602, 1.623, 1.651, 1.683, 1.712, 1.726,
+ 1.677, 1.655, 1.641, 1.628, 1.615, 1.599, 1.581, 1.562, 1.545, 1.535, 1.531, 1.529, 1.529, 1.528, 1.527, 1.527, 1.528, 1.531, 1.533, 1.536, 1.539, 1.545, 1.552, 1.561, 1.572, 1.588, 1.607, 1.626, 1.654, 1.686, 1.716, 1.729,
+ 1.676, 1.655, 1.642, 1.629, 1.617, 1.602, 1.586, 1.564, 1.546, 1.536, 1.531, 1.529, 1.529, 1.529, 1.529, 1.529, 1.529, 1.532, 1.534, 1.536, 1.539, 1.547, 1.553, 1.563, 1.576, 1.591, 1.609, 1.627, 1.655, 1.688, 1.716, 1.729,
+ 1.676, 1.658, 1.641, 1.631, 1.617, 1.605, 1.588, 1.569, 1.553, 1.539, 1.532, 1.531, 1.529, 1.529, 1.529, 1.529, 1.531, 1.532, 1.534, 1.537, 1.541, 1.547, 1.553, 1.564, 1.578, 1.594, 1.613, 1.632, 1.659, 1.691, 1.717, 1.728,
+ 1.676, 1.658, 1.642, 1.631, 1.619, 1.608, 1.592, 1.575, 1.556, 1.542, 1.533, 1.531, 1.529, 1.529, 1.529, 1.531, 1.531, 1.532, 1.534, 1.537, 1.542, 1.548, 1.556, 1.567, 1.582, 1.598, 1.616, 1.638, 1.661, 1.693, 1.717, 1.729,
+ 1.678, 1.661, 1.644, 1.632, 1.621, 1.611, 1.596, 1.579, 1.561, 1.546, 1.536, 1.532, 1.531, 1.531, 1.531, 1.531, 1.532, 1.533, 1.535, 1.538, 1.544, 1.549, 1.559, 1.569, 1.587, 1.604, 1.618, 1.639, 1.669, 1.697, 1.718, 1.731,
+ 1.679, 1.662, 1.648, 1.635, 1.625, 1.615, 1.602, 1.586, 1.569, 1.552, 1.541, 1.535, 1.532, 1.532, 1.531, 1.532, 1.533, 1.534, 1.537, 1.541, 1.546, 1.552, 1.562, 1.576, 1.592, 1.608, 1.622, 1.647, 1.673, 1.703, 1.721, 1.734,
+ 1.684, 1.664, 1.649, 1.637, 1.627, 1.618, 1.606, 1.593, 1.576, 1.561, 1.547, 1.539, 1.535, 1.533, 1.533, 1.533, 1.534, 1.536, 1.539, 1.543, 1.549, 1.555, 1.568, 1.583, 1.596, 1.612, 1.629, 1.651, 1.681, 1.706, 1.723, 1.734,
+ 1.689, 1.669, 1.649, 1.639, 1.629, 1.621, 1.609, 1.597, 1.585, 1.567, 1.554, 1.546, 1.539, 1.536, 1.535, 1.535, 1.537, 1.538, 1.542, 1.546, 1.553, 1.562, 1.572, 1.589, 1.603, 1.619, 1.635, 1.658, 1.686, 1.708, 1.726, 1.736,
+ 1.692, 1.673, 1.655, 1.644, 1.634, 1.624, 1.614, 1.604, 1.592, 1.577, 1.566, 1.554, 1.546, 1.542, 1.538, 1.538, 1.539, 1.542, 1.546, 1.552, 1.559, 1.568, 1.581, 1.596, 1.609, 1.625, 1.642, 1.664, 1.693, 1.714, 1.727, 1.736,
+ 1.695, 1.679, 1.662, 1.647, 1.638, 1.631, 1.623, 1.612, 1.601, 1.589, 1.577, 1.565, 1.555, 1.549, 1.546, 1.545, 1.546, 1.548, 1.552, 1.559, 1.568, 1.579, 1.593, 1.604, 1.618, 1.632, 1.648, 1.676, 1.701, 1.718, 1.728, 1.739,
+ 1.699, 1.684, 1.667, 1.654, 1.644, 1.635, 1.629, 1.621, 1.609, 1.599, 1.589, 1.578, 1.568, 1.559, 1.556, 1.554, 1.554, 1.557, 1.563, 1.569, 1.578, 1.589, 1.599, 1.612, 1.625, 1.641, 1.661, 1.685, 1.707, 1.722, 1.734, 1.742,
+ 1.703, 1.691, 1.672, 1.658, 1.648, 1.639, 1.634, 1.628, 1.618, 1.606, 1.598, 1.589, 1.579, 1.573, 1.568, 1.567, 1.567, 1.568, 1.571, 1.578, 1.587, 1.597, 1.607, 1.618, 1.632, 1.651, 1.672, 1.694, 1.715, 1.728, 1.737, 1.742,
+ 1.707, 1.691, 1.676, 1.662, 1.651, 1.643, 1.638, 1.631, 1.622, 1.614, 1.604, 1.596, 1.589, 1.579, 1.575, 1.573, 1.573, 1.574, 1.578, 1.586, 1.589, 1.598, 1.609, 1.625, 1.638, 1.657, 1.679, 1.701, 1.719, 1.728, 1.738, 1.742
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 2.939, 2.935, 2.916, 2.895, 2.856, 2.825, 2.797, 2.777, 2.761, 2.741, 2.726, 2.709, 2.707, 2.704, 2.702, 2.702, 2.703, 2.706, 2.708, 2.709, 2.719, 2.735, 2.753, 2.776, 2.801, 2.832, 2.874, 2.915, 2.939, 2.943, 2.953, 2.961,
+ 2.936, 2.923, 2.901, 2.863, 2.829, 2.801, 2.781, 2.763, 2.743, 2.732, 2.712, 2.701, 2.696, 2.692, 2.691, 2.691, 2.693, 2.694, 2.696, 2.701, 2.709, 2.725, 2.741, 2.758, 2.779, 2.811, 2.838, 2.879, 2.919, 2.939, 2.948, 2.959,
+ 2.929, 2.909, 2.887, 2.847, 2.808, 2.783, 2.765, 2.748, 2.732, 2.713, 2.699, 2.691, 2.687, 2.686, 2.685, 2.685, 2.687, 2.689, 2.691, 2.694, 2.701, 2.709, 2.725, 2.745, 2.763, 2.786, 2.818, 2.863, 2.907, 2.933, 2.941, 2.955,
+ 2.929, 2.903, 2.875, 2.825, 2.791, 2.769, 2.755, 2.737, 2.718, 2.701, 2.688, 2.683, 2.681, 2.679, 2.681, 2.679, 2.681, 2.682, 2.685, 2.689, 2.694, 2.701, 2.711, 2.737, 2.754, 2.772, 2.803, 2.844, 2.894, 2.931, 2.939, 2.953,
+ 2.926, 2.895, 2.862, 2.816, 2.782, 2.759, 2.744, 2.727, 2.709, 2.691, 2.679, 2.673, 2.671, 2.669, 2.669, 2.669, 2.671, 2.674, 2.678, 2.681, 2.685, 2.694, 2.707, 2.725, 2.739, 2.762, 2.786, 2.829, 2.879, 2.919, 2.942, 2.952,
+ 2.919, 2.886, 2.846, 2.797, 2.772, 2.751, 2.737, 2.719, 2.694, 2.679, 2.672, 2.666, 2.664, 2.661, 2.659, 2.658, 2.661, 2.664, 2.669, 2.673, 2.678, 2.685, 2.696, 2.715, 2.728, 2.749, 2.774, 2.808, 2.866, 2.909, 2.936, 2.951,
+ 2.904, 2.877, 2.835, 2.789, 2.763, 2.744, 2.728, 2.712, 2.686, 2.672, 2.664, 2.657, 2.654, 2.654, 2.652, 2.653, 2.654, 2.657, 2.661, 2.666, 2.672, 2.678, 2.688, 2.703, 2.721, 2.742, 2.762, 2.797, 2.851, 2.902, 2.928, 2.949,
+ 2.901, 2.869, 2.825, 2.781, 2.756, 2.738, 2.721, 2.698, 2.679, 2.665, 2.656, 2.652, 2.649, 2.648, 2.648, 2.648, 2.649, 2.651, 2.654, 2.659, 2.667, 2.675, 2.683, 2.699, 2.711, 2.736, 2.754, 2.789, 2.838, 2.896, 2.926, 2.948,
+ 2.899, 2.862, 2.815, 2.774, 2.752, 2.734, 2.717, 2.689, 2.669, 2.658, 2.651, 2.646, 2.645, 2.643, 2.643, 2.644, 2.645, 2.646, 2.649, 2.654, 2.661, 2.669, 2.681, 2.693, 2.707, 2.729, 2.751, 2.782, 2.834, 2.887, 2.924, 2.947,
+ 2.898, 2.853, 2.812, 2.771, 2.751, 2.731, 2.711, 2.686, 2.663, 2.653, 2.646, 2.642, 2.641, 2.642, 2.642, 2.641, 2.641, 2.641, 2.646, 2.651, 2.657, 2.667, 2.678, 2.693, 2.705, 2.728, 2.746, 2.781, 2.829, 2.885, 2.924, 2.951,
+ 2.896, 2.851, 2.807, 2.771, 2.752, 2.729, 2.709, 2.681, 2.661, 2.649, 2.643, 2.641, 2.639, 2.639, 2.638, 2.636, 2.637, 2.638, 2.644, 2.649, 2.657, 2.666, 2.676, 2.688, 2.705, 2.725, 2.745, 2.777, 2.827, 2.884, 2.927, 2.951,
+ 2.891, 2.846, 2.803, 2.771, 2.749, 2.728, 2.706, 2.677, 2.658, 2.647, 2.641, 2.637, 2.637, 2.636, 2.636, 2.633, 2.632, 2.635, 2.643, 2.649, 2.656, 2.665, 2.675, 2.688, 2.704, 2.719, 2.744, 2.776, 2.822, 2.881, 2.927, 2.958,
+ 2.887, 2.841, 2.797, 2.769, 2.749, 2.729, 2.704, 2.674, 2.655, 2.645, 2.638, 2.635, 2.633, 2.632, 2.631, 2.625, 2.627, 2.631, 2.639, 2.649, 2.654, 2.662, 2.673, 2.686, 2.701, 2.718, 2.742, 2.773, 2.822, 2.881, 2.926, 2.958,
+ 2.883, 2.837, 2.796, 2.769, 2.749, 2.729, 2.701, 2.673, 2.653, 2.641, 2.636, 2.632, 2.631, 2.629, 2.623, 2.612, 2.619, 2.627, 2.637, 2.648, 2.652, 2.659, 2.671, 2.688, 2.699, 2.719, 2.742, 2.774, 2.821, 2.882, 2.927, 2.961,
+ 2.881, 2.832, 2.795, 2.769, 2.751, 2.729, 2.701, 2.672, 2.652, 2.639, 2.633, 2.631, 2.628, 2.625, 2.611, 2.599, 2.607, 2.619, 2.635, 2.644, 2.652, 2.659, 2.669, 2.686, 2.698, 2.719, 2.743, 2.775, 2.822, 2.881, 2.926, 2.961,
+ 2.879, 2.829, 2.793, 2.771, 2.751, 2.731, 2.701, 2.672, 2.651, 2.639, 2.632, 2.628, 2.626, 2.621, 2.601, 2.581, 2.581, 2.611, 2.631, 2.642, 2.648, 2.657, 2.669, 2.685, 2.699, 2.721, 2.743, 2.776, 2.819, 2.879, 2.927, 2.961,
+ 2.876, 2.829, 2.796, 2.773, 2.752, 2.731, 2.705, 2.672, 2.651, 2.637, 2.631, 2.627, 2.625, 2.619, 2.601, 2.581, 2.581, 2.611, 2.629, 2.641, 2.647, 2.658, 2.669, 2.685, 2.697, 2.721, 2.746, 2.777, 2.822, 2.881, 2.929, 2.964,
+ 2.874, 2.827, 2.796, 2.775, 2.755, 2.733, 2.708, 2.674, 2.649, 2.635, 2.629, 2.626, 2.624, 2.621, 2.609, 2.601, 2.606, 2.615, 2.629, 2.638, 2.645, 2.657, 2.669, 2.682, 2.699, 2.722, 2.747, 2.778, 2.822, 2.881, 2.931, 2.964,
+ 2.871, 2.827, 2.797, 2.776, 2.761, 2.734, 2.711, 2.679, 2.651, 2.636, 2.628, 2.626, 2.624, 2.621, 2.618, 2.611, 2.614, 2.619, 2.628, 2.639, 2.644, 2.657, 2.668, 2.683, 2.698, 2.723, 2.749, 2.782, 2.824, 2.882, 2.933, 2.965,
+ 2.869, 2.825, 2.797, 2.777, 2.765, 2.741, 2.718, 2.683, 2.655, 2.638, 2.627, 2.625, 2.624, 2.623, 2.621, 2.618, 2.618, 2.624, 2.629, 2.639, 2.644, 2.657, 2.669, 2.684, 2.701, 2.725, 2.755, 2.782, 2.829, 2.887, 2.937, 2.965,
+ 2.871, 2.826, 2.799, 2.776, 2.765, 2.744, 2.723, 2.689, 2.659, 2.639, 2.629, 2.626, 2.626, 2.624, 2.624, 2.622, 2.624, 2.627, 2.632, 2.639, 2.646, 2.657, 2.671, 2.687, 2.706, 2.732, 2.757, 2.789, 2.836, 2.893, 2.941, 2.965,
+ 2.869, 2.831, 2.803, 2.778, 2.766, 2.748, 2.729, 2.697, 2.667, 2.645, 2.632, 2.628, 2.625, 2.625, 2.625, 2.625, 2.627, 2.629, 2.634, 2.638, 2.648, 2.661, 2.673, 2.688, 2.711, 2.741, 2.762, 2.797, 2.843, 2.901, 2.943, 2.964,
+ 2.872, 2.837, 2.802, 2.781, 2.768, 2.753, 2.734, 2.702, 2.674, 2.647, 2.634, 2.629, 2.626, 2.625, 2.625, 2.627, 2.629, 2.632, 2.635, 2.639, 2.649, 2.663, 2.676, 2.694, 2.719, 2.746, 2.771, 2.799, 2.851, 2.905, 2.947, 2.969,
+ 2.871, 2.837, 2.805, 2.786, 2.771, 2.755, 2.739, 2.714, 2.685, 2.655, 2.639, 2.631, 2.626, 2.625, 2.626, 2.628, 2.629, 2.632, 2.634, 2.642, 2.651, 2.663, 2.679, 2.701, 2.726, 2.756, 2.773, 2.809, 2.861, 2.913, 2.949, 2.968,
+ 2.876, 2.841, 2.808, 2.789, 2.775, 2.759, 2.744, 2.719, 2.693, 2.664, 2.648, 2.636, 2.629, 2.627, 2.627, 2.629, 2.631, 2.633, 2.637, 2.645, 2.653, 2.666, 2.682, 2.708, 2.734, 2.759, 2.779, 2.815, 2.868, 2.918, 2.951, 2.971,
+ 2.882, 2.845, 2.816, 2.791, 2.778, 2.766, 2.748, 2.733, 2.707, 2.681, 2.656, 2.643, 2.636, 2.632, 2.631, 2.632, 2.633, 2.637, 2.643, 2.648, 2.659, 2.672, 2.691, 2.719, 2.747, 2.765, 2.791, 2.829, 2.881, 2.931, 2.952, 2.969,
+ 2.889, 2.855, 2.819, 2.799, 2.782, 2.769, 2.755, 2.741, 2.717, 2.691, 2.672, 2.652, 2.643, 2.639, 2.636, 2.636, 2.638, 2.642, 2.646, 2.655, 2.665, 2.682, 2.703, 2.729, 2.752, 2.774, 2.798, 2.839, 2.891, 2.933, 2.959, 2.975,
+ 2.897, 2.862, 2.829, 2.804, 2.789, 2.776, 2.764, 2.749, 2.734, 2.709, 2.689, 2.669, 2.652, 2.644, 2.642, 2.642, 2.644, 2.647, 2.654, 2.664, 2.677, 2.694, 2.714, 2.742, 2.764, 2.782, 2.809, 2.852, 2.899, 2.936, 2.961, 2.976,
+ 2.902, 2.869, 2.841, 2.811, 2.797, 2.785, 2.776, 2.761, 2.748, 2.727, 2.708, 2.689, 2.671, 2.659, 2.655, 2.654, 2.653, 2.656, 2.666, 2.678, 2.693, 2.713, 2.737, 2.756, 2.775, 2.798, 2.825, 2.871, 2.913, 2.944, 2.966, 2.979,
+ 2.911, 2.885, 2.848, 2.821, 2.804, 2.793, 2.784, 2.774, 2.759, 2.747, 2.726, 2.709, 2.692, 2.679, 2.673, 2.672, 2.671, 2.672, 2.681, 2.694, 2.712, 2.729, 2.749, 2.768, 2.789, 2.811, 2.844, 2.886, 2.928, 2.956, 2.971, 2.984,
+ 2.925, 2.893, 2.861, 2.831, 2.813, 2.802, 2.795, 2.783, 2.773, 2.759, 2.744, 2.729, 2.715, 2.701, 2.698, 2.694, 2.693, 2.694, 2.702, 2.714, 2.729, 2.747, 2.761, 2.781, 2.802, 2.828, 2.864, 2.907, 2.942, 2.967, 2.978, 2.989,
+ 2.932, 2.898, 2.871, 2.843, 2.823, 2.811, 2.802, 2.794, 2.779, 2.772, 2.757, 2.742, 2.729, 2.716, 2.705, 2.704, 2.704, 2.707, 2.715, 2.727, 2.737, 2.754, 2.769, 2.788, 2.812, 2.845, 2.878, 2.923, 2.962, 2.973, 2.979, 2.994
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 3.018, 3.021, 3.026, 3.052, 3.092, 3.143, 3.181, 3.202, 3.209, 3.212, 3.211, 3.209, 3.197, 3.193, 3.185, 3.184, 3.185, 3.187, 3.191, 3.202, 3.211, 3.213, 3.212, 3.203, 3.189, 3.147, 3.099, 3.051, 3.032, 3.031, 3.048, 3.054,
+ 3.019, 3.023, 3.033, 3.066, 3.123, 3.163, 3.196, 3.206, 3.212, 3.212, 3.211, 3.203, 3.193, 3.179, 3.168, 3.159, 3.159, 3.163, 3.174, 3.188, 3.203, 3.208, 3.211, 3.209, 3.195, 3.168, 3.114, 3.064, 3.035, 3.033, 3.044, 3.051,
+ 3.021, 3.028, 3.046, 3.099, 3.156, 3.192, 3.209, 3.215, 3.216, 3.213, 3.203, 3.193, 3.176, 3.159, 3.153, 3.151, 3.149, 3.152, 3.159, 3.171, 3.188, 3.201, 3.209, 3.211, 3.207, 3.189, 3.142, 3.083, 3.042, 3.038, 3.043, 3.046,
+ 3.022, 3.037, 3.065, 3.124, 3.178, 3.206, 3.215, 3.221, 3.218, 3.217, 3.198, 3.179, 3.162, 3.149, 3.138, 3.133, 3.133, 3.136, 3.145, 3.156, 3.174, 3.192, 3.206, 3.215, 3.214, 3.202, 3.159, 3.105, 3.058, 3.042, 3.043, 3.049,
+ 3.024, 3.047, 3.084, 3.151, 3.195, 3.211, 3.219, 3.223, 3.218, 3.208, 3.182, 3.164, 3.149, 3.137, 3.127, 3.119, 3.119, 3.124, 3.134, 3.144, 3.157, 3.178, 3.194, 3.213, 3.215, 3.208, 3.166, 3.124, 3.074, 3.044, 3.044, 3.049,
+ 3.023, 3.058, 3.102, 3.161, 3.201, 3.217, 3.224, 3.223, 3.217, 3.195, 3.174, 3.156, 3.137, 3.125, 3.115, 3.109, 3.109, 3.115, 3.121, 3.131, 3.146, 3.159, 3.186, 3.208, 3.213, 3.211, 3.181, 3.138, 3.084, 3.047, 3.047, 3.049,
+ 3.031, 3.063, 3.126, 3.183, 3.212, 3.224, 3.225, 3.224, 3.216, 3.191, 3.167, 3.143, 3.129, 3.115, 3.105, 3.103, 3.103, 3.107, 3.114, 3.121, 3.131, 3.148, 3.169, 3.199, 3.211, 3.209, 3.186, 3.151, 3.089, 3.051, 3.049, 3.052,
+ 3.033, 3.083, 3.141, 3.201, 3.221, 3.226, 3.226, 3.224, 3.212, 3.187, 3.159, 3.138, 3.119, 3.107, 3.101, 3.098, 3.098, 3.102, 3.107, 3.115, 3.124, 3.138, 3.161, 3.185, 3.207, 3.209, 3.197, 3.162, 3.112, 3.059, 3.056, 3.057,
+ 3.038, 3.092, 3.159, 3.212, 3.225, 3.231, 3.228, 3.224, 3.209, 3.181, 3.152, 3.129, 3.112, 3.103, 3.095, 3.092, 3.093, 3.095, 3.101, 3.108, 3.118, 3.133, 3.152, 3.179, 3.203, 3.209, 3.205, 3.174, 3.124, 3.069, 3.059, 3.058,
+ 3.049, 3.105, 3.176, 3.223, 3.229, 3.231, 3.229, 3.223, 3.206, 3.171, 3.147, 3.125, 3.109, 3.097, 3.091, 3.089, 3.088, 3.091, 3.094, 3.102, 3.111, 3.124, 3.143, 3.169, 3.196, 3.208, 3.207, 3.181, 3.132, 3.079, 3.064, 3.063,
+ 3.055, 3.123, 3.189, 3.226, 3.232, 3.232, 3.229, 3.225, 3.204, 3.169, 3.143, 3.122, 3.108, 3.095, 3.092, 3.089, 3.088, 3.088, 3.092, 3.095, 3.105, 3.117, 3.135, 3.159, 3.191, 3.208, 3.208, 3.189, 3.141, 3.084, 3.064, 3.062,
+ 3.057, 3.127, 3.198, 3.228, 3.233, 3.233, 3.229, 3.225, 3.201, 3.166, 3.139, 3.119, 3.106, 3.096, 3.093, 3.092, 3.088, 3.088, 3.089, 3.093, 3.099, 3.114, 3.129, 3.156, 3.186, 3.208, 3.208, 3.195, 3.143, 3.089, 3.065, 3.064,
+ 3.066, 3.142, 3.209, 3.232, 3.234, 3.233, 3.231, 3.226, 3.198, 3.166, 3.138, 3.117, 3.103, 3.097, 3.095, 3.095, 3.094, 3.089, 3.089, 3.092, 3.097, 3.109, 3.126, 3.155, 3.183, 3.207, 3.207, 3.198, 3.147, 3.091, 3.069, 3.065,
+ 3.072, 3.153, 3.216, 3.231, 3.234, 3.234, 3.229, 3.226, 3.194, 3.165, 3.136, 3.114, 3.101, 3.098, 3.098, 3.104, 3.098, 3.091, 3.088, 3.089, 3.093, 3.103, 3.123, 3.151, 3.181, 3.204, 3.204, 3.197, 3.156, 3.095, 3.069, 3.068,
+ 3.079, 3.159, 3.222, 3.233, 3.236, 3.235, 3.231, 3.226, 3.194, 3.165, 3.133, 3.112, 3.102, 3.099, 3.107, 3.114, 3.111, 3.097, 3.089, 3.089, 3.091, 3.099, 3.121, 3.149, 3.182, 3.202, 3.202, 3.195, 3.156, 3.096, 3.069, 3.068,
+ 3.081, 3.164, 3.226, 3.233, 3.236, 3.235, 3.233, 3.229, 3.199, 3.165, 3.137, 3.113, 3.102, 3.102, 3.111, 3.134, 3.134, 3.103, 3.091, 3.089, 3.092, 3.101, 3.119, 3.147, 3.182, 3.202, 3.202, 3.194, 3.155, 3.095, 3.069, 3.067,
+ 3.085, 3.163, 3.225, 3.236, 3.239, 3.235, 3.234, 3.231, 3.203, 3.169, 3.141, 3.115, 3.103, 3.103, 3.111, 3.134, 3.134, 3.106, 3.092, 3.091, 3.093, 3.103, 3.119, 3.149, 3.185, 3.203, 3.203, 3.193, 3.152, 3.095, 3.068, 3.066,
+ 3.083, 3.168, 3.226, 3.236, 3.241, 3.235, 3.235, 3.231, 3.205, 3.174, 3.144, 3.117, 3.107, 3.103, 3.107, 3.116, 3.109, 3.103, 3.091, 3.091, 3.095, 3.107, 3.123, 3.152, 3.188, 3.204, 3.204, 3.193, 3.151, 3.095, 3.069, 3.066,
+ 3.082, 3.171, 3.228, 3.237, 3.239, 3.235, 3.234, 3.233, 3.217, 3.184, 3.147, 3.119, 3.108, 3.104, 3.103, 3.105, 3.102, 3.095, 3.091, 3.091, 3.097, 3.111, 3.128, 3.157, 3.191, 3.204, 3.204, 3.185, 3.149, 3.094, 3.069, 3.065,
+ 3.086, 3.173, 3.226, 3.237, 3.239, 3.235, 3.234, 3.232, 3.221, 3.185, 3.155, 3.124, 3.112, 3.105, 3.102, 3.099, 3.096, 3.094, 3.092, 3.094, 3.102, 3.114, 3.133, 3.163, 3.197, 3.205, 3.204, 3.183, 3.144, 3.089, 3.068, 3.065,
+ 3.086, 3.166, 3.225, 3.239, 3.239, 3.237, 3.233, 3.231, 3.223, 3.193, 3.165, 3.135, 3.118, 3.108, 3.101, 3.098, 3.095, 3.093, 3.093, 3.099, 3.109, 3.124, 3.145, 3.174, 3.199, 3.204, 3.203, 3.181, 3.132, 3.085, 3.067, 3.062,
+ 3.086, 3.162, 3.224, 3.239, 3.241, 3.236, 3.232, 3.229, 3.224, 3.201, 3.174, 3.147, 3.128, 3.114, 3.103, 3.099, 3.096, 3.095, 3.097, 3.106, 3.116, 3.134, 3.151, 3.182, 3.201, 3.203, 3.201, 3.176, 3.125, 3.078, 3.065, 3.061,
+ 3.077, 3.162, 3.221, 3.239, 3.241, 3.234, 3.229, 3.227, 3.225, 3.207, 3.186, 3.161, 3.137, 3.122, 3.112, 3.102, 3.099, 3.098, 3.106, 3.113, 3.127, 3.139, 3.159, 3.192, 3.204, 3.205, 3.198, 3.167, 3.119, 3.073, 3.062, 3.061,
+ 3.077, 3.161, 3.216, 3.234, 3.236, 3.232, 3.225, 3.225, 3.222, 3.209, 3.194, 3.172, 3.148, 3.132, 3.121, 3.113, 3.107, 3.107, 3.112, 3.124, 3.135, 3.151, 3.175, 3.196, 3.201, 3.201, 3.191, 3.161, 3.114, 3.062, 3.058, 3.057,
+ 3.073, 3.139, 3.201, 3.227, 3.232, 3.227, 3.223, 3.219, 3.216, 3.212, 3.203, 3.181, 3.161, 3.142, 3.129, 3.121, 3.114, 3.114, 3.124, 3.134, 3.145, 3.161, 3.179, 3.196, 3.199, 3.195, 3.182, 3.145, 3.093, 3.052, 3.051, 3.052,
+ 3.066, 3.126, 3.192, 3.218, 3.224, 3.221, 3.218, 3.214, 3.214, 3.209, 3.204, 3.191, 3.174, 3.155, 3.142, 3.129, 3.127, 3.127, 3.136, 3.145, 3.157, 3.175, 3.187, 3.194, 3.196, 3.192, 3.171, 3.134, 3.082, 3.043, 3.042, 3.044,
+ 3.056, 3.114, 3.176, 3.212, 3.219, 3.219, 3.214, 3.209, 3.208, 3.206, 3.203, 3.198, 3.182, 3.171, 3.155, 3.146, 3.144, 3.144, 3.148, 3.156, 3.171, 3.181, 3.188, 3.194, 3.194, 3.187, 3.161, 3.117, 3.066, 3.037, 3.037, 3.044,
+ 3.054, 3.101, 3.162, 3.203, 3.216, 3.215, 3.211, 3.206, 3.203, 3.201, 3.199, 3.197, 3.191, 3.179, 3.171, 3.161, 3.156, 3.156, 3.161, 3.171, 3.179, 3.184, 3.189, 3.192, 3.191, 3.181, 3.142, 3.097, 3.045, 3.032, 3.033, 3.039,
+ 3.041, 3.093, 3.149, 3.194, 3.208, 3.211, 3.208, 3.202, 3.197, 3.197, 3.197, 3.195, 3.191, 3.189, 3.181, 3.176, 3.172, 3.173, 3.178, 3.181, 3.185, 3.187, 3.189, 3.191, 3.189, 3.173, 3.133, 3.085, 3.034, 3.029, 3.031, 3.038,
+ 3.032, 3.079, 3.133, 3.181, 3.197, 3.207, 3.204, 3.198, 3.193, 3.192, 3.189, 3.191, 3.189, 3.187, 3.185, 3.183, 3.183, 3.183, 3.185, 3.188, 3.187, 3.188, 3.189, 3.188, 3.184, 3.164, 3.118, 3.075, 3.031, 3.026, 3.028, 3.039,
+ 3.025, 3.051, 3.099, 3.149, 3.182, 3.193, 3.193, 3.187, 3.181, 3.178, 3.177, 3.177, 3.182, 3.183, 3.183, 3.183, 3.183, 3.184, 3.187, 3.188, 3.186, 3.184, 3.184, 3.181, 3.167, 3.139, 3.098, 3.053, 3.026, 3.024, 3.029, 3.043,
+ 3.016, 3.025, 3.081, 3.122, 3.167, 3.182, 3.185, 3.181, 3.176, 3.171, 3.169, 3.171, 3.174, 3.175, 3.178, 3.178, 3.179, 3.181, 3.185, 3.185, 3.181, 3.179, 3.177, 3.173, 3.151, 3.119, 3.076, 3.031, 3.021, 3.018, 3.024, 3.046
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.503, 1.503, 1.504, 1.515, 1.541, 1.566, 1.587, 1.599, 1.602, 1.603, 1.602, 1.599, 1.595, 1.589, 1.587, 1.586, 1.586, 1.587, 1.589, 1.594, 1.601, 1.604, 1.604, 1.601, 1.589, 1.571, 1.541, 1.517, 1.512, 1.512, 1.522, 1.526,
+ 1.501, 1.502, 1.506, 1.523, 1.557, 1.579, 1.596, 1.603, 1.603, 1.603, 1.601, 1.597, 1.591, 1.582, 1.576, 1.575, 1.574, 1.577, 1.581, 1.588, 1.595, 1.601, 1.603, 1.602, 1.597, 1.578, 1.553, 1.526, 1.512, 1.512, 1.519, 1.526,
+ 1.499, 1.503, 1.512, 1.539, 1.571, 1.593, 1.603, 1.604, 1.604, 1.602, 1.597, 1.591, 1.581, 1.573, 1.568, 1.566, 1.566, 1.568, 1.572, 1.579, 1.587, 1.594, 1.602, 1.603, 1.601, 1.589, 1.566, 1.536, 1.517, 1.516, 1.519, 1.525,
+ 1.499, 1.505, 1.521, 1.553, 1.582, 1.597, 1.604, 1.604, 1.604, 1.601, 1.592, 1.582, 1.573, 1.564, 1.561, 1.558, 1.557, 1.559, 1.564, 1.571, 1.579, 1.588, 1.597, 1.603, 1.603, 1.596, 1.576, 1.545, 1.519, 1.517, 1.518, 1.526,
+ 1.499, 1.509, 1.529, 1.565, 1.591, 1.601, 1.605, 1.604, 1.602, 1.597, 1.586, 1.573, 1.565, 1.558, 1.553, 1.551, 1.551, 1.552, 1.555, 1.563, 1.571, 1.581, 1.592, 1.601, 1.602, 1.599, 1.582, 1.556, 1.528, 1.517, 1.517, 1.526,
+ 1.501, 1.512, 1.539, 1.576, 1.595, 1.603, 1.605, 1.604, 1.601, 1.591, 1.579, 1.567, 1.559, 1.552, 1.548, 1.545, 1.546, 1.548, 1.551, 1.555, 1.563, 1.574, 1.585, 1.598, 1.602, 1.601, 1.589, 1.562, 1.535, 1.519, 1.519, 1.528,
+ 1.501, 1.517, 1.552, 1.587, 1.601, 1.605, 1.605, 1.605, 1.599, 1.588, 1.574, 1.562, 1.553, 1.548, 1.544, 1.543, 1.543, 1.545, 1.547, 1.551, 1.557, 1.567, 1.578, 1.593, 1.601, 1.601, 1.592, 1.571, 1.539, 1.521, 1.521, 1.529,
+ 1.503, 1.524, 1.561, 1.593, 1.605, 1.606, 1.605, 1.603, 1.598, 1.585, 1.569, 1.558, 1.551, 1.545, 1.542, 1.541, 1.541, 1.542, 1.545, 1.547, 1.555, 1.561, 1.573, 1.587, 1.598, 1.601, 1.596, 1.577, 1.546, 1.523, 1.523, 1.529,
+ 1.503, 1.532, 1.568, 1.597, 1.605, 1.606, 1.605, 1.603, 1.596, 1.581, 1.565, 1.555, 1.548, 1.544, 1.541, 1.539, 1.541, 1.541, 1.543, 1.546, 1.549, 1.558, 1.568, 1.583, 1.595, 1.601, 1.599, 1.582, 1.555, 1.525, 1.525, 1.531,
+ 1.508, 1.539, 1.575, 1.601, 1.605, 1.606, 1.605, 1.602, 1.593, 1.577, 1.563, 1.552, 1.546, 1.543, 1.541, 1.539, 1.539, 1.541, 1.542, 1.544, 1.548, 1.553, 1.564, 1.579, 1.592, 1.599, 1.599, 1.585, 1.559, 1.532, 1.531, 1.531,
+ 1.511, 1.544, 1.581, 1.603, 1.606, 1.606, 1.604, 1.603, 1.591, 1.574, 1.561, 1.549, 1.545, 1.542, 1.541, 1.541, 1.541, 1.541, 1.542, 1.543, 1.545, 1.551, 1.561, 1.573, 1.591, 1.599, 1.599, 1.588, 1.563, 1.535, 1.531, 1.531,
+ 1.515, 1.548, 1.589, 1.605, 1.607, 1.607, 1.604, 1.602, 1.591, 1.573, 1.559, 1.549, 1.543, 1.542, 1.541, 1.542, 1.542, 1.542, 1.541, 1.542, 1.543, 1.549, 1.558, 1.571, 1.588, 1.599, 1.599, 1.591, 1.566, 1.537, 1.532, 1.531,
+ 1.517, 1.558, 1.593, 1.606, 1.607, 1.607, 1.605, 1.602, 1.589, 1.572, 1.557, 1.548, 1.543, 1.543, 1.542, 1.544, 1.543, 1.543, 1.541, 1.541, 1.542, 1.546, 1.554, 1.569, 1.585, 1.599, 1.599, 1.593, 1.568, 1.538, 1.533, 1.531,
+ 1.521, 1.563, 1.596, 1.607, 1.608, 1.607, 1.606, 1.603, 1.589, 1.572, 1.557, 1.548, 1.543, 1.543, 1.544, 1.549, 1.546, 1.544, 1.541, 1.541, 1.542, 1.545, 1.553, 1.568, 1.585, 1.598, 1.598, 1.594, 1.571, 1.541, 1.534, 1.531,
+ 1.521, 1.566, 1.599, 1.607, 1.608, 1.607, 1.605, 1.603, 1.591, 1.571, 1.556, 1.547, 1.544, 1.544, 1.551, 1.554, 1.552, 1.546, 1.541, 1.541, 1.541, 1.544, 1.553, 1.567, 1.585, 1.597, 1.598, 1.595, 1.571, 1.541, 1.534, 1.531,
+ 1.523, 1.568, 1.601, 1.607, 1.608, 1.607, 1.606, 1.604, 1.591, 1.572, 1.557, 1.547, 1.545, 1.545, 1.552, 1.566, 1.566, 1.551, 1.542, 1.541, 1.541, 1.544, 1.553, 1.567, 1.586, 1.596, 1.596, 1.593, 1.571, 1.541, 1.533, 1.531,
+ 1.524, 1.569, 1.602, 1.607, 1.608, 1.607, 1.606, 1.604, 1.591, 1.573, 1.559, 1.548, 1.545, 1.546, 1.552, 1.565, 1.565, 1.551, 1.542, 1.541, 1.541, 1.545, 1.553, 1.568, 1.586, 1.597, 1.597, 1.593, 1.571, 1.541, 1.532, 1.532,
+ 1.526, 1.571, 1.602, 1.607, 1.608, 1.606, 1.605, 1.604, 1.593, 1.575, 1.559, 1.549, 1.546, 1.546, 1.549, 1.552, 1.552, 1.546, 1.542, 1.541, 1.542, 1.546, 1.555, 1.569, 1.587, 1.597, 1.597, 1.591, 1.569, 1.539, 1.532, 1.531,
+ 1.526, 1.571, 1.601, 1.608, 1.609, 1.605, 1.605, 1.603, 1.597, 1.579, 1.562, 1.551, 1.546, 1.545, 1.545, 1.549, 1.546, 1.543, 1.542, 1.541, 1.542, 1.547, 1.557, 1.573, 1.588, 1.597, 1.597, 1.589, 1.566, 1.537, 1.531, 1.529,
+ 1.526, 1.569, 1.602, 1.609, 1.609, 1.606, 1.605, 1.604, 1.598, 1.582, 1.567, 1.553, 1.547, 1.545, 1.544, 1.544, 1.544, 1.542, 1.542, 1.542, 1.544, 1.552, 1.559, 1.576, 1.591, 1.597, 1.597, 1.588, 1.563, 1.535, 1.531, 1.529,
+ 1.523, 1.567, 1.601, 1.609, 1.609, 1.606, 1.605, 1.603, 1.599, 1.587, 1.571, 1.558, 1.549, 1.545, 1.544, 1.543, 1.543, 1.542, 1.542, 1.544, 1.548, 1.555, 1.566, 1.581, 1.593, 1.597, 1.597, 1.586, 1.558, 1.534, 1.529, 1.529,
+ 1.523, 1.564, 1.599, 1.609, 1.609, 1.605, 1.604, 1.603, 1.601, 1.592, 1.576, 1.564, 1.553, 1.547, 1.544, 1.543, 1.542, 1.542, 1.544, 1.548, 1.551, 1.561, 1.572, 1.585, 1.594, 1.596, 1.595, 1.581, 1.555, 1.528, 1.527, 1.528,
+ 1.522, 1.561, 1.595, 1.608, 1.608, 1.604, 1.602, 1.601, 1.601, 1.595, 1.582, 1.569, 1.559, 1.552, 1.547, 1.545, 1.543, 1.544, 1.546, 1.551, 1.556, 1.563, 1.576, 1.589, 1.595, 1.596, 1.593, 1.576, 1.551, 1.524, 1.524, 1.528,
+ 1.519, 1.559, 1.591, 1.605, 1.606, 1.603, 1.601, 1.599, 1.601, 1.597, 1.587, 1.576, 1.565, 1.558, 1.552, 1.549, 1.546, 1.547, 1.552, 1.556, 1.561, 1.571, 1.582, 1.593, 1.596, 1.596, 1.591, 1.569, 1.546, 1.521, 1.521, 1.527,
+ 1.516, 1.553, 1.589, 1.602, 1.604, 1.602, 1.599, 1.598, 1.599, 1.598, 1.594, 1.583, 1.572, 1.564, 1.559, 1.553, 1.552, 1.553, 1.556, 1.561, 1.567, 1.578, 1.588, 1.594, 1.596, 1.594, 1.588, 1.567, 1.539, 1.517, 1.517, 1.525,
+ 1.511, 1.548, 1.581, 1.599, 1.602, 1.602, 1.598, 1.597, 1.597, 1.597, 1.595, 1.589, 1.581, 1.571, 1.564, 1.559, 1.559, 1.558, 1.561, 1.567, 1.575, 1.583, 1.591, 1.593, 1.594, 1.591, 1.581, 1.557, 1.529, 1.514, 1.514, 1.521,
+ 1.508, 1.541, 1.576, 1.596, 1.601, 1.601, 1.597, 1.595, 1.594, 1.595, 1.595, 1.592, 1.585, 1.579, 1.571, 1.566, 1.566, 1.566, 1.568, 1.575, 1.582, 1.589, 1.592, 1.593, 1.593, 1.589, 1.575, 1.553, 1.523, 1.511, 1.511, 1.517,
+ 1.505, 1.535, 1.566, 1.591, 1.599, 1.598, 1.596, 1.594, 1.592, 1.592, 1.593, 1.592, 1.589, 1.585, 1.579, 1.575, 1.574, 1.574, 1.577, 1.582, 1.587, 1.591, 1.592, 1.593, 1.592, 1.585, 1.568, 1.541, 1.516, 1.509, 1.509, 1.517,
+ 1.501, 1.528, 1.559, 1.585, 1.595, 1.597, 1.595, 1.593, 1.589, 1.588, 1.591, 1.591, 1.591, 1.589, 1.586, 1.583, 1.582, 1.582, 1.585, 1.588, 1.589, 1.591, 1.592, 1.593, 1.592, 1.582, 1.561, 1.536, 1.512, 1.509, 1.511, 1.517,
+ 1.496, 1.521, 1.549, 1.576, 1.588, 1.594, 1.593, 1.589, 1.586, 1.585, 1.586, 1.588, 1.589, 1.588, 1.588, 1.587, 1.587, 1.587, 1.589, 1.589, 1.591, 1.591, 1.592, 1.592, 1.591, 1.575, 1.555, 1.527, 1.508, 1.507, 1.511, 1.519,
+ 1.495, 1.505, 1.536, 1.563, 1.581, 1.587, 1.588, 1.584, 1.582, 1.578, 1.578, 1.581, 1.583, 1.584, 1.586, 1.587, 1.587, 1.587, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.584, 1.566, 1.544, 1.518, 1.505, 1.505, 1.509, 1.519,
+ 1.493, 1.496, 1.522, 1.547, 1.569, 1.581, 1.582, 1.581, 1.577, 1.575, 1.573, 1.575, 1.579, 1.581, 1.583, 1.584, 1.584, 1.585, 1.587, 1.587, 1.588, 1.588, 1.588, 1.585, 1.573, 1.556, 1.532, 1.511, 1.504, 1.504, 1.508, 1.523
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 4.461, 4.088, 3.793, 3.651, 3.557, 3.439, 3.248, 2.999, 2.751, 2.527, 2.341, 2.191, 2.069, 1.956, 1.907, 1.907, 1.907, 1.908, 1.946, 2.056, 2.179, 2.328, 2.517, 2.747, 2.998, 3.219, 3.359, 3.436, 3.494, 3.621, 3.906, 4.251,
+ 4.297, 3.982, 3.747, 3.634, 3.531, 3.373, 3.136, 2.863, 2.608, 2.386, 2.209, 2.075, 1.957, 1.873, 1.817, 1.789, 1.789, 1.813, 1.865, 1.947, 2.066, 2.198, 2.378, 2.605, 2.872, 3.132, 3.322, 3.431, 3.485, 3.577, 3.802, 4.079,
+ 4.152, 3.905, 3.717, 3.623, 3.499, 3.296, 3.022, 2.735, 2.478, 2.265, 2.094, 1.957, 1.849, 1.763, 1.709, 1.679, 1.679, 1.703, 1.753, 1.837, 1.947, 2.081, 2.253, 2.472, 2.742, 3.032, 3.271, 3.414, 3.479, 3.545, 3.719, 3.937,
+ 4.039, 3.835, 3.688, 3.596, 3.442, 3.196, 2.899, 2.609, 2.356, 2.153, 1.987, 1.849, 1.748, 1.659, 1.605, 1.577, 1.577, 1.599, 1.649, 1.734, 1.837, 1.973, 2.139, 2.348, 2.612, 2.911, 3.192, 3.379, 3.467, 3.516, 3.649, 3.815,
+ 3.952, 3.784, 3.669, 3.562, 3.369, 3.088, 2.778, 2.491, 2.246, 2.049, 1.888, 1.748, 1.657, 1.561, 1.509, 1.481, 1.481, 1.504, 1.552, 1.642, 1.734, 1.869, 2.033, 2.233, 2.489, 2.792, 3.105, 3.331, 3.445, 3.493, 3.591, 3.721,
+ 3.883, 3.741, 3.648, 3.519, 3.287, 2.977, 2.665, 2.382, 2.148, 1.957, 1.796, 1.659, 1.561, 1.474, 1.422, 1.396, 1.396, 1.415, 1.465, 1.552, 1.643, 1.776, 1.936, 2.131, 2.375, 2.678, 3.004, 3.275, 3.416, 3.469, 3.541, 3.643,
+ 3.829, 3.716, 3.617, 3.466, 3.202, 2.876, 2.558, 2.282, 2.059, 1.872, 1.713, 1.577, 1.474, 1.399, 1.345, 1.319, 1.319, 1.338, 1.389, 1.465, 1.559, 1.689, 1.849, 2.042, 2.275, 2.568, 2.903, 3.204, 3.383, 3.446, 3.496, 3.579,
+ 3.793, 3.685, 3.589, 3.411, 3.119, 2.781, 2.466, 2.199, 1.983, 1.798, 1.639, 1.505, 1.399, 1.339, 1.276, 1.253, 1.253, 1.271, 1.327, 1.389, 1.487, 1.612, 1.769, 1.961, 2.189, 2.471, 2.806, 3.133, 3.342, 3.425, 3.459, 3.527,
+ 3.763, 3.666, 3.561, 3.357, 3.042, 2.698, 2.384, 2.129, 1.918, 1.734, 1.575, 1.443, 1.339, 1.276, 1.217, 1.194, 1.194, 1.214, 1.271, 1.327, 1.423, 1.546, 1.702, 1.891, 2.112, 2.386, 2.718, 3.061, 3.301, 3.402, 3.433, 3.486,
+ 3.745, 3.647, 3.529, 3.302, 2.971, 2.627, 2.318, 2.067, 1.859, 1.677, 1.521, 1.389, 1.287, 1.217, 1.171, 1.145, 1.145, 1.165, 1.214, 1.276, 1.369, 1.491, 1.643, 1.831, 2.048, 2.313, 2.644, 2.995, 3.262, 3.381, 3.412, 3.453,
+ 3.731, 3.635, 3.503, 3.249, 2.911, 2.566, 2.259, 2.017, 1.811, 1.629, 1.475, 1.347, 1.246, 1.171, 1.138, 1.103, 1.103, 1.129, 1.165, 1.231, 1.322, 1.443, 1.595, 1.779, 1.993, 2.251, 2.576, 2.936, 3.223, 3.359, 3.392, 3.425,
+ 3.721, 3.625, 3.481, 3.208, 2.861, 2.515, 2.213, 1.976, 1.773, 1.593, 1.439, 1.313, 1.213, 1.138, 1.103, 1.071, 1.071, 1.101, 1.129, 1.194, 1.286, 1.405, 1.555, 1.736, 1.949, 2.202, 2.521, 2.886, 3.189, 3.338, 3.375, 3.406,
+ 3.716, 3.616, 3.458, 3.171, 2.819, 2.472, 2.176, 1.942, 1.741, 1.563, 1.411, 1.285, 1.186, 1.112, 1.071, 1.051, 1.049, 1.069, 1.103, 1.165, 1.256, 1.376, 1.523, 1.702, 1.913, 2.163, 2.477, 2.843, 3.155, 3.318, 3.358, 3.389,
+ 3.712, 3.609, 3.439, 3.142, 2.787, 2.443, 2.147, 1.918, 1.721, 1.541, 1.391, 1.266, 1.167, 1.094, 1.051, 1.035, 1.035, 1.049, 1.085, 1.145, 1.236, 1.355, 1.499, 1.676, 1.886, 2.136, 2.449, 2.814, 3.135, 3.307, 3.351, 3.378,
+ 3.709, 3.604, 3.422, 3.123, 2.768, 2.419, 2.129, 1.903, 1.706, 1.527, 1.377, 1.253, 1.155, 1.083, 1.035, 1.023, 1.023, 1.035, 1.074, 1.134, 1.224, 1.341, 1.484, 1.661, 1.868, 2.117, 2.429, 2.797, 3.122, 3.301, 3.346, 3.374,
+ 3.711, 3.597, 3.412, 3.114, 2.758, 2.409, 2.119, 1.895, 1.701, 1.523, 1.373, 1.251, 1.153, 1.081, 1.033, 1.001, 1.001, 1.032, 1.073, 1.133, 1.222, 1.338, 1.479, 1.655, 1.861, 2.107, 2.418, 2.787, 3.115, 3.297, 3.343, 3.373,
+ 3.713, 3.597, 3.412, 3.113, 2.758, 2.409, 2.119, 1.893, 1.698, 1.523, 1.373, 1.251, 1.153, 1.081, 1.034, 1.011, 1.011, 1.032, 1.074, 1.134, 1.222, 1.338, 1.479, 1.655, 1.861, 2.107, 2.418, 2.787, 3.116, 3.294, 3.341, 3.371,
+ 3.721, 3.599, 3.414, 3.116, 2.763, 2.418, 2.124, 1.895, 1.704, 1.531, 1.382, 1.259, 1.162, 1.091, 1.048, 1.034, 1.032, 1.046, 1.083, 1.145, 1.232, 1.348, 1.491, 1.664, 1.869, 2.115, 2.428, 2.798, 3.123, 3.294, 3.339, 3.372,
+ 3.727, 3.604, 3.421, 3.132, 2.784, 2.438, 2.141, 1.908, 1.716, 1.547, 1.399, 1.276, 1.178, 1.107, 1.069, 1.048, 1.046, 1.067, 1.101, 1.162, 1.249, 1.366, 1.509, 1.684, 1.886, 2.134, 2.449, 2.821, 3.135, 3.299, 3.341, 3.375,
+ 3.739, 3.613, 3.431, 3.154, 2.813, 2.468, 2.166, 1.931, 1.739, 1.569, 1.424, 1.302, 1.203, 1.129, 1.098, 1.069, 1.069, 1.096, 1.123, 1.185, 1.274, 1.391, 1.536, 1.709, 1.914, 2.162, 2.481, 2.851, 3.156, 3.311, 3.342, 3.378,
+ 3.751, 3.626, 3.449, 3.186, 2.855, 2.509, 2.201, 1.961, 1.768, 1.601, 1.454, 1.333, 1.235, 1.159, 1.129, 1.098, 1.098, 1.123, 1.152, 1.216, 1.307, 1.424, 1.569, 1.744, 1.947, 2.202, 2.526, 2.891, 3.182, 3.322, 3.351, 3.387,
+ 3.772, 3.641, 3.473, 3.221, 2.902, 2.559, 2.248, 1.999, 1.804, 1.639, 1.496, 1.373, 1.274, 1.201, 1.159, 1.133, 1.133, 1.152, 1.191, 1.254, 1.347, 1.466, 1.611, 1.785, 1.989, 2.253, 2.582, 2.939, 3.209, 3.334, 3.361, 3.402,
+ 3.797, 3.663, 3.496, 3.263, 2.959, 2.624, 2.308, 2.049, 1.847, 1.684, 1.542, 1.422, 1.321, 1.252, 1.201, 1.175, 1.175, 1.191, 1.239, 1.298, 1.394, 1.516, 1.658, 1.831, 2.041, 2.313, 2.651, 2.998, 3.244, 3.351, 3.375, 3.422,
+ 3.831, 3.686, 3.523, 3.307, 3.023, 2.698, 2.379, 2.112, 1.902, 1.737, 1.596, 1.476, 1.378, 1.315, 1.252, 1.227, 1.227, 1.239, 1.296, 1.355, 1.451, 1.572, 1.715, 1.888, 2.103, 2.386, 2.731, 3.063, 3.279, 3.367, 3.393, 3.456,
+ 3.871, 3.714, 3.551, 3.355, 3.091, 2.781, 2.465, 2.186, 1.965, 1.795, 1.654, 1.538, 1.442, 1.378, 1.318, 1.291, 1.291, 1.304, 1.355, 1.424, 1.515, 1.634, 1.778, 1.952, 2.178, 2.479, 2.821, 3.129, 3.314, 3.381, 3.419, 3.491,
+ 3.925, 3.749, 3.582, 3.401, 3.156, 2.866, 2.559, 2.274, 2.039, 1.859, 1.718, 1.604, 1.513, 1.442, 1.389, 1.363, 1.363, 1.379, 1.424, 1.501, 1.586, 1.702, 1.847, 2.028, 2.269, 2.579, 2.913, 3.193, 3.343, 3.396, 3.447, 3.539,
+ 3.994, 3.794, 3.619, 3.442, 3.231, 2.961, 2.662, 2.375, 2.129, 1.938, 1.789, 1.675, 1.591, 1.513, 1.465, 1.439, 1.439, 1.457, 1.501, 1.582, 1.661, 1.777, 1.925, 2.118, 2.375, 2.691, 3.008, 3.251, 3.371, 3.414, 3.479, 3.598,
+ 4.082, 3.845, 3.656, 3.489, 3.298, 3.053, 2.771, 2.485, 2.232, 2.028, 1.871, 1.751, 1.672, 1.591, 1.544, 1.521, 1.521, 1.539, 1.582, 1.661, 1.741, 1.859, 2.014, 2.224, 2.495, 2.806, 3.098, 3.301, 3.392, 3.431, 3.518, 3.677,
+ 4.196, 3.911, 3.698, 3.534, 3.363, 3.146, 2.881, 2.604, 2.348, 2.132, 1.964, 1.836, 1.751, 1.672, 1.628, 1.606, 1.606, 1.624, 1.665, 1.741, 1.827, 1.951, 2.121, 2.344, 2.624, 2.923, 3.177, 3.336, 3.405, 3.447, 3.567, 3.776,
+ 4.341, 4.002, 3.744, 3.575, 3.415, 3.229, 2.989, 2.729, 2.475, 2.251, 2.071, 1.936, 1.836, 1.759, 1.713, 1.693, 1.693, 1.711, 1.753, 1.827, 1.925, 2.058, 2.243, 2.481, 2.758, 3.027, 3.238, 3.361, 3.409, 3.466, 3.637, 3.896,
+ 4.516, 4.123, 3.804, 3.621, 3.468, 3.308, 3.096, 2.855, 2.609, 2.385, 2.194, 2.045, 1.936, 1.857, 1.807, 1.784, 1.784, 1.803, 1.852, 1.925, 2.033, 2.183, 2.382, 2.623, 2.886, 3.121, 3.284, 3.372, 3.413, 3.494, 3.727, 4.048,
+ 4.716, 4.264, 3.875, 3.674, 3.523, 3.376, 3.189, 2.966, 2.733, 2.511, 2.315, 2.158, 2.039, 1.936, 1.875, 1.872, 1.872, 1.872, 1.925, 2.028, 2.148, 2.308, 2.513, 2.751, 2.994, 3.191, 3.319, 3.384, 3.427, 3.541, 3.838, 4.221
+ ],
+ "sigma": 0.00152,
+ "sigma_Cb": 0.00172
+ }
+ },
+ {
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "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 ]
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "power_min": 0.7,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "power_min": 0.7,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/meson.build b/src/ipa/rpi/pisp/data/meson.build
new file mode 100644
index 00000000..6b8b4e94
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/meson.build
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: CC0-1.0
+
+conf_files = files([
+ 'imx219.json',
+ 'imx219_noir.json',
+ 'imx283.json',
+ 'imx290.json',
+ 'imx296.json',
+ 'imx296_mono.json',
+ 'imx378.json',
+ 'imx415.json',
+ 'imx462.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',
+ 'ov64a40.json',
+ 'ov9281_mono.json',
+ 'se327m12.json',
+ 'uncalibrated.json',
+])
+
+install_data(conf_files,
+ install_dir : ipa_data_dir / 'rpi' / 'pisp')
diff --git a/src/ipa/rpi/pisp/data/ov5647.json b/src/ipa/rpi/pisp/data/ov5647.json
new file mode 100644
index 00000000..d5156767
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/ov5647.json
@@ -0,0 +1,1186 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 1024
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 29381,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 870,
+ "reference_Y": 12388
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 4.371
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 280,
+ "slope": 0.02153
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2873.0, 1.0463, 0.5142,
+ 2965.0, 1.0233, 0.5284,
+ 3606.0, 0.8947, 0.6314,
+ 4700.0, 0.7665, 0.7897,
+ 5890.0, 0.7055, 0.8933,
+ 7600.0, 0.6482, 1.0119
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.04072,
+ "transverse_neg": 0.03906
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.238, 1.238, 1.238, 1.234, 1.227, 1.216, 1.207, 1.198, 1.191, 1.179, 1.169, 1.162, 1.155, 1.153, 1.152, 1.152, 1.152, 1.153, 1.154, 1.157, 1.166, 1.176, 1.183, 1.191, 1.204, 1.216, 1.226, 1.232, 1.239, 1.241, 1.241, 1.242,
+ 1.235, 1.234, 1.227, 1.222, 1.214, 1.203, 1.193, 1.184, 1.169, 1.161, 1.149, 1.139, 1.131, 1.126, 1.122, 1.121, 1.121, 1.123, 1.129, 1.136, 1.145, 1.157, 1.163, 1.175, 1.189, 1.199, 1.212, 1.221, 1.225, 1.231, 1.241, 1.242,
+ 1.234, 1.227, 1.222, 1.214, 1.203, 1.193, 1.183, 1.169, 1.158, 1.145, 1.133, 1.123, 1.113, 1.106, 1.101, 1.101, 1.101, 1.105, 1.111, 1.116, 1.128, 1.137, 1.149, 1.163, 1.174, 1.189, 1.199, 1.212, 1.221, 1.226, 1.234, 1.241,
+ 1.234, 1.226, 1.217, 1.209, 1.195, 1.183, 1.171, 1.158, 1.145, 1.131, 1.119, 1.108, 1.097, 1.088, 1.088, 1.085, 1.085, 1.087, 1.095, 1.102, 1.114, 1.124, 1.137, 1.149, 1.165, 1.176, 1.194, 1.207, 1.214, 1.224, 1.235, 1.247,
+ 1.238, 1.224, 1.213, 1.202, 1.187, 1.175, 1.161, 1.146, 1.132, 1.117, 1.105, 1.094, 1.082, 1.074, 1.071, 1.071, 1.071, 1.073, 1.079, 1.089, 1.099, 1.112, 1.124, 1.137, 1.152, 1.167, 1.183, 1.198, 1.211, 1.222, 1.235, 1.249,
+ 1.232, 1.221, 1.209, 1.195, 1.178, 1.163, 1.149, 1.134, 1.118, 1.104, 1.093, 1.079, 1.069, 1.061, 1.057, 1.056, 1.056, 1.059, 1.066, 1.073, 1.086, 1.098, 1.111, 1.124, 1.141, 1.157, 1.173, 1.188, 1.203, 1.219, 1.234, 1.251,
+ 1.231, 1.213, 1.197, 1.186, 1.169, 1.151, 1.137, 1.121, 1.104, 1.093, 1.079, 1.068, 1.056, 1.048, 1.045, 1.042, 1.042, 1.045, 1.051, 1.061, 1.071, 1.085, 1.098, 1.111, 1.129, 1.145, 1.161, 1.179, 1.197, 1.215, 1.231, 1.249,
+ 1.224, 1.211, 1.194, 1.178, 1.161, 1.141, 1.127, 1.109, 1.094, 1.081, 1.068, 1.055, 1.047, 1.038, 1.034, 1.032, 1.032, 1.035, 1.039, 1.048, 1.059, 1.071, 1.086, 1.098, 1.116, 1.134, 1.154, 1.172, 1.191, 1.209, 1.228, 1.249,
+ 1.223, 1.206, 1.187, 1.171, 1.152, 1.132, 1.117, 1.098, 1.082, 1.069, 1.056, 1.045, 1.037, 1.028, 1.024, 1.022, 1.022, 1.025, 1.031, 1.039, 1.048, 1.059, 1.074, 1.091, 1.106, 1.126, 1.144, 1.163, 1.186, 1.205, 1.227, 1.247,
+ 1.222, 1.199, 1.183, 1.164, 1.143, 1.126, 1.108, 1.091, 1.075, 1.059, 1.045, 1.037, 1.028, 1.019, 1.015, 1.014, 1.014, 1.018, 1.023, 1.031, 1.042, 1.051, 1.065, 1.081, 1.098, 1.118, 1.137, 1.158, 1.181, 1.201, 1.224, 1.245,
+ 1.221, 1.198, 1.179, 1.163, 1.141, 1.119, 1.101, 1.083, 1.066, 1.051, 1.038, 1.028, 1.019, 1.012, 1.009, 1.008, 1.007, 1.008, 1.015, 1.023, 1.033, 1.044, 1.058, 1.072, 1.089, 1.107, 1.131, 1.152, 1.172, 1.196, 1.216, 1.241,
+ 1.216, 1.194, 1.174, 1.155, 1.133, 1.112, 1.094, 1.074, 1.059, 1.045, 1.032, 1.021, 1.012, 1.007, 1.003, 1.002, 1.002, 1.003, 1.008, 1.015, 1.025, 1.038, 1.049, 1.067, 1.084, 1.102, 1.126, 1.147, 1.169, 1.191, 1.214, 1.238,
+ 1.212, 1.188, 1.171, 1.149, 1.127, 1.105, 1.087, 1.069, 1.055, 1.039, 1.027, 1.016, 1.007, 1.003, 0.999, 0.997, 0.998, 1.001, 1.003, 1.011, 1.021, 1.032, 1.043, 1.059, 1.077, 1.101, 1.121, 1.142, 1.164, 1.187, 1.211, 1.236,
+ 1.208, 1.187, 1.169, 1.149, 1.124, 1.104, 1.085, 1.067, 1.051, 1.036, 1.024, 1.013, 1.005, 0.999, 0.996, 0.994, 0.994, 0.996, 1.001, 1.006, 1.017, 1.025, 1.038, 1.053, 1.072, 1.093, 1.116, 1.138, 1.159, 1.183, 1.207, 1.235,
+ 1.208, 1.181, 1.164, 1.144, 1.122, 1.098, 1.079, 1.062, 1.046, 1.033, 1.018, 1.009, 1.002, 0.996, 0.992, 0.989, 0.991, 0.994, 0.996, 1.002, 1.012, 1.021, 1.035, 1.051, 1.069, 1.091, 1.113, 1.137, 1.157, 1.182, 1.206, 1.233,
+ 1.206, 1.179, 1.163, 1.142, 1.119, 1.098, 1.079, 1.061, 1.045, 1.031, 1.017, 1.008, 1.001, 0.995, 0.991, 0.989, 0.989, 0.992, 0.996, 1.001, 1.011, 1.019, 1.034, 1.051, 1.069, 1.089, 1.112, 1.136, 1.157, 1.181, 1.205, 1.233,
+ 1.206, 1.179, 1.163, 1.139, 1.119, 1.098, 1.079, 1.061, 1.044, 1.031, 1.016, 1.007, 1.001, 0.995, 0.991, 0.989, 0.989, 0.991, 0.996, 1.002, 1.011, 1.019, 1.034, 1.049, 1.069, 1.088, 1.113, 1.136, 1.156, 1.179, 1.204, 1.233,
+ 1.207, 1.179, 1.163, 1.139, 1.119, 1.099, 1.079, 1.061, 1.044, 1.031, 1.017, 1.007, 1.001, 0.995, 0.991, 0.989, 0.989, 0.992, 0.997, 1.003, 1.011, 1.021, 1.034, 1.051, 1.071, 1.089, 1.112, 1.136, 1.157, 1.179, 1.204, 1.233,
+ 1.207, 1.179, 1.163, 1.143, 1.121, 1.101, 1.082, 1.063, 1.047, 1.032, 1.019, 1.009, 1.003, 0.998, 0.994, 0.991, 0.991, 0.995, 0.999, 1.004, 1.013, 1.024, 1.038, 1.052, 1.071, 1.091, 1.112, 1.136, 1.159, 1.181, 1.205, 1.233,
+ 1.207, 1.185, 1.166, 1.148, 1.124, 1.104, 1.087, 1.068, 1.052, 1.037, 1.025, 1.016, 1.006, 1.002, 0.998, 0.995, 0.995, 0.999, 1.003, 1.008, 1.017, 1.029, 1.043, 1.056, 1.076, 1.094, 1.116, 1.138, 1.159, 1.183, 1.205, 1.232,
+ 1.211, 1.186, 1.167, 1.151, 1.128, 1.108, 1.089, 1.072, 1.057, 1.042, 1.031, 1.021, 1.013, 1.006, 1.002, 0.999, 0.999, 1.003, 1.007, 1.013, 1.021, 1.031, 1.047, 1.062, 1.081, 1.098, 1.121, 1.141, 1.164, 1.185, 1.207, 1.232,
+ 1.211, 1.188, 1.169, 1.154, 1.134, 1.114, 1.094, 1.078, 1.063, 1.051, 1.039, 1.028, 1.019, 1.013, 1.007, 1.006, 1.006, 1.007, 1.013, 1.019, 1.027, 1.039, 1.051, 1.069, 1.087, 1.105, 1.124, 1.146, 1.165, 1.186, 1.209, 1.232,
+ 1.214, 1.191, 1.175, 1.159, 1.141, 1.123, 1.105, 1.087, 1.072, 1.058, 1.046, 1.036, 1.028, 1.019, 1.014, 1.013, 1.013, 1.015, 1.019, 1.027, 1.037, 1.048, 1.061, 1.076, 1.094, 1.109, 1.132, 1.149, 1.169, 1.189, 1.209, 1.233,
+ 1.219, 1.194, 1.179, 1.163, 1.146, 1.129, 1.113, 1.095, 1.081, 1.066, 1.055, 1.045, 1.036, 1.029, 1.023, 1.021, 1.021, 1.026, 1.031, 1.037, 1.048, 1.057, 1.069, 1.085, 1.101, 1.118, 1.137, 1.156, 1.174, 1.193, 1.213, 1.233,
+ 1.219, 1.199, 1.184, 1.172, 1.155, 1.138, 1.122, 1.104, 1.088, 1.075, 1.065, 1.055, 1.045, 1.038, 1.034, 1.031, 1.031, 1.035, 1.041, 1.048, 1.057, 1.066, 1.081, 1.096, 1.111, 1.125, 1.146, 1.164, 1.178, 1.196, 1.214, 1.233,
+ 1.222, 1.204, 1.189, 1.178, 1.162, 1.148, 1.132, 1.115, 1.101, 1.087, 1.075, 1.064, 1.055, 1.048, 1.043, 1.042, 1.042, 1.046, 1.049, 1.057, 1.066, 1.076, 1.089, 1.106, 1.121, 1.133, 1.149, 1.167, 1.183, 1.199, 1.215, 1.234,
+ 1.222, 1.205, 1.191, 1.184, 1.171, 1.155, 1.142, 1.124, 1.109, 1.097, 1.087, 1.077, 1.065, 1.059, 1.055, 1.053, 1.053, 1.057, 1.059, 1.067, 1.076, 1.088, 1.102, 1.116, 1.131, 1.143, 1.157, 1.175, 1.187, 1.202, 1.215, 1.231,
+ 1.223, 1.211, 1.198, 1.189, 1.178, 1.165, 1.151, 1.136, 1.122, 1.108, 1.097, 1.087, 1.079, 1.073, 1.067, 1.066, 1.066, 1.069, 1.074, 1.079, 1.088, 1.101, 1.114, 1.128, 1.141, 1.152, 1.166, 1.182, 1.194, 1.205, 1.215, 1.229,
+ 1.223, 1.212, 1.204, 1.197, 1.186, 1.173, 1.161, 1.149, 1.133, 1.121, 1.108, 1.101, 1.092, 1.085, 1.082, 1.082, 1.082, 1.085, 1.091, 1.096, 1.101, 1.113, 1.125, 1.138, 1.151, 1.164, 1.175, 1.188, 1.198, 1.207, 1.215, 1.222,
+ 1.217, 1.213, 1.211, 1.203, 1.194, 1.181, 1.169, 1.158, 1.145, 1.133, 1.123, 1.113, 1.106, 1.097, 1.096, 1.094, 1.094, 1.098, 1.104, 1.108, 1.114, 1.124, 1.137, 1.149, 1.161, 1.172, 1.182, 1.194, 1.203, 1.209, 1.211, 1.217,
+ 1.214, 1.211, 1.209, 1.206, 1.201, 1.188, 1.179, 1.168, 1.154, 1.144, 1.136, 1.126, 1.119, 1.112, 1.109, 1.108, 1.108, 1.108, 1.117, 1.119, 1.124, 1.133, 1.147, 1.158, 1.171, 1.178, 1.188, 1.198, 1.205, 1.208, 1.209, 1.211,
+ 1.207, 1.208, 1.209, 1.206, 1.202, 1.192, 1.182, 1.171, 1.159, 1.146, 1.142, 1.136, 1.126, 1.119, 1.116, 1.114, 1.115, 1.117, 1.119, 1.128, 1.129, 1.136, 1.155, 1.162, 1.176, 1.182, 1.188, 1.198, 1.205, 1.208, 1.207, 1.206
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.879, 1.878, 1.872, 1.862, 1.856, 1.842, 1.826, 1.815, 1.811, 1.799, 1.787, 1.777, 1.768, 1.761, 1.761, 1.761, 1.762, 1.763, 1.764, 1.769, 1.776, 1.789, 1.799, 1.807, 1.824, 1.841, 1.853, 1.861, 1.871, 1.874, 1.885, 1.889,
+ 1.879, 1.875, 1.859, 1.846, 1.835, 1.817, 1.806, 1.794, 1.777, 1.771, 1.755, 1.743, 1.733, 1.726, 1.721, 1.721, 1.721, 1.722, 1.729, 1.734, 1.747, 1.759, 1.771, 1.783, 1.801, 1.813, 1.831, 1.841, 1.849, 1.862, 1.876, 1.888,
+ 1.876, 1.861, 1.846, 1.835, 1.817, 1.806, 1.793, 1.777, 1.766, 1.752, 1.736, 1.727, 1.713, 1.702, 1.696, 1.695, 1.695, 1.697, 1.704, 1.715, 1.725, 1.739, 1.754, 1.771, 1.783, 1.801, 1.813, 1.831, 1.841, 1.851, 1.866, 1.888,
+ 1.878, 1.861, 1.843, 1.829, 1.811, 1.794, 1.779, 1.766, 1.751, 1.734, 1.721, 1.711, 1.695, 1.682, 1.679, 1.677, 1.677, 1.678, 1.687, 1.696, 1.713, 1.723, 1.737, 1.754, 1.774, 1.785, 1.811, 1.825, 1.833, 1.849, 1.866, 1.889,
+ 1.882, 1.859, 1.837, 1.821, 1.803, 1.784, 1.769, 1.752, 1.735, 1.717, 1.701, 1.689, 1.676, 1.664, 1.659, 1.658, 1.658, 1.659, 1.668, 1.679, 1.694, 1.711, 1.723, 1.739, 1.756, 1.777, 1.797, 1.813, 1.827, 1.844, 1.865, 1.889,
+ 1.869, 1.849, 1.832, 1.811, 1.792, 1.772, 1.755, 1.737, 1.717, 1.699, 1.688, 1.674, 1.661, 1.646, 1.642, 1.638, 1.638, 1.641, 1.651, 1.659, 1.676, 1.693, 1.708, 1.724, 1.744, 1.763, 1.783, 1.801, 1.819, 1.838, 1.864, 1.889,
+ 1.869, 1.841, 1.817, 1.801, 1.782, 1.758, 1.741, 1.721, 1.699, 1.688, 1.674, 1.658, 1.643, 1.632, 1.627, 1.621, 1.621, 1.622, 1.631, 1.643, 1.658, 1.676, 1.689, 1.708, 1.729, 1.748, 1.767, 1.791, 1.812, 1.836, 1.859, 1.891,
+ 1.861, 1.836, 1.814, 1.792, 1.772, 1.745, 1.728, 1.707, 1.688, 1.673, 1.658, 1.643, 1.629, 1.618, 1.612, 1.609, 1.609, 1.611, 1.615, 1.629, 1.642, 1.658, 1.676, 1.689, 1.711, 1.734, 1.758, 1.782, 1.804, 1.827, 1.859, 1.891,
+ 1.861, 1.829, 1.807, 1.784, 1.759, 1.735, 1.717, 1.692, 1.674, 1.659, 1.644, 1.629, 1.617, 1.605, 1.598, 1.595, 1.595, 1.598, 1.607, 1.615, 1.631, 1.642, 1.661, 1.681, 1.701, 1.724, 1.746, 1.771, 1.799, 1.825, 1.857, 1.891,
+ 1.861, 1.826, 1.804, 1.779, 1.749, 1.729, 1.707, 1.687, 1.665, 1.648, 1.629, 1.617, 1.604, 1.595, 1.589, 1.585, 1.585, 1.592, 1.597, 1.607, 1.623, 1.635, 1.652, 1.674, 1.693, 1.716, 1.739, 1.766, 1.794, 1.822, 1.855, 1.889,
+ 1.861, 1.824, 1.799, 1.777, 1.748, 1.723, 1.701, 1.678, 1.657, 1.639, 1.619, 1.605, 1.596, 1.586, 1.581, 1.579, 1.577, 1.579, 1.588, 1.597, 1.612, 1.625, 1.641, 1.661, 1.681, 1.702, 1.732, 1.757, 1.785, 1.813, 1.847, 1.882,
+ 1.856, 1.819, 1.796, 1.767, 1.739, 1.714, 1.693, 1.666, 1.651, 1.629, 1.613, 1.597, 1.586, 1.579, 1.576, 1.572, 1.572, 1.573, 1.579, 1.588, 1.602, 1.619, 1.633, 1.655, 1.674, 1.698, 1.729, 1.754, 1.782, 1.809, 1.842, 1.874,
+ 1.853, 1.815, 1.792, 1.761, 1.734, 1.707, 1.682, 1.659, 1.639, 1.622, 1.605, 1.591, 1.579, 1.574, 1.569, 1.565, 1.566, 1.569, 1.573, 1.584, 1.597, 1.609, 1.624, 1.645, 1.666, 1.695, 1.722, 1.746, 1.772, 1.799, 1.835, 1.873,
+ 1.847, 1.811, 1.789, 1.759, 1.732, 1.703, 1.681, 1.657, 1.637, 1.619, 1.603, 1.588, 1.575, 1.569, 1.563, 1.561, 1.561, 1.563, 1.569, 1.576, 1.589, 1.601, 1.616, 1.636, 1.659, 1.686, 1.712, 1.741, 1.767, 1.798, 1.832, 1.873,
+ 1.847, 1.803, 1.779, 1.756, 1.727, 1.699, 1.674, 1.652, 1.632, 1.616, 1.595, 1.583, 1.572, 1.564, 1.558, 1.556, 1.557, 1.559, 1.563, 1.569, 1.583, 1.593, 1.613, 1.633, 1.657, 1.684, 1.709, 1.741, 1.766, 1.796, 1.831, 1.871,
+ 1.845, 1.802, 1.779, 1.755, 1.725, 1.696, 1.673, 1.649, 1.629, 1.614, 1.595, 1.582, 1.572, 1.563, 1.557, 1.556, 1.556, 1.558, 1.562, 1.569, 1.581, 1.593, 1.612, 1.633, 1.656, 1.679, 1.709, 1.741, 1.764, 1.796, 1.828, 1.869,
+ 1.845, 1.801, 1.779, 1.749, 1.723, 1.697, 1.673, 1.649, 1.627, 1.613, 1.593, 1.581, 1.573, 1.563, 1.558, 1.555, 1.555, 1.556, 1.562, 1.573, 1.581, 1.594, 1.611, 1.633, 1.656, 1.679, 1.711, 1.739, 1.764, 1.794, 1.828, 1.869,
+ 1.844, 1.801, 1.781, 1.749, 1.723, 1.697, 1.673, 1.649, 1.627, 1.614, 1.595, 1.581, 1.574, 1.564, 1.559, 1.557, 1.556, 1.559, 1.564, 1.574, 1.582, 1.595, 1.611, 1.634, 1.659, 1.683, 1.709, 1.739, 1.765, 1.794, 1.829, 1.872,
+ 1.845, 1.802, 1.781, 1.754, 1.725, 1.701, 1.677, 1.652, 1.632, 1.616, 1.599, 1.586, 1.576, 1.569, 1.563, 1.559, 1.558, 1.562, 1.569, 1.576, 1.587, 1.599, 1.618, 1.635, 1.661, 1.685, 1.709, 1.739, 1.767, 1.796, 1.829, 1.868,
+ 1.845, 1.809, 1.785, 1.762, 1.731, 1.706, 1.685, 1.659, 1.641, 1.622, 1.606, 1.595, 1.581, 1.575, 1.569, 1.564, 1.564, 1.569, 1.574, 1.582, 1.594, 1.607, 1.625, 1.642, 1.668, 1.687, 1.716, 1.741, 1.769, 1.798, 1.829, 1.868,
+ 1.849, 1.811, 1.785, 1.765, 1.734, 1.709, 1.688, 1.666, 1.647, 1.628, 1.613, 1.601, 1.592, 1.581, 1.575, 1.572, 1.572, 1.575, 1.581, 1.589, 1.599, 1.611, 1.631, 1.649, 1.673, 1.694, 1.721, 1.747, 1.771, 1.798, 1.829, 1.868,
+ 1.849, 1.816, 1.787, 1.766, 1.739, 1.716, 1.692, 1.673, 1.657, 1.641, 1.625, 1.612, 1.599, 1.592, 1.584, 1.581, 1.581, 1.581, 1.589, 1.598, 1.608, 1.622, 1.639, 1.659, 1.679, 1.701, 1.724, 1.751, 1.774, 1.802, 1.832, 1.868,
+ 1.855, 1.816, 1.793, 1.773, 1.748, 1.727, 1.707, 1.686, 1.667, 1.649, 1.636, 1.623, 1.612, 1.599, 1.594, 1.592, 1.591, 1.591, 1.598, 1.608, 1.621, 1.634, 1.649, 1.669, 1.693, 1.705, 1.736, 1.757, 1.778, 1.804, 1.833, 1.867,
+ 1.858, 1.818, 1.796, 1.778, 1.754, 1.733, 1.716, 1.695, 1.676, 1.661, 1.648, 1.635, 1.624, 1.613, 1.604, 1.601, 1.601, 1.606, 1.613, 1.621, 1.634, 1.646, 1.661, 1.679, 1.699, 1.714, 1.742, 1.761, 1.782, 1.809, 1.835, 1.867,
+ 1.857, 1.822, 1.801, 1.789, 1.766, 1.744, 1.726, 1.706, 1.688, 1.671, 1.659, 1.647, 1.635, 1.624, 1.621, 1.617, 1.617, 1.621, 1.627, 1.634, 1.645, 1.656, 1.674, 1.694, 1.709, 1.723, 1.751, 1.771, 1.786, 1.811, 1.837, 1.867,
+ 1.858, 1.824, 1.807, 1.794, 1.774, 1.757, 1.739, 1.716, 1.702, 1.687, 1.671, 1.662, 1.648, 1.636, 1.629, 1.629, 1.629, 1.633, 1.635, 1.646, 1.656, 1.669, 1.684, 1.705, 1.719, 1.732, 1.753, 1.774, 1.793, 1.815, 1.837, 1.871,
+ 1.858, 1.827, 1.809, 1.798, 1.782, 1.761, 1.749, 1.727, 1.711, 1.698, 1.687, 1.675, 1.663, 1.649, 1.646, 1.643, 1.643, 1.646, 1.649, 1.658, 1.669, 1.683, 1.698, 1.716, 1.731, 1.746, 1.761, 1.783, 1.795, 1.817, 1.836, 1.862,
+ 1.862, 1.834, 1.816, 1.805, 1.789, 1.774, 1.759, 1.743, 1.725, 1.711, 1.697, 1.688, 1.678, 1.668, 1.661, 1.659, 1.658, 1.659, 1.668, 1.673, 1.684, 1.698, 1.713, 1.728, 1.742, 1.757, 1.771, 1.791, 1.804, 1.821, 1.836, 1.862,
+ 1.859, 1.835, 1.825, 1.813, 1.794, 1.782, 1.771, 1.757, 1.739, 1.725, 1.711, 1.701, 1.693, 1.683, 1.679, 1.679, 1.679, 1.683, 1.689, 1.693, 1.698, 1.714, 1.726, 1.741, 1.754, 1.769, 1.781, 1.797, 1.808, 1.821, 1.835, 1.856,
+ 1.848, 1.836, 1.832, 1.822, 1.806, 1.789, 1.778, 1.765, 1.751, 1.739, 1.726, 1.718, 1.709, 1.699, 1.696, 1.695, 1.695, 1.696, 1.704, 1.705, 1.714, 1.724, 1.739, 1.753, 1.765, 1.777, 1.789, 1.803, 1.816, 1.824, 1.829, 1.842,
+ 1.839, 1.835, 1.834, 1.829, 1.815, 1.801, 1.787, 1.776, 1.759, 1.751, 1.744, 1.736, 1.724, 1.714, 1.711, 1.708, 1.707, 1.707, 1.717, 1.719, 1.724, 1.734, 1.748, 1.762, 1.775, 1.783, 1.796, 1.808, 1.819, 1.825, 1.828, 1.833,
+ 1.836, 1.833, 1.834, 1.832, 1.821, 1.806, 1.792, 1.785, 1.772, 1.759, 1.751, 1.744, 1.736, 1.725, 1.719, 1.715, 1.715, 1.718, 1.721, 1.728, 1.734, 1.736, 1.757, 1.768, 1.779, 1.787, 1.799, 1.812, 1.821, 1.824, 1.825, 1.833
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.189, 2.127, 2.115, 2.106, 2.113, 2.119, 2.131, 2.144, 2.155, 2.168, 2.176, 2.179, 2.181, 2.181, 2.185, 2.187, 2.187, 2.183, 2.179, 2.176, 2.169, 2.167, 2.159, 2.152, 2.145, 2.141, 2.135, 2.128, 2.124, 2.124, 2.139, 2.177,
+ 2.176, 2.133, 2.116, 2.112, 2.116, 2.125, 2.137, 2.154, 2.168, 2.179, 2.187, 2.194, 2.201, 2.204, 2.208, 2.208, 2.205, 2.202, 2.198, 2.195, 2.183, 2.177, 2.166, 2.159, 2.149, 2.143, 2.138, 2.132, 2.124, 2.125, 2.136, 2.164,
+ 2.175, 2.133, 2.117, 2.115, 2.121, 2.136, 2.154, 2.165, 2.179, 2.192, 2.198, 2.211, 2.218, 2.219, 2.221, 2.221, 2.217, 2.216, 2.211, 2.202, 2.197, 2.188, 2.181, 2.171, 2.159, 2.151, 2.141, 2.136, 2.125, 2.125, 2.132, 2.155,
+ 2.172, 2.128, 2.116, 2.116, 2.124, 2.143, 2.161, 2.177, 2.192, 2.204, 2.213, 2.221, 2.227, 2.231, 2.237, 2.237, 2.229, 2.224, 2.221, 2.213, 2.207, 2.197, 2.191, 2.179, 2.169, 2.156, 2.148, 2.138, 2.126, 2.123, 2.124, 2.149,
+ 2.169, 2.124, 2.119, 2.119, 2.135, 2.152, 2.174, 2.187, 2.204, 2.211, 2.224, 2.233, 2.236, 2.241, 2.246, 2.246, 2.243, 2.237, 2.234, 2.226, 2.218, 2.211, 2.199, 2.191, 2.177, 2.166, 2.155, 2.139, 2.129, 2.121, 2.121, 2.143,
+ 2.157, 2.124, 2.121, 2.127, 2.145, 2.157, 2.181, 2.197, 2.208, 2.221, 2.238, 2.245, 2.249, 2.249, 2.254, 2.254, 2.249, 2.247, 2.243, 2.237, 2.228, 2.219, 2.209, 2.198, 2.186, 2.172, 2.161, 2.143, 2.129, 2.121, 2.121, 2.141,
+ 2.157, 2.124, 2.124, 2.131, 2.148, 2.161, 2.188, 2.202, 2.214, 2.238, 2.246, 2.251, 2.255, 2.257, 2.259, 2.259, 2.257, 2.252, 2.251, 2.247, 2.238, 2.231, 2.219, 2.204, 2.193, 2.173, 2.166, 2.152, 2.134, 2.119, 2.119, 2.131,
+ 2.155, 2.125, 2.125, 2.135, 2.151, 2.169, 2.191, 2.207, 2.219, 2.243, 2.253, 2.258, 2.261, 2.266, 2.266, 2.267, 2.265, 2.262, 2.261, 2.254, 2.244, 2.238, 2.228, 2.212, 2.197, 2.179, 2.167, 2.158, 2.137, 2.122, 2.121, 2.131,
+ 2.155, 2.127, 2.127, 2.137, 2.153, 2.173, 2.197, 2.213, 2.231, 2.248, 2.257, 2.266, 2.271, 2.272, 2.274, 2.275, 2.275, 2.273, 2.271, 2.266, 2.257, 2.251, 2.238, 2.227, 2.209, 2.195, 2.175, 2.159, 2.141, 2.128, 2.127, 2.131,
+ 2.155, 2.128, 2.128, 2.139, 2.159, 2.182, 2.206, 2.225, 2.243, 2.252, 2.265, 2.272, 2.277, 2.283, 2.286, 2.284, 2.283, 2.282, 2.274, 2.272, 2.266, 2.256, 2.244, 2.238, 2.221, 2.202, 2.186, 2.169, 2.149, 2.129, 2.129, 2.135,
+ 2.154, 2.131, 2.131, 2.149, 2.166, 2.189, 2.211, 2.234, 2.248, 2.262, 2.272, 2.277, 2.287, 2.291, 2.293, 2.292, 2.291, 2.285, 2.284, 2.279, 2.272, 2.263, 2.254, 2.243, 2.226, 2.206, 2.193, 2.174, 2.153, 2.133, 2.133, 2.135,
+ 2.153, 2.135, 2.135, 2.151, 2.172, 2.198, 2.221, 2.238, 2.255, 2.265, 2.274, 2.287, 2.291, 2.296, 2.298, 2.298, 2.301, 2.297, 2.289, 2.285, 2.277, 2.271, 2.261, 2.251, 2.236, 2.216, 2.199, 2.179, 2.158, 2.135, 2.134, 2.135,
+ 2.152, 2.136, 2.136, 2.154, 2.176, 2.199, 2.224, 2.239, 2.256, 2.267, 2.282, 2.289, 2.295, 2.299, 2.303, 2.303, 2.302, 2.299, 2.297, 2.288, 2.284, 2.274, 2.262, 2.253, 2.238, 2.219, 2.202, 2.181, 2.158, 2.137, 2.135, 2.135,
+ 2.143, 2.134, 2.134, 2.154, 2.177, 2.201, 2.224, 2.241, 2.256, 2.271, 2.282, 2.289, 2.297, 2.302, 2.306, 2.306, 2.304, 2.301, 2.298, 2.289, 2.287, 2.272, 2.265, 2.255, 2.241, 2.221, 2.203, 2.183, 2.164, 2.141, 2.136, 2.135,
+ 2.142, 2.133, 2.133, 2.155, 2.178, 2.202, 2.223, 2.243, 2.258, 2.273, 2.283, 2.288, 2.296, 2.299, 2.306, 2.306, 2.301, 2.299, 2.296, 2.289, 2.286, 2.271, 2.267, 2.256, 2.244, 2.219, 2.206, 2.188, 2.163, 2.141, 2.137, 2.134,
+ 2.141, 2.131, 2.131, 2.153, 2.179, 2.202, 2.224, 2.242, 2.254, 2.274, 2.283, 2.288, 2.295, 2.298, 2.301, 2.301, 2.301, 2.296, 2.295, 2.289, 2.285, 2.271, 2.267, 2.257, 2.246, 2.223, 2.204, 2.188, 2.165, 2.141, 2.136, 2.134,
+ 2.141, 2.133, 2.133, 2.151, 2.179, 2.201, 2.224, 2.241, 2.254, 2.275, 2.283, 2.288, 2.294, 2.296, 2.298, 2.297, 2.295, 2.295, 2.294, 2.291, 2.284, 2.272, 2.267, 2.256, 2.248, 2.225, 2.208, 2.192, 2.167, 2.141, 2.137, 2.134,
+ 2.141, 2.132, 2.132, 2.151, 2.177, 2.199, 2.221, 2.238, 2.252, 2.274, 2.281, 2.287, 2.293, 2.295, 2.296, 2.294, 2.295, 2.295, 2.294, 2.291, 2.284, 2.274, 2.266, 2.257, 2.248, 2.226, 2.206, 2.189, 2.167, 2.143, 2.141, 2.141,
+ 2.141, 2.133, 2.133, 2.153, 2.175, 2.201, 2.221, 2.238, 2.252, 2.271, 2.278, 2.284, 2.288, 2.291, 2.292, 2.291, 2.293, 2.293, 2.293, 2.287, 2.279, 2.275, 2.266, 2.256, 2.243, 2.224, 2.206, 2.189, 2.168, 2.146, 2.142, 2.134,
+ 2.137, 2.131, 2.131, 2.154, 2.173, 2.199, 2.221, 2.236, 2.251, 2.267, 2.272, 2.278, 2.284, 2.287, 2.288, 2.286, 2.288, 2.288, 2.288, 2.283, 2.277, 2.273, 2.265, 2.256, 2.241, 2.219, 2.205, 2.187, 2.167, 2.144, 2.137, 2.132,
+ 2.136, 2.131, 2.131, 2.152, 2.169, 2.197, 2.218, 2.233, 2.246, 2.257, 2.269, 2.274, 2.281, 2.284, 2.286, 2.285, 2.286, 2.286, 2.286, 2.279, 2.274, 2.269, 2.263, 2.254, 2.239, 2.217, 2.203, 2.181, 2.162, 2.143, 2.133, 2.131,
+ 2.136, 2.131, 2.131, 2.151, 2.167, 2.189, 2.205, 2.226, 2.242, 2.253, 2.261, 2.271, 2.275, 2.279, 2.283, 2.283, 2.284, 2.284, 2.281, 2.277, 2.271, 2.264, 2.257, 2.246, 2.232, 2.215, 2.195, 2.176, 2.158, 2.141, 2.131, 2.128,
+ 2.136, 2.129, 2.131, 2.147, 2.162, 2.181, 2.203, 2.219, 2.236, 2.246, 2.256, 2.263, 2.271, 2.274, 2.278, 2.278, 2.276, 2.277, 2.276, 2.273, 2.266, 2.258, 2.251, 2.241, 2.227, 2.198, 2.191, 2.169, 2.154, 2.136, 2.125, 2.122,
+ 2.132, 2.126, 2.126, 2.139, 2.153, 2.168, 2.194, 2.212, 2.224, 2.238, 2.251, 2.258, 2.263, 2.266, 2.269, 2.271, 2.269, 2.269, 2.269, 2.267, 2.259, 2.253, 2.245, 2.237, 2.219, 2.196, 2.179, 2.162, 2.149, 2.132, 2.122, 2.121,
+ 2.124, 2.119, 2.121, 2.137, 2.147, 2.164, 2.183, 2.199, 2.219, 2.231, 2.239, 2.251, 2.257, 2.261, 2.262, 2.262, 2.259, 2.259, 2.261, 2.258, 2.253, 2.245, 2.237, 2.224, 2.209, 2.187, 2.174, 2.157, 2.141, 2.122, 2.121, 2.121,
+ 2.123, 2.115, 2.115, 2.131, 2.138, 2.157, 2.174, 2.188, 2.207, 2.221, 2.233, 2.239, 2.243, 2.244, 2.244, 2.244, 2.246, 2.245, 2.246, 2.244, 2.241, 2.231, 2.224, 2.212, 2.195, 2.176, 2.159, 2.145, 2.128, 2.117, 2.117, 2.123,
+ 2.123, 2.113, 2.113, 2.123, 2.132, 2.141, 2.162, 2.177, 2.191, 2.208, 2.221, 2.231, 2.231, 2.232, 2.234, 2.235, 2.235, 2.235, 2.238, 2.237, 2.225, 2.214, 2.209, 2.199, 2.181, 2.164, 2.146, 2.135, 2.123, 2.116, 2.116, 2.115,
+ 2.129, 2.115, 2.115, 2.121, 2.128, 2.135, 2.149, 2.164, 2.178, 2.193, 2.207, 2.221, 2.222, 2.222, 2.223, 2.224, 2.224, 2.224, 2.224, 2.223, 2.214, 2.205, 2.196, 2.185, 2.171, 2.151, 2.141, 2.129, 2.119, 2.116, 2.116, 2.117,
+ 2.137, 2.119, 2.119, 2.119, 2.122, 2.129, 2.141, 2.159, 2.167, 2.182, 2.195, 2.206, 2.211, 2.216, 2.218, 2.219, 2.219, 2.219, 2.217, 2.212, 2.202, 2.194, 2.184, 2.174, 2.162, 2.145, 2.134, 2.124, 2.118, 2.117, 2.118, 2.121,
+ 2.138, 2.131, 2.121, 2.122, 2.125, 2.128, 2.137, 2.154, 2.162, 2.176, 2.187, 2.194, 2.196, 2.198, 2.205, 2.205, 2.202, 2.202, 2.203, 2.201, 2.191, 2.182, 2.174, 2.162, 2.149, 2.136, 2.126, 2.121, 2.119, 2.118, 2.127, 2.133,
+ 2.157, 2.148, 2.131, 2.129, 2.129, 2.136, 2.148, 2.157, 2.169, 2.177, 2.182, 2.187, 2.188, 2.191, 2.193, 2.193, 2.192, 2.199, 2.201, 2.199, 2.186, 2.178, 2.167, 2.152, 2.146, 2.137, 2.126, 2.124, 2.121, 2.126, 2.133, 2.151,
+ 2.161, 2.157, 2.148, 2.147, 2.147, 2.147, 2.154, 2.162, 2.174, 2.179, 2.181, 2.184, 2.186, 2.187, 2.189, 2.189, 2.187, 2.188, 2.199, 2.201, 2.187, 2.178, 2.163, 2.148, 2.145, 2.141, 2.131, 2.129, 2.128, 2.135, 2.151, 2.153
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.191, 1.165, 1.156, 1.155, 1.157, 1.161, 1.168, 1.176, 1.179, 1.185, 1.187, 1.189, 1.189, 1.189, 1.191, 1.191, 1.191, 1.189, 1.188, 1.188, 1.185, 1.184, 1.182, 1.178, 1.173, 1.171, 1.166, 1.163, 1.159, 1.159, 1.164, 1.187,
+ 1.188, 1.164, 1.157, 1.156, 1.158, 1.166, 1.173, 1.179, 1.185, 1.193, 1.195, 1.198, 1.199, 1.201, 1.201, 1.202, 1.201, 1.199, 1.199, 1.196, 1.194, 1.189, 1.185, 1.182, 1.177, 1.172, 1.168, 1.164, 1.161, 1.161, 1.162, 1.181,
+ 1.184, 1.164, 1.157, 1.157, 1.161, 1.171, 1.179, 1.185, 1.193, 1.197, 1.201, 1.206, 1.208, 1.209, 1.209, 1.208, 1.207, 1.207, 1.207, 1.202, 1.199, 1.195, 1.192, 1.189, 1.182, 1.176, 1.171, 1.166, 1.161, 1.159, 1.161, 1.177,
+ 1.183, 1.162, 1.158, 1.158, 1.163, 1.174, 1.182, 1.191, 1.197, 1.203, 1.208, 1.212, 1.214, 1.214, 1.218, 1.218, 1.214, 1.212, 1.211, 1.208, 1.206, 1.201, 1.197, 1.192, 1.189, 1.179, 1.174, 1.168, 1.162, 1.159, 1.159, 1.173,
+ 1.181, 1.159, 1.159, 1.159, 1.168, 1.178, 1.189, 1.196, 1.204, 1.208, 1.213, 1.217, 1.219, 1.221, 1.222, 1.222, 1.222, 1.221, 1.219, 1.215, 1.212, 1.208, 1.202, 1.197, 1.189, 1.183, 1.178, 1.169, 1.163, 1.158, 1.158, 1.169,
+ 1.174, 1.159, 1.159, 1.164, 1.172, 1.179, 1.192, 1.201, 1.208, 1.212, 1.219, 1.224, 1.225, 1.227, 1.228, 1.228, 1.226, 1.225, 1.224, 1.221, 1.217, 1.212, 1.208, 1.202, 1.194, 1.187, 1.181, 1.172, 1.164, 1.157, 1.157, 1.169,
+ 1.174, 1.159, 1.159, 1.165, 1.174, 1.184, 1.197, 1.205, 1.209, 1.219, 1.224, 1.228, 1.231, 1.231, 1.231, 1.231, 1.229, 1.229, 1.228, 1.226, 1.222, 1.218, 1.212, 1.205, 1.199, 1.188, 1.181, 1.175, 1.165, 1.157, 1.157, 1.163,
+ 1.173, 1.159, 1.159, 1.165, 1.176, 1.186, 1.198, 1.207, 1.213, 1.223, 1.229, 1.231, 1.235, 1.236, 1.236, 1.236, 1.236, 1.235, 1.234, 1.232, 1.226, 1.223, 1.218, 1.209, 1.201, 1.192, 1.183, 1.178, 1.165, 1.157, 1.157, 1.163,
+ 1.172, 1.159, 1.159, 1.166, 1.176, 1.188, 1.201, 1.209, 1.217, 1.227, 1.231, 1.236, 1.238, 1.239, 1.241, 1.242, 1.242, 1.241, 1.239, 1.235, 1.232, 1.227, 1.223, 1.215, 1.208, 1.199, 1.187, 1.179, 1.167, 1.159, 1.159, 1.163,
+ 1.172, 1.159, 1.159, 1.166, 1.177, 1.189, 1.203, 1.212, 1.223, 1.228, 1.236, 1.239, 1.242, 1.245, 1.246, 1.246, 1.247, 1.246, 1.242, 1.241, 1.237, 1.232, 1.226, 1.223, 1.213, 1.202, 1.191, 1.182, 1.172, 1.159, 1.159, 1.163,
+ 1.168, 1.158, 1.158, 1.167, 1.179, 1.192, 1.204, 1.218, 1.225, 1.233, 1.238, 1.242, 1.246, 1.248, 1.251, 1.251, 1.249, 1.248, 1.247, 1.244, 1.239, 1.237, 1.228, 1.223, 1.214, 1.203, 1.194, 1.183, 1.173, 1.161, 1.161, 1.162,
+ 1.166, 1.158, 1.158, 1.168, 1.183, 1.195, 1.207, 1.218, 1.226, 1.233, 1.239, 1.246, 1.248, 1.251, 1.254, 1.254, 1.254, 1.251, 1.249, 1.247, 1.242, 1.239, 1.232, 1.227, 1.219, 1.207, 1.195, 1.186, 1.175, 1.162, 1.161, 1.162,
+ 1.165, 1.158, 1.158, 1.168, 1.183, 1.196, 1.208, 1.219, 1.227, 1.234, 1.241, 1.247, 1.251, 1.254, 1.255, 1.256, 1.256, 1.254, 1.252, 1.249, 1.246, 1.241, 1.234, 1.228, 1.221, 1.211, 1.199, 1.187, 1.175, 1.163, 1.162, 1.162,
+ 1.161, 1.158, 1.158, 1.169, 1.183, 1.196, 1.208, 1.217, 1.227, 1.234, 1.241, 1.247, 1.253, 1.254, 1.256, 1.257, 1.256, 1.255, 1.253, 1.249, 1.247, 1.241, 1.236, 1.229, 1.221, 1.211, 1.199, 1.189, 1.176, 1.164, 1.163, 1.162,
+ 1.161, 1.156, 1.156, 1.169, 1.183, 1.196, 1.207, 1.218, 1.227, 1.235, 1.241, 1.246, 1.252, 1.254, 1.256, 1.257, 1.256, 1.254, 1.253, 1.249, 1.247, 1.241, 1.237, 1.231, 1.223, 1.211, 1.201, 1.191, 1.177, 1.164, 1.164, 1.161,
+ 1.161, 1.155, 1.155, 1.169, 1.182, 1.195, 1.208, 1.216, 1.225, 1.235, 1.241, 1.245, 1.249, 1.252, 1.254, 1.254, 1.254, 1.253, 1.252, 1.249, 1.246, 1.239, 1.237, 1.231, 1.224, 1.211, 1.201, 1.191, 1.178, 1.164, 1.162, 1.161,
+ 1.159, 1.155, 1.155, 1.168, 1.181, 1.195, 1.208, 1.217, 1.223, 1.235, 1.241, 1.244, 1.248, 1.251, 1.252, 1.252, 1.252, 1.252, 1.251, 1.248, 1.245, 1.241, 1.236, 1.231, 1.224, 1.212, 1.202, 1.191, 1.179, 1.164, 1.162, 1.161,
+ 1.158, 1.154, 1.154, 1.167, 1.181, 1.194, 1.206, 1.216, 1.222, 1.234, 1.237, 1.242, 1.245, 1.248, 1.251, 1.249, 1.249, 1.249, 1.249, 1.248, 1.244, 1.241, 1.235, 1.229, 1.223, 1.213, 1.202, 1.191, 1.179, 1.167, 1.163, 1.163,
+ 1.158, 1.154, 1.154, 1.168, 1.181, 1.194, 1.206, 1.215, 1.223, 1.231, 1.236, 1.239, 1.243, 1.245, 1.246, 1.246, 1.248, 1.248, 1.248, 1.245, 1.242, 1.239, 1.235, 1.229, 1.223, 1.213, 1.202, 1.191, 1.179, 1.167, 1.163, 1.162,
+ 1.157, 1.154, 1.154, 1.168, 1.179, 1.194, 1.205, 1.215, 1.222, 1.229, 1.233, 1.236, 1.239, 1.243, 1.244, 1.244, 1.245, 1.245, 1.244, 1.243, 1.239, 1.236, 1.234, 1.229, 1.222, 1.211, 1.202, 1.191, 1.179, 1.166, 1.163, 1.161,
+ 1.156, 1.155, 1.155, 1.168, 1.179, 1.193, 1.205, 1.213, 1.219, 1.225, 1.231, 1.234, 1.238, 1.239, 1.241, 1.243, 1.243, 1.243, 1.243, 1.239, 1.237, 1.235, 1.231, 1.228, 1.221, 1.209, 1.199, 1.189, 1.178, 1.166, 1.162, 1.159,
+ 1.156, 1.156, 1.157, 1.167, 1.178, 1.191, 1.199, 1.209, 1.217, 1.223, 1.226, 1.231, 1.233, 1.236, 1.239, 1.239, 1.241, 1.241, 1.239, 1.237, 1.235, 1.232, 1.229, 1.224, 1.217, 1.209, 1.196, 1.187, 1.176, 1.165, 1.159, 1.157,
+ 1.157, 1.157, 1.157, 1.166, 1.175, 1.187, 1.198, 1.205, 1.213, 1.219, 1.223, 1.227, 1.231, 1.233, 1.236, 1.236, 1.234, 1.235, 1.235, 1.235, 1.231, 1.229, 1.227, 1.222, 1.216, 1.201, 1.194, 1.184, 1.174, 1.163, 1.157, 1.156,
+ 1.158, 1.155, 1.155, 1.165, 1.172, 1.181, 1.194, 1.202, 1.208, 1.215, 1.221, 1.223, 1.227, 1.229, 1.231, 1.231, 1.231, 1.232, 1.233, 1.231, 1.228, 1.227, 1.223, 1.219, 1.213, 1.199, 1.189, 1.181, 1.171, 1.161, 1.157, 1.156,
+ 1.155, 1.154, 1.154, 1.164, 1.169, 1.179, 1.189, 1.196, 1.203, 1.208, 1.215, 1.221, 1.222, 1.224, 1.225, 1.225, 1.226, 1.228, 1.228, 1.227, 1.225, 1.222, 1.219, 1.213, 1.206, 1.196, 1.187, 1.177, 1.168, 1.159, 1.156, 1.156,
+ 1.155, 1.152, 1.152, 1.162, 1.167, 1.175, 1.185, 1.191, 1.198, 1.205, 1.209, 1.214, 1.216, 1.217, 1.217, 1.217, 1.219, 1.219, 1.219, 1.219, 1.217, 1.215, 1.213, 1.207, 1.199, 1.191, 1.179, 1.172, 1.165, 1.156, 1.155, 1.155,
+ 1.155, 1.152, 1.152, 1.161, 1.163, 1.169, 1.179, 1.186, 1.192, 1.198, 1.204, 1.208, 1.211, 1.211, 1.211, 1.212, 1.212, 1.213, 1.215, 1.215, 1.211, 1.208, 1.205, 1.199, 1.194, 1.185, 1.175, 1.167, 1.161, 1.156, 1.155, 1.153,
+ 1.157, 1.152, 1.152, 1.159, 1.162, 1.166, 1.174, 1.181, 1.187, 1.192, 1.197, 1.203, 1.204, 1.205, 1.204, 1.204, 1.204, 1.205, 1.206, 1.206, 1.204, 1.201, 1.198, 1.194, 1.187, 1.176, 1.171, 1.164, 1.159, 1.156, 1.155, 1.154,
+ 1.159, 1.154, 1.154, 1.158, 1.159, 1.163, 1.171, 1.176, 1.181, 1.187, 1.191, 1.195, 1.198, 1.199, 1.199, 1.201, 1.201, 1.202, 1.202, 1.199, 1.196, 1.193, 1.191, 1.188, 1.182, 1.174, 1.166, 1.162, 1.157, 1.156, 1.156, 1.156,
+ 1.162, 1.161, 1.158, 1.159, 1.159, 1.162, 1.167, 1.173, 1.178, 1.181, 1.186, 1.189, 1.189, 1.191, 1.193, 1.193, 1.193, 1.194, 1.194, 1.194, 1.189, 1.187, 1.186, 1.182, 1.176, 1.167, 1.163, 1.159, 1.158, 1.157, 1.158, 1.161,
+ 1.172, 1.165, 1.162, 1.162, 1.163, 1.166, 1.169, 1.173, 1.178, 1.181, 1.182, 1.185, 1.186, 1.186, 1.186, 1.187, 1.187, 1.189, 1.192, 1.191, 1.187, 1.185, 1.181, 1.177, 1.172, 1.167, 1.163, 1.159, 1.159, 1.161, 1.163, 1.166,
+ 1.173, 1.172, 1.166, 1.165, 1.166, 1.168, 1.171, 1.176, 1.179, 1.182, 1.181, 1.183, 1.185, 1.185, 1.185, 1.185, 1.185, 1.185, 1.191, 1.191, 1.185, 1.181, 1.179, 1.173, 1.169, 1.168, 1.163, 1.162, 1.161, 1.164, 1.166, 1.167
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.271, 2.218, 2.105, 2.004, 1.909, 1.829, 1.762, 1.705, 1.665, 1.629, 1.592, 1.559, 1.528, 1.516, 1.511, 1.511, 1.511, 1.514, 1.525, 1.553, 1.585, 1.617, 1.655, 1.697, 1.752, 1.816, 1.893, 1.982, 2.084, 2.195, 2.321, 2.342,
+ 2.218, 2.166, 2.057, 1.959, 1.871, 1.793, 1.726, 1.675, 1.633, 1.592, 1.559, 1.528, 1.503, 1.484, 1.474, 1.472, 1.472, 1.482, 1.499, 1.523, 1.553, 1.585, 1.619, 1.664, 1.715, 1.779, 1.855, 1.938, 2.037, 2.147, 2.259, 2.321,
+ 2.166, 2.101, 1.997, 1.901, 1.818, 1.743, 1.683, 1.634, 1.588, 1.546, 1.508, 1.476, 1.449, 1.429, 1.418, 1.415, 1.415, 1.425, 1.444, 1.469, 1.501, 1.538, 1.577, 1.622, 1.671, 1.728, 1.799, 1.881, 1.975, 2.078, 2.185, 2.259,
+ 2.101, 2.039, 1.938, 1.848, 1.768, 1.699, 1.641, 1.588, 1.541, 1.494, 1.455, 1.421, 1.394, 1.374, 1.361, 1.357, 1.357, 1.367, 1.388, 1.414, 1.448, 1.485, 1.528, 1.577, 1.626, 1.682, 1.748, 1.827, 1.917, 2.014, 2.119, 2.185,
+ 2.039, 1.979, 1.883, 1.795, 1.722, 1.658, 1.596, 1.541, 1.493, 1.443, 1.401, 1.364, 1.336, 1.316, 1.303, 1.301, 1.301, 1.311, 1.331, 1.359, 1.393, 1.432, 1.482, 1.528, 1.582, 1.641, 1.701, 1.775, 1.861, 1.956, 2.056, 2.119,
+ 1.979, 1.932, 1.836, 1.752, 1.685, 1.621, 1.557, 1.497, 1.443, 1.399, 1.351, 1.314, 1.286, 1.264, 1.253, 1.249, 1.249, 1.259, 1.281, 1.311, 1.344, 1.387, 1.432, 1.484, 1.541, 1.601, 1.662, 1.731, 1.816, 1.908, 2.003, 2.056,
+ 1.934, 1.888, 1.798, 1.719, 1.651, 1.584, 1.519, 1.457, 1.401, 1.351, 1.307, 1.268, 1.239, 1.217, 1.206, 1.203, 1.203, 1.212, 1.234, 1.263, 1.298, 1.344, 1.387, 1.442, 1.502, 1.565, 1.628, 1.693, 1.774, 1.864, 1.956, 2.003,
+ 1.901, 1.851, 1.763, 1.688, 1.618, 1.551, 1.483, 1.419, 1.359, 1.307, 1.268, 1.226, 1.195, 1.175, 1.164, 1.161, 1.161, 1.171, 1.192, 1.221, 1.262, 1.298, 1.346, 1.404, 1.466, 1.532, 1.595, 1.661, 1.738, 1.826, 1.917, 1.956,
+ 1.873, 1.821, 1.734, 1.659, 1.591, 1.519, 1.451, 1.386, 1.324, 1.269, 1.226, 1.192, 1.159, 1.141, 1.127, 1.125, 1.125, 1.135, 1.155, 1.187, 1.221, 1.262, 1.311, 1.368, 1.432, 1.499, 1.566, 1.634, 1.708, 1.793, 1.882, 1.917,
+ 1.847, 1.797, 1.713, 1.639, 1.565, 1.493, 1.422, 1.355, 1.291, 1.238, 1.192, 1.159, 1.128, 1.108, 1.097, 1.094, 1.094, 1.104, 1.125, 1.155, 1.187, 1.229, 1.279, 1.338, 1.403, 1.471, 1.541, 1.611, 1.684, 1.766, 1.853, 1.885,
+ 1.828, 1.772, 1.691, 1.614, 1.539, 1.466, 1.394, 1.325, 1.264, 1.209, 1.163, 1.128, 1.104, 1.081, 1.069, 1.067, 1.067, 1.078, 1.101, 1.125, 1.159, 1.201, 1.252, 1.312, 1.379, 1.447, 1.517, 1.591, 1.665, 1.743, 1.831, 1.862,
+ 1.812, 1.754, 1.677, 1.599, 1.519, 1.445, 1.371, 1.302, 1.239, 1.185, 1.139, 1.104, 1.081, 1.061, 1.048, 1.046, 1.046, 1.058, 1.078, 1.102, 1.136, 1.177, 1.229, 1.289, 1.356, 1.425, 1.497, 1.572, 1.647, 1.724, 1.811, 1.847,
+ 1.798, 1.741, 1.663, 1.585, 1.506, 1.429, 1.353, 1.284, 1.221, 1.167, 1.121, 1.086, 1.061, 1.046, 1.031, 1.029, 1.029, 1.044, 1.058, 1.083, 1.116, 1.159, 1.209, 1.271, 1.338, 1.407, 1.479, 1.557, 1.633, 1.709, 1.792, 1.832,
+ 1.792, 1.727, 1.651, 1.572, 1.494, 1.414, 1.339, 1.269, 1.206, 1.152, 1.106, 1.072, 1.046, 1.031, 1.018, 1.016, 1.016, 1.029, 1.044, 1.069, 1.102, 1.145, 1.196, 1.256, 1.324, 1.394, 1.471, 1.545, 1.624, 1.698, 1.782, 1.825,
+ 1.787, 1.724, 1.647, 1.566, 1.484, 1.407, 1.329, 1.258, 1.196, 1.141, 1.097, 1.062, 1.036, 1.018, 1.012, 1.007, 1.011, 1.016, 1.034, 1.059, 1.093, 1.135, 1.186, 1.246, 1.314, 1.386, 1.461, 1.538, 1.616, 1.691, 1.773, 1.818,
+ 1.786, 1.721, 1.642, 1.562, 1.481, 1.402, 1.325, 1.254, 1.191, 1.137, 1.092, 1.057, 1.031, 1.013, 1.004, 1.001, 1.004, 1.011, 1.028, 1.054, 1.088, 1.129, 1.181, 1.241, 1.308, 1.382, 1.458, 1.535, 1.613, 1.687, 1.769, 1.818,
+ 1.786, 1.721, 1.642, 1.562, 1.481, 1.401, 1.325, 1.253, 1.191, 1.136, 1.091, 1.057, 1.031, 1.013, 1.003, 1.001, 1.001, 1.011, 1.028, 1.054, 1.088, 1.129, 1.181, 1.241, 1.308, 1.382, 1.458, 1.535, 1.613, 1.687, 1.769, 1.818,
+ 1.787, 1.722, 1.643, 1.563, 1.482, 1.402, 1.326, 1.254, 1.192, 1.138, 1.092, 1.057, 1.032, 1.013, 1.006, 1.002, 1.006, 1.012, 1.031, 1.057, 1.092, 1.133, 1.185, 1.243, 1.311, 1.385, 1.461, 1.539, 1.618, 1.691, 1.774, 1.821,
+ 1.789, 1.729, 1.651, 1.571, 1.489, 1.411, 1.334, 1.263, 1.201, 1.147, 1.101, 1.065, 1.038, 1.021, 1.013, 1.009, 1.012, 1.021, 1.038, 1.064, 1.098, 1.141, 1.193, 1.254, 1.321, 1.395, 1.472, 1.549, 1.626, 1.701, 1.785, 1.825,
+ 1.799, 1.739, 1.661, 1.581, 1.502, 1.422, 1.347, 1.277, 1.214, 1.159, 1.111, 1.075, 1.049, 1.037, 1.021, 1.019, 1.021, 1.036, 1.049, 1.076, 1.111, 1.154, 1.207, 1.268, 1.334, 1.408, 1.485, 1.562, 1.639, 1.715, 1.799, 1.837,
+ 1.811, 1.755, 1.676, 1.597, 1.518, 1.439, 1.365, 1.295, 1.231, 1.176, 1.129, 1.093, 1.067, 1.049, 1.038, 1.036, 1.036, 1.049, 1.067, 1.094, 1.129, 1.173, 1.225, 1.286, 1.353, 1.425, 1.501, 1.577, 1.653, 1.729, 1.815, 1.851,
+ 1.829, 1.774, 1.693, 1.615, 1.537, 1.462, 1.387, 1.316, 1.253, 1.198, 1.153, 1.115, 1.091, 1.067, 1.059, 1.056, 1.056, 1.067, 1.092, 1.115, 1.151, 1.196, 1.249, 1.309, 1.375, 1.446, 1.522, 1.595, 1.672, 1.752, 1.839, 1.871,
+ 1.851, 1.801, 1.713, 1.636, 1.561, 1.485, 1.411, 1.342, 1.281, 1.226, 1.179, 1.145, 1.115, 1.091, 1.082, 1.081, 1.082, 1.092, 1.115, 1.143, 1.178, 1.223, 1.276, 1.337, 1.402, 1.472, 1.544, 1.618, 1.691, 1.774, 1.865, 1.896,
+ 1.876, 1.831, 1.739, 1.663, 1.588, 1.513, 1.439, 1.374, 1.312, 1.258, 1.212, 1.179, 1.145, 1.123, 1.113, 1.112, 1.112, 1.122, 1.143, 1.177, 1.211, 1.256, 1.308, 1.368, 1.431, 1.501, 1.572, 1.641, 1.716, 1.802, 1.896, 1.931,
+ 1.909, 1.867, 1.771, 1.691, 1.617, 1.545, 1.474, 1.411, 1.349, 1.296, 1.252, 1.212, 1.182, 1.159, 1.149, 1.148, 1.149, 1.158, 1.179, 1.211, 1.253, 1.293, 1.344, 1.403, 1.465, 1.533, 1.603, 1.669, 1.747, 1.836, 1.931, 1.974,
+ 1.952, 1.905, 1.806, 1.722, 1.651, 1.578, 1.511, 1.448, 1.388, 1.338, 1.296, 1.252, 1.223, 1.201, 1.189, 1.189, 1.189, 1.199, 1.224, 1.253, 1.293, 1.338, 1.384, 1.442, 1.504, 1.571, 1.638, 1.704, 1.782, 1.872, 1.974, 2.025,
+ 2.004, 1.951, 1.849, 1.759, 1.688, 1.619, 1.552, 1.491, 1.435, 1.388, 1.338, 1.301, 1.272, 1.249, 1.238, 1.236, 1.236, 1.248, 1.271, 1.301, 1.338, 1.384, 1.431, 1.484, 1.543, 1.609, 1.675, 1.742, 1.825, 1.919, 2.025, 2.081,
+ 2.062, 2.004, 1.898, 1.805, 1.729, 1.661, 1.597, 1.539, 1.486, 1.435, 1.391, 1.354, 1.326, 1.303, 1.291, 1.289, 1.289, 1.301, 1.323, 1.353, 1.389, 1.431, 1.483, 1.528, 1.585, 1.649, 1.713, 1.787, 1.875, 1.971, 2.081, 2.145,
+ 2.129, 2.062, 1.951, 1.854, 1.774, 1.705, 1.642, 1.586, 1.539, 1.486, 1.445, 1.411, 1.383, 1.361, 1.348, 1.347, 1.348, 1.359, 1.379, 1.409, 1.447, 1.484, 1.528, 1.578, 1.631, 1.691, 1.759, 1.836, 1.928, 2.031, 2.145, 2.217,
+ 2.201, 2.129, 2.013, 1.912, 1.827, 1.752, 1.689, 1.642, 1.586, 1.544, 1.501, 1.468, 1.442, 1.421, 1.409, 1.409, 1.411, 1.421, 1.439, 1.467, 1.504, 1.543, 1.578, 1.629, 1.679, 1.739, 1.815, 1.894, 1.985, 2.098, 2.217, 2.298,
+ 2.273, 2.201, 2.081, 1.974, 1.886, 1.807, 1.741, 1.689, 1.643, 1.603, 1.562, 1.527, 1.504, 1.485, 1.475, 1.474, 1.475, 1.487, 1.503, 1.531, 1.565, 1.601, 1.634, 1.678, 1.728, 1.795, 1.877, 1.961, 2.052, 2.169, 2.298, 2.365,
+ 2.317, 2.273, 2.146, 2.039, 1.946, 1.864, 1.792, 1.737, 1.688, 1.643, 1.603, 1.562, 1.533, 1.525, 1.523, 1.523, 1.523, 1.525, 1.534, 1.565, 1.601, 1.634, 1.677, 1.722, 1.772, 1.848, 1.935, 2.023, 2.108, 2.232, 2.365, 2.403
+ ],
+ "sigma": 0.00285,
+ "sigma_Cb": 0.00166
+ }
+ },
+ {
+ "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": 2873,
+ "ccm":
+ [
+ 1.88195, -0.26249, -0.61946,
+ -0.63842, 2.11535, -0.47693,
+ -0.13531, -0.99739, 2.13271
+ ]
+ },
+ {
+ "ct": 2965,
+ "ccm":
+ [
+ 2.15048, -0.51859, -0.63189,
+ -0.53572, 1.92585, -0.39013,
+ 0.01831, -1.48576, 2.46744
+ ]
+ },
+ {
+ "ct": 3606,
+ "ccm":
+ [
+ 1.97522, -0.43847, -0.53675,
+ -0.56151, 1.99765, -0.43614,
+ -0.12438, -0.77056, 1.89493
+ ]
+ },
+ {
+ "ct": 4700,
+ "ccm":
+ [
+ 2.00971, -0.51461, -0.49511,
+ -0.52109, 2.01003, -0.48894,
+ -0.09527, -0.67318, 1.76845
+ ]
+ },
+ {
+ "ct": 5890,
+ "ccm":
+ [
+ 2.13616, -0.65283, -0.48333,
+ -0.48364, 1.93115, -0.44751,
+ -0.13465, -0.54831, 1.68295
+ ]
+ },
+ {
+ "ct": 7600,
+ "ccm":
+ [
+ 2.06599, -0.39161, -0.67439,
+ -0.50883, 2.27467, -0.76583,
+ -0.13961, -0.66121, 1.80081
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/ov5647_noir.json b/src/ipa/rpi/pisp/data/ov5647_noir.json
new file mode 100644
index 00000000..3e04f21b
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/ov5647_noir.json
@@ -0,0 +1,1121 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 1024
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 29381,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 870,
+ "reference_Y": 12388
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 4.371
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 280,
+ "slope": 0.02153
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 1.0, 2.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 15000, 30000 ],
+ "gain": [ 1.0, 2.0, 2.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 15000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.02,
+ 1000, 0.02
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.01,
+ 1000, 0.01
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.9,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.005,
+ 1000, 0.005
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.19,
+ 1000, 0.19,
+ 10000, 0.19
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.238, 1.238, 1.238, 1.234, 1.227, 1.216, 1.207, 1.198, 1.191, 1.179, 1.169, 1.162, 1.155, 1.153, 1.152, 1.152, 1.152, 1.153, 1.154, 1.157, 1.166, 1.176, 1.183, 1.191, 1.204, 1.216, 1.226, 1.232, 1.239, 1.241, 1.241, 1.242,
+ 1.235, 1.234, 1.227, 1.222, 1.214, 1.203, 1.193, 1.184, 1.169, 1.161, 1.149, 1.139, 1.131, 1.126, 1.122, 1.121, 1.121, 1.123, 1.129, 1.136, 1.145, 1.157, 1.163, 1.175, 1.189, 1.199, 1.212, 1.221, 1.225, 1.231, 1.241, 1.242,
+ 1.234, 1.227, 1.222, 1.214, 1.203, 1.193, 1.183, 1.169, 1.158, 1.145, 1.133, 1.123, 1.113, 1.106, 1.101, 1.101, 1.101, 1.105, 1.111, 1.116, 1.128, 1.137, 1.149, 1.163, 1.174, 1.189, 1.199, 1.212, 1.221, 1.226, 1.234, 1.241,
+ 1.234, 1.226, 1.217, 1.209, 1.195, 1.183, 1.171, 1.158, 1.145, 1.131, 1.119, 1.108, 1.097, 1.088, 1.088, 1.085, 1.085, 1.087, 1.095, 1.102, 1.114, 1.124, 1.137, 1.149, 1.165, 1.176, 1.194, 1.207, 1.214, 1.224, 1.235, 1.247,
+ 1.238, 1.224, 1.213, 1.202, 1.187, 1.175, 1.161, 1.146, 1.132, 1.117, 1.105, 1.094, 1.082, 1.074, 1.071, 1.071, 1.071, 1.073, 1.079, 1.089, 1.099, 1.112, 1.124, 1.137, 1.152, 1.167, 1.183, 1.198, 1.211, 1.222, 1.235, 1.249,
+ 1.232, 1.221, 1.209, 1.195, 1.178, 1.163, 1.149, 1.134, 1.118, 1.104, 1.093, 1.079, 1.069, 1.061, 1.057, 1.056, 1.056, 1.059, 1.066, 1.073, 1.086, 1.098, 1.111, 1.124, 1.141, 1.157, 1.173, 1.188, 1.203, 1.219, 1.234, 1.251,
+ 1.231, 1.213, 1.197, 1.186, 1.169, 1.151, 1.137, 1.121, 1.104, 1.093, 1.079, 1.068, 1.056, 1.048, 1.045, 1.042, 1.042, 1.045, 1.051, 1.061, 1.071, 1.085, 1.098, 1.111, 1.129, 1.145, 1.161, 1.179, 1.197, 1.215, 1.231, 1.249,
+ 1.224, 1.211, 1.194, 1.178, 1.161, 1.141, 1.127, 1.109, 1.094, 1.081, 1.068, 1.055, 1.047, 1.038, 1.034, 1.032, 1.032, 1.035, 1.039, 1.048, 1.059, 1.071, 1.086, 1.098, 1.116, 1.134, 1.154, 1.172, 1.191, 1.209, 1.228, 1.249,
+ 1.223, 1.206, 1.187, 1.171, 1.152, 1.132, 1.117, 1.098, 1.082, 1.069, 1.056, 1.045, 1.037, 1.028, 1.024, 1.022, 1.022, 1.025, 1.031, 1.039, 1.048, 1.059, 1.074, 1.091, 1.106, 1.126, 1.144, 1.163, 1.186, 1.205, 1.227, 1.247,
+ 1.222, 1.199, 1.183, 1.164, 1.143, 1.126, 1.108, 1.091, 1.075, 1.059, 1.045, 1.037, 1.028, 1.019, 1.015, 1.014, 1.014, 1.018, 1.023, 1.031, 1.042, 1.051, 1.065, 1.081, 1.098, 1.118, 1.137, 1.158, 1.181, 1.201, 1.224, 1.245,
+ 1.221, 1.198, 1.179, 1.163, 1.141, 1.119, 1.101, 1.083, 1.066, 1.051, 1.038, 1.028, 1.019, 1.012, 1.009, 1.008, 1.007, 1.008, 1.015, 1.023, 1.033, 1.044, 1.058, 1.072, 1.089, 1.107, 1.131, 1.152, 1.172, 1.196, 1.216, 1.241,
+ 1.216, 1.194, 1.174, 1.155, 1.133, 1.112, 1.094, 1.074, 1.059, 1.045, 1.032, 1.021, 1.012, 1.007, 1.003, 1.002, 1.002, 1.003, 1.008, 1.015, 1.025, 1.038, 1.049, 1.067, 1.084, 1.102, 1.126, 1.147, 1.169, 1.191, 1.214, 1.238,
+ 1.212, 1.188, 1.171, 1.149, 1.127, 1.105, 1.087, 1.069, 1.055, 1.039, 1.027, 1.016, 1.007, 1.003, 0.999, 0.997, 0.998, 1.001, 1.003, 1.011, 1.021, 1.032, 1.043, 1.059, 1.077, 1.101, 1.121, 1.142, 1.164, 1.187, 1.211, 1.236,
+ 1.208, 1.187, 1.169, 1.149, 1.124, 1.104, 1.085, 1.067, 1.051, 1.036, 1.024, 1.013, 1.005, 0.999, 0.996, 0.994, 0.994, 0.996, 1.001, 1.006, 1.017, 1.025, 1.038, 1.053, 1.072, 1.093, 1.116, 1.138, 1.159, 1.183, 1.207, 1.235,
+ 1.208, 1.181, 1.164, 1.144, 1.122, 1.098, 1.079, 1.062, 1.046, 1.033, 1.018, 1.009, 1.002, 0.996, 0.992, 0.989, 0.991, 0.994, 0.996, 1.002, 1.012, 1.021, 1.035, 1.051, 1.069, 1.091, 1.113, 1.137, 1.157, 1.182, 1.206, 1.233,
+ 1.206, 1.179, 1.163, 1.142, 1.119, 1.098, 1.079, 1.061, 1.045, 1.031, 1.017, 1.008, 1.001, 0.995, 0.991, 0.989, 0.989, 0.992, 0.996, 1.001, 1.011, 1.019, 1.034, 1.051, 1.069, 1.089, 1.112, 1.136, 1.157, 1.181, 1.205, 1.233,
+ 1.206, 1.179, 1.163, 1.139, 1.119, 1.098, 1.079, 1.061, 1.044, 1.031, 1.016, 1.007, 1.001, 0.995, 0.991, 0.989, 0.989, 0.991, 0.996, 1.002, 1.011, 1.019, 1.034, 1.049, 1.069, 1.088, 1.113, 1.136, 1.156, 1.179, 1.204, 1.233,
+ 1.207, 1.179, 1.163, 1.139, 1.119, 1.099, 1.079, 1.061, 1.044, 1.031, 1.017, 1.007, 1.001, 0.995, 0.991, 0.989, 0.989, 0.992, 0.997, 1.003, 1.011, 1.021, 1.034, 1.051, 1.071, 1.089, 1.112, 1.136, 1.157, 1.179, 1.204, 1.233,
+ 1.207, 1.179, 1.163, 1.143, 1.121, 1.101, 1.082, 1.063, 1.047, 1.032, 1.019, 1.009, 1.003, 0.998, 0.994, 0.991, 0.991, 0.995, 0.999, 1.004, 1.013, 1.024, 1.038, 1.052, 1.071, 1.091, 1.112, 1.136, 1.159, 1.181, 1.205, 1.233,
+ 1.207, 1.185, 1.166, 1.148, 1.124, 1.104, 1.087, 1.068, 1.052, 1.037, 1.025, 1.016, 1.006, 1.002, 0.998, 0.995, 0.995, 0.999, 1.003, 1.008, 1.017, 1.029, 1.043, 1.056, 1.076, 1.094, 1.116, 1.138, 1.159, 1.183, 1.205, 1.232,
+ 1.211, 1.186, 1.167, 1.151, 1.128, 1.108, 1.089, 1.072, 1.057, 1.042, 1.031, 1.021, 1.013, 1.006, 1.002, 0.999, 0.999, 1.003, 1.007, 1.013, 1.021, 1.031, 1.047, 1.062, 1.081, 1.098, 1.121, 1.141, 1.164, 1.185, 1.207, 1.232,
+ 1.211, 1.188, 1.169, 1.154, 1.134, 1.114, 1.094, 1.078, 1.063, 1.051, 1.039, 1.028, 1.019, 1.013, 1.007, 1.006, 1.006, 1.007, 1.013, 1.019, 1.027, 1.039, 1.051, 1.069, 1.087, 1.105, 1.124, 1.146, 1.165, 1.186, 1.209, 1.232,
+ 1.214, 1.191, 1.175, 1.159, 1.141, 1.123, 1.105, 1.087, 1.072, 1.058, 1.046, 1.036, 1.028, 1.019, 1.014, 1.013, 1.013, 1.015, 1.019, 1.027, 1.037, 1.048, 1.061, 1.076, 1.094, 1.109, 1.132, 1.149, 1.169, 1.189, 1.209, 1.233,
+ 1.219, 1.194, 1.179, 1.163, 1.146, 1.129, 1.113, 1.095, 1.081, 1.066, 1.055, 1.045, 1.036, 1.029, 1.023, 1.021, 1.021, 1.026, 1.031, 1.037, 1.048, 1.057, 1.069, 1.085, 1.101, 1.118, 1.137, 1.156, 1.174, 1.193, 1.213, 1.233,
+ 1.219, 1.199, 1.184, 1.172, 1.155, 1.138, 1.122, 1.104, 1.088, 1.075, 1.065, 1.055, 1.045, 1.038, 1.034, 1.031, 1.031, 1.035, 1.041, 1.048, 1.057, 1.066, 1.081, 1.096, 1.111, 1.125, 1.146, 1.164, 1.178, 1.196, 1.214, 1.233,
+ 1.222, 1.204, 1.189, 1.178, 1.162, 1.148, 1.132, 1.115, 1.101, 1.087, 1.075, 1.064, 1.055, 1.048, 1.043, 1.042, 1.042, 1.046, 1.049, 1.057, 1.066, 1.076, 1.089, 1.106, 1.121, 1.133, 1.149, 1.167, 1.183, 1.199, 1.215, 1.234,
+ 1.222, 1.205, 1.191, 1.184, 1.171, 1.155, 1.142, 1.124, 1.109, 1.097, 1.087, 1.077, 1.065, 1.059, 1.055, 1.053, 1.053, 1.057, 1.059, 1.067, 1.076, 1.088, 1.102, 1.116, 1.131, 1.143, 1.157, 1.175, 1.187, 1.202, 1.215, 1.231,
+ 1.223, 1.211, 1.198, 1.189, 1.178, 1.165, 1.151, 1.136, 1.122, 1.108, 1.097, 1.087, 1.079, 1.073, 1.067, 1.066, 1.066, 1.069, 1.074, 1.079, 1.088, 1.101, 1.114, 1.128, 1.141, 1.152, 1.166, 1.182, 1.194, 1.205, 1.215, 1.229,
+ 1.223, 1.212, 1.204, 1.197, 1.186, 1.173, 1.161, 1.149, 1.133, 1.121, 1.108, 1.101, 1.092, 1.085, 1.082, 1.082, 1.082, 1.085, 1.091, 1.096, 1.101, 1.113, 1.125, 1.138, 1.151, 1.164, 1.175, 1.188, 1.198, 1.207, 1.215, 1.222,
+ 1.217, 1.213, 1.211, 1.203, 1.194, 1.181, 1.169, 1.158, 1.145, 1.133, 1.123, 1.113, 1.106, 1.097, 1.096, 1.094, 1.094, 1.098, 1.104, 1.108, 1.114, 1.124, 1.137, 1.149, 1.161, 1.172, 1.182, 1.194, 1.203, 1.209, 1.211, 1.217,
+ 1.214, 1.211, 1.209, 1.206, 1.201, 1.188, 1.179, 1.168, 1.154, 1.144, 1.136, 1.126, 1.119, 1.112, 1.109, 1.108, 1.108, 1.108, 1.117, 1.119, 1.124, 1.133, 1.147, 1.158, 1.171, 1.178, 1.188, 1.198, 1.205, 1.208, 1.209, 1.211,
+ 1.207, 1.208, 1.209, 1.206, 1.202, 1.192, 1.182, 1.171, 1.159, 1.146, 1.142, 1.136, 1.126, 1.119, 1.116, 1.114, 1.115, 1.117, 1.119, 1.128, 1.129, 1.136, 1.155, 1.162, 1.176, 1.182, 1.188, 1.198, 1.205, 1.208, 1.207, 1.206
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.879, 1.878, 1.872, 1.862, 1.856, 1.842, 1.826, 1.815, 1.811, 1.799, 1.787, 1.777, 1.768, 1.761, 1.761, 1.761, 1.762, 1.763, 1.764, 1.769, 1.776, 1.789, 1.799, 1.807, 1.824, 1.841, 1.853, 1.861, 1.871, 1.874, 1.885, 1.889,
+ 1.879, 1.875, 1.859, 1.846, 1.835, 1.817, 1.806, 1.794, 1.777, 1.771, 1.755, 1.743, 1.733, 1.726, 1.721, 1.721, 1.721, 1.722, 1.729, 1.734, 1.747, 1.759, 1.771, 1.783, 1.801, 1.813, 1.831, 1.841, 1.849, 1.862, 1.876, 1.888,
+ 1.876, 1.861, 1.846, 1.835, 1.817, 1.806, 1.793, 1.777, 1.766, 1.752, 1.736, 1.727, 1.713, 1.702, 1.696, 1.695, 1.695, 1.697, 1.704, 1.715, 1.725, 1.739, 1.754, 1.771, 1.783, 1.801, 1.813, 1.831, 1.841, 1.851, 1.866, 1.888,
+ 1.878, 1.861, 1.843, 1.829, 1.811, 1.794, 1.779, 1.766, 1.751, 1.734, 1.721, 1.711, 1.695, 1.682, 1.679, 1.677, 1.677, 1.678, 1.687, 1.696, 1.713, 1.723, 1.737, 1.754, 1.774, 1.785, 1.811, 1.825, 1.833, 1.849, 1.866, 1.889,
+ 1.882, 1.859, 1.837, 1.821, 1.803, 1.784, 1.769, 1.752, 1.735, 1.717, 1.701, 1.689, 1.676, 1.664, 1.659, 1.658, 1.658, 1.659, 1.668, 1.679, 1.694, 1.711, 1.723, 1.739, 1.756, 1.777, 1.797, 1.813, 1.827, 1.844, 1.865, 1.889,
+ 1.869, 1.849, 1.832, 1.811, 1.792, 1.772, 1.755, 1.737, 1.717, 1.699, 1.688, 1.674, 1.661, 1.646, 1.642, 1.638, 1.638, 1.641, 1.651, 1.659, 1.676, 1.693, 1.708, 1.724, 1.744, 1.763, 1.783, 1.801, 1.819, 1.838, 1.864, 1.889,
+ 1.869, 1.841, 1.817, 1.801, 1.782, 1.758, 1.741, 1.721, 1.699, 1.688, 1.674, 1.658, 1.643, 1.632, 1.627, 1.621, 1.621, 1.622, 1.631, 1.643, 1.658, 1.676, 1.689, 1.708, 1.729, 1.748, 1.767, 1.791, 1.812, 1.836, 1.859, 1.891,
+ 1.861, 1.836, 1.814, 1.792, 1.772, 1.745, 1.728, 1.707, 1.688, 1.673, 1.658, 1.643, 1.629, 1.618, 1.612, 1.609, 1.609, 1.611, 1.615, 1.629, 1.642, 1.658, 1.676, 1.689, 1.711, 1.734, 1.758, 1.782, 1.804, 1.827, 1.859, 1.891,
+ 1.861, 1.829, 1.807, 1.784, 1.759, 1.735, 1.717, 1.692, 1.674, 1.659, 1.644, 1.629, 1.617, 1.605, 1.598, 1.595, 1.595, 1.598, 1.607, 1.615, 1.631, 1.642, 1.661, 1.681, 1.701, 1.724, 1.746, 1.771, 1.799, 1.825, 1.857, 1.891,
+ 1.861, 1.826, 1.804, 1.779, 1.749, 1.729, 1.707, 1.687, 1.665, 1.648, 1.629, 1.617, 1.604, 1.595, 1.589, 1.585, 1.585, 1.592, 1.597, 1.607, 1.623, 1.635, 1.652, 1.674, 1.693, 1.716, 1.739, 1.766, 1.794, 1.822, 1.855, 1.889,
+ 1.861, 1.824, 1.799, 1.777, 1.748, 1.723, 1.701, 1.678, 1.657, 1.639, 1.619, 1.605, 1.596, 1.586, 1.581, 1.579, 1.577, 1.579, 1.588, 1.597, 1.612, 1.625, 1.641, 1.661, 1.681, 1.702, 1.732, 1.757, 1.785, 1.813, 1.847, 1.882,
+ 1.856, 1.819, 1.796, 1.767, 1.739, 1.714, 1.693, 1.666, 1.651, 1.629, 1.613, 1.597, 1.586, 1.579, 1.576, 1.572, 1.572, 1.573, 1.579, 1.588, 1.602, 1.619, 1.633, 1.655, 1.674, 1.698, 1.729, 1.754, 1.782, 1.809, 1.842, 1.874,
+ 1.853, 1.815, 1.792, 1.761, 1.734, 1.707, 1.682, 1.659, 1.639, 1.622, 1.605, 1.591, 1.579, 1.574, 1.569, 1.565, 1.566, 1.569, 1.573, 1.584, 1.597, 1.609, 1.624, 1.645, 1.666, 1.695, 1.722, 1.746, 1.772, 1.799, 1.835, 1.873,
+ 1.847, 1.811, 1.789, 1.759, 1.732, 1.703, 1.681, 1.657, 1.637, 1.619, 1.603, 1.588, 1.575, 1.569, 1.563, 1.561, 1.561, 1.563, 1.569, 1.576, 1.589, 1.601, 1.616, 1.636, 1.659, 1.686, 1.712, 1.741, 1.767, 1.798, 1.832, 1.873,
+ 1.847, 1.803, 1.779, 1.756, 1.727, 1.699, 1.674, 1.652, 1.632, 1.616, 1.595, 1.583, 1.572, 1.564, 1.558, 1.556, 1.557, 1.559, 1.563, 1.569, 1.583, 1.593, 1.613, 1.633, 1.657, 1.684, 1.709, 1.741, 1.766, 1.796, 1.831, 1.871,
+ 1.845, 1.802, 1.779, 1.755, 1.725, 1.696, 1.673, 1.649, 1.629, 1.614, 1.595, 1.582, 1.572, 1.563, 1.557, 1.556, 1.556, 1.558, 1.562, 1.569, 1.581, 1.593, 1.612, 1.633, 1.656, 1.679, 1.709, 1.741, 1.764, 1.796, 1.828, 1.869,
+ 1.845, 1.801, 1.779, 1.749, 1.723, 1.697, 1.673, 1.649, 1.627, 1.613, 1.593, 1.581, 1.573, 1.563, 1.558, 1.555, 1.555, 1.556, 1.562, 1.573, 1.581, 1.594, 1.611, 1.633, 1.656, 1.679, 1.711, 1.739, 1.764, 1.794, 1.828, 1.869,
+ 1.844, 1.801, 1.781, 1.749, 1.723, 1.697, 1.673, 1.649, 1.627, 1.614, 1.595, 1.581, 1.574, 1.564, 1.559, 1.557, 1.556, 1.559, 1.564, 1.574, 1.582, 1.595, 1.611, 1.634, 1.659, 1.683, 1.709, 1.739, 1.765, 1.794, 1.829, 1.872,
+ 1.845, 1.802, 1.781, 1.754, 1.725, 1.701, 1.677, 1.652, 1.632, 1.616, 1.599, 1.586, 1.576, 1.569, 1.563, 1.559, 1.558, 1.562, 1.569, 1.576, 1.587, 1.599, 1.618, 1.635, 1.661, 1.685, 1.709, 1.739, 1.767, 1.796, 1.829, 1.868,
+ 1.845, 1.809, 1.785, 1.762, 1.731, 1.706, 1.685, 1.659, 1.641, 1.622, 1.606, 1.595, 1.581, 1.575, 1.569, 1.564, 1.564, 1.569, 1.574, 1.582, 1.594, 1.607, 1.625, 1.642, 1.668, 1.687, 1.716, 1.741, 1.769, 1.798, 1.829, 1.868,
+ 1.849, 1.811, 1.785, 1.765, 1.734, 1.709, 1.688, 1.666, 1.647, 1.628, 1.613, 1.601, 1.592, 1.581, 1.575, 1.572, 1.572, 1.575, 1.581, 1.589, 1.599, 1.611, 1.631, 1.649, 1.673, 1.694, 1.721, 1.747, 1.771, 1.798, 1.829, 1.868,
+ 1.849, 1.816, 1.787, 1.766, 1.739, 1.716, 1.692, 1.673, 1.657, 1.641, 1.625, 1.612, 1.599, 1.592, 1.584, 1.581, 1.581, 1.581, 1.589, 1.598, 1.608, 1.622, 1.639, 1.659, 1.679, 1.701, 1.724, 1.751, 1.774, 1.802, 1.832, 1.868,
+ 1.855, 1.816, 1.793, 1.773, 1.748, 1.727, 1.707, 1.686, 1.667, 1.649, 1.636, 1.623, 1.612, 1.599, 1.594, 1.592, 1.591, 1.591, 1.598, 1.608, 1.621, 1.634, 1.649, 1.669, 1.693, 1.705, 1.736, 1.757, 1.778, 1.804, 1.833, 1.867,
+ 1.858, 1.818, 1.796, 1.778, 1.754, 1.733, 1.716, 1.695, 1.676, 1.661, 1.648, 1.635, 1.624, 1.613, 1.604, 1.601, 1.601, 1.606, 1.613, 1.621, 1.634, 1.646, 1.661, 1.679, 1.699, 1.714, 1.742, 1.761, 1.782, 1.809, 1.835, 1.867,
+ 1.857, 1.822, 1.801, 1.789, 1.766, 1.744, 1.726, 1.706, 1.688, 1.671, 1.659, 1.647, 1.635, 1.624, 1.621, 1.617, 1.617, 1.621, 1.627, 1.634, 1.645, 1.656, 1.674, 1.694, 1.709, 1.723, 1.751, 1.771, 1.786, 1.811, 1.837, 1.867,
+ 1.858, 1.824, 1.807, 1.794, 1.774, 1.757, 1.739, 1.716, 1.702, 1.687, 1.671, 1.662, 1.648, 1.636, 1.629, 1.629, 1.629, 1.633, 1.635, 1.646, 1.656, 1.669, 1.684, 1.705, 1.719, 1.732, 1.753, 1.774, 1.793, 1.815, 1.837, 1.871,
+ 1.858, 1.827, 1.809, 1.798, 1.782, 1.761, 1.749, 1.727, 1.711, 1.698, 1.687, 1.675, 1.663, 1.649, 1.646, 1.643, 1.643, 1.646, 1.649, 1.658, 1.669, 1.683, 1.698, 1.716, 1.731, 1.746, 1.761, 1.783, 1.795, 1.817, 1.836, 1.862,
+ 1.862, 1.834, 1.816, 1.805, 1.789, 1.774, 1.759, 1.743, 1.725, 1.711, 1.697, 1.688, 1.678, 1.668, 1.661, 1.659, 1.658, 1.659, 1.668, 1.673, 1.684, 1.698, 1.713, 1.728, 1.742, 1.757, 1.771, 1.791, 1.804, 1.821, 1.836, 1.862,
+ 1.859, 1.835, 1.825, 1.813, 1.794, 1.782, 1.771, 1.757, 1.739, 1.725, 1.711, 1.701, 1.693, 1.683, 1.679, 1.679, 1.679, 1.683, 1.689, 1.693, 1.698, 1.714, 1.726, 1.741, 1.754, 1.769, 1.781, 1.797, 1.808, 1.821, 1.835, 1.856,
+ 1.848, 1.836, 1.832, 1.822, 1.806, 1.789, 1.778, 1.765, 1.751, 1.739, 1.726, 1.718, 1.709, 1.699, 1.696, 1.695, 1.695, 1.696, 1.704, 1.705, 1.714, 1.724, 1.739, 1.753, 1.765, 1.777, 1.789, 1.803, 1.816, 1.824, 1.829, 1.842,
+ 1.839, 1.835, 1.834, 1.829, 1.815, 1.801, 1.787, 1.776, 1.759, 1.751, 1.744, 1.736, 1.724, 1.714, 1.711, 1.708, 1.707, 1.707, 1.717, 1.719, 1.724, 1.734, 1.748, 1.762, 1.775, 1.783, 1.796, 1.808, 1.819, 1.825, 1.828, 1.833,
+ 1.836, 1.833, 1.834, 1.832, 1.821, 1.806, 1.792, 1.785, 1.772, 1.759, 1.751, 1.744, 1.736, 1.725, 1.719, 1.715, 1.715, 1.718, 1.721, 1.728, 1.734, 1.736, 1.757, 1.768, 1.779, 1.787, 1.799, 1.812, 1.821, 1.824, 1.825, 1.833
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 2.189, 2.127, 2.115, 2.106, 2.113, 2.119, 2.131, 2.144, 2.155, 2.168, 2.176, 2.179, 2.181, 2.181, 2.185, 2.187, 2.187, 2.183, 2.179, 2.176, 2.169, 2.167, 2.159, 2.152, 2.145, 2.141, 2.135, 2.128, 2.124, 2.124, 2.139, 2.177,
+ 2.176, 2.133, 2.116, 2.112, 2.116, 2.125, 2.137, 2.154, 2.168, 2.179, 2.187, 2.194, 2.201, 2.204, 2.208, 2.208, 2.205, 2.202, 2.198, 2.195, 2.183, 2.177, 2.166, 2.159, 2.149, 2.143, 2.138, 2.132, 2.124, 2.125, 2.136, 2.164,
+ 2.175, 2.133, 2.117, 2.115, 2.121, 2.136, 2.154, 2.165, 2.179, 2.192, 2.198, 2.211, 2.218, 2.219, 2.221, 2.221, 2.217, 2.216, 2.211, 2.202, 2.197, 2.188, 2.181, 2.171, 2.159, 2.151, 2.141, 2.136, 2.125, 2.125, 2.132, 2.155,
+ 2.172, 2.128, 2.116, 2.116, 2.124, 2.143, 2.161, 2.177, 2.192, 2.204, 2.213, 2.221, 2.227, 2.231, 2.237, 2.237, 2.229, 2.224, 2.221, 2.213, 2.207, 2.197, 2.191, 2.179, 2.169, 2.156, 2.148, 2.138, 2.126, 2.123, 2.124, 2.149,
+ 2.169, 2.124, 2.119, 2.119, 2.135, 2.152, 2.174, 2.187, 2.204, 2.211, 2.224, 2.233, 2.236, 2.241, 2.246, 2.246, 2.243, 2.237, 2.234, 2.226, 2.218, 2.211, 2.199, 2.191, 2.177, 2.166, 2.155, 2.139, 2.129, 2.121, 2.121, 2.143,
+ 2.157, 2.124, 2.121, 2.127, 2.145, 2.157, 2.181, 2.197, 2.208, 2.221, 2.238, 2.245, 2.249, 2.249, 2.254, 2.254, 2.249, 2.247, 2.243, 2.237, 2.228, 2.219, 2.209, 2.198, 2.186, 2.172, 2.161, 2.143, 2.129, 2.121, 2.121, 2.141,
+ 2.157, 2.124, 2.124, 2.131, 2.148, 2.161, 2.188, 2.202, 2.214, 2.238, 2.246, 2.251, 2.255, 2.257, 2.259, 2.259, 2.257, 2.252, 2.251, 2.247, 2.238, 2.231, 2.219, 2.204, 2.193, 2.173, 2.166, 2.152, 2.134, 2.119, 2.119, 2.131,
+ 2.155, 2.125, 2.125, 2.135, 2.151, 2.169, 2.191, 2.207, 2.219, 2.243, 2.253, 2.258, 2.261, 2.266, 2.266, 2.267, 2.265, 2.262, 2.261, 2.254, 2.244, 2.238, 2.228, 2.212, 2.197, 2.179, 2.167, 2.158, 2.137, 2.122, 2.121, 2.131,
+ 2.155, 2.127, 2.127, 2.137, 2.153, 2.173, 2.197, 2.213, 2.231, 2.248, 2.257, 2.266, 2.271, 2.272, 2.274, 2.275, 2.275, 2.273, 2.271, 2.266, 2.257, 2.251, 2.238, 2.227, 2.209, 2.195, 2.175, 2.159, 2.141, 2.128, 2.127, 2.131,
+ 2.155, 2.128, 2.128, 2.139, 2.159, 2.182, 2.206, 2.225, 2.243, 2.252, 2.265, 2.272, 2.277, 2.283, 2.286, 2.284, 2.283, 2.282, 2.274, 2.272, 2.266, 2.256, 2.244, 2.238, 2.221, 2.202, 2.186, 2.169, 2.149, 2.129, 2.129, 2.135,
+ 2.154, 2.131, 2.131, 2.149, 2.166, 2.189, 2.211, 2.234, 2.248, 2.262, 2.272, 2.277, 2.287, 2.291, 2.293, 2.292, 2.291, 2.285, 2.284, 2.279, 2.272, 2.263, 2.254, 2.243, 2.226, 2.206, 2.193, 2.174, 2.153, 2.133, 2.133, 2.135,
+ 2.153, 2.135, 2.135, 2.151, 2.172, 2.198, 2.221, 2.238, 2.255, 2.265, 2.274, 2.287, 2.291, 2.296, 2.298, 2.298, 2.301, 2.297, 2.289, 2.285, 2.277, 2.271, 2.261, 2.251, 2.236, 2.216, 2.199, 2.179, 2.158, 2.135, 2.134, 2.135,
+ 2.152, 2.136, 2.136, 2.154, 2.176, 2.199, 2.224, 2.239, 2.256, 2.267, 2.282, 2.289, 2.295, 2.299, 2.303, 2.303, 2.302, 2.299, 2.297, 2.288, 2.284, 2.274, 2.262, 2.253, 2.238, 2.219, 2.202, 2.181, 2.158, 2.137, 2.135, 2.135,
+ 2.143, 2.134, 2.134, 2.154, 2.177, 2.201, 2.224, 2.241, 2.256, 2.271, 2.282, 2.289, 2.297, 2.302, 2.306, 2.306, 2.304, 2.301, 2.298, 2.289, 2.287, 2.272, 2.265, 2.255, 2.241, 2.221, 2.203, 2.183, 2.164, 2.141, 2.136, 2.135,
+ 2.142, 2.133, 2.133, 2.155, 2.178, 2.202, 2.223, 2.243, 2.258, 2.273, 2.283, 2.288, 2.296, 2.299, 2.306, 2.306, 2.301, 2.299, 2.296, 2.289, 2.286, 2.271, 2.267, 2.256, 2.244, 2.219, 2.206, 2.188, 2.163, 2.141, 2.137, 2.134,
+ 2.141, 2.131, 2.131, 2.153, 2.179, 2.202, 2.224, 2.242, 2.254, 2.274, 2.283, 2.288, 2.295, 2.298, 2.301, 2.301, 2.301, 2.296, 2.295, 2.289, 2.285, 2.271, 2.267, 2.257, 2.246, 2.223, 2.204, 2.188, 2.165, 2.141, 2.136, 2.134,
+ 2.141, 2.133, 2.133, 2.151, 2.179, 2.201, 2.224, 2.241, 2.254, 2.275, 2.283, 2.288, 2.294, 2.296, 2.298, 2.297, 2.295, 2.295, 2.294, 2.291, 2.284, 2.272, 2.267, 2.256, 2.248, 2.225, 2.208, 2.192, 2.167, 2.141, 2.137, 2.134,
+ 2.141, 2.132, 2.132, 2.151, 2.177, 2.199, 2.221, 2.238, 2.252, 2.274, 2.281, 2.287, 2.293, 2.295, 2.296, 2.294, 2.295, 2.295, 2.294, 2.291, 2.284, 2.274, 2.266, 2.257, 2.248, 2.226, 2.206, 2.189, 2.167, 2.143, 2.141, 2.141,
+ 2.141, 2.133, 2.133, 2.153, 2.175, 2.201, 2.221, 2.238, 2.252, 2.271, 2.278, 2.284, 2.288, 2.291, 2.292, 2.291, 2.293, 2.293, 2.293, 2.287, 2.279, 2.275, 2.266, 2.256, 2.243, 2.224, 2.206, 2.189, 2.168, 2.146, 2.142, 2.134,
+ 2.137, 2.131, 2.131, 2.154, 2.173, 2.199, 2.221, 2.236, 2.251, 2.267, 2.272, 2.278, 2.284, 2.287, 2.288, 2.286, 2.288, 2.288, 2.288, 2.283, 2.277, 2.273, 2.265, 2.256, 2.241, 2.219, 2.205, 2.187, 2.167, 2.144, 2.137, 2.132,
+ 2.136, 2.131, 2.131, 2.152, 2.169, 2.197, 2.218, 2.233, 2.246, 2.257, 2.269, 2.274, 2.281, 2.284, 2.286, 2.285, 2.286, 2.286, 2.286, 2.279, 2.274, 2.269, 2.263, 2.254, 2.239, 2.217, 2.203, 2.181, 2.162, 2.143, 2.133, 2.131,
+ 2.136, 2.131, 2.131, 2.151, 2.167, 2.189, 2.205, 2.226, 2.242, 2.253, 2.261, 2.271, 2.275, 2.279, 2.283, 2.283, 2.284, 2.284, 2.281, 2.277, 2.271, 2.264, 2.257, 2.246, 2.232, 2.215, 2.195, 2.176, 2.158, 2.141, 2.131, 2.128,
+ 2.136, 2.129, 2.131, 2.147, 2.162, 2.181, 2.203, 2.219, 2.236, 2.246, 2.256, 2.263, 2.271, 2.274, 2.278, 2.278, 2.276, 2.277, 2.276, 2.273, 2.266, 2.258, 2.251, 2.241, 2.227, 2.198, 2.191, 2.169, 2.154, 2.136, 2.125, 2.122,
+ 2.132, 2.126, 2.126, 2.139, 2.153, 2.168, 2.194, 2.212, 2.224, 2.238, 2.251, 2.258, 2.263, 2.266, 2.269, 2.271, 2.269, 2.269, 2.269, 2.267, 2.259, 2.253, 2.245, 2.237, 2.219, 2.196, 2.179, 2.162, 2.149, 2.132, 2.122, 2.121,
+ 2.124, 2.119, 2.121, 2.137, 2.147, 2.164, 2.183, 2.199, 2.219, 2.231, 2.239, 2.251, 2.257, 2.261, 2.262, 2.262, 2.259, 2.259, 2.261, 2.258, 2.253, 2.245, 2.237, 2.224, 2.209, 2.187, 2.174, 2.157, 2.141, 2.122, 2.121, 2.121,
+ 2.123, 2.115, 2.115, 2.131, 2.138, 2.157, 2.174, 2.188, 2.207, 2.221, 2.233, 2.239, 2.243, 2.244, 2.244, 2.244, 2.246, 2.245, 2.246, 2.244, 2.241, 2.231, 2.224, 2.212, 2.195, 2.176, 2.159, 2.145, 2.128, 2.117, 2.117, 2.123,
+ 2.123, 2.113, 2.113, 2.123, 2.132, 2.141, 2.162, 2.177, 2.191, 2.208, 2.221, 2.231, 2.231, 2.232, 2.234, 2.235, 2.235, 2.235, 2.238, 2.237, 2.225, 2.214, 2.209, 2.199, 2.181, 2.164, 2.146, 2.135, 2.123, 2.116, 2.116, 2.115,
+ 2.129, 2.115, 2.115, 2.121, 2.128, 2.135, 2.149, 2.164, 2.178, 2.193, 2.207, 2.221, 2.222, 2.222, 2.223, 2.224, 2.224, 2.224, 2.224, 2.223, 2.214, 2.205, 2.196, 2.185, 2.171, 2.151, 2.141, 2.129, 2.119, 2.116, 2.116, 2.117,
+ 2.137, 2.119, 2.119, 2.119, 2.122, 2.129, 2.141, 2.159, 2.167, 2.182, 2.195, 2.206, 2.211, 2.216, 2.218, 2.219, 2.219, 2.219, 2.217, 2.212, 2.202, 2.194, 2.184, 2.174, 2.162, 2.145, 2.134, 2.124, 2.118, 2.117, 2.118, 2.121,
+ 2.138, 2.131, 2.121, 2.122, 2.125, 2.128, 2.137, 2.154, 2.162, 2.176, 2.187, 2.194, 2.196, 2.198, 2.205, 2.205, 2.202, 2.202, 2.203, 2.201, 2.191, 2.182, 2.174, 2.162, 2.149, 2.136, 2.126, 2.121, 2.119, 2.118, 2.127, 2.133,
+ 2.157, 2.148, 2.131, 2.129, 2.129, 2.136, 2.148, 2.157, 2.169, 2.177, 2.182, 2.187, 2.188, 2.191, 2.193, 2.193, 2.192, 2.199, 2.201, 2.199, 2.186, 2.178, 2.167, 2.152, 2.146, 2.137, 2.126, 2.124, 2.121, 2.126, 2.133, 2.151,
+ 2.161, 2.157, 2.148, 2.147, 2.147, 2.147, 2.154, 2.162, 2.174, 2.179, 2.181, 2.184, 2.186, 2.187, 2.189, 2.189, 2.187, 2.188, 2.199, 2.201, 2.187, 2.178, 2.163, 2.148, 2.145, 2.141, 2.131, 2.129, 2.128, 2.135, 2.151, 2.153
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.191, 1.165, 1.156, 1.155, 1.157, 1.161, 1.168, 1.176, 1.179, 1.185, 1.187, 1.189, 1.189, 1.189, 1.191, 1.191, 1.191, 1.189, 1.188, 1.188, 1.185, 1.184, 1.182, 1.178, 1.173, 1.171, 1.166, 1.163, 1.159, 1.159, 1.164, 1.187,
+ 1.188, 1.164, 1.157, 1.156, 1.158, 1.166, 1.173, 1.179, 1.185, 1.193, 1.195, 1.198, 1.199, 1.201, 1.201, 1.202, 1.201, 1.199, 1.199, 1.196, 1.194, 1.189, 1.185, 1.182, 1.177, 1.172, 1.168, 1.164, 1.161, 1.161, 1.162, 1.181,
+ 1.184, 1.164, 1.157, 1.157, 1.161, 1.171, 1.179, 1.185, 1.193, 1.197, 1.201, 1.206, 1.208, 1.209, 1.209, 1.208, 1.207, 1.207, 1.207, 1.202, 1.199, 1.195, 1.192, 1.189, 1.182, 1.176, 1.171, 1.166, 1.161, 1.159, 1.161, 1.177,
+ 1.183, 1.162, 1.158, 1.158, 1.163, 1.174, 1.182, 1.191, 1.197, 1.203, 1.208, 1.212, 1.214, 1.214, 1.218, 1.218, 1.214, 1.212, 1.211, 1.208, 1.206, 1.201, 1.197, 1.192, 1.189, 1.179, 1.174, 1.168, 1.162, 1.159, 1.159, 1.173,
+ 1.181, 1.159, 1.159, 1.159, 1.168, 1.178, 1.189, 1.196, 1.204, 1.208, 1.213, 1.217, 1.219, 1.221, 1.222, 1.222, 1.222, 1.221, 1.219, 1.215, 1.212, 1.208, 1.202, 1.197, 1.189, 1.183, 1.178, 1.169, 1.163, 1.158, 1.158, 1.169,
+ 1.174, 1.159, 1.159, 1.164, 1.172, 1.179, 1.192, 1.201, 1.208, 1.212, 1.219, 1.224, 1.225, 1.227, 1.228, 1.228, 1.226, 1.225, 1.224, 1.221, 1.217, 1.212, 1.208, 1.202, 1.194, 1.187, 1.181, 1.172, 1.164, 1.157, 1.157, 1.169,
+ 1.174, 1.159, 1.159, 1.165, 1.174, 1.184, 1.197, 1.205, 1.209, 1.219, 1.224, 1.228, 1.231, 1.231, 1.231, 1.231, 1.229, 1.229, 1.228, 1.226, 1.222, 1.218, 1.212, 1.205, 1.199, 1.188, 1.181, 1.175, 1.165, 1.157, 1.157, 1.163,
+ 1.173, 1.159, 1.159, 1.165, 1.176, 1.186, 1.198, 1.207, 1.213, 1.223, 1.229, 1.231, 1.235, 1.236, 1.236, 1.236, 1.236, 1.235, 1.234, 1.232, 1.226, 1.223, 1.218, 1.209, 1.201, 1.192, 1.183, 1.178, 1.165, 1.157, 1.157, 1.163,
+ 1.172, 1.159, 1.159, 1.166, 1.176, 1.188, 1.201, 1.209, 1.217, 1.227, 1.231, 1.236, 1.238, 1.239, 1.241, 1.242, 1.242, 1.241, 1.239, 1.235, 1.232, 1.227, 1.223, 1.215, 1.208, 1.199, 1.187, 1.179, 1.167, 1.159, 1.159, 1.163,
+ 1.172, 1.159, 1.159, 1.166, 1.177, 1.189, 1.203, 1.212, 1.223, 1.228, 1.236, 1.239, 1.242, 1.245, 1.246, 1.246, 1.247, 1.246, 1.242, 1.241, 1.237, 1.232, 1.226, 1.223, 1.213, 1.202, 1.191, 1.182, 1.172, 1.159, 1.159, 1.163,
+ 1.168, 1.158, 1.158, 1.167, 1.179, 1.192, 1.204, 1.218, 1.225, 1.233, 1.238, 1.242, 1.246, 1.248, 1.251, 1.251, 1.249, 1.248, 1.247, 1.244, 1.239, 1.237, 1.228, 1.223, 1.214, 1.203, 1.194, 1.183, 1.173, 1.161, 1.161, 1.162,
+ 1.166, 1.158, 1.158, 1.168, 1.183, 1.195, 1.207, 1.218, 1.226, 1.233, 1.239, 1.246, 1.248, 1.251, 1.254, 1.254, 1.254, 1.251, 1.249, 1.247, 1.242, 1.239, 1.232, 1.227, 1.219, 1.207, 1.195, 1.186, 1.175, 1.162, 1.161, 1.162,
+ 1.165, 1.158, 1.158, 1.168, 1.183, 1.196, 1.208, 1.219, 1.227, 1.234, 1.241, 1.247, 1.251, 1.254, 1.255, 1.256, 1.256, 1.254, 1.252, 1.249, 1.246, 1.241, 1.234, 1.228, 1.221, 1.211, 1.199, 1.187, 1.175, 1.163, 1.162, 1.162,
+ 1.161, 1.158, 1.158, 1.169, 1.183, 1.196, 1.208, 1.217, 1.227, 1.234, 1.241, 1.247, 1.253, 1.254, 1.256, 1.257, 1.256, 1.255, 1.253, 1.249, 1.247, 1.241, 1.236, 1.229, 1.221, 1.211, 1.199, 1.189, 1.176, 1.164, 1.163, 1.162,
+ 1.161, 1.156, 1.156, 1.169, 1.183, 1.196, 1.207, 1.218, 1.227, 1.235, 1.241, 1.246, 1.252, 1.254, 1.256, 1.257, 1.256, 1.254, 1.253, 1.249, 1.247, 1.241, 1.237, 1.231, 1.223, 1.211, 1.201, 1.191, 1.177, 1.164, 1.164, 1.161,
+ 1.161, 1.155, 1.155, 1.169, 1.182, 1.195, 1.208, 1.216, 1.225, 1.235, 1.241, 1.245, 1.249, 1.252, 1.254, 1.254, 1.254, 1.253, 1.252, 1.249, 1.246, 1.239, 1.237, 1.231, 1.224, 1.211, 1.201, 1.191, 1.178, 1.164, 1.162, 1.161,
+ 1.159, 1.155, 1.155, 1.168, 1.181, 1.195, 1.208, 1.217, 1.223, 1.235, 1.241, 1.244, 1.248, 1.251, 1.252, 1.252, 1.252, 1.252, 1.251, 1.248, 1.245, 1.241, 1.236, 1.231, 1.224, 1.212, 1.202, 1.191, 1.179, 1.164, 1.162, 1.161,
+ 1.158, 1.154, 1.154, 1.167, 1.181, 1.194, 1.206, 1.216, 1.222, 1.234, 1.237, 1.242, 1.245, 1.248, 1.251, 1.249, 1.249, 1.249, 1.249, 1.248, 1.244, 1.241, 1.235, 1.229, 1.223, 1.213, 1.202, 1.191, 1.179, 1.167, 1.163, 1.163,
+ 1.158, 1.154, 1.154, 1.168, 1.181, 1.194, 1.206, 1.215, 1.223, 1.231, 1.236, 1.239, 1.243, 1.245, 1.246, 1.246, 1.248, 1.248, 1.248, 1.245, 1.242, 1.239, 1.235, 1.229, 1.223, 1.213, 1.202, 1.191, 1.179, 1.167, 1.163, 1.162,
+ 1.157, 1.154, 1.154, 1.168, 1.179, 1.194, 1.205, 1.215, 1.222, 1.229, 1.233, 1.236, 1.239, 1.243, 1.244, 1.244, 1.245, 1.245, 1.244, 1.243, 1.239, 1.236, 1.234, 1.229, 1.222, 1.211, 1.202, 1.191, 1.179, 1.166, 1.163, 1.161,
+ 1.156, 1.155, 1.155, 1.168, 1.179, 1.193, 1.205, 1.213, 1.219, 1.225, 1.231, 1.234, 1.238, 1.239, 1.241, 1.243, 1.243, 1.243, 1.243, 1.239, 1.237, 1.235, 1.231, 1.228, 1.221, 1.209, 1.199, 1.189, 1.178, 1.166, 1.162, 1.159,
+ 1.156, 1.156, 1.157, 1.167, 1.178, 1.191, 1.199, 1.209, 1.217, 1.223, 1.226, 1.231, 1.233, 1.236, 1.239, 1.239, 1.241, 1.241, 1.239, 1.237, 1.235, 1.232, 1.229, 1.224, 1.217, 1.209, 1.196, 1.187, 1.176, 1.165, 1.159, 1.157,
+ 1.157, 1.157, 1.157, 1.166, 1.175, 1.187, 1.198, 1.205, 1.213, 1.219, 1.223, 1.227, 1.231, 1.233, 1.236, 1.236, 1.234, 1.235, 1.235, 1.235, 1.231, 1.229, 1.227, 1.222, 1.216, 1.201, 1.194, 1.184, 1.174, 1.163, 1.157, 1.156,
+ 1.158, 1.155, 1.155, 1.165, 1.172, 1.181, 1.194, 1.202, 1.208, 1.215, 1.221, 1.223, 1.227, 1.229, 1.231, 1.231, 1.231, 1.232, 1.233, 1.231, 1.228, 1.227, 1.223, 1.219, 1.213, 1.199, 1.189, 1.181, 1.171, 1.161, 1.157, 1.156,
+ 1.155, 1.154, 1.154, 1.164, 1.169, 1.179, 1.189, 1.196, 1.203, 1.208, 1.215, 1.221, 1.222, 1.224, 1.225, 1.225, 1.226, 1.228, 1.228, 1.227, 1.225, 1.222, 1.219, 1.213, 1.206, 1.196, 1.187, 1.177, 1.168, 1.159, 1.156, 1.156,
+ 1.155, 1.152, 1.152, 1.162, 1.167, 1.175, 1.185, 1.191, 1.198, 1.205, 1.209, 1.214, 1.216, 1.217, 1.217, 1.217, 1.219, 1.219, 1.219, 1.219, 1.217, 1.215, 1.213, 1.207, 1.199, 1.191, 1.179, 1.172, 1.165, 1.156, 1.155, 1.155,
+ 1.155, 1.152, 1.152, 1.161, 1.163, 1.169, 1.179, 1.186, 1.192, 1.198, 1.204, 1.208, 1.211, 1.211, 1.211, 1.212, 1.212, 1.213, 1.215, 1.215, 1.211, 1.208, 1.205, 1.199, 1.194, 1.185, 1.175, 1.167, 1.161, 1.156, 1.155, 1.153,
+ 1.157, 1.152, 1.152, 1.159, 1.162, 1.166, 1.174, 1.181, 1.187, 1.192, 1.197, 1.203, 1.204, 1.205, 1.204, 1.204, 1.204, 1.205, 1.206, 1.206, 1.204, 1.201, 1.198, 1.194, 1.187, 1.176, 1.171, 1.164, 1.159, 1.156, 1.155, 1.154,
+ 1.159, 1.154, 1.154, 1.158, 1.159, 1.163, 1.171, 1.176, 1.181, 1.187, 1.191, 1.195, 1.198, 1.199, 1.199, 1.201, 1.201, 1.202, 1.202, 1.199, 1.196, 1.193, 1.191, 1.188, 1.182, 1.174, 1.166, 1.162, 1.157, 1.156, 1.156, 1.156,
+ 1.162, 1.161, 1.158, 1.159, 1.159, 1.162, 1.167, 1.173, 1.178, 1.181, 1.186, 1.189, 1.189, 1.191, 1.193, 1.193, 1.193, 1.194, 1.194, 1.194, 1.189, 1.187, 1.186, 1.182, 1.176, 1.167, 1.163, 1.159, 1.158, 1.157, 1.158, 1.161,
+ 1.172, 1.165, 1.162, 1.162, 1.163, 1.166, 1.169, 1.173, 1.178, 1.181, 1.182, 1.185, 1.186, 1.186, 1.186, 1.187, 1.187, 1.189, 1.192, 1.191, 1.187, 1.185, 1.181, 1.177, 1.172, 1.167, 1.163, 1.159, 1.159, 1.161, 1.163, 1.166,
+ 1.173, 1.172, 1.166, 1.165, 1.166, 1.168, 1.171, 1.176, 1.179, 1.182, 1.181, 1.183, 1.185, 1.185, 1.185, 1.185, 1.185, 1.185, 1.191, 1.191, 1.185, 1.181, 1.179, 1.173, 1.169, 1.168, 1.163, 1.162, 1.161, 1.164, 1.166, 1.167
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.271, 2.218, 2.105, 2.004, 1.909, 1.829, 1.762, 1.705, 1.665, 1.629, 1.592, 1.559, 1.528, 1.516, 1.511, 1.511, 1.511, 1.514, 1.525, 1.553, 1.585, 1.617, 1.655, 1.697, 1.752, 1.816, 1.893, 1.982, 2.084, 2.195, 2.321, 2.342,
+ 2.218, 2.166, 2.057, 1.959, 1.871, 1.793, 1.726, 1.675, 1.633, 1.592, 1.559, 1.528, 1.503, 1.484, 1.474, 1.472, 1.472, 1.482, 1.499, 1.523, 1.553, 1.585, 1.619, 1.664, 1.715, 1.779, 1.855, 1.938, 2.037, 2.147, 2.259, 2.321,
+ 2.166, 2.101, 1.997, 1.901, 1.818, 1.743, 1.683, 1.634, 1.588, 1.546, 1.508, 1.476, 1.449, 1.429, 1.418, 1.415, 1.415, 1.425, 1.444, 1.469, 1.501, 1.538, 1.577, 1.622, 1.671, 1.728, 1.799, 1.881, 1.975, 2.078, 2.185, 2.259,
+ 2.101, 2.039, 1.938, 1.848, 1.768, 1.699, 1.641, 1.588, 1.541, 1.494, 1.455, 1.421, 1.394, 1.374, 1.361, 1.357, 1.357, 1.367, 1.388, 1.414, 1.448, 1.485, 1.528, 1.577, 1.626, 1.682, 1.748, 1.827, 1.917, 2.014, 2.119, 2.185,
+ 2.039, 1.979, 1.883, 1.795, 1.722, 1.658, 1.596, 1.541, 1.493, 1.443, 1.401, 1.364, 1.336, 1.316, 1.303, 1.301, 1.301, 1.311, 1.331, 1.359, 1.393, 1.432, 1.482, 1.528, 1.582, 1.641, 1.701, 1.775, 1.861, 1.956, 2.056, 2.119,
+ 1.979, 1.932, 1.836, 1.752, 1.685, 1.621, 1.557, 1.497, 1.443, 1.399, 1.351, 1.314, 1.286, 1.264, 1.253, 1.249, 1.249, 1.259, 1.281, 1.311, 1.344, 1.387, 1.432, 1.484, 1.541, 1.601, 1.662, 1.731, 1.816, 1.908, 2.003, 2.056,
+ 1.934, 1.888, 1.798, 1.719, 1.651, 1.584, 1.519, 1.457, 1.401, 1.351, 1.307, 1.268, 1.239, 1.217, 1.206, 1.203, 1.203, 1.212, 1.234, 1.263, 1.298, 1.344, 1.387, 1.442, 1.502, 1.565, 1.628, 1.693, 1.774, 1.864, 1.956, 2.003,
+ 1.901, 1.851, 1.763, 1.688, 1.618, 1.551, 1.483, 1.419, 1.359, 1.307, 1.268, 1.226, 1.195, 1.175, 1.164, 1.161, 1.161, 1.171, 1.192, 1.221, 1.262, 1.298, 1.346, 1.404, 1.466, 1.532, 1.595, 1.661, 1.738, 1.826, 1.917, 1.956,
+ 1.873, 1.821, 1.734, 1.659, 1.591, 1.519, 1.451, 1.386, 1.324, 1.269, 1.226, 1.192, 1.159, 1.141, 1.127, 1.125, 1.125, 1.135, 1.155, 1.187, 1.221, 1.262, 1.311, 1.368, 1.432, 1.499, 1.566, 1.634, 1.708, 1.793, 1.882, 1.917,
+ 1.847, 1.797, 1.713, 1.639, 1.565, 1.493, 1.422, 1.355, 1.291, 1.238, 1.192, 1.159, 1.128, 1.108, 1.097, 1.094, 1.094, 1.104, 1.125, 1.155, 1.187, 1.229, 1.279, 1.338, 1.403, 1.471, 1.541, 1.611, 1.684, 1.766, 1.853, 1.885,
+ 1.828, 1.772, 1.691, 1.614, 1.539, 1.466, 1.394, 1.325, 1.264, 1.209, 1.163, 1.128, 1.104, 1.081, 1.069, 1.067, 1.067, 1.078, 1.101, 1.125, 1.159, 1.201, 1.252, 1.312, 1.379, 1.447, 1.517, 1.591, 1.665, 1.743, 1.831, 1.862,
+ 1.812, 1.754, 1.677, 1.599, 1.519, 1.445, 1.371, 1.302, 1.239, 1.185, 1.139, 1.104, 1.081, 1.061, 1.048, 1.046, 1.046, 1.058, 1.078, 1.102, 1.136, 1.177, 1.229, 1.289, 1.356, 1.425, 1.497, 1.572, 1.647, 1.724, 1.811, 1.847,
+ 1.798, 1.741, 1.663, 1.585, 1.506, 1.429, 1.353, 1.284, 1.221, 1.167, 1.121, 1.086, 1.061, 1.046, 1.031, 1.029, 1.029, 1.044, 1.058, 1.083, 1.116, 1.159, 1.209, 1.271, 1.338, 1.407, 1.479, 1.557, 1.633, 1.709, 1.792, 1.832,
+ 1.792, 1.727, 1.651, 1.572, 1.494, 1.414, 1.339, 1.269, 1.206, 1.152, 1.106, 1.072, 1.046, 1.031, 1.018, 1.016, 1.016, 1.029, 1.044, 1.069, 1.102, 1.145, 1.196, 1.256, 1.324, 1.394, 1.471, 1.545, 1.624, 1.698, 1.782, 1.825,
+ 1.787, 1.724, 1.647, 1.566, 1.484, 1.407, 1.329, 1.258, 1.196, 1.141, 1.097, 1.062, 1.036, 1.018, 1.012, 1.007, 1.011, 1.016, 1.034, 1.059, 1.093, 1.135, 1.186, 1.246, 1.314, 1.386, 1.461, 1.538, 1.616, 1.691, 1.773, 1.818,
+ 1.786, 1.721, 1.642, 1.562, 1.481, 1.402, 1.325, 1.254, 1.191, 1.137, 1.092, 1.057, 1.031, 1.013, 1.004, 1.001, 1.004, 1.011, 1.028, 1.054, 1.088, 1.129, 1.181, 1.241, 1.308, 1.382, 1.458, 1.535, 1.613, 1.687, 1.769, 1.818,
+ 1.786, 1.721, 1.642, 1.562, 1.481, 1.401, 1.325, 1.253, 1.191, 1.136, 1.091, 1.057, 1.031, 1.013, 1.003, 1.001, 1.001, 1.011, 1.028, 1.054, 1.088, 1.129, 1.181, 1.241, 1.308, 1.382, 1.458, 1.535, 1.613, 1.687, 1.769, 1.818,
+ 1.787, 1.722, 1.643, 1.563, 1.482, 1.402, 1.326, 1.254, 1.192, 1.138, 1.092, 1.057, 1.032, 1.013, 1.006, 1.002, 1.006, 1.012, 1.031, 1.057, 1.092, 1.133, 1.185, 1.243, 1.311, 1.385, 1.461, 1.539, 1.618, 1.691, 1.774, 1.821,
+ 1.789, 1.729, 1.651, 1.571, 1.489, 1.411, 1.334, 1.263, 1.201, 1.147, 1.101, 1.065, 1.038, 1.021, 1.013, 1.009, 1.012, 1.021, 1.038, 1.064, 1.098, 1.141, 1.193, 1.254, 1.321, 1.395, 1.472, 1.549, 1.626, 1.701, 1.785, 1.825,
+ 1.799, 1.739, 1.661, 1.581, 1.502, 1.422, 1.347, 1.277, 1.214, 1.159, 1.111, 1.075, 1.049, 1.037, 1.021, 1.019, 1.021, 1.036, 1.049, 1.076, 1.111, 1.154, 1.207, 1.268, 1.334, 1.408, 1.485, 1.562, 1.639, 1.715, 1.799, 1.837,
+ 1.811, 1.755, 1.676, 1.597, 1.518, 1.439, 1.365, 1.295, 1.231, 1.176, 1.129, 1.093, 1.067, 1.049, 1.038, 1.036, 1.036, 1.049, 1.067, 1.094, 1.129, 1.173, 1.225, 1.286, 1.353, 1.425, 1.501, 1.577, 1.653, 1.729, 1.815, 1.851,
+ 1.829, 1.774, 1.693, 1.615, 1.537, 1.462, 1.387, 1.316, 1.253, 1.198, 1.153, 1.115, 1.091, 1.067, 1.059, 1.056, 1.056, 1.067, 1.092, 1.115, 1.151, 1.196, 1.249, 1.309, 1.375, 1.446, 1.522, 1.595, 1.672, 1.752, 1.839, 1.871,
+ 1.851, 1.801, 1.713, 1.636, 1.561, 1.485, 1.411, 1.342, 1.281, 1.226, 1.179, 1.145, 1.115, 1.091, 1.082, 1.081, 1.082, 1.092, 1.115, 1.143, 1.178, 1.223, 1.276, 1.337, 1.402, 1.472, 1.544, 1.618, 1.691, 1.774, 1.865, 1.896,
+ 1.876, 1.831, 1.739, 1.663, 1.588, 1.513, 1.439, 1.374, 1.312, 1.258, 1.212, 1.179, 1.145, 1.123, 1.113, 1.112, 1.112, 1.122, 1.143, 1.177, 1.211, 1.256, 1.308, 1.368, 1.431, 1.501, 1.572, 1.641, 1.716, 1.802, 1.896, 1.931,
+ 1.909, 1.867, 1.771, 1.691, 1.617, 1.545, 1.474, 1.411, 1.349, 1.296, 1.252, 1.212, 1.182, 1.159, 1.149, 1.148, 1.149, 1.158, 1.179, 1.211, 1.253, 1.293, 1.344, 1.403, 1.465, 1.533, 1.603, 1.669, 1.747, 1.836, 1.931, 1.974,
+ 1.952, 1.905, 1.806, 1.722, 1.651, 1.578, 1.511, 1.448, 1.388, 1.338, 1.296, 1.252, 1.223, 1.201, 1.189, 1.189, 1.189, 1.199, 1.224, 1.253, 1.293, 1.338, 1.384, 1.442, 1.504, 1.571, 1.638, 1.704, 1.782, 1.872, 1.974, 2.025,
+ 2.004, 1.951, 1.849, 1.759, 1.688, 1.619, 1.552, 1.491, 1.435, 1.388, 1.338, 1.301, 1.272, 1.249, 1.238, 1.236, 1.236, 1.248, 1.271, 1.301, 1.338, 1.384, 1.431, 1.484, 1.543, 1.609, 1.675, 1.742, 1.825, 1.919, 2.025, 2.081,
+ 2.062, 2.004, 1.898, 1.805, 1.729, 1.661, 1.597, 1.539, 1.486, 1.435, 1.391, 1.354, 1.326, 1.303, 1.291, 1.289, 1.289, 1.301, 1.323, 1.353, 1.389, 1.431, 1.483, 1.528, 1.585, 1.649, 1.713, 1.787, 1.875, 1.971, 2.081, 2.145,
+ 2.129, 2.062, 1.951, 1.854, 1.774, 1.705, 1.642, 1.586, 1.539, 1.486, 1.445, 1.411, 1.383, 1.361, 1.348, 1.347, 1.348, 1.359, 1.379, 1.409, 1.447, 1.484, 1.528, 1.578, 1.631, 1.691, 1.759, 1.836, 1.928, 2.031, 2.145, 2.217,
+ 2.201, 2.129, 2.013, 1.912, 1.827, 1.752, 1.689, 1.642, 1.586, 1.544, 1.501, 1.468, 1.442, 1.421, 1.409, 1.409, 1.411, 1.421, 1.439, 1.467, 1.504, 1.543, 1.578, 1.629, 1.679, 1.739, 1.815, 1.894, 1.985, 2.098, 2.217, 2.298,
+ 2.273, 2.201, 2.081, 1.974, 1.886, 1.807, 1.741, 1.689, 1.643, 1.603, 1.562, 1.527, 1.504, 1.485, 1.475, 1.474, 1.475, 1.487, 1.503, 1.531, 1.565, 1.601, 1.634, 1.678, 1.728, 1.795, 1.877, 1.961, 2.052, 2.169, 2.298, 2.365,
+ 2.317, 2.273, 2.146, 2.039, 1.946, 1.864, 1.792, 1.737, 1.688, 1.643, 1.603, 1.562, 1.533, 1.525, 1.523, 1.523, 1.523, 1.525, 1.534, 1.565, 1.601, 1.634, 1.677, 1.722, 1.772, 1.848, 1.935, 2.023, 2.108, 2.232, 2.365, 2.403
+ ],
+ "sigma": 0.00285,
+ "sigma_Cb": 0.00166
+ }
+ },
+ {
+ "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":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "short": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/ov64a40.json b/src/ipa/rpi/pisp/data/ov64a40.json
new file mode 100755
index 00000000..d9e263eb
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/ov64a40.json
@@ -0,0 +1,1133 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 17861,
+ "reference_gain": 2.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 1073,
+ "reference_Y": 9022
+ }
+ },
+ {
+ "rpi.dpc":
+ {
+ "strength": 1
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.984
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 215,
+ "slope": 0.01121
+ }
+ },
+ {
+ "rpi.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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": 7700
+ },
+ "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":
+ [
+ 2300.0, 1.0576, 0.4098,
+ 2700.0, 0.7924, 0.4334,
+ 3000.0, 0.7635, 0.4428,
+ 4000.0, 0.6003, 0.5412,
+ 4150.0, 0.5627, 0.5789,
+ 6500.0, 0.4409, 0.7596
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.05597,
+ "transverse_neg": 0.04295
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "channels": [
+ {
+ "comment": "Channel 0 is normal AGC",
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 60000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 10000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 1.5, 2.0, 4.0, 8.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
+ ]
+ },
+ {
+ "comment": "Channel 1 is the HDR short channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 60000 ],
+ "gain": [ 1.0, 1.0, 1.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "highlight": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ],
+ "shadows": [
+ {
+ "bound": "LOWER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ },
+ {
+ "bound": "UPPER",
+ "q_lo": 0.95,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.7,
+ 1000, 0.7
+ ]
+ },
+ {
+ "bound": "LOWER",
+ "q_lo": 0.0,
+ "q_hi": 0.2,
+ "y_target":
+ [
+ 0, 0.002,
+ 1000, 0.002
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 2 is the HDR long channel",
+ "desaturate": 0,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 30000, 60000 ],
+ "gain": [ 1.0, 2.0, 4.0, 8.0 ]
+ }
+ },
+ "constraint_modes":
+ {
+ "normal": [ ],
+ "highlight": [ ],
+ "shadows": [ ]
+ },
+ "channel_constraints": [
+ {
+ "bound": "UPPER",
+ "channel": 4,
+ "factor": 8
+ },
+ {
+ "bound": "LOWER",
+ "channel": 4,
+ "factor": 2
+ }
+ ],
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "comment": "Channel 3 is the night mode channel",
+ "base_ev": 0.33,
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 20000, 66666 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 20000, 33333 ],
+ "gain": [ 1.0, 2.0, 4.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 100, 20000, 66666, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 4.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.98,
+ "q_hi": 1.0,
+ "y_target":
+ [
+ 0, 0.5,
+ 1000, 0.5
+ ]
+ }
+ ]
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.16,
+ 10000, 0.17
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.alsc":
+ {
+ "omega": 1.3,
+ "n_iter": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 6500,
+ "table":
+ [
+ 2.447, 2.441, 2.423, 2.414, 2.401, 2.391, 2.379, 2.376, 2.375, 2.369, 2.364, 2.362, 2.359, 2.356, 2.351, 2.349, 2.349, 2.341, 2.336, 2.334, 2.332, 2.331, 2.331, 2.331, 2.333, 2.338, 2.342, 2.347, 2.359, 2.368, 2.378, 2.391,
+ 2.447, 2.441, 2.422, 2.408, 2.393, 2.388, 2.382, 2.376, 2.369, 2.365, 2.362, 2.359, 2.353, 2.351, 2.345, 2.345, 2.336, 2.336, 2.334, 2.327, 2.325, 2.326, 2.326, 2.329, 2.329, 2.334, 2.338, 2.347, 2.359, 2.368, 2.378, 2.389,
+ 2.446, 2.433, 2.414, 2.403, 2.393, 2.387, 2.382, 2.371, 2.369, 2.362, 2.356, 2.351, 2.335, 2.335, 2.329, 2.326, 2.318, 2.314, 2.313, 2.312, 2.312, 2.312, 2.318, 2.321, 2.326, 2.329, 2.334, 2.344, 2.357, 2.368, 2.378, 2.389,
+ 2.443, 2.431, 2.409, 2.402, 2.393, 2.383, 2.374, 2.369, 2.363, 2.356, 2.343, 2.335, 2.322, 2.317, 2.311, 2.309, 2.302, 2.299, 2.298, 2.295, 2.295, 2.296, 2.309, 2.313, 2.321, 2.326, 2.329, 2.341, 2.352, 2.364, 2.374, 2.385,
+ 2.442, 2.427, 2.409, 2.399, 2.393, 2.383, 2.371, 2.364, 2.356, 2.343, 2.333, 2.319, 2.313, 2.302, 2.297, 2.295, 2.293, 2.288, 2.285, 2.281, 2.281, 2.285, 2.293, 2.306, 2.313, 2.322, 2.327, 2.337, 2.351, 2.364, 2.374, 2.385,
+ 2.442, 2.427, 2.411, 2.398, 2.389, 2.381, 2.369, 2.359, 2.351, 2.335, 2.319, 2.312, 2.301, 2.296, 2.289, 2.283, 2.279, 2.277, 2.273, 2.269, 2.267, 2.268, 2.279, 2.293, 2.306, 2.316, 2.325, 2.335, 2.348, 2.361, 2.372, 2.385,
+ 2.441, 2.428, 2.411, 2.399, 2.389, 2.381, 2.369, 2.356, 2.343, 2.331, 2.312, 2.301, 2.292, 2.286, 2.279, 2.278, 2.273, 2.271, 2.267, 2.259, 2.259, 2.262, 2.268, 2.279, 2.297, 2.311, 2.323, 2.335, 2.347, 2.357, 2.371, 2.388,
+ 2.441, 2.428, 2.412, 2.397, 2.389, 2.381, 2.369, 2.355, 2.337, 2.318, 2.308, 2.292, 2.286, 2.278, 2.276, 2.268, 2.264, 2.263, 2.257, 2.252, 2.252, 2.255, 2.262, 2.273, 2.284, 2.303, 2.319, 2.336, 2.343, 2.356, 2.365, 2.388,
+ 2.441, 2.428, 2.412, 2.401, 2.389, 2.381, 2.363, 2.344, 2.323, 2.311, 2.299, 2.287, 2.278, 2.275, 2.267, 2.264, 2.259, 2.256, 2.249, 2.246, 2.246, 2.249, 2.255, 2.262, 2.279, 2.296, 2.313, 2.336, 2.347, 2.355, 2.366, 2.384,
+ 2.441, 2.427, 2.412, 2.401, 2.391, 2.381, 2.355, 2.339, 2.323, 2.308, 2.297, 2.284, 2.275, 2.267, 2.259, 2.253, 2.251, 2.247, 2.244, 2.239, 2.239, 2.244, 2.249, 2.259, 2.276, 2.288, 2.311, 2.332, 2.348, 2.356, 2.366, 2.384,
+ 2.442, 2.428, 2.415, 2.402, 2.391, 2.379, 2.354, 2.331, 2.323, 2.302, 2.291, 2.278, 2.267, 2.259, 2.253, 2.246, 2.245, 2.241, 2.236, 2.233, 2.235, 2.239, 2.245, 2.255, 2.271, 2.287, 2.307, 2.331, 2.349, 2.356, 2.367, 2.384,
+ 2.441, 2.429, 2.419, 2.404, 2.394, 2.375, 2.352, 2.331, 2.318, 2.301, 2.288, 2.273, 2.263, 2.254, 2.246, 2.242, 2.237, 2.232, 2.229, 2.229, 2.232, 2.235, 2.245, 2.255, 2.271, 2.286, 2.307, 2.331, 2.351, 2.359, 2.371, 2.385,
+ 2.441, 2.432, 2.419, 2.406, 2.395, 2.375, 2.349, 2.332, 2.317, 2.301, 2.288, 2.273, 2.262, 2.249, 2.242, 2.235, 2.226, 2.225, 2.225, 2.227, 2.232, 2.235, 2.246, 2.255, 2.271, 2.286, 2.307, 2.331, 2.351, 2.366, 2.376, 2.388,
+ 2.441, 2.437, 2.424, 2.406, 2.395, 2.375, 2.352, 2.333, 2.317, 2.304, 2.289, 2.274, 2.261, 2.245, 2.235, 2.226, 2.217, 2.216, 2.219, 2.225, 2.229, 2.237, 2.246, 2.255, 2.272, 2.289, 2.307, 2.334, 2.352, 2.369, 2.376, 2.392,
+ 2.447, 2.438, 2.425, 2.412, 2.399, 2.378, 2.352, 2.335, 2.317, 2.304, 2.289, 2.275, 2.259, 2.243, 2.227, 2.217, 2.214, 2.209, 2.214, 2.219, 2.229, 2.238, 2.246, 2.258, 2.272, 2.289, 2.309, 2.334, 2.355, 2.369, 2.385, 2.392,
+ 2.449, 2.441, 2.425, 2.416, 2.399, 2.381, 2.357, 2.336, 2.321, 2.305, 2.289, 2.275, 2.261, 2.242, 2.225, 2.214, 2.207, 2.207, 2.209, 2.218, 2.229, 2.238, 2.249, 2.261, 2.275, 2.291, 2.309, 2.336, 2.354, 2.371, 2.386, 2.396,
+ 2.451, 2.442, 2.426, 2.419, 2.403, 2.383, 2.361, 2.341, 2.321, 2.305, 2.289, 2.276, 2.259, 2.243, 2.225, 2.213, 2.207, 2.207, 2.209, 2.218, 2.227, 2.241, 2.249, 2.261, 2.277, 2.295, 2.316, 2.338, 2.355, 2.374, 2.387, 2.398,
+ 2.452, 2.442, 2.427, 2.419, 2.405, 2.384, 2.361, 2.341, 2.321, 2.305, 2.293, 2.277, 2.259, 2.244, 2.226, 2.213, 2.211, 2.208, 2.211, 2.218, 2.229, 2.241, 2.249, 2.263, 2.279, 2.298, 2.319, 2.339, 2.359, 2.374, 2.387, 2.398,
+ 2.452, 2.442, 2.428, 2.419, 2.405, 2.384, 2.361, 2.341, 2.324, 2.305, 2.293, 2.277, 2.259, 2.245, 2.232, 2.222, 2.213, 2.213, 2.218, 2.223, 2.229, 2.241, 2.249, 2.265, 2.279, 2.298, 2.319, 2.339, 2.363, 2.374, 2.387, 2.406,
+ 2.452, 2.442, 2.428, 2.419, 2.405, 2.384, 2.361, 2.343, 2.324, 2.305, 2.293, 2.279, 2.265, 2.251, 2.241, 2.232, 2.222, 2.222, 2.223, 2.229, 2.233, 2.241, 2.251, 2.265, 2.279, 2.298, 2.321, 2.341, 2.364, 2.374, 2.387, 2.405,
+ 2.454, 2.442, 2.429, 2.419, 2.404, 2.386, 2.365, 2.346, 2.327, 2.309, 2.293, 2.281, 2.269, 2.258, 2.251, 2.241, 2.237, 2.231, 2.231, 2.233, 2.238, 2.245, 2.256, 2.267, 2.283, 2.301, 2.323, 2.344, 2.366, 2.379, 2.388, 2.405,
+ 2.454, 2.439, 2.431, 2.421, 2.404, 2.392, 2.369, 2.348, 2.332, 2.314, 2.299, 2.288, 2.274, 2.268, 2.258, 2.252, 2.249, 2.244, 2.241, 2.241, 2.245, 2.251, 2.259, 2.269, 2.287, 2.301, 2.324, 2.352, 2.366, 2.379, 2.388, 2.405,
+ 2.453, 2.438, 2.431, 2.418, 2.407, 2.393, 2.374, 2.352, 2.337, 2.321, 2.303, 2.293, 2.284, 2.274, 2.268, 2.261, 2.259, 2.257, 2.251, 2.251, 2.251, 2.258, 2.266, 2.276, 2.297, 2.314, 2.333, 2.354, 2.366, 2.381, 2.391, 2.407,
+ 2.453, 2.438, 2.431, 2.417, 2.408, 2.396, 2.379, 2.359, 2.344, 2.329, 2.314, 2.301, 2.293, 2.284, 2.277, 2.276, 2.271, 2.266, 2.266, 2.261, 2.261, 2.266, 2.276, 2.285, 2.301, 2.318, 2.339, 2.356, 2.371, 2.383, 2.393, 2.408,
+ 2.453, 2.441, 2.428, 2.417, 2.409, 2.398, 2.386, 2.371, 2.351, 2.339, 2.324, 2.314, 2.301, 2.296, 2.289, 2.288, 2.281, 2.278, 2.275, 2.271, 2.271, 2.276, 2.285, 2.294, 2.311, 2.327, 2.346, 2.356, 2.373, 2.386, 2.393, 2.409,
+ 2.457, 2.441, 2.428, 2.417, 2.412, 2.403, 2.389, 2.381, 2.365, 2.349, 2.337, 2.324, 2.314, 2.309, 2.306, 2.303, 2.295, 2.294, 2.291, 2.287, 2.287, 2.289, 2.294, 2.309, 2.319, 2.332, 2.351, 2.359, 2.377, 2.386, 2.398, 2.411,
+ 2.457, 2.443, 2.427, 2.417, 2.413, 2.405, 2.399, 2.388, 2.373, 2.365, 2.349, 2.337, 2.327, 2.323, 2.318, 2.314, 2.312, 2.309, 2.304, 2.303, 2.302, 2.303, 2.309, 2.319, 2.332, 2.344, 2.355, 2.365, 2.379, 2.392, 2.403, 2.412,
+ 2.459, 2.449, 2.427, 2.417, 2.413, 2.407, 2.405, 2.399, 2.388, 2.373, 2.366, 2.353, 2.346, 2.338, 2.334, 2.331, 2.328, 2.321, 2.321, 2.317, 2.317, 2.321, 2.321, 2.337, 2.344, 2.355, 2.363, 2.369, 2.383, 2.392, 2.407, 2.418,
+ 2.465, 2.453, 2.439, 2.427, 2.417, 2.413, 2.407, 2.405, 2.399, 2.391, 2.376, 2.368, 2.359, 2.358, 2.349, 2.347, 2.346, 2.341, 2.341, 2.335, 2.334, 2.334, 2.341, 2.347, 2.355, 2.363, 2.368, 2.377, 2.388, 2.399, 2.418, 2.421,
+ 2.467, 2.453, 2.441, 2.431, 2.423, 2.417, 2.416, 2.408, 2.405, 2.399, 2.394, 2.386, 2.381, 2.378, 2.371, 2.369, 2.365, 2.361, 2.359, 2.356, 2.356, 2.356, 2.356, 2.361, 2.367, 2.368, 2.374, 2.387, 2.393, 2.407, 2.418, 2.427,
+ 2.473, 2.456, 2.447, 2.434, 2.431, 2.423, 2.419, 2.419, 2.413, 2.406, 2.404, 2.403, 2.397, 2.395, 2.394, 2.391, 2.386, 2.381, 2.378, 2.371, 2.371, 2.365, 2.365, 2.372, 2.374, 2.376, 2.379, 2.392, 2.401, 2.413, 2.422, 2.431,
+ 2.473, 2.469, 2.449, 2.441, 2.433, 2.431, 2.423, 2.419, 2.419, 2.413, 2.412, 2.409, 2.403, 2.402, 2.402, 2.399, 2.399, 2.391, 2.387, 2.385, 2.381, 2.379, 2.379, 2.379, 2.379, 2.379, 2.388, 2.395, 2.409, 2.413, 2.426, 2.431
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 6500,
+ "table":
+ [
+ 1.308, 1.307, 1.301, 1.301, 1.297, 1.295, 1.295, 1.295, 1.295, 1.295, 1.295, 1.295, 1.295, 1.295, 1.295, 1.296, 1.298, 1.299, 1.298, 1.298, 1.298, 1.299, 1.299, 1.299, 1.299, 1.307, 1.308, 1.311, 1.313, 1.317, 1.322, 1.322,
+ 1.307, 1.304, 1.298, 1.293, 1.292, 1.291, 1.288, 1.288, 1.288, 1.288, 1.289, 1.291, 1.292, 1.292, 1.294, 1.294, 1.294, 1.293, 1.293, 1.292, 1.293, 1.295, 1.296, 1.298, 1.299, 1.299, 1.302, 1.307, 1.309, 1.313, 1.318, 1.322,
+ 1.303, 1.298, 1.293, 1.291, 1.289, 1.287, 1.286, 1.286, 1.287, 1.287, 1.288, 1.289, 1.291, 1.292, 1.292, 1.294, 1.293, 1.293, 1.293, 1.292, 1.291, 1.292, 1.295, 1.296, 1.297, 1.298, 1.299, 1.302, 1.306, 1.308, 1.313, 1.317,
+ 1.299, 1.293, 1.291, 1.289, 1.287, 1.286, 1.286, 1.286, 1.287, 1.287, 1.288, 1.289, 1.291, 1.292, 1.293, 1.293, 1.293, 1.293, 1.293, 1.291, 1.291, 1.291, 1.293, 1.295, 1.296, 1.297, 1.298, 1.301, 1.304, 1.306, 1.308, 1.315,
+ 1.297, 1.291, 1.289, 1.286, 1.286, 1.286, 1.286, 1.287, 1.287, 1.287, 1.288, 1.289, 1.289, 1.292, 1.294, 1.294, 1.293, 1.293, 1.293, 1.291, 1.289, 1.289, 1.292, 1.294, 1.295, 1.296, 1.297, 1.298, 1.301, 1.305, 1.308, 1.313,
+ 1.295, 1.289, 1.287, 1.285, 1.285, 1.285, 1.285, 1.286, 1.287, 1.287, 1.287, 1.288, 1.289, 1.293, 1.294, 1.295, 1.295, 1.294, 1.294, 1.292, 1.289, 1.289, 1.289, 1.294, 1.294, 1.295, 1.295, 1.296, 1.299, 1.301, 1.307, 1.308,
+ 1.292, 1.287, 1.285, 1.284, 1.283, 1.283, 1.283, 1.284, 1.284, 1.286, 1.287, 1.288, 1.289, 1.293, 1.294, 1.295, 1.295, 1.295, 1.294, 1.292, 1.291, 1.289, 1.289, 1.291, 1.293, 1.294, 1.294, 1.295, 1.296, 1.299, 1.305, 1.307,
+ 1.292, 1.285, 1.282, 1.282, 1.282, 1.282, 1.282, 1.283, 1.283, 1.284, 1.286, 1.287, 1.289, 1.293, 1.295, 1.296, 1.296, 1.295, 1.295, 1.292, 1.291, 1.289, 1.288, 1.288, 1.291, 1.292, 1.293, 1.294, 1.296, 1.297, 1.301, 1.306,
+ 1.291, 1.283, 1.282, 1.281, 1.281, 1.281, 1.281, 1.283, 1.282, 1.283, 1.283, 1.286, 1.287, 1.289, 1.293, 1.295, 1.296, 1.296, 1.294, 1.291, 1.291, 1.289, 1.288, 1.288, 1.289, 1.291, 1.292, 1.293, 1.296, 1.297, 1.299, 1.301,
+ 1.288, 1.283, 1.281, 1.279, 1.279, 1.279, 1.281, 1.281, 1.281, 1.282, 1.282, 1.283, 1.286, 1.289, 1.292, 1.295, 1.296, 1.296, 1.293, 1.291, 1.289, 1.288, 1.287, 1.287, 1.289, 1.291, 1.292, 1.292, 1.294, 1.296, 1.297, 1.299,
+ 1.287, 1.282, 1.279, 1.278, 1.279, 1.279, 1.279, 1.279, 1.281, 1.281, 1.282, 1.283, 1.283, 1.288, 1.291, 1.295, 1.295, 1.294, 1.292, 1.289, 1.288, 1.287, 1.287, 1.287, 1.288, 1.289, 1.291, 1.292, 1.293, 1.294, 1.297, 1.298,
+ 1.284, 1.279, 1.278, 1.278, 1.279, 1.279, 1.279, 1.279, 1.279, 1.281, 1.282, 1.283, 1.283, 1.288, 1.291, 1.294, 1.294, 1.294, 1.292, 1.288, 1.288, 1.288, 1.289, 1.288, 1.288, 1.288, 1.291, 1.292, 1.293, 1.294, 1.296, 1.298,
+ 1.284, 1.279, 1.277, 1.277, 1.277, 1.278, 1.279, 1.279, 1.279, 1.281, 1.282, 1.283, 1.285, 1.288, 1.291, 1.294, 1.294, 1.294, 1.292, 1.291, 1.289, 1.289, 1.289, 1.289, 1.288, 1.288, 1.289, 1.293, 1.293, 1.295, 1.296, 1.298,
+ 1.283, 1.279, 1.276, 1.276, 1.276, 1.277, 1.278, 1.279, 1.281, 1.281, 1.282, 1.283, 1.285, 1.289, 1.291, 1.292, 1.293, 1.293, 1.293, 1.292, 1.289, 1.289, 1.291, 1.291, 1.289, 1.288, 1.289, 1.292, 1.293, 1.294, 1.296, 1.299,
+ 1.283, 1.279, 1.275, 1.276, 1.276, 1.277, 1.278, 1.279, 1.281, 1.282, 1.282, 1.283, 1.285, 1.289, 1.291, 1.291, 1.292, 1.293, 1.293, 1.293, 1.291, 1.291, 1.291, 1.291, 1.289, 1.288, 1.288, 1.289, 1.292, 1.294, 1.295, 1.299,
+ 1.283, 1.277, 1.275, 1.275, 1.276, 1.276, 1.279, 1.279, 1.281, 1.282, 1.282, 1.283, 1.285, 1.289, 1.291, 1.291, 1.291, 1.292, 1.293, 1.293, 1.292, 1.291, 1.291, 1.291, 1.291, 1.289, 1.288, 1.289, 1.291, 1.293, 1.295, 1.298,
+ 1.282, 1.277, 1.275, 1.274, 1.275, 1.276, 1.278, 1.279, 1.281, 1.282, 1.282, 1.282, 1.284, 1.287, 1.289, 1.289, 1.289, 1.289, 1.292, 1.292, 1.292, 1.291, 1.291, 1.291, 1.291, 1.289, 1.288, 1.289, 1.291, 1.292, 1.296, 1.298,
+ 1.282, 1.276, 1.274, 1.274, 1.275, 1.276, 1.277, 1.279, 1.279, 1.281, 1.281, 1.281, 1.282, 1.286, 1.287, 1.289, 1.289, 1.289, 1.289, 1.291, 1.291, 1.291, 1.291, 1.291, 1.291, 1.289, 1.289, 1.289, 1.291, 1.292, 1.297, 1.298,
+ 1.282, 1.275, 1.274, 1.274, 1.274, 1.275, 1.275, 1.277, 1.279, 1.279, 1.279, 1.279, 1.281, 1.282, 1.286, 1.287, 1.287, 1.288, 1.288, 1.289, 1.289, 1.289, 1.291, 1.291, 1.291, 1.289, 1.289, 1.289, 1.291, 1.292, 1.297, 1.298,
+ 1.279, 1.274, 1.273, 1.273, 1.274, 1.275, 1.275, 1.276, 1.277, 1.278, 1.278, 1.278, 1.278, 1.281, 1.283, 1.285, 1.286, 1.286, 1.286, 1.286, 1.286, 1.288, 1.289, 1.289, 1.289, 1.289, 1.289, 1.289, 1.291, 1.292, 1.297, 1.298,
+ 1.279, 1.274, 1.272, 1.272, 1.273, 1.274, 1.275, 1.274, 1.276, 1.276, 1.276, 1.276, 1.277, 1.278, 1.282, 1.283, 1.283, 1.283, 1.283, 1.283, 1.284, 1.286, 1.288, 1.288, 1.288, 1.288, 1.289, 1.291, 1.291, 1.292, 1.296, 1.298,
+ 1.279, 1.273, 1.272, 1.272, 1.273, 1.273, 1.274, 1.274, 1.275, 1.275, 1.275, 1.275, 1.276, 1.277, 1.279, 1.281, 1.283, 1.283, 1.282, 1.282, 1.283, 1.284, 1.287, 1.288, 1.288, 1.288, 1.289, 1.291, 1.292, 1.292, 1.296, 1.299,
+ 1.282, 1.272, 1.271, 1.271, 1.272, 1.272, 1.273, 1.274, 1.274, 1.274, 1.274, 1.274, 1.276, 1.277, 1.279, 1.281, 1.282, 1.282, 1.282, 1.281, 1.282, 1.283, 1.286, 1.287, 1.288, 1.288, 1.288, 1.291, 1.292, 1.292, 1.296, 1.297,
+ 1.281, 1.273, 1.272, 1.271, 1.271, 1.271, 1.272, 1.273, 1.274, 1.274, 1.274, 1.275, 1.276, 1.278, 1.279, 1.281, 1.282, 1.282, 1.282, 1.282, 1.282, 1.284, 1.285, 1.287, 1.288, 1.288, 1.288, 1.289, 1.292, 1.293, 1.296, 1.297,
+ 1.281, 1.275, 1.272, 1.271, 1.271, 1.271, 1.272, 1.273, 1.273, 1.273, 1.273, 1.275, 1.277, 1.279, 1.282, 1.282, 1.283, 1.283, 1.283, 1.282, 1.282, 1.284, 1.285, 1.286, 1.288, 1.288, 1.288, 1.289, 1.293, 1.296, 1.297, 1.297,
+ 1.281, 1.276, 1.272, 1.271, 1.269, 1.269, 1.272, 1.273, 1.274, 1.274, 1.275, 1.276, 1.279, 1.282, 1.283, 1.283, 1.284, 1.284, 1.285, 1.285, 1.284, 1.285, 1.285, 1.286, 1.289, 1.289, 1.289, 1.289, 1.293, 1.295, 1.297, 1.301,
+ 1.286, 1.276, 1.272, 1.271, 1.271, 1.269, 1.272, 1.273, 1.274, 1.276, 1.276, 1.276, 1.279, 1.282, 1.284, 1.285, 1.285, 1.285, 1.286, 1.286, 1.285, 1.285, 1.286, 1.286, 1.289, 1.289, 1.289, 1.291, 1.292, 1.295, 1.301, 1.302,
+ 1.286, 1.277, 1.272, 1.272, 1.271, 1.271, 1.272, 1.273, 1.276, 1.276, 1.276, 1.277, 1.279, 1.282, 1.284, 1.285, 1.285, 1.285, 1.286, 1.286, 1.286, 1.286, 1.286, 1.286, 1.288, 1.289, 1.289, 1.291, 1.292, 1.294, 1.301, 1.304,
+ 1.285, 1.276, 1.274, 1.272, 1.271, 1.271, 1.271, 1.273, 1.274, 1.276, 1.276, 1.278, 1.279, 1.282, 1.283, 1.284, 1.285, 1.285, 1.286, 1.286, 1.286, 1.286, 1.286, 1.287, 1.287, 1.288, 1.291, 1.292, 1.292, 1.295, 1.301, 1.307,
+ 1.285, 1.278, 1.275, 1.273, 1.272, 1.272, 1.271, 1.272, 1.274, 1.275, 1.276, 1.279, 1.279, 1.281, 1.284, 1.284, 1.285, 1.285, 1.285, 1.285, 1.285, 1.285, 1.286, 1.287, 1.287, 1.287, 1.291, 1.292, 1.293, 1.297, 1.301, 1.307,
+ 1.283, 1.277, 1.275, 1.273, 1.272, 1.271, 1.269, 1.271, 1.272, 1.274, 1.275, 1.276, 1.279, 1.279, 1.279, 1.284, 1.284, 1.284, 1.284, 1.284, 1.284, 1.284, 1.285, 1.285, 1.287, 1.287, 1.291, 1.292, 1.293, 1.298, 1.301, 1.301,
+ 1.283, 1.277, 1.275, 1.272, 1.271, 1.268, 1.268, 1.269, 1.271, 1.272, 1.273, 1.272, 1.277, 1.279, 1.279, 1.281, 1.281, 1.282, 1.282, 1.282, 1.281, 1.283, 1.285, 1.285, 1.287, 1.287, 1.288, 1.291, 1.293, 1.298, 1.299, 1.299
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 4.903, 4.653, 4.193, 3.818, 3.501, 3.239, 3.015, 2.824, 2.666, 2.529, 2.414, 2.316, 2.241, 2.205, 2.184, 2.178, 2.178, 2.178, 2.188, 2.216, 2.269, 2.359, 2.465, 2.597, 2.738, 2.915, 3.125, 3.374, 3.662, 4.011, 4.418, 4.656,
+ 4.653, 4.392, 3.985, 3.621, 3.323, 3.071, 2.855, 2.678, 2.534, 2.414, 2.316, 2.234, 2.168, 2.116, 2.079, 2.058, 2.058, 2.065, 2.091, 2.135, 2.196, 2.269, 2.359, 2.465, 2.599, 2.761, 2.962, 3.195, 3.472, 3.801, 4.188, 4.418,
+ 4.392, 4.149, 3.766, 3.423, 3.139, 2.901, 2.697, 2.534, 2.409, 2.286, 2.191, 2.106, 2.034, 1.977, 1.938, 1.914, 1.914, 1.923, 1.952, 1.998, 2.063, 2.139, 2.231, 2.341, 2.465, 2.612, 2.796, 3.019, 3.284, 3.596, 3.963, 4.188,
+ 4.149, 3.936, 3.566, 3.252, 2.982, 2.749, 2.561, 2.409, 2.286, 2.169, 2.066, 1.976, 1.901, 1.842, 1.801, 1.777, 1.777, 1.785, 1.814, 1.861, 1.929, 2.012, 2.107, 2.227, 2.341, 2.483, 2.651, 2.863, 3.119, 3.415, 3.764, 3.979,
+ 3.951, 3.743, 3.392, 3.091, 2.831, 2.615, 2.438, 2.297, 2.169, 2.061, 1.942, 1.847, 1.768, 1.711, 1.669, 1.646, 1.646, 1.653, 1.681, 1.729, 1.796, 1.882, 1.987, 2.107, 2.227, 2.367, 2.528, 2.727, 2.968, 3.254, 3.583, 3.805,
+ 3.785, 3.572, 3.242, 2.952, 2.703, 2.501, 2.336, 2.195, 2.061, 1.942, 1.825, 1.726, 1.651, 1.591, 1.549, 1.527, 1.527, 1.533, 1.562, 1.611, 1.676, 1.763, 1.881, 1.987, 2.119, 2.263, 2.422, 2.608, 2.833, 3.109, 3.428, 3.652,
+ 3.644, 3.424, 3.107, 2.828, 2.592, 2.401, 2.244, 2.097, 1.954, 1.825, 1.724, 1.614, 1.538, 1.479, 1.441, 1.419, 1.419, 1.424, 1.452, 1.498, 1.564, 1.656, 1.763, 1.881, 2.018, 2.169, 2.325, 2.504, 2.718, 2.979, 3.291, 3.519,
+ 3.525, 3.297, 2.986, 2.718, 2.495, 2.318, 2.156, 2.004, 1.856, 1.724, 1.614, 1.524, 1.439, 1.384, 1.348, 1.328, 1.328, 1.332, 1.358, 1.402, 1.466, 1.564, 1.656, 1.781, 1.923, 2.076, 2.241, 2.414, 2.616, 2.864, 3.166, 3.411,
+ 3.413, 3.191, 2.889, 2.631, 2.421, 2.245, 2.082, 1.924, 1.772, 1.637, 1.524, 1.439, 1.359, 1.307, 1.272, 1.252, 1.252, 1.258, 1.283, 1.324, 1.391, 1.466, 1.569, 1.694, 1.838, 1.999, 2.166, 2.341, 2.537, 2.774, 3.069, 3.306,
+ 3.333, 3.086, 2.793, 2.547, 2.349, 2.174, 2.006, 1.843, 1.689, 1.556, 1.444, 1.359, 1.299, 1.239, 1.207, 1.189, 1.189, 1.194, 1.219, 1.261, 1.324, 1.391, 1.491, 1.614, 1.757, 1.921, 2.092, 2.269, 2.463, 2.687, 2.967, 3.225,
+ 3.256, 3.016, 2.727, 2.491, 2.295, 2.118, 1.947, 1.779, 1.625, 1.494, 1.383, 1.299, 1.239, 1.189, 1.158, 1.141, 1.141, 1.147, 1.171, 1.217, 1.261, 1.334, 1.429, 1.551, 1.695, 1.857, 2.035, 2.217, 2.409, 2.628, 2.895, 3.153,
+ 3.211, 2.946, 2.666, 2.433, 2.246, 2.068, 1.891, 1.721, 1.569, 1.437, 1.332, 1.249, 1.189, 1.156, 1.113, 1.096, 1.096, 1.102, 1.133, 1.171, 1.217, 1.286, 1.378, 1.496, 1.638, 1.802, 1.981, 2.166, 2.358, 2.573, 2.832, 3.105,
+ 3.165, 2.901, 2.625, 2.401, 2.211, 2.031, 1.848, 1.678, 1.528, 1.398, 1.294, 1.216, 1.156, 1.113, 1.085, 1.061, 1.061, 1.067, 1.102, 1.133, 1.185, 1.253, 1.341, 1.456, 1.597, 1.761, 1.942, 2.131, 2.325, 2.534, 2.793, 3.069,
+ 3.132, 2.862, 2.593, 2.371, 2.182, 1.997, 1.816, 1.646, 1.496, 1.367, 1.267, 1.189, 1.131, 1.085, 1.061, 1.032, 1.032, 1.043, 1.067, 1.108, 1.161, 1.228, 1.313, 1.426, 1.566, 1.728, 1.911, 2.102, 2.297, 2.508, 2.761, 3.039,
+ 3.118, 2.833, 2.566, 2.344, 2.157, 1.972, 1.786, 1.618, 1.469, 1.343, 1.244, 1.169, 1.111, 1.063, 1.032, 1.018, 1.009, 1.027, 1.043, 1.086, 1.141, 1.207, 1.292, 1.403, 1.541, 1.703, 1.884, 2.077, 2.274, 2.484, 2.735, 3.031,
+ 3.111, 2.815, 2.553, 2.334, 2.145, 1.959, 1.774, 1.605, 1.455, 1.331, 1.234, 1.159, 1.101, 1.053, 1.018, 1.005, 1.004, 1.006, 1.033, 1.077, 1.132, 1.199, 1.283, 1.393, 1.531, 1.692, 1.873, 2.067, 2.265, 2.477, 2.726, 3.028,
+ 3.111, 2.815, 2.552, 2.333, 2.145, 1.959, 1.774, 1.605, 1.455, 1.331, 1.234, 1.159, 1.101, 1.053, 1.018, 1.001, 1.001, 1.006, 1.033, 1.077, 1.132, 1.199, 1.283, 1.393, 1.531, 1.692, 1.873, 2.067, 2.265, 2.475, 2.726, 3.028,
+ 3.111, 2.822, 2.552, 2.333, 2.146, 1.961, 1.775, 1.607, 1.457, 1.333, 1.236, 1.161, 1.103, 1.056, 1.021, 1.015, 1.004, 1.017, 1.037, 1.079, 1.135, 1.201, 1.287, 1.395, 1.533, 1.695, 1.876, 2.067, 2.266, 2.475, 2.727, 3.028,
+ 3.123, 2.844, 2.579, 2.357, 2.166, 1.983, 1.797, 1.627, 1.477, 1.351, 1.253, 1.177, 1.119, 1.073, 1.046, 1.021, 1.021, 1.037, 1.055, 1.097, 1.151, 1.218, 1.305, 1.416, 1.555, 1.717, 1.898, 2.089, 2.287, 2.499, 2.753, 3.041,
+ 3.149, 2.881, 2.609, 2.383, 2.191, 2.009, 1.827, 1.658, 1.506, 1.378, 1.277, 1.199, 1.142, 1.099, 1.073, 1.046, 1.046, 1.055, 1.089, 1.121, 1.173, 1.242, 1.331, 1.444, 1.584, 1.747, 1.928, 2.118, 2.315, 2.528, 2.787, 3.071,
+ 3.187, 2.933, 2.654, 2.422, 2.228, 2.049, 1.871, 1.699, 1.547, 1.417, 1.313, 1.232, 1.173, 1.136, 1.099, 1.081, 1.081, 1.089, 1.121, 1.152, 1.205, 1.275, 1.368, 1.484, 1.626, 1.789, 1.971, 2.159, 2.354, 2.571, 2.834, 3.107,
+ 3.247, 2.989, 2.703, 2.464, 2.269, 2.091, 1.915, 1.748, 1.595, 1.464, 1.356, 1.272, 1.216, 1.173, 1.136, 1.121, 1.121, 1.128, 1.152, 1.199, 1.242, 1.316, 1.411, 1.532, 1.675, 1.839, 2.019, 2.202, 2.398, 2.618, 2.891, 3.164,
+ 3.306, 3.068, 2.776, 2.524, 2.324, 2.148, 1.979, 1.814, 1.661, 1.526, 1.416, 1.328, 1.272, 1.216, 1.185, 1.169, 1.169, 1.177, 1.199, 1.242, 1.297, 1.371, 1.473, 1.596, 1.741, 1.904, 2.081, 2.263, 2.459, 2.687, 2.971, 3.221,
+ 3.394, 3.161, 2.855, 2.598, 2.387, 2.211, 2.047, 1.885, 1.734, 1.599, 1.485, 1.399, 1.328, 1.273, 1.242, 1.224, 1.224, 1.231, 1.256, 1.297, 1.369, 1.438, 1.542, 1.669, 1.813, 1.976, 2.149, 2.331, 2.527, 2.764, 3.057, 3.304,
+ 3.496, 3.263, 2.949, 2.684, 2.463, 2.279, 2.118, 1.965, 1.817, 1.681, 1.568, 1.485, 1.399, 1.345, 1.309, 1.291, 1.291, 1.299, 1.325, 1.369, 1.438, 1.528, 1.623, 1.751, 1.897, 2.056, 2.225, 2.405, 2.608, 2.858, 3.162, 3.402,
+ 3.611, 3.397, 3.072, 2.793, 2.558, 2.368, 2.205, 2.057, 1.914, 1.779, 1.673, 1.568, 1.492, 1.435, 1.398, 1.377, 1.377, 1.385, 1.414, 1.461, 1.528, 1.623, 1.722, 1.851, 1.996, 2.152, 2.318, 2.499, 2.712, 2.977, 3.294, 3.519,
+ 3.764, 3.537, 3.204, 2.908, 2.661, 2.459, 2.294, 2.147, 2.012, 1.885, 1.779, 1.673, 1.595, 1.535, 1.496, 1.476, 1.476, 1.484, 1.515, 1.564, 1.631, 1.722, 1.837, 1.954, 2.096, 2.247, 2.411, 2.599, 2.825, 3.108, 3.431, 3.662,
+ 3.919, 3.704, 3.353, 3.046, 2.787, 2.571, 2.393, 2.247, 2.117, 2.007, 1.885, 1.789, 1.709, 1.651, 1.613, 1.591, 1.591, 1.599, 1.631, 1.679, 1.749, 1.837, 1.954, 2.069, 2.204, 2.352, 2.518, 2.719, 2.962, 3.257, 3.591, 3.815,
+ 4.126, 3.894, 3.521, 3.203, 2.931, 2.702, 2.512, 2.358, 2.238, 2.117, 2.007, 1.915, 1.839, 1.779, 1.739, 1.719, 1.719, 1.726, 1.759, 1.808, 1.877, 1.965, 2.069, 2.202, 2.319, 2.467, 2.645, 2.859, 3.122, 3.427, 3.784, 4.019,
+ 4.391, 4.126, 3.721, 3.389, 3.103, 2.857, 2.655, 2.493, 2.358, 2.238, 2.138, 2.049, 1.976, 1.921, 1.882, 1.859, 1.859, 1.868, 1.899, 1.949, 2.015, 2.102, 2.202, 2.319, 2.456, 2.605, 2.794, 3.026, 3.305, 3.629, 4.019, 4.275,
+ 4.671, 4.391, 3.966, 3.603, 3.297, 3.041, 2.821, 2.643, 2.493, 2.374, 2.277, 2.191, 2.122, 2.069, 2.031, 2.011, 2.011, 2.021, 2.049, 2.097, 2.163, 2.245, 2.343, 2.456, 2.601, 2.762, 2.972, 3.225, 3.514, 3.867, 4.275, 4.547,
+ 4.899, 4.671, 4.209, 3.824, 3.501, 3.229, 2.999, 2.805, 2.643, 2.493, 2.374, 2.277, 2.208, 2.172, 2.153, 2.148, 2.148, 2.148, 2.164, 2.192, 2.245, 2.343, 2.456, 2.592, 2.749, 2.935, 3.161, 3.423, 3.736, 4.121, 4.547, 4.751
+ ],
+ "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.ccm":
+ {
+ "ccms": [
+ {
+ "ct": 2300,
+ "ccm":
+ [
+ 1.77591, -0.16036, -0.61554,
+ -0.26235, 1.66133, -0.39898,
+ -0.22474, -1.94117, 3.16591
+ ]
+ },
+ {
+ "ct": 2700,
+ "ccm":
+ [
+ 1.54016, 0.02018, -0.56034,
+ -0.27333, 1.78261, -0.50928,
+ -0.13821, -1.22069, 2.35891
+ ]
+ },
+ {
+ "ct": 3000,
+ "ccm":
+ [
+ 1.73266, -0.19227, -0.54039,
+ -0.44685, 2.04704, -0.60018,
+ -0.13631, -0.94323, 2.07953
+ ]
+ },
+ {
+ "ct": 4000,
+ "ccm":
+ [
+ 1.70137, -0.23462, -0.46675,
+ -0.34126, 1.80328, -0.46202,
+ -0.14242, -0.75105, 1.89347
+ ]
+ },
+ {
+ "ct": 4150,
+ "ccm":
+ [
+ 2.09386, -0.69875, -0.39511,
+ -0.38239, 1.78872, -0.40633,
+ -0.11896, -0.74324, 1.86219
+ ]
+ },
+ {
+ "ct": 6500,
+ "ccm":
+ [
+ 1.69679, -0.27504, -0.42174,
+ -0.23619, 1.87692, -0.64073,
+ -0.07905, -0.61889, 1.69795
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.cac": { }
+ },
+ {
+ "rpi.sharpen":
+ {
+ "threshold": 0.25,
+ "limit": 1.0,
+ "strength": 1.0
+ }
+ },
+ {
+ "rpi.hdr":
+ {
+ "Off":
+ {
+ "cadence": [ 0 ]
+ },
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ },
+ "SingleExposure":
+ {
+ "cadence": [ 1 ],
+ "channel_map":
+ {
+ "short": 1
+ },
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "MultiExposure":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ },
+ "stitch_enable": 1,
+ "spatial_gain": 2.0,
+ "tonemap_enable": 1
+ },
+ "Night":
+ {
+ "cadence": [ 3 ],
+ "channel_map":
+ {
+ "night": 3
+ },
+ "tonemap_enable": 1,
+ "tonemap":
+ [
+ 0, 0,
+ 5000, 20000,
+ 10000, 30000,
+ 20000, 47000,
+ 30000, 55000,
+ 65535, 65535
+ ]
+ }
+ }
+ },
+ {
+ "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": 0,
+ "dropout_frames": 0,
+ "step_frames": 4
+ }
+ },
+ "conf_epsilon": 8,
+ "conf_thresh": 16,
+ "conf_clip": 512,
+ "skip_frames": 5,
+ "map": [ 0.0, 0, 15.0, 1023 ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/pisp/data/ov9281_mono.json b/src/ipa/rpi/pisp/data/ov9281_mono.json
new file mode 100644
index 00000000..54229b83
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/ov9281_mono.json
@@ -0,0 +1,215 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ }
+ },
+ "exposure_modes":
+ {
+ "normal":
+ {
+ "shutter": [ 100, 15000, 30000, 60000, 120000 ],
+ "gain": [ 1.0, 2.0, 3.0, 4.0, 8.0 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 30000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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/pisp/data/se327m12.json b/src/ipa/rpi/pisp/data/se327m12.json
new file mode 100644
index 00000000..46f2378c
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/se327m12.json
@@ -0,0 +1,639 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "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.denoise":
+ {
+ "normal":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 0.8,
+ "threshold": 0.05
+ }
+ },
+ "hdr":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ },
+ "night":
+ {
+ "sdn":
+ {
+ "deviation": 1.6,
+ "strength": 0.5,
+ "deviation2": 3.2,
+ "deviation_no_tdn": 3.2,
+ "strength_no_tdn": 0.75
+ },
+ "cdn":
+ {
+ "deviation": 200,
+ "strength": 0.3
+ },
+ "tdn":
+ {
+ "deviation": 1.3,
+ "threshold": 0.1
+ }
+ }
+ }
+ },
+ {
+ "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":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0
+ ]
+ },
+ "spot":
+ {
+ "weights":
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 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,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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
+ ]
+ }
+ ]
+ },
+ "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.476, 1.471, 1.461, 1.45, 1.441, 1.431, 1.424, 1.418, 1.412, 1.406, 1.401, 1.396, 1.393, 1.39, 1.389, 1.389, 1.389, 1.389, 1.39, 1.391, 1.393, 1.395, 1.398, 1.401, 1.405, 1.411, 1.417, 1.423, 1.429, 1.433, 1.437,
+ 1.478, 1.472, 1.466, 1.456, 1.446, 1.436, 1.427, 1.42, 1.414, 1.408, 1.402, 1.398, 1.394, 1.391, 1.388, 1.387, 1.387, 1.387, 1.387, 1.387, 1.389, 1.391, 1.392, 1.395, 1.399, 1.403, 1.408, 1.414, 1.42, 1.427, 1.43, 1.434,
+ 1.475, 1.468, 1.461, 1.451, 1.441, 1.432, 1.423, 1.416, 1.41, 1.404, 1.399, 1.395, 1.392, 1.389, 1.387, 1.385, 1.384, 1.384, 1.384, 1.385, 1.387, 1.388, 1.39, 1.392, 1.396, 1.401, 1.405, 1.411, 1.418, 1.424, 1.428, 1.431,
+ 1.472, 1.464, 1.456, 1.446, 1.437, 1.428, 1.419, 1.412, 1.406, 1.401, 1.395, 1.392, 1.39, 1.387, 1.385, 1.383, 1.382, 1.382, 1.382, 1.382, 1.384, 1.386, 1.387, 1.389, 1.394, 1.398, 1.403, 1.407, 1.415, 1.422, 1.425, 1.429,
+ 1.469, 1.46, 1.451, 1.442, 1.433, 1.425, 1.417, 1.41, 1.403, 1.398, 1.393, 1.39, 1.388, 1.385, 1.382, 1.381, 1.38, 1.38, 1.38, 1.381, 1.382, 1.384, 1.385, 1.387, 1.391, 1.395, 1.399, 1.404, 1.411, 1.418, 1.422, 1.426,
+ 1.467, 1.457, 1.447, 1.438, 1.429, 1.422, 1.414, 1.407, 1.401, 1.396, 1.392, 1.388, 1.385, 1.383, 1.38, 1.378, 1.378, 1.378, 1.378, 1.379, 1.38, 1.381, 1.383, 1.386, 1.388, 1.391, 1.396, 1.401, 1.407, 1.414, 1.419, 1.424,
+ 1.465, 1.454, 1.443, 1.435, 1.427, 1.419, 1.412, 1.405, 1.399, 1.394, 1.39, 1.387, 1.384, 1.381, 1.378, 1.377, 1.377, 1.377, 1.377, 1.377, 1.378, 1.379, 1.382, 1.384, 1.386, 1.388, 1.393, 1.398, 1.405, 1.411, 1.416, 1.421,
+ 1.464, 1.453, 1.443, 1.434, 1.426, 1.418, 1.411, 1.405, 1.398, 1.393, 1.389, 1.385, 1.382, 1.38, 1.378, 1.376, 1.376, 1.376, 1.376, 1.376, 1.377, 1.378, 1.38, 1.382, 1.384, 1.387, 1.392, 1.397, 1.403, 1.409, 1.415, 1.42,
+ 1.462, 1.452, 1.442, 1.433, 1.425, 1.418, 1.411, 1.404, 1.397, 1.392, 1.387, 1.384, 1.381, 1.379, 1.377, 1.376, 1.375, 1.374, 1.374, 1.375, 1.375, 1.376, 1.378, 1.38, 1.383, 1.386, 1.39, 1.395, 1.402, 1.408, 1.413, 1.419,
+ 1.462, 1.452, 1.441, 1.432, 1.424, 1.417, 1.41, 1.403, 1.397, 1.391, 1.387, 1.383, 1.38, 1.378, 1.377, 1.375, 1.374, 1.374, 1.374, 1.374, 1.374, 1.375, 1.377, 1.379, 1.381, 1.384, 1.389, 1.394, 1.4, 1.407, 1.412, 1.417,
+ 1.461, 1.451, 1.441, 1.432, 1.423, 1.416, 1.409, 1.403, 1.396, 1.391, 1.387, 1.383, 1.381, 1.379, 1.377, 1.375, 1.374, 1.373, 1.373, 1.374, 1.374, 1.374, 1.376, 1.378, 1.38, 1.383, 1.388, 1.392, 1.399, 1.405, 1.411, 1.416,
+ 1.461, 1.45, 1.44, 1.431, 1.422, 1.415, 1.409, 1.402, 1.396, 1.391, 1.386, 1.384, 1.382, 1.379, 1.377, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.374, 1.375, 1.377, 1.379, 1.382, 1.386, 1.39, 1.397, 1.404, 1.41, 1.415,
+ 1.461, 1.45, 1.44, 1.431, 1.422, 1.415, 1.408, 1.401, 1.395, 1.39, 1.386, 1.383, 1.381, 1.379, 1.377, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.374, 1.375, 1.376, 1.379, 1.381, 1.385, 1.39, 1.396, 1.403, 1.409, 1.414,
+ 1.461, 1.45, 1.44, 1.43, 1.421, 1.414, 1.407, 1.4, 1.394, 1.39, 1.386, 1.383, 1.381, 1.379, 1.377, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.374, 1.375, 1.376, 1.378, 1.381, 1.385, 1.39, 1.396, 1.402, 1.408, 1.414,
+ 1.461, 1.45, 1.44, 1.43, 1.42, 1.413, 1.406, 1.399, 1.394, 1.389, 1.385, 1.382, 1.38, 1.378, 1.377, 1.375, 1.374, 1.373, 1.372, 1.372, 1.373, 1.374, 1.375, 1.376, 1.378, 1.38, 1.385, 1.39, 1.396, 1.401, 1.407, 1.413,
+ 1.461, 1.45, 1.439, 1.43, 1.42, 1.412, 1.405, 1.399, 1.393, 1.388, 1.385, 1.382, 1.379, 1.378, 1.376, 1.375, 1.374, 1.373, 1.372, 1.372, 1.373, 1.374, 1.374, 1.376, 1.378, 1.381, 1.385, 1.389, 1.395, 1.401, 1.407, 1.413,
+ 1.461, 1.45, 1.439, 1.43, 1.42, 1.412, 1.404, 1.398, 1.392, 1.388, 1.384, 1.381, 1.379, 1.377, 1.376, 1.375, 1.374, 1.373, 1.372, 1.372, 1.372, 1.373, 1.374, 1.376, 1.378, 1.381, 1.385, 1.389, 1.395, 1.401, 1.408, 1.414,
+ 1.461, 1.45, 1.439, 1.429, 1.42, 1.412, 1.404, 1.397, 1.391, 1.387, 1.384, 1.381, 1.378, 1.376, 1.375, 1.374, 1.374, 1.373, 1.372, 1.372, 1.372, 1.373, 1.374, 1.376, 1.379, 1.382, 1.385, 1.389, 1.395, 1.401, 1.408, 1.414,
+ 1.461, 1.45, 1.439, 1.429, 1.42, 1.412, 1.404, 1.398, 1.391, 1.387, 1.383, 1.381, 1.378, 1.376, 1.375, 1.374, 1.373, 1.373, 1.373, 1.372, 1.373, 1.373, 1.374, 1.376, 1.379, 1.382, 1.385, 1.389, 1.395, 1.401, 1.408, 1.414,
+ 1.462, 1.45, 1.439, 1.429, 1.42, 1.412, 1.404, 1.398, 1.392, 1.387, 1.383, 1.38, 1.378, 1.376, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.373, 1.374, 1.375, 1.376, 1.379, 1.382, 1.385, 1.39, 1.395, 1.401, 1.408, 1.414,
+ 1.462, 1.451, 1.439, 1.43, 1.421, 1.413, 1.405, 1.399, 1.393, 1.388, 1.383, 1.38, 1.378, 1.376, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.374, 1.374, 1.375, 1.377, 1.379, 1.382, 1.386, 1.39, 1.396, 1.402, 1.408, 1.414,
+ 1.462, 1.451, 1.44, 1.431, 1.422, 1.414, 1.406, 1.399, 1.393, 1.388, 1.383, 1.38, 1.378, 1.376, 1.375, 1.374, 1.373, 1.373, 1.373, 1.373, 1.373, 1.374, 1.375, 1.377, 1.379, 1.382, 1.386, 1.391, 1.396, 1.402, 1.408, 1.414,
+ 1.462, 1.452, 1.441, 1.432, 1.423, 1.415, 1.406, 1.4, 1.393, 1.389, 1.384, 1.381, 1.378, 1.376, 1.375, 1.374, 1.373, 1.373, 1.372, 1.372, 1.373, 1.374, 1.375, 1.377, 1.38, 1.383, 1.387, 1.391, 1.397, 1.402, 1.408, 1.414,
+ 1.462, 1.452, 1.442, 1.433, 1.424, 1.416, 1.407, 1.4, 1.394, 1.389, 1.384, 1.381, 1.378, 1.376, 1.375, 1.373, 1.373, 1.372, 1.372, 1.372, 1.372, 1.373, 1.375, 1.377, 1.38, 1.383, 1.387, 1.391, 1.397, 1.402, 1.408, 1.414,
+ 1.464, 1.453, 1.443, 1.434, 1.425, 1.416, 1.408, 1.401, 1.394, 1.389, 1.384, 1.381, 1.378, 1.376, 1.374, 1.373, 1.372, 1.371, 1.371, 1.371, 1.372, 1.373, 1.374, 1.376, 1.379, 1.382, 1.386, 1.391, 1.397, 1.402, 1.409, 1.416,
+ 1.465, 1.454, 1.444, 1.435, 1.425, 1.417, 1.408, 1.401, 1.395, 1.389, 1.384, 1.381, 1.379, 1.376, 1.374, 1.372, 1.37, 1.369, 1.369, 1.37, 1.371, 1.373, 1.374, 1.376, 1.379, 1.382, 1.386, 1.39, 1.396, 1.402, 1.41, 1.417,
+ 1.466, 1.456, 1.446, 1.436, 1.426, 1.418, 1.41, 1.403, 1.396, 1.39, 1.384, 1.381, 1.379, 1.377, 1.375, 1.372, 1.37, 1.369, 1.369, 1.37, 1.371, 1.373, 1.374, 1.376, 1.379, 1.383, 1.387, 1.391, 1.397, 1.404, 1.411, 1.418,
+ 1.467, 1.457, 1.448, 1.437, 1.427, 1.419, 1.412, 1.404, 1.397, 1.391, 1.385, 1.382, 1.38, 1.378, 1.375, 1.373, 1.371, 1.37, 1.37, 1.371, 1.372, 1.373, 1.375, 1.377, 1.381, 1.384, 1.388, 1.392, 1.399, 1.405, 1.413, 1.42,
+ 1.469, 1.459, 1.449, 1.439, 1.428, 1.421, 1.414, 1.406, 1.398, 1.392, 1.386, 1.383, 1.381, 1.379, 1.376, 1.374, 1.372, 1.371, 1.371, 1.371, 1.372, 1.374, 1.375, 1.378, 1.382, 1.386, 1.389, 1.394, 1.4, 1.407, 1.414, 1.422,
+ 1.47, 1.461, 1.452, 1.441, 1.431, 1.423, 1.416, 1.409, 1.401, 1.395, 1.388, 1.385, 1.382, 1.38, 1.377, 1.375, 1.374, 1.373, 1.373, 1.373, 1.374, 1.375, 1.377, 1.379, 1.383, 1.388, 1.392, 1.397, 1.405, 1.412, 1.417, 1.423,
+ 1.472, 1.463, 1.454, 1.444, 1.434, 1.426, 1.418, 1.412, 1.405, 1.398, 1.391, 1.387, 1.383, 1.381, 1.379, 1.377, 1.376, 1.375, 1.375, 1.375, 1.376, 1.377, 1.378, 1.381, 1.385, 1.39, 1.395, 1.401, 1.409, 1.417, 1.421, 1.425,
+ 1.474, 1.465, 1.457, 1.447, 1.437, 1.429, 1.421, 1.414, 1.409, 1.401, 1.394, 1.388, 1.385, 1.382, 1.38, 1.378, 1.378, 1.377, 1.377, 1.377, 1.378, 1.378, 1.38, 1.382, 1.387, 1.392, 1.399, 1.405, 1.414, 1.422, 1.424, 1.426
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.742, 1.732, 1.722, 1.707, 1.691, 1.677, 1.664, 1.652, 1.642, 1.633, 1.626, 1.62, 1.615, 1.612, 1.61, 1.608, 1.608, 1.607, 1.606, 1.607, 1.608, 1.61, 1.614, 1.618, 1.623, 1.627, 1.635, 1.643, 1.654, 1.666, 1.673, 1.681,
+ 1.737, 1.726, 1.715, 1.7, 1.685, 1.671, 1.658, 1.648, 1.639, 1.63, 1.622, 1.616, 1.611, 1.608, 1.606, 1.605, 1.604, 1.603, 1.603, 1.603, 1.605, 1.607, 1.611, 1.615, 1.62, 1.625, 1.631, 1.639, 1.65, 1.661, 1.669, 1.677,
+ 1.732, 1.721, 1.709, 1.694, 1.679, 1.665, 1.652, 1.643, 1.635, 1.627, 1.619, 1.613, 1.607, 1.604, 1.603, 1.601, 1.6, 1.599, 1.599, 1.6, 1.602, 1.604, 1.608, 1.612, 1.617, 1.622, 1.628, 1.635, 1.646, 1.657, 1.665, 1.674,
+ 1.727, 1.715, 1.703, 1.688, 1.673, 1.66, 1.647, 1.639, 1.632, 1.624, 1.616, 1.61, 1.604, 1.601, 1.599, 1.598, 1.596, 1.596, 1.596, 1.597, 1.599, 1.602, 1.605, 1.609, 1.614, 1.619, 1.625, 1.632, 1.642, 1.653, 1.661, 1.67,
+ 1.722, 1.71, 1.699, 1.684, 1.668, 1.656, 1.643, 1.635, 1.628, 1.62, 1.613, 1.607, 1.601, 1.598, 1.596, 1.595, 1.593, 1.593, 1.593, 1.594, 1.596, 1.598, 1.602, 1.606, 1.61, 1.615, 1.622, 1.629, 1.639, 1.649, 1.657, 1.666,
+ 1.716, 1.705, 1.694, 1.679, 1.663, 1.651, 1.64, 1.631, 1.623, 1.616, 1.61, 1.604, 1.599, 1.595, 1.594, 1.592, 1.591, 1.59, 1.59, 1.591, 1.592, 1.595, 1.599, 1.604, 1.607, 1.612, 1.619, 1.627, 1.636, 1.644, 1.653, 1.661,
+ 1.712, 1.701, 1.69, 1.675, 1.659, 1.647, 1.636, 1.628, 1.619, 1.613, 1.607, 1.602, 1.597, 1.593, 1.591, 1.589, 1.588, 1.587, 1.587, 1.588, 1.59, 1.592, 1.596, 1.601, 1.604, 1.609, 1.616, 1.624, 1.632, 1.641, 1.649, 1.658,
+ 1.71, 1.699, 1.687, 1.672, 1.657, 1.645, 1.633, 1.625, 1.618, 1.611, 1.605, 1.6, 1.595, 1.592, 1.589, 1.587, 1.586, 1.586, 1.586, 1.587, 1.588, 1.59, 1.594, 1.597, 1.601, 1.606, 1.613, 1.621, 1.629, 1.638, 1.647, 1.657,
+ 1.708, 1.696, 1.683, 1.669, 1.654, 1.642, 1.631, 1.623, 1.616, 1.609, 1.602, 1.597, 1.593, 1.59, 1.587, 1.585, 1.584, 1.584, 1.584, 1.585, 1.587, 1.588, 1.591, 1.594, 1.598, 1.604, 1.61, 1.618, 1.626, 1.635, 1.645, 1.655,
+ 1.705, 1.693, 1.68, 1.666, 1.652, 1.64, 1.628, 1.62, 1.614, 1.607, 1.6, 1.595, 1.592, 1.588, 1.586, 1.584, 1.583, 1.582, 1.583, 1.584, 1.585, 1.587, 1.589, 1.592, 1.596, 1.602, 1.608, 1.615, 1.624, 1.633, 1.644, 1.654,
+ 1.703, 1.69, 1.677, 1.663, 1.649, 1.638, 1.626, 1.618, 1.611, 1.604, 1.598, 1.593, 1.59, 1.587, 1.584, 1.582, 1.581, 1.581, 1.582, 1.583, 1.584, 1.585, 1.587, 1.59, 1.595, 1.6, 1.607, 1.614, 1.623, 1.633, 1.643, 1.653,
+ 1.7, 1.687, 1.674, 1.66, 1.646, 1.635, 1.625, 1.616, 1.609, 1.602, 1.596, 1.591, 1.588, 1.585, 1.583, 1.581, 1.58, 1.58, 1.581, 1.582, 1.583, 1.584, 1.586, 1.589, 1.594, 1.599, 1.606, 1.613, 1.622, 1.632, 1.642, 1.652,
+ 1.698, 1.685, 1.671, 1.658, 1.644, 1.633, 1.623, 1.615, 1.607, 1.6, 1.594, 1.59, 1.587, 1.584, 1.582, 1.58, 1.579, 1.579, 1.58, 1.581, 1.582, 1.583, 1.585, 1.588, 1.593, 1.598, 1.605, 1.611, 1.621, 1.631, 1.641, 1.652,
+ 1.698, 1.683, 1.669, 1.655, 1.642, 1.631, 1.621, 1.613, 1.605, 1.599, 1.593, 1.589, 1.586, 1.583, 1.581, 1.579, 1.578, 1.578, 1.579, 1.58, 1.581, 1.582, 1.584, 1.587, 1.593, 1.598, 1.604, 1.61, 1.62, 1.629, 1.641, 1.652,
+ 1.697, 1.682, 1.666, 1.653, 1.639, 1.629, 1.619, 1.611, 1.603, 1.597, 1.591, 1.587, 1.585, 1.583, 1.58, 1.579, 1.578, 1.577, 1.578, 1.579, 1.58, 1.582, 1.584, 1.587, 1.592, 1.598, 1.603, 1.608, 1.618, 1.628, 1.64, 1.652,
+ 1.697, 1.681, 1.665, 1.651, 1.638, 1.628, 1.618, 1.61, 1.602, 1.597, 1.591, 1.588, 1.585, 1.582, 1.58, 1.578, 1.577, 1.577, 1.577, 1.578, 1.579, 1.581, 1.584, 1.587, 1.592, 1.598, 1.603, 1.608, 1.618, 1.628, 1.64, 1.652,
+ 1.697, 1.681, 1.664, 1.65, 1.637, 1.626, 1.616, 1.609, 1.602, 1.596, 1.592, 1.588, 1.585, 1.582, 1.579, 1.578, 1.577, 1.576, 1.577, 1.577, 1.579, 1.581, 1.584, 1.587, 1.593, 1.598, 1.603, 1.608, 1.618, 1.628, 1.641, 1.653,
+ 1.697, 1.68, 1.663, 1.649, 1.636, 1.625, 1.615, 1.608, 1.601, 1.596, 1.592, 1.588, 1.585, 1.582, 1.579, 1.577, 1.577, 1.576, 1.576, 1.577, 1.578, 1.58, 1.584, 1.588, 1.593, 1.598, 1.603, 1.608, 1.619, 1.629, 1.641, 1.653,
+ 1.697, 1.68, 1.663, 1.649, 1.635, 1.625, 1.615, 1.607, 1.6, 1.596, 1.592, 1.588, 1.584, 1.581, 1.579, 1.577, 1.577, 1.576, 1.576, 1.577, 1.579, 1.581, 1.585, 1.588, 1.593, 1.598, 1.604, 1.61, 1.621, 1.631, 1.643, 1.654,
+ 1.697, 1.68, 1.663, 1.649, 1.635, 1.625, 1.615, 1.607, 1.6, 1.595, 1.591, 1.587, 1.584, 1.581, 1.579, 1.577, 1.577, 1.576, 1.577, 1.578, 1.58, 1.582, 1.586, 1.589, 1.594, 1.599, 1.605, 1.611, 1.623, 1.634, 1.644, 1.654,
+ 1.697, 1.68, 1.664, 1.649, 1.635, 1.625, 1.615, 1.608, 1.6, 1.595, 1.591, 1.587, 1.583, 1.581, 1.579, 1.578, 1.577, 1.576, 1.577, 1.578, 1.581, 1.583, 1.587, 1.59, 1.595, 1.6, 1.606, 1.613, 1.625, 1.636, 1.646, 1.655,
+ 1.699, 1.682, 1.665, 1.651, 1.636, 1.626, 1.616, 1.609, 1.602, 1.596, 1.591, 1.587, 1.584, 1.581, 1.579, 1.578, 1.577, 1.577, 1.578, 1.58, 1.581, 1.584, 1.587, 1.591, 1.596, 1.601, 1.608, 1.615, 1.626, 1.637, 1.647, 1.657,
+ 1.7, 1.683, 1.666, 1.652, 1.637, 1.627, 1.617, 1.61, 1.603, 1.597, 1.591, 1.587, 1.584, 1.581, 1.579, 1.578, 1.577, 1.578, 1.579, 1.581, 1.582, 1.584, 1.588, 1.592, 1.597, 1.602, 1.609, 1.617, 1.628, 1.639, 1.649, 1.658,
+ 1.702, 1.685, 1.668, 1.653, 1.639, 1.628, 1.618, 1.611, 1.604, 1.598, 1.591, 1.587, 1.584, 1.582, 1.58, 1.578, 1.578, 1.578, 1.58, 1.581, 1.583, 1.585, 1.589, 1.593, 1.598, 1.603, 1.611, 1.619, 1.63, 1.641, 1.65, 1.66,
+ 1.705, 1.687, 1.67, 1.655, 1.641, 1.63, 1.619, 1.611, 1.604, 1.598, 1.592, 1.588, 1.585, 1.582, 1.58, 1.579, 1.578, 1.578, 1.58, 1.582, 1.583, 1.585, 1.59, 1.594, 1.599, 1.604, 1.612, 1.621, 1.632, 1.643, 1.653, 1.663,
+ 1.707, 1.689, 1.672, 1.657, 1.642, 1.631, 1.62, 1.612, 1.605, 1.599, 1.593, 1.589, 1.585, 1.583, 1.581, 1.58, 1.579, 1.579, 1.58, 1.582, 1.584, 1.586, 1.59, 1.595, 1.6, 1.605, 1.614, 1.623, 1.634, 1.646, 1.655, 1.665,
+ 1.709, 1.692, 1.674, 1.659, 1.645, 1.633, 1.621, 1.613, 1.606, 1.6, 1.595, 1.59, 1.587, 1.584, 1.583, 1.581, 1.58, 1.58, 1.581, 1.582, 1.585, 1.587, 1.592, 1.597, 1.602, 1.608, 1.616, 1.625, 1.637, 1.648, 1.658, 1.668,
+ 1.711, 1.695, 1.678, 1.662, 1.647, 1.635, 1.623, 1.615, 1.608, 1.602, 1.597, 1.593, 1.59, 1.587, 1.584, 1.582, 1.581, 1.581, 1.582, 1.584, 1.586, 1.589, 1.594, 1.599, 1.605, 1.611, 1.619, 1.628, 1.639, 1.651, 1.66, 1.67,
+ 1.714, 1.698, 1.681, 1.666, 1.65, 1.637, 1.624, 1.616, 1.609, 1.604, 1.6, 1.596, 1.592, 1.589, 1.585, 1.584, 1.583, 1.583, 1.583, 1.585, 1.587, 1.59, 1.595, 1.601, 1.608, 1.615, 1.622, 1.63, 1.642, 1.653, 1.663, 1.673,
+ 1.715, 1.7, 1.685, 1.669, 1.653, 1.64, 1.627, 1.619, 1.613, 1.607, 1.603, 1.598, 1.594, 1.591, 1.587, 1.586, 1.586, 1.586, 1.586, 1.588, 1.59, 1.593, 1.598, 1.604, 1.611, 1.618, 1.626, 1.634, 1.646, 1.657, 1.666, 1.675,
+ 1.717, 1.703, 1.688, 1.673, 1.657, 1.644, 1.63, 1.623, 1.616, 1.611, 1.605, 1.601, 1.596, 1.593, 1.59, 1.588, 1.588, 1.589, 1.589, 1.591, 1.594, 1.597, 1.601, 1.607, 1.614, 1.622, 1.63, 1.639, 1.65, 1.661, 1.67, 1.678,
+ 1.719, 1.705, 1.692, 1.677, 1.661, 1.647, 1.634, 1.626, 1.62, 1.614, 1.608, 1.603, 1.598, 1.595, 1.592, 1.591, 1.591, 1.591, 1.592, 1.594, 1.597, 1.6, 1.605, 1.61, 1.617, 1.625, 1.634, 1.643, 1.655, 1.666, 1.673, 1.681
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 4000,
+ "table":
+ [
+ 2.253, 2.26, 2.267, 2.277, 2.288, 2.301, 2.314, 2.327, 2.339, 2.348, 2.356, 2.364, 2.37, 2.375, 2.379, 2.381, 2.381, 2.38, 2.379, 2.376, 2.371, 2.367, 2.363, 2.359, 2.351, 2.343, 2.34, 2.336, 2.324, 2.314, 2.307, 2.301,
+ 2.256, 2.264, 2.272, 2.284, 2.296, 2.309, 2.321, 2.332, 2.343, 2.352, 2.36, 2.368, 2.374, 2.379, 2.383, 2.385, 2.385, 2.385, 2.383, 2.381, 2.376, 2.371, 2.367, 2.362, 2.355, 2.349, 2.343, 2.337, 2.327, 2.316, 2.31, 2.303,
+ 2.259, 2.269, 2.278, 2.292, 2.305, 2.316, 2.328, 2.337, 2.347, 2.356, 2.365, 2.372, 2.378, 2.382, 2.386, 2.388, 2.389, 2.389, 2.388, 2.385, 2.38, 2.375, 2.37, 2.365, 2.36, 2.355, 2.347, 2.339, 2.329, 2.319, 2.313, 2.306,
+ 2.263, 2.274, 2.285, 2.298, 2.313, 2.323, 2.334, 2.342, 2.351, 2.359, 2.369, 2.375, 2.381, 2.386, 2.39, 2.392, 2.393, 2.393, 2.392, 2.39, 2.385, 2.38, 2.373, 2.368, 2.364, 2.36, 2.351, 2.341, 2.332, 2.322, 2.316, 2.309,
+ 2.268, 2.28, 2.291, 2.303, 2.315, 2.326, 2.337, 2.346, 2.355, 2.363, 2.372, 2.379, 2.384, 2.388, 2.391, 2.393, 2.394, 2.394, 2.394, 2.392, 2.389, 2.385, 2.378, 2.372, 2.367, 2.362, 2.354, 2.346, 2.336, 2.326, 2.32, 2.313,
+ 2.274, 2.286, 2.298, 2.308, 2.318, 2.33, 2.341, 2.35, 2.359, 2.367, 2.376, 2.382, 2.387, 2.391, 2.393, 2.395, 2.396, 2.396, 2.396, 2.395, 2.393, 2.39, 2.383, 2.376, 2.37, 2.364, 2.357, 2.35, 2.339, 2.329, 2.324, 2.318,
+ 2.277, 2.29, 2.302, 2.312, 2.321, 2.332, 2.344, 2.353, 2.362, 2.371, 2.379, 2.385, 2.389, 2.392, 2.394, 2.396, 2.396, 2.397, 2.397, 2.397, 2.396, 2.393, 2.387, 2.38, 2.374, 2.367, 2.36, 2.353, 2.343, 2.333, 2.327, 2.322,
+ 2.277, 2.29, 2.303, 2.313, 2.323, 2.334, 2.345, 2.355, 2.364, 2.373, 2.381, 2.387, 2.391, 2.393, 2.395, 2.396, 2.396, 2.397, 2.397, 2.397, 2.396, 2.394, 2.389, 2.384, 2.377, 2.37, 2.363, 2.355, 2.345, 2.335, 2.33, 2.324,
+ 2.277, 2.29, 2.303, 2.314, 2.325, 2.335, 2.346, 2.356, 2.366, 2.375, 2.384, 2.389, 2.392, 2.394, 2.395, 2.396, 2.396, 2.397, 2.397, 2.397, 2.396, 2.395, 2.392, 2.387, 2.38, 2.373, 2.365, 2.357, 2.347, 2.338, 2.332, 2.327,
+ 2.277, 2.291, 2.304, 2.315, 2.326, 2.337, 2.348, 2.358, 2.368, 2.377, 2.385, 2.39, 2.392, 2.394, 2.395, 2.396, 2.396, 2.397, 2.397, 2.398, 2.397, 2.395, 2.393, 2.39, 2.383, 2.375, 2.367, 2.358, 2.349, 2.34, 2.334, 2.329,
+ 2.278, 2.292, 2.307, 2.316, 2.326, 2.337, 2.349, 2.36, 2.371, 2.379, 2.386, 2.39, 2.392, 2.394, 2.396, 2.397, 2.397, 2.397, 2.398, 2.398, 2.396, 2.395, 2.393, 2.39, 2.384, 2.378, 2.369, 2.36, 2.351, 2.341, 2.336, 2.33,
+ 2.279, 2.294, 2.309, 2.318, 2.326, 2.338, 2.351, 2.362, 2.373, 2.381, 2.387, 2.39, 2.392, 2.394, 2.396, 2.397, 2.397, 2.397, 2.398, 2.397, 2.396, 2.395, 2.394, 2.391, 2.386, 2.38, 2.37, 2.361, 2.352, 2.343, 2.337, 2.332,
+ 2.28, 2.295, 2.31, 2.318, 2.326, 2.339, 2.351, 2.363, 2.374, 2.381, 2.386, 2.39, 2.393, 2.395, 2.396, 2.397, 2.397, 2.397, 2.397, 2.397, 2.396, 2.395, 2.394, 2.392, 2.387, 2.38, 2.372, 2.363, 2.353, 2.344, 2.338, 2.332,
+ 2.281, 2.295, 2.31, 2.319, 2.327, 2.339, 2.352, 2.363, 2.374, 2.381, 2.386, 2.39, 2.393, 2.395, 2.396, 2.396, 2.396, 2.396, 2.396, 2.397, 2.396, 2.396, 2.395, 2.392, 2.387, 2.381, 2.373, 2.364, 2.354, 2.345, 2.339, 2.333,
+ 2.282, 2.296, 2.31, 2.319, 2.328, 2.339, 2.352, 2.363, 2.374, 2.38, 2.385, 2.389, 2.394, 2.396, 2.396, 2.396, 2.395, 2.395, 2.396, 2.396, 2.397, 2.396, 2.395, 2.393, 2.387, 2.381, 2.374, 2.366, 2.355, 2.346, 2.339, 2.333,
+ 2.282, 2.297, 2.311, 2.32, 2.329, 2.34, 2.351, 2.362, 2.373, 2.38, 2.385, 2.39, 2.394, 2.395, 2.396, 2.396, 2.395, 2.395, 2.395, 2.396, 2.396, 2.396, 2.395, 2.393, 2.388, 2.382, 2.374, 2.366, 2.357, 2.348, 2.341, 2.334,
+ 2.283, 2.297, 2.312, 2.321, 2.331, 2.341, 2.351, 2.362, 2.373, 2.38, 2.386, 2.39, 2.393, 2.395, 2.395, 2.395, 2.395, 2.395, 2.395, 2.396, 2.396, 2.396, 2.395, 2.393, 2.389, 2.383, 2.375, 2.367, 2.359, 2.351, 2.343, 2.335,
+ 2.283, 2.298, 2.313, 2.322, 2.332, 2.341, 2.351, 2.361, 2.372, 2.38, 2.387, 2.391, 2.393, 2.394, 2.395, 2.395, 2.395, 2.395, 2.395, 2.395, 2.396, 2.396, 2.395, 2.393, 2.389, 2.384, 2.376, 2.367, 2.36, 2.353, 2.345, 2.336,
+ 2.285, 2.298, 2.311, 2.321, 2.331, 2.341, 2.351, 2.361, 2.371, 2.379, 2.386, 2.39, 2.393, 2.394, 2.395, 2.395, 2.395, 2.395, 2.395, 2.396, 2.396, 2.396, 2.395, 2.393, 2.389, 2.384, 2.376, 2.368, 2.361, 2.353, 2.345, 2.337,
+ 2.286, 2.298, 2.31, 2.32, 2.33, 2.34, 2.35, 2.36, 2.371, 2.378, 2.385, 2.389, 2.393, 2.394, 2.395, 2.395, 2.395, 2.395, 2.396, 2.396, 2.396, 2.396, 2.395, 2.393, 2.388, 2.383, 2.376, 2.369, 2.361, 2.353, 2.346, 2.338,
+ 2.287, 2.298, 2.308, 2.319, 2.329, 2.339, 2.349, 2.36, 2.37, 2.377, 2.384, 2.388, 2.392, 2.394, 2.395, 2.395, 2.395, 2.395, 2.396, 2.396, 2.396, 2.396, 2.395, 2.393, 2.388, 2.383, 2.376, 2.37, 2.361, 2.353, 2.346, 2.339,
+ 2.288, 2.298, 2.307, 2.317, 2.327, 2.338, 2.348, 2.358, 2.368, 2.376, 2.383, 2.388, 2.392, 2.394, 2.395, 2.396, 2.396, 2.396, 2.397, 2.397, 2.397, 2.396, 2.394, 2.392, 2.387, 2.382, 2.375, 2.368, 2.36, 2.353, 2.345, 2.338,
+ 2.289, 2.298, 2.307, 2.316, 2.326, 2.336, 2.346, 2.356, 2.367, 2.375, 2.383, 2.388, 2.391, 2.394, 2.396, 2.397, 2.397, 2.397, 2.398, 2.397, 2.397, 2.396, 2.394, 2.391, 2.387, 2.382, 2.374, 2.367, 2.359, 2.352, 2.345, 2.337,
+ 2.289, 2.297, 2.306, 2.315, 2.324, 2.334, 2.344, 2.355, 2.365, 2.374, 2.381, 2.386, 2.39, 2.393, 2.395, 2.397, 2.397, 2.398, 2.398, 2.398, 2.397, 2.396, 2.393, 2.39, 2.386, 2.381, 2.373, 2.366, 2.358, 2.351, 2.343, 2.336,
+ 2.287, 2.296, 2.304, 2.314, 2.323, 2.333, 2.342, 2.352, 2.362, 2.371, 2.379, 2.385, 2.389, 2.392, 2.394, 2.396, 2.397, 2.398, 2.398, 2.398, 2.397, 2.396, 2.393, 2.389, 2.385, 2.38, 2.373, 2.365, 2.357, 2.348, 2.341, 2.334,
+ 2.286, 2.295, 2.303, 2.312, 2.321, 2.331, 2.341, 2.35, 2.36, 2.368, 2.377, 2.383, 2.388, 2.391, 2.393, 2.395, 2.396, 2.397, 2.398, 2.398, 2.397, 2.395, 2.392, 2.388, 2.384, 2.38, 2.372, 2.365, 2.356, 2.346, 2.339, 2.333,
+ 2.286, 2.293, 2.3, 2.309, 2.318, 2.328, 2.337, 2.347, 2.356, 2.365, 2.374, 2.381, 2.385, 2.389, 2.392, 2.394, 2.395, 2.396, 2.397, 2.397, 2.395, 2.393, 2.39, 2.387, 2.383, 2.378, 2.371, 2.364, 2.354, 2.344, 2.337, 2.33,
+ 2.285, 2.29, 2.296, 2.305, 2.315, 2.324, 2.333, 2.342, 2.353, 2.362, 2.372, 2.378, 2.383, 2.386, 2.39, 2.392, 2.394, 2.395, 2.395, 2.395, 2.393, 2.391, 2.389, 2.385, 2.381, 2.376, 2.369, 2.362, 2.351, 2.341, 2.334, 2.328,
+ 2.284, 2.288, 2.292, 2.301, 2.311, 2.32, 2.328, 2.338, 2.349, 2.359, 2.369, 2.375, 2.38, 2.384, 2.388, 2.39, 2.392, 2.393, 2.394, 2.393, 2.391, 2.389, 2.387, 2.384, 2.379, 2.373, 2.367, 2.361, 2.349, 2.338, 2.332, 2.325,
+ 2.284, 2.287, 2.29, 2.299, 2.309, 2.317, 2.325, 2.334, 2.345, 2.355, 2.366, 2.373, 2.377, 2.381, 2.385, 2.388, 2.389, 2.391, 2.391, 2.391, 2.389, 2.387, 2.385, 2.382, 2.377, 2.371, 2.363, 2.355, 2.344, 2.334, 2.328, 2.323,
+ 2.283, 2.286, 2.289, 2.297, 2.306, 2.314, 2.321, 2.331, 2.341, 2.352, 2.364, 2.37, 2.375, 2.379, 2.382, 2.385, 2.386, 2.388, 2.388, 2.388, 2.387, 2.386, 2.383, 2.38, 2.374, 2.368, 2.358, 2.348, 2.338, 2.329, 2.325, 2.32,
+ 2.283, 2.285, 2.288, 2.296, 2.304, 2.311, 2.318, 2.327, 2.336, 2.348, 2.361, 2.368, 2.372, 2.376, 2.379, 2.382, 2.383, 2.384, 2.385, 2.386, 2.385, 2.384, 2.381, 2.378, 2.372, 2.365, 2.353, 2.341, 2.333, 2.325, 2.321, 2.318
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.897, 1.908, 1.918, 1.929, 1.94, 1.953, 1.966, 1.977, 1.986, 1.994, 2.001, 2.007, 2.012, 2.015, 2.018, 2.019, 2.019, 2.019, 2.018, 2.016, 2.015, 2.013, 2.01, 2.007, 2.002, 1.998, 1.993, 1.987, 1.977, 1.967, 1.956, 1.944,
+ 1.903, 1.913, 1.923, 1.934, 1.945, 1.958, 1.971, 1.981, 1.99, 1.997, 2.005, 2.01, 2.015, 2.019, 2.021, 2.023, 2.023, 2.023, 2.022, 2.02, 2.018, 2.016, 2.013, 2.009, 2.005, 2.0, 1.995, 1.989, 1.98, 1.97, 1.959, 1.948,
+ 1.909, 1.918, 1.928, 1.939, 1.951, 1.963, 1.976, 1.985, 1.993, 2.001, 2.008, 2.014, 2.019, 2.022, 2.025, 2.026, 2.027, 2.027, 2.026, 2.024, 2.022, 2.018, 2.015, 2.011, 2.007, 2.003, 1.998, 1.992, 1.982, 1.973, 1.962, 1.952,
+ 1.915, 1.924, 1.933, 1.944, 1.956, 1.968, 1.981, 1.989, 1.997, 2.005, 2.012, 2.018, 2.022, 2.025, 2.028, 2.03, 2.031, 2.031, 2.03, 2.028, 2.025, 2.022, 2.018, 2.014, 2.01, 2.006, 2.0, 1.994, 1.985, 1.976, 1.966, 1.956,
+ 1.919, 1.929, 1.939, 1.951, 1.963, 1.974, 1.986, 1.994, 2.002, 2.01, 2.017, 2.022, 2.026, 2.03, 2.032, 2.033, 2.034, 2.034, 2.033, 2.032, 2.029, 2.026, 2.022, 2.018, 2.013, 2.009, 2.003, 1.997, 1.988, 1.979, 1.969, 1.958,
+ 1.923, 1.934, 1.946, 1.957, 1.969, 1.98, 1.991, 1.999, 2.007, 2.015, 2.022, 2.027, 2.031, 2.034, 2.036, 2.037, 2.037, 2.037, 2.036, 2.035, 2.033, 2.031, 2.026, 2.022, 2.017, 2.012, 2.006, 1.999, 1.99, 1.982, 1.971, 1.961,
+ 1.926, 1.938, 1.951, 1.963, 1.974, 1.985, 1.995, 2.004, 2.012, 2.019, 2.026, 2.03, 2.034, 2.037, 2.038, 2.039, 2.04, 2.04, 2.039, 2.038, 2.037, 2.034, 2.03, 2.026, 2.02, 2.015, 2.008, 2.002, 1.993, 1.985, 1.974, 1.964,
+ 1.928, 1.941, 1.954, 1.966, 1.978, 1.989, 1.999, 2.008, 2.016, 2.023, 2.028, 2.033, 2.036, 2.039, 2.04, 2.04, 2.041, 2.042, 2.042, 2.041, 2.039, 2.037, 2.033, 2.028, 2.023, 2.018, 2.011, 2.004, 1.997, 1.989, 1.978, 1.967,
+ 1.931, 1.943, 1.956, 1.969, 1.982, 1.993, 2.003, 2.012, 2.02, 2.026, 2.031, 2.035, 2.039, 2.04, 2.041, 2.042, 2.043, 2.044, 2.044, 2.043, 2.042, 2.039, 2.035, 2.031, 2.026, 2.02, 2.014, 2.007, 2.0, 1.992, 1.981, 1.97,
+ 1.934, 1.946, 1.958, 1.972, 1.986, 1.996, 2.006, 2.015, 2.023, 2.028, 2.033, 2.037, 2.04, 2.042, 2.042, 2.043, 2.045, 2.045, 2.045, 2.045, 2.043, 2.041, 2.037, 2.033, 2.028, 2.023, 2.016, 2.009, 2.002, 1.995, 1.983, 1.972,
+ 1.937, 1.949, 1.961, 1.974, 1.989, 1.999, 2.008, 2.016, 2.025, 2.03, 2.035, 2.038, 2.041, 2.043, 2.043, 2.044, 2.045, 2.046, 2.046, 2.045, 2.044, 2.042, 2.039, 2.035, 2.03, 2.025, 2.018, 2.011, 2.003, 1.995, 1.985, 1.974,
+ 1.941, 1.952, 1.963, 1.977, 1.991, 2.001, 2.01, 2.018, 2.026, 2.032, 2.036, 2.039, 2.042, 2.044, 2.045, 2.046, 2.046, 2.047, 2.046, 2.046, 2.045, 2.044, 2.041, 2.037, 2.032, 2.027, 2.02, 2.012, 2.004, 1.996, 1.986, 1.976,
+ 1.943, 1.954, 1.966, 1.98, 1.993, 2.003, 2.011, 2.019, 2.027, 2.033, 2.037, 2.04, 2.043, 2.044, 2.046, 2.047, 2.047, 2.047, 2.047, 2.046, 2.045, 2.044, 2.041, 2.038, 2.033, 2.028, 2.021, 2.014, 2.006, 1.997, 1.987, 1.977,
+ 1.944, 1.957, 1.969, 1.982, 1.995, 2.004, 2.012, 2.02, 2.028, 2.034, 2.038, 2.041, 2.044, 2.045, 2.046, 2.047, 2.047, 2.047, 2.047, 2.046, 2.045, 2.044, 2.042, 2.039, 2.034, 2.029, 2.023, 2.016, 2.007, 1.998, 1.988, 1.978,
+ 1.946, 1.959, 1.973, 1.985, 1.997, 2.006, 2.013, 2.021, 2.029, 2.034, 2.039, 2.043, 2.045, 2.046, 2.047, 2.047, 2.048, 2.048, 2.047, 2.046, 2.045, 2.044, 2.042, 2.04, 2.035, 2.03, 2.024, 2.018, 2.008, 1.998, 1.988, 1.978,
+ 1.947, 1.96, 1.973, 1.986, 1.998, 2.007, 2.014, 2.022, 2.029, 2.035, 2.039, 2.043, 2.045, 2.046, 2.047, 2.047, 2.048, 2.048, 2.048, 2.047, 2.045, 2.044, 2.042, 2.04, 2.034, 2.029, 2.024, 2.018, 2.008, 1.998, 1.988, 1.978,
+ 1.947, 1.961, 1.974, 1.987, 1.999, 2.008, 2.015, 2.022, 2.029, 2.035, 2.039, 2.043, 2.045, 2.046, 2.047, 2.047, 2.048, 2.048, 2.048, 2.047, 2.046, 2.044, 2.042, 2.04, 2.034, 2.029, 2.023, 2.018, 2.008, 1.998, 1.988, 1.978,
+ 1.948, 1.961, 1.974, 1.987, 2.0, 2.009, 2.016, 2.023, 2.029, 2.035, 2.039, 2.043, 2.045, 2.046, 2.047, 2.047, 2.048, 2.048, 2.048, 2.047, 2.046, 2.044, 2.042, 2.039, 2.034, 2.028, 2.023, 2.017, 2.007, 1.997, 1.988, 1.978,
+ 1.948, 1.961, 1.973, 1.987, 2.0, 2.009, 2.016, 2.023, 2.029, 2.034, 2.04, 2.043, 2.045, 2.046, 2.047, 2.048, 2.048, 2.048, 2.048, 2.047, 2.046, 2.044, 2.041, 2.038, 2.033, 2.027, 2.022, 2.016, 2.006, 1.995, 1.987, 1.978,
+ 1.948, 1.96, 1.973, 1.986, 2.0, 2.009, 2.016, 2.022, 2.028, 2.034, 2.04, 2.043, 2.045, 2.046, 2.047, 2.048, 2.048, 2.048, 2.048, 2.047, 2.045, 2.043, 2.04, 2.037, 2.032, 2.026, 2.02, 2.014, 2.004, 1.994, 1.986, 1.978,
+ 1.948, 1.96, 1.972, 1.986, 2.0, 2.008, 2.016, 2.022, 2.027, 2.033, 2.039, 2.043, 2.044, 2.046, 2.047, 2.048, 2.048, 2.048, 2.048, 2.047, 2.045, 2.042, 2.039, 2.035, 2.03, 2.025, 2.019, 2.012, 2.002, 1.992, 1.985, 1.977,
+ 1.947, 1.959, 1.97, 1.984, 1.998, 2.007, 2.015, 2.021, 2.027, 2.033, 2.038, 2.041, 2.044, 2.046, 2.047, 2.047, 2.047, 2.047, 2.047, 2.045, 2.043, 2.041, 2.038, 2.034, 2.029, 2.023, 2.017, 2.01, 2.0, 1.991, 1.983, 1.975,
+ 1.946, 1.957, 1.969, 1.983, 1.997, 2.006, 2.014, 2.02, 2.027, 2.032, 2.036, 2.04, 2.044, 2.045, 2.046, 2.047, 2.047, 2.047, 2.045, 2.044, 2.042, 2.039, 2.036, 2.032, 2.027, 2.022, 2.015, 2.008, 1.999, 1.989, 1.981, 1.972,
+ 1.944, 1.956, 1.967, 1.981, 1.995, 2.004, 2.013, 2.019, 2.026, 2.03, 2.035, 2.039, 2.042, 2.044, 2.045, 2.046, 2.046, 2.046, 2.044, 2.042, 2.04, 2.037, 2.034, 2.03, 2.025, 2.019, 2.012, 2.005, 1.996, 1.987, 1.978, 1.97,
+ 1.942, 1.954, 1.966, 1.979, 1.993, 2.002, 2.011, 2.018, 2.024, 2.029, 2.033, 2.036, 2.039, 2.041, 2.043, 2.044, 2.044, 2.044, 2.042, 2.041, 2.038, 2.036, 2.031, 2.027, 2.021, 2.015, 2.009, 2.002, 1.992, 1.982, 1.975, 1.967,
+ 1.94, 1.952, 1.964, 1.977, 1.99, 2.0, 2.01, 2.017, 2.023, 2.027, 2.031, 2.034, 2.036, 2.039, 2.041, 2.043, 2.043, 2.042, 2.041, 2.039, 2.037, 2.034, 2.029, 2.024, 2.018, 2.012, 2.005, 1.998, 1.988, 1.978, 1.972, 1.965,
+ 1.937, 1.949, 1.961, 1.974, 1.987, 1.998, 2.008, 2.015, 2.021, 2.025, 2.029, 2.031, 2.033, 2.036, 2.038, 2.04, 2.04, 2.04, 2.038, 2.036, 2.034, 2.031, 2.026, 2.02, 2.015, 2.009, 2.002, 1.995, 1.984, 1.974, 1.968, 1.962,
+ 1.935, 1.946, 1.957, 1.97, 1.983, 1.995, 2.006, 2.012, 2.018, 2.022, 2.026, 2.029, 2.031, 2.033, 2.035, 2.036, 2.037, 2.037, 2.035, 2.033, 2.03, 2.027, 2.022, 2.017, 2.012, 2.006, 1.999, 1.991, 1.981, 1.97, 1.965, 1.959,
+ 1.932, 1.943, 1.953, 1.966, 1.98, 1.992, 2.004, 2.01, 2.015, 2.019, 2.023, 2.026, 2.028, 2.029, 2.031, 2.032, 2.034, 2.034, 2.032, 2.03, 2.027, 2.023, 2.019, 2.014, 2.009, 2.004, 1.996, 1.988, 1.977, 1.966, 1.961, 1.956,
+ 1.931, 1.94, 1.95, 1.963, 1.977, 1.989, 2.002, 2.008, 2.012, 2.016, 2.02, 2.023, 2.025, 2.027, 2.028, 2.03, 2.031, 2.031, 2.03, 2.028, 2.024, 2.021, 2.016, 2.012, 2.007, 2.001, 1.992, 1.984, 1.973, 1.963, 1.958, 1.953,
+ 1.929, 1.938, 1.947, 1.96, 1.974, 1.987, 1.999, 2.005, 2.009, 2.013, 2.017, 2.02, 2.022, 2.024, 2.026, 2.028, 2.028, 2.028, 2.028, 2.026, 2.022, 2.018, 2.014, 2.009, 2.004, 1.998, 1.988, 1.979, 1.969, 1.96, 1.955, 1.95,
+ 1.928, 1.936, 1.943, 1.957, 1.971, 1.984, 1.996, 2.002, 2.006, 2.01, 2.015, 2.017, 2.018, 2.021, 2.024, 2.025, 2.026, 2.026, 2.025, 2.023, 2.02, 2.016, 2.011, 2.007, 2.001, 1.995, 1.984, 1.974, 1.966, 1.958, 1.952, 1.947
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.877, 1.742, 1.606, 1.507, 1.41, 1.343, 1.281, 1.239, 1.201, 1.17, 1.141, 1.119, 1.1, 1.089, 1.081, 1.076, 1.073, 1.071, 1.07, 1.072, 1.081, 1.094, 1.118, 1.146, 1.188, 1.232, 1.285, 1.34, 1.409, 1.481, 1.593, 1.704,
+ 1.832, 1.702, 1.573, 1.479, 1.387, 1.324, 1.266, 1.224, 1.186, 1.155, 1.125, 1.104, 1.087, 1.077, 1.072, 1.068, 1.065, 1.063, 1.062, 1.063, 1.07, 1.082, 1.103, 1.13, 1.169, 1.211, 1.261, 1.314, 1.381, 1.45, 1.556, 1.662,
+ 1.786, 1.663, 1.541, 1.451, 1.364, 1.305, 1.251, 1.21, 1.171, 1.14, 1.11, 1.09, 1.074, 1.066, 1.062, 1.059, 1.058, 1.056, 1.054, 1.055, 1.059, 1.069, 1.089, 1.114, 1.15, 1.19, 1.238, 1.288, 1.352, 1.419, 1.52, 1.621,
+ 1.743, 1.627, 1.51, 1.425, 1.343, 1.288, 1.237, 1.196, 1.157, 1.125, 1.096, 1.077, 1.063, 1.056, 1.054, 1.052, 1.051, 1.05, 1.048, 1.047, 1.051, 1.059, 1.076, 1.099, 1.133, 1.17, 1.216, 1.264, 1.326, 1.39, 1.486, 1.582,
+ 1.712, 1.601, 1.49, 1.408, 1.328, 1.274, 1.225, 1.183, 1.144, 1.114, 1.086, 1.069, 1.059, 1.053, 1.052, 1.05, 1.05, 1.049, 1.048, 1.048, 1.05, 1.056, 1.07, 1.089, 1.121, 1.156, 1.2, 1.247, 1.308, 1.371, 1.463, 1.555,
+ 1.681, 1.576, 1.47, 1.391, 1.314, 1.261, 1.212, 1.171, 1.132, 1.102, 1.076, 1.062, 1.054, 1.05, 1.05, 1.049, 1.048, 1.048, 1.048, 1.049, 1.049, 1.053, 1.064, 1.08, 1.109, 1.141, 1.185, 1.23, 1.289, 1.351, 1.44, 1.528,
+ 1.655, 1.554, 1.453, 1.376, 1.301, 1.249, 1.201, 1.16, 1.12, 1.092, 1.068, 1.056, 1.051, 1.048, 1.048, 1.048, 1.047, 1.047, 1.048, 1.049, 1.049, 1.052, 1.059, 1.072, 1.099, 1.129, 1.171, 1.215, 1.274, 1.335, 1.42, 1.506,
+ 1.639, 1.539, 1.438, 1.364, 1.291, 1.239, 1.19, 1.149, 1.11, 1.085, 1.064, 1.054, 1.05, 1.048, 1.048, 1.047, 1.047, 1.047, 1.048, 1.049, 1.05, 1.052, 1.057, 1.068, 1.092, 1.12, 1.161, 1.204, 1.263, 1.324, 1.408, 1.492,
+ 1.622, 1.523, 1.424, 1.352, 1.281, 1.229, 1.18, 1.139, 1.101, 1.077, 1.059, 1.051, 1.049, 1.047, 1.047, 1.047, 1.047, 1.047, 1.048, 1.049, 1.051, 1.052, 1.055, 1.063, 1.085, 1.111, 1.151, 1.194, 1.253, 1.313, 1.395, 1.477,
+ 1.607, 1.51, 1.412, 1.342, 1.273, 1.221, 1.171, 1.131, 1.093, 1.071, 1.056, 1.05, 1.047, 1.046, 1.046, 1.046, 1.047, 1.047, 1.048, 1.05, 1.051, 1.053, 1.054, 1.061, 1.08, 1.104, 1.143, 1.185, 1.244, 1.305, 1.385, 1.466,
+ 1.594, 1.498, 1.403, 1.334, 1.268, 1.215, 1.164, 1.124, 1.086, 1.067, 1.055, 1.049, 1.046, 1.045, 1.045, 1.045, 1.046, 1.047, 1.048, 1.05, 1.051, 1.053, 1.054, 1.059, 1.077, 1.098, 1.137, 1.179, 1.237, 1.297, 1.378, 1.458,
+ 1.58, 1.487, 1.394, 1.327, 1.262, 1.208, 1.156, 1.117, 1.08, 1.062, 1.053, 1.048, 1.045, 1.044, 1.044, 1.045, 1.045, 1.046, 1.048, 1.05, 1.052, 1.053, 1.054, 1.058, 1.073, 1.092, 1.131, 1.172, 1.231, 1.29, 1.37, 1.449,
+ 1.572, 1.48, 1.388, 1.322, 1.259, 1.205, 1.152, 1.113, 1.077, 1.061, 1.052, 1.047, 1.045, 1.044, 1.044, 1.044, 1.045, 1.046, 1.047, 1.049, 1.051, 1.052, 1.053, 1.057, 1.07, 1.088, 1.127, 1.168, 1.226, 1.285, 1.364, 1.443,
+ 1.567, 1.475, 1.384, 1.319, 1.256, 1.202, 1.149, 1.11, 1.075, 1.06, 1.052, 1.047, 1.045, 1.044, 1.044, 1.044, 1.044, 1.045, 1.046, 1.048, 1.049, 1.051, 1.053, 1.057, 1.068, 1.085, 1.123, 1.165, 1.222, 1.281, 1.359, 1.438,
+ 1.561, 1.47, 1.379, 1.316, 1.253, 1.199, 1.146, 1.108, 1.073, 1.059, 1.051, 1.047, 1.045, 1.044, 1.044, 1.044, 1.044, 1.044, 1.045, 1.046, 1.047, 1.049, 1.052, 1.056, 1.066, 1.081, 1.12, 1.161, 1.218, 1.277, 1.355, 1.432,
+ 1.564, 1.472, 1.38, 1.315, 1.252, 1.199, 1.146, 1.108, 1.074, 1.06, 1.053, 1.05, 1.047, 1.046, 1.046, 1.046, 1.046, 1.046, 1.047, 1.047, 1.047, 1.048, 1.051, 1.055, 1.064, 1.079, 1.118, 1.159, 1.217, 1.276, 1.353, 1.43,
+ 1.568, 1.475, 1.382, 1.316, 1.252, 1.198, 1.147, 1.109, 1.075, 1.061, 1.055, 1.052, 1.05, 1.049, 1.049, 1.049, 1.049, 1.049, 1.048, 1.048, 1.048, 1.048, 1.049, 1.052, 1.062, 1.077, 1.116, 1.157, 1.216, 1.276, 1.352, 1.429,
+ 1.571, 1.478, 1.384, 1.317, 1.251, 1.199, 1.148, 1.11, 1.076, 1.063, 1.057, 1.054, 1.053, 1.052, 1.051, 1.051, 1.051, 1.05, 1.049, 1.048, 1.047, 1.047, 1.047, 1.05, 1.06, 1.076, 1.115, 1.156, 1.216, 1.276, 1.352, 1.428,
+ 1.575, 1.483, 1.391, 1.323, 1.257, 1.205, 1.154, 1.117, 1.083, 1.069, 1.062, 1.058, 1.056, 1.054, 1.053, 1.052, 1.051, 1.05, 1.048, 1.047, 1.046, 1.045, 1.046, 1.049, 1.061, 1.078, 1.117, 1.16, 1.22, 1.281, 1.357, 1.434,
+ 1.579, 1.488, 1.397, 1.329, 1.263, 1.211, 1.161, 1.124, 1.089, 1.075, 1.067, 1.062, 1.059, 1.056, 1.054, 1.052, 1.05, 1.049, 1.047, 1.045, 1.044, 1.043, 1.044, 1.048, 1.062, 1.08, 1.12, 1.163, 1.224, 1.286, 1.363, 1.44,
+ 1.586, 1.496, 1.405, 1.337, 1.27, 1.218, 1.168, 1.131, 1.096, 1.08, 1.072, 1.066, 1.062, 1.058, 1.056, 1.054, 1.051, 1.049, 1.047, 1.045, 1.043, 1.042, 1.043, 1.048, 1.063, 1.084, 1.124, 1.168, 1.229, 1.292, 1.369, 1.447,
+ 1.601, 1.509, 1.417, 1.347, 1.279, 1.226, 1.176, 1.138, 1.103, 1.086, 1.074, 1.068, 1.065, 1.062, 1.059, 1.057, 1.054, 1.051, 1.048, 1.046, 1.044, 1.044, 1.045, 1.051, 1.069, 1.091, 1.133, 1.177, 1.238, 1.301, 1.379, 1.457,
+ 1.615, 1.522, 1.428, 1.357, 1.288, 1.234, 1.184, 1.146, 1.11, 1.091, 1.077, 1.071, 1.068, 1.065, 1.063, 1.06, 1.056, 1.053, 1.05, 1.047, 1.046, 1.046, 1.048, 1.055, 1.074, 1.099, 1.141, 1.185, 1.248, 1.311, 1.389, 1.467,
+ 1.634, 1.538, 1.441, 1.369, 1.299, 1.245, 1.194, 1.155, 1.119, 1.098, 1.082, 1.074, 1.071, 1.068, 1.065, 1.062, 1.059, 1.055, 1.052, 1.049, 1.048, 1.048, 1.052, 1.06, 1.082, 1.108, 1.151, 1.197, 1.259, 1.323, 1.402, 1.481,
+ 1.658, 1.557, 1.457, 1.384, 1.312, 1.258, 1.206, 1.167, 1.13, 1.107, 1.088, 1.078, 1.073, 1.07, 1.067, 1.064, 1.061, 1.058, 1.055, 1.053, 1.051, 1.052, 1.057, 1.068, 1.092, 1.121, 1.165, 1.211, 1.273, 1.337, 1.417, 1.498,
+ 1.682, 1.577, 1.472, 1.398, 1.326, 1.271, 1.219, 1.179, 1.141, 1.115, 1.093, 1.082, 1.075, 1.071, 1.069, 1.067, 1.064, 1.061, 1.059, 1.057, 1.054, 1.055, 1.063, 1.076, 1.103, 1.133, 1.178, 1.225, 1.288, 1.351, 1.433, 1.515,
+ 1.717, 1.606, 1.495, 1.417, 1.342, 1.286, 1.233, 1.192, 1.154, 1.126, 1.103, 1.089, 1.079, 1.074, 1.071, 1.068, 1.065, 1.063, 1.061, 1.06, 1.058, 1.061, 1.071, 1.087, 1.116, 1.149, 1.194, 1.242, 1.304, 1.367, 1.451, 1.535,
+ 1.759, 1.64, 1.521, 1.439, 1.361, 1.302, 1.247, 1.206, 1.168, 1.139, 1.114, 1.097, 1.085, 1.077, 1.073, 1.069, 1.067, 1.065, 1.063, 1.062, 1.063, 1.068, 1.081, 1.1, 1.131, 1.166, 1.212, 1.26, 1.321, 1.384, 1.47, 1.556,
+ 1.8, 1.674, 1.547, 1.461, 1.379, 1.319, 1.262, 1.22, 1.182, 1.152, 1.125, 1.106, 1.09, 1.081, 1.075, 1.07, 1.068, 1.066, 1.065, 1.065, 1.068, 1.075, 1.092, 1.113, 1.146, 1.182, 1.23, 1.279, 1.339, 1.401, 1.489, 1.578,
+ 1.855, 1.721, 1.588, 1.495, 1.405, 1.342, 1.283, 1.239, 1.199, 1.168, 1.141, 1.12, 1.103, 1.091, 1.082, 1.077, 1.075, 1.073, 1.074, 1.076, 1.081, 1.091, 1.109, 1.132, 1.167, 1.204, 1.251, 1.3, 1.362, 1.425, 1.518, 1.611,
+ 1.912, 1.772, 1.632, 1.531, 1.433, 1.367, 1.306, 1.26, 1.217, 1.186, 1.158, 1.136, 1.117, 1.103, 1.091, 1.085, 1.082, 1.082, 1.084, 1.088, 1.096, 1.108, 1.128, 1.152, 1.188, 1.226, 1.273, 1.322, 1.386, 1.452, 1.549, 1.646,
+ 1.969, 1.822, 1.676, 1.567, 1.461, 1.392, 1.329, 1.28, 1.235, 1.203, 1.175, 1.152, 1.131, 1.115, 1.101, 1.093, 1.09, 1.091, 1.095, 1.101, 1.111, 1.125, 1.147, 1.173, 1.21, 1.248, 1.295, 1.345, 1.41, 1.478, 1.579, 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/pisp/data/uncalibrated.json b/src/ipa/rpi/pisp/data/uncalibrated.json
new file mode 100644
index 00000000..ff1e316e
--- /dev/null
+++ b/src/ipa/rpi/pisp/data/uncalibrated.json
@@ -0,0 +1,135 @@
+{
+ "version": 2.0,
+ "target": "pisp",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.awb":
+ {
+ "use_derivatives": 0,
+ "bayes": 0
+ }
+ },
+ {
+ "rpi.agc":
+ {
+ "metering_modes":
+ {
+ "centre-weighted":
+ {
+ "weights":
+ [
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 3, 4, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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/pisp/meson.build b/src/ipa/rpi/pisp/meson.build
new file mode 100644
index 00000000..878e3492
--- /dev/null
+++ b/src/ipa/rpi/pisp/meson.build
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: CC0-1.0
+
+ipa_name = 'ipa_rpi_pisp'
+
+pisp_ipa_deps = [
+ libcamera_private,
+ libatomic,
+ libpisp_dep,
+]
+
+pisp_ipa_libs = [
+ rpi_ipa_cam_helper_lib,
+ rpi_ipa_common_lib,
+ rpi_ipa_controller_lib
+]
+
+pisp_ipa_includes = [
+ ipa_includes,
+ libipa_includes,
+]
+
+pisp_ipa_sources = files([
+ 'pisp.cpp',
+])
+
+pisp_ipa_includes += include_directories('..')
+
+mod = shared_module(ipa_name, pisp_ipa_sources,
+ name_prefix : '',
+ include_directories : pisp_ipa_includes,
+ dependencies : pisp_ipa_deps,
+ link_with : libipa,
+ link_whole : pisp_ipa_libs,
+ install : true,
+ cpp_args : '-Wno-address-of-packed-member',
+ 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/pisp/pisp.cpp b/src/ipa/rpi/pisp/pisp.cpp
new file mode 100644
index 00000000..bb50a9e0
--- /dev/null
+++ b/src/ipa/rpi/pisp/pisp.cpp
@@ -0,0 +1,1068 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2023, Raspberry Pi Ltd
+ *
+ * pisp.cpp - Raspberry Pi PiSP IPA
+ */
+#include <algorithm>
+#include <cmath>
+#include <mutex>
+#include <string>
+#include <sys/mman.h>
+#include <utility>
+#include <vector>
+
+#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libipa/pwl.h>
+
+#include "libpisp/backend/backend.hpp"
+#include "libpisp/frontend/frontend.hpp"
+
+#include "common/ipa_base.h"
+#include "controller/af_status.h"
+#include "controller/agc_algorithm.h"
+#include "controller/alsc_status.h"
+#include "controller/awb_algorithm.h"
+#include "controller/awb_status.h"
+#include "controller/black_level_algorithm.h"
+#include "controller/black_level_status.h"
+#include "controller/cac_status.h"
+#include "controller/ccm_status.h"
+#include "controller/contrast_status.h"
+#include "controller/denoise_algorithm.h"
+#include "controller/denoise_status.h"
+#include "controller/dpc_status.h"
+#include "controller/geq_status.h"
+#include "controller/hdr_status.h"
+#include "controller/lux_status.h"
+#include "controller/noise_status.h"
+#include "controller/saturation_status.h"
+#include "controller/sharpen_status.h"
+#include "controller/stitch_status.h"
+#include "controller/tonemap_status.h"
+
+using namespace std::literals::chrono_literals;
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPARPI)
+
+namespace {
+
+constexpr unsigned int NumLscCells = PISP_BE_LSC_GRID_SIZE;
+constexpr unsigned int NumLscVertexes = NumLscCells + 1;
+
+inline int32_t clampField(double value, std::size_t fieldBits, std::size_t fracBits = 0,
+ bool isSigned = false, const char *desc = nullptr)
+{
+ ASSERT(fracBits <= fieldBits && fieldBits <= 32);
+
+ int min = -(isSigned << (fieldBits - 1));
+ int max = (1 << (fieldBits - isSigned)) - 1;
+ int32_t val =
+ std::clamp<int32_t>(std::round(value * (1 << fracBits)), min, max);
+
+ if (desc && val / (1 << fracBits) != value)
+ LOG(IPARPI, Warning)
+ << desc << " rounded/clamped to " << val / (1 << fracBits);
+
+ return val;
+}
+
+int generateLut(const ipa::Pwl &pwl, uint32_t *lut, std::size_t lutSize,
+ unsigned int SlopeBits = 14, unsigned int PosBits = 16)
+{
+ if (pwl.empty())
+ return -EINVAL;
+
+ int lastY = 0;
+ for (unsigned int i = 0; i < lutSize; i++) {
+ int x, y;
+ if (i < 32)
+ x = i * 512;
+ else if (i < 48)
+ x = (i - 32) * 1024 + 16384;
+ else
+ x = std::min(65535u, (i - 48) * 2048 + 32768);
+
+ y = pwl.eval(x);
+ if (y < 0 || (i && y < lastY)) {
+ LOG(IPARPI, Error)
+ << "Malformed PWL for Gamma, disabling!";
+ return -1;
+ }
+
+ if (i) {
+ unsigned int slope = y - lastY;
+ if (slope >= (1u << SlopeBits)) {
+ slope = (1u << SlopeBits) - 1;
+ LOG(IPARPI, Info)
+ << ("Maximum Gamma slope exceeded, adjusting!");
+ y = lastY + slope;
+ }
+ lut[i - 1] |= slope << PosBits;
+ }
+
+ lut[i] = y;
+ lastY = y;
+ }
+
+ return 0;
+}
+
+void packLscLut(uint32_t packed[NumLscVertexes][NumLscVertexes],
+ double const rgb[3][NumLscVertexes][NumLscVertexes])
+{
+ for (unsigned int y = 0; y < NumLscVertexes; ++y) {
+ for (unsigned int x = 0; x < NumLscVertexes; ++x) {
+ /* Jointly encode RGB gains in one of 4 ranges: [0.5:1.5), [0:2), [0:4), [0:8) */
+ double lo = std::min({ rgb[0][y][x], rgb[1][y][x], rgb[2][y][x] });
+ double hi = std::max({ rgb[0][y][x], rgb[1][y][x], rgb[2][y][x] });
+ uint32_t range;
+ double scale, offset;
+ if (lo >= 0.5 && hi < 1.5) {
+ range = 0;
+ scale = 1024.0;
+ offset = -511.5;
+ } else if (hi < 2.0) {
+ range = 1;
+ scale = 512.0;
+ offset = 0.5;
+ } else if (hi < 4.0) {
+ range = 2;
+ scale = 256.0;
+ offset = 0.5;
+ } else {
+ range = 3;
+ scale = 128.0;
+ offset = 0.5;
+ }
+ int r = clampField(offset + scale * rgb[0][y][x], 10);
+ int g = clampField(offset + scale * rgb[1][y][x], 10);
+ int b = clampField(offset + scale * rgb[2][y][x], 10);
+ packed[y][x] = (range << 30) | (b << 20) | (g << 10) | r;
+ }
+ }
+}
+
+/*
+ * Resamples a srcW x srcH table with central sampling to destW x destH with
+ * corner sampling.
+ */
+void resampleTable(double *dest, int destW, int destH, double const *src,
+ int srcW, int srcH)
+{
+ /*
+ * 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 = srcW / (destW - 1);
+ for (int i = 0; i < destW; i++, x += xInc) {
+ xLo[i] = floor(x);
+ xf[i] = x - xLo[i];
+ xHi[i] = xLo[i] < (srcW - 1) ? (xLo[i] + 1) : (srcW - 1);
+ xLo[i] = xLo[i] > 0 ? xLo[i] : 0;
+ }
+
+ /* Now march over the output table generating the new values. */
+ double y = -0.5, yInc = srcH / (destH - 1);
+ for (int j = 0; j < destH; j++, y += yInc) {
+ int yLo = floor(y);
+ double yf = y - yLo;
+ int yHi = yLo < (srcH - 1) ? (yLo + 1) : (srcH - 1);
+ yLo = yLo > 0 ? yLo : 0;
+ double const *rowAbove = src + yLo * srcW;
+ double const *rowBelow = src + yHi * srcW;
+ 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];
+ *(dest++) = above * (1 - yf) + below * yf;
+ }
+ }
+}
+
+} /* namespace */
+
+using ::libpisp::BackEnd;
+using ::libpisp::FrontEnd;
+
+namespace ipa::RPi {
+
+class IpaPiSP final : public IpaBase
+{
+public:
+ IpaPiSP()
+ : IpaBase(), fe_(nullptr), be_(nullptr)
+ {
+ }
+
+ ~IpaPiSP()
+ {
+ if (fe_)
+ munmap(fe_, sizeof(FrontEnd));
+ if (be_)
+ munmap(be_, sizeof(BackEnd));
+ }
+
+private:
+ int32_t platformInit(const InitParams &params, InitResult *result) override;
+ int32_t platformStart(const ControlList &controls, StartResult *result) override;
+ int32_t platformConfigure(const ConfigParams &params, ConfigResult *result) override;
+
+ void platformPrepareIsp(const PrepareParams &params,
+ RPiController::Metadata &rpiMetadata) override;
+ RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
+
+ void handleControls(const ControlList &controls) override;
+
+ void applyWBG(const AwbStatus *awbStatus, const AgcPrepareStatus *agcStatus,
+ pisp_be_global_config &global);
+ void applyDgOnly(const AgcPrepareStatus *agcPrepareStatus, pisp_be_global_config &global);
+ void applyCAC(const CacStatus *cacStatus, pisp_be_global_config &global);
+ void applyContrast(const ContrastStatus *contrastStatus,
+ pisp_be_global_config &global);
+ void applyCCM(const CcmStatus *ccmStatus, pisp_be_global_config &global);
+ void applyBlackLevel(const BlackLevelStatus *blackLevelStatus,
+ pisp_be_global_config &global);
+ void applyLensShading(const AlscStatus *alscStatus,
+ pisp_be_global_config &global);
+ void applyDPC(const DpcStatus *dpcStatus, pisp_be_global_config &global);
+ void applySdn(const SdnStatus *sdnStatus, pisp_be_global_config &global);
+ void applyTdn(const TdnStatus *tdnStatus, const DeviceStatus *deviceStatus,
+ pisp_be_global_config &global);
+ void applyCdn(const CdnStatus *cdnStatus, pisp_be_global_config &global);
+ void applyGeq(const GeqStatus *geqStatus, pisp_be_global_config &global);
+ void applySaturation(const SaturationStatus *geqStatus,
+ pisp_be_global_config &global);
+ void applySharpen(const SharpenStatus *sharpenStatus,
+ pisp_be_global_config &global);
+ bool applyStitch(const StitchStatus *stitchStatus, const DeviceStatus *deviceStatus,
+ const AgcStatus *agcStatus, pisp_be_global_config &global);
+ void applyTonemap(const TonemapStatus *tonemapStatus,
+ pisp_be_global_config &global);
+ void applyFocusStats(const NoiseStatus *noiseStatus);
+ void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);
+
+ void setDefaultConfig();
+ void setStatsAndDebin();
+ void setHistogramWeights();
+
+ /* Frontend/Backend objects passed in from the pipeline handler. */
+ SharedFD feFD_;
+ SharedFD beFD_;
+ FrontEnd *fe_;
+ BackEnd *be_;
+
+ /* TDN/HDR runtime need the following state. */
+ bool tdnReset_;
+ utils::Duration lastExposure_;
+ std::map<std::string, utils::Duration> lastStitchExposures_;
+ HdrStatus lastStitchHdrStatus_;
+};
+
+int32_t IpaPiSP::platformInit(const InitParams &params,
+ [[maybe_unused]] InitResult *result)
+{
+ const std::string &target = controller_.getTarget();
+ if (target != "pisp") {
+ LOG(IPARPI, Error)
+ << "Tuning data file target returned \"" << target << "\""
+ << ", expected \"pisp\"";
+ return -EINVAL;
+ }
+
+ /* Acquire the Frontend and Backend objects. */
+ feFD_ = std::move(params.fe);
+ beFD_ = std::move(params.be);
+
+ if (!feFD_.isValid() || !beFD_.isValid()) {
+ LOG(IPARPI, Error) << "Invalid FE/BE handles!";
+ return -ENODEV;
+ }
+
+ fe_ = static_cast<FrontEnd *>(mmap(nullptr, sizeof(FrontEnd),
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ feFD_.get(), 0));
+ be_ = static_cast<BackEnd *>(mmap(nullptr, sizeof(BackEnd),
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ beFD_.get(), 0));
+
+ if (!fe_ || !be_) {
+ LOG(IPARPI, Error) << "Unable to map FE/BE handles!";
+ return -ENODEV;
+ }
+
+ setDefaultConfig();
+
+ return 0;
+}
+
+int32_t IpaPiSP::platformStart([[maybe_unused]] const ControlList &controls,
+ [[maybe_unused]] StartResult *result)
+{
+ tdnReset_ = true;
+
+ /* Cause the stitch block to be reset correctly. */
+ lastStitchHdrStatus_ = HdrStatus();
+
+ return 0;
+}
+
+int32_t IpaPiSP::platformConfigure([[maybe_unused]] const ConfigParams &params,
+ [[maybe_unused]] ConfigResult *result)
+{
+ setStatsAndDebin();
+ return 0;
+}
+
+void IpaPiSP::platformPrepareIsp([[maybe_unused]] const PrepareParams &params,
+ RPiController::Metadata &rpiMetadata)
+{
+ std::scoped_lock<RPiController::Metadata> l(rpiMetadata);
+
+ pisp_be_global_config global;
+ be_->GetGlobal(global);
+
+ global.bayer_enables &= ~(PISP_BE_BAYER_ENABLE_BLC + PISP_BE_BAYER_ENABLE_WBG +
+ PISP_BE_BAYER_ENABLE_GEQ + PISP_BE_BAYER_ENABLE_LSC +
+ PISP_BE_BAYER_ENABLE_SDN + PISP_BE_BAYER_ENABLE_CDN +
+ PISP_BE_BAYER_ENABLE_TDN_OUTPUT + PISP_BE_BAYER_ENABLE_TDN_INPUT +
+ PISP_BE_BAYER_ENABLE_STITCH_INPUT + PISP_BE_BAYER_ENABLE_STITCH_OUTPUT +
+ PISP_BE_BAYER_ENABLE_STITCH + PISP_BE_BAYER_ENABLE_TONEMAP);
+ /* We leave the YCbCr and inverse conversion enabled in case of false colour or sharpening. */
+ global.rgb_enables &= ~(PISP_BE_RGB_ENABLE_GAMMA + PISP_BE_RGB_ENABLE_CCM +
+ PISP_BE_RGB_ENABLE_SHARPEN + PISP_BE_RGB_ENABLE_SAT_CONTROL);
+
+ NoiseStatus *noiseStatus = rpiMetadata.getLocked<NoiseStatus>("noise.status");
+ AgcPrepareStatus *agcPrepareStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status");
+
+ {
+ /* All Frontend config goes first, we do not want to hold the FE lock for long! */
+ std::scoped_lock<FrontEnd> lf(*fe_);
+
+ if (noiseStatus)
+ applyFocusStats(noiseStatus);
+
+ BlackLevelStatus *blackLevelStatus =
+ rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
+ if (blackLevelStatus)
+ applyBlackLevel(blackLevelStatus, global);
+
+ AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
+ if (awbStatus && agcPrepareStatus) {
+ /* Applies digital gain as well. */
+ applyWBG(awbStatus, agcPrepareStatus, global);
+ } else if (agcPrepareStatus) {
+ /* Mono sensor fallback for digital gain. */
+ applyDgOnly(agcPrepareStatus, global);
+ }
+ }
+
+ CacStatus *cacStatus = rpiMetadata.getLocked<CacStatus>("cac.status");
+ if (cacStatus)
+ applyCAC(cacStatus, global);
+
+ ContrastStatus *contrastStatus =
+ rpiMetadata.getLocked<ContrastStatus>("contrast.status");
+ if (contrastStatus)
+ applyContrast(contrastStatus, global);
+
+ CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
+ if (ccmStatus)
+ applyCCM(ccmStatus, global);
+
+ AlscStatus *alscStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
+ if (alscStatus)
+ applyLensShading(alscStatus, global);
+
+ DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
+ if (dpcStatus)
+ applyDPC(dpcStatus, global);
+
+ SdnStatus *sdnStatus = rpiMetadata.getLocked<SdnStatus>("sdn.status");
+ if (sdnStatus)
+ applySdn(sdnStatus, global);
+
+ DeviceStatus *deviceStatus = rpiMetadata.getLocked<DeviceStatus>("device.status");
+ TdnStatus *tdnStatus = rpiMetadata.getLocked<TdnStatus>("tdn.status");
+ if (tdnStatus && deviceStatus)
+ applyTdn(tdnStatus, deviceStatus, global);
+
+ CdnStatus *cdnStatus = rpiMetadata.getLocked<CdnStatus>("cdn.status");
+ if (cdnStatus)
+ applyCdn(cdnStatus, global);
+
+ GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
+ if (geqStatus)
+ applyGeq(geqStatus, global);
+
+ SaturationStatus *saturationStatus =
+ rpiMetadata.getLocked<SaturationStatus>("saturation.status");
+ if (saturationStatus)
+ applySaturation(saturationStatus, global);
+
+ SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
+ if (sharpenStatus)
+ applySharpen(sharpenStatus, global);
+
+ StitchStatus *stitchStatus = rpiMetadata.getLocked<StitchStatus>("stitch.status");
+ if (stitchStatus) {
+ /*
+ * Note that it's the *delayed* AGC status that contains the HDR mode/channel
+ * info that pertains to this frame!
+ */
+ AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status");
+ /* prepareIsp() will fetch this value. Maybe pass it back differently? */
+ stitchSwapBuffers_ = applyStitch(stitchStatus, deviceStatus, agcStatus, global);
+ } else
+ lastStitchHdrStatus_ = HdrStatus();
+
+ TonemapStatus *tonemapStatus = rpiMetadata.getLocked<TonemapStatus>("tonemap.status");
+ if (tonemapStatus)
+ applyTonemap(tonemapStatus, global);
+
+ be_->SetGlobal(global);
+
+ /* Save this for TDN and HDR on the next frame. */
+ lastExposure_ = deviceStatus->exposureTime * deviceStatus->analogueGain;
+
+ /* Lens control */
+ const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
+ if (afStatus) {
+ ControlList lensctrls(lensCtrls_);
+ applyAF(afStatus, lensctrls);
+ if (!lensctrls.empty())
+ setLensControls.emit(lensctrls);
+ }
+}
+
+RPiController::StatisticsPtr IpaPiSP::platformProcessStats(Span<uint8_t> mem)
+{
+ using namespace RPiController;
+
+ const pisp_statistics *stats = reinterpret_cast<pisp_statistics *>(mem.data());
+
+ unsigned int i;
+ StatisticsPtr statistics =
+ std::make_unique<Statistics>(Statistics::AgcStatsPos::PostWb,
+ Statistics::ColourStatsPos::PreLsc);
+
+ /* RGB histograms are not used, so do not populate them. */
+ statistics->yHist = RPiController::Histogram(stats->agc.histogram,
+ PISP_AGC_STATS_NUM_BINS);
+
+ statistics->awbRegions.init({ PISP_AWB_STATS_SIZE, PISP_AWB_STATS_SIZE });
+ for (i = 0; i < statistics->awbRegions.numRegions(); i++)
+ statistics->awbRegions.set(i, { { stats->awb.zones[i].R_sum,
+ stats->awb.zones[i].G_sum,
+ stats->awb.zones[i].B_sum },
+ stats->awb.zones[i].counted, 0 });
+
+ /* AGC region sums only get collected on floating zones. */
+ statistics->agcRegions.init({ 0, 0 }, PISP_FLOATING_STATS_NUM_ZONES);
+ for (i = 0; i < statistics->agcRegions.numRegions(); i++)
+ statistics->agcRegions.setFloating(i,
+ { { 0, 0, 0, stats->agc.floating[i].Y_sum },
+ stats->agc.floating[i].counted, 0 });
+
+ statistics->focusRegions.init({ PISP_CDAF_STATS_SIZE, PISP_CDAF_STATS_SIZE });
+ for (i = 0; i < statistics->focusRegions.numRegions(); i++)
+ statistics->focusRegions.set(i, { stats->cdaf.foms[i] >> 20, 0, 0 });
+
+ if (statsMetadataOutput_) {
+ Span<const uint8_t> statsSpan(reinterpret_cast<const uint8_t *>(stats),
+ sizeof(pisp_statistics));
+ libcameraMetadata_.set(controls::rpi::PispStatsOutput, statsSpan);
+ }
+
+ return statistics;
+}
+
+void IpaPiSP::handleControls(const ControlList &controls)
+{
+ for (auto const &ctrl : controls) {
+ switch (ctrl.first) {
+ case controls::HDR_MODE:
+ case controls::AE_METERING_MODE:
+ setHistogramWeights();
+ break;
+
+ case controls::draft::NOISE_REDUCTION_MODE: {
+ RPiController::DenoiseAlgorithm *denoise = dynamic_cast<RPiController::DenoiseAlgorithm *>(
+ controller_.getAlgorithm("denoise"));
+
+ if (!denoise) {
+ LOG(IPARPI, Warning)
+ << "Could not set NOISE_REDUCTION_MODE - no Denoise algorithm";
+ return;
+ }
+
+ if (ctrl.second.get<int32_t>() == controls::draft::NoiseReductionModeOff)
+ denoise->setMode(RPiController::DenoiseMode::Off);
+ else
+ denoise->setMode(RPiController::DenoiseMode::ColourHighQuality);
+
+ break;
+ }
+ }
+ }
+}
+
+void IpaPiSP::applyWBG(const AwbStatus *awbStatus, const AgcPrepareStatus *agcPrepareStatus,
+ pisp_be_global_config &global)
+{
+ pisp_wbg_config wbg;
+ pisp_fe_rgby_config rgby = {};
+ double dg = agcPrepareStatus ? agcPrepareStatus->digitalGain : 1.0;
+
+ wbg.gain_r = clampField(dg * awbStatus->gainR, 14, 10);
+ wbg.gain_g = clampField(dg * awbStatus->gainG, 14, 10);
+ wbg.gain_b = clampField(dg * awbStatus->gainB, 14, 10);
+
+ /*
+ * The YCbCr conversion block should contain the appropriate YCbCr
+ * matrix. We should not rely on the CSC0 block as that might be
+ * programmed for RGB outputs.
+ */
+ pisp_be_ccm_config csc;
+ be_->GetYcbcr(csc);
+
+ /* The CSC coefficients already have the << 10 scaling applied. */
+ rgby.gain_r = clampField(csc.coeffs[0] * awbStatus->gainR, 14);
+ rgby.gain_g = clampField(csc.coeffs[1] * awbStatus->gainG, 14);
+ rgby.gain_b = clampField(csc.coeffs[2] * awbStatus->gainB, 14);
+
+ LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
+ << awbStatus->gainB;
+
+ be_->SetWbg(wbg);
+ fe_->SetRGBY(rgby);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_WBG;
+}
+
+void IpaPiSP::applyDgOnly(const AgcPrepareStatus *agcPrepareStatus, pisp_be_global_config &global)
+{
+ pisp_wbg_config wbg;
+
+ wbg.gain_r = clampField(agcPrepareStatus->digitalGain, 14, 10);
+ wbg.gain_g = clampField(agcPrepareStatus->digitalGain, 14, 10);
+ wbg.gain_b = clampField(agcPrepareStatus->digitalGain, 14, 10);
+
+ LOG(IPARPI, Debug) << "Applying DG (only) : " << agcPrepareStatus->digitalGain;
+
+ be_->SetWbg(wbg);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_WBG;
+}
+
+void IpaPiSP::applyContrast(const ContrastStatus *contrastStatus,
+ pisp_be_global_config &global)
+{
+ pisp_be_gamma_config gamma;
+
+ if (!generateLut(contrastStatus->gammaCurve, gamma.lut, PISP_BE_GAMMA_LUT_SIZE)) {
+ be_->SetGamma(gamma);
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_GAMMA;
+ }
+}
+
+void IpaPiSP::applyCCM(const CcmStatus *ccmStatus, pisp_be_global_config &global)
+{
+ pisp_be_ccm_config ccm = {};
+
+ for (unsigned int i = 0; i < 9; i++)
+ ccm.coeffs[i] = clampField(ccmStatus->matrix[i], 14, 10, true);
+
+ be_->SetCcm(ccm);
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_CCM;
+}
+
+void IpaPiSP::applyCAC(const CacStatus *cacStatus, pisp_be_global_config &global)
+{
+ pisp_be_cac_config cac = {};
+
+ for (int x = 0; x < PISP_BE_CAC_GRID_SIZE + 1; x++) {
+ for (int y = 0; y < PISP_BE_CAC_GRID_SIZE + 1; y++) {
+ cac.lut[y][x][0][0] = clampField(cacStatus->lutRx[y * (PISP_BE_CAC_GRID_SIZE + 1) + x], 7, 5, true);
+ cac.lut[y][x][0][1] = clampField(cacStatus->lutRy[y * (PISP_BE_CAC_GRID_SIZE + 1) + x], 7, 5, true);
+ cac.lut[y][x][1][0] = clampField(cacStatus->lutBx[y * (PISP_BE_CAC_GRID_SIZE + 1) + x], 7, 5, true);
+ cac.lut[y][x][1][1] = clampField(cacStatus->lutBy[y * (PISP_BE_CAC_GRID_SIZE + 1) + x], 7, 5, true);
+ }
+ }
+
+ be_->SetCac(cac);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_CAC;
+}
+
+void IpaPiSP::applyBlackLevel(const BlackLevelStatus *blackLevelStatus, pisp_be_global_config &global)
+{
+ uint16_t minBlackLevel = std::min({ blackLevelStatus->blackLevelR, blackLevelStatus->blackLevelG,
+ blackLevelStatus->blackLevelB });
+ pisp_bla_config bla;
+
+ /*
+ * Set the Frontend to adjust the black level to the smallest black level
+ * of all channels (in 16-bits).
+ */
+ bla.black_level_r = blackLevelStatus->blackLevelR;
+ bla.black_level_gr = blackLevelStatus->blackLevelG;
+ bla.black_level_gb = blackLevelStatus->blackLevelG;
+ bla.black_level_b = blackLevelStatus->blackLevelB;
+ bla.output_black_level = minBlackLevel;
+ fe_->SetBla(bla);
+
+ /* Frontend Stats and Backend black level correction. */
+ bla.black_level_r = bla.black_level_gr =
+ bla.black_level_gb = bla.black_level_b = minBlackLevel;
+ bla.output_black_level = 0;
+ fe_->SetBlc(bla);
+ be_->SetBlc(bla);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_BLC;
+}
+
+void IpaPiSP::applyLensShading(const AlscStatus *alscStatus,
+ pisp_be_global_config &global)
+{
+ pisp_be_lsc_extra lscExtra = {};
+ pisp_be_lsc_config lsc = {};
+ double rgb[3][NumLscVertexes][NumLscVertexes] = {};
+
+ resampleTable(&rgb[0][0][0], NumLscVertexes, NumLscVertexes,
+ alscStatus->r.data(), NumLscCells, NumLscCells);
+ resampleTable(&rgb[1][0][0], NumLscVertexes, NumLscVertexes,
+ alscStatus->g.data(), NumLscCells, NumLscCells);
+ resampleTable(&rgb[2][0][0], NumLscVertexes, NumLscVertexes,
+ alscStatus->b.data(), NumLscCells, NumLscCells);
+ packLscLut(lsc.lut_packed, rgb);
+ be_->SetLsc(lsc, lscExtra);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_LSC;
+}
+
+void IpaPiSP::applyDPC(const DpcStatus *dpcStatus, pisp_be_global_config &global)
+{
+ pisp_be_dpc_config dpc = {};
+
+ switch (dpcStatus->strength) {
+ case 0: /* "off" */
+ break;
+ case 1: /* "normal" */
+ dpc.coeff_level = 1;
+ dpc.coeff_range = 8;
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_DPC;
+ break;
+ case 2: /* "strong" */
+ dpc.coeff_level = 0;
+ dpc.coeff_range = 0;
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_DPC;
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ be_->SetDpc(dpc);
+}
+
+void IpaPiSP::applySdn(const SdnStatus *sdnStatus, pisp_be_global_config &global)
+{
+ pisp_be_sdn_config sdn = {};
+ pisp_bla_config blc;
+
+ be_->GetBlc(blc);
+ /* All R/G/B black levels are the same value in the BE after FE alignment */
+ sdn.black_level = blc.black_level_r;
+ /* leakage is "amount of the original pixel we let through", thus 1 - strength */
+ sdn.leakage = clampField(1.0 - sdnStatus->strength, 8, 8);
+ sdn.noise_constant = clampField(sdnStatus->noiseConstant, 16);
+ sdn.noise_slope = clampField(sdnStatus->noiseSlope, 16, 8);
+ sdn.noise_constant2 = clampField(sdnStatus->noiseConstant2, 16);
+ sdn.noise_slope2 = clampField(sdnStatus->noiseSlope2, 16, 8);
+ be_->SetSdn(sdn);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_SDN;
+}
+
+void IpaPiSP::applyTdn(const TdnStatus *tdnStatus, const DeviceStatus *deviceStatus,
+ pisp_be_global_config &global)
+{
+ utils::Duration exposure = deviceStatus->exposureTime * deviceStatus->analogueGain;
+ pisp_be_tdn_config tdn = {};
+
+ double ratio = tdnReset_ ? 1.0 : exposure / lastExposure_;
+ if (ratio >= 4.0) {
+ /* If the exposure ratio goes above 4x, we need to reset TDN. */
+ ratio = 1;
+ tdnReset_ = true;
+ }
+
+ LOG(IPARPI, Debug) << "TDN: exposure: " << exposure
+ << " last: " << lastExposure_
+ << " ratio: " << ratio;
+
+ pisp_bla_config blc;
+ be_->GetBlc(blc);
+ /* All R/G/B black levels are the same value in the BE after FE alignment */
+ tdn.black_level = blc.black_level_r;
+ tdn.ratio = clampField(ratio, 16, 14);
+ tdn.noise_constant = clampField(tdnStatus->noiseConstant, 16);
+ tdn.noise_slope = clampField(tdnStatus->noiseSlope, 16, 8);
+ tdn.threshold = clampField(tdnStatus->threshold, 16, 16);
+
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_TDN + PISP_BE_BAYER_ENABLE_TDN_OUTPUT;
+
+ /* Only enable the TDN Input after a state reset. */
+ if (!tdnReset_) {
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_TDN_INPUT;
+ tdn.reset = 0;
+ } else
+ tdn.reset = 1;
+
+ be_->SetTdn(tdn);
+ tdnReset_ = false;
+}
+
+void IpaPiSP::applyCdn(const CdnStatus *cdnStatus, pisp_be_global_config &global)
+{
+ pisp_be_cdn_config cdn = {};
+
+ cdn.thresh = clampField(cdnStatus->threshold, 16);
+ cdn.iir_strength = clampField(cdnStatus->strength, 8, 8);
+ cdn.g_adjust = clampField(0, 8, 8);
+ be_->SetCdn(cdn);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_CDN;
+}
+
+void IpaPiSP::applyGeq(const GeqStatus *geqStatus, pisp_be_global_config &global)
+{
+ pisp_be_geq_config geq = {};
+
+ geq.min = 0;
+ geq.max = 0xffff;
+ geq.offset = clampField(geqStatus->offset, 16);
+ geq.slope_sharper = clampField(geqStatus->slope, 10, 10);
+ be_->SetGeq(geq);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_GEQ;
+}
+
+void IpaPiSP::applySaturation(const SaturationStatus *saturationStatus,
+ pisp_be_global_config &global)
+{
+ pisp_be_sat_control_config saturation;
+ pisp_wbg_config wbg;
+
+ saturation.shift_r = std::min<uint8_t>(2, saturationStatus->shiftR);
+ saturation.shift_g = std::min<uint8_t>(2, saturationStatus->shiftG);
+ saturation.shift_b = std::min<uint8_t>(2, saturationStatus->shiftB);
+ be_->SetSatControl(saturation);
+
+ be_->GetWbg(wbg);
+ wbg.gain_r >>= saturationStatus->shiftR;
+ wbg.gain_g >>= saturationStatus->shiftG;
+ wbg.gain_b >>= saturationStatus->shiftB;
+ be_->SetWbg(wbg);
+
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_SAT_CONTROL;
+}
+
+void IpaPiSP::applySharpen(const SharpenStatus *sharpenStatus,
+ pisp_be_global_config &global)
+{
+ /*
+ * This threshold scaling is to normalise the VC4 and PiSP parameter
+ * scales in the tuning config.
+ */
+ static constexpr double ThresholdScaling = 0.25;
+ const double scaling = sharpenStatus->threshold * ThresholdScaling;
+
+ pisp_be_sh_fc_combine_config shfc;
+ pisp_be_sharpen_config sharpen;
+
+ be_->InitialiseSharpen(sharpen, shfc);
+ sharpen.threshold_offset0 = clampField(sharpen.threshold_offset0 * scaling, 16);
+ sharpen.threshold_offset1 = clampField(sharpen.threshold_offset1 * scaling, 16);
+ sharpen.threshold_offset2 = clampField(sharpen.threshold_offset2 * scaling, 16);
+ sharpen.threshold_offset3 = clampField(sharpen.threshold_offset3 * scaling, 16);
+ sharpen.threshold_offset4 = clampField(sharpen.threshold_offset4 * scaling, 16);
+ sharpen.threshold_slope0 = clampField(sharpen.threshold_slope0 * scaling, 12);
+ sharpen.threshold_slope1 = clampField(sharpen.threshold_slope1 * scaling, 12);
+ sharpen.threshold_slope2 = clampField(sharpen.threshold_slope2 * scaling, 12);
+ sharpen.threshold_slope3 = clampField(sharpen.threshold_slope3 * scaling, 12);
+ sharpen.threshold_slope4 = clampField(sharpen.threshold_slope4 * scaling, 12);
+ sharpen.positive_strength = clampField(sharpen.positive_strength * sharpenStatus->strength, 12);
+ sharpen.negative_strength = clampField(sharpen.negative_strength * sharpenStatus->strength, 12);
+ sharpen.positive_pre_limit = clampField(sharpen.positive_pre_limit * sharpenStatus->limit, 16);
+ sharpen.positive_limit = clampField(sharpen.positive_limit * sharpenStatus->limit, 16);
+ sharpen.negative_pre_limit = clampField(sharpen.negative_pre_limit * sharpenStatus->limit, 16);
+ sharpen.negative_limit = clampField(sharpen.negative_limit * sharpenStatus->limit, 16);
+
+ be_->SetSharpen(sharpen);
+ /* The conversion to YCbCr and back is always enabled. */
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_SHARPEN;
+}
+
+bool IpaPiSP::applyStitch(const StitchStatus *stitchStatus, const DeviceStatus *deviceStatus,
+ const AgcStatus *agcStatus, pisp_be_global_config &global)
+{
+ /*
+ * Find out what HDR mode/channel this frame is. Normally this will be in the delayed
+ * HDR status (in the AGC status), though after a mode switch this will be absent and
+ * the information will have been stored in the hdrStatus_ field.
+ */
+ const HdrStatus *hdrStatus = &hdrStatus_;
+ if (agcStatus)
+ hdrStatus = &agcStatus->hdr;
+
+ bool modeChange = hdrStatus->mode != lastStitchHdrStatus_.mode;
+ bool channelChange = !modeChange && hdrStatus->channel != lastStitchHdrStatus_.channel;
+ lastStitchHdrStatus_ = *hdrStatus;
+
+ /* Check for a change of HDR mode. That forces us to start over. */
+ if (modeChange)
+ lastStitchExposures_.clear();
+
+ if (hdrStatus->channel != "short" && hdrStatus->channel != "long") {
+ /* The channel *must* be long or short, anything else does not make sense. */
+ LOG(IPARPI, Warning) << "Stitch channel is not long or short";
+ return false;
+ }
+
+ /* Whatever happens, we're going to output this buffer now. */
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_STITCH_OUTPUT;
+
+ utils::Duration exposure = deviceStatus->exposureTime * deviceStatus->analogueGain;
+ lastStitchExposures_[hdrStatus->channel] = exposure;
+
+ /* If the other channel hasn't been seen there's nothing more we can do. */
+ std::string otherChannel = hdrStatus->channel == "short" ? "long" : "short";
+ if (lastStitchExposures_.find(otherChannel) == lastStitchExposures_.end()) {
+ /* The first channel should be "short". */
+ if (hdrStatus->channel != "short")
+ LOG(IPARPI, Warning) << "First frame is not short";
+ return false;
+ }
+
+ /* We have both channels, we need to enable stitching. */
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_STITCH_INPUT + PISP_BE_BAYER_ENABLE_STITCH;
+
+ utils::Duration otherExposure = lastStitchExposures_[otherChannel];
+ bool phaseLong = hdrStatus->channel == "long";
+ double ratio = phaseLong ? otherExposure / exposure : exposure / otherExposure;
+
+ pisp_be_stitch_config stitch = {};
+ stitch.exposure_ratio = clampField(ratio, 15, 15);
+ if (phaseLong)
+ stitch.exposure_ratio |= PISP_BE_STITCH_STREAMING_LONG;
+ /* These will be filled in correctly once we have implemented the HDR algorithm. */
+ stitch.threshold_lo = stitchStatus->thresholdLo;
+ stitch.threshold_diff_power = stitchStatus->diffPower;
+ stitch.motion_threshold_256 = stitchStatus->motionThreshold;
+ be_->SetStitch(stitch);
+
+ return channelChange;
+}
+
+void IpaPiSP::applyTonemap(const TonemapStatus *tonemapStatus, pisp_be_global_config &global)
+{
+ pisp_be_tonemap_config tonemap = {};
+
+ tonemap.detail_constant = clampField(tonemapStatus->detailConstant, 16);
+ tonemap.detail_slope = clampField(tonemapStatus->detailSlope, 16, 8);
+ tonemap.iir_strength = clampField(tonemapStatus->iirStrength, 12, 4);
+ tonemap.strength = clampField(tonemapStatus->strength, 12, 8);
+
+ if (!generateLut(tonemapStatus->tonemap, tonemap.lut, PISP_BE_TONEMAP_LUT_SIZE)) {
+ be_->SetTonemap(tonemap);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_TONEMAP;
+ }
+}
+
+void IpaPiSP::applyFocusStats(const NoiseStatus *noiseStatus)
+{
+ pisp_fe_cdaf_stats_config cdaf;
+ fe_->GetCdafStats(cdaf);
+
+ cdaf.noise_constant = noiseStatus->noiseConstant;
+ cdaf.noise_slope = noiseStatus->noiseSlope;
+ fe_->SetCdafStats(cdaf);
+}
+
+void IpaPiSP::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
+{
+ if (afStatus->lensSetting) {
+ ControlValue v(afStatus->lensSetting.value());
+ lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v);
+ }
+}
+
+void IpaPiSP::setDefaultConfig()
+{
+ std::scoped_lock<FrontEnd> l(*fe_);
+
+ pisp_be_global_config beGlobal;
+ pisp_fe_global_config feGlobal;
+
+ fe_->GetGlobal(feGlobal);
+ be_->GetGlobal(beGlobal);
+ /*
+ * Always go to YCbCr and back. We need them if the false colour block is enabled,
+ * and even for mono sensors if sharpening is enabled. So we're better off enabling
+ * them all the time.
+ */
+ beGlobal.rgb_enables |= PISP_BE_RGB_ENABLE_YCBCR + PISP_BE_RGB_ENABLE_YCBCR_INVERSE;
+
+ if (!monoSensor()) {
+ beGlobal.bayer_enables |= PISP_BE_BAYER_ENABLE_DEMOSAIC;
+ beGlobal.rgb_enables |= PISP_BE_RGB_ENABLE_FALSE_COLOUR;
+ }
+
+ /*
+ * Ask the AWB algorithm for reasonable gain values so that we can program the
+ * front end stats sensibly. We must also factor in the conversion to luminance.
+ */
+ pisp_fe_rgby_config rgby = {};
+ double gainR = 1.5, gainB = 1.5;
+ RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
+ controller_.getAlgorithm("awb"));
+ if (awb)
+ awb->initialValues(gainR, gainB);
+ /* The BT.601 RGB -> Y coefficients will do. The precise values are not critical. */
+ rgby.gain_r = clampField(gainR * 0.299, 14, 10);
+ rgby.gain_g = clampField(1.0 * .587, 14, 10);
+ rgby.gain_b = clampField(gainB * .114, 14, 10);
+ fe_->SetRGBY(rgby);
+ feGlobal.enables |= PISP_FE_ENABLE_RGBY;
+
+ /* Also get sensible front end black level defaults, for the same reason. */
+ RPiController::BlackLevelAlgorithm *blackLevel = dynamic_cast<RPiController::BlackLevelAlgorithm *>(
+ controller_.getAlgorithm("black_level"));
+ if (blackLevel) {
+ uint16_t blackLevelR, blackLevelG, blackLevelB;
+ BlackLevelStatus blackLevelStatus;
+
+ blackLevel->initialValues(blackLevelR, blackLevelG, blackLevelB);
+ blackLevelStatus.blackLevelR = blackLevelR;
+ blackLevelStatus.blackLevelG = blackLevelG;
+ blackLevelStatus.blackLevelB = blackLevelB;
+ applyBlackLevel(&blackLevelStatus, beGlobal);
+ feGlobal.enables |= PISP_FE_ENABLE_BLA + PISP_FE_ENABLE_BLC;
+ }
+
+ fe_->SetGlobal(feGlobal);
+ be_->SetGlobal(beGlobal);
+}
+
+void IpaPiSP::setStatsAndDebin()
+{
+ pisp_fe_crop_config crop{ 0, 0, mode_.width, mode_.height };
+
+ pisp_fe_awb_stats_config awb = {};
+ awb.r_lo = awb.g_lo = awb.b_lo = 0;
+ awb.r_hi = awb.g_hi = awb.b_hi = 65535 * 0.98;
+
+ pisp_fe_cdaf_stats_config cdaf = {};
+ cdaf.mode = (1 << 4) + (1 << 2) + 1; /* Gr / Gb count with weights of (1, 1) */
+
+ {
+ std::scoped_lock<FrontEnd> l(*fe_);
+ pisp_fe_global_config feGlobal;
+ fe_->GetGlobal(feGlobal);
+ feGlobal.enables |= PISP_FE_ENABLE_AWB_STATS + PISP_FE_ENABLE_AGC_STATS +
+ PISP_FE_ENABLE_CDAF_STATS;
+
+ fe_->SetGlobal(feGlobal);
+ fe_->SetStatsCrop(crop);
+ fe_->SetAwbStats(awb);
+ fe_->SetCdafStats(cdaf);
+ }
+
+ /*
+ * Apply the correct AGC region weights to the Frontend. Need to do this
+ * out of the Frontend scoped lock.
+ */
+ setHistogramWeights();
+
+ pisp_be_global_config beGlobal;
+ be_->GetGlobal(beGlobal);
+
+ if (mode_.binX > 1 || mode_.binY > 1) {
+ pisp_be_debin_config debin;
+
+ be_->GetDebin(debin);
+ debin.h_enable = (mode_.binX > 1);
+ debin.v_enable = (mode_.binY > 1);
+ be_->SetDebin(debin);
+ beGlobal.bayer_enables |= PISP_BE_BAYER_ENABLE_DEBIN;
+ } else
+ beGlobal.bayer_enables &= ~PISP_BE_BAYER_ENABLE_DEBIN;
+
+ be_->SetGlobal(beGlobal);
+}
+
+void IpaPiSP::setHistogramWeights()
+{
+ RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
+ controller_.getAlgorithm("agc"));
+ if (!agc)
+ return;
+
+ const std::vector<double> &weights = agc->getWeights();
+
+ pisp_fe_agc_stats_config config;
+ memset(&config, 0, sizeof(config));
+
+ /*
+ * The AGC software gives us a 15x15 table of weights which we
+ * map onto 16x16 in the hardware, ensuring the rightmost column
+ * and bottom row all have zero weight. We align everything to
+ * the native 2x2 Bayer pixel blocks.
+ */
+ const Size &size = controller_.getHardwareConfig().agcZoneWeights;
+ int width = (mode_.width / size.width) & ~1;
+ int height = (mode_.height / size.height) & ~1;
+ config.offset_x = ((mode_.width - size.width * width) / 2) & ~1;
+ config.offset_y = ((mode_.height - size.height * height) / 2) & ~1;
+ config.size_x = width;
+ config.size_y = height;
+
+ unsigned int idx = 0;
+ for (unsigned int row = 0; row < size.height; row++) {
+ unsigned int col = 0;
+ for (; col < size.width / 2; col++) {
+ int wt0 = clampField(weights[idx++], 4, 0, false, "agc weights");
+ int wt1 = clampField(weights[idx++], 4, 0, false, "agc weights");
+ config.weights[row * 8 + col] = (wt1 << 4) | wt0;
+ }
+ if (size.width & 1)
+ config.weights[row * 8 + col] =
+ clampField(weights[idx++], 4, 0, false, "agc weights");
+ }
+
+ std::scoped_lock<FrontEnd> l(*fe_);
+ fe_->SetAgcStats(config);
+}
+
+} /* namespace ipa::RPi */
+
+/*
+ * External IPA module interface
+ */
+extern "C" {
+const IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 1,
+ "rpi/pisp",
+ "rpi/pisp",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::RPi::IpaPiSP();
+}
+
+} /* extern "C" */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rpi/vc4/data/imx219.json b/src/ipa/rpi/vc4/data/imx219.json
index e8fce164..a020b12f 100644
--- a/src/ipa/rpi/vc4/data/imx219.json
+++ b/src/ipa/rpi/vc4/data/imx219.json
@@ -131,93 +131,306 @@
{
"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":
+ "channels": [
{
- "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 ]
+ "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
+ ]
},
- "long":
{
- "shutter": [ 100, 10000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 0.125,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
+ "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":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ ]
+ }
]
- }
- ],
- "shadows": [
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "base_ev": 1.5,
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.0,
- "q_hi": 0.5,
- "y_target":
- [
- 0, 0.17,
- 1000, 0.17
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
]
}
},
@@ -463,6 +676,20 @@
},
{
"rpi.sharpen": { }
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-} \ 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
index cfedb943..d8bc9639 100644
--- a/src/ipa/rpi/vc4/data/imx219_noir.json
+++ b/src/ipa/rpi/vc4/data/imx219_noir.json
@@ -47,93 +47,306 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 5000, 10000, 20000, 33333 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ "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
+ ]
},
- "long":
{
- "shutter": [ 100, 10000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 0.125,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
+ "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":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ ]
+ }
]
- }
- ],
- "shadows": [
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.0,
- "q_hi": 0.5,
- "y_target":
- [
- 0, 0.17,
- 1000, 0.17
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
]
}
},
@@ -397,6 +610,20 @@
},
{
"rpi.sharpen": { }
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-} \ No newline at end of file
+}
diff --git a/src/ipa/rpi/vc4/data/imx283.json b/src/ipa/rpi/vc4/data/imx283.json
new file mode 100644
index 00000000..89e945cc
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/imx283.json
@@ -0,0 +1,548 @@
+{
+ "version": 2.0,
+ "target": "bcm2835",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3200
+ }
+ },
+ {
+ "rpi.dpc": { }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 10857,
+ "reference_gain": 1.49,
+ "reference_aperture": 1.0,
+ "reference_lux": 1050,
+ "reference_Y": 13959
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.147
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 249,
+ "slope": 0.02036
+ }
+ },
+ {
+ "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": 6000,
+ "hi": 6800
+ }
+ },
+ "bayes": 1,
+ "ct_curve":
+ [
+ 2500.0, 0.9429, 0.2809,
+ 2820.0, 0.8488, 0.3472,
+ 2830.0, 0.8303, 0.3609,
+ 2885.0, 0.8177, 0.3703,
+ 3601.0, 0.6935, 0.4705,
+ 3615.0, 0.6918, 0.4719,
+ 3622.0, 0.6894, 0.4741,
+ 4345.0, 0.5999, 0.5546,
+ 4410.0, 0.5942, 0.5601,
+ 4486.0, 0.5878, 0.5661,
+ 4576.0, 0.5779, 0.5756,
+ 5672.0, 0.5211, 0.6318,
+ 5710.0, 0.5168, 0.6362,
+ 6850.0, 0.4841, 0.6702
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.02601,
+ "transverse_neg": 0.0246
+ }
+ },
+ {
+ "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": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 2940,
+ "table":
+ [
+ 1.021, 1.026, 1.028, 1.029, 1.031, 1.029, 1.029, 1.029, 1.029, 1.031, 1.031, 1.028, 1.027, 1.022, 1.013, 1.008,
+ 1.022, 1.026, 1.027, 1.028, 1.027, 1.026, 1.026, 1.025, 1.026, 1.026, 1.027, 1.027, 1.027, 1.022, 1.014, 1.009,
+ 1.023, 1.026, 1.026, 1.027, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.026, 1.027, 1.026, 1.023, 1.017, 1.012,
+ 1.024, 1.026, 1.026, 1.026, 1.025, 1.024, 1.024, 1.023, 1.023, 1.024, 1.025, 1.026, 1.026, 1.024, 1.018, 1.013,
+ 1.024, 1.026, 1.026, 1.026, 1.025, 1.024, 1.023, 1.023, 1.023, 1.023, 1.024, 1.026, 1.026, 1.025, 1.019, 1.013,
+ 1.025, 1.026, 1.026, 1.026, 1.025, 1.024, 1.023, 1.023, 1.023, 1.023, 1.024, 1.026, 1.026, 1.025, 1.018, 1.013,
+ 1.025, 1.027, 1.027, 1.027, 1.026, 1.025, 1.024, 1.023, 1.023, 1.024, 1.024, 1.026, 1.026, 1.024, 1.018, 1.013,
+ 1.025, 1.027, 1.028, 1.028, 1.027, 1.026, 1.025, 1.024, 1.024, 1.024, 1.025, 1.026, 1.026, 1.024, 1.017, 1.012,
+ 1.024, 1.027, 1.029, 1.029, 1.028, 1.027, 1.026, 1.026, 1.025, 1.025, 1.026, 1.026, 1.025, 1.022, 1.014, 1.009,
+ 1.024, 1.027, 1.029, 1.031, 1.031, 1.029, 1.028, 1.028, 1.028, 1.028, 1.027, 1.026, 1.025, 1.021, 1.011, 1.007,
+ 1.022, 1.026, 1.031, 1.031, 1.031, 1.032, 1.031, 1.031, 1.029, 1.029, 1.028, 1.026, 1.022, 1.017, 1.007, 1.003,
+ 1.019, 1.024, 1.029, 1.031, 1.032, 1.032, 1.032, 1.031, 1.029, 1.029, 1.027, 1.024, 1.019, 1.013, 1.003, 1.001
+ ]
+ },
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1.027, 1.035, 1.039, 1.041, 1.043, 1.043, 1.043, 1.043, 1.044, 1.044, 1.044, 1.041, 1.041, 1.034, 1.021, 1.014,
+ 1.029, 1.035, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.041, 1.041, 1.041, 1.041, 1.035, 1.024, 1.017,
+ 1.029, 1.034, 1.036, 1.038, 1.038, 1.038, 1.039, 1.039, 1.039, 1.039, 1.039, 1.041, 1.039, 1.036, 1.027, 1.021,
+ 1.031, 1.034, 1.036, 1.036, 1.037, 1.037, 1.038, 1.037, 1.037, 1.038, 1.038, 1.039, 1.039, 1.037, 1.029, 1.021,
+ 1.031, 1.034, 1.035, 1.036, 1.037, 1.037, 1.037, 1.037, 1.037, 1.037, 1.038, 1.038, 1.038, 1.037, 1.029, 1.022,
+ 1.031, 1.034, 1.035, 1.036, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.038, 1.038, 1.037, 1.029, 1.022,
+ 1.031, 1.035, 1.036, 1.037, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.038, 1.038, 1.036, 1.028, 1.021,
+ 1.031, 1.034, 1.036, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.036, 1.036, 1.037, 1.037, 1.035, 1.026, 1.019,
+ 1.028, 1.034, 1.037, 1.037, 1.037, 1.037, 1.037, 1.036, 1.036, 1.036, 1.037, 1.037, 1.037, 1.033, 1.022, 1.016,
+ 1.028, 1.034, 1.037, 1.038, 1.039, 1.038, 1.037, 1.037, 1.037, 1.037, 1.037, 1.037, 1.035, 1.031, 1.017, 1.011,
+ 1.025, 1.031, 1.036, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.038, 1.038, 1.036, 1.031, 1.024, 1.011, 1.006,
+ 1.021, 1.028, 1.034, 1.037, 1.039, 1.039, 1.039, 1.038, 1.038, 1.038, 1.036, 1.033, 1.027, 1.019, 1.006, 1.001
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 1.026, 1.037, 1.048, 1.054, 1.057, 1.058, 1.059, 1.059, 1.061, 1.059, 1.059, 1.056, 1.049, 1.038, 1.019, 1.013,
+ 1.031, 1.039, 1.049, 1.054, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.056, 1.051, 1.042, 1.026, 1.018,
+ 1.033, 1.044, 1.051, 1.054, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.058, 1.058, 1.055, 1.046, 1.031, 1.023,
+ 1.035, 1.045, 1.051, 1.055, 1.057, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.056, 1.049, 1.035, 1.026,
+ 1.037, 1.046, 1.052, 1.055, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.051, 1.037, 1.027,
+ 1.037, 1.047, 1.053, 1.056, 1.059, 1.059, 1.061, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.051, 1.037, 1.027,
+ 1.037, 1.047, 1.053, 1.057, 1.059, 1.059, 1.061, 1.061, 1.059, 1.059, 1.059, 1.058, 1.056, 1.049, 1.036, 1.026,
+ 1.037, 1.047, 1.054, 1.057, 1.059, 1.059, 1.061, 1.061, 1.059, 1.059, 1.059, 1.058, 1.056, 1.048, 1.034, 1.025,
+ 1.034, 1.045, 1.054, 1.057, 1.059, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.057, 1.053, 1.045, 1.029, 1.021,
+ 1.032, 1.043, 1.052, 1.057, 1.058, 1.059, 1.059, 1.059, 1.059, 1.059, 1.058, 1.055, 1.049, 1.041, 1.022, 1.013,
+ 1.028, 1.037, 1.048, 1.053, 1.057, 1.059, 1.059, 1.059, 1.059, 1.058, 1.056, 1.051, 1.044, 1.032, 1.013, 1.007,
+ 1.021, 1.033, 1.044, 1.051, 1.055, 1.058, 1.059, 1.059, 1.058, 1.057, 1.052, 1.047, 1.039, 1.026, 1.007, 1.001
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 2940,
+ "table":
+ [
+ 1.002, 1.012, 1.031, 1.042, 1.051, 1.056, 1.058, 1.058, 1.058, 1.058, 1.057, 1.055, 1.045, 1.033, 1.017, 1.016,
+ 1.011, 1.026, 1.041, 1.048, 1.056, 1.063, 1.066, 1.067, 1.067, 1.066, 1.064, 1.061, 1.051, 1.045, 1.028, 1.017,
+ 1.016, 1.033, 1.047, 1.056, 1.063, 1.067, 1.071, 1.072, 1.072, 1.071, 1.068, 1.064, 1.061, 1.051, 1.033, 1.024,
+ 1.021, 1.038, 1.051, 1.061, 1.067, 1.071, 1.073, 1.075, 1.075, 1.074, 1.071, 1.068, 1.063, 1.054, 1.036, 1.025,
+ 1.023, 1.041, 1.054, 1.063, 1.069, 1.073, 1.075, 1.077, 1.077, 1.076, 1.074, 1.069, 1.064, 1.055, 1.038, 1.027,
+ 1.023, 1.043, 1.055, 1.063, 1.069, 1.074, 1.076, 1.078, 1.078, 1.077, 1.075, 1.071, 1.065, 1.056, 1.039, 1.028,
+ 1.023, 1.043, 1.055, 1.063, 1.069, 1.074, 1.076, 1.077, 1.078, 1.076, 1.074, 1.071, 1.065, 1.056, 1.039, 1.028,
+ 1.023, 1.041, 1.052, 1.062, 1.068, 1.072, 1.074, 1.076, 1.076, 1.075, 1.073, 1.069, 1.064, 1.055, 1.038, 1.028,
+ 1.021, 1.038, 1.051, 1.059, 1.066, 1.069, 1.072, 1.074, 1.074, 1.073, 1.069, 1.067, 1.062, 1.052, 1.036, 1.027,
+ 1.018, 1.032, 1.046, 1.055, 1.061, 1.066, 1.069, 1.069, 1.069, 1.069, 1.067, 1.062, 1.057, 1.047, 1.031, 1.021,
+ 1.011, 1.023, 1.039, 1.049, 1.056, 1.061, 1.062, 1.064, 1.065, 1.064, 1.062, 1.058, 1.049, 1.038, 1.021, 1.016,
+ 1.001, 1.019, 1.035, 1.046, 1.053, 1.058, 1.061, 1.062, 1.062, 1.062, 1.059, 1.053, 1.043, 1.033, 1.016, 1.011
+ ]
+ },
+ {
+ "ct": 4000,
+ "table":
+ [
+ 1.001, 1.003, 1.011, 1.016, 1.019, 1.019, 1.021, 1.021, 1.019, 1.019, 1.019, 1.017, 1.017, 1.013, 1.007, 1.006,
+ 1.003, 1.011, 1.015, 1.021, 1.024, 1.026, 1.027, 1.027, 1.027, 1.026, 1.025, 1.023, 1.022, 1.016, 1.012, 1.007,
+ 1.007, 1.015, 1.021, 1.024, 1.027, 1.029, 1.031, 1.031, 1.031, 1.029, 1.028, 1.026, 1.024, 1.022, 1.015, 1.011,
+ 1.011, 1.017, 1.023, 1.027, 1.029, 1.032, 1.033, 1.033, 1.033, 1.033, 1.031, 1.028, 1.026, 1.024, 1.016, 1.011,
+ 1.012, 1.019, 1.025, 1.029, 1.032, 1.033, 1.034, 1.035, 1.035, 1.034, 1.033, 1.031, 1.028, 1.025, 1.018, 1.014,
+ 1.013, 1.021, 1.026, 1.031, 1.033, 1.034, 1.036, 1.036, 1.036, 1.035, 1.034, 1.032, 1.029, 1.026, 1.019, 1.015,
+ 1.013, 1.021, 1.026, 1.031, 1.033, 1.035, 1.036, 1.037, 1.037, 1.036, 1.034, 1.032, 1.029, 1.027, 1.019, 1.016,
+ 1.013, 1.021, 1.026, 1.031, 1.033, 1.035, 1.036, 1.036, 1.036, 1.036, 1.035, 1.033, 1.031, 1.027, 1.021, 1.016,
+ 1.013, 1.021, 1.025, 1.029, 1.032, 1.034, 1.035, 1.035, 1.036, 1.035, 1.034, 1.032, 1.031, 1.027, 1.021, 1.015,
+ 1.012, 1.019, 1.024, 1.027, 1.029, 1.032, 1.034, 1.034, 1.034, 1.034, 1.032, 1.031, 1.029, 1.026, 1.019, 1.015,
+ 1.009, 1.015, 1.022, 1.025, 1.028, 1.029, 1.031, 1.032, 1.032, 1.031, 1.031, 1.029, 1.026, 1.023, 1.017, 1.015,
+ 1.005, 1.014, 1.021, 1.025, 1.027, 1.029, 1.029, 1.031, 1.031, 1.031, 1.029, 1.029, 1.024, 1.021, 1.016, 1.015
+ ]
+ },
+ {
+ "ct": 6000,
+ "table":
+ [
+ 1.001, 1.001, 1.006, 1.007, 1.008, 1.009, 1.009, 1.009, 1.009, 1.009, 1.009, 1.011, 1.011, 1.011, 1.009, 1.008,
+ 1.001, 1.005, 1.008, 1.011, 1.012, 1.013, 1.014, 1.014, 1.014, 1.013, 1.013, 1.014, 1.014, 1.012, 1.011, 1.009,
+ 1.004, 1.008, 1.011, 1.012, 1.014, 1.016, 1.016, 1.016, 1.016, 1.016, 1.015, 1.015, 1.015, 1.014, 1.012, 1.011,
+ 1.005, 1.009, 1.012, 1.014, 1.016, 1.017, 1.018, 1.018, 1.018, 1.018, 1.017, 1.016, 1.016, 1.015, 1.012, 1.011,
+ 1.006, 1.011, 1.013, 1.015, 1.017, 1.018, 1.018, 1.019, 1.019, 1.019, 1.018, 1.017, 1.016, 1.015, 1.012, 1.011,
+ 1.007, 1.011, 1.013, 1.015, 1.017, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.017, 1.016, 1.013, 1.011,
+ 1.007, 1.012, 1.013, 1.015, 1.017, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.018, 1.017, 1.014, 1.013,
+ 1.007, 1.012, 1.013, 1.015, 1.016, 1.018, 1.019, 1.019, 1.019, 1.019, 1.019, 1.018, 1.018, 1.017, 1.015, 1.014,
+ 1.007, 1.011, 1.012, 1.014, 1.016, 1.017, 1.018, 1.018, 1.019, 1.019, 1.019, 1.018, 1.018, 1.018, 1.016, 1.015,
+ 1.007, 1.011, 1.012, 1.013, 1.015, 1.016, 1.017, 1.017, 1.018, 1.018, 1.018, 1.018, 1.018, 1.017, 1.016, 1.015,
+ 1.006, 1.009, 1.012, 1.013, 1.014, 1.015, 1.015, 1.016, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.016,
+ 1.005, 1.009, 1.012, 1.013, 1.015, 1.015, 1.015, 1.015, 1.016, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017, 1.017
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 1.223, 1.187, 1.129, 1.085, 1.061, 1.049, 1.046, 1.046, 1.046, 1.051, 1.061, 1.089, 1.134, 1.212, 1.359, 1.367,
+ 1.188, 1.141, 1.098, 1.065, 1.048, 1.037, 1.029, 1.029, 1.034, 1.036, 1.046, 1.066, 1.095, 1.158, 1.269, 1.359,
+ 1.158, 1.109, 1.073, 1.049, 1.035, 1.025, 1.019, 1.016, 1.017, 1.022, 1.033, 1.047, 1.072, 1.127, 1.219, 1.269,
+ 1.147, 1.092, 1.058, 1.039, 1.026, 1.017, 1.011, 1.007, 1.009, 1.015, 1.022, 1.035, 1.058, 1.107, 1.191, 1.236,
+ 1.144, 1.082, 1.051, 1.033, 1.021, 1.011, 1.005, 1.002, 1.004, 1.009, 1.017, 1.031, 1.051, 1.097, 1.177, 1.232,
+ 1.144, 1.081, 1.049, 1.031, 1.018, 1.008, 1.002, 1.001, 1.001, 1.006, 1.015, 1.029, 1.048, 1.096, 1.177, 1.232,
+ 1.144, 1.084, 1.051, 1.032, 1.018, 1.009, 1.004, 1.001, 1.002, 1.009, 1.016, 1.029, 1.051, 1.098, 1.183, 1.232,
+ 1.149, 1.096, 1.057, 1.037, 1.022, 1.014, 1.008, 1.005, 1.007, 1.012, 1.019, 1.033, 1.059, 1.113, 1.205, 1.248,
+ 1.166, 1.117, 1.071, 1.046, 1.031, 1.021, 1.014, 1.012, 1.014, 1.019, 1.029, 1.045, 1.078, 1.141, 1.247, 1.314,
+ 1.202, 1.151, 1.096, 1.061, 1.044, 1.031, 1.023, 1.021, 1.022, 1.029, 1.044, 1.067, 1.109, 1.182, 1.314, 1.424,
+ 1.242, 1.202, 1.134, 1.088, 1.061, 1.045, 1.038, 1.036, 1.039, 1.048, 1.066, 1.103, 1.157, 1.248, 1.424, 1.532,
+ 1.318, 1.238, 1.162, 1.111, 1.078, 1.059, 1.048, 1.048, 1.049, 1.063, 1.089, 1.133, 1.189, 1.296, 1.532, 1.606
+ ],
+ "sigma": 0.00175,
+ "sigma_Cb": 0.00268
+ }
+ },
+ {
+ "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.82257, -0.40941, -0.41316,
+ -0.52091, 1.83005, -0.30915,
+ 0.22503, -1.41259, 2.18757
+ ]
+ },
+ {
+ "ct": 2820,
+ "ccm":
+ [
+ 1.80564, -0.47587, -0.32977,
+ -0.47385, 1.83075, -0.35691,
+ 0.21369, -1.22609, 2.01239
+ ]
+ },
+ {
+ "ct": 2830,
+ "ccm":
+ [
+ 1.80057, -0.51479, -0.28578,
+ -0.64031, 2.16074, -0.52044,
+ 0.11794, -0.95667, 1.83873
+ ]
+ },
+ {
+ "ct": 2885,
+ "ccm":
+ [
+ 1.78452, -0.49769, -0.28683,
+ -0.63651, 2.13634, -0.49983,
+ 0.08547, -0.86501, 1.77954
+ ]
+ },
+ {
+ "ct": 3601,
+ "ccm":
+ [
+ 1.85165, -0.57008, -0.28156,
+ -0.56249, 2.08321, -0.52072,
+ 0.03724, -0.70964, 1.67239
+ ]
+ },
+ {
+ "ct": 3615,
+ "ccm":
+ [
+ 1.87611, -0.60772, -0.26839,
+ -0.55497, 2.07257, -0.51761,
+ 0.04151, -0.70635, 1.66485
+ ]
+ },
+ {
+ "ct": 3622,
+ "ccm":
+ [
+ 1.85505, -0.58542, -0.26963,
+ -0.55053, 2.05981, -0.50928,
+ 0.04005, -0.69302, 1.65297
+ ]
+ },
+ {
+ "ct": 4345,
+ "ccm":
+ [
+ 1.81872, -0.57511, -0.24361,
+ -0.49071, 2.16621, -0.67551,
+ 0.02641, -0.67838, 1.65196
+ ]
+ },
+ {
+ "ct": 4410,
+ "ccm":
+ [
+ 1.83689, -0.60178, -0.23512,
+ -0.48204, 2.14729, -0.66525,
+ 0.02773, -0.67615, 1.64841
+ ]
+ },
+ {
+ "ct": 4486,
+ "ccm":
+ [
+ 1.85101, -0.60733, -0.24368,
+ -0.47635, 2.13101, -0.65465,
+ 0.02229, -0.66412, 1.64183
+ ]
+ },
+ {
+ "ct": 4576,
+ "ccm":
+ [
+ 1.84076, -0.59449, -0.24626,
+ -0.47307, 2.13369, -0.66062,
+ 0.01984, -0.65788, 1.63804
+ ]
+ },
+ {
+ "ct": 5657,
+ "ccm":
+ [
+ 1.84536, -0.57827, -0.26709,
+ -0.44532, 2.04086, -0.59554,
+ -0.01738, -0.52806, 1.54544
+ ]
+ },
+ {
+ "ct": 5672,
+ "ccm":
+ [
+ 1.84251, -0.57486, -0.26765,
+ -0.44925, 2.04615, -0.59689,
+ -0.03179, -0.51748, 1.54928
+ ]
+ },
+ {
+ "ct": 5710,
+ "ccm":
+ [
+ 1.84081, -0.58127, -0.25953,
+ -0.44169, 2.03593, -0.59424,
+ -0.02503, -0.52696, 1.55199
+ ]
+ },
+ {
+ "ct": 6850,
+ "ccm":
+ [
+ 1.80426, -0.22567, -0.57859,
+ -0.48629, 2.49024, -1.00395,
+ -0.10865, -0.63841, 1.74705
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen": { }
+ }
+ ]
+}
diff --git a/src/ipa/rpi/vc4/data/imx290.json b/src/ipa/rpi/vc4/data/imx290.json
index 8a7cadba..8f41bf51 100644
--- a/src/ipa/rpi/vc4/data/imx290.json
+++ b/src/ipa/rpi/vc4/data/imx290.json
@@ -52,15 +52,24 @@
{
"matrix":
{
- "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/imx296.json b/src/ipa/rpi/vc4/data/imx296.json
index 7621f759..8f24ce5b 100644
--- a/src/ipa/rpi/vc4/data/imx296.json
+++ b/src/ipa/rpi/vc4/data/imx296.json
@@ -135,15 +135,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
@@ -431,4 +440,4 @@
}
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/imx296_mono.json b/src/ipa/rpi/vc4/data/imx296_mono.json
index d4140c81..fe331569 100644
--- a/src/ipa/rpi/vc4/data/imx296_mono.json
+++ b/src/ipa/rpi/vc4/data/imx296_mono.json
@@ -38,15 +38,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
@@ -228,4 +237,4 @@
}
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/imx327.json b/src/ipa/rpi/vc4/data/imx327.json
new file mode 100644
index 00000000..40a56842
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/imx327.json
@@ -0,0 +1,215 @@
+{
+ "version": 2.0,
+ "target": "bcm2835",
+ "description": "This is an interim tuning only. Please consider doing a more formal tuning for your application.",
+ "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 ]
+ },
+ "short":
+ {
+ "shutter": [ 10, 5000, 10000, 20000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.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/imx378.json b/src/ipa/rpi/vc4/data/imx378.json
index f7b68011..363b47e1 100644
--- a/src/ipa/rpi/vc4/data/imx378.json
+++ b/src/ipa/rpi/vc4/data/imx378.json
@@ -133,15 +133,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/imx415.json b/src/ipa/rpi/vc4/data/imx415.json
new file mode 100755
index 00000000..6ed16b17
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/imx415.json
@@ -0,0 +1,413 @@
+{
+ "version": 2.0,
+ "target": "bcm2835",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 3840
+ }
+ },
+ {
+ "rpi.dpc": { }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 19230,
+ "reference_gain": 1.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 1198,
+ "reference_Y": 14876
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 17,
+ "reference_slope": 3.439
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 193,
+ "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": 8600
+ }
+ },
+ "bayes": 1,
+ "ct_curve":
+ [
+ 2698.0, 0.7681, 0.2026,
+ 2930.0, 0.7515, 0.2116,
+ 3643.0, 0.6355, 0.2858,
+ 4605.0, 0.4992, 0.4041,
+ 5658.0, 0.4498, 0.4574
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.0112,
+ "transverse_neg": 0.01424
+ }
+ },
+ {
+ "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": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.025, 1.016, 1.013, 1.011, 1.008, 1.005, 1.003, 1.001, 1.003, 1.005, 1.008, 1.011, 1.014, 1.019, 1.027, 1.035,
+ 1.025, 1.017, 1.013, 1.011, 1.008, 1.005, 1.003, 1.003, 1.004, 1.005, 1.009, 1.012, 1.017, 1.023, 1.029, 1.035,
+ 1.022, 1.017, 1.013, 1.009, 1.007, 1.005, 1.003, 1.003, 1.004, 1.006, 1.009, 1.012, 1.017, 1.023, 1.029, 1.035,
+ 1.019, 1.015, 1.011, 1.007, 1.005, 1.003, 1.001, 1.001, 1.003, 1.004, 1.007, 1.009, 1.015, 1.022, 1.028, 1.035,
+ 1.018, 1.014, 1.009, 1.006, 1.004, 1.002, 1.001, 1.001, 1.001, 1.003, 1.006, 1.009, 1.015, 1.021, 1.028, 1.035,
+ 1.018, 1.013, 1.011, 1.006, 1.003, 1.002, 1.001, 1.001, 1.001, 1.003, 1.006, 1.009, 1.015, 1.022, 1.028, 1.036,
+ 1.018, 1.014, 1.011, 1.007, 1.004, 1.002, 1.001, 1.001, 1.001, 1.004, 1.007, 1.009, 1.015, 1.023, 1.029, 1.036,
+ 1.019, 1.014, 1.012, 1.008, 1.005, 1.003, 1.002, 1.001, 1.003, 1.005, 1.008, 1.012, 1.016, 1.024, 1.031, 1.037,
+ 1.021, 1.016, 1.013, 1.009, 1.008, 1.005, 1.003, 1.003, 1.005, 1.008, 1.011, 1.014, 1.019, 1.026, 1.033, 1.039,
+ 1.025, 1.021, 1.016, 1.013, 1.009, 1.008, 1.006, 1.006, 1.008, 1.011, 1.014, 1.019, 1.024, 1.031, 1.038, 1.046,
+ 1.029, 1.025, 1.021, 1.018, 1.014, 1.013, 1.011, 1.011, 1.012, 1.015, 1.019, 1.023, 1.028, 1.035, 1.046, 1.051,
+ 1.032, 1.029, 1.023, 1.021, 1.018, 1.015, 1.014, 1.014, 1.015, 1.018, 1.022, 1.027, 1.033, 1.041, 1.051, 1.054
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.025, 1.011, 1.009, 1.005, 1.004, 1.003, 1.001, 1.001, 1.002, 1.006, 1.009, 1.012, 1.016, 1.021, 1.031, 1.041,
+ 1.025, 1.014, 1.009, 1.007, 1.005, 1.004, 1.003, 1.003, 1.004, 1.007, 1.009, 1.013, 1.021, 1.028, 1.037, 1.041,
+ 1.023, 1.014, 1.009, 1.007, 1.005, 1.004, 1.003, 1.003, 1.005, 1.007, 1.011, 1.014, 1.021, 1.028, 1.037, 1.048,
+ 1.022, 1.012, 1.007, 1.005, 1.002, 1.001, 1.001, 1.001, 1.003, 1.005, 1.009, 1.014, 1.019, 1.028, 1.039, 1.048,
+ 1.022, 1.011, 1.006, 1.003, 1.001, 1.001, 1.001, 1.001, 1.002, 1.005, 1.009, 1.014, 1.021, 1.029, 1.039, 1.051,
+ 1.022, 1.012, 1.007, 1.003, 1.002, 1.001, 1.001, 1.001, 1.002, 1.005, 1.009, 1.015, 1.021, 1.031, 1.041, 1.053,
+ 1.023, 1.013, 1.009, 1.005, 1.003, 1.003, 1.001, 1.002, 1.004, 1.006, 1.011, 1.015, 1.022, 1.031, 1.042, 1.056,
+ 1.024, 1.015, 1.012, 1.008, 1.005, 1.004, 1.004, 1.004, 1.006, 1.009, 1.013, 1.018, 1.024, 1.034, 1.045, 1.057,
+ 1.027, 1.017, 1.015, 1.012, 1.009, 1.007, 1.007, 1.008, 1.009, 1.013, 1.018, 1.023, 1.029, 1.038, 1.051, 1.061,
+ 1.029, 1.023, 1.017, 1.015, 1.014, 1.012, 1.011, 1.011, 1.014, 1.018, 1.024, 1.029, 1.036, 1.044, 1.056, 1.066,
+ 1.034, 1.028, 1.023, 1.022, 1.019, 1.019, 1.018, 1.018, 1.021, 1.025, 1.031, 1.035, 1.042, 1.053, 1.066, 1.074,
+ 1.041, 1.034, 1.027, 1.025, 1.025, 1.023, 1.023, 1.023, 1.025, 1.031, 1.035, 1.041, 1.049, 1.059, 1.074, 1.079
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 3000,
+ "table":
+ [
+ 1.001, 1.001, 1.007, 1.015, 1.027, 1.034, 1.038, 1.041, 1.042, 1.043, 1.043, 1.043, 1.041, 1.039, 1.049, 1.054,
+ 1.011, 1.011, 1.013, 1.023, 1.032, 1.039, 1.044, 1.047, 1.052, 1.056, 1.059, 1.059, 1.055, 1.051, 1.054, 1.056,
+ 1.015, 1.015, 1.019, 1.032, 1.039, 1.044, 1.047, 1.052, 1.055, 1.059, 1.061, 1.066, 1.063, 1.058, 1.061, 1.064,
+ 1.016, 1.017, 1.023, 1.032, 1.041, 1.045, 1.048, 1.053, 1.056, 1.061, 1.066, 1.069, 1.067, 1.064, 1.065, 1.068,
+ 1.018, 1.019, 1.025, 1.033, 1.042, 1.045, 1.049, 1.054, 1.058, 1.063, 1.071, 1.072, 1.071, 1.068, 1.069, 1.071,
+ 1.023, 1.024, 1.029, 1.035, 1.043, 1.048, 1.052, 1.057, 1.061, 1.065, 1.074, 1.075, 1.075, 1.072, 1.072, 1.075,
+ 1.027, 1.028, 1.031, 1.038, 1.045, 1.051, 1.054, 1.059, 1.064, 1.068, 1.075, 1.079, 1.078, 1.075, 1.076, 1.081,
+ 1.029, 1.031, 1.033, 1.044, 1.048, 1.054, 1.059, 1.064, 1.067, 1.073, 1.079, 1.082, 1.082, 1.079, 1.081, 1.085,
+ 1.033, 1.033, 1.035, 1.047, 1.053, 1.058, 1.064, 1.067, 1.073, 1.079, 1.084, 1.086, 1.086, 1.084, 1.089, 1.091,
+ 1.037, 1.037, 1.038, 1.049, 1.057, 1.062, 1.068, 1.073, 1.079, 1.084, 1.089, 1.092, 1.092, 1.092, 1.096, 1.104,
+ 1.041, 1.041, 1.043, 1.051, 1.061, 1.068, 1.073, 1.079, 1.083, 1.089, 1.092, 1.094, 1.097, 1.099, 1.105, 1.115,
+ 1.048, 1.044, 1.044, 1.051, 1.063, 1.071, 1.076, 1.082, 1.088, 1.091, 1.094, 1.097, 1.099, 1.104, 1.115, 1.126
+ ]
+ },
+ {
+ "ct": 5000,
+ "table":
+ [
+ 1.001, 1.001, 1.005, 1.011, 1.014, 1.018, 1.019, 1.019, 1.019, 1.021, 1.021, 1.021, 1.019, 1.017, 1.014, 1.014,
+ 1.009, 1.009, 1.011, 1.014, 1.019, 1.024, 1.026, 1.029, 1.031, 1.032, 1.032, 1.031, 1.027, 1.023, 1.022, 1.022,
+ 1.011, 1.012, 1.015, 1.018, 1.024, 1.026, 1.029, 1.032, 1.035, 1.036, 1.036, 1.034, 1.031, 1.027, 1.025, 1.025,
+ 1.012, 1.013, 1.015, 1.019, 1.025, 1.029, 1.032, 1.035, 1.036, 1.038, 1.038, 1.036, 1.034, 1.029, 1.026, 1.026,
+ 1.013, 1.014, 1.016, 1.019, 1.027, 1.031, 1.034, 1.037, 1.039, 1.039, 1.041, 1.039, 1.036, 1.031, 1.028, 1.027,
+ 1.014, 1.014, 1.017, 1.021, 1.027, 1.033, 1.037, 1.039, 1.041, 1.041, 1.042, 1.042, 1.039, 1.033, 1.029, 1.028,
+ 1.015, 1.015, 1.018, 1.021, 1.027, 1.033, 1.037, 1.041, 1.041, 1.042, 1.042, 1.042, 1.039, 1.034, 1.029, 1.029,
+ 1.015, 1.016, 1.018, 1.022, 1.027, 1.033, 1.037, 1.041, 1.041, 1.042, 1.043, 1.043, 1.041, 1.035, 1.031, 1.031,
+ 1.015, 1.016, 1.018, 1.022, 1.027, 1.032, 1.037, 1.041, 1.042, 1.042, 1.044, 1.043, 1.041, 1.036, 1.034, 1.033,
+ 1.016, 1.017, 1.017, 1.022, 1.027, 1.032, 1.036, 1.039, 1.042, 1.042, 1.043, 1.043, 1.041, 1.039, 1.036, 1.034,
+ 1.017, 1.017, 1.018, 1.022, 1.027, 1.031, 1.035, 1.039, 1.041, 1.042, 1.042, 1.042, 1.042, 1.039, 1.039, 1.039,
+ 1.018, 1.017, 1.017, 1.021, 1.027, 1.031, 1.033, 1.038, 1.041, 1.041, 1.042, 1.042, 1.041, 1.041, 1.041, 1.041
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 2.102, 1.903, 1.658, 1.483, 1.358, 1.267, 1.202, 1.202, 1.202, 1.242, 1.323, 1.431, 1.585, 1.797, 2.096, 2.351,
+ 1.996, 1.776, 1.549, 1.385, 1.273, 1.204, 1.138, 1.133, 1.133, 1.185, 1.252, 1.343, 1.484, 1.679, 1.954, 2.228,
+ 1.923, 1.689, 1.474, 1.318, 1.204, 1.138, 1.079, 1.071, 1.071, 1.133, 1.185, 1.284, 1.415, 1.597, 1.854, 2.146,
+ 1.881, 1.631, 1.423, 1.272, 1.159, 1.079, 1.051, 1.026, 1.046, 1.071, 1.144, 1.245, 1.369, 1.543, 1.801, 2.095,
+ 1.867, 1.595, 1.391, 1.242, 1.131, 1.051, 1.013, 1.002, 1.013, 1.046, 1.121, 1.219, 1.343, 1.511, 1.752, 2.079,
+ 1.867, 1.589, 1.385, 1.236, 1.125, 1.048, 1.001, 1.001, 1.003, 1.045, 1.118, 1.217, 1.342, 1.511, 1.746, 2.079,
+ 1.867, 1.589, 1.385, 1.236, 1.125, 1.048, 1.011, 1.003, 1.011, 1.046, 1.118, 1.217, 1.343, 1.511, 1.746, 2.079,
+ 1.884, 1.621, 1.411, 1.261, 1.149, 1.071, 1.048, 1.024, 1.046, 1.069, 1.141, 1.239, 1.369, 1.541, 1.781, 2.093,
+ 1.913, 1.675, 1.459, 1.304, 1.191, 1.125, 1.071, 1.065, 1.069, 1.124, 1.181, 1.278, 1.413, 1.592, 1.842, 2.133,
+ 1.981, 1.755, 1.529, 1.368, 1.251, 1.191, 1.125, 1.124, 1.124, 1.181, 1.242, 1.337, 1.479, 1.669, 1.935, 2.207,
+ 2.078, 1.867, 1.625, 1.453, 1.344, 1.251, 1.202, 1.201, 1.201, 1.242, 1.333, 1.418, 1.571, 1.776, 2.063, 2.321,
+ 2.217, 2.011, 1.747, 1.562, 1.431, 1.331, 1.278, 1.278, 1.278, 1.313, 1.407, 1.523, 1.686, 1.911, 2.226, 2.484
+ ],
+ "sigma": 0.00135,
+ "sigma_Cb": 0.00279
+ }
+ },
+ {
+ "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": 2698,
+ "ccm":
+ [
+ 1.57227, -0.32596, -0.24631,
+ -0.61264, 1.70791, -0.09526,
+ -0.43254, 0.48489, 0.94765
+ ]
+ },
+ {
+ "ct": 2930,
+ "ccm":
+ [
+ 1.69455, -0.52724, -0.16731,
+ -0.67131, 1.78468, -0.11338,
+ -0.41609, 0.54693, 0.86916
+ ]
+ },
+ {
+ "ct": 3643,
+ "ccm":
+ [
+ 1.74041, -0.77553, 0.03512,
+ -0.44073, 1.34131, 0.09943,
+ -0.11035, -0.93919, 2.04954
+ ]
+ },
+ {
+ "ct": 4605,
+ "ccm":
+ [
+ 1.49865, -0.41638, -0.08227,
+ -0.39445, 1.70114, -0.30669,
+ 0.01319, -0.88009, 1.86689
+ ]
+ },
+ {
+ "ct": 5658,
+ "ccm":
+ [
+ 1.38601, -0.23128, -0.15472,
+ -0.37641, 1.70444, -0.32803,
+ -0.01575, -0.71466, 1.73041
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rpi.sharpen": { }
+ }
+ ]
+}
diff --git a/src/ipa/rpi/vc4/data/imx462.json b/src/ipa/rpi/vc4/data/imx462.json
new file mode 100644
index 00000000..40a56842
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/imx462.json
@@ -0,0 +1,215 @@
+{
+ "version": 2.0,
+ "target": "bcm2835",
+ "description": "This is an interim tuning only. Please consider doing a more formal tuning for your application.",
+ "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 ]
+ },
+ "short":
+ {
+ "shutter": [ 10, 5000, 10000, 20000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.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/imx477.json b/src/ipa/rpi/vc4/data/imx477.json
index 0e39d419..fa25ee86 100644
--- a/src/ipa/rpi/vc4/data/imx477.json
+++ b/src/ipa/rpi/vc4/data/imx477.json
@@ -115,16 +115,16 @@
"ct_curve":
[
2360.0, 0.6009, 0.3093,
- 2848.0, 0.5071, 0.4000,
+ 2848.0, 0.5071, 0.4,
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,
+ 4660.0, 0.3529, 0.68,
+ 5579.0, 0.3227, 0.7,
+ 6125.0, 0.3129, 0.71,
+ 6671.0, 0.3065, 0.72,
+ 7217.0, 0.3014, 0.73,
+ 7763.0, 0.295, 0.74,
9505.0, 0.2524, 0.7856
],
"sensitivity_r": 1.05,
@@ -136,93 +136,306 @@
{
"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":
+ "channels": [
{
- "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 ]
+ "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
+ ]
},
- "long":
{
- "shutter": [ 100, 10000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 0.125,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.3,
- 1000, 0.3
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.3,
- 1000, 0.3
- ]
+ "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":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ ]
+ }
]
- }
- ],
- "shadows": [
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "base_ev": 1.5,
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.0,
- "q_hi": 0.5,
- "y_target":
- [
- 0, 0.17,
- 1000, 0.17
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
]
}
},
@@ -468,6 +681,20 @@
},
{
"rpi.sharpen": { }
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
} \ 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
index 52d7f072..472f33fe 100644
--- a/src/ipa/rpi/vc4/data/imx477_noir.json
+++ b/src/ipa/rpi/vc4/data/imx477_noir.json
@@ -47,93 +47,306 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 5000, 10000, 20000, 33333 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ "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
+ ]
},
- "long":
{
- "shutter": [ 100, 10000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 0.125,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.3,
- 1000, 0.3
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.3,
- 1000, 0.3
- ]
+ "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":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ ]
+ }
]
- }
- ],
- "shadows": [
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.0,
- "q_hi": 0.5,
- "y_target":
- [
- 0, 0.17,
- 1000, 0.17
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
]
}
},
@@ -424,6 +637,20 @@
},
{
"rpi.sharpen": { }
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-} \ 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
index 26c692fd..9dc32eb1 100644
--- a/src/ipa/rpi/vc4/data/imx477_scientific.json
+++ b/src/ipa/rpi/vc4/data/imx477_scientific.json
@@ -148,15 +148,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/imx477_v1.json b/src/ipa/rpi/vc4/data/imx477_v1.json
index d6402009..55e4adc1 100644
--- a/src/ipa/rpi/vc4/data/imx477_v1.json
+++ b/src/ipa/rpi/vc4/data/imx477_v1.json
@@ -138,15 +138,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/imx519.json b/src/ipa/rpi/vc4/data/imx519.json
index 1b0a7747..ce194256 100644
--- a/src/ipa/rpi/vc4/data/imx519.json
+++ b/src/ipa/rpi/vc4/data/imx519.json
@@ -133,15 +133,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/imx708.json b/src/ipa/rpi/vc4/data/imx708.json
index c40a5994..4de6f079 100644
--- a/src/ipa/rpi/vc4/data/imx708.json
+++ b/src/ipa/rpi/vc4/data/imx708.json
@@ -139,85 +139,280 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 15000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "short":
{
- "shutter": [ 100, 5000, 10000, 20000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+ "base_ev": 0.125,
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "long":
{
- "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
+ }
},
+ "constraint_modes":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ],
+ "startup_frames": 5,
+ "convergence_frames": 6,
+ "speed": 0.15
+ }
+ ]
}
},
{
@@ -457,6 +652,20 @@
"skip_frames": 5,
"map": [ 0.0, 445, 15.0, 925 ]
}
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/imx708_noir.json b/src/ipa/rpi/vc4/data/imx708_noir.json
index 8d498153..7b7ee874 100644
--- a/src/ipa/rpi/vc4/data/imx708_noir.json
+++ b/src/ipa/rpi/vc4/data/imx708_noir.json
@@ -139,85 +139,280 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 15000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "short":
{
- "shutter": [ 100, 5000, 10000, 20000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+ "base_ev": 0.125,
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "long":
{
- "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
+ }
},
+ "constraint_modes":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ],
+ "startup_frames": 5,
+ "convergence_frames": 6,
+ "speed": 0.15
+ }
+ ]
}
},
{
@@ -556,6 +751,20 @@
"skip_frames": 5,
"map": [ 0.0, 445, 15.0, 925 ]
}
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/imx708_wide.json b/src/ipa/rpi/vc4/data/imx708_wide.json
index 65543628..6f45aafc 100644
--- a/src/ipa/rpi/vc4/data/imx708_wide.json
+++ b/src/ipa/rpi/vc4/data/imx708_wide.json
@@ -129,85 +129,280 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 15000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "short":
{
- "shutter": [ 100, 5000, 10000, 20000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+ "base_ev": 0.125,
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "long":
{
- "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
+ }
},
+ "constraint_modes":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ],
+ "startup_frames": 5,
+ "convergence_frames": 6,
+ "speed": 0.15
+ }
+ ]
}
},
{
@@ -468,6 +663,20 @@
"skip_frames": 5,
"map": [ 0.0, 420, 35.0, 920 ]
}
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/imx708_wide_noir.json b/src/ipa/rpi/vc4/data/imx708_wide_noir.json
index 49442c0f..b9a5227e 100644
--- a/src/ipa/rpi/vc4/data/imx708_wide_noir.json
+++ b/src/ipa/rpi/vc4/data/imx708_wide_noir.json
@@ -129,85 +129,280 @@
{
"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":
+ "channels": [
{
- "shutter": [ 100, 15000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "short":
{
- "shutter": [ 100, 5000, 10000, 20000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ]
+ "base_ev": 0.125,
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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
},
- "long":
{
- "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 1.5,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.2,
- 1000, 0.2
- ]
+ "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 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 120000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
+ }
},
+ "constraint_modes":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ],
+ "startup_frames": 5,
+ "convergence_frames": 6,
+ "speed": 0.15
+ }
+ ]
}
},
{
@@ -459,6 +654,20 @@
"skip_frames": 5,
"map": [ 0.0, 420, 35.0, 920 ]
}
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-}
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/meson.build b/src/ipa/rpi/vc4/data/meson.build
index bcf5658b..7a8001ee 100644
--- a/src/ipa/rpi/vc4/data/meson.build
+++ b/src/ipa/rpi/vc4/data/meson.build
@@ -3,10 +3,14 @@
conf_files = files([
'imx219.json',
'imx219_noir.json',
+ 'imx283.json',
'imx290.json',
'imx296.json',
'imx296_mono.json',
+ 'imx327.json',
'imx378.json',
+ 'imx415.json',
+ 'imx462.json',
'imx477.json',
'imx477_noir.json',
'imx477_scientific.json',
@@ -17,10 +21,13 @@ conf_files = files([
'imx708_wide_noir.json',
'ov5647.json',
'ov5647_noir.json',
+ 'ov64a40.json',
+ 'ov7251_mono.json',
'ov9281_mono.json',
'se327m12.json',
'uncalibrated.json',
])
install_data(conf_files,
- install_dir : ipa_data_dir / 'rpi' / 'vc4')
+ install_dir : ipa_data_dir / 'rpi' / 'vc4',
+ install_tag : 'runtime')
diff --git a/src/ipa/rpi/vc4/data/ov5647.json b/src/ipa/rpi/vc4/data/ov5647.json
index a1b42a18..40c6059c 100644
--- a/src/ipa/rpi/vc4/data/ov5647.json
+++ b/src/ipa/rpi/vc4/data/ov5647.json
@@ -131,95 +131,308 @@
{
"rpi.agc":
{
- "metering_modes":
- {
- "centre-weighted":
- {
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
- },
- "spot":
+ "channels": [
{
- "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 ]
+ "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
},
- "long":
{
- "shutter": [ 100, 10000, 30000, 60000, 120000 ],
- "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
- }
- },
- "constraint_modes":
- {
- "normal": [
+ "base_ev": 1.25,
+ "metering_modes":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
- }
- ],
- "highlight": [
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.5,
- 1000, 0.5
- ]
+ "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":
{
- "bound": "UPPER",
- "q_lo": 0.98,
- "q_hi": 1.0,
- "y_target":
- [
- 0, 0.8,
- 1000, 0.8
+ "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
+ ]
+ }
]
- }
- ],
- "shadows": [
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ },
+ {
+ "base_ev": 1.25,
+ "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":
{
- "bound": "LOWER",
- "q_lo": 0.0,
- "q_hi": 0.5,
- "y_target":
- [
- 0, 0.17,
- 1000, 0.17
+ "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
+ },
+ "y_target":
+ [
+ 0, 0.16,
+ 1000, 0.165,
+ 10000, 0.17
+ ]
+ }
+ ]
}
},
{
@@ -464,6 +677,20 @@
},
{
"rpi.sharpen": { }
+ },
+ {
+ "rpi.hdr":
+ {
+ "MultiExposureUnmerged":
+ {
+ "cadence": [ 1, 2 ],
+ "channel_map":
+ {
+ "short": 1,
+ "long": 2
+ }
+ }
+ }
}
]
-} \ 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
index a6c6722f..488b7119 100644
--- a/src/ipa/rpi/vc4/data/ov5647_noir.json
+++ b/src/ipa/rpi/vc4/data/ov5647_noir.json
@@ -51,15 +51,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/ov64a40.json b/src/ipa/rpi/vc4/data/ov64a40.json
new file mode 100644
index 00000000..096f0b1e
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/ov64a40.json
@@ -0,0 +1,422 @@
+{
+ "version": 2.0,
+ "target": "bcm2835",
+ "algorithms": [
+ {
+ "rpi.black_level":
+ {
+ "black_level": 4096
+ }
+ },
+ {
+ "rpi.dpc": { }
+ },
+ {
+ "rpi.lux":
+ {
+ "reference_shutter_speed": 17861,
+ "reference_gain": 2.0,
+ "reference_aperture": 1.0,
+ "reference_lux": 1073,
+ "reference_Y": 9022
+ }
+ },
+ {
+ "rpi.noise":
+ {
+ "reference_constant": 0,
+ "reference_slope": 2.984
+ }
+ },
+ {
+ "rpi.geq":
+ {
+ "offset": 215,
+ "slope": 0.01121
+ }
+ },
+ {
+ "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
+ },
+ },
+ "bayes": 1,
+ "ct_curve":
+ [
+ 2300.0, 1.0522, 0.4091,
+ 2700.0, 0.7884, 0.4327,
+ 3000.0, 0.7597, 0.4421,
+ 4000.0, 0.5972, 0.5404,
+ 4150.0, 0.5598, 0.5779,
+ 6500.0, 0.4388, 0.7582
+ ],
+ "sensitivity_r": 1.0,
+ "sensitivity_b": 1.0,
+ "transverse_pos": 0.0558,
+ "transverse_neg": 0.04278
+ }
+ },
+ {
+ "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": 100,
+ "luminance_strength": 0.8,
+ "calibrations_Cr": [
+ {
+ "ct": 6500,
+ "table":
+ [
+ 2.437, 2.415, 2.392, 2.378, 2.369, 2.353, 2.344, 2.336, 2.329, 2.325, 2.325, 2.325, 2.333, 2.344, 2.366, 2.381,
+ 2.434, 2.405, 2.386, 2.369, 2.361, 2.334, 2.314, 2.302, 2.295, 2.289, 2.289, 2.303, 2.327, 2.334, 2.356, 2.378,
+ 2.434, 2.405, 2.385, 2.363, 2.334, 2.314, 2.289, 2.277, 2.269, 2.262, 2.262, 2.283, 2.303, 2.328, 2.352, 2.375,
+ 2.434, 2.405, 2.385, 2.348, 2.315, 2.289, 2.277, 2.258, 2.251, 2.242, 2.249, 2.258, 2.283, 2.321, 2.352, 2.375,
+ 2.434, 2.413, 2.385, 2.343, 2.311, 2.282, 2.258, 2.251, 2.229, 2.233, 2.242, 2.251, 2.281, 2.321, 2.356, 2.375,
+ 2.437, 2.418, 2.388, 2.343, 2.311, 2.282, 2.251, 2.229, 2.221, 2.226, 2.233, 2.251, 2.281, 2.322, 2.361, 2.381,
+ 2.444, 2.422, 2.393, 2.351, 2.314, 2.284, 2.251, 2.227, 2.221, 2.227, 2.234, 2.256, 2.287, 2.326, 2.366, 2.389,
+ 2.445, 2.424, 2.395, 2.353, 2.316, 2.287, 2.266, 2.251, 2.228, 2.234, 2.251, 2.259, 2.289, 2.331, 2.371, 2.395,
+ 2.445, 2.424, 2.399, 2.364, 2.329, 2.308, 2.287, 2.266, 2.259, 2.254, 2.259, 2.283, 2.304, 2.343, 2.375, 2.395,
+ 2.445, 2.425, 2.407, 2.385, 2.364, 2.329, 2.308, 2.299, 2.291, 2.284, 2.284, 2.304, 2.335, 2.354, 2.381, 2.399,
+ 2.449, 2.427, 2.418, 2.407, 2.385, 2.364, 2.349, 2.338, 2.333, 2.326, 2.326, 2.335, 2.354, 2.374, 2.389, 2.408,
+ 2.458, 2.441, 2.427, 2.411, 2.403, 2.395, 2.391, 2.383, 2.375, 2.369, 2.369, 2.369, 2.369, 2.385, 2.408, 2.418
+ ]
+ }
+ ],
+ "calibrations_Cb": [
+ {
+ "ct": 6500,
+ "table":
+ [
+ 1.297, 1.297, 1.289, 1.289, 1.289, 1.291, 1.293, 1.294, 1.294, 1.294, 1.294, 1.296, 1.298, 1.304, 1.312, 1.313,
+ 1.297, 1.289, 1.286, 1.286, 1.287, 1.289, 1.292, 1.294, 1.294, 1.294, 1.294, 1.294, 1.296, 1.298, 1.306, 1.312,
+ 1.289, 1.286, 1.283, 1.283, 1.285, 1.287, 1.291, 1.294, 1.294, 1.292, 1.291, 1.289, 1.293, 1.294, 1.298, 1.304,
+ 1.283, 1.282, 1.279, 1.281, 1.282, 1.285, 1.287, 1.294, 1.294, 1.291, 1.289, 1.289, 1.289, 1.293, 1.294, 1.298,
+ 1.281, 1.279, 1.279, 1.279, 1.281, 1.283, 1.287, 1.292, 1.292, 1.291, 1.291, 1.289, 1.289, 1.291, 1.294, 1.297,
+ 1.279, 1.277, 1.277, 1.279, 1.281, 1.282, 1.286, 1.289, 1.291, 1.291, 1.291, 1.291, 1.289, 1.291, 1.293, 1.297,
+ 1.277, 1.275, 1.275, 1.278, 1.279, 1.281, 1.284, 1.287, 1.289, 1.291, 1.291, 1.291, 1.289, 1.289, 1.292, 1.297,
+ 1.277, 1.275, 1.274, 1.275, 1.277, 1.278, 1.279, 1.284, 1.285, 1.285, 1.286, 1.288, 1.289, 1.289, 1.292, 1.297,
+ 1.277, 1.272, 1.272, 1.274, 1.274, 1.277, 1.279, 1.282, 1.284, 1.284, 1.285, 1.286, 1.288, 1.289, 1.292, 1.297,
+ 1.277, 1.272, 1.272, 1.273, 1.274, 1.276, 1.279, 1.282, 1.284, 1.284, 1.286, 1.286, 1.288, 1.289, 1.293, 1.297,
+ 1.279, 1.272, 1.271, 1.272, 1.274, 1.276, 1.279, 1.283, 1.284, 1.284, 1.285, 1.286, 1.288, 1.291, 1.294, 1.299,
+ 1.281, 1.274, 1.271, 1.271, 1.273, 1.276, 1.278, 1.282, 1.284, 1.284, 1.285, 1.286, 1.286, 1.291, 1.295, 1.302
+ ]
+ }
+ ],
+ "luminance_lut":
+ [
+ 3.811, 3.611, 3.038, 2.632, 2.291, 2.044, 1.967, 1.957, 1.957, 1.957, 2.009, 2.222, 2.541, 2.926, 3.455, 3.652,
+ 3.611, 3.135, 2.636, 2.343, 2.044, 1.846, 1.703, 1.626, 1.626, 1.671, 1.796, 1.983, 2.266, 2.549, 3.007, 3.455,
+ 3.135, 2.781, 2.343, 2.044, 1.831, 1.554, 1.411, 1.337, 1.337, 1.379, 1.502, 1.749, 1.983, 2.266, 2.671, 3.007,
+ 2.903, 2.538, 2.149, 1.831, 1.554, 1.401, 1.208, 1.145, 1.145, 1.183, 1.339, 1.502, 1.749, 2.072, 2.446, 2.801,
+ 2.812, 2.389, 2.018, 1.684, 1.401, 1.208, 1.139, 1.028, 1.028, 1.109, 1.183, 1.339, 1.604, 1.939, 2.309, 2.723,
+ 2.799, 2.317, 1.948, 1.606, 1.327, 1.139, 1.028, 1.019, 1.001, 1.021, 1.109, 1.272, 1.531, 1.869, 2.246, 2.717,
+ 2.799, 2.317, 1.948, 1.606, 1.327, 1.139, 1.027, 1.006, 1.001, 1.007, 1.109, 1.272, 1.531, 1.869, 2.246, 2.717,
+ 2.799, 2.372, 1.997, 1.661, 1.378, 1.184, 1.118, 1.019, 1.012, 1.103, 1.158, 1.326, 1.589, 1.926, 2.302, 2.717,
+ 2.884, 2.507, 2.116, 1.795, 1.511, 1.361, 1.184, 1.118, 1.118, 1.158, 1.326, 1.461, 1.726, 2.056, 2.434, 2.799,
+ 3.083, 2.738, 2.303, 1.989, 1.783, 1.511, 1.361, 1.291, 1.291, 1.337, 1.461, 1.726, 1.942, 2.251, 2.657, 2.999,
+ 3.578, 3.083, 2.589, 2.303, 1.989, 1.783, 1.637, 1.563, 1.563, 1.613, 1.743, 1.942, 2.251, 2.537, 2.999, 3.492,
+ 3.764, 3.578, 2.999, 2.583, 2.237, 1.986, 1.913, 1.905, 1.905, 1.905, 1.962, 2.196, 2.525, 2.932, 3.492, 3.659
+ ],
+ "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.ccm":
+ {
+ "ccms": [
+ {
+ "ct": 2300,
+ "ccm":
+ [
+ 1.77644, -0.14825, -0.62819,
+ -0.25816, 1.66348, -0.40532,
+ -0.21633, -1.95132, 3.16765
+ ]
+ },
+ {
+ "ct": 2700,
+ "ccm":
+ [
+ 1.53605, 0.03047, -0.56652,
+ -0.27159, 1.78525, -0.51366,
+ -0.13581, -1.22128, 2.35709
+ ]
+ },
+ {
+ "ct": 3000,
+ "ccm":
+ [
+ 1.72928, -0.18819, -0.54108,
+ -0.44398, 2.04756, -0.60358,
+ -0.13203, -0.94711, 2.07913
+ ]
+ },
+ {
+ "ct": 4000,
+ "ccm":
+ [
+ 1.69895, -0.23055, -0.46841,
+ -0.33934, 1.80391, -0.46456,
+ -0.13902, -0.75385, 1.89287
+ ]
+ },
+ {
+ "ct": 4150,
+ "ccm":
+ [
+ 2.08494, -0.68698, -0.39796,
+ -0.37928, 1.78795, -0.40867,
+ -0.11537, -0.74686, 1.86223
+ ]
+ },
+ {
+ "ct": 6500,
+ "ccm":
+ [
+ 1.69813, -0.27304, -0.42509,
+ -0.23364, 1.87586, -0.64221,
+ -0.07587, -0.62348, 1.69935
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "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": 0,
+ "dropout_frames": 0,
+ "step_frames": 4
+ }
+ },
+ "conf_epsilon": 8,
+ "conf_thresh": 16,
+ "conf_clip": 512,
+ "skip_frames": 5,
+ "map": [ 0.0, 0, 15.0, 1023 ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/ipa/rpi/vc4/data/ov7251_mono.json b/src/ipa/rpi/vc4/data/ov7251_mono.json
new file mode 100644
index 00000000..a9d05a01
--- /dev/null
+++ b/src/ipa/rpi/vc4/data/ov7251_mono.json
@@ -0,0 +1,136 @@
+{
+ "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 ]
+ },
+ "short":
+ {
+ "shutter": [ 100, 5000, 10000, 20000, 30000 ],
+ "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
+ },
+ "long":
+ {
+ "shutter": [ 1000, 30000, 60000, 90000, 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.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/ov9281_mono.json b/src/ipa/rpi/vc4/data/ov9281_mono.json
index 2b7292ec..a9d05a01 100644
--- a/src/ipa/rpi/vc4/data/ov9281_mono.json
+++ b/src/ipa/rpi/vc4/data/ov9281_mono.json
@@ -35,7 +35,10 @@
{
"centre-weighted":
{
- "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "weights":
+ [
+ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/se327m12.json b/src/ipa/rpi/vc4/data/se327m12.json
index 8552ed92..948169db 100644
--- a/src/ipa/rpi/vc4/data/se327m12.json
+++ b/src/ipa/rpi/vc4/data/se327m12.json
@@ -133,15 +133,24 @@
{
"centre-weighted":
{
- "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "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 ]
+ "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 ]
+ "weights":
+ [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/data/uncalibrated.json b/src/ipa/rpi/vc4/data/uncalibrated.json
index 7654defa..cdc56b32 100644
--- a/src/ipa/rpi/vc4/data/uncalibrated.json
+++ b/src/ipa/rpi/vc4/data/uncalibrated.json
@@ -22,7 +22,10 @@
{
"centre-weighted":
{
- "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
+ "weights":
+ [
+ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0
+ ]
}
},
"exposure_modes":
diff --git a/src/ipa/rpi/vc4/meson.build b/src/ipa/rpi/vc4/meson.build
index 590e9197..c10fa17e 100644
--- a/src/ipa/rpi/vc4/meson.build
+++ b/src/ipa/rpi/vc4/meson.build
@@ -15,7 +15,6 @@ vc4_ipa_libs = [
vc4_ipa_includes = [
ipa_includes,
- libipa_includes,
]
vc4_ipa_sources = files([
@@ -24,12 +23,10 @@ vc4_ipa_sources = files([
vc4_ipa_includes += include_directories('..')
-mod = shared_module(ipa_name,
- [vc4_ipa_sources, libcamera_generated_ipa_headers],
+mod = shared_module(ipa_name, vc4_ipa_sources,
name_prefix : '',
include_directories : vc4_ipa_includes,
- dependencies : vc4_ipa_deps,
- link_with : libipa,
+ dependencies : [vc4_ipa_deps, libipa_dep],
link_whole : vc4_ipa_libs,
install : true,
install_dir : ipa_install_dir)
diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp
index 4a4d720c..ba43e474 100644
--- a/src/ipa/rpi/vc4/vc4.cpp
+++ b/src/ipa/rpi/vc4/vc4.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2021, Raspberry Pi Ltd
*
- * rpi.cpp - Raspberry Pi VC4/BCM2835 ISP IPA.
+ * Raspberry Pi VC4/BCM2835 ISP IPA.
*/
#include <string.h>
@@ -11,6 +11,8 @@
#include <linux/bcm2835-isp.h>
#include <libcamera/base/log.h>
+#include <libcamera/base/span.h>
+#include <libcamera/control_ids.h>
#include <libcamera/ipa/ipa_module_info.h>
#include "common/ipa_base.h"
@@ -244,12 +246,48 @@ RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
stats->focus_stats[i].contrast_val_num[1][1],
stats->focus_stats[i].contrast_val_num[1][0] });
+ if (statsMetadataOutput_) {
+ Span<const uint8_t> statsSpan(reinterpret_cast<const uint8_t *>(stats),
+ sizeof(bcm2835_isp_stats));
+ libcameraMetadata_.set(controls::rpi::Bcm2835StatsOutput, statsSpan);
+ }
+
return statistics;
}
-void IpaVc4::handleControls([[maybe_unused]] const ControlList &controls)
+void IpaVc4::handleControls(const ControlList &controls)
{
- /* No controls require any special updates to the hardware configuration. */
+ static const std::map<int32_t, RPiController::DenoiseMode> 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 },
+ };
+
+ for (auto const &ctrl : controls) {
+ switch (ctrl.first) {
+ case controls::draft::NOISE_REDUCTION_MODE: {
+ RPiController::DenoiseAlgorithm *sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(
+ controller_.getAlgorithm("SDN"));
+ /* Some platforms may have a combined "denoise" algorithm instead. */
+ if (!sdn)
+ sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(
+ controller_.getAlgorithm("denoise"));
+ if (!sdn) {
+ LOG(IPARPI, Warning)
+ << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm";
+ return;
+ }
+
+ int32_t idx = ctrl.second.get<int32_t>();
+ auto mode = DenoiseModeTable.find(idx);
+ if (mode != DenoiseModeTable.end())
+ sdn->setMode(mode->second);
+ break;
+ }
+ }
+ }
}
bool IpaVc4::validateIspControls()
@@ -545,7 +583,7 @@ extern "C" {
const struct IPAModuleInfo ipaModuleInfo = {
IPA_MODULE_API_VERSION,
1,
- "PipelineHandlerVc4",
+ "rpi/vc4",
"rpi/vc4",
};
diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
new file mode 100644
index 00000000..c46bb0eb
--- /dev/null
+++ b/src/ipa/simple/algorithms/agc.cpp
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Exposure and gain
+ */
+
+#include "agc.h"
+
+#include <stdint.h>
+
+#include <libcamera/base/log.h>
+
+#include "control_ids.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoftExposure)
+
+namespace ipa::soft::algorithms {
+
+/*
+ * The number of bins to use for the optimal exposure calculations.
+ */
+static constexpr unsigned int kExposureBinsCount = 5;
+
+/*
+ * The exposure is optimal when the mean sample value of the histogram is
+ * in the middle of the range.
+ */
+static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;
+
+/*
+ * This implements the hysteresis for the exposure adjustment.
+ * It is small enough to have the exposure close to the optimal, and is big
+ * enough to prevent the exposure from wobbling around the optimal value.
+ */
+static constexpr float kExposureSatisfactory = 0.2;
+
+Agc::Agc()
+{
+}
+
+void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV)
+{
+ /*
+ * kExpDenominator of 10 gives ~10% increment/decrement;
+ * kExpDenominator of 5 - about ~20%
+ */
+ static constexpr uint8_t kExpDenominator = 10;
+ static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
+ static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
+
+ double next;
+ int32_t &exposure = frameContext.sensor.exposure;
+ double &again = frameContext.sensor.gain;
+
+ if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
+ next = exposure * kExpNumeratorUp / kExpDenominator;
+ if (next - exposure < 1)
+ exposure += 1;
+ else
+ exposure = next;
+ if (exposure >= context.configuration.agc.exposureMax) {
+ next = again * kExpNumeratorUp / kExpDenominator;
+ if (next - again < context.configuration.agc.againMinStep)
+ again += context.configuration.agc.againMinStep;
+ else
+ again = next;
+ }
+ }
+
+ if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
+ if (exposure == context.configuration.agc.exposureMax &&
+ again > context.configuration.agc.againMin) {
+ next = again * kExpNumeratorDown / kExpDenominator;
+ if (again - next < context.configuration.agc.againMinStep)
+ again -= context.configuration.agc.againMinStep;
+ else
+ again = next;
+ } else {
+ next = exposure * kExpNumeratorDown / kExpDenominator;
+ if (exposure - next < 1)
+ exposure -= 1;
+ else
+ exposure = next;
+ }
+ }
+
+ exposure = std::clamp(exposure, context.configuration.agc.exposureMin,
+ context.configuration.agc.exposureMax);
+ again = std::clamp(again, context.configuration.agc.againMin,
+ context.configuration.agc.againMax);
+
+ LOG(IPASoftExposure, Debug)
+ << "exposureMSV " << exposureMSV
+ << " exp " << exposure << " again " << again;
+}
+
+void Agc::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata)
+{
+ utils::Duration exposureTime =
+ context.configuration.agc.lineDuration * frameContext.sensor.exposure;
+ metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
+ metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
+
+ /*
+ * Calculate Mean Sample Value (MSV) according to formula from:
+ * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+ */
+ const auto &histogram = stats->yHistogram;
+ const unsigned int blackLevelHistIdx =
+ context.activeState.blc.level / (256 / SwIspStats::kYHistogramSize);
+ const unsigned int histogramSize =
+ SwIspStats::kYHistogramSize - blackLevelHistIdx;
+ const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
+ const unsigned int yHistValsPerBinMod =
+ histogramSize / (histogramSize % kExposureBinsCount + 1);
+ int exposureBins[kExposureBinsCount] = {};
+ unsigned int denom = 0;
+ unsigned int num = 0;
+
+ for (unsigned int i = 0; i < histogramSize; i++) {
+ unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
+ exposureBins[idx] += histogram[blackLevelHistIdx + i];
+ }
+
+ for (unsigned int i = 0; i < kExposureBinsCount; i++) {
+ LOG(IPASoftExposure, Debug) << i << ": " << exposureBins[i];
+ denom += exposureBins[i];
+ num += exposureBins[i] * (i + 1);
+ }
+
+ float exposureMSV = (denom == 0 ? 0 : static_cast<float>(num) / denom);
+ updateExposure(context, frameContext, exposureMSV);
+}
+
+REGISTER_IPA_ALGORITHM(Agc, "Agc")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
new file mode 100644
index 00000000..112d9f5a
--- /dev/null
+++ b/src/ipa/simple/algorithms/agc.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Exposure and gain
+ */
+
+#pragma once
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Agc : public Algorithm
+{
+public:
+ Agc();
+ ~Agc() = default;
+
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata) override;
+
+private:
+ void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV);
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/algorithm.h b/src/ipa/simple/algorithms/algorithm.h
new file mode 100644
index 00000000..41f63170
--- /dev/null
+++ b/src/ipa/simple/algorithms/algorithm.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * Software ISP control algorithm interface
+ */
+
+#pragma once
+
+#include <libipa/algorithm.h>
+
+#include "module.h"
+
+namespace libcamera {
+
+namespace ipa::soft {
+
+using Algorithm = libcamera::ipa::Algorithm<Module>;
+
+} /* namespace ipa::soft */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
new file mode 100644
index 00000000..cf567e89
--- /dev/null
+++ b/src/ipa/simple/algorithms/awb.cpp
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Auto white balance
+ */
+
+#include "awb.h"
+
+#include <numeric>
+#include <stdint.h>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+#include "libipa/colours.h"
+#include "simple/ipa_context.h"
+
+#include "control_ids.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoftAwb)
+
+namespace ipa::soft::algorithms {
+
+int Awb::configure(IPAContext &context,
+ [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+ auto &gains = context.activeState.awb.gains;
+ gains = { { 1.0, 1.0, 1.0 } };
+
+ return 0;
+}
+
+void Awb::prepare(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] DebayerParams *params)
+{
+ auto &gains = context.activeState.awb.gains;
+ /* Just report, the gains are applied in LUT algorithm. */
+ frameContext.gains.red = gains.r();
+ frameContext.gains.blue = gains.b();
+}
+
+void Awb::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata)
+{
+ const SwIspStats::Histogram &histogram = stats->yHistogram;
+ const uint8_t blackLevel = context.activeState.blc.level;
+
+ const float maxGain = 1024.0;
+ const float mdGains[] = {
+ static_cast<float>(frameContext.gains.red / maxGain),
+ static_cast<float>(frameContext.gains.blue / maxGain)
+ };
+ metadata.set(controls::ColourGains, mdGains);
+
+ /*
+ * Black level must be subtracted to get the correct AWB ratios, they
+ * would be off if they were computed from the whole brightness range
+ * rather than from the sensor range.
+ */
+ const uint64_t nPixels = std::accumulate(
+ histogram.begin(), histogram.end(), 0);
+ const uint64_t offset = blackLevel * nPixels;
+ const uint64_t sumR = stats->sumR_ - offset / 4;
+ const uint64_t sumG = stats->sumG_ - offset / 2;
+ const uint64_t sumB = stats->sumB_ - offset / 4;
+
+ /*
+ * Calculate red and blue gains for AWB.
+ * Clamp max gain at 4.0, this also avoids 0 division.
+ */
+ auto &gains = context.activeState.awb.gains;
+ gains = { {
+ sumR <= sumG / 4 ? 4.0f : static_cast<float>(sumG) / sumR,
+ 1.0,
+ sumB <= sumG / 4 ? 4.0f : static_cast<float>(sumG) / sumB,
+ } };
+
+ RGB<double> rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } };
+ context.activeState.awb.temperatureK = estimateCCT(rgbGains);
+ metadata.set(controls::ColourTemperature, context.activeState.awb.temperatureK);
+
+ LOG(IPASoftAwb, Debug)
+ << "gain R/B: " << gains << "; temperature: "
+ << context.activeState.awb.temperatureK;
+}
+
+REGISTER_IPA_ALGORITHM(Awb, "Awb")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
new file mode 100644
index 00000000..ad993f39
--- /dev/null
+++ b/src/ipa/simple/algorithms/awb.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024-2025 Red Hat Inc.
+ *
+ * Auto white balance
+ */
+
+#pragma once
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Awb : public Algorithm
+{
+public:
+ Awb() = default;
+ ~Awb() = default;
+
+ int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+ void prepare(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ DebayerParams *params) override;
+ void process(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata) override;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp
new file mode 100644
index 00000000..8c1e9ed0
--- /dev/null
+++ b/src/ipa/simple/algorithms/blc.cpp
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024-2025, Red Hat Inc.
+ *
+ * Black level handling
+ */
+
+#include "blc.h"
+
+#include <numeric>
+
+#include <libcamera/base/log.h>
+
+#include "control_ids.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+LOG_DEFINE_CATEGORY(IPASoftBL)
+
+BlackLevel::BlackLevel()
+{
+}
+
+int BlackLevel::init([[maybe_unused]] IPAContext &context,
+ const YamlObject &tuningData)
+{
+ auto blackLevel = tuningData["blackLevel"].get<int16_t>();
+ if (blackLevel.has_value()) {
+ /*
+ * Convert 16 bit values from the tuning file to 8 bit black
+ * level for the SoftISP.
+ */
+ definedLevel_ = blackLevel.value() >> 8;
+ }
+ return 0;
+}
+
+int BlackLevel::configure(IPAContext &context,
+ [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+ if (definedLevel_.has_value())
+ context.configuration.black.level = definedLevel_;
+ context.activeState.blc.level =
+ context.configuration.black.level.value_or(255);
+ return 0;
+}
+
+void BlackLevel::process(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata)
+{
+ /* Assign each of the R G G B channels as the same black level. */
+ const int32_t blackLevel = context.activeState.blc.level * 256;
+ const int32_t blackLevels[] = {
+ blackLevel, blackLevel, blackLevel, blackLevel
+ };
+ metadata.set(controls::SensorBlackLevels, blackLevels);
+
+ if (context.configuration.black.level.has_value())
+ return;
+
+ if (frameContext.sensor.exposure == context.activeState.blc.lastExposure &&
+ frameContext.sensor.gain == context.activeState.blc.lastGain) {
+ return;
+ }
+
+ const SwIspStats::Histogram &histogram = stats->yHistogram;
+
+ /*
+ * The constant is selected to be "good enough", not overly
+ * conservative or aggressive. There is no magic about the given value.
+ */
+ constexpr float ignoredPercentage = 0.02;
+ const unsigned int total =
+ std::accumulate(begin(histogram), end(histogram), 0);
+ const unsigned int pixelThreshold = ignoredPercentage * total;
+ const unsigned int histogramRatio = 256 / SwIspStats::kYHistogramSize;
+ const unsigned int currentBlackIdx =
+ context.activeState.blc.level / histogramRatio;
+
+ for (unsigned int i = 0, seen = 0;
+ i < currentBlackIdx && i < SwIspStats::kYHistogramSize;
+ i++) {
+ seen += histogram[i];
+ if (seen >= pixelThreshold) {
+ context.activeState.blc.level = i * histogramRatio;
+ context.activeState.blc.lastExposure = frameContext.sensor.exposure;
+ context.activeState.blc.lastGain = frameContext.sensor.gain;
+ LOG(IPASoftBL, Debug)
+ << "Auto-set black level: "
+ << i << "/" << SwIspStats::kYHistogramSize
+ << " (" << 100 * (seen - histogram[i]) / total << "% below, "
+ << 100 * seen / total << "% at or below)";
+ break;
+ }
+ };
+}
+
+REGISTER_IPA_ALGORITHM(BlackLevel, "BlackLevel")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/blc.h b/src/ipa/simple/algorithms/blc.h
new file mode 100644
index 00000000..db9e6d63
--- /dev/null
+++ b/src/ipa/simple/algorithms/blc.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Black level handling
+ */
+
+#pragma once
+
+#include <optional>
+#include <stdint.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class BlackLevel : public Algorithm
+{
+public:
+ BlackLevel();
+ ~BlackLevel() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata) override;
+
+private:
+ std::optional<uint8_t> definedLevel_;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
new file mode 100644
index 00000000..d5ba928d
--- /dev/null
+++ b/src/ipa/simple/algorithms/ccm.cpp
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board
+ * Copyright (C) 2024-2025, Red Hat Inc.
+ *
+ * Color correction matrix
+ */
+
+#include "ccm.h"
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/control_ids.h>
+
+namespace {
+
+constexpr unsigned int kTemperatureThreshold = 100;
+
+}
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+LOG_DEFINE_CATEGORY(IPASoftCcm)
+
+int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
+{
+ int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
+ if (ret < 0) {
+ LOG(IPASoftCcm, Error)
+ << "Failed to parse 'ccm' parameter from tuning file.";
+ return ret;
+ }
+
+ context.ccmEnabled = true;
+
+ return 0;
+}
+
+void Ccm::prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
+{
+ const unsigned int ct = context.activeState.awb.temperatureK;
+
+ /* Change CCM only on bigger temperature changes. */
+ if (frame > 0 &&
+ utils::abs_diff(ct, lastCt_) < kTemperatureThreshold) {
+ frameContext.ccm.ccm = context.activeState.ccm.ccm;
+ context.activeState.ccm.changed = false;
+ return;
+ }
+
+ lastCt_ = ct;
+ Matrix<float, 3, 3> ccm = ccm_.getInterpolated(ct);
+
+ context.activeState.ccm.ccm = ccm;
+ frameContext.ccm.ccm = ccm;
+ context.activeState.ccm.changed = true;
+}
+
+void Ccm::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ [[maybe_unused]] const SwIspStats *stats,
+ ControlList &metadata)
+{
+ metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data());
+}
+
+REGISTER_IPA_ALGORITHM(Ccm, "Ccm")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h
new file mode 100644
index 00000000..f4e2b85b
--- /dev/null
+++ b/src/ipa/simple/algorithms/ccm.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024-2025, Red Hat Inc.
+ *
+ * Color correction matrix
+ */
+
+#pragma once
+
+#include "libcamera/internal/matrix.h"
+
+#include <libipa/interpolator.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Ccm : public Algorithm
+{
+public:
+ Ccm() = default;
+ ~Ccm() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ void prepare(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ DebayerParams *params) override;
+ void process(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata) override;
+
+private:
+ unsigned int lastCt_;
+ Interpolator<Matrix<float, 3, 3>> ccm_;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
new file mode 100644
index 00000000..d1d5f727
--- /dev/null
+++ b/src/ipa/simple/algorithms/lut.cpp
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024-2025, Red Hat Inc.
+ *
+ * Color lookup tables construction
+ */
+
+#include "lut.h"
+
+#include <algorithm>
+#include <cmath>
+#include <optional>
+#include <stdint.h>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+#include "simple/ipa_context.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoftLut)
+
+namespace ipa::soft::algorithms {
+
+int Lut::init(IPAContext &context,
+ [[maybe_unused]] const YamlObject &tuningData)
+{
+ context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
+ return 0;
+}
+
+int Lut::configure(IPAContext &context,
+ [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+ /* Gamma value is fixed */
+ context.configuration.gamma = 0.5;
+ context.activeState.knobs.contrast = std::optional<double>();
+ updateGammaTable(context);
+
+ return 0;
+}
+
+void Lut::queueRequest(typename Module::Context &context,
+ [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] typename Module::FrameContext &frameContext,
+ const ControlList &controls)
+{
+ const auto &contrast = controls.get(controls::Contrast);
+ if (contrast.has_value()) {
+ context.activeState.knobs.contrast = contrast;
+ LOG(IPASoftLut, Debug) << "Setting contrast to " << contrast.value();
+ }
+}
+
+void Lut::updateGammaTable(IPAContext &context)
+{
+ auto &gammaTable = context.activeState.gamma.gammaTable;
+ const auto blackLevel = context.activeState.blc.level;
+ const unsigned int blackIndex = blackLevel * gammaTable.size() / 256;
+ const auto contrast = context.activeState.knobs.contrast.value_or(1.0);
+
+ std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, 0);
+ const float divisor = gammaTable.size() - blackIndex - 1.0;
+ for (unsigned int i = blackIndex; i < gammaTable.size(); i++) {
+ double normalized = (i - blackIndex) / divisor;
+ /* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */
+ double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001));
+ /* Apply simple S-curve */
+ if (normalized < 0.5)
+ normalized = 0.5 * std::pow(normalized / 0.5, contrastExp);
+ else
+ normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp);
+ gammaTable[i] = UINT8_MAX *
+ std::pow(normalized, context.configuration.gamma);
+ }
+
+ context.activeState.gamma.blackLevel = blackLevel;
+ context.activeState.gamma.contrast = contrast;
+}
+
+int16_t Lut::ccmValue(unsigned int i, float ccm) const
+{
+ return std::round(i * ccm);
+}
+
+void Lut::prepare(IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext,
+ DebayerParams *params)
+{
+ frameContext.contrast = context.activeState.knobs.contrast;
+
+ /*
+ * Update the gamma table if needed. This means if black level changes
+ * and since the black level gets updated only if a lower value is
+ * observed, it's not permanently prone to minor fluctuations or
+ * rounding errors.
+ */
+ const bool gammaUpdateNeeded =
+ context.activeState.gamma.blackLevel != context.activeState.blc.level ||
+ context.activeState.gamma.contrast != context.activeState.knobs.contrast;
+ if (gammaUpdateNeeded)
+ updateGammaTable(context);
+
+ auto &gains = context.activeState.awb.gains;
+ auto &gammaTable = context.activeState.gamma.gammaTable;
+ const unsigned int gammaTableSize = gammaTable.size();
+ const double div = static_cast<double>(DebayerParams::kRGBLookupSize) /
+ gammaTableSize;
+
+ if (!context.ccmEnabled) {
+ for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
+ /* Apply gamma after gain! */
+ const RGB<float> lutGains = (gains * i / div).min(gammaTableSize - 1);
+ params->red[i] = gammaTable[static_cast<unsigned int>(lutGains.r())];
+ params->green[i] = gammaTable[static_cast<unsigned int>(lutGains.g())];
+ params->blue[i] = gammaTable[static_cast<unsigned int>(lutGains.b())];
+ }
+ } else if (context.activeState.ccm.changed || gammaUpdateNeeded) {
+ Matrix<float, 3, 3> gainCcm = { { gains.r(), 0, 0,
+ 0, gains.g(), 0,
+ 0, 0, gains.b() } };
+ auto ccm = context.activeState.ccm.ccm * gainCcm;
+ auto &red = params->redCcm;
+ auto &green = params->greenCcm;
+ auto &blue = params->blueCcm;
+ for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
+ red[i].r = ccmValue(i, ccm[0][0]);
+ red[i].g = ccmValue(i, ccm[1][0]);
+ red[i].b = ccmValue(i, ccm[2][0]);
+ green[i].r = ccmValue(i, ccm[0][1]);
+ green[i].g = ccmValue(i, ccm[1][1]);
+ green[i].b = ccmValue(i, ccm[2][1]);
+ blue[i].r = ccmValue(i, ccm[0][2]);
+ blue[i].g = ccmValue(i, ccm[1][2]);
+ blue[i].b = ccmValue(i, ccm[2][2]);
+ params->gammaLut[i] = gammaTable[i / div];
+ }
+ }
+}
+
+void Lut::process([[maybe_unused]] IPAContext &context,
+ [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] IPAFrameContext &frameContext,
+ [[maybe_unused]] const SwIspStats *stats,
+ ControlList &metadata)
+{
+ const auto &contrast = frameContext.contrast;
+ if (contrast)
+ metadata.set(controls::Contrast, contrast.value());
+}
+
+REGISTER_IPA_ALGORITHM(Lut, "Lut")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/lut.h b/src/ipa/simple/algorithms/lut.h
new file mode 100644
index 00000000..ba8b9021
--- /dev/null
+++ b/src/ipa/simple/algorithms/lut.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Color lookup tables construction
+ */
+
+#pragma once
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Lut : public Algorithm
+{
+public:
+ Lut() = default;
+ ~Lut() = default;
+
+ int init(IPAContext &context, const YamlObject &tuningData) override;
+ int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+ void queueRequest(typename Module::Context &context,
+ const uint32_t frame,
+ typename Module::FrameContext &frameContext,
+ const ControlList &controls)
+ override;
+ void prepare(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ DebayerParams *params) override;
+ void process(IPAContext &context,
+ const uint32_t frame,
+ IPAFrameContext &frameContext,
+ const SwIspStats *stats,
+ ControlList &metadata) override;
+
+private:
+ void updateGammaTable(IPAContext &context);
+ int16_t ccmValue(unsigned int i, float ccm) const;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build
new file mode 100644
index 00000000..2d0adb05
--- /dev/null
+++ b/src/ipa/simple/algorithms/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: CC0-1.0
+
+soft_simple_ipa_algorithms = files([
+ 'awb.cpp',
+ 'agc.cpp',
+ 'blc.cpp',
+ 'ccm.cpp',
+ 'lut.cpp',
+])
diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build
new file mode 100644
index 00000000..92795ee4
--- /dev/null
+++ b/src/ipa/simple/data/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: CC0-1.0
+
+conf_files = files([
+ 'uncalibrated.yaml',
+])
+
+# The install_dir must match the name from the IPAModuleInfo
+install_data(conf_files,
+ install_dir : ipa_data_dir / 'simple',
+ install_tag : 'runtime')
diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml
new file mode 100644
index 00000000..5508e668
--- /dev/null
+++ b/src/ipa/simple/data/uncalibrated.yaml
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: CC0-1.0
+%YAML 1.1
+---
+version: 1
+algorithms:
+ - BlackLevel:
+ - Awb:
+ # Color correction matrices can be defined here. The CCM algorithm
+ # has a significant performance impact, and should only be enabled
+ # if tuned.
+ # - Ccm:
+ # ccms:
+ # - ct: 6500
+ # ccm: [ 1, 0, 0,
+ # 0, 1, 0,
+ # 0, 0, 1]
+ - Lut:
+ - Agc:
+...
diff --git a/src/ipa/simple/ipa_context.cpp b/src/ipa/simple/ipa_context.cpp
new file mode 100644
index 00000000..3f94bbeb
--- /dev/null
+++ b/src/ipa/simple/ipa_context.cpp
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ * Copyright (C) 2024 Red Hat Inc.
+ *
+ * Software ISP IPA Context
+ */
+
+#include "ipa_context.h"
+
+/**
+ * \file ipa_context.h
+ * \brief Context and state information shared between the algorithms
+ */
+
+namespace libcamera::ipa::soft {
+
+/**
+ * \struct IPASessionConfiguration
+ * \brief Session configuration for the IPA module
+ *
+ * The session configuration contains all IPA configuration parameters that
+ * remain constant during the capture session, from IPA module start to stop.
+ * It is typically set during the configure() operation of the IPA module, but
+ * may also be updated in the start() operation.
+ */
+
+/**
+ * \struct IPAActiveState
+ * \brief The active state of the IPA algorithms
+ *
+ * The IPA is fed with the statistics generated from the latest frame processed.
+ * The statistics are then processed by the IPA algorithms to compute parameters
+ * required for the next frame capture and processing. The current state of the
+ * algorithms is reflected through the IPAActiveState to store the values most
+ * recently computed by the IPA algorithms.
+ */
+
+/**
+ * \struct IPAContext
+ * \brief Global IPA context data shared between all algorithms
+ *
+ * \var IPAContext::configuration
+ * \brief The IPA session configuration, immutable during the session
+ *
+ * \var IPAContext::frameContexts
+ * \brief Ring buffer of the IPAFrameContext(s)
+ *
+ * \var IPAContext::activeState
+ * \brief The current state of IPA algorithms
+ */
+
+/**
+ * \var IPASessionConfiguration::gamma
+ * \brief Gamma value to be used in the raw image processing
+ */
+
+/**
+ * \var IPAActiveState::black
+ * \brief Context for the Black Level algorithm
+ *
+ * \var IPAActiveState::black.level
+ * \brief Current determined black level
+ */
+
+/**
+ * \var IPAActiveState::gains
+ * \brief Context for gains in the Colors algorithm
+ *
+ * \var IPAActiveState::gains.red
+ * \brief Gain of red color
+ *
+ * \var IPAActiveState::gains.green
+ * \brief Gain of green color
+ *
+ * \var IPAActiveState::gains.blue
+ * \brief Gain of blue color
+ */
+
+/**
+ * \var IPAActiveState::agc
+ * \brief Context for the AGC algorithm
+ *
+ * \var IPAActiveState::agc.exposure
+ * \brief Current exposure value
+ *
+ * \var IPAActiveState::agc.again
+ * \brief Current analog gain value
+ */
+
+/**
+ * \var IPAActiveState::gamma
+ * \brief Context for gamma in the Colors algorithm
+ *
+ * \var IPAActiveState::gamma.gammaTable
+ * \brief Computed gamma table
+ *
+ * \var IPAActiveState::gamma.blackLevel
+ * \brief Black level used for the gamma table computation
+ */
+
+} /* namespace libcamera::ipa::soft */
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
new file mode 100644
index 00000000..88cc6c35
--- /dev/null
+++ b/src/ipa/simple/ipa_context.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024-2025 Red Hat, Inc.
+ *
+ * Simple pipeline IPA Context
+ */
+
+#pragma once
+
+#include <array>
+#include <optional>
+#include <stdint.h>
+
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/matrix.h"
+#include "libcamera/internal/vector.h"
+
+#include <libipa/fc_queue.h>
+
+#include "core_ipa_interface.h"
+
+namespace libcamera {
+
+namespace ipa::soft {
+
+struct IPASessionConfiguration {
+ float gamma;
+ struct {
+ int32_t exposureMin, exposureMax;
+ double againMin, againMax, againMinStep;
+ utils::Duration lineDuration;
+ } agc;
+ struct {
+ std::optional<uint8_t> level;
+ } black;
+};
+
+struct IPAActiveState {
+ struct {
+ uint8_t level;
+ int32_t lastExposure;
+ double lastGain;
+ } blc;
+
+ struct {
+ RGB<float> gains;
+ unsigned int temperatureK;
+ } awb;
+
+ static constexpr unsigned int kGammaLookupSize = 1024;
+ struct {
+ std::array<double, kGammaLookupSize> gammaTable;
+ uint8_t blackLevel;
+ double contrast;
+ } gamma;
+
+ struct {
+ Matrix<float, 3, 3> ccm;
+ bool changed;
+ } ccm;
+
+ struct {
+ /* 0..2 range, 1.0 = normal */
+ std::optional<double> contrast;
+ } knobs;
+};
+
+struct IPAFrameContext : public FrameContext {
+ struct {
+ Matrix<float, 3, 3> ccm;
+ } ccm;
+
+ struct {
+ int32_t exposure;
+ double gain;
+ } sensor;
+ struct {
+ double red;
+ double blue;
+ } gains;
+ std::optional<double> contrast;
+};
+
+struct IPAContext {
+ IPAContext(unsigned int frameContextSize)
+ : frameContexts(frameContextSize)
+ {
+ }
+
+ IPACameraSensorInfo sensorInfo;
+ IPASessionConfiguration configuration;
+ IPAActiveState activeState;
+ FCQueue<IPAFrameContext> frameContexts;
+ ControlInfoMap::Map ctrlMap;
+ bool ccmEnabled = false;
+};
+
+} /* namespace ipa::soft */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
new file mode 100644
index 00000000..2f9f15f4
--- /dev/null
+++ b/src/ipa/simple/meson.build
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: CC0-1.0
+
+subdir('algorithms')
+subdir('data')
+
+ipa_name = 'ipa_soft_simple'
+
+soft_simple_sources = files([
+ 'ipa_context.cpp',
+ 'soft_simple.cpp',
+])
+
+soft_simple_sources += soft_simple_ipa_algorithms
+
+mod = shared_module(ipa_name, soft_simple_sources,
+ name_prefix : '',
+ include_directories : [ipa_includes],
+ dependencies : [libcamera_private, libipa_dep],
+ 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
+
+ipa_names += ipa_name
diff --git a/src/ipa/simple/module.h b/src/ipa/simple/module.h
new file mode 100644
index 00000000..8d4d53fb
--- /dev/null
+++ b/src/ipa/simple/module.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * Software ISP IPA Module
+ */
+
+#pragma once
+
+#include <libcamera/controls.h>
+
+#include <libcamera/ipa/soft_ipa_interface.h>
+
+#include "libcamera/internal/software_isp/debayer_params.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
+
+#include <libipa/module.h>
+
+#include "ipa_context.h"
+
+namespace libcamera {
+
+namespace ipa::soft {
+
+using Module = ipa::Module<IPAContext, IPAFrameContext, IPAConfigInfo,
+ DebayerParams, SwIspStats>;
+
+} /* namespace ipa::soft */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
new file mode 100644
index 00000000..c94c4cd5
--- /dev/null
+++ b/src/ipa/simple/soft_simple.cpp
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * Simple Software Image Processing Algorithm module
+ */
+
+#include <chrono>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <linux/v4l2-controls.h>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/shared_fd.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/soft_ipa_interface.h>
+
+#include "libcamera/internal/software_isp/debayer_params.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "libipa/camera_sensor_helper.h"
+
+#include "module.h"
+
+namespace libcamera {
+LOG_DEFINE_CATEGORY(IPASoft)
+
+using namespace std::literals::chrono_literals;
+
+namespace ipa::soft {
+
+/* Maximum number of frame contexts to be held */
+static constexpr uint32_t kMaxFrameContexts = 16;
+
+class IPASoftSimple : public ipa::soft::IPASoftInterface, public Module
+{
+public:
+ IPASoftSimple()
+ : context_(kMaxFrameContexts)
+ {
+ }
+
+ ~IPASoftSimple();
+
+ int init(const IPASettings &settings,
+ const SharedFD &fdStats,
+ const SharedFD &fdParams,
+ const IPACameraSensorInfo &sensorInfo,
+ const ControlInfoMap &sensorControls,
+ ControlInfoMap *ipaControls,
+ bool *ccmEnabled) override;
+ int configure(const IPAConfigInfo &configInfo) override;
+
+ int start() override;
+ void stop() override;
+
+ void queueRequest(const uint32_t frame, const ControlList &controls) override;
+ void computeParams(const uint32_t frame) override;
+ void processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls) override;
+
+protected:
+ std::string logPrefix() const override;
+
+private:
+ void updateExposure(double exposureMSV);
+
+ DebayerParams *params_;
+ SwIspStats *stats_;
+ std::unique_ptr<CameraSensorHelper> camHelper_;
+ ControlInfoMap sensorInfoMap_;
+
+ /* Local parameter storage */
+ struct IPAContext context_;
+};
+
+IPASoftSimple::~IPASoftSimple()
+{
+ if (stats_)
+ munmap(stats_, sizeof(SwIspStats));
+ if (params_)
+ munmap(params_, sizeof(DebayerParams));
+}
+
+int IPASoftSimple::init(const IPASettings &settings,
+ const SharedFD &fdStats,
+ const SharedFD &fdParams,
+ const IPACameraSensorInfo &sensorInfo,
+ const ControlInfoMap &sensorControls,
+ ControlInfoMap *ipaControls,
+ bool *ccmEnabled)
+{
+ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
+ if (!camHelper_) {
+ LOG(IPASoft, Warning)
+ << "Failed to create camera sensor helper for "
+ << settings.sensorModel;
+ }
+
+ context_.sensorInfo = sensorInfo;
+
+ /* Load the tuning data file */
+ File file(settings.configurationFile);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ int ret = file.error();
+ LOG(IPASoft, Error)
+ << "Failed to open configuration file "
+ << settings.configurationFile << ": " << strerror(-ret);
+ return ret;
+ }
+
+ std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);
+ if (!data)
+ return -EINVAL;
+
+ /* \todo Use the IPA configuration file for real. */
+ unsigned int version = (*data)["version"].get<uint32_t>(0);
+ LOG(IPASoft, Debug) << "Tuning file version " << version;
+
+ if (!data->contains("algorithms")) {
+ LOG(IPASoft, Error) << "Tuning file doesn't contain algorithms";
+ return -EINVAL;
+ }
+
+ int ret = createAlgorithms(context_, (*data)["algorithms"]);
+ if (ret)
+ return ret;
+
+ *ccmEnabled = context_.ccmEnabled;
+
+ params_ = nullptr;
+ stats_ = nullptr;
+
+ if (!fdStats.isValid()) {
+ LOG(IPASoft, Error) << "Invalid Statistics handle";
+ return -ENODEV;
+ }
+
+ if (!fdParams.isValid()) {
+ LOG(IPASoft, Error) << "Invalid Parameters handle";
+ return -ENODEV;
+ }
+
+ {
+ void *mem = mmap(nullptr, sizeof(DebayerParams), PROT_WRITE,
+ MAP_SHARED, fdParams.get(), 0);
+ if (mem == MAP_FAILED) {
+ LOG(IPASoft, Error) << "Unable to map Parameters";
+ return -errno;
+ }
+
+ params_ = static_cast<DebayerParams *>(mem);
+ }
+
+ {
+ void *mem = mmap(nullptr, sizeof(SwIspStats), PROT_READ,
+ MAP_SHARED, fdStats.get(), 0);
+ if (mem == MAP_FAILED) {
+ LOG(IPASoft, Error) << "Unable to map Statistics";
+ return -errno;
+ }
+
+ stats_ = static_cast<SwIspStats *>(mem);
+ }
+
+ ControlInfoMap::Map ctrlMap = context_.ctrlMap;
+ *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
+
+ /*
+ * Check if the sensor driver supports the controls required by the
+ * Soft IPA.
+ * Don't save the min and max control values yet, as e.g. the limits
+ * for V4L2_CID_EXPOSURE depend on the configured sensor resolution.
+ */
+ if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) {
+ LOG(IPASoft, Error) << "Don't have exposure control";
+ return -EINVAL;
+ }
+
+ if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) {
+ LOG(IPASoft, Error) << "Don't have gain control";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int IPASoftSimple::configure(const IPAConfigInfo &configInfo)
+{
+ sensorInfoMap_ = configInfo.sensorControls;
+
+ const ControlInfo &exposureInfo = sensorInfoMap_.find(V4L2_CID_EXPOSURE)->second;
+ const ControlInfo &gainInfo = sensorInfoMap_.find(V4L2_CID_ANALOGUE_GAIN)->second;
+
+ /* Clear the IPA context before the streaming session. */
+ context_.configuration = {};
+ context_.activeState = {};
+ context_.frameContexts.clear();
+
+ context_.configuration.agc.lineDuration =
+ context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;
+ context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();
+ context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();
+ if (!context_.configuration.agc.exposureMin) {
+ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
+ context_.configuration.agc.exposureMin = 1;
+ }
+
+ int32_t againMin = gainInfo.min().get<int32_t>();
+ int32_t againMax = gainInfo.max().get<int32_t>();
+
+ if (camHelper_) {
+ context_.configuration.agc.againMin = camHelper_->gain(againMin);
+ context_.configuration.agc.againMax = camHelper_->gain(againMax);
+ context_.configuration.agc.againMinStep =
+ (context_.configuration.agc.againMax -
+ context_.configuration.agc.againMin) /
+ 100.0;
+ if (camHelper_->blackLevel().has_value()) {
+ /*
+ * The black level from camHelper_ is a 16 bit value, software ISP
+ * works with 8 bit pixel values, both regardless of the actual
+ * sensor pixel width. Hence we obtain the pixel-based black value
+ * by dividing the value from the helper by 256.
+ */
+ context_.configuration.black.level =
+ camHelper_->blackLevel().value() / 256;
+ }
+ } else {
+ /*
+ * The camera sensor gain (g) is usually not equal to the value written
+ * into the gain register (x). But the way how the AGC algorithm changes
+ * the gain value to make the total exposure closer to the optimum
+ * assumes that g(x) is not too far from linear function. If the minimal
+ * gain is 0, the g(x) is likely to be far from the linear, like
+ * g(x) = a / (b * x + c). To avoid unexpected changes to the gain by
+ * the AGC algorithm (abrupt near one edge, and very small near the
+ * other) we limit the range of the gain values used.
+ */
+ context_.configuration.agc.againMax = againMax;
+ if (!againMin) {
+ LOG(IPASoft, Warning)
+ << "Minimum gain is zero, that can't be linear";
+ context_.configuration.agc.againMin =
+ std::min(100, againMin / 2 + againMax / 2);
+ }
+ context_.configuration.agc.againMinStep = 1.0;
+ }
+
+ for (auto const &algo : algorithms()) {
+ int ret = algo->configure(context_, configInfo);
+ if (ret)
+ return ret;
+ }
+
+ LOG(IPASoft, Info)
+ << "Exposure " << context_.configuration.agc.exposureMin << "-"
+ << context_.configuration.agc.exposureMax
+ << ", gain " << context_.configuration.agc.againMin << "-"
+ << context_.configuration.agc.againMax
+ << " (" << context_.configuration.agc.againMinStep << ")";
+
+ return 0;
+}
+
+int IPASoftSimple::start()
+{
+ return 0;
+}
+
+void IPASoftSimple::stop()
+{
+ context_.frameContexts.clear();
+}
+
+void IPASoftSimple::queueRequest(const uint32_t frame, const ControlList &controls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.alloc(frame);
+
+ for (auto const &algo : algorithms())
+ algo->queueRequest(context_, frame, frameContext, controls);
+}
+
+void IPASoftSimple::computeParams(const uint32_t frame)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+ for (auto const &algo : algorithms())
+ algo->prepare(context_, frame, frameContext, params_);
+ setIspParams.emit();
+}
+
+void IPASoftSimple::processStats(const uint32_t frame,
+ [[maybe_unused]] const uint32_t bufferId,
+ const ControlList &sensorControls)
+{
+ IPAFrameContext &frameContext = context_.frameContexts.get(frame);
+
+ frameContext.sensor.exposure =
+ sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();
+ int32_t again = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
+ frameContext.sensor.gain = camHelper_ ? camHelper_->gain(again) : again;
+
+ ControlList metadata(controls::controls);
+ for (auto const &algo : algorithms())
+ algo->process(context_, frame, frameContext, stats_, metadata);
+ metadataReady.emit(frame, metadata);
+
+ /* Sanity check */
+ if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
+ !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
+ LOG(IPASoft, Error) << "Control(s) missing";
+ return;
+ }
+
+ ControlList ctrls(sensorInfoMap_);
+
+ auto &againNew = frameContext.sensor.gain;
+ ctrls.set(V4L2_CID_EXPOSURE, frameContext.sensor.exposure);
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN,
+ static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(againNew) : againNew));
+
+ setSensorControls.emit(ctrls);
+}
+
+std::string IPASoftSimple::logPrefix() const
+{
+ return "IPASoft";
+}
+
+} /* namespace ipa::soft */
+
+/*
+ * External IPA module interface
+ */
+extern "C" {
+const struct IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 0,
+ "simple",
+ "simple",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::soft::IPASoftSimple();
+}
+
+} /* extern "C" */
+
+} /* namespace libcamera */
diff --git a/src/ipa/vimc/data/meson.build b/src/ipa/vimc/data/meson.build
index 42ec651c..628d6a29 100644
--- a/src/ipa/vimc/data/meson.build
+++ b/src/ipa/vimc/data/meson.build
@@ -5,4 +5,5 @@ conf_files = files([
])
install_data(conf_files,
- install_dir : ipa_data_dir / 'vimc')
+ install_dir : ipa_data_dir / 'vimc',
+ install_tag : 'runtime')
diff --git a/src/ipa/vimc/meson.build b/src/ipa/vimc/meson.build
index 264a2d9a..2cc5f80b 100644
--- a/src/ipa/vimc/meson.build
+++ b/src/ipa/vimc/meson.build
@@ -2,12 +2,10 @@
ipa_name = 'ipa_vimc'
-mod = shared_module(ipa_name,
- ['vimc.cpp', libcamera_generated_ipa_headers],
+mod = shared_module(ipa_name, 'vimc.cpp',
name_prefix : '',
- include_directories : [ipa_includes, libipa_includes],
- dependencies : libcamera_private,
- link_with : libipa,
+ include_directories : [ipa_includes],
+ dependencies : [libcamera_private, libipa_dep],
install : true,
install_dir : ipa_install_dir)
diff --git a/src/ipa/vimc/vimc.cpp b/src/ipa/vimc/vimc.cpp
index 2c255778..a1351a0f 100644
--- a/src/ipa/vimc/vimc.cpp
+++ b/src/ipa/vimc/vimc.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * vimc.cpp - Vimc Image Processing Algorithm module
+ * Vimc Image Processing Algorithm module
*/
#include <libcamera/ipa/vimc_ipa_interface.h>
@@ -14,6 +14,7 @@
#include <iostream>
#include <libcamera/base/file.h>
+#include <libcamera/base/flags.h>
#include <libcamera/base/log.h>
#include <libcamera/ipa/ipa_interface.h>
@@ -47,7 +48,7 @@ public:
void unmapBuffers(const std::vector<unsigned int> &ids) override;
void queueRequest(uint32_t frame, const ControlList &controls) override;
- void fillParamsBuffer(uint32_t frame, uint32_t bufferId) override;
+ void computeParams(uint32_t frame, uint32_t bufferId) override;
private:
void initTrace();
@@ -149,7 +150,7 @@ void IPAVimc::queueRequest([[maybe_unused]] uint32_t frame,
{
}
-void IPAVimc::fillParamsBuffer([[maybe_unused]] uint32_t frame, uint32_t bufferId)
+void IPAVimc::computeParams([[maybe_unused]] uint32_t frame, uint32_t bufferId)
{
auto it = buffers_.find(bufferId);
if (it == buffers_.end()) {
@@ -158,7 +159,7 @@ void IPAVimc::fillParamsBuffer([[maybe_unused]] uint32_t frame, uint32_t bufferI
}
Flags<ipa::vimc::TestFlag> flags;
- paramsBufferReady.emit(bufferId, flags);
+ paramsComputed.emit(bufferId, flags);
}
void IPAVimc::initTrace()
@@ -200,7 +201,7 @@ extern "C" {
const struct IPAModuleInfo ipaModuleInfo = {
IPA_MODULE_API_VERSION,
0,
- "PipelineHandlerVimc",
+ "vimc",
"vimc",
};
diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp
index be30589d..0b04629c 100644
--- a/src/libcamera/base/backtrace.cpp
+++ b/src/libcamera/base/backtrace.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Ideas on Board Oy
*
- * backtrace.h - Call stack backtraces
+ * Call stack backtraces
*/
#include <libcamera/base/backtrace.h>
diff --git a/src/libcamera/base/bound_method.cpp b/src/libcamera/base/bound_method.cpp
index 3ecec51c..322029a8 100644
--- a/src/libcamera/base/bound_method.cpp
+++ b/src/libcamera/base/bound_method.cpp
@@ -2,11 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * bound_method.cpp - Method bind and invocation
+ * Method bind and invocation
*/
#include <libcamera/base/bound_method.h>
#include <libcamera/base/message.h>
+#include <libcamera/base/object.h>
#include <libcamera/base/semaphore.h>
#include <libcamera/base/thread.h>
diff --git a/src/libcamera/base/class.cpp b/src/libcamera/base/class.cpp
index 9c2d9f21..61998398 100644
--- a/src/libcamera/base/class.cpp
+++ b/src/libcamera/base/class.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * class.cpp - Utilities and helpers for classes
+ * Utilities and helpers for classes
*/
#include <libcamera/base/class.h>
diff --git a/src/libcamera/base/event_dispatcher.cpp b/src/libcamera/base/event_dispatcher.cpp
index 4be89e81..5f4a5cb4 100644
--- a/src/libcamera/base/event_dispatcher.cpp
+++ b/src/libcamera/base/event_dispatcher.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_dispatcher.cpp - Event dispatcher
+ * Event dispatcher
*/
#include <libcamera/base/event_dispatcher.h>
diff --git a/src/libcamera/base/event_dispatcher_poll.cpp b/src/libcamera/base/event_dispatcher_poll.cpp
index 7238a316..52bfb34e 100644
--- a/src/libcamera/base/event_dispatcher_poll.cpp
+++ b/src/libcamera/base/event_dispatcher_poll.cpp
@@ -2,19 +2,18 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_dispatcher_poll.cpp - Poll-based event dispatcher
+ * Poll-based event dispatcher
*/
#include <libcamera/base/event_dispatcher_poll.h>
-#include <algorithm>
-#include <chrono>
#include <iomanip>
#include <poll.h>
#include <stdint.h>
#include <string.h>
#include <sys/eventfd.h>
#include <unistd.h>
+#include <vector>
#include <libcamera/base/event_notifier.h>
#include <libcamera/base/log.h>
diff --git a/src/libcamera/base/event_notifier.cpp b/src/libcamera/base/event_notifier.cpp
index fd93c087..495c281d 100644
--- a/src/libcamera/base/event_notifier.cpp
+++ b/src/libcamera/base/event_notifier.cpp
@@ -2,12 +2,13 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * event_notifier.cpp - File descriptor event notifier
+ * File descriptor event notifier
*/
#include <libcamera/base/event_notifier.h>
#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/log.h>
#include <libcamera/base/message.h>
#include <libcamera/base/thread.h>
@@ -20,6 +21,8 @@
namespace libcamera {
+LOG_DECLARE_CATEGORY(Event)
+
/**
* \class EventNotifier
* \brief Notify of activity on a file descriptor
@@ -104,6 +107,9 @@ EventNotifier::~EventNotifier()
*/
void EventNotifier::setEnabled(bool enable)
{
+ if (!assertThreadBound("EventNotifier can't be enabled from another thread"))
+ return;
+
if (enabled_ == enable)
return;
diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
index d1ab1aa5..2b83a517 100644
--- a/src/libcamera/base/file.cpp
+++ b/src/libcamera/base/file.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * file.cpp - File I/O operations
+ * File I/O operations
*/
#include <libcamera/base/file.h>
diff --git a/src/libcamera/base/flags.cpp b/src/libcamera/base/flags.cpp
index 3e4320ac..9981f2ed 100644
--- a/src/libcamera/base/flags.cpp
+++ b/src/libcamera/base/flags.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * flags.cpp - Type-safe enum-based bitfields
+ * Type-safe enum-based bitfields
*/
#include <libcamera/base/flags.h>
diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp
index c8045ef7..8bf3e1da 100644
--- a/src/libcamera/base/log.cpp
+++ b/src/libcamera/base/log.cpp
@@ -2,18 +2,21 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * log.cpp - Logging infrastructure
+ * Logging infrastructure
*/
#include <libcamera/base/log.h>
#include <array>
+#include <charconv>
+#include <fnmatch.h>
#include <fstream>
#include <iostream>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <string_view>
#include <syslog.h>
#include <time.h>
#include <unordered_set>
@@ -38,8 +41,8 @@
* The levels are configurable through the LIBCAMERA_LOG_LEVELS environment
* variable that contains a comma-separated list of 'category:level' pairs.
*
- * The category names are strings and can include a wildcard ('*') character at
- * the end to match multiple categories.
+ * The category names are strings and can include a wildcard ('*') character to
+ * match multiple categories.
*
* The level are either numeric values, or strings containing the log level
* name. The available log levels are DEBUG, INFO, WARN, ERROR and FATAL. Log
@@ -311,15 +314,15 @@ private:
void parseLogFile();
void parseLogLevels();
- static LogSeverity parseLogLevel(const std::string &level);
+ static LogSeverity parseLogLevel(std::string_view level);
friend LogCategory;
- void registerCategory(LogCategory *category);
- LogCategory *findCategory(const char *name) const;
+ LogCategory *findOrCreateCategory(std::string_view name);
static bool destroyed_;
- std::vector<LogCategory *> categories_;
+ Mutex mutex_;
+ std::vector<std::unique_ptr<LogCategory>> categories_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
std::list<std::pair<std::string, LogSeverity>> levels_;
std::shared_ptr<LogOutput> output_;
@@ -436,9 +439,6 @@ void logSetLevel(const char *category, const char *level)
Logger::~Logger()
{
destroyed_ = true;
-
- for (LogCategory *category : categories_)
- delete category;
}
/**
@@ -569,7 +569,9 @@ void Logger::logSetLevel(const char *category, const char *level)
if (severity == LogInvalid)
return;
- for (LogCategory *c : categories_) {
+ MutexLocker locker(mutex_);
+
+ for (const auto &c : categories_) {
if (c->name() == category) {
c->setSeverity(severity);
break;
@@ -640,17 +642,17 @@ void Logger::parseLogLevels()
if (!len)
continue;
- std::string category;
- std::string level;
+ std::string_view category;
+ std::string_view level;
const char *colon = static_cast<const char *>(memchr(pair, ':', len));
if (!colon) {
/* 'x' is a shortcut for '*:x'. */
category = "*";
- level = std::string(pair, len);
+ level = std::string_view(pair, len);
} else {
- category = std::string(pair, colon - pair);
- level = std::string(colon + 1, comma - colon - 1);
+ category = std::string_view(pair, colon - pair);
+ level = std::string_view(colon + 1, comma - colon - 1);
}
/* Both the category and the level must be specified. */
@@ -661,7 +663,7 @@ void Logger::parseLogLevels()
if (severity == LogInvalid)
continue;
- levels_.push_back({ category, severity });
+ levels_.emplace_back(category, severity);
}
}
@@ -675,7 +677,7 @@ void Logger::parseLogLevels()
*
* \return The log severity, or LogInvalid if the string is invalid
*/
-LogSeverity Logger::parseLogLevel(const std::string &level)
+LogSeverity Logger::parseLogLevel(std::string_view level)
{
static const char *const names[] = {
"DEBUG",
@@ -685,15 +687,13 @@ LogSeverity Logger::parseLogLevel(const std::string &level)
"FATAL",
};
- int severity;
+ unsigned int severity = LogInvalid;
if (std::isdigit(level[0])) {
- char *endptr;
- severity = strtoul(level.c_str(), &endptr, 10);
- if (*endptr != '\0' || severity > LogFatal)
+ auto [end, ec] = std::from_chars(level.data(), level.data() + level.size(), severity);
+ if (ec != std::errc() || *end != '\0' || severity > LogFatal)
severity = LogInvalid;
} else {
- severity = LogInvalid;
for (unsigned int i = 0; i < std::size(names); ++i) {
if (names[i] == level) {
severity = i;
@@ -706,52 +706,28 @@ LogSeverity Logger::parseLogLevel(const std::string &level)
}
/**
- * \brief Register a log category with the logger
- * \param[in] category The log category
- *
- * Log categories must have unique names. It is invalid to call this function
- * if a log category with the same name already exists.
+ * \brief Find an existing log category with the given name or create one
+ * \param[in] name Name of the log category
+ * \return The pointer to the log category found or created
*/
-void Logger::registerCategory(LogCategory *category)
+LogCategory *Logger::findOrCreateCategory(std::string_view name)
{
- categories_.push_back(category);
-
- const std::string &name = category->name();
- for (const std::pair<std::string, LogSeverity> &level : levels_) {
- bool match = true;
-
- for (unsigned int i = 0; i < level.first.size(); ++i) {
- if (level.first[i] == '*')
- break;
-
- if (i >= name.size() ||
- name[i] != level.first[i]) {
- match = false;
- break;
- }
- }
+ MutexLocker locker(mutex_);
- if (match) {
- category->setSeverity(level.second);
- break;
- }
+ for (const auto &category : categories_) {
+ if (category->name() == name)
+ return category.get();
}
-}
-/**
- * \brief Find an existing log category with the given name
- * \param[in] name Name of the log category
- * \return The pointer to the found log category or nullptr if not found
- */
-LogCategory *Logger::findCategory(const char *name) const
-{
- if (auto it = std::find_if(categories_.begin(), categories_.end(),
- [name](auto c) { return c->name() == name; });
- it != categories_.end()) {
- return *it;
+ LogCategory *category = categories_.emplace_back(std::unique_ptr<LogCategory>(new LogCategory(name))).get();
+ const char *categoryName = category->name().c_str();
+
+ for (const auto &[pattern, severity] : levels_) {
+ if (fnmatch(pattern.c_str(), categoryName, FNM_NOESCAPE) == 0)
+ category->setSeverity(severity);
}
- return nullptr;
+ return category;
}
/**
@@ -787,25 +763,16 @@ LogCategory *Logger::findCategory(const char *name) const
*
* \return The pointer to the LogCategory
*/
-LogCategory *LogCategory::create(const char *name)
+LogCategory *LogCategory::create(std::string_view name)
{
- static Mutex mutex_;
- MutexLocker locker(mutex_);
- LogCategory *category = Logger::instance()->findCategory(name);
-
- if (!category) {
- category = new LogCategory(name);
- Logger::instance()->registerCategory(category);
- }
-
- return category;
+ return Logger::instance()->findOrCreateCategory(name);
}
/**
* \brief Construct a log category
* \param[in] name The category name
*/
-LogCategory::LogCategory(const char *name)
+LogCategory::LogCategory(std::string_view name)
: name_(name), severity_(LogSeverity::LogInfo)
{
}
@@ -824,15 +791,12 @@ LogCategory::LogCategory(const char *name)
*/
/**
+ * \fn LogCategory::setSeverity(LogSeverity severity)
* \brief Set the severity of the log category
*
* Messages of severity higher than or equal to the severity of the log category
* are printed, other messages are discarded.
*/
-void LogCategory::setSeverity(LogSeverity severity)
-{
- severity_ = severity;
-}
/**
* \brief Retrieve the default log category
@@ -874,39 +838,12 @@ const LogCategory &LogCategory::defaultCategory()
*/
LogMessage::LogMessage(const char *fileName, unsigned int line,
const LogCategory &category, LogSeverity severity,
- const std::string &prefix)
- : category_(category), severity_(severity), prefix_(prefix)
+ std::string prefix)
+ : category_(category), severity_(severity),
+ timestamp_(utils::clock::now()),
+ fileInfo_(static_cast<std::ostringstream &&>(std::ostringstream() << utils::basename(fileName) << ":" << line).str()),
+ prefix_(std::move(prefix))
{
- init(fileName, line);
-}
-
-/**
- * \brief Move-construct a log message
- * \param[in] other The other message
- *
- * The move constructor is meant to support the _log() functions. Thanks to copy
- * elision it will likely never be called, but C++11 only permits copy elision,
- * it doesn't enforce it unlike C++17. To avoid potential link errors depending
- * on the compiler type and version, and optimization level, the move
- * constructor is defined even if it will likely never be called, and ensures
- * that the destructor of the \a other message will not output anything to the
- * log by setting the severity to LogInvalid.
- */
-LogMessage::LogMessage(LogMessage &&other)
- : msgStream_(std::move(other.msgStream_)), category_(other.category_),
- severity_(other.severity_)
-{
- other.severity_ = LogInvalid;
-}
-
-void LogMessage::init(const char *fileName, unsigned int line)
-{
- /* Log the timestamp, severity and file information. */
- timestamp_ = utils::clock::now();
-
- std::ostringstream ossFileInfo;
- ossFileInfo << utils::basename(fileName) << ":" << line;
- fileInfo_ = ossFileInfo.str();
}
LogMessage::~LogMessage()
diff --git a/src/libcamera/base/memfd.cpp b/src/libcamera/base/memfd.cpp
new file mode 100644
index 00000000..ed0b299b
--- /dev/null
+++ b/src/libcamera/base/memfd.cpp
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Anonymous file creation
+ */
+
+#include <libcamera/base/memfd.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file base/memfd.h
+ * \brief Anonymous file creation
+ */
+
+#ifndef __DOXYGEN__
+namespace {
+
+/* uClibc doesn't provide the file sealing API. */
+#if not HAVE_FILE_SEALS
+#define F_ADD_SEALS 1033
+#define F_SEAL_SHRINK 0x0002
+#define F_SEAL_GROW 0x0004
+#endif
+
+#if not HAVE_MEMFD_CREATE
+int memfd_create(const char *name, unsigned int flags)
+{
+ return syscall(SYS_memfd_create, name, flags);
+}
+#endif
+
+} /* namespace */
+#endif /* __DOXYGEN__ */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(File)
+
+/**
+ * \class MemFd
+ * \brief Helper class to create anonymous files
+ *
+ * Anonymous files behave like regular files, and can be modified, truncated,
+ * memory-mapped and so on. Unlike regular files, they however live in RAM and
+ * don't have permanent backing storage.
+ */
+
+/**
+ * \enum MemFd::Seal
+ * \brief Seals for the MemFd::create() function
+ * \var MemFd::Seal::None
+ * \brief No seals (used as default value)
+ * \var MemFd::Seal::Shrink
+ * \brief Prevent the memfd from shrinking
+ * \var MemFd::Seal::Grow
+ * \brief Prevent the memfd from growing
+ */
+
+/**
+ * \typedef MemFd::Seals
+ * \brief A bitwise combination of MemFd::Seal values
+ */
+
+/**
+ * \brief Create an anonymous file
+ * \param[in] name The file name (displayed in symbolic links in /proc/self/fd/)
+ * \param[in] size The file size
+ * \param[in] seals The file seals
+ *
+ * This function is a helper that wraps anonymous file (memfd) creation and
+ * sets the file size and optional seals.
+ *
+ * \return The descriptor of the anonymous file if creation succeeded, or an
+ * invalid UniqueFD otherwise
+ */
+UniqueFD MemFd::create(const char *name, std::size_t size, Seals seals)
+{
+ int ret = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
+ if (ret < 0) {
+ ret = errno;
+ LOG(File, Error)
+ << "Failed to allocate memfd storage for " << name
+ << ": " << strerror(ret);
+ return {};
+ }
+
+ UniqueFD memfd(ret);
+
+ ret = ftruncate(memfd.get(), size);
+ if (ret < 0) {
+ ret = errno;
+ LOG(File, Error)
+ << "Failed to set memfd size for " << name
+ << ": " << strerror(ret);
+ return {};
+ }
+
+ if (seals) {
+ int fileSeals = (seals & Seal::Shrink ? F_SEAL_SHRINK : 0)
+ | (seals & Seal::Grow ? F_SEAL_GROW : 0);
+
+ ret = fcntl(memfd.get(), F_ADD_SEALS, fileSeals);
+ if (ret < 0) {
+ ret = errno;
+ LOG(File, Error)
+ << "Failed to seal the memfd for " << name
+ << ": " << strerror(ret);
+ return {};
+ }
+ }
+
+ return memfd;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
index 7a7fd7e4..a742dfdf 100644
--- a/src/libcamera/base/meson.build
+++ b/src/libcamera/base/meson.build
@@ -1,24 +1,28 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_base_sources = files([
- 'backtrace.cpp',
- 'class.cpp',
+libcamera_base_public_sources = files([
'bound_method.cpp',
+ 'class.cpp',
+ 'flags.cpp',
+ 'object.cpp',
+ 'shared_fd.cpp',
+ 'signal.cpp',
+ 'unique_fd.cpp',
+])
+
+libcamera_base_internal_sources = files([
+ 'backtrace.cpp',
'event_dispatcher.cpp',
'event_dispatcher_poll.cpp',
'event_notifier.cpp',
'file.cpp',
- 'flags.cpp',
'log.cpp',
+ 'memfd.cpp',
'message.cpp',
'mutex.cpp',
- 'object.cpp',
'semaphore.cpp',
- 'shared_fd.cpp',
- 'signal.cpp',
'thread.cpp',
'timer.cpp',
- 'unique_fd.cpp',
'utils.cpp',
])
@@ -49,7 +53,11 @@ libcamera_base_deps = [
libcamera_base_args = [ '-DLIBCAMERA_BASE_PRIVATE' ]
libcamera_base_lib = shared_library('libcamera-base',
- [libcamera_base_sources, libcamera_base_headers],
+ [
+ libcamera_base_public_sources,
+ libcamera_base_internal_sources,
+ libcamera_base_headers,
+ ],
version : libcamera_version,
soversion : libcamera_soversion,
name_prefix : '',
diff --git a/src/libcamera/base/message.cpp b/src/libcamera/base/message.cpp
index 2da2a7ed..098faac6 100644
--- a/src/libcamera/base/message.cpp
+++ b/src/libcamera/base/message.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * message.cpp - Message queue support
+ * Message queue support
*/
#include <libcamera/base/message.h>
diff --git a/src/libcamera/base/mutex.cpp b/src/libcamera/base/mutex.cpp
index e34e8618..2a4542c4 100644
--- a/src/libcamera/base/mutex.cpp
+++ b/src/libcamera/base/mutex.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * mutex.cpp - Mutex classes with clang thread safety annotation
+ * Mutex classes with clang thread safety annotation
*/
#include <libcamera/base/mutex.h>
diff --git a/src/libcamera/base/object.cpp b/src/libcamera/base/object.cpp
index 92cecd22..37d133cc 100644
--- a/src/libcamera/base/object.cpp
+++ b/src/libcamera/base/object.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * object.cpp - Base object
+ * Base object
*/
#include <libcamera/base/object.h>
@@ -40,8 +40,9 @@ LOG_DEFINE_CATEGORY(Object)
* Object class.
*
* Deleting an object from a thread other than the one the object is bound to is
- * unsafe, unless the caller ensures that the object isn't processing any
- * message concurrently.
+ * unsafe, unless the caller ensures that the object's thread is stopped and no
+ * parent or child of the object gets deleted concurrently. See
+ * Object::~Object() for more information.
*
* Object slots connected to signals will also run in the context of the
* object's thread, regardless of whether the signal is emitted in the same or
@@ -84,9 +85,20 @@ Object::Object(Object *parent)
* Object instances shall be destroyed from the thread they are bound to,
* otherwise undefined behaviour may occur. If deletion of an Object needs to
* be scheduled from a different thread, deleteLater() shall be used.
+ *
+ * As an exception to this rule, Object instances may be deleted from a
+ * different thread if the thread the instance is bound to is stopped through
+ * the whole duration of the object's destruction, *and* the parent and children
+ * of the object do not get deleted concurrently. The caller is responsible for
+ * fulfilling those requirements.
+ *
+ * In all cases Object instances shall be deleted before the Thread they are
+ * bound to.
*/
Object::~Object()
{
+ ASSERT(Thread::current() == thread_ || !thread_->isRunning());
+
/*
* Move signals to a private list to avoid concurrent iteration and
* deletion of items from Signal::disconnect().
@@ -116,8 +128,9 @@ Object::~Object()
* event loop that the object belongs to. This ensures the object is destroyed
* from the right context, as required by the libcamera threading model.
*
- * If this function is called before the thread's event loop is started, the
- * object will be deleted when the event loop starts.
+ * If this function is called before the thread's event loop is started or after
+ * it has stopped, the object will be deleted when the event loop (re)starts. If
+ * this never occurs, the object will be leaked.
*
* Deferred deletion can be used to control the destruction context with shared
* pointers. An object managed with shared pointers is deleted when the last
@@ -213,6 +226,35 @@ void Object::message(Message *msg)
}
/**
+ * \fn Object::assertThreadBound()
+ * \brief Check if the caller complies with thread-bound constraints
+ * \param[in] message The message to be printed on error
+ *
+ * This function verifies the calling constraints required by the \threadbound
+ * definition. It shall be called at the beginning of member functions of an
+ * Object subclass that are explicitly marked as thread-bound in their
+ * documentation.
+ *
+ * If the thread-bound constraints are not met, the function prints \a message
+ * as an error message. For debug builds, it additionally causes an assertion
+ * error.
+ *
+ * \todo Verify the thread-bound requirements for functions marked as
+ * thread-bound at the class level.
+ *
+ * \return True if the call is thread-bound compliant, false otherwise
+ */
+bool Object::assertThreadBound(const char *message)
+{
+ if (Thread::current() == thread_)
+ return true;
+
+ LOG(Object, Error) << message;
+ ASSERT(false);
+ return false;
+}
+
+/**
* \fn R Object::invokeMethod()
* \brief Invoke a method asynchronously on an Object instance
* \param[in] func The object method to invoke
@@ -259,11 +301,12 @@ void Object::message(Message *msg)
* Moving an object that has a parent is not allowed, and causes undefined
* behaviour.
*
- * \context This function is thread-bound.
+ * \context This function is \threadbound.
*/
void Object::moveToThread(Thread *thread)
{
- ASSERT(Thread::current() == thread_);
+ if (!assertThreadBound("Object can't be moved from another thread"))
+ return;
if (thread_ == thread)
return;
@@ -307,7 +350,7 @@ void Object::connect(SignalBase *signal)
void Object::disconnect(SignalBase *signal)
{
- for (auto iter = signals_.begin(); iter != signals_.end(); ) {
+ for (auto iter = signals_.begin(); iter != signals_.end();) {
if (*iter == signal)
iter = signals_.erase(iter);
else
diff --git a/src/libcamera/base/semaphore.cpp b/src/libcamera/base/semaphore.cpp
index 6217e386..862f3b31 100644
--- a/src/libcamera/base/semaphore.cpp
+++ b/src/libcamera/base/semaphore.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * semaphore.cpp - General-purpose counting semaphore
+ * General-purpose counting semaphore
*/
#include <libcamera/base/semaphore.h>
diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
index c711cf57..7afc8ca5 100644
--- a/src/libcamera/base/shared_fd.cpp
+++ b/src/libcamera/base/shared_fd.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * shared_fd.cpp - File descriptor wrapper with shared ownership
+ * File descriptor wrapper with shared ownership
*/
#include <libcamera/base/shared_fd.h>
diff --git a/src/libcamera/base/signal.cpp b/src/libcamera/base/signal.cpp
index a46386a0..7876a21d 100644
--- a/src/libcamera/base/signal.cpp
+++ b/src/libcamera/base/signal.cpp
@@ -2,12 +2,13 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * signal.cpp - Signal & slot implementation
+ * Signal & slot implementation
*/
#include <libcamera/base/signal.h>
#include <libcamera/base/mutex.h>
+#include <libcamera/base/object.h>
/**
* \file base/signal.h
@@ -48,7 +49,7 @@ void SignalBase::disconnect(std::function<bool(SlotList::iterator &)> match)
{
MutexLocker locker(signalsLock);
- for (auto iter = slots_.begin(); iter != slots_.end(); ) {
+ for (auto iter = slots_.begin(); iter != slots_.end();) {
if (match(iter)) {
Object *object = (*iter)->object();
if (object)
@@ -74,7 +75,7 @@ SignalBase::SlotList SignalBase::slots()
*
* Signals and slots are a language construct aimed at communication between
* objects through the observer pattern without the need for boilerplate code.
- * See http://doc.qt.io/qt-5/signalsandslots.html for more information.
+ * See http://doc.qt.io/qt-6/signalsandslots.html for more information.
*
* Signals model events that can be observed from objects unrelated to the event
* source. Slots are functions that are called in response to a signal. Signals
diff --git a/src/libcamera/base/thread.cpp b/src/libcamera/base/thread.cpp
index b96951ac..d8fe0d69 100644
--- a/src/libcamera/base/thread.cpp
+++ b/src/libcamera/base/thread.cpp
@@ -2,13 +2,14 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * thread.cpp - Thread support
+ * Thread support
*/
#include <libcamera/base/thread.h>
#include <atomic>
#include <list>
+#include <optional>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -18,6 +19,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/message.h>
#include <libcamera/base/mutex.h>
+#include <libcamera/base/object.h>
/**
* \page thread Thread Support
@@ -63,42 +65,6 @@
* receiver's event loop, running in the receiver's thread. This mechanism can
* be overridden by selecting a different connection type when calling
* Signal::connect().
- *
- * \section thread-reentrancy Reentrancy and Thread-Safety
- *
- * Through the documentation, several terms are used to define how classes and
- * their member functions can be used from multiple threads.
- *
- * - A **reentrant** function may be called simultaneously from multiple
- * threads if and only if each invocation uses a different instance of the
- * class. This is the default for all member functions not explictly marked
- * otherwise.
- *
- * - \anchor thread-safe A **thread-safe** function may be called
- * simultaneously from multiple threads on the same instance of a class. A
- * thread-safe function is thus reentrant. Thread-safe functions may also be
- * called simultaneously with any other reentrant function of the same class
- * on the same instance.
- *
- * - \anchor thread-bound A **thread-bound** function may be called only from
- * the thread that the class instances lives in (see section \ref
- * thread-objects). For instances of classes that do not derive from the
- * Object class, this is the thread in which the instance was created. A
- * thread-bound function is not thread-safe, and may or may not be reentrant.
- *
- * Neither reentrancy nor thread-safety, in this context, mean that a function
- * may be called simultaneously from the same thread, for instance from a
- * callback invoked by the function. This may deadlock and isn't allowed unless
- * separately documented.
- *
- * A class is defined as reentrant, thread-safe or thread-bound if all its
- * member functions are reentrant, thread-safe or thread-bound respectively.
- * Some member functions may additionally be documented as having additional
- * thread-related attributes.
- *
- * Most classes are reentrant but not thread-safe, as making them fully
- * thread-safe would incur locking costs considered prohibitive for the
- * expected use cases.
*/
/**
@@ -163,6 +129,8 @@ private:
int exitCode_;
MessageQueue messages_;
+
+ std::optional<cpu_set_t> cpuset_;
};
/**
@@ -289,6 +257,8 @@ void Thread::start()
data_->exit_.store(false, std::memory_order_relaxed);
thread_ = std::thread(&Thread::startThread, this);
+
+ setThreadAffinityInternal();
}
void Thread::startThread()
@@ -370,6 +340,12 @@ void Thread::run()
void Thread::finishThread()
{
+ /*
+ * Objects may have been scheduled for deletion right before the thread
+ * exited. Ensure they get deleted now, before the thread stops.
+ */
+ dispatchMessages(Message::Type::DeferredDelete);
+
data_->mutex_.lock();
data_->running_ = false;
data_->mutex_.unlock();
@@ -440,6 +416,48 @@ bool Thread::wait(utils::duration duration)
}
/**
+ * \brief Set the CPU affinity mask of the thread
+ * \param[in] cpus The list of CPU indices that the thread is set affinity to
+ *
+ * The CPU indices should be within [0, std::thread::hardware_concurrency()).
+ * If any index is invalid, this function won't modify the thread affinity and
+ * will return an error.
+ *
+ * \return 0 if all indices are valid, -EINVAL otherwise
+ */
+int Thread::setThreadAffinity(const Span<const unsigned int> &cpus)
+{
+ const unsigned int numCpus = std::thread::hardware_concurrency();
+
+ MutexLocker locker(data_->mutex_);
+ data_->cpuset_ = cpu_set_t();
+ CPU_ZERO(&data_->cpuset_.value());
+
+ for (const unsigned int &cpu : cpus) {
+ if (cpu >= numCpus) {
+ LOG(Thread, Error) << "Invalid CPU " << cpu << "for thread affinity";
+ return -EINVAL;
+ }
+
+ CPU_SET(cpu, &data_->cpuset_.value());
+ }
+
+ if (data_->running_)
+ setThreadAffinityInternal();
+
+ return 0;
+}
+
+void Thread::setThreadAffinityInternal()
+{
+ if (!data_->cpuset_)
+ return;
+
+ const cpu_set_t &cpuset = data_->cpuset_.value();
+ pthread_setaffinity_np(thread_.native_handle(), sizeof(cpuset), &cpuset);
+}
+
+/**
* \brief Check if the thread is running
*
* A Thread instance is considered as running once the underlying thread has
@@ -586,10 +604,12 @@ void Thread::removeMessages(Object *receiver)
/**
* \brief Dispatch posted messages for this thread
* \param[in] type The message type
+ * \param[in] receiver The receiver whose messages to dispatch
*
- * This function immediately dispatches all the messages previously posted for
- * this thread with postMessage() that match the message \a type. If the \a type
- * is Message::Type::None, all messages are dispatched.
+ * This function immediately dispatches all the messages of the given \a type
+ * previously posted to this thread for the \a receiver with postMessage(). If
+ * the \a type is Message::Type::None, all messages types are dispatched. If the
+ * \a receiver is null, messages to all receivers are dispatched.
*
* Messages shall only be dispatched from the current thread, typically within
* the thread from the run() function. Calling this function outside of the
@@ -599,7 +619,7 @@ void Thread::removeMessages(Object *receiver)
* same thread from an object's message handler. It guarantees delivery of
* messages in the order they have been posted in all cases.
*/
-void Thread::dispatchMessages(Message::Type type)
+void Thread::dispatchMessages(Message::Type type, Object *receiver)
{
ASSERT(data_ == ThreadData::current());
@@ -616,6 +636,9 @@ void Thread::dispatchMessages(Message::Type type)
if (type != Message::Type::None && msg->type() != type)
continue;
+ if (receiver && receiver != msg->receiver_)
+ continue;
+
/*
* Move the message, setting the entry in the list to null. It
* will cause recursive calls to ignore the entry, and the erase
@@ -623,12 +646,12 @@ void Thread::dispatchMessages(Message::Type type)
*/
std::unique_ptr<Message> message = std::move(msg);
- Object *receiver = message->receiver_;
- ASSERT(data_ == receiver->thread()->data_);
- receiver->pendingMessages_--;
+ Object *messageReceiver = message->receiver_;
+ ASSERT(data_ == messageReceiver->thread()->data_);
+ messageReceiver->pendingMessages_--;
locker.unlock();
- receiver->message(message.get());
+ messageReceiver->message(message.get());
message.reset();
locker.lock();
}
@@ -639,7 +662,7 @@ void Thread::dispatchMessages(Message::Type type)
* the outer calls.
*/
if (!--data_->messages_.recursion_) {
- for (auto iter = messages.begin(); iter != messages.end(); ) {
+ for (auto iter = messages.begin(); iter != messages.end();) {
if (!*iter)
iter = messages.erase(iter);
else
diff --git a/src/libcamera/base/timer.cpp b/src/libcamera/base/timer.cpp
index 74b060af..7b0f3725 100644
--- a/src/libcamera/base/timer.cpp
+++ b/src/libcamera/base/timer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * timer.cpp - Generic timer
+ * Generic timer
*/
#include <libcamera/base/timer.h>
@@ -85,10 +85,8 @@ void Timer::start(std::chrono::milliseconds duration)
*/
void Timer::start(std::chrono::steady_clock::time_point deadline)
{
- if (Thread::current() != thread()) {
- LOG(Timer, Error) << "Timer " << this << " << can't be started from another thread";
+ if (!assertThreadBound("Timer can't be started from another thread"))
return;
- }
deadline_ = deadline;
@@ -114,13 +112,11 @@ void Timer::start(std::chrono::steady_clock::time_point deadline)
*/
void Timer::stop()
{
- if (!isRunning())
+ if (!assertThreadBound("Timer can't be stopped from another thread"))
return;
- if (Thread::current() != thread()) {
- LOG(Timer, Error) << "Timer " << this << " can't be stopped from another thread";
+ if (!isRunning())
return;
- }
unregisterTimer();
}
diff --git a/src/libcamera/base/unique_fd.cpp b/src/libcamera/base/unique_fd.cpp
index 83d6919c..d0649e4d 100644
--- a/src/libcamera/base/unique_fd.cpp
+++ b/src/libcamera/base/unique_fd.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * unique_fd.cpp - File descriptor wrapper that owns a file descriptor
+ * File descriptor wrapper that owns a file descriptor
*/
#include <libcamera/base/unique_fd.h>
diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp
index 3b73b442..bcfc1941 100644
--- a/src/libcamera/base/utils.cpp
+++ b/src/libcamera/base/utils.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * utils.cpp - Miscellaneous utility functions
+ * Miscellaneous utility functions
*/
#include <libcamera/base/utils.h>
@@ -276,21 +276,6 @@ std::string details::StringSplitter::iterator::operator*() const
return ss_->str_.substr(pos_, count);
}
-bool details::StringSplitter::iterator::operator!=(const details::StringSplitter::iterator &other) const
-{
- return pos_ != other.pos_;
-}
-
-details::StringSplitter::iterator details::StringSplitter::begin() const
-{
- return iterator(this, 0);
-}
-
-details::StringSplitter::iterator details::StringSplitter::end() const
-{
- return iterator(this, std::string::npos);
-}
-
/**
* \fn template<typename Container, typename UnaryOp> \
* std::string utils::join(const Container &items, const std::string &sep, UnaryOp op)
@@ -517,10 +502,152 @@ double strtod(const char *__restrict nptr, char **__restrict endptr)
* If the libc implementation doesn't provide locale object support,
* assume that strtod() is locale-independent.
*/
- return strtod(nptr, endptr);
+ return ::strtod(nptr, endptr);
#endif
}
+/**
+ * \fn to_underlying(Enum e)
+ * \brief Convert an enumeration to its underlygin type
+ * \param[in] e Enumeration value to convert
+ *
+ * This function is equivalent to the C++23 std::to_underlying().
+ *
+ * \return The value of e converted to its underlying type
+ */
+
+/**
+ * \class ScopeExitActions
+ * \brief An object that performs actions upon destruction
+ *
+ * The ScopeExitActions class is a simple object that performs user-provided
+ * actions upon destruction. It is meant to simplify cleanup tasks in error
+ * handling paths.
+ *
+ * When the code flow performs multiple sequential actions that each need a
+ * corresponding cleanup action, error handling quickly become tedious:
+ *
+ * \code{.cpp}
+ * {
+ * int ret = allocateMemory();
+ * if (ret)
+ * return ret;
+ *
+ * ret = startProducer();
+ * if (ret) {
+ * freeMemory();
+ * return ret;
+ * }
+ *
+ * ret = startConsumer();
+ * if (ret) {
+ * stopProducer();
+ * freeMemory();
+ * return ret;
+ * }
+ *
+ * return 0;
+ * }
+ * \endcode
+ *
+ * This is prone to programming mistakes, as cleanup actions can easily be
+ * forgotten or ordered incorrectly. One strategy to simplify error handling is
+ * to use goto statements:
+ *
+ * \code{.cpp}
+ * {
+ * int ret = allocateMemory();
+ * if (ret)
+ * return ret;
+ *
+ * ret = startProducer();
+ * if (ret)
+ * goto error_free;
+ *
+ * ret = startConsumer();
+ * if (ret)
+ * goto error_stop;
+ *
+ * return 0;
+ *
+ * error_stop:
+ * stopProducer();
+ * error_free:
+ * freeMemory();
+ * return ret;
+ * }
+ * \endcode
+ *
+ * While this may be considered better, this solution is still quite
+ * error-prone. Beside the risk of picking the wrong error label, the error
+ * handling logic is separated from the normal code flow, which increases the
+ * risk of error when refactoring the code. Additionally, C++ doesn't allow
+ * goto statements to jump over local variable declarations, which can make
+ * usage of this pattern more difficult.
+ *
+ * The ScopeExitActions class solves these issues by allowing code that
+ * requires cleanup actions to be grouped with its corresponding error handling
+ * code:
+ *
+ * \code{.cpp}
+ * {
+ * ScopeExitActions actions;
+ *
+ * int ret = allocateMemory();
+ * if (ret)
+ * return ret;
+ *
+ * actions += [&]() { freeMemory(); };
+ *
+ * ret = startProducer();
+ * if (ret)
+ * return ret;
+ *
+ * actions += [&]() { stopProducer(); };
+ *
+ * ret = startConsumer();
+ * if (ret)
+ * return ret;
+ *
+ * actions.release();
+ * return 0;
+ * }
+ * \endcode
+ *
+ * Error handlers are executed when the ScopeExitActions instance is destroyed,
+ * in the reverse order of their addition.
+ */
+
+ScopeExitActions::~ScopeExitActions()
+{
+ for (const auto &action : utils::reverse(actions_))
+ action();
+}
+
+/**
+ * \brief Add an exit action
+ * \param[in] action The action
+ *
+ * Add an exit action to the ScopeExitActions. Actions will be called upon
+ * destruction in the reverse order of their addition.
+ */
+void ScopeExitActions::operator+=(std::function<void()> &&action)
+{
+ actions_.push_back(std::move(action));
+}
+
+/**
+ * \brief Remove all exit actions
+ *
+ * This function should be called in scope exit paths that don't need the
+ * actions to be executed, such as success return paths from a function when
+ * the ScopeExitActions is used for error cleanup.
+ */
+void ScopeExitActions::release()
+{
+ actions_.clear();
+}
+
} /* namespace utils */
#ifndef __DOXYGEN__
diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp
index 3bf15fb4..3dab91fc 100644
--- a/src/libcamera/bayer_format.cpp
+++ b/src/libcamera/bayer_format.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * bayer_format.cpp - Class to represent Bayer formats
+ * Class to represent Bayer formats
*/
#include "libcamera/internal/bayer_format.h"
@@ -61,6 +61,10 @@ namespace libcamera {
* \brief Format uses MIPI CSI-2 style packing
* \var BayerFormat::Packing::IPU3
* \brief Format uses IPU3 style packing
+ * \var BayerFormat::Packing::PISP1
+ * \brief Format uses PISP mode 1 compression
+ * \var BayerFormat::Packing::PISP2
+ * \brief Format uses PISP mode 2 compression
*/
namespace {
@@ -164,12 +168,28 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
{ formats::SGRBG16, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16) } },
{ { BayerFormat::RGGB, 16, BayerFormat::Packing::None },
{ formats::SRGGB16, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16) } },
+ { { BayerFormat::BGGR, 16, BayerFormat::Packing::PISP1 },
+ { formats::BGGR_PISP_COMP1, V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_BGGR) } },
+ { { BayerFormat::GBRG, 16, BayerFormat::Packing::PISP1 },
+ { formats::GBRG_PISP_COMP1, V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GBRG) } },
+ { { BayerFormat::GRBG, 16, BayerFormat::Packing::PISP1 },
+ { formats::GRBG_PISP_COMP1, V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GRBG) } },
+ { { BayerFormat::RGGB, 16, BayerFormat::Packing::PISP1 },
+ { formats::RGGB_PISP_COMP1, V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_RGGB) } },
{ { BayerFormat::MONO, 8, BayerFormat::Packing::None },
{ formats::R8, V4L2PixelFormat(V4L2_PIX_FMT_GREY) } },
{ { BayerFormat::MONO, 10, BayerFormat::Packing::None },
{ formats::R10, V4L2PixelFormat(V4L2_PIX_FMT_Y10) } },
{ { BayerFormat::MONO, 10, BayerFormat::Packing::CSI2 },
{ formats::R10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_Y10P) } },
+ { { BayerFormat::MONO, 12, BayerFormat::Packing::None },
+ { formats::R12, V4L2PixelFormat(V4L2_PIX_FMT_Y12) } },
+ { { BayerFormat::MONO, 12, BayerFormat::Packing::CSI2 },
+ { formats::R12_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_Y12P) } },
+ { { BayerFormat::MONO, 16, BayerFormat::Packing::None },
+ { formats::R16, V4L2PixelFormat(V4L2_PIX_FMT_Y16) } },
+ { { BayerFormat::MONO, 16, BayerFormat::Packing::PISP1 },
+ { formats::MONO_PISP_COMP1, V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_MONO) } },
};
const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{
@@ -205,9 +225,14 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{
{ MEDIA_BUS_FMT_SGBRG16_1X16, { BayerFormat::GBRG, 16, BayerFormat::Packing::None } },
{ MEDIA_BUS_FMT_SGRBG16_1X16, { BayerFormat::GRBG, 16, BayerFormat::Packing::None } },
{ MEDIA_BUS_FMT_SRGGB16_1X16, { BayerFormat::RGGB, 16, BayerFormat::Packing::None } },
+ { MEDIA_BUS_FMT_SBGGR20_1X20, { BayerFormat::BGGR, 20, BayerFormat::Packing::None } },
+ { MEDIA_BUS_FMT_SGBRG20_1X20, { BayerFormat::GBRG, 20, BayerFormat::Packing::None } },
+ { MEDIA_BUS_FMT_SGRBG20_1X20, { BayerFormat::GRBG, 20, BayerFormat::Packing::None } },
+ { MEDIA_BUS_FMT_SRGGB20_1X20, { BayerFormat::RGGB, 20, BayerFormat::Packing::None } },
{ MEDIA_BUS_FMT_Y8_1X8, { BayerFormat::MONO, 8, BayerFormat::Packing::None } },
{ MEDIA_BUS_FMT_Y10_1X10, { BayerFormat::MONO, 10, BayerFormat::Packing::None } },
{ MEDIA_BUS_FMT_Y12_1X12, { BayerFormat::MONO, 12, BayerFormat::Packing::None } },
+ { MEDIA_BUS_FMT_Y16_1X16, { BayerFormat::MONO, 16, BayerFormat::Packing::None } },
};
} /* namespace */
@@ -298,6 +323,10 @@ std::ostream &operator<<(std::ostream &out, const BayerFormat &f)
out << "-CSI2P";
else if (f.packing == BayerFormat::Packing::IPU3)
out << "-IPU3P";
+ else if (f.packing == BayerFormat::Packing::PISP1)
+ out << "-PISP1";
+ else if (f.packing == BayerFormat::Packing::PISP2)
+ out << "-PISP2";
return out;
}
diff --git a/src/libcamera/byte_stream_buffer.cpp b/src/libcamera/byte_stream_buffer.cpp
index 881cd371..fba9a6f3 100644
--- a/src/libcamera/byte_stream_buffer.cpp
+++ b/src/libcamera/byte_stream_buffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * byte_stream_buffer.cpp - Byte stream buffer
+ * Byte stream buffer
*/
#include "libcamera/internal/byte_stream_buffer.h"
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 0ad1a4b5..c180a5fd 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -2,26 +2,31 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * camera.cpp - Camera device
+ * Camera device
*/
#include <libcamera/camera.h>
#include <array>
#include <atomic>
-#include <iomanip>
+#include <ios>
+#include <memory>
+#include <optional>
+#include <set>
+#include <sstream>
#include <libcamera/base/log.h>
#include <libcamera/base/thread.h>
#include <libcamera/color_space.h>
+#include <libcamera/control_ids.h>
#include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_controls.h"
-#include "libcamera/internal/formats.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/request.h"
@@ -117,6 +122,12 @@
* of view is affected by the pipeline.
*/
+/**
+ * \internal
+ * \file libcamera/internal/camera.h
+ * \brief Internal camera device handling
+ */
+
namespace libcamera {
LOG_DECLARE_CATEGORY(Camera)
@@ -327,7 +338,7 @@ void CameraConfiguration::addConfiguration(const StreamConfiguration &cfg)
* \retval CameraConfiguration::Invalid The configuration is invalid and can't
* be adjusted. This may only occur in extreme cases such as when the
* configuration is empty.
- * \retval CameraConfigutation::Adjusted The configuration has been adjusted
+ * \retval CameraConfiguration::Adjusted The configuration has been adjusted
* and is now valid. Parameters may have changed for any stream, and stream
* configurations may have been removed. The caller shall check the
* configuration carefully.
@@ -494,7 +505,7 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF
std::optional<ColorSpace> colorSpace;
Size size;
- for (auto [i, cfg] : utils::enumerate(config_)) {
+ for (StreamConfiguration &cfg : config_) {
if (!cfg.colorSpace)
continue;
@@ -559,6 +570,7 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF
* \brief The vector of stream configurations
*/
+#ifndef __DOXYGEN_PUBLIC__
/**
* \class Camera::Private
* \brief Base class for camera private data
@@ -576,7 +588,8 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF
* \param[in] pipe The pipeline handler responsible for the camera device
*/
Camera::Private::Private(PipelineHandler *pipe)
- : requestSequence_(0), pipe_(pipe->shared_from_this()),
+ : controlInfo_({}, controls::controls), properties_(properties::properties),
+ requestSequence_(0), pipe_(pipe->shared_from_this()),
disconnected_(false), state_(CameraAvailable)
{
}
@@ -594,6 +607,11 @@ Camera::Private::~Private()
*/
/**
+ * \fn Camera::Private::pipe() const
+ * \copydoc Camera::Private::pipe()
+ */
+
+/**
* \fn Camera::Private::validator()
* \brief Retrieve the control validator related to this camera
* \return The control validator associated with this camera
@@ -719,6 +737,7 @@ void Camera::Private::setState(State state)
{
state_.store(state, std::memory_order_release);
}
+#endif /* __DOXYGEN_PUBLIC__ */
/**
* \class Camera
@@ -813,6 +832,7 @@ void Camera::Private::setState(State state)
*/
/**
+ * \internal
* \brief Create a camera instance
* \param[in] d Camera private data
* \param[in] id The ID of the camera device
@@ -986,7 +1006,8 @@ int Camera::acquire()
if (ret < 0)
return ret == -EACCES ? -EBUSY : ret;
- if (!d->pipe_->acquire()) {
+ if (!d->pipe_->invokeMethod(&PipelineHandler::acquire,
+ ConnectionTypeBlocking, this)) {
LOG(Camera, Info)
<< "Pipeline handler in use by another process";
return -EBUSY;
@@ -1021,7 +1042,8 @@ int Camera::release()
return ret == -EACCES ? -EBUSY : ret;
if (d->isAcquired())
- d->pipe_->release(this);
+ d->pipe_->invokeMethod(&PipelineHandler::release,
+ ConnectionTypeBlocking, this);
d->setState(Private::CameraAvailable);
@@ -1164,8 +1186,8 @@ int Camera::configure(CameraConfiguration *config)
if (ret < 0)
return ret;
- for (auto it : *config)
- it.setStream(nullptr);
+ for (auto &cfg : *config)
+ cfg.setStream(nullptr);
if (config->validate() != CameraConfiguration::Valid) {
LOG(Camera, Error)
@@ -1306,6 +1328,25 @@ int Camera::queueRequest(Request *request)
}
}
+ /* Pre-process AeEnable. */
+ ControlList &controls = request->controls();
+ const auto &aeEnable = controls.get(controls::AeEnable);
+ if (aeEnable) {
+ if (_d()->controlInfo_.count(controls::AnalogueGainMode.id()) &&
+ !controls.contains(controls::AnalogueGainMode.id())) {
+ controls.set(controls::AnalogueGainMode,
+ *aeEnable ? controls::AnalogueGainModeAuto
+ : controls::AnalogueGainModeManual);
+ }
+
+ if (_d()->controlInfo_.count(controls::ExposureTimeMode.id()) &&
+ !controls.contains(controls::ExposureTimeMode.id())) {
+ controls.set(controls::ExposureTimeMode,
+ *aeEnable ? controls::ExposureTimeModeAuto
+ : controls::ExposureTimeModeManual);
+ }
+ }
+
d->pipe_->invokeMethod(&PipelineHandler::queueRequest,
ConnectionTypeQueued, request);
diff --git a/src/libcamera/camera_controls.cpp b/src/libcamera/camera_controls.cpp
index cabdcf75..b672c7cf 100644
--- a/src/libcamera/camera_controls.cpp
+++ b/src/libcamera/camera_controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_controls.cpp - Camera controls
+ * Camera controls
*/
#include "libcamera/internal/camera_controls.h"
diff --git a/src/libcamera/camera_lens.cpp b/src/libcamera/camera_lens.cpp
index b3d48199..ccc2a6a6 100644
--- a/src/libcamera/camera_lens.cpp
+++ b/src/libcamera/camera_lens.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_lens.cpp - A camera lens
+ * A camera lens
*/
#include "libcamera/internal/camera_lens.h"
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 355f3ada..e62e7193 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * camera_manager.h - Camera management
+ * Camera management
*/
#include "libcamera/internal/camera_manager.h"
@@ -15,6 +15,7 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/pipeline_handler.h"
/**
@@ -23,6 +24,7 @@
*/
/**
+ * \internal
* \file libcamera/internal/camera_manager.h
* \brief Internal camera manager support
*/
@@ -34,9 +36,11 @@ namespace libcamera {
LOG_DEFINE_CATEGORY(Camera)
+#ifndef __DOXYGEN_PUBLIC__
CameraManager::Private::Private()
: initialized_(false)
{
+ ipaManager_ = std::make_unique<IPAManager>();
}
int CameraManager::Private::start()
@@ -76,8 +80,10 @@ void CameraManager::Private::run()
mutex_.unlock();
cv_.notify_one();
- if (ret < 0)
+ if (ret < 0) {
+ cleanup();
return;
+ }
/* Now start processing events and messages. */
exec();
@@ -99,16 +105,37 @@ int CameraManager::Private::init()
void CameraManager::Private::createPipelineHandlers()
{
- CameraManager *const o = LIBCAMERA_O_PTR();
-
/*
* \todo Try to read handlers and order from configuration
- * file and only fallback on all handlers if there is no
- * configuration file.
+ * file and only fallback on environment variable or all handlers, if
+ * there is no configuration file.
*/
+ const char *pipesList =
+ utils::secure_getenv("LIBCAMERA_PIPELINES_MATCH_LIST");
+ if (pipesList) {
+ /*
+ * When a list of preferred pipelines is defined, iterate
+ * through the ordered list to match the enumerated devices.
+ */
+ for (const auto &pipeName : utils::split(pipesList, ",")) {
+ const PipelineHandlerFactoryBase *factory;
+ factory = PipelineHandlerFactoryBase::getFactoryByName(pipeName);
+ if (!factory)
+ continue;
+
+ LOG(Camera, Debug)
+ << "Found listed pipeline handler '"
+ << pipeName << "'";
+ pipelineFactoryMatch(factory);
+ }
+
+ return;
+ }
+
const std::vector<PipelineHandlerFactoryBase *> &factories =
PipelineHandlerFactoryBase::factories();
+ /* Match all the registered pipeline handlers. */
for (const PipelineHandlerFactoryBase *factory : factories) {
LOG(Camera, Debug)
<< "Found registered pipeline handler '"
@@ -117,15 +144,23 @@ void CameraManager::Private::createPipelineHandlers()
* Try each pipeline handler until it exhaust
* all pipelines it can provide.
*/
- while (1) {
- std::shared_ptr<PipelineHandler> pipe = factory->create(o);
- if (!pipe->match(enumerator_.get()))
- break;
+ pipelineFactoryMatch(factory);
+ }
+}
- LOG(Camera, Debug)
- << "Pipeline handler \"" << factory->name()
- << "\" matched";
- }
+void CameraManager::Private::pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory)
+{
+ CameraManager *const o = LIBCAMERA_O_PTR();
+
+ /* Provide as many matching pipelines as possible. */
+ while (1) {
+ std::shared_ptr<PipelineHandler> pipe = factory->create(o);
+ if (!pipe->match(enumerator_.get()))
+ break;
+
+ LOG(Camera, Debug)
+ << "Pipeline handler \"" << factory->name()
+ << "\" matched";
}
}
@@ -167,24 +202,24 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
{
ASSERT(Thread::current() == this);
- MutexLocker locker(mutex_);
+ {
+ MutexLocker locker(mutex_);
- for (const std::shared_ptr<Camera> &c : cameras_) {
- if (c->id() == camera->id()) {
- LOG(Camera, Fatal)
- << "Trying to register a camera with a duplicated ID '"
- << camera->id() << "'";
- return;
+ for (const std::shared_ptr<Camera> &c : cameras_) {
+ if (c->id() == camera->id()) {
+ LOG(Camera, Fatal)
+ << "Trying to register a camera with a duplicated ID '"
+ << camera->id() << "'";
+ return;
+ }
}
- }
-
- cameras_.push_back(std::move(camera));
- unsigned int index = cameras_.size() - 1;
+ cameras_.push_back(camera);
+ }
/* Report the addition to the public signal */
CameraManager *const o = LIBCAMERA_O_PTR();
- o->cameraAdded.emit(cameras_[index]);
+ o->cameraAdded.emit(camera);
}
/**
@@ -201,26 +236,33 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
{
ASSERT(Thread::current() == this);
- MutexLocker locker(mutex_);
+ {
+ MutexLocker locker(mutex_);
- auto iter = std::find_if(cameras_.begin(), cameras_.end(),
- [camera](std::shared_ptr<Camera> &c) {
- return c.get() == camera.get();
- });
- if (iter == cameras_.end())
- return;
+ auto iter = std::find(cameras_.begin(), cameras_.end(), camera);
+ if (iter == cameras_.end())
+ return;
+
+ cameras_.erase(iter);
+ }
LOG(Camera, Debug)
<< "Unregistering camera '" << camera->id() << "'";
- cameras_.erase(iter);
-
/* Report the removal to the public signal */
CameraManager *const o = LIBCAMERA_O_PTR();
o->cameraRemoved.emit(camera);
}
/**
+ * \fn CameraManager::Private::ipaManager() const
+ * \brief Retrieve the IPAManager
+ * \context This function is \threadsafe.
+ * \return The IPAManager for this CameraManager
+ */
+#endif /* __DOXYGEN_PUBLIC__ */
+
+/**
* \class CameraManager
* \brief Provide access and manage all cameras in the system
*
@@ -339,13 +381,13 @@ std::vector<std::shared_ptr<Camera>> CameraManager::cameras() const
*
* \return Shared pointer to Camera object or nullptr if camera not found
*/
-std::shared_ptr<Camera> CameraManager::get(const std::string &id)
+std::shared_ptr<Camera> CameraManager::get(std::string_view id)
{
Private *const d = _d();
MutexLocker locker(d->mutex_);
- for (std::shared_ptr<Camera> camera : d->cameras_) {
+ for (const std::shared_ptr<Camera> &camera : d->cameras_) {
if (camera->id() == id)
return camera;
}
diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
index 7356bf7d..3d1c456c 100644
--- a/src/libcamera/color_space.cpp
+++ b/src/libcamera/color_space.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Raspberry Pi Ltd
*
- * color_space.cpp - color spaces.
+ * color spaces.
*/
#include <libcamera/color_space.h>
diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in
index 5fb1c2c3..65668d48 100644
--- a/src/libcamera/control_ids.cpp.in
+++ b/src/libcamera/control_ids.cpp.in
@@ -2,61 +2,122 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_ids.cpp : Control ID list
+ * {{mode}} ID list
*
* This file is auto-generated. Do not edit.
*/
-#include <libcamera/control_ids.h>
+#include <libcamera/{{filename}}.h>
#include <libcamera/controls.h>
/**
- * \file control_ids.h
- * \brief Camera control identifiers
+ * \file {{filename}}.h
+ * \brief Camera {{mode}} identifiers
*/
namespace libcamera {
/**
- * \brief Namespace for libcamera controls
+ * \brief Namespace for libcamera {{mode}}
*/
-namespace controls {
+namespace {{mode}} {
-${controls_doc}
+{%- for vendor, ctrls in controls -%}
+{%- if vendor != 'libcamera' %}
/**
- * \brief Namespace for libcamera draft controls
+ * \brief Namespace for {{vendor}} {{mode}}
*/
-namespace draft {
+namespace {{vendor}} {
+{%- endif -%}
-${draft_controls_doc}
+{% for ctrl in ctrls %}
-} /* namespace draft */
+{% if ctrl.is_enum -%}
+/**
+ * \enum {{ctrl.name}}Enum
+ * \brief Supported {{ctrl.name}} values
+{%- for enum in ctrl.enum_values %}
+ *
+ * \var {{enum.name}}
+ * \brief {{enum.description|format_description}}
+{%- endfor %}
+ */
+
+/**
+ * \var {{ctrl.name}}Values
+ * \brief List of all {{ctrl.name}} supported values
+ */
+
+/**
+ * \var {{ctrl.name}}NameValueMap
+ * \brief Map of all {{ctrl.name}} supported value names (in std::string format) to value
+ */
+
+{% endif -%}
+/**
+ * \var {{ctrl.name}}
+ * \brief {{ctrl.description|format_description}}
+ */
+{%- endfor %}
+{% if vendor != 'libcamera' %}
+} /* namespace {{vendor}} */
+{% endif -%}
+
+{%- endfor %}
#ifndef __DOXYGEN__
/*
- * Keep the controls definitions hidden from doxygen as it incorrectly parses
+ * Keep the {{mode}} definitions hidden from doxygen as it incorrectly parses
* them as functions.
*/
-${controls_def}
+{% for vendor, ctrls in controls -%}
+
+{% if vendor != 'libcamera' %}
+namespace {{vendor}} {
+{% endif %}
-namespace draft {
+{%- for ctrl in ctrls %}
+{% if ctrl.is_enum -%}
+extern const std::array<const ControlValue, {{ctrl.enum_values_count}}> {{ctrl.name}}Values = {
+{%- for enum in ctrl.enum_values %}
+ static_cast<{{ctrl.type}}>({{enum.name}}),
+{%- endfor %}
+};
+extern const std::map<std::string, {{ctrl.type}}> {{ctrl.name}}NameValueMap = {
+{%- for enum in ctrl.enum_values %}
+ { "{{enum.name}}", {{enum.name}} },
+{%- endfor %}
+};
+extern const Control<{{ctrl.type}}> {{ctrl.name}}({{ctrl.name|snake_case|upper}}, "{{ctrl.name}}", "{{vendor}}", {{ctrl.direction}}, {{ctrl.name}}NameValueMap);
+{% else -%}
+extern const Control<{{ctrl.type}}> {{ctrl.name}}({{ctrl.name|snake_case|upper}}, "{{ctrl.name}}", "{{vendor}}", {{ctrl.direction}});
+{% endif -%}
+{%- endfor %}
-${draft_controls_def}
+{% if vendor != 'libcamera' %}
+} /* namespace {{vendor}} */
+{% endif -%}
-} /* namespace draft */
-#endif
+{%- endfor %}
+#endif /* __DOXYGEN__ */
/**
- * \brief List of all supported libcamera controls
+ * \brief List of all supported libcamera {{mode}}
+{%- if mode == 'controls' %}
*
* Unless otherwise stated, all controls are bi-directional, i.e. they can be
* set through Request::controls() and returned out through Request::metadata().
+{%- endif %}
*/
-extern const ControlIdMap controls {
-${controls_map}
+extern const ControlIdMap {{mode}} {
+{%- for vendor, ctrls in controls -%}
+{%- for ctrl in ctrls %}
+ { {{ctrl.namespace}}{{ctrl.name|snake_case|upper}}, &{{ctrl.namespace}}{{ctrl.name}} },
+{%- endfor -%}
+{%- endfor %}
};
-} /* namespace controls */
+} /* namespace {{mode}} */
} /* namespace libcamera */
diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
deleted file mode 100644
index f2e542f4..00000000
--- a/src/libcamera/control_ids.yaml
+++ /dev/null
@@ -1,1009 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Copyright (C) 2019, Google Inc.
-#
-%YAML 1.1
----
-# Unless otherwise stated, all controls are bi-directional, i.e. they can be
-# set through Request::controls() and returned out through Request::metadata().
-controls:
- - AeEnable:
- type: bool
- description: |
- Enable or disable the AE.
-
- \sa ExposureTime AnalogueGain
-
- - AeLocked:
- type: bool
- description: |
- Report the lock status of a running AE algorithm.
-
- If the AE algorithm is locked the value shall be set to true, if it's
- converging it shall be set to false. If the AE algorithm is not
- running the control shall not be present in the metadata control list.
-
- \sa AeEnable
-
- # AeMeteringMode needs further attention:
- # - Auto-generate max enum value.
- # - Better handling of custom types.
- - AeMeteringMode:
- type: int32_t
- description: |
- Specify a metering mode for the AE algorithm to use. The metering
- modes determine which parts of the image are used to determine the
- scene brightness. Metering modes may be platform specific and not
- all metering modes may be supported.
- enum:
- - name: MeteringCentreWeighted
- value: 0
- description: Centre-weighted metering mode.
- - name: MeteringSpot
- value: 1
- description: Spot metering mode.
- - name: MeteringMatrix
- value: 2
- description: Matrix metering mode.
- - name: MeteringCustom
- value: 3
- description: Custom metering mode.
-
- # AeConstraintMode needs further attention:
- # - Auto-generate max enum value.
- # - Better handling of custom types.
- - AeConstraintMode:
- type: int32_t
- description: |
- Specify a constraint mode for the AE algorithm to use. These determine
- how the measured scene brightness is adjusted to reach the desired
- target exposure. Constraint modes may be platform specific, and not
- all constraint modes may be supported.
- enum:
- - name: ConstraintNormal
- value: 0
- description: Default constraint mode.
- This mode aims to balance the exposure of different parts of the
- image so as to reach a reasonable average level. However, highlights
- in the image may appear over-exposed and lowlights may appear
- under-exposed.
- - name: ConstraintHighlight
- value: 1
- description: Highlight constraint mode.
- This mode adjusts the exposure levels in order to try and avoid
- over-exposing the brightest parts (highlights) of an image.
- Other non-highlight parts of the image may appear under-exposed.
- - name: ConstraintShadows
- value: 2
- description: Shadows constraint mode.
- This mode adjusts the exposure levels in order to try and avoid
- under-exposing the dark parts (shadows) of an image. Other normally
- exposed parts of the image may appear over-exposed.
- - name: ConstraintCustom
- value: 3
- description: Custom constraint mode.
-
- # AeExposureMode needs further attention:
- # - Auto-generate max enum value.
- # - Better handling of custom types.
- - AeExposureMode:
- type: int32_t
- description: |
- Specify an exposure mode for the AE algorithm to use. These specify
- how the desired total exposure is divided between the shutter time
- and the sensor's analogue gain. The exposure modes are platform
- specific, and not all exposure modes may be supported.
- enum:
- - name: ExposureNormal
- value: 0
- description: Default exposure mode.
- - name: ExposureShort
- value: 1
- description: Exposure mode allowing only short exposure times.
- - name: ExposureLong
- value: 2
- description: Exposure mode allowing long exposure times.
- - name: ExposureCustom
- value: 3
- description: Custom exposure mode.
-
- - ExposureValue:
- type: float
- description: |
- Specify an Exposure Value (EV) parameter. The EV parameter will only be
- applied if the AE algorithm is currently enabled.
-
- By convention EV adjusts the exposure as log2. For example
- EV = [-2, -1, 0.5, 0, 0.5, 1, 2] results in an exposure adjustment
- of [1/4x, 1/2x, 1/sqrt(2)x, 1x, sqrt(2)x, 2x, 4x].
-
- \sa AeEnable
-
- - ExposureTime:
- type: int32_t
- description: |
- Exposure time (shutter speed) for the frame applied in the sensor
- device. This value is specified in micro-seconds.
-
- Setting this value means that it is now fixed and the AE algorithm may
- not change it. Setting it back to zero returns it to the control of the
- AE algorithm.
-
- \sa AnalogueGain AeEnable
-
- \todo Document the interactions between AeEnable and setting a fixed
- value for this control. Consider interactions with other AE features,
- such as aperture and aperture/shutter priority mode, and decide if
- control of which features should be automatically adjusted shouldn't
- better be handled through a separate AE mode control.
-
- - AnalogueGain:
- type: float
- description: |
- Analogue gain value applied in the sensor device.
- The value of the control specifies the gain multiplier applied to all
- colour channels. This value cannot be lower than 1.0.
-
- Setting this value means that it is now fixed and the AE algorithm may
- not change it. Setting it back to zero returns it to the control of the
- AE algorithm.
-
- \sa ExposureTime AeEnable
-
- \todo Document the interactions between AeEnable and setting a fixed
- value for this control. Consider interactions with other AE features,
- such as aperture and aperture/shutter priority mode, and decide if
- control of which features should be automatically adjusted shouldn't
- better be handled through a separate AE mode control.
-
- - AeFlickerMode:
- type: int32_t
- description: |
- Set the flicker mode, which determines whether, and how, the AGC/AEC
- algorithm attempts to hide flicker effects caused by the duty cycle of
- artificial lighting.
-
- Although implementation dependent, many algorithms for "flicker
- avoidance" work by restricting this exposure time to integer multiples
- of the cycle period, wherever possible.
-
- Implementations may not support all of the flicker modes listed below.
-
- By default the system will start in FlickerAuto mode if this is
- supported, otherwise the flicker mode will be set to FlickerOff.
-
- enum:
- - name: FlickerOff
- value: 0
- description: No flicker avoidance is performed.
- - name: FlickerManual
- value: 1
- description: Manual flicker avoidance.
- Suppress flicker effects caused by lighting running with a period
- specified by the AeFlickerPeriod control.
- \sa AeFlickerPeriod
- - name: FlickerAuto
- value: 2
- description: Automatic flicker period detection and avoidance.
- The system will automatically determine the most likely value of
- flicker period, and avoid flicker of this frequency. Once flicker
- is being corrected, it is implementation dependent whether the
- system is still able to detect a change in the flicker period.
- \sa AeFlickerDetected
-
- - AeFlickerPeriod:
- type: int32_t
- description: Manual flicker period in microseconds.
- This value sets the current flicker period to avoid. It is used when
- AeFlickerMode is set to FlickerManual.
-
- To cancel 50Hz mains flicker, this should be set to 10000 (corresponding
- to 100Hz), or 8333 (120Hz) for 60Hz mains.
-
- Setting the mode to FlickerManual when no AeFlickerPeriod has ever been
- set means that no flicker cancellation occurs (until the value of this
- control is updated).
-
- Switching to modes other than FlickerManual has no effect on the
- value of the AeFlickerPeriod control.
-
- \sa AeFlickerMode
-
- - AeFlickerDetected:
- type: int32_t
- description: Flicker period detected in microseconds.
- The value reported here indicates the currently detected flicker
- period, or zero if no flicker at all is detected.
-
- When AeFlickerMode is set to FlickerAuto, there may be a period during
- which the value reported here remains zero. Once a non-zero value is
- reported, then this is the flicker period that has been detected and is
- now being cancelled.
-
- In the case of 50Hz mains flicker, the value would be 10000
- (corresponding to 100Hz), or 8333 (120Hz) for 60Hz mains flicker.
-
- It is implementation dependent whether the system can continue to detect
- flicker of different periods when another frequency is already being
- cancelled.
-
- \sa AeFlickerMode
-
- - Brightness:
- type: float
- description: |
- Specify a fixed brightness parameter. Positive values (up to 1.0)
- produce brighter images; negative values (up to -1.0) produce darker
- images and 0.0 leaves pixels unchanged.
-
- - Contrast:
- type: float
- description: |
- Specify a fixed contrast parameter. Normal contrast is given by the
- value 1.0; larger values produce images with more contrast.
-
- - Lux:
- type: float
- description: |
- Report an estimate of the current illuminance level in lux. The Lux
- control can only be returned in metadata.
-
- - AwbEnable:
- type: bool
- description: |
- Enable or disable the AWB.
-
- \sa ColourGains
-
- # AwbMode needs further attention:
- # - Auto-generate max enum value.
- # - Better handling of custom types.
- - AwbMode:
- type: int32_t
- description: |
- Specify the range of illuminants to use for the AWB algorithm. The modes
- supported are platform specific, and not all modes may be supported.
- enum:
- - name: AwbAuto
- value: 0
- description: Search over the whole colour temperature range.
- - name: AwbIncandescent
- value: 1
- description: Incandescent AWB lamp mode.
- - name: AwbTungsten
- value: 2
- description: Tungsten AWB lamp mode.
- - name: AwbFluorescent
- value: 3
- description: Fluorescent AWB lamp mode.
- - name: AwbIndoor
- value: 4
- description: Indoor AWB lighting mode.
- - name: AwbDaylight
- value: 5
- description: Daylight AWB lighting mode.
- - name: AwbCloudy
- value: 6
- description: Cloudy AWB lighting mode.
- - name: AwbCustom
- value: 7
- description: Custom AWB mode.
-
- - AwbLocked:
- type: bool
- description: |
- Report the lock status of a running AWB algorithm.
-
- If the AWB algorithm is locked the value shall be set to true, if it's
- converging it shall be set to false. If the AWB algorithm is not
- running the control shall not be present in the metadata control list.
-
- \sa AwbEnable
-
- - ColourGains:
- type: float
- description: |
- Pair of gain values for the Red and Blue colour channels, in that
- order. ColourGains can only be applied in a Request when the AWB is
- disabled.
-
- \sa AwbEnable
- size: [2]
-
- - ColourTemperature:
- type: int32_t
- description: Report the current estimate of the colour temperature, in
- kelvin, for this frame. The ColourTemperature control can only be
- returned in metadata.
-
- - Saturation:
- type: float
- description: |
- Specify a fixed saturation parameter. Normal saturation is given by
- the value 1.0; larger values produce more saturated colours; 0.0
- produces a greyscale image.
-
- - SensorBlackLevels:
- type: int32_t
- description: |
- Reports the sensor black levels used for processing a frame, in the
- order R, Gr, Gb, B. These values are returned as numbers out of a 16-bit
- pixel range (as if pixels ranged from 0 to 65535). The SensorBlackLevels
- control can only be returned in metadata.
- size: [4]
-
- - Sharpness:
- type: float
- description: |
- A value of 0.0 means no sharpening. The minimum value means
- minimal sharpening, and shall be 0.0 unless the camera can't
- disable sharpening completely. The default value shall give a
- "reasonable" level of sharpening, suitable for most use cases.
- The maximum value may apply extremely high levels of sharpening,
- higher than anyone could reasonably want. Negative values are
- not allowed. Note also that sharpening is not applied to raw
- streams.
-
- - FocusFoM:
- type: int32_t
- description: |
- Reports a Figure of Merit (FoM) to indicate how in-focus the frame is.
- A larger FocusFoM value indicates a more in-focus frame. This singular
- value may be based on a combination of statistics gathered from
- multiple focus regions within an image. The number of focus regions and
- method of combination is platform dependent. In this respect, it is not
- necessarily aimed at providing a way to implement a focus algorithm by
- the application, rather an indication of how in-focus a frame is.
-
- - ColourCorrectionMatrix:
- type: float
- description: |
- The 3x3 matrix that converts camera RGB to sRGB within the
- imaging pipeline. This should describe the matrix that is used
- after pixels have been white-balanced, but before any gamma
- transformation. The 3x3 matrix is stored in conventional reading
- order in an array of 9 floating point values.
-
- size: [3,3]
-
- - ScalerCrop:
- type: Rectangle
- description: |
- Sets the image portion that will be scaled to form the whole of
- the final output image. The (x,y) location of this rectangle is
- relative to the PixelArrayActiveAreas that is being used. The units
- remain native sensor pixels, even if the sensor is being used in
- a binning or skipping mode.
-
- This control is only present when the pipeline supports scaling. Its
- maximum valid value is given by the properties::ScalerCropMaximum
- property, and the two can be used to implement digital zoom.
-
- - DigitalGain:
- type: float
- description: |
- Digital gain value applied during the processing steps applied
- to the image as captured from the sensor.
-
- The global digital gain factor is applied to all the colour channels
- of the RAW image. Different pipeline models are free to
- specify how the global gain factor applies to each separate
- channel.
-
- If an imaging pipeline applies digital gain in distinct
- processing steps, this value indicates their total sum.
- Pipelines are free to decide how to adjust each processing
- step to respect the received gain factor and shall report
- their total value in the request metadata.
-
- - FrameDuration:
- type: int64_t
- description: |
- The instantaneous frame duration from start of frame exposure to start
- of next exposure, expressed in microseconds. This control is meant to
- be returned in metadata.
-
- - FrameDurationLimits:
- type: int64_t
- description: |
- The minimum and maximum (in that order) frame duration, expressed in
- microseconds.
-
- When provided by applications, the control specifies the sensor frame
- duration interval the pipeline has to use. This limits the largest
- exposure time the sensor can use. For example, if a maximum frame
- duration of 33ms is requested (corresponding to 30 frames per second),
- the sensor will not be able to raise the exposure time above 33ms.
- A fixed frame duration is achieved by setting the minimum and maximum
- values to be the same. Setting both values to 0 reverts to using the
- camera defaults.
-
- The maximum frame duration provides the absolute limit to the shutter
- speed computed by the AE algorithm and it overrides any exposure mode
- setting specified with controls::AeExposureMode. Similarly, when a
- manual exposure time is set through controls::ExposureTime, it also
- gets clipped to the limits set by this control. When reported in
- metadata, the control expresses the minimum and maximum frame
- durations used after being clipped to the sensor provided frame
- duration limits.
-
- \sa AeExposureMode
- \sa ExposureTime
-
- \todo Define how to calculate the capture frame rate by
- defining controls to report additional delays introduced by
- the capture pipeline or post-processing stages (ie JPEG
- conversion, frame scaling).
-
- \todo Provide an explicit definition of default control values, for
- this and all other controls.
-
- size: [2]
-
- - SensorTemperature:
- type: float
- description: |
- Temperature measure from the camera sensor in Celsius. This is typically
- obtained by a thermal sensor present on-die or in the camera module. The
- range of reported temperatures is device dependent.
-
- The SensorTemperature control will only be returned in metadata if a
- themal sensor is present.
-
- - SensorTimestamp:
- type: int64_t
- description: |
- The time when the first row of the image sensor active array is exposed.
-
- The timestamp, expressed in nanoseconds, represents a monotonically
- increasing counter since the system boot time, as defined by the
- Linux-specific CLOCK_BOOTTIME clock id.
-
- The SensorTimestamp control can only be returned in metadata.
-
- \todo Define how the sensor timestamp has to be used in the reprocessing
- use case.
-
- - AfMode:
- type: int32_t
- description: |
- Control to set the mode of the AF (autofocus) algorithm.
-
- An implementation may choose not to implement all the modes.
-
- enum:
- - name: AfModeManual
- value: 0
- description: |
- The AF algorithm is in manual mode. In this mode it will never
- perform any action nor move the lens of its own accord, but an
- application can specify the desired lens position using the
- LensPosition control.
-
- In this mode the AfState will always report AfStateIdle.
-
- If the camera is started in AfModeManual, it will move the focus
- lens to the position specified by the LensPosition control.
-
- This mode is the recommended default value for the AfMode control.
- External cameras (as reported by the Location property set to
- CameraLocationExternal) may use a different default value.
- - name: AfModeAuto
- value: 1
- description: |
- The AF algorithm is in auto mode. This means that the algorithm
- will never move the lens or change state unless the AfTrigger
- control is used. The AfTrigger control can be used to initiate a
- focus scan, the results of which will be reported by AfState.
-
- If the autofocus algorithm is moved from AfModeAuto to another
- mode while a scan is in progress, the scan is cancelled
- immediately, without waiting for the scan to finish.
-
- When first entering this mode the AfState will report
- AfStateIdle. When a trigger control is sent, AfState will
- report AfStateScanning for a period before spontaneously
- changing to AfStateFocused or AfStateFailed, depending on
- the outcome of the scan. It will remain in this state until
- another scan is initiated by the AfTrigger control. If a scan is
- cancelled (without changing to another mode), AfState will return
- to AfStateIdle.
- - name: AfModeContinuous
- value: 2
- description: |
- The AF algorithm is in continuous mode. This means that the lens can
- re-start a scan spontaneously at any moment, without any user
- intervention. The AfState still reports whether the algorithm is
- currently scanning or not, though the application has no ability to
- initiate or cancel scans, nor to move the lens for itself.
-
- However, applications can pause the AF algorithm from continuously
- scanning by using the AfPause control. This allows video or still
- images to be captured whilst guaranteeing that the focus is fixed.
-
- When set to AfModeContinuous, the system will immediately initiate a
- scan so AfState will report AfStateScanning, and will settle on one
- of AfStateFocused or AfStateFailed, depending on the scan result.
-
- - AfRange:
- type: int32_t
- description: |
- Control to set the range of focus distances that is scanned. An
- implementation may choose not to implement all the options here.
- enum:
- - name: AfRangeNormal
- value: 0
- description: |
- A wide range of focus distances is scanned, all the way from
- infinity down to close distances, though depending on the
- implementation, possibly not including the very closest macro
- positions.
- - name: AfRangeMacro
- value: 1
- description: Only close distances are scanned.
- - name: AfRangeFull
- value: 2
- description: |
- The full range of focus distances is scanned just as with
- AfRangeNormal but this time including the very closest macro
- positions.
-
- - AfSpeed:
- type: int32_t
- description: |
- Control that determines whether the AF algorithm is to move the lens
- as quickly as possible or more steadily. For example, during video
- recording it may be desirable not to move the lens too abruptly, but
- when in a preview mode (waiting for a still capture) it may be
- helpful to move the lens as quickly as is reasonably possible.
- enum:
- - name: AfSpeedNormal
- value: 0
- description: Move the lens at its usual speed.
- - name: AfSpeedFast
- value: 1
- description: Move the lens more quickly.
-
- - AfMetering:
- type: int32_t
- description: |
- Instruct the AF algorithm how it should decide which parts of the image
- should be used to measure focus.
- enum:
- - name: AfMeteringAuto
- value: 0
- description: The AF algorithm should decide for itself where it will
- measure focus.
- - name: AfMeteringWindows
- value: 1
- description: The AF algorithm should use the rectangles defined by
- the AfWindows control to measure focus. If no windows are specified
- the behaviour is platform dependent.
-
- - AfWindows:
- type: Rectangle
- description: |
- Sets the focus windows used by the AF algorithm when AfMetering is set
- to AfMeteringWindows. The units used are pixels within the rectangle
- returned by the ScalerCropMaximum property.
-
- In order to be activated, a rectangle must be programmed with non-zero
- width and height. Internally, these rectangles are intersected with the
- ScalerCropMaximum rectangle. If the window becomes empty after this
- operation, then the window is ignored. If all the windows end up being
- ignored, then the behaviour is platform dependent.
-
- On platforms that support the ScalerCrop control (for implementing
- digital zoom, for example), no automatic recalculation or adjustment of
- AF windows is performed internally if the ScalerCrop is changed. If any
- window lies outside the output image after the scaler crop has been
- applied, it is up to the application to recalculate them.
-
- The details of how the windows are used are platform dependent. We note
- that when there is more than one AF window, a typical implementation
- might find the optimal focus position for each one and finally select
- the window where the focal distance for the objects shown in that part
- of the image are closest to the camera.
-
- size: [n]
-
- - AfTrigger:
- type: int32_t
- description: |
- This control starts an autofocus scan when AfMode is set to AfModeAuto,
- and can also be used to terminate a scan early.
-
- It is ignored if AfMode is set to AfModeManual or AfModeContinuous.
-
- enum:
- - name: AfTriggerStart
- value: 0
- description: Start an AF scan. Ignored if a scan is in progress.
- - name: AfTriggerCancel
- value: 1
- description: Cancel an AF scan. This does not cause the lens to move
- anywhere else. Ignored if no scan is in progress.
-
- - AfPause:
- type: int32_t
- description: |
- This control has no effect except when in continuous autofocus mode
- (AfModeContinuous). It can be used to pause any lens movements while
- (for example) images are captured. The algorithm remains inactive
- until it is instructed to resume.
-
- enum:
- - name: AfPauseImmediate
- value: 0
- description: |
- Pause the continuous autofocus algorithm immediately, whether or not
- any kind of scan is underway. AfPauseState will subsequently report
- AfPauseStatePaused. AfState may report any of AfStateScanning,
- AfStateFocused or AfStateFailed, depending on the algorithm's state
- when it received this control.
- - name: AfPauseDeferred
- value: 1
- description: |
- This is similar to AfPauseImmediate, and if the AfState is currently
- reporting AfStateFocused or AfStateFailed it will remain in that
- state and AfPauseState will report AfPauseStatePaused.
-
- However, if the algorithm is scanning (AfStateScanning),
- AfPauseState will report AfPauseStatePausing until the scan is
- finished, at which point AfState will report one of AfStateFocused
- or AfStateFailed, and AfPauseState will change to
- AfPauseStatePaused.
-
- - name: AfPauseResume
- value: 2
- description: |
- Resume continuous autofocus operation. The algorithm starts again
- from exactly where it left off, and AfPauseState will report
- AfPauseStateRunning.
-
- - LensPosition:
- type: float
- description: |
- Acts as a control to instruct the lens to move to a particular position
- and also reports back the position of the lens for each frame.
-
- The LensPosition control is ignored unless the AfMode is set to
- AfModeManual, though the value is reported back unconditionally in all
- modes.
-
- This value, which is generally a non-integer, is the reciprocal of the
- focal distance in metres, also known as dioptres. That is, to set a
- focal distance D, the lens position LP is given by
-
- \f$LP = \frac{1\mathrm{m}}{D}\f$
-
- For example:
-
- 0 moves the lens to infinity.
- 0.5 moves the lens to focus on objects 2m away.
- 2 moves the lens to focus on objects 50cm away.
- And larger values will focus the lens closer.
-
- The default value of the control should indicate a good general position
- for the lens, often corresponding to the hyperfocal distance (the
- closest position for which objects at infinity are still acceptably
- sharp). The minimum will often be zero (meaning infinity), and the
- maximum value defines the closest focus position.
-
- \todo Define a property to report the Hyperfocal distance of calibrated
- lenses.
-
- - AfState:
- type: int32_t
- description: |
- Reports the current state of the AF algorithm in conjunction with the
- reported AfMode value and (in continuous AF mode) the AfPauseState
- value. The possible state changes are described below, though we note
- the following state transitions that occur when the AfMode is changed.
-
- If the AfMode is set to AfModeManual, then the AfState will always
- report AfStateIdle (even if the lens is subsequently moved). Changing to
- the AfModeManual state does not initiate any lens movement.
-
- If the AfMode is set to AfModeAuto then the AfState will report
- AfStateIdle. However, if AfModeAuto and AfTriggerStart are sent together
- then AfState will omit AfStateIdle and move straight to AfStateScanning
- (and start a scan).
-
- If the AfMode is set to AfModeContinuous then the AfState will initially
- report AfStateScanning.
-
- enum:
- - name: AfStateIdle
- value: 0
- description: |
- The AF algorithm is in manual mode (AfModeManual) or in auto mode
- (AfModeAuto) and a scan has not yet been triggered, or an
- in-progress scan was cancelled.
- - name: AfStateScanning
- value: 1
- description: |
- The AF algorithm is in auto mode (AfModeAuto), and a scan has been
- started using the AfTrigger control. The scan can be cancelled by
- sending AfTriggerCancel at which point the algorithm will either
- move back to AfStateIdle or, if the scan actually completes before
- the cancel request is processed, to one of AfStateFocused or
- AfStateFailed.
-
- Alternatively the AF algorithm could be in continuous mode
- (AfModeContinuous) at which point it may enter this state
- spontaneously whenever it determines that a rescan is needed.
- - name: AfStateFocused
- value: 2
- description: |
- The AF algorithm is in auto (AfModeAuto) or continuous
- (AfModeContinuous) mode and a scan has completed with the result
- that the algorithm believes the image is now in focus.
- - name: AfStateFailed
- value: 3
- description: |
- The AF algorithm is in auto (AfModeAuto) or continuous
- (AfModeContinuous) mode and a scan has completed with the result
- that the algorithm did not find a good focus position.
-
- - AfPauseState:
- type: int32_t
- description: |
- Only applicable in continuous (AfModeContinuous) mode, this reports
- whether the algorithm is currently running, paused or pausing (that is,
- will pause as soon as any in-progress scan completes).
-
- Any change to AfMode will cause AfPauseStateRunning to be reported.
-
- enum:
- - name: AfPauseStateRunning
- value: 0
- description: |
- Continuous AF is running and the algorithm may restart a scan
- spontaneously.
- - name: AfPauseStatePausing
- value: 1
- description: |
- Continuous AF has been sent an AfPauseDeferred control, and will
- pause as soon as any in-progress scan completes (and then report
- AfPauseStatePaused). No new scans will be start spontaneously until
- the AfPauseResume control is sent.
- - name: AfPauseStatePaused
- value: 2
- description: |
- Continuous AF is paused. No further state changes or lens movements
- will occur until the AfPauseResume control is sent.
-
- # ----------------------------------------------------------------------------
- # Draft controls section
-
- - AePrecaptureTrigger:
- type: int32_t
- draft: true
- description: |
- Control for AE metering trigger. Currently identical to
- ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER.
-
- Whether the camera device will trigger a precapture metering sequence
- when it processes this request.
- enum:
- - name: AePrecaptureTriggerIdle
- value: 0
- description: The trigger is idle.
- - name: AePrecaptureTriggerStart
- value: 1
- description: The pre-capture AE metering is started by the camera.
- - name: AePrecaptureTriggerCancel
- value: 2
- description: |
- The camera will cancel any active or completed metering sequence.
- The AE algorithm is reset to its initial state.
-
- - NoiseReductionMode:
- type: int32_t
- draft: true
- description: |
- Control to select the noise reduction algorithm mode. Currently
- identical to ANDROID_NOISE_REDUCTION_MODE.
-
- Mode of operation for the noise reduction algorithm.
- enum:
- - name: NoiseReductionModeOff
- value: 0
- description: No noise reduction is applied
- - name: NoiseReductionModeFast
- value: 1
- description: |
- Noise reduction is applied without reducing the frame rate.
- - name: NoiseReductionModeHighQuality
- value: 2
- description: |
- High quality noise reduction at the expense of frame rate.
- - name: NoiseReductionModeMinimal
- value: 3
- description: |
- Minimal noise reduction is applied without reducing the frame rate.
- - name: NoiseReductionModeZSL
- value: 4
- description: |
- Noise reduction is applied at different levels to different streams.
-
- - ColorCorrectionAberrationMode:
- type: int32_t
- draft: true
- description: |
- Control to select the color correction aberration mode. Currently
- identical to ANDROID_COLOR_CORRECTION_ABERRATION_MODE.
-
- Mode of operation for the chromatic aberration correction algorithm.
- enum:
- - name: ColorCorrectionAberrationOff
- value: 0
- description: No aberration correction is applied.
- - name: ColorCorrectionAberrationFast
- value: 1
- description: Aberration correction will not slow down the frame rate.
- - name: ColorCorrectionAberrationHighQuality
- value: 2
- description: |
- High quality aberration correction which might reduce the frame
- rate.
-
- - AeState:
- type: int32_t
- draft: true
- description: |
- Control to report the current AE algorithm state. Currently identical to
- ANDROID_CONTROL_AE_STATE.
-
- Current state of the AE algorithm.
- enum:
- - name: AeStateInactive
- value: 0
- description: The AE algorithm is inactive.
- - name: AeStateSearching
- value: 1
- description: The AE algorithm has not converged yet.
- - name: AeStateConverged
- value: 2
- description: The AE algorithm has converged.
- - name: AeStateLocked
- value: 3
- description: The AE algorithm is locked.
- - name: AeStateFlashRequired
- value: 4
- description: The AE algorithm would need a flash for good results
- - name: AeStatePrecapture
- value: 5
- description: |
- The AE algorithm has started a pre-capture metering session.
- \sa AePrecaptureTrigger
-
- - AwbState:
- type: int32_t
- draft: true
- description: |
- Control to report the current AWB algorithm state. Currently identical
- to ANDROID_CONTROL_AWB_STATE.
-
- Current state of the AWB algorithm.
- enum:
- - name: AwbStateInactive
- value: 0
- description: The AWB algorithm is inactive.
- - name: AwbStateSearching
- value: 1
- description: The AWB algorithm has not converged yet.
- - name: AwbConverged
- value: 2
- description: The AWB algorithm has converged.
- - name: AwbLocked
- value: 3
- description: The AWB algorithm is locked.
-
- - SensorRollingShutterSkew:
- type: int64_t
- draft: true
- description: |
- Control to report the time between the start of exposure of the first
- row and the start of exposure of the last row. Currently identical to
- ANDROID_SENSOR_ROLLING_SHUTTER_SKEW
-
- - LensShadingMapMode:
- type: int32_t
- draft: true
- description: |
- Control to report if the lens shading map is available. Currently
- identical to ANDROID_STATISTICS_LENS_SHADING_MAP_MODE.
- enum:
- - name: LensShadingMapModeOff
- value: 0
- description: No lens shading map mode is available.
- - name: LensShadingMapModeOn
- value: 1
- description: The lens shading map mode is available.
-
- - PipelineDepth:
- type: int32_t
- draft: true
- description: |
- Specifies the number of pipeline stages the frame went through from when
- it was exposed to when the final completed result was available to the
- framework. Always less than or equal to PipelineMaxDepth. Currently
- identical to ANDROID_REQUEST_PIPELINE_DEPTH.
-
- The typical value for this control is 3 as a frame is first exposed,
- captured and then processed in a single pass through the ISP. Any
- additional processing step performed after the ISP pass (in example face
- detection, additional format conversions etc) count as an additional
- pipeline stage.
-
- - MaxLatency:
- type: int32_t
- draft: true
- description: |
- The maximum number of frames that can occur after a request (different
- than the previous) has been submitted, and before the result's state
- becomes synchronized. A value of -1 indicates unknown latency, and 0
- indicates per-frame control. Currently identical to
- ANDROID_SYNC_MAX_LATENCY.
-
- - TestPatternMode:
- type: int32_t
- draft: true
- description: |
- Control to select the test pattern mode. Currently identical to
- ANDROID_SENSOR_TEST_PATTERN_MODE.
- enum:
- - name: TestPatternModeOff
- value: 0
- description: |
- No test pattern mode is used. The camera device returns frames from
- the image sensor.
- - name: TestPatternModeSolidColor
- value: 1
- description: |
- Each pixel in [R, G_even, G_odd, B] is replaced by its respective
- color channel provided in test pattern data.
- \todo Add control for test pattern data.
- - name: TestPatternModeColorBars
- value: 2
- description: |
- All pixel data is replaced with an 8-bar color pattern. The vertical
- bars (left-to-right) are as follows; white, yellow, cyan, green,
- magenta, red, blue and black. Each bar should take up 1/8 of the
- sensor pixel array width. When this is not possible, the bar size
- should be rounded down to the nearest integer and the pattern can
- repeat on the right side. Each bar's height must always take up the
- full sensor pixel array height.
- - name: TestPatternModeColorBarsFadeToGray
- value: 3
- description: |
- The test pattern is similar to TestPatternModeColorBars,
- except that each bar should start at its specified color at the top
- and fade to gray at the bottom. Furthermore each bar is further
- subdevided into a left and right half. The left half should have a
- smooth gradient, and the right half should have a quantized
- gradient. In particular, the right half's should consist of blocks
- of the same color for 1/16th active sensor pixel array width. The
- least significant bits in the quantized gradient should be copied
- from the most significant bits of the smooth gradient. The height of
- each bar should always be a multiple of 128. When this is not the
- case, the pattern should repeat at the bottom of the image.
- - name: TestPatternModePn9
- value: 4
- description: |
- All pixel data is replaced by a pseudo-random sequence generated
- from a PN9 512-bit sequence (typically implemented in hardware with
- a linear feedback shift register). The generator should be reset at
- the beginning of each frame, and thus each subsequent raw frame with
- this test pattern should be exactly the same as the last.
- - name: TestPatternModeCustom1
- value: 256
- description: |
- The first custom test pattern. All custom patterns that are
- available only on this camera device are at least this numeric
- value. All of the custom test patterns will be static (that is the
- raw image must not vary from frame to frame).
-
-...
diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml
new file mode 100644
index 00000000..aa744864
--- /dev/null
+++ b/src/libcamera/control_ids_core.yaml
@@ -0,0 +1,1271 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2019, Google Inc.
+#
+%YAML 1.1
+---
+# Unless otherwise stated, all controls are bi-directional, i.e. they can be
+# set through Request::controls() and returned out through Request::metadata().
+vendor: libcamera
+controls:
+ - AeEnable:
+ type: bool
+ direction: in
+ description: |
+ Enable or disable the AEGC algorithm. When this control is set to true,
+ both ExposureTimeMode and AnalogueGainMode are set to auto, and if this
+ control is set to false then both are set to manual.
+
+ If ExposureTimeMode or AnalogueGainMode are also set in the same
+ request as AeEnable, then the modes supplied by ExposureTimeMode or
+ AnalogueGainMode will take precedence.
+
+ \sa ExposureTimeMode AnalogueGainMode
+
+ - AeState:
+ type: int32_t
+ direction: out
+ description: |
+ Report the AEGC algorithm state.
+
+ The AEGC algorithm computes the exposure time and the analogue gain
+ to be applied to the image sensor.
+
+ The AEGC algorithm behaviour is controlled by the ExposureTimeMode and
+ AnalogueGainMode controls, which allow applications to decide how
+ the exposure time and gain are computed, in Auto or Manual mode,
+ independently from one another.
+
+ The AeState control reports the AEGC algorithm state through a single
+ value and describes it as a single computation block which computes
+ both the exposure time and the analogue gain values.
+
+ When both the exposure time and analogue gain values are configured to
+ be in Manual mode, the AEGC algorithm is quiescent and does not actively
+ compute any value and the AeState control will report AeStateIdle.
+
+ When at least the exposure time or analogue gain are configured to be
+ computed by the AEGC algorithm, the AeState control will report if the
+ algorithm has converged to stable values for all of the controls set
+ to be computed in Auto mode.
+
+ \sa AnalogueGainMode
+ \sa ExposureTimeMode
+
+ enum:
+ - name: AeStateIdle
+ value: 0
+ description: |
+ The AEGC algorithm is inactive.
+
+ This state is returned when both AnalogueGainMode and
+ ExposureTimeMode are set to Manual and the algorithm is not
+ actively computing any value.
+ - name: AeStateSearching
+ value: 1
+ description: |
+ The AEGC algorithm is actively computing new values, for either the
+ exposure time or the analogue gain, but has not converged to a
+ stable result yet.
+
+ This state is returned if at least one of AnalogueGainMode or
+ ExposureTimeMode is auto and the algorithm hasn't converged yet.
+
+ The AEGC algorithm converges once stable values are computed for
+ all of the controls set to be computed in Auto mode. Once the
+ algorithm converges the state is moved to AeStateConverged.
+ - name: AeStateConverged
+ value: 2
+ description: |
+ The AEGC algorithm has converged.
+
+ This state is returned if at least one of AnalogueGainMode or
+ ExposureTimeMode is Auto, and the AEGC algorithm has converged to a
+ stable value.
+
+ If the measurements move too far away from the convergence point
+ then the AEGC algorithm might start adjusting again, in which case
+ the state is moved to AeStateSearching.
+
+ # AeMeteringMode needs further attention:
+ # - Auto-generate max enum value.
+ # - Better handling of custom types.
+ - AeMeteringMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Specify a metering mode for the AE algorithm to use.
+
+ The metering modes determine which parts of the image are used to
+ determine the scene brightness. Metering modes may be platform specific
+ and not all metering modes may be supported.
+ enum:
+ - name: MeteringCentreWeighted
+ value: 0
+ description: Centre-weighted metering mode.
+ - name: MeteringSpot
+ value: 1
+ description: Spot metering mode.
+ - name: MeteringMatrix
+ value: 2
+ description: Matrix metering mode.
+ - name: MeteringCustom
+ value: 3
+ description: Custom metering mode.
+
+ # AeConstraintMode needs further attention:
+ # - Auto-generate max enum value.
+ # - Better handling of custom types.
+ - AeConstraintMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Specify a constraint mode for the AE algorithm to use.
+
+ The constraint modes determine how the measured scene brightness is
+ adjusted to reach the desired target exposure. Constraint modes may be
+ platform specific, and not all constraint modes may be supported.
+ enum:
+ - name: ConstraintNormal
+ value: 0
+ description: |
+ Default constraint mode.
+
+ This mode aims to balance the exposure of different parts of the
+ image so as to reach a reasonable average level. However, highlights
+ in the image may appear over-exposed and lowlights may appear
+ under-exposed.
+ - name: ConstraintHighlight
+ value: 1
+ description: |
+ Highlight constraint mode.
+
+ This mode adjusts the exposure levels in order to try and avoid
+ over-exposing the brightest parts (highlights) of an image.
+ Other non-highlight parts of the image may appear under-exposed.
+ - name: ConstraintShadows
+ value: 2
+ description: |
+ Shadows constraint mode.
+
+ This mode adjusts the exposure levels in order to try and avoid
+ under-exposing the dark parts (shadows) of an image. Other normally
+ exposed parts of the image may appear over-exposed.
+ - name: ConstraintCustom
+ value: 3
+ description: |
+ Custom constraint mode.
+
+ # AeExposureMode needs further attention:
+ # - Auto-generate max enum value.
+ # - Better handling of custom types.
+ - AeExposureMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Specify an exposure mode for the AE algorithm to use.
+
+ The exposure modes specify how the desired total exposure is divided
+ between the exposure time and the sensor's analogue gain. They are
+ platform specific, and not all exposure modes may be supported.
+
+ When one of AnalogueGainMode or ExposureTimeMode is set to Manual,
+ the fixed values will override any choices made by AeExposureMode.
+
+ \sa AnalogueGainMode
+ \sa ExposureTimeMode
+
+ enum:
+ - name: ExposureNormal
+ value: 0
+ description: Default exposure mode.
+ - name: ExposureShort
+ value: 1
+ description: Exposure mode allowing only short exposure times.
+ - name: ExposureLong
+ value: 2
+ description: Exposure mode allowing long exposure times.
+ - name: ExposureCustom
+ value: 3
+ description: Custom exposure mode.
+
+ - ExposureValue:
+ type: float
+ direction: inout
+ description: |
+ Specify an Exposure Value (EV) parameter.
+
+ The EV parameter will only be applied if the AE algorithm is currently
+ enabled, that is, at least one of AnalogueGainMode and ExposureTimeMode
+ are in Auto mode.
+
+ By convention EV adjusts the exposure as log2. For example
+ EV = [-2, -1, -0.5, 0, 0.5, 1, 2] results in an exposure adjustment
+ of [1/4x, 1/2x, 1/sqrt(2)x, 1x, sqrt(2)x, 2x, 4x].
+
+ \sa AnalogueGainMode
+ \sa ExposureTimeMode
+
+ - ExposureTime:
+ type: int32_t
+ direction: inout
+ description: |
+ Exposure time for the frame applied in the sensor device.
+
+ This value is specified in micro-seconds.
+
+ This control will only take effect if ExposureTimeMode is Manual. If
+ this control is set when ExposureTimeMode is Auto, the value will be
+ ignored and will not be retained.
+
+ When reported in metadata, this control indicates what exposure time
+ was used for the current frame, regardless of ExposureTimeMode.
+ ExposureTimeMode will indicate the source of the exposure time value,
+ whether it came from the AE algorithm or not.
+
+ \sa AnalogueGain
+ \sa ExposureTimeMode
+
+ - ExposureTimeMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Controls the source of the exposure time that is applied to the image
+ sensor.
+
+ When set to Auto, the AE algorithm computes the exposure time and
+ configures the image sensor accordingly. When set to Manual, the value
+ of the ExposureTime control is used.
+
+ When transitioning from Auto to Manual mode and no ExposureTime control
+ is provided by the application, the last value computed by the AE
+ algorithm when the mode was Auto will be used. If the ExposureTimeMode
+ was never set to Auto (either because the camera started in Manual mode,
+ or Auto is not supported by the camera), the camera should use a
+ best-effort default value.
+
+ If ExposureTimeModeManual is supported, the ExposureTime control must
+ also be supported.
+
+ Cameras that support manual control of the sensor shall support manual
+ mode for both ExposureTimeMode and AnalogueGainMode, and shall expose
+ the ExposureTime and AnalogueGain controls. If the camera also has an
+ AEGC implementation, both ExposureTimeMode and AnalogueGainMode shall
+ support both manual and auto mode. If auto mode is available, it shall
+ be the default mode. These rules do not apply to black box cameras
+ such as UVC cameras, where the available gain and exposure modes are
+ completely dependent on what the device exposes.
+
+ \par Flickerless exposure mode transitions
+
+ Applications that wish to transition from ExposureTimeModeAuto to direct
+ control of the exposure time without causing extra flicker can do so by
+ selecting an ExposureTime value as close as possible to the last value
+ computed by the auto exposure algorithm in order to avoid any visible
+ flickering.
+
+ To select the correct value to use as ExposureTime value, applications
+ should accommodate the natural delay in applying controls caused by the
+ capture pipeline frame depth.
+
+ When switching to manual exposure mode, applications should not
+ immediately specify an ExposureTime value in the same request where
+ ExposureTimeMode is set to Manual. They should instead wait for the
+ first Request where ExposureTimeMode is reported as
+ ExposureTimeModeManual in the Request metadata, and use the reported
+ ExposureTime to populate the control value in the next Request to be
+ queued to the Camera.
+
+ The implementation of the auto-exposure algorithm should equally try to
+ minimize flickering and when transitioning from manual exposure mode to
+ auto exposure use the last value provided by the application as starting
+ point.
+
+ 1. Start with ExposureTimeMode set to Auto
+
+ 2. Set ExposureTimeMode to Manual
+
+ 3. Wait for the first completed request that has ExposureTimeMode
+ set to Manual
+
+ 4. Copy the value reported in ExposureTime into a new request, and
+ submit it
+
+ 5. Proceed to run manual exposure time as desired
+
+ \sa ExposureTime
+ enum:
+ - name: ExposureTimeModeAuto
+ value: 0
+ description: |
+ The exposure time will be calculated automatically and set by the
+ AE algorithm.
+
+ If ExposureTime is set while this mode is active, it will be
+ ignored, and its value will not be retained.
+
+ When transitioning from Manual to Auto mode, the AEGC should start
+ its adjustments based on the last set manual ExposureTime value.
+ - name: ExposureTimeModeManual
+ value: 1
+ description: |
+ The exposure time will not be updated by the AE algorithm.
+
+ When transitioning from Auto to Manual mode, the last computed
+ exposure value is used until a new value is specified through the
+ ExposureTime control. If an ExposureTime value is specified in the
+ same request where the ExposureTimeMode is changed from Auto to
+ Manual, the provided ExposureTime is applied immediately.
+
+ - AnalogueGain:
+ type: float
+ direction: inout
+ description: |
+ Analogue gain value applied in the sensor device.
+
+ The value of the control specifies the gain multiplier applied to all
+ colour channels. This value cannot be lower than 1.0.
+
+ This control will only take effect if AnalogueGainMode is Manual. If
+ this control is set when AnalogueGainMode is Auto, the value will be
+ ignored and will not be retained.
+
+ When reported in metadata, this control indicates what analogue gain
+ was used for the current request, regardless of AnalogueGainMode.
+ AnalogueGainMode will indicate the source of the analogue gain value,
+ whether it came from the AEGC algorithm or not.
+
+ \sa ExposureTime
+ \sa AnalogueGainMode
+
+ - AnalogueGainMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Controls the source of the analogue gain that is applied to the image
+ sensor.
+
+ When set to Auto, the AEGC algorithm computes the analogue gain and
+ configures the image sensor accordingly. When set to Manual, the value
+ of the AnalogueGain control is used.
+
+ When transitioning from Auto to Manual mode and no AnalogueGain control
+ is provided by the application, the last value computed by the AEGC
+ algorithm when the mode was Auto will be used. If the AnalogueGainMode
+ was never set to Auto (either because the camera started in Manual mode,
+ or Auto is not supported by the camera), the camera should use a
+ best-effort default value.
+
+ If AnalogueGainModeManual is supported, the AnalogueGain control must
+ also be supported.
+
+ For cameras where we have control over the ISP, both ExposureTimeMode
+ and AnalogueGainMode are expected to support manual mode, and both
+ controls (as well as ExposureTimeMode and AnalogueGain) are expected to
+ be present. If the camera also has an AEGC implementation, both
+ ExposureTimeMode and AnalogueGainMode shall support both manual and
+ auto mode. If auto mode is available, it shall be the default mode.
+ These rules do not apply to black box cameras such as UVC cameras,
+ where the available gain and exposure modes are completely dependent on
+ what the hardware exposes.
+
+ The same procedure described for performing flickerless transitions in
+ the ExposureTimeMode control documentation can be applied to analogue
+ gain.
+
+ \sa ExposureTimeMode
+ \sa AnalogueGain
+ enum:
+ - name: AnalogueGainModeAuto
+ value: 0
+ description: |
+ The analogue gain will be calculated automatically and set by the
+ AEGC algorithm.
+
+ If AnalogueGain is set while this mode is active, it will be
+ ignored, and it will also not be retained.
+
+ When transitioning from Manual to Auto mode, the AEGC should start
+ its adjustments based on the last set manual AnalogueGain value.
+ - name: AnalogueGainModeManual
+ value: 1
+ description: |
+ The analogue gain will not be updated by the AEGC algorithm.
+
+ When transitioning from Auto to Manual mode, the last computed
+ gain value is used until a new value is specified through the
+ AnalogueGain control. If an AnalogueGain value is specified in the
+ same request where the AnalogueGainMode is changed from Auto to
+ Manual, the provided AnalogueGain is applied immediately.
+
+ - AeFlickerMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Set the flicker avoidance mode for AGC/AEC.
+
+ The flicker mode determines whether, and how, the AGC/AEC algorithm
+ attempts to hide flicker effects caused by the duty cycle of artificial
+ lighting.
+
+ Although implementation dependent, many algorithms for "flicker
+ avoidance" work by restricting this exposure time to integer multiples
+ of the cycle period, wherever possible.
+
+ Implementations may not support all of the flicker modes listed below.
+
+ By default the system will start in FlickerAuto mode if this is
+ supported, otherwise the flicker mode will be set to FlickerOff.
+
+ enum:
+ - name: FlickerOff
+ value: 0
+ description: |
+ No flicker avoidance is performed.
+ - name: FlickerManual
+ value: 1
+ description: |
+ Manual flicker avoidance.
+
+ Suppress flicker effects caused by lighting running with a period
+ specified by the AeFlickerPeriod control.
+ \sa AeFlickerPeriod
+ - name: FlickerAuto
+ value: 2
+ description: |
+ Automatic flicker period detection and avoidance.
+
+ The system will automatically determine the most likely value of
+ flicker period, and avoid flicker of this frequency. Once flicker
+ is being corrected, it is implementation dependent whether the
+ system is still able to detect a change in the flicker period.
+ \sa AeFlickerDetected
+
+ - AeFlickerPeriod:
+ type: int32_t
+ direction: inout
+ description: |
+ Manual flicker period in microseconds.
+
+ This value sets the current flicker period to avoid. It is used when
+ AeFlickerMode is set to FlickerManual.
+
+ To cancel 50Hz mains flicker, this should be set to 10000 (corresponding
+ to 100Hz), or 8333 (120Hz) for 60Hz mains.
+
+ Setting the mode to FlickerManual when no AeFlickerPeriod has ever been
+ set means that no flicker cancellation occurs (until the value of this
+ control is updated).
+
+ Switching to modes other than FlickerManual has no effect on the
+ value of the AeFlickerPeriod control.
+
+ \sa AeFlickerMode
+
+ - AeFlickerDetected:
+ type: int32_t
+ direction: out
+ description: |
+ Flicker period detected in microseconds.
+
+ The value reported here indicates the currently detected flicker
+ period, or zero if no flicker at all is detected.
+
+ When AeFlickerMode is set to FlickerAuto, there may be a period during
+ which the value reported here remains zero. Once a non-zero value is
+ reported, then this is the flicker period that has been detected and is
+ now being cancelled.
+
+ In the case of 50Hz mains flicker, the value would be 10000
+ (corresponding to 100Hz), or 8333 (120Hz) for 60Hz mains flicker.
+
+ It is implementation dependent whether the system can continue to detect
+ flicker of different periods when another frequency is already being
+ cancelled.
+
+ \sa AeFlickerMode
+
+ - Brightness:
+ type: float
+ direction: inout
+ description: |
+ Specify a fixed brightness parameter.
+
+ Positive values (up to 1.0) produce brighter images; negative values
+ (up to -1.0) produce darker images and 0.0 leaves pixels unchanged.
+
+ - Contrast:
+ type: float
+ direction: inout
+ description: |
+ Specify a fixed contrast parameter.
+
+ Normal contrast is given by the value 1.0; larger values produce images
+ with more contrast.
+
+ - Lux:
+ type: float
+ direction: out
+ description: |
+ Report an estimate of the current illuminance level in lux.
+
+ The Lux control can only be returned in metadata.
+
+ - AwbEnable:
+ type: bool
+ direction: inout
+ description: |
+ Enable or disable the AWB.
+
+ When AWB is enabled, the algorithm estimates the colour temperature of
+ the scene and computes colour gains and the colour correction matrix
+ automatically. The computed colour temperature, gains and correction
+ matrix are reported in metadata. The corresponding controls are ignored
+ if set in a request.
+
+ When AWB is disabled, the colour temperature, gains and correction
+ matrix are not updated automatically and can be set manually in
+ requests.
+
+ \sa ColourCorrectionMatrix
+ \sa ColourGains
+ \sa ColourTemperature
+
+ # AwbMode needs further attention:
+ # - Auto-generate max enum value.
+ # - Better handling of custom types.
+ - AwbMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Specify the range of illuminants to use for the AWB algorithm.
+
+ The modes supported are platform specific, and not all modes may be
+ supported.
+ enum:
+ - name: AwbAuto
+ value: 0
+ description: Search over the whole colour temperature range.
+ - name: AwbIncandescent
+ value: 1
+ description: Incandescent AWB lamp mode.
+ - name: AwbTungsten
+ value: 2
+ description: Tungsten AWB lamp mode.
+ - name: AwbFluorescent
+ value: 3
+ description: Fluorescent AWB lamp mode.
+ - name: AwbIndoor
+ value: 4
+ description: Indoor AWB lighting mode.
+ - name: AwbDaylight
+ value: 5
+ description: Daylight AWB lighting mode.
+ - name: AwbCloudy
+ value: 6
+ description: Cloudy AWB lighting mode.
+ - name: AwbCustom
+ value: 7
+ description: Custom AWB mode.
+
+ - AwbLocked:
+ type: bool
+ direction: out
+ description: |
+ Report the lock status of a running AWB algorithm.
+
+ If the AWB algorithm is locked the value shall be set to true, if it's
+ converging it shall be set to false. If the AWB algorithm is not
+ running the control shall not be present in the metadata control list.
+
+ \sa AwbEnable
+
+ - ColourGains:
+ type: float
+ direction: inout
+ description: |
+ Pair of gain values for the Red and Blue colour channels, in that
+ order.
+
+ ColourGains can only be applied in a Request when the AWB is disabled.
+ If ColourGains is set in a request but ColourTemperature is not, the
+ implementation shall calculate and set the ColourTemperature based on
+ the ColourGains.
+
+ \sa AwbEnable
+ \sa ColourTemperature
+ size: [2]
+
+ - ColourTemperature:
+ type: int32_t
+ direction: out
+ description: |
+ ColourTemperature of the frame, in kelvin.
+
+ ColourTemperature can only be applied in a Request when the AWB is
+ disabled.
+
+ If ColourTemperature is set in a request but ColourGains is not, the
+ implementation shall calculate and set the ColourGains based on the
+ given ColourTemperature. If ColourTemperature is set (either directly,
+ or indirectly by setting ColourGains) but ColourCorrectionMatrix is not,
+ the ColourCorrectionMatrix is updated based on the ColourTemperature.
+
+ The ColourTemperature used to process the frame is reported in metadata.
+
+ \sa AwbEnable
+ \sa ColourCorrectionMatrix
+ \sa ColourGains
+
+ - Saturation:
+ type: float
+ direction: inout
+ description: |
+ Specify a fixed saturation parameter.
+
+ Normal saturation is given by the value 1.0; larger values produce more
+ saturated colours; 0.0 produces a greyscale image.
+
+ - SensorBlackLevels:
+ type: int32_t
+ direction: out
+ description: |
+ Reports the sensor black levels used for processing a frame.
+
+ The values are in the order R, Gr, Gb, B. They are returned as numbers
+ out of a 16-bit pixel range (as if pixels ranged from 0 to 65535). The
+ SensorBlackLevels control can only be returned in metadata.
+ size: [4]
+
+ - Sharpness:
+ type: float
+ direction: inout
+ description: |
+ Intensity of the sharpening applied to the image.
+
+ A value of 0.0 means no sharpening. The minimum value means
+ minimal sharpening, and shall be 0.0 unless the camera can't
+ disable sharpening completely. The default value shall give a
+ "reasonable" level of sharpening, suitable for most use cases.
+ The maximum value may apply extremely high levels of sharpening,
+ higher than anyone could reasonably want. Negative values are
+ not allowed. Note also that sharpening is not applied to raw
+ streams.
+
+ - FocusFoM:
+ type: int32_t
+ direction: out
+ description: |
+ Reports a Figure of Merit (FoM) to indicate how in-focus the frame is.
+
+ A larger FocusFoM value indicates a more in-focus frame. This singular
+ value may be based on a combination of statistics gathered from
+ multiple focus regions within an image. The number of focus regions and
+ method of combination is platform dependent. In this respect, it is not
+ necessarily aimed at providing a way to implement a focus algorithm by
+ the application, rather an indication of how in-focus a frame is.
+
+ - ColourCorrectionMatrix:
+ type: float
+ direction: inout
+ description: |
+ The 3x3 matrix that converts camera RGB to sRGB within the imaging
+ pipeline.
+
+ This should describe the matrix that is used after pixels have been
+ white-balanced, but before any gamma transformation. The 3x3 matrix is
+ stored in conventional reading order in an array of 9 floating point
+ values.
+
+ ColourCorrectionMatrix can only be applied in a Request when the AWB is
+ disabled.
+
+ \sa AwbEnable
+ \sa ColourTemperature
+ size: [3,3]
+
+ - ScalerCrop:
+ type: Rectangle
+ direction: inout
+ description: |
+ Sets the image portion that will be scaled to form the whole of
+ the final output image.
+
+ The (x,y) location of this rectangle is relative to the
+ PixelArrayActiveAreas that is being used. The units remain native
+ sensor pixels, even if the sensor is being used in a binning or
+ skipping mode.
+
+ This control is only present when the pipeline supports scaling. Its
+ maximum valid value is given by the properties::ScalerCropMaximum
+ property, and the two can be used to implement digital zoom.
+
+ - DigitalGain:
+ type: float
+ direction: inout
+ description: |
+ Digital gain value applied during the processing steps applied
+ to the image as captured from the sensor.
+
+ The global digital gain factor is applied to all the colour channels
+ of the RAW image. Different pipeline models are free to
+ specify how the global gain factor applies to each separate
+ channel.
+
+ If an imaging pipeline applies digital gain in distinct
+ processing steps, this value indicates their total sum.
+ Pipelines are free to decide how to adjust each processing
+ step to respect the received gain factor and shall report
+ their total value in the request metadata.
+
+ - FrameDuration:
+ type: int64_t
+ direction: out
+ description: |
+ The instantaneous frame duration from start of frame exposure to start
+ of next exposure, expressed in microseconds.
+
+ This control is meant to be returned in metadata.
+
+ - FrameDurationLimits:
+ type: int64_t
+ direction: inout
+ description: |
+ The minimum and maximum (in that order) frame duration, expressed in
+ microseconds.
+
+ When provided by applications, the control specifies the sensor frame
+ duration interval the pipeline has to use. This limits the largest
+ exposure time the sensor can use. For example, if a maximum frame
+ duration of 33ms is requested (corresponding to 30 frames per second),
+ the sensor will not be able to raise the exposure time above 33ms.
+ A fixed frame duration is achieved by setting the minimum and maximum
+ values to be the same. Setting both values to 0 reverts to using the
+ camera defaults.
+
+ The maximum frame duration provides the absolute limit to the exposure
+ time computed by the AE algorithm and it overrides any exposure mode
+ setting specified with controls::AeExposureMode. Similarly, when a
+ manual exposure time is set through controls::ExposureTime, it also
+ gets clipped to the limits set by this control. When reported in
+ metadata, the control expresses the minimum and maximum frame durations
+ used after being clipped to the sensor provided frame duration limits.
+
+ \sa AeExposureMode
+ \sa ExposureTime
+
+ \todo Define how to calculate the capture frame rate by
+ defining controls to report additional delays introduced by
+ the capture pipeline or post-processing stages (ie JPEG
+ conversion, frame scaling).
+
+ \todo Provide an explicit definition of default control values, for
+ this and all other controls.
+
+ size: [2]
+
+ - SensorTemperature:
+ type: float
+ direction: out
+ description: |
+ Temperature measure from the camera sensor in Celsius.
+
+ This value is typically obtained by a thermal sensor present on-die or
+ in the camera module. The range of reported temperatures is device
+ dependent.
+
+ The SensorTemperature control will only be returned in metadata if a
+ thermal sensor is present.
+
+ - SensorTimestamp:
+ type: int64_t
+ direction: out
+ description: |
+ The time when the first row of the image sensor active array is exposed.
+
+ The timestamp, expressed in nanoseconds, represents a monotonically
+ increasing counter since the system boot time, as defined by the
+ Linux-specific CLOCK_BOOTTIME clock id.
+
+ The SensorTimestamp control can only be returned in metadata.
+
+ \todo Define how the sensor timestamp has to be used in the reprocessing
+ use case.
+
+ - AfMode:
+ type: int32_t
+ direction: inout
+ description: |
+ The mode of the AF (autofocus) algorithm.
+
+ An implementation may choose not to implement all the modes.
+
+ enum:
+ - name: AfModeManual
+ value: 0
+ description: |
+ The AF algorithm is in manual mode.
+
+ In this mode it will never perform any action nor move the lens of
+ its own accord, but an application can specify the desired lens
+ position using the LensPosition control. The AfState will always
+ report AfStateIdle.
+
+ If the camera is started in AfModeManual, it will move the focus
+ lens to the position specified by the LensPosition control.
+
+ This mode is the recommended default value for the AfMode control.
+ External cameras (as reported by the Location property set to
+ CameraLocationExternal) may use a different default value.
+ - name: AfModeAuto
+ value: 1
+ description: |
+ The AF algorithm is in auto mode.
+
+ In this mode the algorithm will never move the lens or change state
+ unless the AfTrigger control is used. The AfTrigger control can be
+ used to initiate a focus scan, the results of which will be
+ reported by AfState.
+
+ If the autofocus algorithm is moved from AfModeAuto to another mode
+ while a scan is in progress, the scan is cancelled immediately,
+ without waiting for the scan to finish.
+
+ When first entering this mode the AfState will report AfStateIdle.
+ When a trigger control is sent, AfState will report AfStateScanning
+ for a period before spontaneously changing to AfStateFocused or
+ AfStateFailed, depending on the outcome of the scan. It will remain
+ in this state until another scan is initiated by the AfTrigger
+ control. If a scan is cancelled (without changing to another mode),
+ AfState will return to AfStateIdle.
+ - name: AfModeContinuous
+ value: 2
+ description: |
+ The AF algorithm is in continuous mode.
+
+ In this mode the lens can re-start a scan spontaneously at any
+ moment, without any user intervention. The AfState still reports
+ whether the algorithm is currently scanning or not, though the
+ application has no ability to initiate or cancel scans, nor to move
+ the lens for itself.
+
+ However, applications can pause the AF algorithm from continuously
+ scanning by using the AfPause control. This allows video or still
+ images to be captured whilst guaranteeing that the focus is fixed.
+
+ When set to AfModeContinuous, the system will immediately initiate a
+ scan so AfState will report AfStateScanning, and will settle on one
+ of AfStateFocused or AfStateFailed, depending on the scan result.
+
+ - AfRange:
+ type: int32_t
+ direction: inout
+ description: |
+ The range of focus distances that is scanned.
+
+ An implementation may choose not to implement all the options here.
+ enum:
+ - name: AfRangeNormal
+ value: 0
+ description: |
+ A wide range of focus distances is scanned.
+
+ Scanned distances cover all the way from infinity down to close
+ distances, though depending on the implementation, possibly not
+ including the very closest macro positions.
+ - name: AfRangeMacro
+ value: 1
+ description: |
+ Only close distances are scanned.
+ - name: AfRangeFull
+ value: 2
+ description: |
+ The full range of focus distances is scanned.
+
+ This range is similar to AfRangeNormal but includes the very
+ closest macro positions.
+
+ - AfSpeed:
+ type: int32_t
+ direction: inout
+ description: |
+ Determine whether the AF is to move the lens as quickly as possible or
+ more steadily.
+
+ For example, during video recording it may be desirable not to move the
+ lens too abruptly, but when in a preview mode (waiting for a still
+ capture) it may be helpful to move the lens as quickly as is reasonably
+ possible.
+ enum:
+ - name: AfSpeedNormal
+ value: 0
+ description: Move the lens at its usual speed.
+ - name: AfSpeedFast
+ value: 1
+ description: Move the lens more quickly.
+
+ - AfMetering:
+ type: int32_t
+ direction: inout
+ description: |
+ The parts of the image used by the AF algorithm to measure focus.
+ enum:
+ - name: AfMeteringAuto
+ value: 0
+ description: |
+ Let the AF algorithm decide for itself where it will measure focus.
+ - name: AfMeteringWindows
+ value: 1
+ description: |
+ Use the rectangles defined by the AfWindows control to measure focus.
+
+ If no windows are specified the behaviour is platform dependent.
+
+ - AfWindows:
+ type: Rectangle
+ direction: inout
+ description: |
+ The focus windows used by the AF algorithm when AfMetering is set to
+ AfMeteringWindows.
+
+ The units used are pixels within the rectangle returned by the
+ ScalerCropMaximum property.
+
+ In order to be activated, a rectangle must be programmed with non-zero
+ width and height. Internally, these rectangles are intersected with the
+ ScalerCropMaximum rectangle. If the window becomes empty after this
+ operation, then the window is ignored. If all the windows end up being
+ ignored, then the behaviour is platform dependent.
+
+ On platforms that support the ScalerCrop control (for implementing
+ digital zoom, for example), no automatic recalculation or adjustment of
+ AF windows is performed internally if the ScalerCrop is changed. If any
+ window lies outside the output image after the scaler crop has been
+ applied, it is up to the application to recalculate them.
+
+ The details of how the windows are used are platform dependent. We note
+ that when there is more than one AF window, a typical implementation
+ might find the optimal focus position for each one and finally select
+ the window where the focal distance for the objects shown in that part
+ of the image are closest to the camera.
+
+ size: [n]
+
+ - AfTrigger:
+ type: int32_t
+ direction: in
+ description: |
+ Start an autofocus scan.
+
+ This control starts an autofocus scan when AfMode is set to AfModeAuto,
+ and is ignored if AfMode is set to AfModeManual or AfModeContinuous. It
+ can also be used to terminate a scan early.
+
+ enum:
+ - name: AfTriggerStart
+ value: 0
+ description: |
+ Start an AF scan.
+
+ Setting the control to AfTriggerStart is ignored if a scan is in
+ progress.
+ - name: AfTriggerCancel
+ value: 1
+ description: |
+ Cancel an AF scan.
+
+ This does not cause the lens to move anywhere else. Ignored if no
+ scan is in progress.
+
+ - AfPause:
+ type: int32_t
+ direction: in
+ description: |
+ Pause lens movements when in continuous autofocus mode.
+
+ This control has no effect except when in continuous autofocus mode
+ (AfModeContinuous). It can be used to pause any lens movements while
+ (for example) images are captured. The algorithm remains inactive
+ until it is instructed to resume.
+
+ enum:
+ - name: AfPauseImmediate
+ value: 0
+ description: |
+ Pause the continuous autofocus algorithm immediately.
+
+ The autofocus algorithm is paused whether or not any kind of scan
+ is underway. AfPauseState will subsequently report
+ AfPauseStatePaused. AfState may report any of AfStateScanning,
+ AfStateFocused or AfStateFailed, depending on the algorithm's state
+ when it received this control.
+ - name: AfPauseDeferred
+ value: 1
+ description: |
+ Pause the continuous autofocus algorithm at the end of the scan.
+
+ This is similar to AfPauseImmediate, and if the AfState is
+ currently reporting AfStateFocused or AfStateFailed it will remain
+ in that state and AfPauseState will report AfPauseStatePaused.
+
+ However, if the algorithm is scanning (AfStateScanning),
+ AfPauseState will report AfPauseStatePausing until the scan is
+ finished, at which point AfState will report one of AfStateFocused
+ or AfStateFailed, and AfPauseState will change to
+ AfPauseStatePaused.
+
+ - name: AfPauseResume
+ value: 2
+ description: |
+ Resume continuous autofocus operation.
+
+ The algorithm starts again from exactly where it left off, and
+ AfPauseState will report AfPauseStateRunning.
+
+ - LensPosition:
+ type: float
+ direction: inout
+ description: |
+ Set and report the focus lens position.
+
+ This control instructs the lens to move to a particular position and
+ also reports back the position of the lens for each frame.
+
+ The LensPosition control is ignored unless the AfMode is set to
+ AfModeManual, though the value is reported back unconditionally in all
+ modes.
+
+ This value, which is generally a non-integer, is the reciprocal of the
+ focal distance in metres, also known as dioptres. That is, to set a
+ focal distance D, the lens position LP is given by
+
+ \f$LP = \frac{1\mathrm{m}}{D}\f$
+
+ For example:
+
+ - 0 moves the lens to infinity.
+ - 0.5 moves the lens to focus on objects 2m away.
+ - 2 moves the lens to focus on objects 50cm away.
+ - And larger values will focus the lens closer.
+
+ The default value of the control should indicate a good general
+ position for the lens, often corresponding to the hyperfocal distance
+ (the closest position for which objects at infinity are still
+ acceptably sharp). The minimum will often be zero (meaning infinity),
+ and the maximum value defines the closest focus position.
+
+ \todo Define a property to report the Hyperfocal distance of calibrated
+ lenses.
+
+ - AfState:
+ type: int32_t
+ direction: out
+ description: |
+ The current state of the AF algorithm.
+
+ This control reports the current state of the AF algorithm in
+ conjunction with the reported AfMode value and (in continuous AF mode)
+ the AfPauseState value. The possible state changes are described below,
+ though we note the following state transitions that occur when the
+ AfMode is changed.
+
+ If the AfMode is set to AfModeManual, then the AfState will always
+ report AfStateIdle (even if the lens is subsequently moved). Changing
+ to the AfModeManual state does not initiate any lens movement.
+
+ If the AfMode is set to AfModeAuto then the AfState will report
+ AfStateIdle. However, if AfModeAuto and AfTriggerStart are sent
+ together then AfState will omit AfStateIdle and move straight to
+ AfStateScanning (and start a scan).
+
+ If the AfMode is set to AfModeContinuous then the AfState will
+ initially report AfStateScanning.
+
+ enum:
+ - name: AfStateIdle
+ value: 0
+ description: |
+ The AF algorithm is in manual mode (AfModeManual) or in auto mode
+ (AfModeAuto) and a scan has not yet been triggered, or an
+ in-progress scan was cancelled.
+ - name: AfStateScanning
+ value: 1
+ description: |
+ The AF algorithm is in auto mode (AfModeAuto), and a scan has been
+ started using the AfTrigger control.
+
+ The scan can be cancelled by sending AfTriggerCancel at which point
+ the algorithm will either move back to AfStateIdle or, if the scan
+ actually completes before the cancel request is processed, to one
+ of AfStateFocused or AfStateFailed.
+
+ Alternatively the AF algorithm could be in continuous mode
+ (AfModeContinuous) at which point it may enter this state
+ spontaneously whenever it determines that a rescan is needed.
+ - name: AfStateFocused
+ value: 2
+ description: |
+ The AF algorithm is in auto (AfModeAuto) or continuous
+ (AfModeContinuous) mode and a scan has completed with the result
+ that the algorithm believes the image is now in focus.
+ - name: AfStateFailed
+ value: 3
+ description: |
+ The AF algorithm is in auto (AfModeAuto) or continuous
+ (AfModeContinuous) mode and a scan has completed with the result
+ that the algorithm did not find a good focus position.
+
+ - AfPauseState:
+ type: int32_t
+ direction: out
+ description: |
+ Report whether the autofocus is currently running, paused or pausing.
+
+ This control is only applicable in continuous (AfModeContinuous) mode,
+ and reports whether the algorithm is currently running, paused or
+ pausing (that is, will pause as soon as any in-progress scan
+ completes).
+
+ Any change to AfMode will cause AfPauseStateRunning to be reported.
+
+ enum:
+ - name: AfPauseStateRunning
+ value: 0
+ description: |
+ Continuous AF is running and the algorithm may restart a scan
+ spontaneously.
+ - name: AfPauseStatePausing
+ value: 1
+ description: |
+ Continuous AF has been sent an AfPauseDeferred control, and will
+ pause as soon as any in-progress scan completes.
+
+ When the scan completes, the AfPauseState control will report
+ AfPauseStatePaused. No new scans will be start spontaneously until
+ the AfPauseResume control is sent.
+ - name: AfPauseStatePaused
+ value: 2
+ description: |
+ Continuous AF is paused.
+
+ No further state changes or lens movements will occur until the
+ AfPauseResume control is sent.
+
+ - HdrMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Set the mode to be used for High Dynamic Range (HDR) imaging.
+
+ HDR techniques typically include multiple exposure, image fusion and
+ tone mapping techniques to improve the dynamic range of the resulting
+ images.
+
+ When using an HDR mode, images are captured with different sets of AGC
+ settings called HDR channels. Channels indicate in particular the type
+ of exposure (short, medium or long) used to capture the raw image,
+ before fusion. Each HDR image is tagged with the corresponding channel
+ using the HdrChannel control.
+
+ \sa HdrChannel
+
+ enum:
+ - name: HdrModeOff
+ value: 0
+ description: |
+ HDR is disabled.
+
+ Metadata for this frame will not include the HdrChannel control.
+ - name: HdrModeMultiExposureUnmerged
+ value: 1
+ description: |
+ Multiple exposures will be generated in an alternating fashion.
+
+ The multiple exposures will not be merged together and will be
+ returned to the application as they are. Each image will be tagged
+ with the correct HDR channel, indicating what kind of exposure it
+ is. The tag should be the same as in the HdrModeMultiExposure case.
+
+ The expectation is that an application using this mode would merge
+ the frames to create HDR images for itself if it requires them.
+ - name: HdrModeMultiExposure
+ value: 2
+ description: |
+ Multiple exposures will be generated and merged to create HDR
+ images.
+
+ Each image will be tagged with the HDR channel (long, medium or
+ short) that arrived and which caused this image to be output.
+
+ Systems that use two channels for HDR will return images tagged
+ alternately as the short and long channel. Systems that use three
+ channels for HDR will cycle through the short, medium and long
+ channel before repeating.
+ - name: HdrModeSingleExposure
+ value: 3
+ description: |
+ Multiple frames all at a single exposure will be used to create HDR
+ images.
+
+ These images should be reported as all corresponding to the HDR
+ short channel.
+ - name: HdrModeNight
+ value: 4
+ description: |
+ Multiple frames will be combined to produce "night mode" images.
+
+ It is up to the implementation exactly which HDR channels it uses,
+ and the images will all be tagged accordingly with the correct HDR
+ channel information.
+
+ - HdrChannel:
+ type: int32_t
+ direction: out
+ description: |
+ The HDR channel used to capture the frame.
+
+ This value is reported back to the application so that it can discover
+ whether this capture corresponds to the short or long exposure image
+ (or any other image used by the HDR procedure). An application can
+ monitor the HDR channel to discover when the differently exposed images
+ have arrived.
+
+ This metadata is only available when an HDR mode has been enabled.
+
+ \sa HdrMode
+
+ enum:
+ - name: HdrChannelNone
+ value: 0
+ description: |
+ This image does not correspond to any of the captures used to create
+ an HDR image.
+ - name: HdrChannelShort
+ value: 1
+ description: |
+ This is a short exposure image.
+ - name: HdrChannelMedium
+ value: 2
+ description: |
+ This is a medium exposure image.
+ - name: HdrChannelLong
+ value: 3
+ description: |
+ This is a long exposure image.
+
+ - Gamma:
+ type: float
+ direction: inout
+ description: |
+ Specify a fixed gamma value.
+
+ The default gamma value must be 2.2 which closely mimics sRGB gamma.
+ Note that this is camera gamma, so it is applied as 1.0/gamma.
+
+ - DebugMetadataEnable:
+ type: bool
+ direction: inout
+ description: |
+ Enable or disable the debug metadata.
+
+...
diff --git a/src/libcamera/control_ids_debug.yaml b/src/libcamera/control_ids_debug.yaml
new file mode 100644
index 00000000..79753271
--- /dev/null
+++ b/src/libcamera/control_ids_debug.yaml
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+%YAML 1.1
+---
+vendor: debug
+controls: []
diff --git a/src/libcamera/control_ids_draft.yaml b/src/libcamera/control_ids_draft.yaml
new file mode 100644
index 00000000..03309eea
--- /dev/null
+++ b/src/libcamera/control_ids_draft.yaml
@@ -0,0 +1,297 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2019, Google Inc.
+#
+%YAML 1.1
+---
+# Unless otherwise stated, all controls are bi-directional, i.e. they can be
+# set through Request::controls() and returned out through Request::metadata().
+vendor: draft
+controls:
+ - AePrecaptureTrigger:
+ type: int32_t
+ direction: inout
+ description: |
+ Control for AE metering trigger. Currently identical to
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER.
+
+ Whether the camera device will trigger a precapture metering sequence
+ when it processes this request.
+ enum:
+ - name: AePrecaptureTriggerIdle
+ value: 0
+ description: The trigger is idle.
+ - name: AePrecaptureTriggerStart
+ value: 1
+ description: The pre-capture AE metering is started by the camera.
+ - name: AePrecaptureTriggerCancel
+ value: 2
+ description: |
+ The camera will cancel any active or completed metering sequence.
+ The AE algorithm is reset to its initial state.
+
+ - NoiseReductionMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Control to select the noise reduction algorithm mode. Currently
+ identical to ANDROID_NOISE_REDUCTION_MODE.
+
+ Mode of operation for the noise reduction algorithm.
+ enum:
+ - name: NoiseReductionModeOff
+ value: 0
+ description: No noise reduction is applied
+ - name: NoiseReductionModeFast
+ value: 1
+ description: |
+ Noise reduction is applied without reducing the frame rate.
+ - name: NoiseReductionModeHighQuality
+ value: 2
+ description: |
+ High quality noise reduction at the expense of frame rate.
+ - name: NoiseReductionModeMinimal
+ value: 3
+ description: |
+ Minimal noise reduction is applied without reducing the frame rate.
+ - name: NoiseReductionModeZSL
+ value: 4
+ description: |
+ Noise reduction is applied at different levels to different streams.
+
+ - ColorCorrectionAberrationMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Control to select the color correction aberration mode. Currently
+ identical to ANDROID_COLOR_CORRECTION_ABERRATION_MODE.
+
+ Mode of operation for the chromatic aberration correction algorithm.
+ enum:
+ - name: ColorCorrectionAberrationOff
+ value: 0
+ description: No aberration correction is applied.
+ - name: ColorCorrectionAberrationFast
+ value: 1
+ description: Aberration correction will not slow down the frame rate.
+ - name: ColorCorrectionAberrationHighQuality
+ value: 2
+ description: |
+ High quality aberration correction which might reduce the frame
+ rate.
+
+ - AwbState:
+ type: int32_t
+ direction: out
+ description: |
+ Control to report the current AWB algorithm state. Currently identical
+ to ANDROID_CONTROL_AWB_STATE.
+
+ Current state of the AWB algorithm.
+ enum:
+ - name: AwbStateInactive
+ value: 0
+ description: The AWB algorithm is inactive.
+ - name: AwbStateSearching
+ value: 1
+ description: The AWB algorithm has not converged yet.
+ - name: AwbConverged
+ value: 2
+ description: The AWB algorithm has converged.
+ - name: AwbLocked
+ value: 3
+ description: The AWB algorithm is locked.
+
+ - SensorRollingShutterSkew:
+ type: int64_t
+ direction: out
+ description: |
+ Control to report the time between the start of exposure of the first
+ row and the start of exposure of the last row. Currently identical to
+ ANDROID_SENSOR_ROLLING_SHUTTER_SKEW
+
+ - LensShadingMapMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Control to report if the lens shading map is available. Currently
+ identical to ANDROID_STATISTICS_LENS_SHADING_MAP_MODE.
+ enum:
+ - name: LensShadingMapModeOff
+ value: 0
+ description: No lens shading map mode is available.
+ - name: LensShadingMapModeOn
+ value: 1
+ description: The lens shading map mode is available.
+
+ - PipelineDepth:
+ type: int32_t
+ direction: out
+ description: |
+ Specifies the number of pipeline stages the frame went through from when
+ it was exposed to when the final completed result was available to the
+ framework. Always less than or equal to PipelineMaxDepth. Currently
+ identical to ANDROID_REQUEST_PIPELINE_DEPTH.
+
+ The typical value for this control is 3 as a frame is first exposed,
+ captured and then processed in a single pass through the ISP. Any
+ additional processing step performed after the ISP pass (in example face
+ detection, additional format conversions etc) count as an additional
+ pipeline stage.
+
+ - MaxLatency:
+ type: int32_t
+ direction: out
+ description: |
+ The maximum number of frames that can occur after a request (different
+ than the previous) has been submitted, and before the result's state
+ becomes synchronized. A value of -1 indicates unknown latency, and 0
+ indicates per-frame control. Currently identical to
+ ANDROID_SYNC_MAX_LATENCY.
+
+ - TestPatternMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Control to select the test pattern mode. Currently identical to
+ ANDROID_SENSOR_TEST_PATTERN_MODE.
+ enum:
+ - name: TestPatternModeOff
+ value: 0
+ description: |
+ No test pattern mode is used. The camera device returns frames from
+ the image sensor.
+ - name: TestPatternModeSolidColor
+ value: 1
+ description: |
+ Each pixel in [R, G_even, G_odd, B] is replaced by its respective
+ color channel provided in test pattern data.
+ \todo Add control for test pattern data.
+ - name: TestPatternModeColorBars
+ value: 2
+ description: |
+ All pixel data is replaced with an 8-bar color pattern. The vertical
+ bars (left-to-right) are as follows; white, yellow, cyan, green,
+ magenta, red, blue and black. Each bar should take up 1/8 of the
+ sensor pixel array width. When this is not possible, the bar size
+ should be rounded down to the nearest integer and the pattern can
+ repeat on the right side. Each bar's height must always take up the
+ full sensor pixel array height.
+ - name: TestPatternModeColorBarsFadeToGray
+ value: 3
+ description: |
+ The test pattern is similar to TestPatternModeColorBars,
+ except that each bar should start at its specified color at the top
+ and fade to gray at the bottom. Furthermore each bar is further
+ subdevided into a left and right half. The left half should have a
+ smooth gradient, and the right half should have a quantized
+ gradient. In particular, the right half's should consist of blocks
+ of the same color for 1/16th active sensor pixel array width. The
+ least significant bits in the quantized gradient should be copied
+ from the most significant bits of the smooth gradient. The height of
+ each bar should always be a multiple of 128. When this is not the
+ case, the pattern should repeat at the bottom of the image.
+ - name: TestPatternModePn9
+ value: 4
+ description: |
+ All pixel data is replaced by a pseudo-random sequence generated
+ from a PN9 512-bit sequence (typically implemented in hardware with
+ a linear feedback shift register). The generator should be reset at
+ the beginning of each frame, and thus each subsequent raw frame with
+ this test pattern should be exactly the same as the last.
+ - name: TestPatternModeCustom1
+ value: 256
+ description: |
+ The first custom test pattern. All custom patterns that are
+ available only on this camera device are at least this numeric
+ value. All of the custom test patterns will be static (that is the
+ raw image must not vary from frame to frame).
+
+ - FaceDetectMode:
+ type: int32_t
+ direction: inout
+ description: |
+ Control to select the face detection mode used by the pipeline.
+
+ Currently identical to ANDROID_STATISTICS_FACE_DETECT_MODE.
+
+ \sa FaceDetectFaceRectangles
+ \sa FaceDetectFaceScores
+ \sa FaceDetectFaceLandmarks
+ \sa FaceDetectFaceIds
+
+ enum:
+ - name: FaceDetectModeOff
+ value: 0
+ description: |
+ Pipeline doesn't perform face detection and doesn't report any
+ control related to face detection.
+ - name: FaceDetectModeSimple
+ value: 1
+ description: |
+ Pipeline performs face detection and reports the
+ FaceDetectFaceRectangles and FaceDetectFaceScores controls for each
+ detected face. FaceDetectFaceLandmarks and FaceDetectFaceIds are
+ optional.
+ - name: FaceDetectModeFull
+ value: 2
+ description: |
+ Pipeline performs face detection and reports all the controls
+ related to face detection including FaceDetectFaceRectangles,
+ FaceDetectFaceScores, FaceDetectFaceLandmarks, and
+ FaceDeteceFaceIds for each detected face.
+
+ - FaceDetectFaceRectangles:
+ type: Rectangle
+ direction: out
+ description: |
+ Boundary rectangles of the detected faces. The number of values is
+ the number of detected faces.
+
+ The FaceDetectFaceRectangles control can only be returned in metadata.
+
+ Currently identical to ANDROID_STATISTICS_FACE_RECTANGLES.
+ size: [n]
+
+ - FaceDetectFaceScores:
+ type: uint8_t
+ direction: out
+ description: |
+ Confidence score of each of the detected faces. The range of score is
+ [0, 100]. The number of values should be the number of faces reported
+ in FaceDetectFaceRectangles.
+
+ The FaceDetectFaceScores control can only be returned in metadata.
+
+ Currently identical to ANDROID_STATISTICS_FACE_SCORES.
+ size: [n]
+
+ - FaceDetectFaceLandmarks:
+ type: Point
+ direction: out
+ description: |
+ Array of human face landmark coordinates in format [..., left_eye_i,
+ right_eye_i, mouth_i, left_eye_i+1, ...], with i = index of face. The
+ number of values should be 3 * the number of faces reported in
+ FaceDetectFaceRectangles.
+
+ The FaceDetectFaceLandmarks control can only be returned in metadata.
+
+ Currently identical to ANDROID_STATISTICS_FACE_LANDMARKS.
+ size: [n]
+
+ - FaceDetectFaceIds:
+ type: int32_t
+ direction: out
+ description: |
+ Each detected face is given a unique ID that is valid for as long as the
+ face is visible to the camera device. A face that leaves the field of
+ view and later returns may be assigned a new ID. The number of values
+ should be the number of faces reported in FaceDetectFaceRectangles.
+
+ The FaceDetectFaceIds control can only be returned in metadata.
+
+ Currently identical to ANDROID_STATISTICS_FACE_IDS.
+ size: [n]
+
+...
diff --git a/src/libcamera/control_ids_rpi.yaml b/src/libcamera/control_ids_rpi.yaml
new file mode 100644
index 00000000..8d1e8b47
--- /dev/null
+++ b/src/libcamera/control_ids_rpi.yaml
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2023, Raspberry Pi Ltd
+#
+%YAML 1.1
+---
+# Raspberry Pi (VC4 and PiSP) specific vendor controls
+vendor: rpi
+controls:
+ - StatsOutputEnable:
+ type: bool
+ direction: inout
+ description: |
+ Toggles the Raspberry Pi IPA to output the hardware generated statistics.
+
+ When this control is set to true, the IPA outputs a binary dump of the
+ hardware generated statistics through the Request metadata in the
+ Bcm2835StatsOutput control.
+
+ \sa Bcm2835StatsOutput
+
+ - Bcm2835StatsOutput:
+ type: uint8_t
+ size: [n]
+ direction: out
+ description: |
+ Span of the BCM2835 ISP generated statistics for the current frame.
+
+ This is sent in the Request metadata if the StatsOutputEnable is set to
+ true. The statistics struct definition can be found in
+ include/linux/bcm2835-isp.h.
+
+ \sa StatsOutputEnable
+
+ - ScalerCrops:
+ type: Rectangle
+ size: [n]
+ direction: out
+ description: |
+ An array of rectangles, where each singular value has identical
+ functionality to the ScalerCrop control. This control allows the
+ Raspberry Pi pipeline handler to control individual scaler crops per
+ output stream.
+
+ The order of rectangles passed into the control must match the order of
+ streams configured by the application. The pipeline handler will only
+ configure crop retangles up-to the number of output streams configured.
+ All subsequent rectangles passed into this control are ignored by the
+ pipeline handler.
+
+ If both rpi::ScalerCrops and ScalerCrop controls are present in a
+ ControlList, the latter is discarded, and crops are obtained from this
+ control.
+
+ Note that using different crop rectangles for each output stream with
+ this control is only applicable on the Pi5/PiSP platform. This control
+ should also be considered temporary/draft and will be replaced with
+ official libcamera API support for per-stream controls in the future.
+
+ \sa ScalerCrop
+
+ - PispStatsOutput:
+ type: uint8_t
+ direction: out
+ size: [n]
+ description: |
+ Span of the PiSP Frontend ISP generated statistics for the current
+ frame. This is sent in the Request metadata if the StatsOutputEnable is
+ set to true. The statistics struct definition can be found in
+ https://github.com/raspberrypi/libpisp/blob/main/src/libpisp/frontend/pisp_statistics.h
+
+ \sa StatsOutputEnable
+
+...
diff --git a/src/libcamera/control_ranges.yaml b/src/libcamera/control_ranges.yaml
new file mode 100644
index 00000000..6752eb98
--- /dev/null
+++ b/src/libcamera/control_ranges.yaml
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2023, Raspberry Pi Ltd
+#
+%YAML 1.1
+---
+# Specifies the control id ranges/offsets for core/draft libcamera and vendor
+# controls and properties.
+ranges:
+ # Core libcamera controls
+ libcamera: 0
+ # Draft designated libcamera controls
+ draft: 10000
+ # Raspberry Pi vendor controls
+ rpi: 20000
+ # Controls for debug metadata
+ debug: 30000
+ # Next range starts at 40000
+
+...
diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp
index 0cf719bd..17834648 100644
--- a/src/libcamera/control_serializer.cpp
+++ b/src/libcamera/control_serializer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_serializer.cpp - Control (de)serializer
+ * Control (de)serializer
*/
#include "libcamera/internal/control_serializer.h"
@@ -281,6 +281,7 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,
entry.id = id->id();
entry.type = id->type();
entry.offset = values.offset();
+ entry.direction = static_cast<ControlId::DirectionFlags::Type>(id->direction());
entries.write(&entry);
store(info, values);
@@ -493,12 +494,17 @@ ControlInfoMap ControlSerializer::deserialize<ControlInfoMap>(ByteStreamBuffer &
/* If we're using a local id map, populate it. */
if (localIdMap) {
+ ControlId::DirectionFlags flags{
+ static_cast<ControlId::Direction>(entry->direction)
+ };
+
/**
* \todo Find a way to preserve the control name for
* debugging purpose.
*/
controlIds_.emplace_back(std::make_unique<ControlId>(entry->id,
- "", type));
+ "", "local", type,
+ flags));
(*localIdMap)[entry->id] = controlIds_.back().get();
}
diff --git a/src/libcamera/control_validator.cpp b/src/libcamera/control_validator.cpp
index cf08b34a..93982cff 100644
--- a/src/libcamera/control_validator.cpp
+++ b/src/libcamera/control_validator.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * control_validator.cpp - Control validator
+ * Control validator
*/
#include "libcamera/internal/control_validator.h"
diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
index b808116c..98fa7583 100644
--- a/src/libcamera/controls.cpp
+++ b/src/libcamera/controls.cpp
@@ -2,15 +2,14 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * controls.cpp - Control handling
+ * Control handling
*/
#include <libcamera/controls.h>
-#include <iomanip>
#include <sstream>
-#include <string>
#include <string.h>
+#include <string>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
@@ -55,12 +54,15 @@ static constexpr size_t ControlValueSize[] = {
[ControlTypeNone] = 0,
[ControlTypeBool] = sizeof(bool),
[ControlTypeByte] = sizeof(uint8_t),
+ [ControlTypeUnsigned16] = sizeof(uint16_t),
+ [ControlTypeUnsigned32] = sizeof(uint32_t),
[ControlTypeInteger32] = sizeof(int32_t),
[ControlTypeInteger64] = sizeof(int64_t),
[ControlTypeFloat] = sizeof(float),
[ControlTypeString] = sizeof(char),
[ControlTypeRectangle] = sizeof(Rectangle),
[ControlTypeSize] = sizeof(Size),
+ [ControlTypePoint] = sizeof(Point),
};
} /* namespace */
@@ -74,10 +76,14 @@ static constexpr size_t ControlValueSize[] = {
* The control stores a boolean value
* \var ControlTypeByte
* The control stores a byte value as an unsigned 8-bit integer
+ * \var ControlTypeUnsigned16
+ * The control stores an unsigned 16-bit integer value
+ * \var ControlTypeUnsigned32
+ * The control stores an unsigned 32-bit integer value
* \var ControlTypeInteger32
- * The control stores a 32-bit integer value
+ * The control stores a signed 32-bit integer value
* \var ControlTypeInteger64
- * The control stores a 64-bit integer value
+ * The control stores a signed 64-bit integer value
* \var ControlTypeFloat
* The control stores a 32-bit floating point value
* \var ControlTypeString
@@ -230,6 +236,16 @@ std::string ControlValue::toString() const
str += std::to_string(*value);
break;
}
+ case ControlTypeUnsigned16: {
+ const uint16_t *value = reinterpret_cast<const uint16_t *>(data);
+ str += std::to_string(*value);
+ break;
+ }
+ case ControlTypeUnsigned32: {
+ const uint32_t *value = reinterpret_cast<const uint32_t *>(data);
+ str += std::to_string(*value);
+ break;
+ }
case ControlTypeInteger32: {
const int32_t *value = reinterpret_cast<const int32_t *>(data);
str += std::to_string(*value);
@@ -255,6 +271,11 @@ std::string ControlValue::toString() const
str += value->toString();
break;
}
+ case ControlTypePoint: {
+ const Point *value = reinterpret_cast<const Point *>(data);
+ str += value->toString();
+ break;
+ }
case ControlTypeNone:
case ControlTypeString:
break;
@@ -389,8 +410,22 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen
* \brief Construct a ControlId instance
* \param[in] id The control numerical ID
* \param[in] name The control name
+ * \param[in] vendor The vendor name
* \param[in] type The control data type
- */
+ * \param[in] direction The direction of the control, if it can be used in Controls or Metadata
+ * \param[in] size The size of the array control, or 0 if scalar control
+ * \param[in] enumStrMap The map from enum names to values (optional)
+ */
+ControlId::ControlId(unsigned int id, const std::string &name,
+ const std::string &vendor, ControlType type,
+ DirectionFlags direction, std::size_t size,
+ const std::map<std::string, int32_t> &enumStrMap)
+ : id_(id), name_(name), vendor_(vendor), type_(type),
+ direction_(direction), size_(size), enumStrMap_(enumStrMap)
+{
+ for (const auto &pair : enumStrMap_)
+ reverseMap_[pair.second] = pair.first;
+}
/**
* \fn unsigned int ControlId::id() const
@@ -405,12 +440,68 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen
*/
/**
+ * \fn const std::string &ControlId::vendor() const
+ * \brief Retrieve the vendor name
+ * \return The vendor name, as a string
+ */
+
+/**
* \fn ControlType ControlId::type() const
* \brief Retrieve the control data type
* \return The control data type
*/
/**
+ * \fn DirectionFlags ControlId::direction() const
+ * \brief Return the direction that the control can be used in
+ *
+ * This is similar to \sa isInput() and \sa isOutput(), but returns the flags
+ * direction instead of booleans for each direction.
+ *
+ * \return The direction flags corresponding to if the control can be used as
+ * an input control or as output metadata
+ */
+
+/**
+ * \fn bool ControlId::isInput() const
+ * \brief Determine if the control is available to be used as an input control
+ *
+ * Controls can be used either as input in controls, or as output in metadata.
+ * This function checks if the control is allowed to be used as the former.
+ *
+ * \return True if the control can be used as an input control, false otherwise
+ */
+
+/**
+ * \fn bool ControlId::isOutput() const
+ * \brief Determine if the control is available to be used in output metadata
+ *
+ * Controls can be used either as input in controls, or as output in metadata.
+ * This function checks if the control is allowed to be used as the latter.
+ *
+ * \return True if the control can be returned in output metadata, false otherwise
+ */
+
+/**
+ * \fn bool ControlId::isArray() const
+ * \brief Determine if the control is an array control
+ * \return True if the control is an array control, false otherwise
+ */
+
+/**
+ * \fn std::size_t ControlId::size() const
+ * \brief Retrieve the size of the control if it is an array control
+ * \return The size of the array control, size_t::max for dynamic extent, or 0
+ * for non-array
+ */
+
+/**
+ * \fn const std::map<int32_t, std::string> &ControlId::enumerators() const
+ * \brief Retrieve the map of enum values to enum names
+ * \return The map of enum values to enum names
+ */
+
+/**
* \fn bool operator==(unsigned int lhs, const ControlId &rhs)
* \brief Compare a ControlId with a control numerical ID
* \param[in] lhs Left-hand side numerical ID
@@ -429,6 +520,22 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen
*/
/**
+ * \enum ControlId::Direction
+ * \brief The direction the control is capable of being passed from/to
+ *
+ * \var ControlId::Direction::In
+ * \brief The control can be passed as input in controls
+ *
+ * \var ControlId::Direction::Out
+ * \brief The control can be returned as output in metadata
+ */
+
+/**
+ * \typedef ControlId::DirectionFlags
+ * \brief A wrapper for ControlId::Direction so that it can be used as flags
+ */
+
+/**
* \class Control
* \brief Describe a control and its intrinsic properties
*
@@ -456,10 +563,14 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen
*/
/**
- * \fn Control::Control(unsigned int id, const char *name)
+ * \fn Control::Control(unsigned int id, const char *name, const char *vendor)
* \brief Construct a Control instance
* \param[in] id The control numerical ID
* \param[in] name The control name
+ * \param[in] vendor The vendor name
+ * \param[in] direction The direction of the control, if it can be used in
+ * Controls or Metadata
+ * \param[in] enumStrMap The map from enum names to values (optional)
*
* The control data type is automatically deduced from the template type T.
*/
@@ -746,15 +857,7 @@ const ControlInfoMap::mapped_type &ControlInfoMap::at(unsigned int id) const
*/
ControlInfoMap::size_type ControlInfoMap::count(unsigned int id) const
{
- if (!idmap_)
- return 0;
-
- /*
- * The ControlInfoMap and its idmap have a 1:1 mapping between their
- * entries, we can thus just count the matching entries in idmap to
- * avoid an additional lookup.
- */
- return idmap_->count(id);
+ return find(id) != end();
}
/**
@@ -908,12 +1011,26 @@ ControlList::ControlList(const ControlInfoMap &infoMap,
*/
/**
+ * \enum ControlList::MergePolicy
+ * \brief The policy used by the merge function
+ *
+ * \var ControlList::MergePolicy::KeepExisting
+ * \brief Existing controls in the target list are kept
+ *
+ * \var ControlList::MergePolicy::OverwriteExisting
+ * \brief Existing controls in the target list are updated
+ */
+
+/**
* \brief Merge the \a source into the ControlList
* \param[in] source The ControlList to merge into this object
+ * \param[in] policy Controls if existing elements in *this shall be
+ * overwritten
*
* Merging two control lists copies elements from the \a source and inserts
* them in *this. If the \a source contains elements whose key is already
- * present in *this, then those elements are not overwritten.
+ * present in *this, then those elements are only overwritten if
+ * \a policy is MergePolicy::OverwriteExisting.
*
* Only control lists created from the same ControlIdMap or ControlInfoMap may
* be merged. Attempting to do otherwise results in undefined behaviour.
@@ -921,7 +1038,7 @@ ControlList::ControlList(const ControlInfoMap &infoMap,
* \todo Reimplement or implement an overloaded version which internally uses
* std::unordered_map::merge() and accepts a non-const argument.
*/
-void ControlList::merge(const ControlList &source)
+void ControlList::merge(const ControlList &source, MergePolicy policy)
{
/**
* \todo ASSERT that the current and source ControlList are derived
@@ -936,7 +1053,7 @@ void ControlList::merge(const ControlList &source)
*/
for (const auto &ctrl : source) {
- if (contains(ctrl.first)) {
+ if (policy == MergePolicy::KeepExisting && contains(ctrl.first)) {
const ControlId *id = idmap_->at(ctrl.first);
LOG(Controls, Warning)
<< "Control " << id->name() << " not overwritten";
diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
index 9f64eb51..d551b908 100644
--- a/src/libcamera/converter.cpp
+++ b/src/libcamera/converter.cpp
@@ -2,7 +2,7 @@
/*
* Copyright 2022 NXP
*
- * converter.cpp - Generic format converter interface
+ * Generic format converter interface
*/
#include "libcamera/internal/converter.h"
@@ -11,10 +11,12 @@
#include <libcamera/base/log.h>
+#include <libcamera/stream.h>
+
#include "libcamera/internal/media_device.h"
/**
- * \file internal/converter.h
+ * \file converter.h
* \brief Abstract converter
*/
@@ -35,13 +37,38 @@ LOG_DEFINE_CATEGORY(Converter)
*/
/**
+ * \enum Converter::Feature
+ * \brief Specify the features supported by the converter
+ * \var Converter::Feature::None
+ * \brief No extra features supported by the converter
+ * \var Converter::Feature::InputCrop
+ * \brief Cropping capability at input is supported by the converter
+ */
+
+/**
+ * \typedef Converter::Features
+ * \brief A bitwise combination of features supported by the converter
+ */
+
+/**
+ * \enum Converter::Alignment
+ * \brief The alignment mode specified when adjusting the converter input or
+ * output sizes
+ * \var Converter::Alignment::Down
+ * \brief Adjust the Converter sizes to a smaller valid size
+ * \var Converter::Alignment::Up
+ * \brief Adjust the Converter sizes to a larger valid size
+ */
+
+/**
* \brief Construct a Converter instance
* \param[in] media The media device implementing the converter
+ * \param[in] features Features flags representing supported features
*
* This searches for the entity implementing the data streaming function in the
* media graph entities and use its device node as the converter device node.
*/
-Converter::Converter(MediaDevice *media)
+Converter::Converter(MediaDevice *media, Features features)
{
const std::vector<MediaEntity *> &entities = media->entities();
auto it = std::find_if(entities.begin(), entities.end(),
@@ -56,6 +83,7 @@ Converter::Converter(MediaDevice *media)
}
deviceNode_ = (*it)->deviceNode();
+ features_ = features;
}
Converter::~Converter()
@@ -93,6 +121,26 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::adjustInputSize()
+ * \brief Adjust the converter input \a size to a valid value
+ * \param[in] pixFmt The pixel format of the converter input stream
+ * \param[in] size The converter input size to adjust to a valid value
+ * \param[in] align The desired alignment
+ * \return The adjusted converter input size or a null Size if \a size cannot
+ * be adjusted
+ */
+
+/**
+ * \fn Converter::adjustOutputSize()
+ * \brief Adjust the converter output \a size to a valid value
+ * \param[in] pixFmt The pixel format of the converter output stream
+ * \param[in] size The converter output size to adjust to a valid value
+ * \param[in] align The desired alignment
+ * \return The adjusted converter output size or a null Size if \a size cannot
+ * be adjusted
+ */
+
+/**
* \fn Converter::strideAndFrameSize()
* \brief Retrieve the output stride and frame size for an input configutation
* \param[in] pixelFormat Input stream pixel format
@@ -101,6 +149,16 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::validateOutput()
+ * \brief Validate and possibily adjust \a cfg to a valid converter output
+ * \param[inout] cfg The StreamConfiguration to validate and adjust
+ * \param[out] adjusted Set to true if \a cfg has been adjusted
+ * \param[in] align The desired alignment
+ * \return 0 if \a cfg is valid or has been adjusted, a negative error code
+ * otherwise if \a cfg cannot be adjusted
+ */
+
+/**
* \fn Converter::configure()
* \brief Configure a set of output stream conversion from an input stream
* \param[in] inputCfg Input stream configuration
@@ -109,14 +167,21 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::isConfigured()
+ * \brief Check if a given stream is configured
+ * \param[in] stream The output stream
+ * \return True if the \a stream is configured or false otherwise
+ */
+
+/**
* \fn Converter::exportBuffers()
* \brief Export buffers from the converter device
- * \param[in] output Output stream index exporting the buffers
+ * \param[in] stream Output stream pointer exporting the buffers
* \param[in] count Number of buffers to allocate
* \param[out] buffers Vector to store allocated buffers
*
* This function operates similarly to V4L2VideoDevice::exportBuffers() on the
- * output stream indicated by the \a output index.
+ * output stream indicated by the \a output.
*
* \return The number of allocated buffers on success or a negative error code
* otherwise
@@ -137,7 +202,7 @@ Converter::~Converter()
* \fn Converter::queueBuffers()
* \brief Queue buffers to converter device
* \param[in] input The frame buffer to apply the conversion
- * \param[out] outputs The container holding the output stream indexes and
+ * \param[out] outputs The container holding the output stream pointers and
* their respective frame buffer outputs.
*
* This function queues the \a input frame buffer on the output streams of the
@@ -148,6 +213,52 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::setInputCrop()
+ * \brief Set the crop rectangle \a rect for \a stream
+ * \param[in] stream The output stream
+ * \param[inout] rect The crop rectangle to apply and return the rectangle
+ * that is actually applied
+ *
+ * Set the crop rectangle \a rect for \a stream provided the converter supports
+ * cropping. The converter has the Feature::InputCrop flag in this case.
+ *
+ * The underlying hardware can adjust the rectangle supplied by the user
+ * due to hardware constraints. The caller can inspect \a rect to determine the
+ * actual rectangle that has been applied by the converter, after this function
+ * returns.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
+ * \fn Converter::inputCropBounds()
+ * \brief Retrieve the crop bounds of the converter
+ *
+ * Retrieve the minimum and maximum crop bounds of the converter. This can be
+ * used to query the crop bounds before configuring a stream.
+ *
+ * \return A pair containing the minimum and maximum crop bound in that order
+ */
+
+/**
+ * \fn Converter::inputCropBounds(const Stream *stream)
+ * \brief Retrieve the crop bounds for \a stream
+ * \param[in] stream The output stream
+ *
+ * Retrieve the minimum and maximum crop bounds for \a stream. The converter
+ * should support cropping (Feature::InputCrop).
+ *
+ * The crop bounds depend on the configuration of the output stream and hence
+ * this function should be called after the \a stream has been configured using
+ * configure().
+ *
+ * When called with an unconfigured \a stream, this function returns a pair of
+ * null rectangles.
+ *
+ * \return A pair containing the minimum and maximum crop bound in that order
+ */
+
+/**
* \var Converter::inputBufferReady
* \brief A signal emitted when the input frame buffer completes
*/
@@ -158,12 +269,23 @@ Converter::~Converter()
*/
/**
+ * \var Converter::features_
+ * \brief Stores the features supported by the converter
+ */
+
+/**
* \fn Converter::deviceNode()
* \brief The converter device node attribute accessor
* \return The converter device node string
*/
/**
+ * \fn Converter::features()
+ * \brief Retrieve the features supported by the converter
+ * \return The converter Features flags
+ */
+
+/**
* \class ConverterFactoryBase
* \brief Base class for converter factories
*
@@ -263,8 +385,9 @@ std::vector<std::string> ConverterFactoryBase::names()
for (ConverterFactoryBase *factory : factories) {
list.push_back(factory->name_);
- for (auto alias : factory->compatibles())
- list.push_back(alias);
+
+ const auto &compatibles = factory->compatibles();
+ list.insert(list.end(), compatibles.begin(), compatibles.end());
}
return list;
diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
index 2a4d1d99..ee05b798 100644
--- a/src/libcamera/converter/converter_v4l2_m2m.cpp
+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Laurent Pinchart
* Copyright 2022 NXP
*
- * converter_v4l2_m2m.cpp - V4L2 M2M Format converter
+ * V4L2 M2M Format converter
*/
#include "libcamera/internal/converter/converter_v4l2_m2m.h"
@@ -23,7 +23,7 @@
#include "libcamera/internal/v4l2_videodevice.h"
/**
- * \file internal/converter/converter_v4l2_m2m.h
+ * \file converter/converter_v4l2_m2m.h
* \brief V4L2 M2M based converter
*/
@@ -31,25 +31,71 @@ namespace libcamera {
LOG_DECLARE_CATEGORY(Converter)
+namespace {
+
+int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
+ Rectangle &maxCrop)
+{
+ Rectangle minC;
+ Rectangle maxC;
+
+ /* Find crop bounds */
+ minC.width = 1;
+ minC.height = 1;
+ maxC.width = UINT_MAX;
+ maxC.height = UINT_MAX;
+
+ int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
+ if (ret) {
+ LOG(Converter, Error)
+ << "Could not query minimum selection crop: "
+ << strerror(-ret);
+ return ret;
+ }
+
+ ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
+ if (ret) {
+ LOG(Converter, Error)
+ << "Could not query maximum selection crop: "
+ << strerror(-ret);
+ return ret;
+ }
+
+ /* Reset the crop to its maximum */
+ ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
+ if (ret) {
+ LOG(Converter, Error)
+ << "Could not reset selection crop: "
+ << strerror(-ret);
+ return ret;
+ }
+
+ minCrop = minC;
+ maxCrop = maxC;
+ return 0;
+}
+
+} /* namespace */
+
/* -----------------------------------------------------------------------------
- * V4L2M2MConverter::Stream
+ * V4L2M2MConverter::V4L2M2MStream
*/
-V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, unsigned int index)
- : converter_(converter), index_(index)
+V4L2M2MConverter::V4L2M2MStream::V4L2M2MStream(V4L2M2MConverter *converter, const Stream *stream)
+ : converter_(converter), stream_(stream)
{
m2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode());
- m2m_->output()->bufferReady.connect(this, &Stream::outputBufferReady);
- m2m_->capture()->bufferReady.connect(this, &Stream::captureBufferReady);
+ m2m_->output()->bufferReady.connect(this, &V4L2M2MStream::outputBufferReady);
+ m2m_->capture()->bufferReady.connect(this, &V4L2M2MStream::captureBufferReady);
int ret = m2m_->open();
if (ret < 0)
m2m_.reset();
}
-int V4L2M2MConverter::Stream::configure(const StreamConfiguration &inputCfg,
- const StreamConfiguration &outputCfg)
+int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputCfg,
+ const StreamConfiguration &outputCfg)
{
V4L2PixelFormat videoFormat =
m2m_->output()->toV4L2PixelFormat(inputCfg.pixelFormat);
@@ -98,16 +144,23 @@ int V4L2M2MConverter::Stream::configure(const StreamConfiguration &inputCfg,
inputBufferCount_ = inputCfg.bufferCount;
outputBufferCount_ = outputCfg.bufferCount;
+ if (converter_->features() & Feature::InputCrop) {
+ ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
+ inputCropBounds_.second);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
-int V4L2M2MConverter::Stream::exportBuffers(unsigned int count,
- std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+int V4L2M2MConverter::V4L2M2MStream::exportBuffers(unsigned int count,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
return m2m_->capture()->exportBuffers(count, buffers);
}
-int V4L2M2MConverter::Stream::start()
+int V4L2M2MConverter::V4L2M2MStream::start()
{
int ret = m2m_->output()->importBuffers(inputBufferCount_);
if (ret < 0)
@@ -134,7 +187,7 @@ int V4L2M2MConverter::Stream::start()
return 0;
}
-void V4L2M2MConverter::Stream::stop()
+void V4L2M2MConverter::V4L2M2MStream::stop()
{
m2m_->capture()->streamOff();
m2m_->output()->streamOff();
@@ -142,7 +195,7 @@ void V4L2M2MConverter::Stream::stop()
m2m_->output()->releaseBuffers();
}
-int V4L2M2MConverter::Stream::queueBuffers(FrameBuffer *input, FrameBuffer *output)
+int V4L2M2MConverter::V4L2M2MStream::queueBuffers(FrameBuffer *input, FrameBuffer *output)
{
int ret = m2m_->output()->queueBuffer(input);
if (ret < 0)
@@ -155,12 +208,27 @@ int V4L2M2MConverter::Stream::queueBuffers(FrameBuffer *input, FrameBuffer *outp
return 0;
}
-std::string V4L2M2MConverter::Stream::logPrefix() const
+int V4L2M2MConverter::V4L2M2MStream::getInputSelection(unsigned int target, Rectangle *rect)
+{
+ return m2m_->output()->getSelection(target, rect);
+}
+
+int V4L2M2MConverter::V4L2M2MStream::setInputSelection(unsigned int target, Rectangle *rect)
+{
+ return m2m_->output()->setSelection(target, rect);
+}
+
+std::pair<Rectangle, Rectangle> V4L2M2MConverter::V4L2M2MStream::inputCropBounds()
{
- return "stream" + std::to_string(index_);
+ return inputCropBounds_;
}
-void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer)
+std::string V4L2M2MConverter::V4L2M2MStream::logPrefix() const
+{
+ return stream_->configuration().toString();
+}
+
+void V4L2M2MConverter::V4L2M2MStream::outputBufferReady(FrameBuffer *buffer)
{
auto it = converter_->queue_.find(buffer);
if (it == converter_->queue_.end())
@@ -172,7 +240,7 @@ void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer)
}
}
-void V4L2M2MConverter::Stream::captureBufferReady(FrameBuffer *buffer)
+void V4L2M2MConverter::V4L2M2MStream::captureBufferReady(FrameBuffer *buffer)
{
converter_->outputBufferReady.emit(buffer);
}
@@ -205,6 +273,15 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)
m2m_.reset();
return;
}
+
+ ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
+ inputCropBounds_.second);
+ if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
+ features_ |= Feature::InputCrop;
+
+ LOG(Converter, Info)
+ << "Converter supports cropping on its input";
+ }
}
/**
@@ -325,6 +402,127 @@ V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat,
}
/**
+ * \copydoc libcamera::Converter::adjustInputSize
+ */
+Size V4L2M2MConverter::adjustInputSize(const PixelFormat &pixFmt,
+ const Size &size, Alignment align)
+{
+ auto formats = m2m_->output()->formats();
+ V4L2PixelFormat v4l2PixFmt = m2m_->output()->toV4L2PixelFormat(pixFmt);
+
+ auto it = formats.find(v4l2PixFmt);
+ if (it == formats.end()) {
+ LOG(Converter, Info)
+ << "Unsupported pixel format " << pixFmt;
+ return {};
+ }
+
+ return adjustSizes(size, it->second, align);
+}
+
+/**
+ * \copydoc libcamera::Converter::adjustOutputSize
+ */
+Size V4L2M2MConverter::adjustOutputSize(const PixelFormat &pixFmt,
+ const Size &size, Alignment align)
+{
+ auto formats = m2m_->capture()->formats();
+ V4L2PixelFormat v4l2PixFmt = m2m_->capture()->toV4L2PixelFormat(pixFmt);
+
+ auto it = formats.find(v4l2PixFmt);
+ if (it == formats.end()) {
+ LOG(Converter, Info)
+ << "Unsupported pixel format " << pixFmt;
+ return {};
+ }
+
+ return adjustSizes(size, it->second, align);
+}
+
+Size V4L2M2MConverter::adjustSizes(const Size &cfgSize,
+ const std::vector<SizeRange> &ranges,
+ Alignment align)
+{
+ Size size = cfgSize;
+
+ if (ranges.size() == 1) {
+ /*
+ * The device supports either V4L2_FRMSIZE_TYPE_CONTINUOUS or
+ * V4L2_FRMSIZE_TYPE_STEPWISE.
+ */
+ const SizeRange &range = *ranges.begin();
+
+ size.width = std::clamp(size.width, range.min.width,
+ range.max.width);
+ size.height = std::clamp(size.height, range.min.height,
+ range.max.height);
+
+ /*
+ * Check if any alignment is needed. If the sizes are already
+ * aligned, or the device supports V4L2_FRMSIZE_TYPE_CONTINUOUS
+ * with hStep and vStep equal to 1, we're done here.
+ */
+ int widthR = size.width % range.hStep;
+ int heightR = size.height % range.vStep;
+
+ /* Align up or down according to the caller request. */
+
+ if (widthR != 0)
+ size.width = size.width - widthR
+ + ((align == Alignment::Up) ? range.hStep : 0);
+
+ if (heightR != 0)
+ size.height = size.height - heightR
+ + ((align == Alignment::Up) ? range.vStep : 0);
+ } else {
+ /*
+ * The device supports V4L2_FRMSIZE_TYPE_DISCRETE, find the
+ * size closer to the requested output configuration.
+ *
+ * The size ranges vector is not ordered, so we sort it first.
+ * If we align up, start from the larger element.
+ */
+ std::vector<Size> sizes(ranges.size());
+ std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
+ [](const SizeRange &range) { return range.max; });
+ std::sort(sizes.begin(), sizes.end());
+
+ if (align == Alignment::Up)
+ std::reverse(sizes.begin(), sizes.end());
+
+ /*
+ * Return true if s2 is valid according to the desired
+ * alignment: smaller than s1 if we align down, larger than s1
+ * if we align up.
+ */
+ auto nextSizeValid = [](const Size &s1, const Size &s2, Alignment a) {
+ return a == Alignment::Down
+ ? (s1.width > s2.width && s1.height > s2.height)
+ : (s1.width < s2.width && s1.height < s2.height);
+ };
+
+ Size newSize;
+ for (const Size &sz : sizes) {
+ if (!nextSizeValid(size, sz, align))
+ break;
+
+ newSize = sz;
+ }
+
+ if (newSize.isNull()) {
+ LOG(Converter, Error)
+ << "Cannot adjust " << cfgSize
+ << " to a supported converter size";
+ return {};
+ }
+
+ size = newSize;
+ }
+
+ return size;
+}
+
+/**
* \copydoc libcamera::Converter::configure
*/
int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
@@ -333,21 +531,24 @@ int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
int ret = 0;
streams_.clear();
- streams_.reserve(outputCfgs.size());
for (unsigned int i = 0; i < outputCfgs.size(); ++i) {
- Stream &stream = streams_.emplace_back(this, i);
+ const StreamConfiguration &cfg = outputCfgs[i];
+ std::unique_ptr<V4L2M2MStream> stream =
+ std::make_unique<V4L2M2MStream>(this, cfg.stream());
- if (!stream.isValid()) {
+ if (!stream->isValid()) {
LOG(Converter, Error)
<< "Failed to create stream " << i;
ret = -EINVAL;
break;
}
- ret = stream.configure(inputCfg, outputCfgs[i]);
+ ret = stream->configure(inputCfg, cfg);
if (ret < 0)
break;
+
+ streams_.emplace(cfg.stream(), std::move(stream));
}
if (ret < 0) {
@@ -359,15 +560,61 @@ int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
}
/**
+ * \copydoc libcamera::Converter::isConfigured
+ */
+bool V4L2M2MConverter::isConfigured(const Stream *stream) const
+{
+ return streams_.find(stream) != streams_.end();
+}
+
+/**
* \copydoc libcamera::Converter::exportBuffers
*/
-int V4L2M2MConverter::exportBuffers(unsigned int output, unsigned int count,
+int V4L2M2MConverter::exportBuffers(const Stream *stream, unsigned int count,
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
- if (output >= streams_.size())
+ auto iter = streams_.find(stream);
+ if (iter == streams_.end())
+ return -EINVAL;
+
+ return iter->second->exportBuffers(count, buffers);
+}
+
+/**
+ * \copydoc libcamera::Converter::setInputCrop
+ */
+int V4L2M2MConverter::setInputCrop(const Stream *stream, Rectangle *rect)
+{
+ if (!(features_ & Feature::InputCrop))
+ return -ENOTSUP;
+
+ auto iter = streams_.find(stream);
+ if (iter == streams_.end()) {
+ LOG(Converter, Error) << "Invalid output stream";
return -EINVAL;
+ }
+
+ return iter->second->setInputSelection(V4L2_SEL_TGT_CROP, rect);
+}
+
+/**
+ * \fn libcamera::V4L2M2MConverter::inputCropBounds()
+ * \copydoc libcamera::Converter::inputCropBounds()
+ */
+
+/**
+ * \copydoc libcamera::Converter::inputCropBounds(const Stream *stream)
+ */
+std::pair<Rectangle, Rectangle>
+V4L2M2MConverter::inputCropBounds(const Stream *stream)
+{
+ auto iter = streams_.find(stream);
+ if (iter == streams_.end()) {
+ LOG(Converter, Error) << "Invalid output stream";
+ return {};
+ }
- return streams_[output].exportBuffers(count, buffers);
+ return iter->second->inputCropBounds();
}
/**
@@ -377,8 +624,8 @@ int V4L2M2MConverter::start()
{
int ret;
- for (Stream &stream : streams_) {
- ret = stream.start();
+ for (auto &iter : streams_) {
+ ret = iter.second->start();
if (ret < 0) {
stop();
return ret;
@@ -393,41 +640,87 @@ int V4L2M2MConverter::start()
*/
void V4L2M2MConverter::stop()
{
- for (Stream &stream : utils::reverse(streams_))
- stream.stop();
+ for (auto &iter : streams_)
+ iter.second->stop();
+}
+
+/**
+ * \copydoc libcamera::Converter::validateOutput
+ */
+int V4L2M2MConverter::validateOutput(StreamConfiguration *cfg, bool *adjusted,
+ Alignment align)
+{
+ V4L2VideoDevice *capture = m2m_->capture();
+ V4L2VideoDevice::Formats fmts = capture->formats();
+
+ if (adjusted)
+ *adjusted = false;
+
+ PixelFormat fmt = cfg->pixelFormat;
+ V4L2PixelFormat v4l2PixFmt = capture->toV4L2PixelFormat(fmt);
+
+ auto it = fmts.find(v4l2PixFmt);
+ if (it == fmts.end()) {
+ it = fmts.begin();
+ v4l2PixFmt = it->first;
+ cfg->pixelFormat = v4l2PixFmt.toPixelFormat();
+
+ if (adjusted)
+ *adjusted = true;
+
+ LOG(Converter, Info)
+ << "Converter output pixel format adjusted to "
+ << cfg->pixelFormat;
+ }
+
+ const Size cfgSize = cfg->size;
+ cfg->size = adjustSizes(cfgSize, it->second, align);
+
+ if (cfg->size.isNull())
+ return -EINVAL;
+
+ if (cfg->size.width != cfgSize.width ||
+ cfg->size.height != cfgSize.height) {
+ LOG(Converter, Info)
+ << "Converter size adjusted to "
+ << cfg->size;
+ if (adjusted)
+ *adjusted = true;
+ }
+
+ return 0;
}
/**
* \copydoc libcamera::Converter::queueBuffers
*/
int V4L2M2MConverter::queueBuffers(FrameBuffer *input,
- const std::map<unsigned int, FrameBuffer *> &outputs)
+ const std::map<const Stream *, FrameBuffer *> &outputs)
{
- unsigned int mask = 0;
+ std::set<FrameBuffer *> outputBufs;
int ret;
/*
* Validate the outputs as a sanity check: at least one output is
* required, all outputs must reference a valid stream and no two
- * outputs can reference the same stream.
+ * streams can reference same output framebuffers.
*/
if (outputs.empty())
return -EINVAL;
- for (auto [index, buffer] : outputs) {
+ for (auto [stream, buffer] : outputs) {
if (!buffer)
return -EINVAL;
- if (index >= streams_.size())
- return -EINVAL;
- if (mask & (1 << index))
- return -EINVAL;
- mask |= 1 << index;
+ outputBufs.insert(buffer);
}
+ if (outputBufs.size() != streams_.size())
+ return -EINVAL;
+
/* Queue the input and output buffers to all the streams. */
- for (auto [index, buffer] : outputs) {
- ret = streams_[index].queueBuffers(input, buffer);
+ for (auto [stream, buffer] : outputs) {
+ ret = streams_.at(stream)->queueBuffers(input, buffer);
if (ret < 0)
return ret;
}
@@ -444,7 +737,12 @@ int V4L2M2MConverter::queueBuffers(FrameBuffer *input,
return 0;
}
+/*
+ * \todo This should be extended to include Feature::Flag to denote
+ * what each converter supports feature-wise.
+ */
static std::initializer_list<std::string> compatibles = {
+ "mtk-mdp",
"pxp",
};
diff --git a/src/libcamera/converter/meson.build b/src/libcamera/converter/meson.build
index 2aa72fe4..af1a80fe 100644
--- a/src/libcamera/converter/meson.build
+++ b/src/libcamera/converter/meson.build
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'converter_v4l2_m2m.cpp'
])
diff --git a/src/libcamera/debug_controls.cpp b/src/libcamera/debug_controls.cpp
new file mode 100644
index 00000000..33960231
--- /dev/null
+++ b/src/libcamera/debug_controls.cpp
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Helper to easily record debug metadata inside libcamera.
+ */
+
+#include "libcamera/internal/debug_controls.h"
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(DebugControls)
+
+/**
+ * \file debug_controls.h
+ * \brief Helper to easily record debug metadata inside libcamera
+ */
+
+/**
+ * \class DebugMetadata
+ * \brief Helper to record metadata for later use
+ *
+ * Metadata is a useful tool for debugging the internal state of libcamera. It
+ * has the benefit that it is easy to use and related tooling is readily
+ * available. The difficulty is that the metadata control list is often not
+ * directly available (either because the variable to debug lives inside
+ * process() of an IPA or inside a closed algorithm class with no direct access
+ * to the IPA and therefore the metadata list).
+ *
+ * This class helps in both cases. It allows to forward the data to a parent or
+ * alternatively record the data and at a later point in time copy it to the
+ * metadata list when it becomes available. Both mechanisms allow easy reuse and
+ * loose coupling.
+ *
+ * Typical usage is to instantiate a DebugMetadata object in every
+ * class/algorithm where debug metadata shall be recorded (the inner object). If
+ * the IPA doesn't support debug metadata, the object is still usable, but the
+ * debug data gets dropped. If the IPA supports debug metadata it will either
+ * register a parent DebugMetadata object on the inner object or manually
+ * retrieve the data using enable()/moveToList().
+ *
+ * The concepts of forwarding to a parent and recording for later retrieval are
+ * mutually exclusive and the parent takes precedence. E.g. it is not allowed to
+ * enable a DebugMetadata object, log entries to it and later set the parent.
+ *
+ * This is done to keep the path open for using other means of data transport
+ * (like tracing). For every tracing event a corresponding context needs to be
+ * available on set() time. The parent can be treated as such, the top level
+ * object (the one where enable() get's called) also lives in a place where that
+ * information is also available.
+ */
+
+/**
+ * \fn DebugMetadata::enableByControl()
+ * \brief Enable based on controls::DebugMetadataEnable in the supplied
+ * ControlList
+ * \param[in] controls The supplied ControlList
+ *
+ * This function looks for controls::DebugMetadataEnable and enables or disables
+ * debug metadata handling accordingly.
+ */
+void DebugMetadata::enableByControl(const ControlList &controls)
+{
+ const auto &ctrl = controls.get(controls::DebugMetadataEnable);
+ if (ctrl)
+ enable(*ctrl);
+}
+
+/**
+ * \fn DebugMetadata::enable()
+ * \brief Enable or disable metadata handling
+ * \param[in] enable The enable state
+ *
+ * When \a enable is true, all calls to set() get cached and can later be
+ * retrieved using moveEntries(). When \a enable is false, the cache gets
+ * cleared and no further metadata is recorded.
+ *
+ * Forwarding to a parent is independent of the enabled state.
+ */
+void DebugMetadata::enable(bool enable)
+{
+ enabled_ = enable;
+ if (!enabled_)
+ cache_.clear();
+}
+
+/**
+ * \fn DebugMetadata::setParent()
+ * \brief Set the parent metadata handler to \a parent
+ * \param[in] parent The parent handler
+ *
+ * When a \a parent is set, all further calls to set() are unconditionally
+ * forwarded to that instance.
+ *
+ * The parent can be reset by passing a nullptr.
+ */
+void DebugMetadata::setParent(DebugMetadata *parent)
+{
+ parent_ = parent;
+
+ if (!parent_)
+ return;
+
+ if (!cache_.empty())
+ LOG(DebugControls, Error)
+ << "Controls were recorded before setting a parent."
+ << " These are dropped.";
+
+ cache_.clear();
+}
+
+/**
+ * \fn DebugMetadata::moveEntries()
+ * \brief Move all cached entries into control list \a list
+ * \param[in] list The control list
+ *
+ * This function moves all entries into the list specified by \a list. Duplicate
+ * entries in \a list get overwritten.
+ */
+void DebugMetadata::moveEntries(ControlList &list)
+{
+ list.merge(std::move(cache_), ControlList::MergePolicy::OverwriteExisting);
+ cache_.clear();
+}
+
+/**
+ * \fn DebugMetadata::set(const Control<T> &ctrl, const V &value)
+ * \brief Set the value of \a ctrl to \a value
+ * \param[in] ctrl The control to set
+ * \param[in] value The control value
+ *
+ * If a parent is set, the value gets passed there unconditionally. Otherwise it
+ * gets cached if the instance is enabled or dropped silently when disabled.
+ *
+ * \sa enable()
+ */
+
+/**
+ * \fn DebugMetadata::set(unsigned int id, const ControlValue &value)
+ * \brief Set the value of control \a id to \a value
+ * \param[in] id The id of the control
+ * \param[in] value The control value
+ *
+ * If a parent is set, the value gets passed there unconditionally. Otherwise it
+ * gets cached if the instance is enabled or dropped silently when disabled.
+ *
+ * \sa enable()
+ */
+void DebugMetadata::set(unsigned int id, const ControlValue &value)
+{
+ if (parent_) {
+ parent_->set(id, value);
+ return;
+ }
+
+ if (!enabled_)
+ return;
+
+ cache_.set(id, value);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/delayed_controls.cpp b/src/libcamera/delayed_controls.cpp
index 777441e8..94d0a575 100644
--- a/src/libcamera/delayed_controls.cpp
+++ b/src/libcamera/delayed_controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * delayed_controls.h - Helper to deal with controls that take effect with a delay
+ * Helper to deal with controls that take effect with a delay
*/
#include "libcamera/internal/delayed_controls.h"
diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp
index f2e055de..ae17862f 100644
--- a/src/libcamera/device_enumerator.cpp
+++ b/src/libcamera/device_enumerator.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * device_enumerator.cpp - Enumeration and matching
+ * Enumeration and matching
*/
#include "libcamera/internal/device_enumerator.h"
@@ -56,7 +56,7 @@ LOG_DEFINE_CATEGORY(DeviceEnumerator)
* names can be added as match criteria.
*
* Pipeline handlers are recommended to add entities to DeviceMatch as
- * appropriare to ensure that the media device they need can be uniquely
+ * appropriate to ensure that the media device they need can be uniquely
* identified. This is useful when the corresponding kernel driver can produce
* different graphs, for instance as a result of different driver versions or
* hardware configurations, and not all those graphs are suitable for a pipeline
@@ -101,8 +101,14 @@ bool DeviceMatch::match(const MediaDevice *device) const
for (const MediaEntity *entity : device->entities()) {
if (name == entity->name()) {
- found = true;
- break;
+ if (!entity->deviceNode().empty()) {
+ found = true;
+ break;
+ } else {
+ LOG(DeviceEnumerator, Debug)
+ << "Skip " << entity->name()
+ << ": no device node";
+ }
}
}
diff --git a/src/libcamera/device_enumerator_sysfs.cpp b/src/libcamera/device_enumerator_sysfs.cpp
index 686bb809..7866885c 100644
--- a/src/libcamera/device_enumerator_sysfs.cpp
+++ b/src/libcamera/device_enumerator_sysfs.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * device_enumerator_sysfs.cpp - sysfs-based device enumerator
+ * sysfs-based device enumerator
*/
#include "libcamera/internal/device_enumerator_sysfs.h"
@@ -33,7 +33,7 @@ int DeviceEnumeratorSysfs::init()
int DeviceEnumeratorSysfs::enumerate()
{
struct dirent *ent;
- DIR *dir;
+ DIR *dir = nullptr;
static const char * const sysfs_dirs[] = {
"/sys/subsystem/media/devices",
diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp
index 0abc1248..4e20a3cc 100644
--- a/src/libcamera/device_enumerator_udev.cpp
+++ b/src/libcamera/device_enumerator_udev.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018-2019, Google Inc.
*
- * device_enumerator_udev.cpp - udev-based device enumerator
+ * udev-based device enumerator
*/
#include "libcamera/internal/device_enumerator_udev.h"
@@ -332,6 +332,14 @@ int DeviceEnumeratorUdev::addV4L2Device(dev_t devnum)
void DeviceEnumeratorUdev::udevNotify()
{
struct udev_device *dev = udev_monitor_receive_device(monitor_);
+ if (!dev) {
+ int err = errno;
+ LOG(DeviceEnumerator, Warning)
+ << "Ignoring notfication received without a device: "
+ << strerror(err);
+ return;
+ }
+
std::string_view action(udev_device_get_action(dev));
std::string_view deviceNode(udev_device_get_devnode(dev));
diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
new file mode 100644
index 00000000..d8c62dd6
--- /dev/null
+++ b/src/libcamera/dma_buf_allocator.cpp
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ * Copyright (C) 2020, Raspberry Pi Ltd
+ *
+ * Helper class for dma-buf allocations.
+ */
+
+#include "libcamera/internal/dma_buf_allocator.h"
+
+#include <array>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+#include <linux/udmabuf.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/memfd.h>
+#include <libcamera/base/shared_fd.h>
+
+#include <libcamera/framebuffer.h>
+
+/**
+ * \file dma_buf_allocator.cpp
+ * \brief dma-buf allocator
+ */
+
+namespace libcamera {
+
+#ifndef __DOXYGEN__
+struct DmaBufAllocatorInfo {
+ DmaBufAllocator::DmaBufAllocatorFlag type;
+ const char *deviceNodeName;
+};
+#endif
+
+static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
+ /*
+ * /dev/dma_heap/linux,cma is the CMA dma-heap. When the cma heap size is
+ * specified on the kernel command line, this gets renamed to "reserved".
+ */
+ { DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap, "/dev/dma_heap/linux,cma" },
+ { DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap, "/dev/dma_heap/reserved" },
+ { DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap, "/dev/dma_heap/system" },
+ { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
+} };
+
+LOG_DEFINE_CATEGORY(DmaBufAllocator)
+
+/**
+ * \class DmaBufAllocator
+ * \brief Helper class for dma-buf allocations
+ *
+ * This class wraps a userspace dma-buf provider selected at construction time,
+ * and exposes functions to allocate dma-buffers from this provider.
+ *
+ * Different providers may provide dma-buffers with different properties for
+ * the underlying memory. Which providers are acceptable is specified through
+ * the type argument passed to the DmaBufAllocator() constructor.
+ */
+
+/**
+ * \enum DmaBufAllocator::DmaBufAllocatorFlag
+ * \brief Type of the dma-buf provider
+ * \var DmaBufAllocator::CmaHeap
+ * \brief Allocate from a CMA dma-heap, providing physically-contiguous memory
+ * \var DmaBufAllocator::SystemHeap
+ * \brief Allocate from the system dma-heap, using the page allocator
+ * \var DmaBufAllocator::UDmaBuf
+ * \brief Allocate using a memfd + /dev/udmabuf
+ */
+
+/**
+ * \typedef DmaBufAllocator::DmaBufAllocatorFlags
+ * \brief A bitwise combination of DmaBufAllocator::DmaBufAllocatorFlag values
+ */
+
+/**
+ * \brief Construct a DmaBufAllocator of a given type
+ * \param[in] type The type(s) of the dma-buf providers to allocate from
+ *
+ * The dma-buf provider type is selected with the \a type parameter, which
+ * defaults to the CMA heap. If no provider of the given type can be accessed,
+ * the constructed DmaBufAllocator instance is invalid as indicated by
+ * the isValid() function.
+ *
+ * Multiple types can be selected by combining type flags, in which case
+ * the constructed DmaBufAllocator will match one of the types. If multiple
+ * requested types can work on the system, which provider is used is undefined.
+ */
+DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
+{
+ for (const auto &info : providerInfos) {
+ if (!(type & info.type))
+ continue;
+
+ int ret = ::open(info.deviceNodeName, O_RDWR | O_CLOEXEC, 0);
+ if (ret < 0) {
+ ret = errno;
+ LOG(DmaBufAllocator, Debug)
+ << "Failed to open " << info.deviceNodeName << ": "
+ << strerror(ret);
+ continue;
+ }
+
+ LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
+ providerHandle_ = UniqueFD(ret);
+ type_ = info.type;
+ break;
+ }
+
+ if (!providerHandle_.isValid())
+ LOG(DmaBufAllocator, Error) << "Could not open any dma-buf provider";
+}
+
+/**
+ * \brief Destroy the DmaBufAllocator instance
+ */
+DmaBufAllocator::~DmaBufAllocator() = default;
+
+/**
+ * \fn DmaBufAllocator::isValid()
+ * \brief Check if the DmaBufAllocator instance is valid
+ * \return True if the DmaBufAllocator is valid, false otherwise
+ */
+UniqueFD DmaBufAllocator::allocFromUDmaBuf(const char *name, std::size_t size)
+{
+ /* Size must be a multiple of the page size. Round it up. */
+ std::size_t pageMask = sysconf(_SC_PAGESIZE) - 1;
+ size = (size + pageMask) & ~pageMask;
+
+ /* udmabuf dma-buffers *must* have the F_SEAL_SHRINK seal. */
+ UniqueFD memfd = MemFd::create(name, size, MemFd::Seal::Shrink);
+ if (!memfd.isValid())
+ return {};
+
+ struct udmabuf_create create;
+
+ create.memfd = memfd.get();
+ create.flags = UDMABUF_FLAGS_CLOEXEC;
+ create.offset = 0;
+ create.size = size;
+
+ int ret = ::ioctl(providerHandle_.get(), UDMABUF_CREATE, &create);
+ if (ret < 0) {
+ ret = errno;
+ LOG(DmaBufAllocator, Error)
+ << "Failed to create dma buf for " << name
+ << ": " << strerror(ret);
+ return {};
+ }
+
+ /* The underlying memfd is kept as as a reference in the kernel. */
+ return UniqueFD(ret);
+}
+
+UniqueFD DmaBufAllocator::allocFromHeap(const char *name, std::size_t size)
+{
+ struct dma_heap_allocation_data alloc = {};
+ int ret;
+
+ alloc.len = size;
+ alloc.fd_flags = O_CLOEXEC | O_RDWR;
+
+ ret = ::ioctl(providerHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
+ if (ret < 0) {
+ LOG(DmaBufAllocator, Error)
+ << "dma-heap allocation failure for " << name;
+ return {};
+ }
+
+ UniqueFD allocFd(alloc.fd);
+ ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
+ if (ret < 0) {
+ LOG(DmaBufAllocator, Error)
+ << "dma-heap naming failure for " << name;
+ return {};
+ }
+
+ return allocFd;
+}
+
+/**
+ * \brief Allocate a dma-buf from the DmaBufAllocator
+ * \param [in] name The name to set for the allocated buffer
+ * \param [in] size The size of the buffer to allocate
+ *
+ * Allocates a dma-buf with read/write access.
+ *
+ * If the allocation fails, return an invalid UniqueFD.
+ *
+ * \return The UniqueFD of the allocated buffer
+ */
+UniqueFD DmaBufAllocator::alloc(const char *name, std::size_t size)
+{
+ if (!name)
+ return {};
+
+ if (type_ == DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
+ return allocFromUDmaBuf(name, size);
+ else
+ return allocFromHeap(name, size);
+}
+
+/**
+ * \brief Allocate and export buffers from the DmaBufAllocator
+ * \param[in] count The number of requested FrameBuffers
+ * \param[in] planeSizes The sizes of planes in each FrameBuffer
+ * \param[out] buffers Array of buffers successfully allocated
+ *
+ * Planes in a FrameBuffer are allocated with a single dma buf.
+ * \todo Add the option to allocate each plane with a dma buf respectively.
+ *
+ * \return The number of allocated buffers on success or a negative error code
+ * otherwise
+ */
+int DmaBufAllocator::exportBuffers(unsigned int count,
+ const std::vector<unsigned int> &planeSizes,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ for (unsigned int i = 0; i < count; ++i) {
+ std::unique_ptr<FrameBuffer> buffer =
+ createBuffer("frame-" + std::to_string(i), planeSizes);
+ if (!buffer) {
+ LOG(DmaBufAllocator, Error) << "Unable to create buffer";
+
+ buffers->clear();
+ return -EINVAL;
+ }
+
+ buffers->push_back(std::move(buffer));
+ }
+
+ return count;
+}
+
+std::unique_ptr<FrameBuffer>
+DmaBufAllocator::createBuffer(std::string name,
+ const std::vector<unsigned int> &planeSizes)
+{
+ std::vector<FrameBuffer::Plane> planes;
+
+ unsigned int frameSize = 0, offset = 0;
+ for (auto planeSize : planeSizes)
+ frameSize += planeSize;
+
+ SharedFD fd(alloc(name.c_str(), frameSize));
+ if (!fd.isValid())
+ return nullptr;
+
+ for (auto planeSize : planeSizes) {
+ planes.emplace_back(FrameBuffer::Plane{ fd, offset, planeSize });
+ offset += planeSize;
+ }
+
+ return std::make_unique<FrameBuffer>(planes);
+}
+
+/**
+ * \class DmaSyncer
+ * \brief Helper class for dma-buf's synchronization
+ *
+ * This class wraps a userspace dma-buf's synchronization process with an
+ * object's lifetime.
+ *
+ * It's used when the user needs to access a dma-buf with CPU, mostly mapped
+ * with MappedFrameBuffer, so that the buffer is synchronized between CPU and
+ * ISP.
+ */
+
+/**
+ * \enum DmaSyncer::SyncType
+ * \brief Read and/or write access via the CPU map
+ * \var DmaSyncer::Read
+ * \brief Indicates that the mapped dma-buf will be read by the client via the
+ * CPU map
+ * \var DmaSyncer::Write
+ * \brief Indicates that the mapped dm-buf will be written by the client via the
+ * CPU map
+ * \var DmaSyncer::ReadWrite
+ * \brief Indicates that the mapped dma-buf will be read and written by the
+ * client via the CPU map
+ */
+
+/**
+ * \brief Construct a DmaSyncer with a dma-buf's fd and the access type
+ * \param[in] fd The dma-buf's file descriptor to synchronize
+ * \param[in] type Read and/or write access via the CPU map
+ */
+DmaSyncer::DmaSyncer(SharedFD fd, SyncType type)
+ : fd_(fd)
+{
+ switch (type) {
+ case SyncType::Read:
+ flags_ = DMA_BUF_SYNC_READ;
+ break;
+ case SyncType::Write:
+ flags_ = DMA_BUF_SYNC_WRITE;
+ break;
+ case SyncType::ReadWrite:
+ flags_ = DMA_BUF_SYNC_RW;
+ break;
+ }
+
+ sync(DMA_BUF_SYNC_START);
+}
+
+/**
+ * \fn DmaSyncer::DmaSyncer(DmaSyncer &&other);
+ * \param[in] other The other instance
+ * \brief Enable move on class DmaSyncer
+ */
+
+/**
+ * \fn DmaSyncer::operator=(DmaSyncer &&other);
+ * \param[in] other The other instance
+ * \brief Enable move on class DmaSyncer
+ */
+
+DmaSyncer::~DmaSyncer()
+{
+ /*
+ * DmaSyncer might be moved and left with an empty SharedFD.
+ * Avoid syncing with an invalid file descriptor in this case.
+ */
+ if (fd_.isValid())
+ sync(DMA_BUF_SYNC_END);
+}
+
+void DmaSyncer::sync(uint64_t step)
+{
+ struct dma_buf_sync sync = {
+ .flags = flags_ | step
+ };
+
+ int ret;
+ do {
+ ret = ioctl(fd_.get(), DMA_BUF_IOCTL_SYNC, &sync);
+ } while (ret && (errno == EINTR || errno == EAGAIN));
+
+ if (ret) {
+ ret = errno;
+ LOG(DmaBufAllocator, Error)
+ << "Unable to sync dma fd: " << fd_.get()
+ << ", err: " << strerror(ret)
+ << ", flags: " << sync.flags;
+ }
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/fence.cpp b/src/libcamera/fence.cpp
index 7b784778..73299b40 100644
--- a/src/libcamera/fence.cpp
+++ b/src/libcamera/fence.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * fence.cpp - Synchronization fence
+ * Synchronization fence
*/
#include "libcamera/fence.h"
@@ -11,7 +11,7 @@ namespace libcamera {
/**
*
- * \file libcamera/fence.h
+ * \file fence.h
* \brief Definition of the Fence class
*/
diff --git a/src/libcamera/formats.c b/src/libcamera/formats.c
deleted file mode 100644
index e69de29b..00000000
--- a/src/libcamera/formats.c
+++ /dev/null
diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
index 447e6238..bfcdfc08 100644
--- a/src/libcamera/formats.cpp
+++ b/src/libcamera/formats.cpp
@@ -2,13 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * formats.cpp - libcamera image formats
+ * libcamera image formats
*/
#include "libcamera/internal/formats.h"
-#include <algorithm>
-#include <errno.h>
+#include <map>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
@@ -16,7 +15,7 @@
#include <libcamera/formats.h>
/**
- * \file internal/formats.h
+ * \file libcamera/internal/formats.h
* \brief Types and helper functions to handle libcamera image formats
*/
@@ -33,7 +32,7 @@ LOG_DEFINE_CATEGORY(Formats)
* used in pipeline handlers.
*
* \var PixelFormatInfo::name
- * \brief The format name as a human-readable string, used as the test
+ * \brief The format name as a human-readable string, used as the text
* representation of the PixelFormat
*
* \var PixelFormatInfo::format
@@ -49,9 +48,9 @@ LOG_DEFINE_CATEGORY(Formats)
* \var PixelFormatInfo::bitsPerPixel
* \brief The average number of bits per pixel
*
- * The number per pixel averages the total number of bits for all colour
- * components over the whole image, excluding any padding bits or padding
- * pixels.
+ * The number of bits per pixel averages the total number of bits for all
+ * colour components over the whole image, excluding any padding bits or
+ * padding pixels.
*
* For formats that store pixels with bit padding within words, only the
* effective bits are taken into account. For instance, 12-bit Bayer data
@@ -158,7 +157,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
- .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::RGB565_BE, {
.name = "RGB565_BE",
@@ -168,7 +167,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
- .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::BGR888, {
.name = "BGR888",
@@ -270,6 +269,26 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
+ { formats::BGR161616, {
+ .name = "BGR161616",
+ .format = formats::BGR161616,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_RGB48), },
+ .bitsPerPixel = 48,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ .packed = false,
+ .pixelsPerGroup = 1,
+ .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::RGB161616, {
+ .name = "RGB161616",
+ .format = formats::RGB161616,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_BGR48), },
+ .bitsPerPixel = 48,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ .packed = false,
+ .pixelsPerGroup = 1,
+ .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
/* YUV packed formats. */
{ formats::YUYV, {
@@ -497,6 +516,26 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.pixelsPerGroup = 1,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
+ { formats::R10_CSI2P, {
+ .name = "R10_CSI2P",
+ .format = formats::R10_CSI2P,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_Y10P), },
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ .packed = true,
+ .pixelsPerGroup = 4,
+ .planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::R12_CSI2P, {
+ .name = "R12_CSI2P",
+ .format = formats::R12_CSI2P,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_Y12P), },
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ .packed = true,
+ .pixelsPerGroup = 2,
+ .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
{ formats::R12, {
.name = "R12",
.format = formats::R12,
@@ -507,15 +546,25 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.pixelsPerGroup = 1,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
- { formats::R10_CSI2P, {
- .name = "R10_CSI2P",
- .format = formats::R10_CSI2P,
- .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_Y10P), },
- .bitsPerPixel = 10,
+ { formats::R16, {
+ .name = "R16",
+ .format = formats::R16,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_Y16), },
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ .packed = false,
+ .pixelsPerGroup = 1,
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::MONO_PISP_COMP1, {
+ .name = "MONO_PISP_COMP1",
+ .format = formats::MONO_PISP_COMP1,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_MONO), },
+ .bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = true,
- .pixelsPerGroup = 4,
- .planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
+ .pixelsPerGroup = 1,
+ .planes = {{ { 1, 1 }, { 0, 0 }, { 0, 0 } }},
} },
/* Bayer formats. */
@@ -880,7 +929,46 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
.pixelsPerGroup = 25,
.planes = {{ { 32, 1 }, { 0, 0 }, { 0, 0 } }},
} },
-
+ { formats::BGGR_PISP_COMP1, {
+ .name = "BGGR_PISP_COMP1",
+ .format = formats::BGGR_PISP_COMP1,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_BGGR), },
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ .packed = true,
+ .pixelsPerGroup = 2,
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::GBRG_PISP_COMP1, {
+ .name = "GBRG_PISP_COMP1",
+ .format = formats::GBRG_PISP_COMP1,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GBRG), },
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ .packed = true,
+ .pixelsPerGroup = 2,
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::GRBG_PISP_COMP1, {
+ .name = "GRBG_PISP_COMP1",
+ .format = formats::GRBG_PISP_COMP1,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GRBG), },
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ .packed = true,
+ .pixelsPerGroup = 2,
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
+ { formats::RGGB_PISP_COMP1, {
+ .name = "RGGB_PISP_COMP1",
+ .format = formats::RGGB_PISP_COMP1,
+ .v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_RGGB), },
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ .packed = true,
+ .pixelsPerGroup = 2,
+ .planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
+ } },
/* Compressed formats. */
{ formats::MJPEG, {
.name = "MJPEG",
@@ -987,7 +1075,7 @@ unsigned int PixelFormatInfo::stride(unsigned int width, unsigned int plane,
return 0;
}
- if (plane > planes.size() || !planes[plane].bytesPerGroup) {
+ if (plane >= planes.size() || !planes[plane].bytesPerGroup) {
LOG(Formats, Warning) << "Invalid plane index, stride is zero";
return 0;
}
diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml
index 539ac0b3..2d54d391 100644
--- a/src/libcamera/formats.yaml
+++ b/src/libcamera/formats.yaml
@@ -11,6 +11,8 @@ formats:
fourcc: DRM_FORMAT_R10
- R12:
fourcc: DRM_FORMAT_R12
+ - R16:
+ fourcc: DRM_FORMAT_R16
- RGB565:
fourcc: DRM_FORMAT_RGB565
@@ -41,6 +43,11 @@ formats:
- BGRA8888:
fourcc: DRM_FORMAT_BGRA8888
+ - RGB161616:
+ fourcc: DRM_FORMAT_RGB161616
+ - BGR161616:
+ fourcc: DRM_FORMAT_BGR161616
+
- YUYV:
fourcc: DRM_FORMAT_YUYV
- YVYU:
@@ -131,6 +138,9 @@ formats:
- R10_CSI2P:
fourcc: DRM_FORMAT_R10
mod: MIPI_FORMAT_MOD_CSI2_PACKED
+ - R12_CSI2P:
+ fourcc: DRM_FORMAT_R12
+ mod: MIPI_FORMAT_MOD_CSI2_PACKED
- SRGGB10_CSI2P:
fourcc: DRM_FORMAT_SRGGB10
@@ -183,4 +193,20 @@ formats:
- SBGGR10_IPU3:
fourcc: DRM_FORMAT_SBGGR10
mod: IPU3_FORMAT_MOD_PACKED
+
+ - RGGB_PISP_COMP1:
+ fourcc: DRM_FORMAT_SRGGB16
+ mod: PISP_FORMAT_MOD_COMPRESS_MODE1
+ - GRBG_PISP_COMP1:
+ fourcc: DRM_FORMAT_SGRBG16
+ mod: PISP_FORMAT_MOD_COMPRESS_MODE1
+ - GBRG_PISP_COMP1:
+ fourcc: DRM_FORMAT_SGBRG16
+ mod: PISP_FORMAT_MOD_COMPRESS_MODE1
+ - BGGR_PISP_COMP1:
+ fourcc: DRM_FORMAT_SBGGR16
+ mod: PISP_FORMAT_MOD_COMPRESS_MODE1
+ - MONO_PISP_COMP1:
+ fourcc: DRM_FORMAT_R16
+ mod: PISP_FORMAT_MOD_COMPRESS_MODE1
...
diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
index 5a7f3c0b..826848f7 100644
--- a/src/libcamera/framebuffer.cpp
+++ b/src/libcamera/framebuffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * framebuffer.cpp - Frame buffer handling
+ * Frame buffer handling
*/
#include <libcamera/framebuffer.h>
@@ -16,7 +16,10 @@
/**
* \file libcamera/framebuffer.h
* \brief Frame buffer handling
- *
+ */
+
+/**
+ * \internal
* \file libcamera/internal/framebuffer.h
* \brief Internal frame buffer handling support
*/
@@ -104,6 +107,7 @@ LOG_DEFINE_CATEGORY(Buffer)
* \return The array of per-plane metadata
*/
+#ifndef __DOXYGEN_PUBLIC__
/**
* \class FrameBuffer::Private
* \brief Base class for FrameBuffer private data
@@ -206,6 +210,7 @@ FrameBuffer::Private::~Private()
* \brief Retrieve the dynamic metadata
* \return Dynamic metadata for the frame contained in the buffer
*/
+#endif /* __DOXYGEN_PUBLIC__ */
/**
* \class FrameBuffer
diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp
index dabd9219..3d53bde2 100644
--- a/src/libcamera/framebuffer_allocator.cpp
+++ b/src/libcamera/framebuffer_allocator.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * framebuffer_allocator.cpp - FrameBuffer allocator
+ * FrameBuffer allocator
*/
#include <libcamera/framebuffer_allocator.h>
@@ -59,14 +59,11 @@ LOG_DEFINE_CATEGORY(Allocator)
* \param[in] camera The camera
*/
FrameBufferAllocator::FrameBufferAllocator(std::shared_ptr<Camera> camera)
- : camera_(camera)
+ : camera_(std::move(camera))
{
}
-FrameBufferAllocator::~FrameBufferAllocator()
-{
- buffers_.clear();
-}
+FrameBufferAllocator::~FrameBufferAllocator() = default;
/**
* \brief Allocate buffers for a configured stream
@@ -100,6 +97,10 @@ int FrameBufferAllocator::allocate(Stream *stream)
LOG(Allocator, Error)
<< "Stream is not part of " << camera_->id()
<< " active configuration";
+
+ if (ret < 0)
+ buffers_.erase(it);
+
return ret;
}
@@ -121,8 +122,6 @@ int FrameBufferAllocator::free(Stream *stream)
if (iter == buffers_.end())
return -EINVAL;
- std::vector<std::unique_ptr<FrameBuffer>> &buffers = iter->second;
- buffers.clear();
buffers_.erase(iter);
return 0;
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index e50b46c5..81cc8cd5 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * geometry.cpp - Geometry-related structures
+ * Geometry-related structures
*/
#include <libcamera/geometry.h>
@@ -95,10 +95,10 @@ std::ostream &operator<<(std::ostream &out, const Point &p)
}
/**
- * \struct Size
+ * \class Size
* \brief Describe a two-dimensional size
*
- * The Size structure defines a two-dimensional size with integer precision.
+ * The Size class defines a two-dimensional size with integer precision.
*/
/**
@@ -455,7 +455,7 @@ std::ostream &operator<<(std::ostream &out, const Size &s)
}
/**
- * \struct SizeRange
+ * \class SizeRange
* \brief Describe a range of sizes
*
* A SizeRange describes a range of sizes included in the [min, max] interval
@@ -589,11 +589,13 @@ std::ostream &operator<<(std::ostream &out, const SizeRange &sr)
}
/**
- * \struct Rectangle
+ * \class Rectangle
* \brief Describe a rectangle's position and dimensions
*
* Rectangles are used to identify an area of an image. They are specified by
* the coordinates of top-left corner and their horizontal and vertical size.
+ * By convention, the top-left corner is defined as the corner with the lowest
+ * x and y coordinates, regardless of the origin and direction of the axes.
*
* The measure unit of the rectangle coordinates and size, as well as the
* reference point from which the Rectangle::x and Rectangle::y displacements
@@ -611,6 +613,8 @@ std::ostream &operator<<(std::ostream &out, const SizeRange &sr)
* \param[in] x The horizontal coordinate of the top-left corner
* \param[in] y The vertical coordinate of the top-left corner
* \param[in] size The size
+ *
+ * The rectangle's top-left corner is the point with the smaller x and y values.
*/
/**
@@ -620,6 +624,8 @@ std::ostream &operator<<(std::ostream &out, const SizeRange &sr)
* \param[in] y The vertical coordinate of the top-left corner
* \param[in] width The width
* \param[in] height The height
+ *
+ * The rectangle's top-left corner is the point with the smaller x and y values.
*/
/**
@@ -630,13 +636,24 @@ std::ostream &operator<<(std::ostream &out, const SizeRange &sr)
*/
/**
+ * \fn Rectangle::Rectangle(const Point &point1, const Point &point2)
+ * \brief Construct a Rectangle from two opposite corners
+ * \param[in] point1 One of corners of the rectangle
+ * \param[in] point2 The opposite corner of \a point1
+ */
+
+/**
* \var Rectangle::x
* \brief The horizontal coordinate of the rectangle's top-left corner
+ *
+ * The rectangle's top-left corner is the point with the smaller x and y values.
*/
/**
* \var Rectangle::y
* \brief The vertical coordinate of the rectangle's top-left corner
+ *
+ * The rectangle's top-left corner is the point with the smaller x and y values.
*/
/**
@@ -685,6 +702,9 @@ Point Rectangle::center() const
/**
* \fn Point Rectangle::topLeft() const
* \brief Retrieve the coordinates of the top left corner of this Rectangle
+ *
+ * The rectangle's top-left corner is the point with the smaller x and y values.
+ *
* \return The Rectangle's top left corner
*/
@@ -818,6 +838,55 @@ Rectangle Rectangle::translatedBy(const Point &point) const
}
/**
+ * \brief Transform a Rectangle from one reference rectangle to another
+ * \param[in] source The \a source reference rectangle
+ * \param[in] destination The \a destination reference rectangle
+ *
+ * The \a source and \a destination parameters describe two rectangles defined
+ * in different reference systems. The Rectangle is translated from the source
+ * reference system into the destination reference system.
+ *
+ * The typical use case for this function is to translate a selection rectangle
+ * specified in a reference system, in example the sensor's pixel array, into
+ * the same rectangle re-scaled and translated into a different reference
+ * system, in example the output frame on which the selection rectangle is
+ * applied to.
+ *
+ * For example, consider a sensor with a resolution of 4040x2360 pixels and a
+ * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
+ * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
+ * display coordinates. This function can be used to transform an arbitrary
+ * rectangle from display coordinates to sensor coordinates or vice versa:
+ *
+ * \code{.cpp}
+ * Rectangle sensorReference(100, 100, 3840, 2160);
+ * Rectangle displayReference(0, 0, 1920, 1080);
+ *
+ * // Bottom right quarter in sensor coordinates
+ * Rectangle sensorRect(2020, 100, 1920, 1080);
+ * displayRect = sensorRect.transformedBetween(sensorReference, displayReference);
+ * // displayRect is now (960, 540)/960x540
+ *
+ * // Transformation back to sensor coordinates
+ * sensorRect = displayRect.transformedBetween(displayReference, sensorReference);
+ * \endcode
+ */
+Rectangle Rectangle::transformedBetween(const Rectangle &source,
+ const Rectangle &destination) const
+{
+ Rectangle r;
+ double sx = static_cast<double>(destination.width) / source.width;
+ double sy = static_cast<double>(destination.height) / source.height;
+
+ r.x = static_cast<int>((x - source.x) * sx) + destination.x;
+ r.y = static_cast<int>((y - source.y) * sy) + destination.y;
+ r.width = static_cast<int>(width * sx);
+ r.height = static_cast<int>(height * sy);
+
+ return r;
+}
+
+/**
* \brief Compare rectangles for equality
* \return True if the two rectangles are equal, false otherwise
*/
diff --git a/src/libcamera/ipa/meson.build b/src/libcamera/ipa/meson.build
index 44695240..ef73b3f9 100644
--- a/src/libcamera/ipa/meson.build
+++ b/src/libcamera/ipa/meson.build
@@ -3,13 +3,10 @@
libcamera_ipa_interfaces = []
foreach file : ipa_mojom_files
- name = '@0@'.format(file).split('/')[-1].split('.')[0]
-
# {pipeline}_ipa_interface.cpp
libcamera_ipa_interfaces += \
- custom_target(name + '_ipa_interface_cpp',
- input : file,
- output : name + '_ipa_interface.cpp',
+ custom_target(input : file,
+ output : '@BASENAME@_ipa_interface.cpp',
command : [
mojom_docs_extractor,
'-o', '@OUTPUT@', '@INPUT@'
diff --git a/src/libcamera/ipa_controls.cpp b/src/libcamera/ipa_controls.cpp
index 870a443b..12d92ebe 100644
--- a/src/libcamera/ipa_controls.cpp
+++ b/src/libcamera/ipa_controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_controls.cpp - IPA control handling
+ * IPA control handling
*/
#include <libcamera/ipa/ipa_controls.h>
@@ -220,6 +220,10 @@ static_assert(sizeof(ipa_control_value_entry) == 16,
* \var ipa_control_info_entry::offset
* The offset in bytes from the beginning of the data section to the control
* info data (shall be a multiple of 8 bytes)
+ * \var ipa_control_info_entry::direction
+ * The directions in which the control is allowed to be sent. This is a flags
+ * value, where 0x1 signifies input (as controls), and 0x2 signifies output (as
+ * metadata). \sa ControlId::Direction
* \var ipa_control_info_entry::padding
* Padding bytes (shall be set to 0)
*/
diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
index 0a259305..0537f785 100644
--- a/src/libcamera/ipa_data_serializer.cpp
+++ b/src/libcamera/ipa_data_serializer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
+ * Image Processing Algorithm data serializer
*/
#include "libcamera/internal/ipa_data_serializer.h"
@@ -11,6 +11,8 @@
#include <libcamera/base/log.h>
+#include "libcamera/internal/byte_stream_buffer.h"
+
/**
* \file ipa_data_serializer.h
* \brief IPA Data Serializer
@@ -194,7 +196,6 @@ IPADataSerializer<type>::serialize(const type &data, \
[[maybe_unused]] ControlSerializer *cs) \
{ \
std::vector<uint8_t> dataVec; \
- dataVec.reserve(sizeof(type)); \
appendPOD<type>(dataVec, data); \
\
return { dataVec, {} }; \
@@ -537,7 +538,6 @@ IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
if (data.isValid())
fdVec.push_back(data);
-
return { dataVec, fdVec };
}
@@ -604,7 +604,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
FrameBuffer::Plane ret;
ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
- fdsBegin, fdsBegin + 1);
+ fdsBegin, fdsBegin + 1);
ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
diff --git a/src/libcamera/ipa_interface.cpp b/src/libcamera/ipa_interface.cpp
index 8ea6cbee..a9dc54ad 100644
--- a/src/libcamera/ipa_interface.cpp
+++ b/src/libcamera/ipa_interface.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_interface.cpp - Image Processing Algorithm interface
+ * Image Processing Algorithm interface
*/
#include <libcamera/ipa/ipa_interface.h>
diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp
index 7a4515d9..830750dc 100644
--- a/src/libcamera/ipa_manager.cpp
+++ b/src/libcamera/ipa_manager.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_manager.cpp - Image Processing Algorithm module manager
+ * Image Processing Algorithm module manager
*/
#include "libcamera/internal/ipa_manager.h"
@@ -95,8 +95,6 @@ LOG_DEFINE_CATEGORY(IPAManager)
* IPC.
*/
-IPAManager *IPAManager::self_ = nullptr;
-
/**
* \brief Construct an IPAManager instance
*
@@ -105,10 +103,6 @@ IPAManager *IPAManager::self_ = nullptr;
*/
IPAManager::IPAManager()
{
- if (self_)
- LOG(IPAManager, Fatal)
- << "Multiple IPAManager objects are not allowed";
-
#if HAVE_IPA_PUBKEY
if (!pubKey_.isValid())
LOG(IPAManager, Warning) << "Public key not valid";
@@ -153,17 +147,9 @@ IPAManager::IPAManager()
if (!ipaCount)
LOG(IPAManager, Warning)
<< "No IPA found in '" IPA_MODULE_DIR "'";
-
- self_ = this;
}
-IPAManager::~IPAManager()
-{
- for (IPAModule *module : modules_)
- delete module;
-
- self_ = nullptr;
-}
+IPAManager::~IPAManager() = default;
/**
* \brief Identify shared library objects within a directory
@@ -236,15 +222,13 @@ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth)
unsigned int count = 0;
for (const std::string &file : files) {
- IPAModule *ipaModule = new IPAModule(file);
- if (!ipaModule->isValid()) {
- delete ipaModule;
+ auto ipaModule = std::make_unique<IPAModule>(file);
+ if (!ipaModule->isValid())
continue;
- }
LOG(IPAManager, Debug) << "Loaded IPA module '" << file << "'";
- modules_.push_back(ipaModule);
+ modules_.push_back(std::move(ipaModule));
count++;
}
@@ -260,9 +244,9 @@ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth)
IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,
uint32_t maxVersion)
{
- for (IPAModule *module : modules_) {
+ for (const auto &module : modules_) {
if (module->match(pipe, minVersion, maxVersion))
- return module;
+ return module.get();
}
return nullptr;
diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp
index f2dd87e5..e6ea61e4 100644
--- a/src/libcamera/ipa_module.cpp
+++ b/src/libcamera/ipa_module.cpp
@@ -2,13 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_module.cpp - Image Processing Algorithm module
+ * Image Processing Algorithm module
*/
#include "libcamera/internal/ipa_module.h"
#include <algorithm>
-#include <array>
#include <ctype.h>
#include <dlfcn.h>
#include <elf.h>
@@ -51,8 +50,8 @@ typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf,
if (size > elf.size() || size < objSize)
return nullptr;
- return reinterpret_cast<typename std::remove_extent_t<T> *>
- (reinterpret_cast<const char *>(elf.data()) + offset);
+ return reinterpret_cast<typename std::remove_extent_t<T> *>(
+ reinterpret_cast<const char *>(elf.data()) + offset);
}
template<typename T>
@@ -374,7 +373,7 @@ const struct IPAModuleInfo &IPAModule::info() const
*
* \return The IPA module signature
*/
-const std::vector<uint8_t> IPAModule::signature() const
+const std::vector<uint8_t> &IPAModule::signature() const
{
return signature_;
}
diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp
index 3f2cc6b8..9907b961 100644
--- a/src/libcamera/ipa_proxy.cpp
+++ b/src/libcamera/ipa_proxy.cpp
@@ -2,12 +2,11 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipa_proxy.cpp - Image Processing Algorithm proxy
+ * Image Processing Algorithm proxy
*/
#include "libcamera/internal/ipa_proxy.h"
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -72,6 +71,7 @@ IPAProxy::~IPAProxy()
/**
* \brief Retrieve the absolute path to an IPA configuration file
* \param[in] name The configuration file name
+ * \param[in] fallbackName The name of a fallback configuration file
*
* This function locates the configuration file for an IPA and returns its
* absolute path. It searches the following directories, in order:
@@ -89,21 +89,42 @@ IPAProxy::~IPAProxy()
* named after the IPA module name, as reported in IPAModuleInfo::name, and for
* a file named \a name within that directory. The \a name is IPA-specific.
*
+ * If the file named \a name is not found and \a fallbackName is non-empty then
+ * the whole search is repeated for \a fallbackName.
+ *
* \return The full path to the IPA configuration file, or an empty string if
* no configuration file can be found
*/
-std::string IPAProxy::configurationFile(const std::string &name) const
+std::string IPAProxy::configurationFile(const std::string &name,
+ const std::string &fallbackName) const
{
- struct stat statbuf;
- int ret;
-
/*
* The IPA module name can be used as-is to build directory names as it
* has been validated when loading the module.
*/
- std::string ipaName = ipam_->info().name;
+ const std::string ipaName = ipam_->info().name;
+
+ /*
+ * Start with any user override through the module-specific environment
+ * variable. Use the name of the IPA module up to the first '/' to
+ * construct the variable name.
+ */
+ std::string ipaEnvName = ipaName.substr(0, ipaName.find('/'));
+ std::transform(ipaEnvName.begin(), ipaEnvName.end(), ipaEnvName.begin(),
+ [](unsigned char c) { return std::toupper(c); });
+ ipaEnvName = "LIBCAMERA_" + ipaEnvName + "_TUNING_FILE";
+
+ char const *configFromEnv = utils::secure_getenv(ipaEnvName.c_str());
+ if (configFromEnv && *configFromEnv != '\0')
+ return { configFromEnv };
+
+ struct stat statbuf;
+ int ret;
- /* Check the environment variable first. */
+ /*
+ * Check the directory pointed to by the IPA config path environment
+ * variable next.
+ */
const char *confPaths = utils::secure_getenv("LIBCAMERA_IPA_CONFIG_PATH");
if (confPaths) {
for (const auto &dir : utils::split(confPaths, ":")) {
@@ -146,11 +167,18 @@ std::string IPAProxy::configurationFile(const std::string &name) const
}
}
- LOG(IPAProxy, Error)
- << "Configuration file '" << name
- << "' not found for IPA module '" << ipaName << "'";
+ if (fallbackName.empty()) {
+ LOG(IPAProxy, Error)
+ << "Configuration file '" << name
+ << "' not found for IPA module '" << ipaName << "'";
+ return std::string();
+ }
- return std::string();
+ LOG(IPAProxy, Warning)
+ << "Configuration file '" << name
+ << "' not found for IPA module '" << ipaName
+ << "', falling back to '" << fallbackName << "'";
+ return configurationFile(fallbackName);
}
/**
diff --git a/src/libcamera/ipa_pub_key.cpp.in b/src/libcamera/ipa_pub_key.cpp.in
index 01e5333b..5d8c92c2 100644
--- a/src/libcamera/ipa_pub_key.cpp.in
+++ b/src/libcamera/ipa_pub_key.cpp.in
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
- * ipa_pub_key.cpp - IPA module signing public key
+ * IPA module signing public key
*
* This file is auto-generated. Do not edit.
*/
diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
index 31a0ca09..548299d0 100644
--- a/src/libcamera/ipc_pipe.cpp
+++ b/src/libcamera/ipc_pipe.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * ipc_pipe.cpp - Image Processing Algorithm IPC module for IPA proxies
+ * Image Processing Algorithm IPC module for IPA proxies
*/
#include "libcamera/internal/ipc_pipe.h"
diff --git a/src/libcamera/ipc_pipe_unixsocket.cpp b/src/libcamera/ipc_pipe_unixsocket.cpp
index da2cffc3..668ec73b 100644
--- a/src/libcamera/ipc_pipe_unixsocket.cpp
+++ b/src/libcamera/ipc_pipe_unixsocket.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * ipc_pipe_unixsocket.cpp - Image Processing Algorithm IPC module using unix socket
+ * Image Processing Algorithm IPC module using unix socket
*/
#include "libcamera/internal/ipc_pipe_unixsocket.h"
diff --git a/src/libcamera/ipc_unixsocket.cpp b/src/libcamera/ipc_unixsocket.cpp
index 1980d374..002053e3 100644
--- a/src/libcamera/ipc_unixsocket.cpp
+++ b/src/libcamera/ipc_unixsocket.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipc_unixsocket.cpp - IPC mechanism based on Unix sockets
+ * IPC mechanism based on Unix sockets
*/
#include "libcamera/internal/ipc_unixsocket.h"
@@ -12,6 +12,7 @@
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
+#include <vector>
#include <libcamera/base/event_notifier.h>
#include <libcamera/base/log.h>
@@ -247,10 +248,9 @@ int IPCUnixSocket::sendData(const void *buffer, size_t length,
iov[0].iov_base = const_cast<void *>(buffer);
iov[0].iov_len = length;
- char buf[CMSG_SPACE(num * sizeof(uint32_t))];
- memset(buf, 0, sizeof(buf));
+ std::vector<uint8_t> buf(CMSG_SPACE(num * sizeof(uint32_t)));
- struct cmsghdr *cmsg = (struct cmsghdr *)buf;
+ struct cmsghdr *cmsg = reinterpret_cast<struct cmsghdr *>(buf.data());
cmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
@@ -283,10 +283,9 @@ int IPCUnixSocket::recvData(void *buffer, size_t length,
iov[0].iov_base = buffer;
iov[0].iov_len = length;
- char buf[CMSG_SPACE(num * sizeof(uint32_t))];
- memset(buf, 0, sizeof(buf));
+ std::vector<uint8_t> buf(CMSG_SPACE(num * sizeof(uint32_t)));
- struct cmsghdr *cmsg = (struct cmsghdr *)buf;
+ struct cmsghdr *cmsg = reinterpret_cast<struct cmsghdr *>(buf.data());
cmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
diff --git a/src/libcamera/mapped_framebuffer.cpp b/src/libcamera/mapped_framebuffer.cpp
index 6860069b..f54bbf21 100644
--- a/src/libcamera/mapped_framebuffer.cpp
+++ b/src/libcamera/mapped_framebuffer.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * mapped_framebuffer.cpp - Mapped Framebuffer support
+ * Mapped Framebuffer support
*/
#include "libcamera/internal/mapped_framebuffer.h"
@@ -16,7 +16,7 @@
#include <libcamera/base/log.h>
/**
- * \file libcamera/internal/mapped_framebuffer.h
+ * \file mapped_framebuffer.h
* \brief Frame buffer memory mapping support
*/
@@ -72,7 +72,7 @@ MappedBuffer::MappedBuffer(MappedBuffer &&other)
/**
* \brief Move assignment operator, replace the mappings with those of \a other
-* \param[in] other The other MappedBuffer
+ * \param[in] other The other MappedBuffer
*
* Moving a MappedBuffer moves the mappings contained in the \a other to the new
* MappedBuffer and invalidates the \a other.
diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp
new file mode 100644
index 00000000..b7c07e89
--- /dev/null
+++ b/src/libcamera/matrix.cpp
@@ -0,0 +1,333 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Matrix and related operations
+ */
+
+#include "libcamera/internal/matrix.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file matrix.h
+ * \brief Matrix class
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Matrix)
+
+/**
+ * \class Matrix
+ * \brief Matrix class
+ * \tparam T Type of numerical values to be stored in the matrix
+ * \tparam Rows Number of rows in the matrix
+ * \tparam Cols Number of columns in the matrix
+ */
+
+/**
+ * \fn Matrix::Matrix()
+ * \brief Construct a zero matrix
+ */
+
+/**
+ * \fn Matrix::Matrix(const std::array<T, Rows * Cols> &data)
+ * \brief Construct a matrix from supplied data
+ * \param[in] data Data from which to construct a matrix
+ *
+ * \a data is a one-dimensional vector and will be turned into a matrix in
+ * row-major order. The size of \a data must be equal to the product of the
+ * number of rows and columns of the matrix (Rows x Cols).
+ */
+
+/**
+ * \fn Matrix::Matrix(const Span<const T, Rows * Cols> data)
+ * \brief Construct a matrix from supplied data
+ * \param[in] data Data from which to construct a matrix
+ *
+ * \a data is a one-dimensional Span and will be turned into a matrix in
+ * row-major order. The size of \a data must be equal to the product of the
+ * number of rows and columns of the matrix (Rows x Cols).
+ */
+
+/**
+ * \fn Matrix::identity()
+ * \brief Construct an identity matrix
+ */
+
+/**
+ * \fn Matrix::toString()
+ * \brief Assemble and return a string describing the matrix
+ * \return A string describing the matrix
+ */
+
+/**
+ * \fn Matrix::data()
+ * \brief Access the matrix data as a linear array
+ *
+ * Access the contents of the matrix as a one-dimensional linear array of
+ * values in row-major order. The size of the array is equal to the product of
+ * the number of rows and columns of the matrix (Rows x Cols).
+ *
+ * \return A span referencing the matrix data as a linear array
+ */
+
+/**
+ * \fn Span<const T, Cols> Matrix::operator[](size_t i) const
+ * \brief Index to a row in the matrix
+ * \param[in] i Index of row to retrieve
+ *
+ * This operator[] returns a Span, which can then be indexed into again with
+ * another operator[], allowing a convenient m[i][j] to access elements of the
+ * matrix. Note that the lifetime of the Span returned by this first-level
+ * operator[] is bound to that of the Matrix itself, so it is not recommended
+ * to save the Span that is the result of this operator[].
+ *
+ * \return Row \a i from the matrix, as a Span
+ */
+
+/**
+ * \fn Matrix::inverse(bool *ok) const
+ * \param[out] ok Indicate if the matrix was successfully inverted
+ * \brief Compute the inverse of the matrix
+ *
+ * This function computes the inverse of the matrix. It is only implemented for
+ * matrices of float and double types. If \a ok is provided it will be set to a
+ * boolean value to indicate of the inversion was successful. This can be used
+ * to check if the matrix is singular, in which case the function will return
+ * an identity matrix.
+ *
+ * \return The inverse of the matrix
+ */
+
+/**
+ * \fn Matrix::operator[](size_t i)
+ * \copydoc Matrix::operator[](size_t i) const
+ */
+
+/**
+ * \fn Matrix<T, Rows, Cols> &Matrix::operator*=(U d)
+ * \brief Multiply the matrix by a scalar in-place
+ * \tparam U Type of the numerical scalar value
+ * \param d The scalar multiplier
+ * \return Product of this matrix and scalar \a d
+ */
+
+/**
+ * \fn Matrix::Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)
+ * \brief Multiply the matrix by a scalar
+ * \tparam T Type of the numerical scalar value
+ * \tparam U Type of numerical values in the matrix
+ * \tparam Rows Number of rows in the matrix
+ * \tparam Cols Number of columns in the matrix
+ * \param d The scalar multiplier
+ * \param m The matrix
+ * \return Product of scalar \a d and matrix \a m
+ */
+
+/**
+ * \fn Matrix::Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)
+ * \copydoc operator*(T d, const Matrix<U, Rows, Cols> &m)
+ */
+
+/**
+ * \fn operator*(const Matrix<T1, R1, C1> &m1, const Matrix<T2, R2, C2> &m2)
+ * \brief Matrix multiplication
+ * \tparam T1 Type of numerical values in the first matrix
+ * \tparam R1 Number of rows in the first matrix
+ * \tparam C1 Number of columns in the first matrix
+ * \tparam T2 Type of numerical values in the secont matrix
+ * \tparam R2 Number of rows in the second matrix
+ * \tparam C2 Number of columns in the second matrix
+ * \param m1 Multiplicand matrix
+ * \param m2 Multiplier matrix
+ * \return Matrix product of matrices \a m1 and \a m2
+ */
+
+/**
+ * \fn Matrix<T, Rows, Cols> operator+(const Matrix<T, Rows, Cols> &m1, const Matrix<T, Rows, Cols> &m2)
+ * \brief Matrix addition
+ * \tparam T Type of numerical values in the matrices
+ * \tparam Rows Number of rows in the matrices
+ * \tparam Cols Number of columns in the matrices
+ * \param m1 Summand matrix
+ * \param m2 Summand matrix
+ * \return Matrix sum of matrices \a m1 and \a m2
+ */
+
+#ifndef __DOXYGEN__
+template<typename T>
+bool matrixInvert(Span<const T> dataIn, Span<T> dataOut, unsigned int dim,
+ Span<T> scratchBuffer, Span<unsigned int> swapBuffer)
+{
+ /*
+ * Convenience class to access matrix data, providing a row-major (i,j)
+ * element accessor through the call operator, and the ability to swap
+ * rows without modifying the backing storage.
+ */
+ class MatrixAccessor
+ {
+ public:
+ MatrixAccessor(Span<T> data, Span<unsigned int> swapBuffer, unsigned int rows, unsigned int cols)
+ : data_(data), swap_(swapBuffer), rows_(rows), cols_(cols)
+ {
+ ASSERT(swap_.size() == rows);
+ std::iota(swap_.begin(), swap_.end(), T{ 0 });
+ }
+
+ T &operator()(unsigned int row, unsigned int col)
+ {
+ assert(row < rows_ && col < cols_);
+ return data_[index(row, col)];
+ }
+
+ void swap(unsigned int a, unsigned int b)
+ {
+ assert(a < rows_ && a < cols_);
+ std::swap(swap_[a], swap_[b]);
+ }
+
+ private:
+ unsigned int index(unsigned int row, unsigned int col) const
+ {
+ return swap_[row] * cols_ + col;
+ }
+
+ Span<T> data_;
+ Span<unsigned int> swap_;
+ unsigned int rows_;
+ unsigned int cols_;
+ };
+
+ /*
+ * Matrix inversion using Gaussian elimination.
+ *
+ * Start by augmenting the original matrix with an identiy matrix of
+ * the same size.
+ */
+ ASSERT(scratchBuffer.size() == dim * dim * 2);
+ MatrixAccessor matrix(scratchBuffer, swapBuffer, dim, dim * 2);
+
+ for (unsigned int i = 0; i < dim; ++i) {
+ for (unsigned int j = 0; j < dim; ++j) {
+ matrix(i, j) = dataIn[i * dim + j];
+ matrix(i, j + dim) = T{ 0 };
+ }
+ matrix(i, i + dim) = T{ 1 };
+ }
+
+ /* Start by triangularizing the input . */
+ for (unsigned int pivot = 0; pivot < dim; ++pivot) {
+ /*
+ * Locate the next pivot. To improve numerical stability, use
+ * the row with the largest value in the pivot's column.
+ */
+ unsigned int row = pivot;
+ T maxValue{ 0 };
+
+ for (unsigned int i = pivot; i < dim; ++i) {
+ T value = std::abs(matrix(i, pivot));
+ if (maxValue < value) {
+ maxValue = value;
+ row = i;
+ }
+ }
+
+ /*
+ * If no pivot is found in the column, the matrix is not
+ * invertible. Return an identity matrix.
+ */
+ if (maxValue == 0) {
+ std::fill(dataOut.begin(), dataOut.end(), T{ 0 });
+ for (unsigned int i = 0; i < dim; ++i)
+ dataOut[i * dim + i] = T{ 1 };
+ return false;
+ }
+
+ /* Swap rows to bring the pivot in the right location. */
+ matrix.swap(pivot, row);
+
+ /* Process all rows below the pivot to zero the pivot column. */
+ const T pivotValue = matrix(pivot, pivot);
+
+ for (unsigned int i = pivot + 1; i < dim; ++i) {
+ const T factor = matrix(i, pivot) / pivotValue;
+
+ /*
+ * We know the element in the pivot column will be 0,
+ * hardcode it instead of computing it.
+ */
+ matrix(i, pivot) = T{ 0 };
+
+ for (unsigned int j = pivot + 1; j < dim * 2; ++j)
+ matrix(i, j) -= matrix(pivot, j) * factor;
+ }
+ }
+
+ /*
+ * Then diagonalize the input, walking the diagonal backwards. There's
+ * no need to update the input matrix, as all the values we would write
+ * in the top-right triangle aren't used in further calculations (and
+ * would all by definition be zero).
+ */
+ for (unsigned int pivot = dim - 1; pivot > 0; --pivot) {
+ const T pivotValue = matrix(pivot, pivot);
+
+ for (unsigned int i = 0; i < pivot; ++i) {
+ const T factor = matrix(i, pivot) / pivotValue;
+
+ for (unsigned int j = dim; j < dim * 2; ++j)
+ matrix(i, j) -= matrix(pivot, j) * factor;
+ }
+ }
+
+ /*
+ * Finally, normalize the diagonal and store the result in the output
+ * data.
+ */
+ for (unsigned int i = 0; i < dim; ++i) {
+ const T factor = matrix(i, i);
+
+ for (unsigned int j = 0; j < dim; ++j)
+ dataOut[i * dim + j] = matrix(i, j + dim) / factor;
+ }
+
+ return true;
+}
+
+template bool matrixInvert<float>(Span<const float> dataIn, Span<float> dataOut,
+ unsigned int dim, Span<float> scratchBuffer,
+ Span<unsigned int> swapBuffer);
+template bool matrixInvert<double>(Span<const double> data, Span<double> dataOut,
+ unsigned int dim, Span<double> scratchBuffer,
+ Span<unsigned int> swapBuffer);
+
+/*
+ * The YAML data shall be a list of numerical values. Its size shall be equal
+ * to the product of the number of rows and columns of the matrix (Rows x
+ * Cols). The values shall be stored in row-major order.
+ */
+bool matrixValidateYaml(const YamlObject &obj, unsigned int size)
+{
+ if (!obj.isList())
+ return false;
+
+ if (obj.size() != size) {
+ LOG(Matrix, Error)
+ << "Wrong number of values in matrix: expected "
+ << size << ", got " << obj.size();
+ return false;
+ }
+
+ return true;
+}
+#endif /* __DOXYGEN__ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
index 2949816b..353f34a8 100644
--- a/src/libcamera/media_device.cpp
+++ b/src/libcamera/media_device.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * media_device.cpp - Media device handler
+ * Media device handler
*/
#include "libcamera/internal/media_device.h"
@@ -13,6 +13,7 @@
#include <string>
#include <string.h>
#include <sys/ioctl.h>
+#include <tuple>
#include <unistd.h>
#include <vector>
@@ -164,7 +165,7 @@ void MediaDevice::unlock()
if (!fd_.isValid())
return;
- lockf(fd_.get(), F_ULOCK, 0);
+ std::ignore = lockf(fd_.get(), F_ULOCK, 0);
}
/**
@@ -793,7 +794,7 @@ void MediaDevice::fixupEntityFlags(struct media_v2_entity *entity)
* low-level link setup as it performs no checks on the validity of the \a
* flags, and assumes that the supplied \a flags are valid for the link (e.g.
* immutable links cannot be disabled).
-*
+ *
* \sa MediaLink::setEnabled(bool enable)
*
* \return 0 on success or a negative error code otherwise
@@ -818,22 +819,36 @@ int MediaDevice::setupLink(const MediaLink *link, unsigned int flags)
if (ret) {
ret = -errno;
LOG(MediaDevice, Error)
- << "Failed to setup link "
- << source->entity()->name() << "["
- << source->index() << "] -> "
- << sink->entity()->name() << "["
- << sink->index() << "]: "
+ << "Failed to setup link " << *link << ": "
<< strerror(-ret);
return ret;
}
- LOG(MediaDevice, Debug)
- << source->entity()->name() << "["
- << source->index() << "] -> "
- << sink->entity()->name() << "["
- << sink->index() << "]: " << flags;
+ LOG(MediaDevice, Debug) << *link << ": " << flags;
return 0;
}
+/**
+ * \brief Identify all entities of a common function in the MediaDevice
+ * \param[in] function The entity function to search for
+ *
+ * Search all entities within the graph of the MediaDevice and return
+ * a vector of those which match the given function.
+ *
+ * \return A vector of matching entities
+ */
+std::vector<MediaEntity *> MediaDevice::locateEntities(unsigned int function)
+{
+ std::vector<MediaEntity *> found;
+
+ /* Gather all the entities matching the function they expose. */
+ for (MediaEntity *entity : entities()) {
+ if (entity->function() == function)
+ found.push_back(entity);
+ }
+
+ return found;
+}
+
} /* namespace libcamera */
diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp
index c78f4758..3e3772a6 100644
--- a/src/libcamera/media_object.cpp
+++ b/src/libcamera/media_object.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * media_object.cpp - Media device objects: entities, pads and links
+ * Media device objects: entities, pads and links
*/
#include "libcamera/internal/media_object.h"
@@ -147,6 +147,31 @@ MediaLink::MediaLink(const struct media_v2_link *link, MediaPad *source,
}
/**
+ * \brief Generate a string representation of the MediaLink
+ * \return A string representing the MediaLink
+ */
+std::string MediaLink::toString() const
+{
+ std::stringstream ss;
+ ss << *this;
+
+ return ss.str();
+}
+
+/**
+ * \brief Insert a text representation of a Link into an output stream
+ * \param[in] out The output stream
+ * \param[in] link The MediaLink
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const MediaLink &link)
+{
+ out << *link.source() << " -> " << *link.sink();
+
+ return out;
+}
+
+/**
* \fn MediaLink::source()
* \brief Retrieve the link's source pad
* \return The source pad at the origin of the link
@@ -236,6 +261,31 @@ void MediaPad::addLink(MediaLink *link)
}
/**
+ * \brief Generate a string representation of the MediaPad
+ * \return A string representing the MediaPad
+ */
+std::string MediaPad::toString() const
+{
+ std::stringstream ss;
+ ss << *this;
+
+ return ss.str();
+}
+
+/**
+ * \brief Insert a text representation of a MediaPad into an output stream
+ * \param[in] out The output stream
+ * \param[in] pad The MediaPad
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const MediaPad &pad)
+{
+ out << "'" << pad.entity()->name() << "'[" << pad.index() << "]";
+
+ return out;
+}
+
+/**
* \class MediaEntity
* \brief The MediaEntity represents an entity in the media graph
*
diff --git a/src/libcamera/media_pipeline.cpp b/src/libcamera/media_pipeline.cpp
new file mode 100644
index 00000000..c4e9f69b
--- /dev/null
+++ b/src/libcamera/media_pipeline.cpp
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Media pipeline support
+ */
+
+#include "libcamera/internal/media_pipeline.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <queue>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <linux/media.h>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/media_device.h"
+#include "libcamera/internal/media_object.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+
+/**
+ * \file media_pipeline.h
+ * \brief Provide a representation of a pipeline of devices using the Media
+ * Controller.
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(MediaPipeline)
+
+/**
+ * \class MediaPipeline
+ * \brief The MediaPipeline represents a set of entities that together form a
+ * data path for stream data.
+ *
+ * A MediaPipeline instance is constructed from a sink and a source between
+ * two entities in a media graph.
+ */
+
+/**
+ * \brief Retrieve all source pads connected to a sink pad through active routes
+ *
+ * Examine the entity using the V4L2 Subdevice Routing API to collect all the
+ * source pads which are connected with an active route to the sink pad.
+ *
+ * \return A vector of source MediaPads
+ */
+static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink)
+{
+ MediaEntity *entity = sink->entity();
+ std::unique_ptr<V4L2Subdevice> subdev =
+ std::make_unique<V4L2Subdevice>(entity);
+
+ int ret = subdev->open();
+ if (ret < 0)
+ return {};
+
+ V4L2Subdevice::Routing routing = {};
+ ret = subdev->getRouting(&routing, V4L2Subdevice::ActiveFormat);
+ if (ret < 0)
+ return {};
+
+ std::vector<const MediaPad *> pads;
+
+ for (const V4L2Subdevice::Route &route : routing) {
+ if (sink->index() != route.sink.pad ||
+ !(route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ const MediaPad *pad = entity->getPadByIndex(route.source.pad);
+ if (!pad) {
+ LOG(MediaPipeline, Error)
+ << "Entity " << entity->name()
+ << " has invalid route source pad "
+ << route.source.pad;
+ return {};
+ }
+
+ pads.push_back(pad);
+ }
+
+ return pads;
+}
+
+/**
+ * \brief Find the path from source to sink
+ *
+ * Starting from a source entity, determine the shortest path to the target
+ * described by \a sink.
+ *
+ * If \a sink can not be found, or a route from source to sink can not be
+ * achieved an error of -ENOLINK will be returned.
+ *
+ * When successful, the MediaPipeline will internally store the representation
+ * of entities and links to describe the path between the two entities.
+ *
+ * \return 0 on success, a negative errno otherwise
+ */
+int MediaPipeline::init(MediaEntity *source, std::string_view sink)
+{
+ /*
+ * Find the shortest path between from the Camera Sensor and the
+ * target entity.
+ */
+ std::unordered_set<MediaEntity *> visited;
+ std::queue<std::tuple<MediaEntity *, MediaPad *>> queue;
+
+ /* Remember at each entity where we came from. */
+ std::unordered_map<MediaEntity *, Entity> parents;
+ MediaEntity *entity = nullptr;
+ MediaEntity *target = nullptr;
+ MediaPad *sinkPad;
+
+ queue.push({ source, nullptr });
+
+ while (!queue.empty()) {
+ std::tie(entity, sinkPad) = queue.front();
+ queue.pop();
+
+ /* Found the target device. */
+ if (entity->name() == sink) {
+ LOG(MediaPipeline, Debug)
+ << "Found Pipeline target " << entity->name();
+ target = entity;
+ break;
+ }
+
+ visited.insert(entity);
+
+ /*
+ * Add direct downstream entities to the search queue. If the
+ * current entity supports the subdev internal routing API,
+ * restrict the search to downstream entities reachable through
+ * active routes.
+ */
+
+ std::vector<const MediaPad *> pads;
+ bool supportsRouting = false;
+
+ if (sinkPad) {
+ pads = routedSourcePads(sinkPad);
+ if (!pads.empty())
+ supportsRouting = true;
+ }
+
+ if (pads.empty()) {
+ for (const MediaPad *pad : entity->pads()) {
+ if (!(pad->flags() & MEDIA_PAD_FL_SOURCE))
+ continue;
+ pads.push_back(pad);
+ }
+ }
+
+ for (const MediaPad *pad : pads) {
+ for (MediaLink *link : pad->links()) {
+ MediaEntity *next = link->sink()->entity();
+ if (visited.find(next) == visited.end()) {
+ queue.push({ next, link->sink() });
+
+ Entity e{ entity, supportsRouting, sinkPad, pad, link };
+ parents.insert({ next, e });
+ }
+ }
+ }
+ }
+
+ if (!target) {
+ LOG(MediaPipeline, Error)
+ << "Failed to connect " << source->name()
+ << " to " << sink;
+ return -ENOLINK;
+ }
+
+ /*
+ * With the parents, we can follow back our way from the capture device
+ * to the sensor. Store all the entities in the pipeline, from the
+ * camera sensor to the video node, in entities_.
+ */
+ entities_.push_front({ entity, false, sinkPad, nullptr, nullptr });
+
+ for (auto it = parents.find(entity); it != parents.end();
+ it = parents.find(entity)) {
+ const Entity &e = it->second;
+ entities_.push_front(e);
+ entity = e.entity;
+ }
+
+ LOG(MediaPipeline, Info)
+ << "Found pipeline: "
+ << utils::join(entities_, " -> ",
+ [](const Entity &e) {
+ std::string s = "[";
+ if (e.sink)
+ s += std::to_string(e.sink->index()) + "|";
+ s += e.entity->name();
+ if (e.source)
+ s += "|" + std::to_string(e.source->index());
+ s += "]";
+ return s;
+ });
+
+ return 0;
+}
+
+/**
+ * \brief Initialise and enable all links through the MediaPipeline
+ * \return 0 on success, or a negative errno otherwise
+ */
+int MediaPipeline::initLinks()
+{
+ int ret = 0;
+
+ MediaLink *sinkLink = nullptr;
+ for (Entity &e : entities_) {
+ /* Sensor entities have no connected sink. */
+ if (!sinkLink) {
+ sinkLink = e.sourceLink;
+ continue;
+ }
+
+ LOG(MediaPipeline, Debug) << "Enabling : " << *sinkLink;
+
+ if (!(sinkLink->flags() & MEDIA_LNK_FL_ENABLED)) {
+ ret = sinkLink->setEnabled(true);
+ if (ret < 0)
+ return ret;
+ }
+
+ sinkLink = e.sourceLink;
+ }
+
+ return ret;
+}
+
+/**
+ * \brief Configure the entities of this MediaPipeline
+ *
+ * Propagate formats through each of the entities of the Pipeline, validating
+ * that each one was not adjusted by the driver from the desired format.
+ *
+ * \return 0 on success or a negative errno otherwise
+ */
+int MediaPipeline::configure(CameraSensor *sensor, V4L2SubdeviceFormat *format)
+{
+ int ret;
+
+ for (const Entity &e : entities_) {
+ /* The sensor is configured through the CameraSensor */
+ if (!e.sourceLink)
+ break;
+
+ MediaLink *link = e.sourceLink;
+ MediaPad *source = link->source();
+ MediaPad *sink = link->sink();
+
+ /* 'format' already contains the sensor configuration */
+ if (source->entity() != sensor->entity()) {
+ /* \todo Add MediaDevice cache to reduce FD pressure */
+ V4L2Subdevice subdev(source->entity());
+ ret = subdev.open();
+ if (ret)
+ return ret;
+
+ ret = subdev.getFormat(source->index(), format);
+ if (ret < 0)
+ return ret;
+ }
+
+ V4L2SubdeviceFormat sourceFormat = *format;
+ /* \todo Add MediaDevice cache to reduce FD pressure */
+ V4L2Subdevice subdev(sink->entity());
+ ret = subdev.open();
+ if (ret)
+ return ret;
+
+ ret = subdev.setFormat(sink->index(), format);
+ if (ret < 0)
+ return ret;
+
+ if (format->code != sourceFormat.code ||
+ format->size != sourceFormat.size) {
+ LOG(MediaPipeline, Debug)
+ << "Source '" << *source
+ << " produces " << sourceFormat
+ << ", sink '" << *sink
+ << " requires " << *format;
+ return -EINVAL;
+ }
+
+ LOG(MediaPipeline, Debug)
+ << "Link " << *link << " configured with format "
+ << *format;
+ }
+
+ return 0;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index d0e26f6b..202db1ef 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -1,27 +1,35 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources = files([
- 'bayer_format.cpp',
- 'byte_stream_buffer.cpp',
+libcamera_public_sources = files([
'camera.cpp',
- 'camera_controls.cpp',
- 'camera_lens.cpp',
'camera_manager.cpp',
- 'camera_sensor.cpp',
- 'camera_sensor_properties.cpp',
'color_space.cpp',
'controls.cpp',
+ 'fence.cpp',
+ 'framebuffer.cpp',
+ 'framebuffer_allocator.cpp',
+ 'geometry.cpp',
+ 'orientation.cpp',
+ 'pixel_format.cpp',
+ 'request.cpp',
+ 'stream.cpp',
+ 'transform.cpp',
+])
+
+libcamera_internal_sources = files([
+ 'bayer_format.cpp',
+ 'byte_stream_buffer.cpp',
+ 'camera_controls.cpp',
+ 'camera_lens.cpp',
'control_serializer.cpp',
'control_validator.cpp',
'converter.cpp',
+ 'debug_controls.cpp',
'delayed_controls.cpp',
'device_enumerator.cpp',
'device_enumerator_sysfs.cpp',
- 'fence.cpp',
+ 'dma_buf_allocator.cpp',
'formats.cpp',
- 'framebuffer.cpp',
- 'framebuffer_allocator.cpp',
- 'geometry.cpp',
'ipa_controls.cpp',
'ipa_data_serializer.cpp',
'ipa_interface.cpp',
@@ -32,29 +40,24 @@ libcamera_sources = files([
'ipc_pipe_unixsocket.cpp',
'ipc_unixsocket.cpp',
'mapped_framebuffer.cpp',
+ 'matrix.cpp',
'media_device.cpp',
'media_object.cpp',
- 'orientation.cpp',
+ 'media_pipeline.cpp',
'pipeline_handler.cpp',
- 'pixel_format.cpp',
'process.cpp',
'pub_key.cpp',
- 'request.cpp',
+ 'shared_mem_object.cpp',
'source_paths.cpp',
- 'stream.cpp',
'sysfs.cpp',
- 'transform.cpp',
'v4l2_device.cpp',
'v4l2_pixelformat.cpp',
'v4l2_subdevice.cpp',
'v4l2_videodevice.cpp',
+ 'vector.cpp',
'yaml_parser.cpp',
])
-libcamera_sources += libcamera_public_headers
-libcamera_sources += libcamera_generated_ipa_headers
-libcamera_sources += libcamera_tracepoint_header
-
includes = [
libcamera_includes,
]
@@ -69,6 +72,8 @@ subdir('converter')
subdir('ipa')
subdir('pipeline')
subdir('proxy')
+subdir('sensor')
+subdir('software_isp')
null_dep = dependency('', required : false)
@@ -102,14 +107,14 @@ endif
if liblttng.found()
tracing_enabled = true
config_h.set('HAVE_TRACING', 1)
- libcamera_sources += files(['tracepoints.cpp'])
+ libcamera_internal_sources += files(['tracepoints.cpp'])
else
tracing_enabled = false
endif
if libudev.found()
config_h.set('HAVE_LIBUDEV', 1)
- libcamera_sources += files([
+ libcamera_internal_sources += files([
'device_enumerator_udev.cpp',
])
endif
@@ -127,15 +132,35 @@ endif
control_sources = []
-foreach source : control_source_files
- input_files = files(source +'.yaml', source + '.cpp.in')
- control_sources += custom_target(source + '_cpp',
+controls_mode_files = {
+ 'controls': [
+ controls_files,
+ 'control_ids.cpp',
+ ],
+ 'properties': [
+ properties_files,
+ 'property_ids.cpp',
+ ],
+}
+
+foreach mode, inout_files : controls_mode_files
+ input_files = inout_files[0]
+ output_file = inout_files[1]
+
+ template_file = files('control_ids.cpp.in')
+ ranges_file = files('control_ranges.yaml')
+
+ control_sources += custom_target(mode + '_ids_cpp',
input : input_files,
- output : source + '.cpp',
- command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'])
+ output : output_file,
+ command : [gen_controls, '-o', '@OUTPUT@',
+ '--mode', mode, '-t', template_file,
+ '-r', ranges_file, '@INPUT@'],
+ depend_files : [py_mod_controls],
+ env : py_build_env)
endforeach
-libcamera_sources += control_sources
+libcamera_public_sources += control_sources
gen_version = meson.project_source_root() / 'utils' / 'gen-version.sh'
@@ -146,7 +171,7 @@ version_cpp = vcs_tag(command : [gen_version, meson.project_build_root(), meson.
output : 'version.cpp',
fallback : meson.project_version())
-libcamera_sources += version_cpp
+libcamera_public_sources += version_cpp
if ipa_sign_module
ipa_pub_key_cpp = custom_target('ipa_pub_key_cpp',
@@ -154,7 +179,7 @@ if ipa_sign_module
output : 'ipa_pub_key.cpp',
command : [gen_ipa_pub_key, '@INPUT@', '@OUTPUT@'])
- libcamera_sources += ipa_pub_key_cpp
+ libcamera_internal_sources += ipa_pub_key_cpp
endif
libcamera_deps += [
@@ -174,7 +199,13 @@ libcamera_deps += [
# for the presence or abscence of the dynamic tag.
libcamera = shared_library('libcamera',
- libcamera_sources,
+ [
+ libcamera_public_headers,
+ libcamera_public_sources,
+ libcamera_ipa_headers,
+ libcamera_internal_headers,
+ libcamera_internal_sources,
+ ],
version : libcamera_version,
soversion : libcamera_soversion,
name_prefix : '',
@@ -184,7 +215,6 @@ libcamera = shared_library('libcamera',
dependencies : libcamera_deps)
libcamera_public = declare_dependency(sources : [
- libcamera_ipa_headers,
libcamera_public_headers,
],
include_directories : libcamera_includes,
@@ -193,7 +223,7 @@ libcamera_public = declare_dependency(sources : [
# Internal dependency for components and plugins which can use private APIs
libcamera_private = declare_dependency(sources : [
- libcamera_generated_ipa_headers,
+ libcamera_ipa_headers,
],
dependencies : [
libcamera_public,
diff --git a/src/libcamera/orientation.cpp b/src/libcamera/orientation.cpp
index 965f5a8b..7d7d21ae 100644
--- a/src/libcamera/orientation.cpp
+++ b/src/libcamera/orientation.cpp
@@ -2,16 +2,15 @@
/*
* Copyright (C) 2023, Ideas On Board Oy
*
- * orientation.cpp - Image orientation
+ * Image orientation
*/
#include <libcamera/orientation.h>
#include <array>
-#include <string>
/**
- * \file libcamera/orientation.h
+ * \file orientation.h
* \brief Image orientation definition
*/
@@ -102,10 +101,14 @@ std::ostream &operator<<(std::ostream &out, const Orientation &orientation)
{
constexpr std::array<const char *, 9> orientationNames = {
"", /* Orientation starts counting from 1. */
- "Rotate0", "Rotate0Mirror",
- "Rotate180", "Rotate180Mirror",
- "Rotate90Mirror", "Rotate270",
- "Rotate270Mirror", "Rotate90",
+ "Rotate0",
+ "Rotate0Mirror",
+ "Rotate180",
+ "Rotate180Mirror",
+ "Rotate90Mirror",
+ "Rotate270",
+ "Rotate270Mirror",
+ "Rotate90",
};
out << orientationNames[static_cast<unsigned int>(orientation)];
diff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
index 9bdfff0b..ecda426a 100644
--- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
+++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2022 - Jacopo Mondi <jacopo@jmondi.org>
*
- * imx8-isi.cpp - Pipeline handler for ISI interface found on NXP i.MX8 SoC
+ * Pipeline handler for ISI interface found on NXP i.MX8 SoC
*/
#include <algorithm>
@@ -157,11 +157,10 @@ PipelineHandlerISI *ISICameraData::pipe()
/* Open and initialize pipe components. */
int ISICameraData::init()
{
- int ret = sensor_->init();
- if (ret)
- return ret;
+ if (!sensor_)
+ return -ENODEV;
- ret = csis_->open();
+ int ret = csis_->open();
if (ret)
return ret;
@@ -367,7 +366,6 @@ ISICameraConfiguration::validateRaw(std::set<Stream *> &availableStreams,
* Make sure the requested RAW format is supported by the
* pipeline, otherwise adjust it.
*/
- std::vector<unsigned int> mbusCodes = data_->sensor_->mbusCodes();
StreamConfiguration &rawConfig = config_[0];
PixelFormat rawFormat = rawConfig.pixelFormat;
@@ -554,7 +552,7 @@ CameraConfiguration::Status ISICameraConfiguration::validate()
PixelFormat pixelFormat = config_[0].pixelFormat;
V4L2SubdeviceFormat sensorFormat{};
- sensorFormat.mbus_code = data_->getMediaBusFormat(&pixelFormat);
+ sensorFormat.code = data_->getMediaBusFormat(&pixelFormat);
sensorFormat.size = maxSize;
LOG(ISI, Debug) << "Computed sensor configuration: " << sensorFormat;
@@ -569,7 +567,7 @@ CameraConfiguration::Status ISICameraConfiguration::validate()
* the smallest larger format without considering the aspect ratio
* as the ISI can freely scale.
*/
- auto sizes = sensor->sizes(sensorFormat.mbus_code);
+ auto sizes = sensor->sizes(sensorFormat.code);
Size bestSize;
for (const Size &s : sizes) {
@@ -595,7 +593,7 @@ CameraConfiguration::Status ISICameraConfiguration::validate()
return Invalid;
}
- sensorFormat_.mbus_code = sensorFormat.mbus_code;
+ sensorFormat_.code = sensorFormat.code;
sensorFormat_.size = bestSize;
LOG(ISI, Debug) << "Selected sensor format: " << sensorFormat_;
@@ -632,7 +630,7 @@ StreamConfiguration PipelineHandlerISI::generateYUVConfiguration(Camera *camera,
/* Adjust the requested size to the sensor's capabilities. */
V4L2SubdeviceFormat sensorFmt;
- sensorFmt.mbus_code = mbusCode;
+ sensorFmt.code = mbusCode;
sensorFmt.size = size;
int ret = data->sensor_->tryFormat(&sensorFmt);
@@ -827,16 +825,10 @@ int PipelineHandlerISI::configure(Camera *camera, CameraConfiguration *c)
unsigned int xbarFirstSource = crossbar_->entity()->pads().size() / 2 + 1;
for (const auto &[idx, config] : utils::enumerate(*c)) {
- struct v4l2_subdev_route route = {
- .sink_pad = data->xbarSink_,
- .sink_stream = 0,
- .source_pad = static_cast<uint32_t>(xbarFirstSource + idx),
- .source_stream = 0,
- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
- .reserved = {}
- };
-
- routing.push_back(route);
+ uint32_t sourcePad = xbarFirstSource + idx;
+ routing.emplace_back(V4L2Subdevice::Stream{ data->xbarSink_, 0 },
+ V4L2Subdevice::Stream{ sourcePad, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
}
int ret = crossbar_->setRouting(&routing, V4L2Subdevice::ActiveFormat);
@@ -891,7 +883,7 @@ int PipelineHandlerISI::configure(Camera *camera, CameraConfiguration *c)
unsigned int isiCode = ISICameraConfiguration::formatsMap_.at(config.pixelFormat);
V4L2SubdeviceFormat isiFormat{};
- isiFormat.mbus_code = isiCode;
+ isiFormat.code = isiCode;
isiFormat.size = config.size;
ret = pipe->isi->setFormat(1, &isiFormat);
@@ -1063,7 +1055,7 @@ bool PipelineHandlerISI::match(DeviceEnumerator *enumerator)
std::unique_ptr<ISICameraData> data =
std::make_unique<ISICameraData>(this);
- data->sensor_ = std::make_unique<CameraSensor>(sensor);
+ data->sensor_ = CameraSensorFactoryBase::create(sensor);
data->csis_ = std::make_unique<V4L2Subdevice>(csi);
data->xbarSink_ = sink;
@@ -1118,6 +1110,6 @@ void PipelineHandlerISI::bufferReady(FrameBuffer *buffer)
completeRequest(request);
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerISI)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerISI, "imx8-isi")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/imx8-isi/meson.build b/src/libcamera/pipeline/imx8-isi/meson.build
index ffd0ce54..b369b031 100644
--- a/src/libcamera/pipeline/imx8-isi/meson.build
+++ b/src/libcamera/pipeline/imx8-isi/meson.build
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'imx8-isi.cpp'
])
diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp
index 7400cb0b..aa544d7b 100644
--- a/src/libcamera/pipeline/ipu3/cio2.cpp
+++ b/src/libcamera/pipeline/ipu3/cio2.cpp
@@ -2,13 +2,13 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * cio2.cpp - Intel IPU3 CIO2
+ * Intel IPU3 CIO2
*/
#include "cio2.h"
+#include <cmath>
#include <limits>
-#include <math.h>
#include <linux/media-bus-format.h>
@@ -134,10 +134,9 @@ int CIO2Device::init(const MediaDevice *media, unsigned int index)
MediaLink *link = links[0];
MediaEntity *sensorEntity = link->source()->entity();
- sensor_ = std::make_unique<CameraSensor>(sensorEntity);
- ret = sensor_->init();
- if (ret)
- return ret;
+ sensor_ = CameraSensorFactoryBase::create(sensorEntity);
+ if (!sensor_)
+ return -ENODEV;
ret = link->setEnabled(true);
if (ret)
@@ -202,7 +201,7 @@ int CIO2Device::configure(const Size &size, const Transform &transform,
if (ret)
return ret;
- const auto &itInfo = mbusCodesToPixelFormat.find(sensorFormat.mbus_code);
+ const auto &itInfo = mbusCodesToPixelFormat.find(sensorFormat.code);
if (itInfo == mbusCodesToPixelFormat.end())
return -EINVAL;
@@ -230,13 +229,13 @@ StreamConfiguration CIO2Device::generateConfiguration(Size size) const
/* Query the sensor static information for closest match. */
std::vector<unsigned int> mbusCodes = utils::map_keys(mbusCodesToPixelFormat);
V4L2SubdeviceFormat sensorFormat = getSensorFormat(mbusCodes, size);
- if (!sensorFormat.mbus_code) {
+ if (!sensorFormat.code) {
LOG(IPU3, Error) << "Sensor does not support mbus code";
return {};
}
cfg.size = sensorFormat.size;
- cfg.pixelFormat = mbusCodesToPixelFormat.at(sensorFormat.mbus_code);
+ cfg.pixelFormat = mbusCodesToPixelFormat.at(sensorFormat.code);
cfg.bufferCount = kBufferCount;
return cfg;
@@ -304,7 +303,7 @@ V4L2SubdeviceFormat CIO2Device::getSensorFormat(const std::vector<unsigned int>
* comparing it with a single precision digit is enough.
*/
ratio = static_cast<unsigned int>(ratio * 10) / 10.0;
- float ratioDiff = fabsf(ratio - desiredRatio);
+ float ratioDiff = std::abs(ratio - desiredRatio);
unsigned int area = sz.width * sz.height;
unsigned int areaDiff = area - desiredArea;
@@ -326,7 +325,7 @@ V4L2SubdeviceFormat CIO2Device::getSensorFormat(const std::vector<unsigned int>
}
V4L2SubdeviceFormat format{};
- format.mbus_code = bestCode;
+ format.code = bestCode;
format.size = bestSize;
return format;
diff --git a/src/libcamera/pipeline/ipu3/cio2.h b/src/libcamera/pipeline/ipu3/cio2.h
index bbd87eb8..963c2f6b 100644
--- a/src/libcamera/pipeline/ipu3/cio2.h
+++ b/src/libcamera/pipeline/ipu3/cio2.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * cio2.h - Intel IPU3 CIO2
+ * Intel IPU3 CIO2
*/
#pragma once
diff --git a/src/libcamera/pipeline/ipu3/frames.cpp b/src/libcamera/pipeline/ipu3/frames.cpp
index a4c3477c..bc0526a7 100644
--- a/src/libcamera/pipeline/ipu3/frames.cpp
+++ b/src/libcamera/pipeline/ipu3/frames.cpp
@@ -2,17 +2,18 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * frames.cpp - Intel IPU3 Frames helper
+ * Intel IPU3 Frames helper
*/
#include "frames.h"
+#include <libcamera/base/log.h>
+
#include <libcamera/framebuffer.h>
#include <libcamera/request.h>
#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/pipeline_handler.h"
-#include "libcamera/internal/v4l2_videodevice.h"
namespace libcamera {
diff --git a/src/libcamera/pipeline/ipu3/frames.h b/src/libcamera/pipeline/ipu3/frames.h
index 6e3cb915..a347b66f 100644
--- a/src/libcamera/pipeline/ipu3/frames.h
+++ b/src/libcamera/pipeline/ipu3/frames.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * frames.h - Intel IPU3 Frames helper
+ * Intel IPU3 Frames helper
*/
#pragma once
diff --git a/src/libcamera/pipeline/ipu3/imgu.cpp b/src/libcamera/pipeline/ipu3/imgu.cpp
index 531879f1..7be78091 100644
--- a/src/libcamera/pipeline/ipu3/imgu.cpp
+++ b/src/libcamera/pipeline/ipu3/imgu.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * imgu.cpp - Intel IPU3 ImgU
+ * Intel IPU3 ImgU
*/
#include "imgu.h"
@@ -504,7 +504,7 @@ int ImgUDevice::configure(const PipeConfig &pipeConfig, V4L2DeviceFormat *inputF
LOG(IPU3, Debug) << "ImgU BDS rectangle = " << bds;
V4L2SubdeviceFormat gdcFormat = {};
- gdcFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+ gdcFormat.code = MEDIA_BUS_FMT_FIXED;
gdcFormat.size = pipeConfig.gdc;
ret = imgu_->setFormat(PAD_INPUT, &gdcFormat);
@@ -543,7 +543,7 @@ int ImgUDevice::configureVideoDevice(V4L2VideoDevice *dev, unsigned int pad,
V4L2DeviceFormat *outputFormat)
{
V4L2SubdeviceFormat imguFormat = {};
- imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+ imguFormat.code = MEDIA_BUS_FMT_FIXED;
imguFormat.size = cfg.size;
int ret = imgu_->setFormat(pad, &imguFormat);
diff --git a/src/libcamera/pipeline/ipu3/imgu.h b/src/libcamera/pipeline/ipu3/imgu.h
index 0af4dd8a..fa508316 100644
--- a/src/libcamera/pipeline/ipu3/imgu.h
+++ b/src/libcamera/pipeline/ipu3/imgu.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * imgu.h - Intel IPU3 ImgU
+ * Intel IPU3 ImgU
*/
#pragma once
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index fa4bd0bb..e31e3879 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -2,11 +2,10 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * ipu3.cpp - Pipeline handler for Intel IPU3
+ * Pipeline handler for Intel IPU3
*/
#include <algorithm>
-#include <iomanip>
#include <memory>
#include <queue>
#include <vector>
@@ -19,15 +18,17 @@
#include <libcamera/camera.h>
#include <libcamera/control_ids.h>
#include <libcamera/formats.h>
-#include <libcamera/ipa/ipu3_ipa_interface.h>
-#include <libcamera/ipa/ipu3_ipa_proxy.h>
#include <libcamera/property_ids.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
+#include <libcamera/ipa/ipu3_ipa_interface.h>
+#include <libcamera/ipa/ipu3_ipa_proxy.h>
+
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_lens.h"
#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/camera_sensor_properties.h"
#include "libcamera/internal/delayed_controls.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/framebuffer.h"
@@ -88,7 +89,7 @@ public:
private:
void metadataReady(unsigned int id, const ControlList &metadata);
- void paramsBufferReady(unsigned int id);
+ void paramsComputed(unsigned int id);
void setSensorControls(unsigned int id, const ControlList &sensorControls,
const ControlList &lensControls);
};
@@ -958,7 +959,7 @@ int PipelineHandlerIPU3::updateControls(IPU3CameraData *data)
values.reserve(testPatternModes.size());
for (auto pattern : testPatternModes)
- values.emplace_back(static_cast<int32_t>(pattern));
+ values.emplace_back(pattern);
controls[&controls::draft::TestPatternMode] = ControlInfo(values);
}
@@ -1077,14 +1078,10 @@ int PipelineHandlerIPU3::registerCameras()
if (ret)
continue;
- /*
- * \todo Read delay values from the sensor itself or from a
- * a sensor database. For now use generic values taken from
- * the Raspberry Pi and listed as 'generic values'.
- */
+ const CameraSensorProperties::SensorDelays &delays = cio2->sensor()->sensorDelays();
std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
- { V4L2_CID_ANALOGUE_GAIN, { 1, false } },
- { V4L2_CID_EXPOSURE, { 2, false } },
+ { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+ { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
};
data->delayedCtrls_ =
@@ -1117,19 +1114,19 @@ int PipelineHandlerIPU3::registerCameras()
* returned through the ImgU main and secondary outputs.
*/
data->cio2_.bufferReady().connect(data.get(),
- &IPU3CameraData::cio2BufferReady);
+ &IPU3CameraData::cio2BufferReady);
data->cio2_.bufferAvailable.connect(
data.get(), &IPU3CameraData::queuePendingRequests);
data->imgu_->input_->bufferReady.connect(&data->cio2_,
- &CIO2Device::tryReturnBuffer);
+ &CIO2Device::tryReturnBuffer);
data->imgu_->output_->bufferReady.connect(data.get(),
- &IPU3CameraData::imguOutputBufferReady);
+ &IPU3CameraData::imguOutputBufferReady);
data->imgu_->viewfinder_->bufferReady.connect(data.get(),
- &IPU3CameraData::imguOutputBufferReady);
+ &IPU3CameraData::imguOutputBufferReady);
data->imgu_->param_->bufferReady.connect(data.get(),
- &IPU3CameraData::paramBufferReady);
+ &IPU3CameraData::paramBufferReady);
data->imgu_->stat_->bufferReady.connect(data.get(),
- &IPU3CameraData::statBufferReady);
+ &IPU3CameraData::statBufferReady);
/* Create and register the Camera instance. */
const std::string &cameraId = cio2->sensor()->id();
@@ -1156,7 +1153,7 @@ int IPU3CameraData::loadIPA()
return -ENOENT;
ipa_->setSensorControls.connect(this, &IPU3CameraData::setSensorControls);
- ipa_->paramsBufferReady.connect(this, &IPU3CameraData::paramsBufferReady);
+ ipa_->paramsComputed.connect(this, &IPU3CameraData::paramsComputed);
ipa_->metadataReady.connect(this, &IPU3CameraData::metadataReady);
/*
@@ -1186,9 +1183,8 @@ int IPU3CameraData::loadIPA()
* The API tuning file is made from the sensor name. If the tuning file
* isn't found, fall back to the 'uncalibrated' file.
*/
- std::string ipaTuningFile = ipa_->configurationFile(sensor->model() + ".yaml");
- if (ipaTuningFile.empty())
- ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
+ std::string ipaTuningFile =
+ ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
sensorInfo, sensor->controls(), &ipaControls_);
@@ -1218,7 +1214,7 @@ void IPU3CameraData::setSensorControls([[maybe_unused]] unsigned int id,
focusLens->setFocusPosition(focusValue.get<int32_t>());
}
-void IPU3CameraData::paramsBufferReady(unsigned int id)
+void IPU3CameraData::paramsComputed(unsigned int id)
{
IPU3Frames::Info *info = frameInfos_.find(id);
if (!info)
@@ -1329,7 +1325,7 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
if (request->findBuffer(&rawStream_))
pipe()->completeBuffer(request, buffer);
- ipa_->fillParamsBuffer(info->id, info->paramBuffer->cookie());
+ ipa_->computeParams(info->id, info->paramBuffer->cookie());
}
void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)
@@ -1373,8 +1369,8 @@ void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
return;
}
- ipa_->processStatsBuffer(info->id, request->metadata().get(controls::SensorTimestamp).value_or(0),
- info->statBuffer->cookie(), info->effectiveSensorControls);
+ ipa_->processStats(info->id, request->metadata().get(controls::SensorTimestamp).value_or(0),
+ info->statBuffer->cookie(), info->effectiveSensorControls);
}
/*
@@ -1420,6 +1416,6 @@ void IPU3CameraData::frameStart(uint32_t sequence)
*testPatternMode);
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3, "ipu3")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/ipu3/meson.build b/src/libcamera/pipeline/ipu3/meson.build
index a1b0b31a..f2904b4a 100644
--- a/src/libcamera/pipeline/ipu3/meson.build
+++ b/src/libcamera/pipeline/ipu3/meson.build
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'cio2.cpp',
'frames.cpp',
'imgu.cpp',
diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
new file mode 100644
index 00000000..a05e11fc
--- /dev/null
+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
@@ -0,0 +1,1755 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy
+ *
+ * Pipeline Handler for ARM's Mali-C55 ISP
+ */
+
+#include <algorithm>
+#include <array>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include <linux/mali-c55-config.h>
+#include <linux/media-bus-format.h>
+#include <linux/media.h>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/camera.h>
+#include <libcamera/formats.h>
+#include <libcamera/geometry.h>
+#include <libcamera/property_ids.h>
+#include <libcamera/stream.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+#include <libcamera/ipa/mali-c55_ipa_interface.h>
+#include <libcamera/ipa/mali-c55_ipa_proxy.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera.h"
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/camera_sensor_properties.h"
+#include "libcamera/internal/delayed_controls.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/request.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+#include "libcamera/internal/v4l2_videodevice.h"
+
+namespace {
+
+bool isFormatRaw(const libcamera::PixelFormat &pixFmt)
+{
+ return libcamera::PixelFormatInfo::info(pixFmt).colourEncoding ==
+ libcamera::PixelFormatInfo::ColourEncodingRAW;
+}
+
+} /* namespace */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(MaliC55)
+
+const std::map<libcamera::PixelFormat, unsigned int> maliC55FmtToCode = {
+ /* \todo Support all formats supported by the driver in libcamera. */
+
+ { formats::RGB565, MEDIA_BUS_FMT_RGB121212_1X36 },
+ { formats::RGB888, MEDIA_BUS_FMT_RGB121212_1X36 },
+ { formats::YUYV, MEDIA_BUS_FMT_YUV10_1X30 },
+ { formats::UYVY, MEDIA_BUS_FMT_YUV10_1X30 },
+ { formats::R8, MEDIA_BUS_FMT_YUV10_1X30 },
+ { formats::NV12, MEDIA_BUS_FMT_YUV10_1X30 },
+ { formats::NV21, MEDIA_BUS_FMT_YUV10_1X30 },
+
+ /* RAW formats, FR pipe only. */
+ { formats::SGBRG16, MEDIA_BUS_FMT_SGBRG16_1X16 },
+ { formats::SRGGB16, MEDIA_BUS_FMT_SRGGB16_1X16 },
+ { formats::SBGGR16, MEDIA_BUS_FMT_SBGGR16_1X16 },
+ { formats::SGRBG16, MEDIA_BUS_FMT_SGRBG16_1X16 },
+};
+
+constexpr Size kMaliC55MinInputSize = { 640, 480 };
+constexpr Size kMaliC55MinSize = { 128, 128 };
+constexpr Size kMaliC55MaxSize = { 8192, 8192 };
+constexpr unsigned int kMaliC55ISPInternalFormat = MEDIA_BUS_FMT_RGB121212_1X36;
+
+struct MaliC55FrameInfo {
+ Request *request;
+
+ FrameBuffer *paramBuffer;
+ FrameBuffer *statBuffer;
+
+ bool paramsDone;
+ bool statsDone;
+};
+
+class MaliC55CameraData : public Camera::Private
+{
+public:
+ MaliC55CameraData(PipelineHandler *pipe, MediaEntity *entity)
+ : Camera::Private(pipe), entity_(entity)
+ {
+ }
+
+ int init();
+ int loadIPA();
+
+ /* Deflect these functionalities to either TPG or CameraSensor. */
+ const std::vector<Size> sizes(unsigned int mbusCode) const;
+ const Size resolution() const;
+
+ int pixfmtToMbusCode(const PixelFormat &pixFmt) const;
+ const PixelFormat &bestRawFormat() const;
+
+ void updateControls(const ControlInfoMap &ipaControls);
+
+ PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const;
+ Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;
+
+ std::unique_ptr<CameraSensor> sensor_;
+
+ MediaEntity *entity_;
+ std::unique_ptr<V4L2Subdevice> csi_;
+ std::unique_ptr<V4L2Subdevice> sd_;
+ Stream frStream_;
+ Stream dsStream_;
+
+ std::unique_ptr<ipa::mali_c55::IPAProxyMaliC55> ipa_;
+ std::vector<IPABuffer> ipaStatBuffers_;
+ std::vector<IPABuffer> ipaParamBuffers_;
+
+ std::unique_ptr<DelayedControls> delayedCtrls_;
+
+private:
+ void initTPGData();
+ void setSensorControls(const ControlList &sensorControls);
+
+ std::string id_;
+ std::vector<unsigned int> tpgCodes_;
+ std::vector<Size> tpgSizes_;
+ Size tpgResolution_;
+};
+
+int MaliC55CameraData::init()
+{
+ int ret;
+
+ sd_ = std::make_unique<V4L2Subdevice>(entity_);
+ ret = sd_->open();
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to open sensor subdevice";
+ return ret;
+ }
+
+ /* If this camera is created from TPG, we return here. */
+ if (entity_->name() == "mali-c55 tpg") {
+ initTPGData();
+ return 0;
+ }
+
+ /*
+ * Register a CameraSensor if we connect to a sensor and create
+ * an entity for the connected CSI-2 receiver.
+ */
+ sensor_ = CameraSensorFactoryBase::create(entity_);
+ if (!sensor_)
+ return ret;
+
+ const MediaPad *sourcePad = entity_->getPadByIndex(0);
+ MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity();
+
+ csi_ = std::make_unique<V4L2Subdevice>(csiEntity);
+ if (csi_->open()) {
+ LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice";
+ return false;
+ }
+
+ return 0;
+}
+
+void MaliC55CameraData::initTPGData()
+{
+ /* Replicate the CameraSensor implementation for TPG. */
+ V4L2Subdevice::Formats formats = sd_->formats(0);
+ if (formats.empty())
+ return;
+
+ tpgCodes_ = utils::map_keys(formats);
+ std::sort(tpgCodes_.begin(), tpgCodes_.end());
+
+ for (const auto &format : formats) {
+ const std::vector<SizeRange> &ranges = format.second;
+ std::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes_),
+ [](const SizeRange &range) { return range.max; });
+ }
+
+ tpgResolution_ = tpgSizes_.back();
+}
+
+void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)
+{
+ delayedCtrls_->push(sensorControls);
+}
+
+const std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const
+{
+ if (sensor_)
+ return sensor_->sizes(mbusCode);
+
+ V4L2Subdevice::Formats formats = sd_->formats(0);
+ if (formats.empty())
+ return {};
+
+ std::vector<Size> sizes;
+ const auto &format = formats.find(mbusCode);
+ if (format == formats.end())
+ return {};
+
+ const std::vector<SizeRange> &ranges = format->second;
+ std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
+ [](const SizeRange &range) { return range.max; });
+
+ std::sort(sizes.begin(), sizes.end());
+
+ return sizes;
+}
+
+const Size MaliC55CameraData::resolution() const
+{
+ if (sensor_)
+ return sensor_->resolution();
+
+ return tpgResolution_;
+}
+
+/*
+ * The Mali C55 ISP can only produce 16-bit RAW output in bypass modes, but the
+ * sensors connected to it might produce 8/10/12/16 bits. We simply search the
+ * sensor's supported formats for the one with a matching bayer order and the
+ * greatest bitdepth.
+ */
+int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const
+{
+ auto it = maliC55FmtToCode.find(pixFmt);
+ if (it == maliC55FmtToCode.end())
+ return -EINVAL;
+
+ BayerFormat bayerFormat = BayerFormat::fromMbusCode(it->second);
+ if (!bayerFormat.isValid())
+ return -EINVAL;
+
+ V4L2Subdevice::Formats formats = sd_->formats(0);
+ unsigned int sensorMbusCode = 0;
+ unsigned int bitDepth = 0;
+
+ for (const auto &[code, sizes] : formats) {
+ BayerFormat sdBayerFormat = BayerFormat::fromMbusCode(code);
+ if (!sdBayerFormat.isValid())
+ continue;
+
+ if (sdBayerFormat.order != bayerFormat.order)
+ continue;
+
+ if (sdBayerFormat.bitDepth > bitDepth) {
+ bitDepth = sdBayerFormat.bitDepth;
+ sensorMbusCode = code;
+ }
+ }
+
+ if (!sensorMbusCode)
+ return -EINVAL;
+
+ return sensorMbusCode;
+}
+
+/*
+ * Find a RAW PixelFormat supported by both the ISP and the sensor.
+ *
+ * The situation is mildly complicated by the fact that we expect the sensor to
+ * output something like RAW8/10/12/16, but the ISP can only accept as input
+ * RAW20 and can only produce as output RAW16. The one constant in that is the
+ * bayer order of the data, so we'll simply check that the sensor produces a
+ * format with a bayer order that matches that of one of the formats we support,
+ * and select that.
+ */
+const PixelFormat &MaliC55CameraData::bestRawFormat() const
+{
+ static const PixelFormat invalidPixFmt = {};
+
+ for (const auto &fmt : sd_->formats(0)) {
+ BayerFormat sensorBayer = BayerFormat::fromMbusCode(fmt.first);
+
+ if (!sensorBayer.isValid())
+ continue;
+
+ for (const auto &[pixFmt, rawCode] : maliC55FmtToCode) {
+ if (!isFormatRaw(pixFmt))
+ continue;
+
+ BayerFormat bayer = BayerFormat::fromMbusCode(rawCode);
+ if (bayer.order == sensorBayer.order)
+ return pixFmt;
+ }
+ }
+
+ LOG(MaliC55, Error) << "Sensor doesn't provide a compatible format";
+ return invalidPixFmt;
+}
+
+void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls)
+{
+ if (!sensor_)
+ return;
+
+ IPACameraSensorInfo sensorInfo;
+ int ret = sensor_->sensorInfo(&sensorInfo);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to retrieve sensor info";
+ return;
+ }
+
+ ControlInfoMap::Map controls;
+ Rectangle ispMinCrop{ 0, 0, 640, 480 };
+ controls[&controls::ScalerCrop] =
+ ControlInfo(ispMinCrop, sensorInfo.analogCrop,
+ sensorInfo.analogCrop);
+
+ for (auto const &c : ipaControls)
+ controls.emplace(c.first, c.second);
+
+ controlInfo_ = ControlInfoMap(std::move(controls), controls::controls);
+}
+
+/*
+ * Make sure the provided raw pixel format is supported and adjust it to
+ * one of the supported ones if it's not.
+ */
+PixelFormat MaliC55CameraData::adjustRawFormat(const PixelFormat &rawFmt) const
+{
+ /* Make sure the RAW mbus code is supported by the image source. */
+ int rawCode = pixfmtToMbusCode(rawFmt);
+ if (rawCode < 0)
+ return bestRawFormat();
+
+ const auto rawSizes = sizes(rawCode);
+ if (rawSizes.empty())
+ return bestRawFormat();
+
+ return rawFmt;
+}
+
+Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &size) const
+{
+ /* Expand the RAW size to the minimum ISP input size. */
+ Size rawSize = size.expandedTo(kMaliC55MinInputSize);
+
+ /* Check if the size is natively supported. */
+ int rawCode = pixfmtToMbusCode(rawFmt);
+ if (rawCode < 0)
+ return {};
+
+ const auto rawSizes = sizes(rawCode);
+ auto sizeIt = std::find(rawSizes.begin(), rawSizes.end(), rawSize);
+ if (sizeIt != rawSizes.end())
+ return rawSize;
+
+ /* Or adjust it to the closest supported size. */
+ uint16_t distance = std::numeric_limits<uint16_t>::max();
+ Size bestSize;
+ for (const Size &sz : rawSizes) {
+ uint16_t dist = std::abs(static_cast<int>(rawSize.width) -
+ static_cast<int>(sz.width)) +
+ std::abs(static_cast<int>(rawSize.height) -
+ static_cast<int>(sz.height));
+ if (dist < distance) {
+ dist = distance;
+ bestSize = sz;
+ }
+ }
+
+ return bestSize;
+}
+
+int MaliC55CameraData::loadIPA()
+{
+ int ret;
+
+ /* Do not initialize IPA for TPG. */
+ if (!sensor_)
+ return 0;
+
+ ipa_ = IPAManager::createIPA<ipa::mali_c55::IPAProxyMaliC55>(pipe(), 1, 1);
+ if (!ipa_)
+ return -ENOENT;
+
+ ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls);
+
+ std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml",
+ "uncalibrated.yaml");
+
+ /* We need to inform the IPA of the sensor configuration */
+ ipa::mali_c55::IPAConfigInfo ipaConfig{};
+
+ ret = sensor_->sensorInfo(&ipaConfig.sensorInfo);
+ if (ret)
+ return ret;
+
+ ipaConfig.sensorControls = sensor_->controls();
+
+ ControlInfoMap ipaControls;
+ ret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig,
+ &ipaControls);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to initialise the Mali-C55 IPA";
+ return ret;
+ }
+
+ updateControls(ipaControls);
+
+ return 0;
+}
+
+class MaliC55CameraConfiguration : public CameraConfiguration
+{
+public:
+ MaliC55CameraConfiguration(MaliC55CameraData *data)
+ : CameraConfiguration(), data_(data)
+ {
+ }
+
+ Status validate() override;
+ const Transform &combinedTransform() { return combinedTransform_; }
+
+ V4L2SubdeviceFormat sensorFormat_;
+
+private:
+ static constexpr unsigned int kMaxStreams = 2;
+
+ const MaliC55CameraData *data_;
+ Transform combinedTransform_;
+};
+
+CameraConfiguration::Status MaliC55CameraConfiguration::validate()
+{
+ Status status = Valid;
+
+ if (config_.empty())
+ return Invalid;
+
+ /*
+ * The TPG doesn't support flips, so we only need to calculate a
+ * transform if we have a sensor.
+ */
+ if (data_->sensor_) {
+ Orientation requestedOrientation = orientation;
+ combinedTransform_ = data_->sensor_->computeTransform(&orientation);
+ if (orientation != requestedOrientation)
+ status = Adjusted;
+ } else {
+ combinedTransform_ = Transform::Rot0;
+ }
+
+ /* Only 2 streams available. */
+ if (config_.size() > kMaxStreams) {
+ config_.resize(kMaxStreams);
+ status = Adjusted;
+ }
+
+ bool frPipeAvailable = true;
+ StreamConfiguration *rawConfig = nullptr;
+ for (StreamConfiguration &config : config_) {
+ if (!isFormatRaw(config.pixelFormat))
+ continue;
+
+ if (rawConfig) {
+ LOG(MaliC55, Error)
+ << "Only a single RAW stream is supported";
+ return Invalid;
+ }
+
+ rawConfig = &config;
+ }
+
+ /*
+ * The C55 can not upscale. Limit the configuration to the ISP
+ * capabilities and the sensor resolution.
+ */
+ Size maxSize = kMaliC55MaxSize.boundedTo(data_->resolution());
+ if (rawConfig) {
+ /*
+ * \todo Take into account the Bayer components ordering once
+ * we support rotations.
+ */
+ PixelFormat rawFormat =
+ data_->adjustRawFormat(rawConfig->pixelFormat);
+
+ if (!rawFormat.isValid())
+ return Invalid;
+
+ if (rawFormat != rawConfig->pixelFormat) {
+ LOG(MaliC55, Debug)
+ << "RAW format adjusted to " << rawFormat;
+ rawConfig->pixelFormat = rawFormat;
+ status = Adjusted;
+ }
+
+ Size rawSize =
+ data_->adjustRawSizes(rawFormat, rawConfig->size);
+ if (rawSize != rawConfig->size) {
+ LOG(MaliC55, Debug)
+ << "RAW sizes adjusted to " << rawSize;
+ rawConfig->size = rawSize;
+ status = Adjusted;
+ }
+
+ maxSize = rawSize;
+
+ const PixelFormatInfo &info = PixelFormatInfo::info(rawConfig->pixelFormat);
+ rawConfig->stride = info.stride(rawConfig->size.width, 0, 4);
+ rawConfig->frameSize = info.frameSize(rawConfig->size, 4);
+
+ rawConfig->setStream(const_cast<Stream *>(&data_->frStream_));
+ frPipeAvailable = false;
+ }
+
+ /*
+ * Adjust processed streams.
+ *
+ * Compute the minimum sensor size to be later used to select the
+ * sensor configuration.
+ */
+ Size minSensorSize = kMaliC55MinInputSize;
+ for (StreamConfiguration &config : config_) {
+ if (isFormatRaw(config.pixelFormat))
+ continue;
+
+ /* Adjust format and size for processed streams. */
+ const auto it = maliC55FmtToCode.find(config.pixelFormat);
+ if (it == maliC55FmtToCode.end()) {
+ LOG(MaliC55, Debug)
+ << "Format adjusted to " << formats::RGB565;
+ config.pixelFormat = formats::RGB565;
+ status = Adjusted;
+ }
+
+ Size size = std::clamp(config.size, kMaliC55MinSize, maxSize);
+ if (size != config.size) {
+ LOG(MaliC55, Debug)
+ << "Size adjusted to " << size;
+ config.size = size;
+ status = Adjusted;
+ }
+
+ if (minSensorSize < size)
+ minSensorSize = size;
+
+ if (frPipeAvailable) {
+ config.setStream(const_cast<Stream *>(&data_->frStream_));
+ frPipeAvailable = false;
+ } else {
+ config.setStream(const_cast<Stream *>(&data_->dsStream_));
+ }
+ }
+
+ /* Compute the sensor format. */
+
+ /* If there's a RAW config, sensor configuration follows it. */
+ if (rawConfig) {
+ sensorFormat_.code = data_->pixfmtToMbusCode(rawConfig->pixelFormat);
+ sensorFormat_.size = rawConfig->size.expandedTo(minSensorSize);
+
+ return status;
+ }
+
+ /* If there's no RAW config, compute the sensor configuration here. */
+ PixelFormat rawFormat = data_->bestRawFormat();
+ if (!rawFormat.isValid())
+ return Invalid;
+
+ sensorFormat_.code = data_->pixfmtToMbusCode(rawFormat);
+
+ uint16_t distance = std::numeric_limits<uint16_t>::max();
+ const auto sizes = data_->sizes(sensorFormat_.code);
+ Size bestSize;
+ for (const auto &size : sizes) {
+ if (minSensorSize.width > size.width ||
+ minSensorSize.height > size.height)
+ continue;
+
+ uint16_t dist = std::abs(static_cast<int>(minSensorSize.width) -
+ static_cast<int>(size.width)) +
+ std::abs(static_cast<int>(minSensorSize.height) -
+ static_cast<int>(size.height));
+ if (dist < distance) {
+ dist = distance;
+ bestSize = size;
+ }
+ }
+ sensorFormat_.size = bestSize;
+
+ LOG(MaliC55, Debug) << "Computed sensor configuration " << sensorFormat_;
+
+ return status;
+}
+
+class PipelineHandlerMaliC55 : public PipelineHandler
+{
+public:
+ PipelineHandlerMaliC55(CameraManager *manager);
+
+ std::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles) override;
+ int configure(Camera *camera, CameraConfiguration *config) override;
+
+ int exportFrameBuffers(Camera *camera, Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
+ int allocateBuffers(Camera *camera);
+ void freeBuffers(Camera *camera);
+
+ int start(Camera *camera, const ControlList *controls) override;
+ void stopDevice(Camera *camera) override;
+
+ int queueRequestDevice(Camera *camera, Request *request) override;
+
+ void imageBufferReady(FrameBuffer *buffer);
+ void paramsBufferReady(FrameBuffer *buffer);
+ void statsBufferReady(FrameBuffer *buffer);
+ void paramsComputed(unsigned int requestId);
+ void statsProcessed(unsigned int requestId, const ControlList &metadata);
+
+ bool match(DeviceEnumerator *enumerator) override;
+
+private:
+ struct MaliC55Pipe {
+ std::unique_ptr<V4L2Subdevice> resizer;
+ std::unique_ptr<V4L2VideoDevice> cap;
+ MediaLink *link;
+ Stream *stream;
+ };
+
+ enum {
+ MaliC55FR,
+ MaliC55DS,
+ MaliC55NumPipes,
+ };
+
+ MaliC55CameraData *cameraData(Camera *camera)
+ {
+ return static_cast<MaliC55CameraData *>(camera->_d());
+ }
+
+ MaliC55Pipe *pipeFromStream(MaliC55CameraData *data, Stream *stream)
+ {
+ if (stream == &data->frStream_)
+ return &pipes_[MaliC55FR];
+ else if (stream == &data->dsStream_)
+ return &pipes_[MaliC55DS];
+ else
+ LOG(MaliC55, Fatal) << "Stream " << stream << " not valid";
+ return nullptr;
+ }
+
+ MaliC55Pipe *pipeFromStream(MaliC55CameraData *data, const Stream *stream)
+ {
+ return pipeFromStream(data, const_cast<Stream *>(stream));
+ }
+
+ void resetPipes()
+ {
+ for (MaliC55Pipe &pipe : pipes_)
+ pipe.stream = nullptr;
+ }
+
+ MaliC55FrameInfo *findFrameInfo(FrameBuffer *buffer);
+ MaliC55FrameInfo *findFrameInfo(Request *request);
+ void tryComplete(MaliC55FrameInfo *info);
+
+ int configureRawStream(MaliC55CameraData *data,
+ const StreamConfiguration &config,
+ V4L2SubdeviceFormat &subdevFormat);
+ int configureProcessedStream(MaliC55CameraData *data,
+ const StreamConfiguration &config,
+ V4L2SubdeviceFormat &subdevFormat);
+
+ void applyScalerCrop(Camera *camera, const ControlList &controls);
+
+ bool registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,
+ const std::string &name);
+ bool registerTPGCamera(MediaLink *link);
+ bool registerSensorCamera(MediaLink *link);
+
+ MediaDevice *media_;
+ std::unique_ptr<V4L2Subdevice> isp_;
+ std::unique_ptr<V4L2VideoDevice> stats_;
+ std::unique_ptr<V4L2VideoDevice> params_;
+
+ std::vector<std::unique_ptr<FrameBuffer>> statsBuffers_;
+ std::queue<FrameBuffer *> availableStatsBuffers_;
+
+ std::vector<std::unique_ptr<FrameBuffer>> paramsBuffers_;
+ std::queue<FrameBuffer *> availableParamsBuffers_;
+
+ std::map<unsigned int, MaliC55FrameInfo> frameInfoMap_;
+
+ std::array<MaliC55Pipe, MaliC55NumPipes> pipes_;
+
+ bool dsFitted_;
+};
+
+PipelineHandlerMaliC55::PipelineHandlerMaliC55(CameraManager *manager)
+ : PipelineHandler(manager), dsFitted_(true)
+{
+}
+
+std::unique_ptr<CameraConfiguration>
+PipelineHandlerMaliC55::generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles)
+{
+ MaliC55CameraData *data = cameraData(camera);
+ std::unique_ptr<CameraConfiguration> config =
+ std::make_unique<MaliC55CameraConfiguration>(data);
+ bool frPipeAvailable = true;
+
+ if (roles.empty())
+ return config;
+
+ /* Check if one stream is RAW to reserve the FR pipe for it. */
+ if (std::find(roles.begin(), roles.end(), StreamRole::Raw) != roles.end())
+ frPipeAvailable = false;
+
+ for (const StreamRole &role : roles) {
+ struct MaliC55Pipe *pipe;
+
+ /* Assign pipe for this role. */
+ if (role == StreamRole::Raw) {
+ pipe = &pipes_[MaliC55FR];
+ } else {
+ if (frPipeAvailable) {
+ pipe = &pipes_[MaliC55FR];
+ frPipeAvailable = false;
+ } else {
+ pipe = &pipes_[MaliC55DS];
+ }
+ }
+
+ Size size = std::min(Size{ 1920, 1080 }, data->resolution());
+ PixelFormat pixelFormat;
+
+ switch (role) {
+ case StreamRole::StillCapture:
+ size = data->resolution();
+ [[fallthrough]];
+ case StreamRole::VideoRecording:
+ pixelFormat = formats::NV12;
+ break;
+
+ case StreamRole::Viewfinder:
+ pixelFormat = formats::RGB565;
+ break;
+
+ case StreamRole::Raw:
+ pixelFormat = data->bestRawFormat();
+ if (!pixelFormat.isValid()) {
+ LOG(MaliC55, Error)
+ << "Camera does not support RAW formats";
+ return nullptr;
+ }
+
+ size = data->resolution();
+ break;
+
+ default:
+ LOG(MaliC55, Error)
+ << "Requested stream role not supported: " << role;
+ return nullptr;
+ }
+
+ std::map<PixelFormat, std::vector<SizeRange>> formats;
+ for (const auto &maliFormat : maliC55FmtToCode) {
+ PixelFormat pixFmt = maliFormat.first;
+ bool isRaw = isFormatRaw(pixFmt);
+
+ /* RAW formats are only supported on the FR pipe. */
+ if (pipe != &pipes_[MaliC55FR] && isRaw)
+ continue;
+
+ if (isRaw) {
+ /* Make sure the mbus code is supported. */
+ int rawCode = data->pixfmtToMbusCode(pixFmt);
+ if (rawCode < 0)
+ continue;
+
+ const auto sizes = data->sizes(rawCode);
+ if (sizes.empty())
+ continue;
+
+ /* And list all sizes the sensor can produce. */
+ std::vector<SizeRange> sizeRanges;
+ std::transform(sizes.begin(), sizes.end(),
+ std::back_inserter(sizeRanges),
+ [](const Size &s) {
+ return SizeRange(s);
+ });
+
+ formats[pixFmt] = sizeRanges;
+ } else {
+ /* Processed formats are always available. */
+ Size maxSize = std::min(kMaliC55MaxSize,
+ data->resolution());
+ formats[pixFmt] = { kMaliC55MinSize, maxSize };
+ }
+ }
+
+ StreamFormats streamFormats(formats);
+ StreamConfiguration cfg(streamFormats);
+ cfg.pixelFormat = pixelFormat;
+ cfg.bufferCount = 4;
+ cfg.size = size;
+
+ config->addConfiguration(cfg);
+ }
+
+ if (config->validate() == CameraConfiguration::Invalid)
+ return nullptr;
+
+ return config;
+}
+
+int PipelineHandlerMaliC55::configureRawStream(MaliC55CameraData *data,
+ const StreamConfiguration &config,
+ V4L2SubdeviceFormat &subdevFormat)
+{
+ Stream *stream = config.stream();
+ MaliC55Pipe *pipe = pipeFromStream(data, stream);
+
+ if (pipe != &pipes_[MaliC55FR]) {
+ LOG(MaliC55, Fatal) << "Only the FR pipe supports RAW capture.";
+ return -EINVAL;
+ }
+
+ /* Enable the debayer route to set fixed internal format on pad #0. */
+ V4L2Subdevice::Routing routing = {};
+ routing.emplace_back(V4L2Subdevice::Stream{ 0, 0 },
+ V4L2Subdevice::Stream{ 1, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+ int ret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);
+ if (ret)
+ return ret;
+
+ unsigned int rawCode = subdevFormat.code;
+ subdevFormat.code = kMaliC55ISPInternalFormat;
+ ret = pipe->resizer->setFormat(0, &subdevFormat);
+ if (ret)
+ return ret;
+
+ /* Enable the bypass route and apply RAW formats there. */
+ routing.clear();
+ routing.emplace_back(V4L2Subdevice::Stream{ 2, 0 },
+ V4L2Subdevice::Stream{ 1, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+ ret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);
+ if (ret)
+ return ret;
+
+ subdevFormat.code = rawCode;
+ ret = pipe->resizer->setFormat(2, &subdevFormat);
+ if (ret)
+ return ret;
+
+ ret = pipe->resizer->setFormat(1, &subdevFormat);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int PipelineHandlerMaliC55::configureProcessedStream(MaliC55CameraData *data,
+ const StreamConfiguration &config,
+ V4L2SubdeviceFormat &subdevFormat)
+{
+ Stream *stream = config.stream();
+ MaliC55Pipe *pipe = pipeFromStream(data, stream);
+
+ /* Enable the debayer route on the resizer pipe. */
+ V4L2Subdevice::Routing routing = {};
+ routing.emplace_back(V4L2Subdevice::Stream{ 0, 0 },
+ V4L2Subdevice::Stream{ 1, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+ int ret = pipe->resizer->setRouting(&routing, V4L2Subdevice::ActiveFormat);
+ if (ret)
+ return ret;
+
+ subdevFormat.code = kMaliC55ISPInternalFormat;
+ ret = pipe->resizer->setFormat(0, &subdevFormat);
+ if (ret)
+ return ret;
+
+ /*
+ * Compute the scaler-in to scaler-out ratio: first center-crop to align
+ * the FOV to the desired resolution, then scale to the desired size.
+ */
+ Size scalerIn = subdevFormat.size.boundedToAspectRatio(config.size);
+ int xCrop = (subdevFormat.size.width - scalerIn.width) / 2;
+ int yCrop = (subdevFormat.size.height - scalerIn.height) / 2;
+ Rectangle ispCrop = { xCrop, yCrop, scalerIn };
+ ret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_CROP, &ispCrop);
+ if (ret)
+ return ret;
+
+ Rectangle ispCompose = { 0, 0, config.size };
+ ret = pipe->resizer->setSelection(0, V4L2_SEL_TGT_COMPOSE, &ispCompose);
+ if (ret)
+ return ret;
+
+ /*
+ * The source pad format size comes directly from the sink
+ * compose rectangle.
+ */
+ subdevFormat.size = ispCompose.size();
+ subdevFormat.code = maliC55FmtToCode.find(config.pixelFormat)->second;
+ return pipe->resizer->setFormat(1, &subdevFormat);
+}
+
+int PipelineHandlerMaliC55::configure(Camera *camera,
+ CameraConfiguration *config)
+{
+ resetPipes();
+
+ int ret = media_->disableLinks();
+ if (ret)
+ return ret;
+
+ /* Link the graph depending if we are operating the TPG or a sensor. */
+ MaliC55CameraData *data = cameraData(camera);
+ if (data->csi_) {
+ const MediaEntity *csiEntity = data->csi_->entity();
+ ret = csiEntity->getPadByIndex(1)->links()[0]->setEnabled(true);
+ } else {
+ ret = data->entity_->getPadByIndex(0)->links()[0]->setEnabled(true);
+ }
+ if (ret)
+ return ret;
+
+ MaliC55CameraConfiguration *maliConfig =
+ static_cast<MaliC55CameraConfiguration *>(config);
+ V4L2SubdeviceFormat subdevFormat = maliConfig->sensorFormat_;
+ ret = data->sd_->getFormat(0, &subdevFormat);
+ if (ret)
+ return ret;
+
+ if (data->sensor_) {
+ ret = data->sensor_->setFormat(&subdevFormat,
+ maliConfig->combinedTransform());
+ if (ret)
+ return ret;
+ }
+
+ if (data->csi_) {
+ ret = data->csi_->setFormat(0, &subdevFormat);
+ if (ret)
+ return ret;
+
+ ret = data->csi_->getFormat(1, &subdevFormat);
+ if (ret)
+ return ret;
+ }
+
+ V4L2DeviceFormat statsFormat;
+ ret = stats_->getFormat(&statsFormat);
+ if (ret)
+ return ret;
+
+ if (statsFormat.planes[0].size != sizeof(struct mali_c55_stats_buffer)) {
+ LOG(MaliC55, Error) << "3a stats buffer size invalid";
+ return -EINVAL;
+ }
+
+ /*
+ * Propagate the format to the ISP sink pad and configure the input
+ * crop rectangle (no crop at the moment).
+ *
+ * \todo Configure the CSI-2 receiver.
+ */
+ ret = isp_->setFormat(0, &subdevFormat);
+ if (ret)
+ return ret;
+
+ Rectangle ispCrop(0, 0, subdevFormat.size);
+ ret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &ispCrop);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure the resizer: fixed format the sink pad; use the media
+ * bus code associated with the desired capture format on the source
+ * pad.
+ *
+ * Configure the crop and compose rectangles to match the desired
+ * stream output size
+ *
+ * \todo Make the crop/scaler configurable
+ */
+ for (const StreamConfiguration &streamConfig : *config) {
+ Stream *stream = streamConfig.stream();
+ MaliC55Pipe *pipe = pipeFromStream(data, stream);
+
+ /*
+ * Enable the media link between the pipe's resizer and the
+ * capture video device
+ */
+
+ ret = pipe->link->setEnabled(true);
+ if (ret) {
+ LOG(MaliC55, Error) << "Couldn't enable resizer's link";
+ return ret;
+ }
+
+ if (isFormatRaw(streamConfig.pixelFormat))
+ ret = configureRawStream(data, streamConfig, subdevFormat);
+ else
+ ret = configureProcessedStream(data, streamConfig, subdevFormat);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to configure pipeline";
+ return ret;
+ }
+
+ /* Now apply the pixel format and size to the capture device. */
+ V4L2DeviceFormat captureFormat;
+ captureFormat.fourcc = pipe->cap->toV4L2PixelFormat(streamConfig.pixelFormat);
+ captureFormat.size = streamConfig.size;
+
+ ret = pipe->cap->setFormat(&captureFormat);
+ if (ret)
+ return ret;
+
+ pipe->stream = stream;
+ }
+
+ if (!data->ipa_)
+ return 0;
+
+ /*
+ * Enable the media link between the ISP subdevice and the statistics
+ * video device.
+ */
+ const MediaEntity *ispEntity = isp_->entity();
+ ret = ispEntity->getPadByIndex(3)->links()[0]->setEnabled(true);
+ if (ret) {
+ LOG(MaliC55, Error) << "Couldn't enable statistics link";
+ return ret;
+ }
+
+ /*
+ * Enable the media link between the ISP subdevice and the parameters
+ * video device.
+ */
+ ret = ispEntity->getPadByIndex(4)->links()[0]->setEnabled(true);
+ if (ret) {
+ LOG(MaliC55, Error) << "Couldn't enable parameters link";
+ return ret;
+ }
+
+ /* We need to inform the IPA of the sensor configuration */
+ ipa::mali_c55::IPAConfigInfo ipaConfig{};
+
+ ret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);
+ if (ret)
+ return ret;
+
+ ipaConfig.sensorControls = data->sensor_->controls();
+
+ /*
+ * And we also need to tell the IPA the bayerOrder of the data (as
+ * affected by any flips that we've configured)
+ */
+ const Transform &combinedTransform = maliConfig->combinedTransform();
+ BayerFormat::Order bayerOrder = data->sensor_->bayerOrder(combinedTransform);
+
+ ControlInfoMap ipaControls;
+ ret = data->ipa_->configure(ipaConfig, utils::to_underlying(bayerOrder),
+ &ipaControls);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to configure IPA";
+ return ret;
+ }
+
+ data->updateControls(ipaControls);
+
+ return 0;
+}
+
+int PipelineHandlerMaliC55::exportFrameBuffers(Camera *camera, Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ MaliC55Pipe *pipe = pipeFromStream(cameraData(camera), stream);
+ unsigned int count = stream->configuration().bufferCount;
+
+ return pipe->cap->exportBuffers(count, buffers);
+}
+
+void PipelineHandlerMaliC55::freeBuffers(Camera *camera)
+{
+ MaliC55CameraData *data = cameraData(camera);
+
+ while (!availableStatsBuffers_.empty())
+ availableStatsBuffers_.pop();
+ while (!availableParamsBuffers_.empty())
+ availableParamsBuffers_.pop();
+
+ statsBuffers_.clear();
+ paramsBuffers_.clear();
+
+ if (data->ipa_) {
+ data->ipa_->unmapBuffers(data->ipaStatBuffers_);
+ data->ipa_->unmapBuffers(data->ipaParamBuffers_);
+ }
+ data->ipaStatBuffers_.clear();
+ data->ipaParamBuffers_.clear();
+
+ if (stats_->releaseBuffers())
+ LOG(MaliC55, Error) << "Failed to release stats buffers";
+
+ if (params_->releaseBuffers())
+ LOG(MaliC55, Error) << "Failed to release params buffers";
+
+ return;
+}
+
+int PipelineHandlerMaliC55::allocateBuffers(Camera *camera)
+{
+ MaliC55CameraData *data = cameraData(camera);
+ unsigned int ipaBufferId = 1;
+ unsigned int bufferCount;
+ int ret;
+
+ bufferCount = std::max({
+ data->frStream_.configuration().bufferCount,
+ data->dsStream_.configuration().bufferCount,
+ });
+
+ ret = stats_->allocateBuffers(bufferCount, &statsBuffers_);
+ if (ret < 0)
+ return ret;
+
+ for (std::unique_ptr<FrameBuffer> &buffer : statsBuffers_) {
+ buffer->setCookie(ipaBufferId++);
+ data->ipaStatBuffers_.emplace_back(buffer->cookie(),
+ buffer->planes());
+ availableStatsBuffers_.push(buffer.get());
+ }
+
+ ret = params_->allocateBuffers(bufferCount, &paramsBuffers_);
+ if (ret < 0)
+ return ret;
+
+ for (std::unique_ptr<FrameBuffer> &buffer : paramsBuffers_) {
+ buffer->setCookie(ipaBufferId++);
+ data->ipaParamBuffers_.emplace_back(buffer->cookie(),
+ buffer->planes());
+ availableParamsBuffers_.push(buffer.get());
+ }
+
+ if (data->ipa_) {
+ data->ipa_->mapBuffers(data->ipaStatBuffers_, true);
+ data->ipa_->mapBuffers(data->ipaParamBuffers_, false);
+ }
+
+ return 0;
+}
+
+int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const ControlList *controls)
+{
+ MaliC55CameraData *data = cameraData(camera);
+ int ret;
+
+ ret = allocateBuffers(camera);
+ if (ret)
+ return ret;
+
+ if (data->ipa_) {
+ ret = data->ipa_->start();
+ if (ret) {
+ LOG(MaliC55, Error)
+ << "Failed to start IPA" << camera->id();
+ freeBuffers(camera);
+ return ret;
+ }
+ }
+
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (!pipe.stream)
+ continue;
+
+ Stream *stream = pipe.stream;
+
+ ret = pipe.cap->importBuffers(stream->configuration().bufferCount);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to import buffers";
+ if (data->ipa_)
+ data->ipa_->stop();
+ freeBuffers(camera);
+ return ret;
+ }
+
+ ret = pipe.cap->streamOn();
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to start stream";
+ if (data->ipa_)
+ data->ipa_->stop();
+ freeBuffers(camera);
+ return ret;
+ }
+ }
+
+ ret = stats_->streamOn();
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to start stats stream";
+
+ if (data->ipa_)
+ data->ipa_->stop();
+
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (pipe.stream)
+ pipe.cap->streamOff();
+ }
+
+ freeBuffers(camera);
+ return ret;
+ }
+
+ ret = params_->streamOn();
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to start params stream";
+
+ stats_->streamOff();
+ if (data->ipa_)
+ data->ipa_->stop();
+
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (pipe.stream)
+ pipe.cap->streamOff();
+ }
+
+ freeBuffers(camera);
+ return ret;
+ }
+
+ ret = isp_->setFrameStartEnabled(true);
+ if (ret)
+ LOG(MaliC55, Error) << "Failed to enable frame start events";
+
+ return 0;
+}
+
+void PipelineHandlerMaliC55::stopDevice(Camera *camera)
+{
+ MaliC55CameraData *data = cameraData(camera);
+
+ isp_->setFrameStartEnabled(false);
+
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (!pipe.stream)
+ continue;
+
+ pipe.cap->streamOff();
+ pipe.cap->releaseBuffers();
+ }
+
+ stats_->streamOff();
+ params_->streamOff();
+ if (data->ipa_)
+ data->ipa_->stop();
+ freeBuffers(camera);
+}
+
+void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera,
+ const ControlList &controls)
+{
+ MaliC55CameraData *data = cameraData(camera);
+
+ const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
+ if (!scalerCrop)
+ return;
+
+ if (!data->sensor_) {
+ LOG(MaliC55, Error) << "ScalerCrop not supported for TPG";
+ return;
+ }
+
+ Rectangle nativeCrop = *scalerCrop;
+
+ IPACameraSensorInfo sensorInfo;
+ int ret = data->sensor_->sensorInfo(&sensorInfo);
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to retrieve sensor info";
+ return;
+ }
+
+ /*
+ * The ScalerCrop rectangle re-scaling in the ISP crop rectangle
+ * comes straight from the RPi pipeline handler.
+ *
+ * Create a version of the crop rectangle aligned to the analogue crop
+ * rectangle top-left coordinates and scaled in the [analogue crop to
+ * output frame] ratio to take into account binning/skipping on the
+ * sensor.
+ */
+ Rectangle ispCrop = nativeCrop.translatedBy(-sensorInfo.analogCrop
+ .topLeft());
+ ispCrop.scaleBy(sensorInfo.outputSize, sensorInfo.analogCrop.size());
+
+ /*
+ * The crop rectangle should 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.
+ */
+ Rectangle ispMinCrop{ 0, 0, 640, 480 };
+ Size minSize = ispMinCrop.size().expandedToAspectRatio(nativeCrop.size());
+ Size size = ispCrop.size().expandedTo(minSize);
+ ispCrop = size.centeredTo(ispCrop.center())
+ .enclosedIn(Rectangle(sensorInfo.outputSize));
+
+ /*
+ * As the resizer can't upscale, the crop rectangle has to be larger
+ * than the larger stream output size.
+ */
+ Size maxYuvSize;
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (!pipe.stream)
+ continue;
+
+ const StreamConfiguration &config = pipe.stream->configuration();
+ if (isFormatRaw(config.pixelFormat)) {
+ LOG(MaliC55, Debug) << "Cannot crop with a RAW stream";
+ return;
+ }
+
+ Size streamSize = config.size;
+ if (streamSize.width > maxYuvSize.width)
+ maxYuvSize.width = streamSize.width;
+ if (streamSize.height > maxYuvSize.height)
+ maxYuvSize.height = streamSize.height;
+ }
+
+ ispCrop.size().expandTo(maxYuvSize);
+
+ /*
+ * Now apply the scaler crop to each enabled output. This overrides the
+ * crop configuration performed at configure() time and can cause
+ * square pixels if the crop rectangle and scaler output FOV ratio are
+ * different.
+ */
+ for (MaliC55Pipe &pipe : pipes_) {
+ if (!pipe.stream)
+ continue;
+
+ /* Create a copy to avoid setSelection() to modify ispCrop. */
+ Rectangle pipeCrop = ispCrop;
+ ret = pipe.resizer->setSelection(0, V4L2_SEL_TGT_CROP, &pipeCrop);
+ if (ret) {
+ LOG(MaliC55, Error)
+ << "Failed to apply crop to "
+ << (pipe.stream == &data->frStream_ ?
+ "FR" : "DS") << " pipe";
+ return;
+ }
+ }
+}
+
+int PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request)
+{
+ MaliC55CameraData *data = cameraData(camera);
+
+ /* Do not run the IPA if the TPG is in use. */
+ if (!data->ipa_) {
+ MaliC55FrameInfo frameInfo;
+ frameInfo.request = request;
+ frameInfo.statBuffer = nullptr;
+ frameInfo.paramBuffer = nullptr;
+ frameInfo.paramsDone = true;
+ frameInfo.statsDone = true;
+
+ frameInfoMap_[request->sequence()] = frameInfo;
+
+ for (auto &[stream, buffer] : request->buffers()) {
+ MaliC55Pipe *pipe = pipeFromStream(data, stream);
+
+ pipe->cap->queueBuffer(buffer);
+ }
+
+ return 0;
+ }
+
+ if (availableStatsBuffers_.empty()) {
+ LOG(MaliC55, Error) << "Stats buffer underrun";
+ return -ENOENT;
+ }
+
+ if (availableParamsBuffers_.empty()) {
+ LOG(MaliC55, Error) << "Params buffer underrun";
+ return -ENOENT;
+ }
+
+ MaliC55FrameInfo frameInfo;
+ frameInfo.request = request;
+
+ frameInfo.statBuffer = availableStatsBuffers_.front();
+ availableStatsBuffers_.pop();
+ frameInfo.paramBuffer = availableParamsBuffers_.front();
+ availableParamsBuffers_.pop();
+
+ frameInfo.paramsDone = false;
+ frameInfo.statsDone = false;
+
+ frameInfoMap_[request->sequence()] = frameInfo;
+
+ data->ipa_->queueRequest(request->sequence(), request->controls());
+ data->ipa_->fillParams(request->sequence(),
+ frameInfo.paramBuffer->cookie());
+
+ return 0;
+}
+
+MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(Request *request)
+{
+ for (auto &[sequence, info] : frameInfoMap_) {
+ if (info.request == request)
+ return &info;
+ }
+
+ return nullptr;
+}
+
+MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(FrameBuffer *buffer)
+{
+ for (auto &[sequence, info] : frameInfoMap_) {
+ if (info.paramBuffer == buffer ||
+ info.statBuffer == buffer)
+ return &info;
+ }
+
+ return nullptr;
+}
+
+void PipelineHandlerMaliC55::tryComplete(MaliC55FrameInfo *info)
+{
+ if (!info->paramsDone)
+ return;
+ if (!info->statsDone)
+ return;
+
+ Request *request = info->request;
+ if (request->hasPendingBuffers())
+ return;
+
+ if (info->statBuffer)
+ availableStatsBuffers_.push(info->statBuffer);
+ if (info->paramBuffer)
+ availableParamsBuffers_.push(info->paramBuffer);
+
+ frameInfoMap_.erase(request->sequence());
+
+ completeRequest(request);
+}
+
+void PipelineHandlerMaliC55::imageBufferReady(FrameBuffer *buffer)
+{
+ Request *request = buffer->request();
+ MaliC55FrameInfo *info = findFrameInfo(request);
+ ASSERT(info);
+
+ if (completeBuffer(request, buffer))
+ tryComplete(info);
+}
+
+void PipelineHandlerMaliC55::paramsBufferReady(FrameBuffer *buffer)
+{
+ MaliC55FrameInfo *info = findFrameInfo(buffer);
+ ASSERT(info);
+
+ info->paramsDone = true;
+
+ tryComplete(info);
+}
+
+void PipelineHandlerMaliC55::statsBufferReady(FrameBuffer *buffer)
+{
+ MaliC55FrameInfo *info = findFrameInfo(buffer);
+ ASSERT(info);
+
+ Request *request = info->request;
+ MaliC55CameraData *data = cameraData(request->_d()->camera());
+
+ ControlList sensorControls = data->delayedCtrls_->get(buffer->metadata().sequence);
+
+ data->ipa_->processStats(request->sequence(), buffer->cookie(),
+ sensorControls);
+}
+
+void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId)
+{
+ MaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];
+ Request *request = frameInfo.request;
+ MaliC55CameraData *data = cameraData(request->_d()->camera());
+
+ /*
+ * Queue buffers for stats and params, then queue buffers to the capture
+ * video devices.
+ */
+
+ frameInfo.paramBuffer->_d()->metadata().planes()[0].bytesused =
+ sizeof(struct mali_c55_params_buffer);
+ params_->queueBuffer(frameInfo.paramBuffer);
+ stats_->queueBuffer(frameInfo.statBuffer);
+
+ for (auto &[stream, buffer] : request->buffers()) {
+ MaliC55Pipe *pipe = pipeFromStream(data, stream);
+
+ pipe->cap->queueBuffer(buffer);
+ }
+}
+
+void PipelineHandlerMaliC55::statsProcessed(unsigned int requestId,
+ const ControlList &metadata)
+{
+ MaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];
+
+ frameInfo.statsDone = true;
+ frameInfo.request->metadata().merge(metadata);
+
+ tryComplete(&frameInfo);
+}
+
+bool PipelineHandlerMaliC55::registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,
+ const std::string &name)
+{
+ if (data->loadIPA())
+ return false;
+
+ if (data->ipa_) {
+ data->ipa_->statsProcessed.connect(this, &PipelineHandlerMaliC55::statsProcessed);
+ data->ipa_->paramsComputed.connect(this, &PipelineHandlerMaliC55::paramsComputed);
+ }
+
+ std::set<Stream *> streams{ &data->frStream_ };
+ if (dsFitted_)
+ streams.insert(&data->dsStream_);
+
+ std::shared_ptr<Camera> camera = Camera::create(std::move(data),
+ name, streams);
+ registerCamera(std::move(camera));
+
+ return true;
+}
+
+/*
+ * The only camera we support through direct connection to the ISP is the
+ * Mali-C55 TPG. Check we have that and warn if not.
+ */
+bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link)
+{
+ const std::string &name = link->source()->entity()->name();
+ if (name != "mali-c55 tpg") {
+ LOG(MaliC55, Warning) << "Unsupported direct connection to "
+ << link->source()->entity()->name();
+ /*
+ * Return true and just skip registering a camera for this
+ * entity.
+ */
+ return true;
+ }
+
+ std::unique_ptr<MaliC55CameraData> data =
+ std::make_unique<MaliC55CameraData>(this, link->source()->entity());
+
+ if (data->init())
+ return false;
+
+ return registerMaliCamera(std::move(data), name);
+}
+
+/*
+ * Register a Camera for each sensor connected to the ISP through a CSI-2
+ * receiver.
+ *
+ * \todo Support more complex topologies, such as video muxes.
+ */
+bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)
+{
+ MediaEntity *csi2 = ispLink->source()->entity();
+ const MediaPad *csi2Sink = csi2->getPadByIndex(0);
+
+ for (MediaLink *link : csi2Sink->links()) {
+ MediaEntity *sensor = link->source()->entity();
+ unsigned int function = sensor->function();
+
+ if (function != MEDIA_ENT_F_CAM_SENSOR)
+ continue;
+
+ std::unique_ptr<MaliC55CameraData> data =
+ std::make_unique<MaliC55CameraData>(this, sensor);
+ if (data->init())
+ return false;
+
+ data->properties_ = data->sensor_->properties();
+
+ const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
+ std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
+ { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+ { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
+ };
+
+ data->delayedCtrls_ =
+ std::make_unique<DelayedControls>(data->sensor_->device(),
+ params);
+ isp_->frameStart.connect(data->delayedCtrls_.get(),
+ &DelayedControls::applyControls);
+
+ /* \todo Init properties. */
+
+ if (!registerMaliCamera(std::move(data), sensor->name()))
+ return false;
+ }
+
+ return true;
+}
+
+bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
+{
+ const MediaPad *ispSink;
+
+ /*
+ * We search for just the always-available elements of the media graph.
+ * The TPG and the downscale pipe are both optional blocks and may not
+ * be fitted.
+ */
+ DeviceMatch dm("mali-c55");
+ dm.add("mali-c55 isp");
+ dm.add("mali-c55 resizer fr");
+ dm.add("mali-c55 fr");
+ dm.add("mali-c55 3a stats");
+ dm.add("mali-c55 3a params");
+
+ media_ = acquireMediaDevice(enumerator, dm);
+ if (!media_)
+ return false;
+
+ isp_ = V4L2Subdevice::fromEntityName(media_, "mali-c55 isp");
+ if (isp_->open() < 0)
+ return false;
+
+ stats_ = V4L2VideoDevice::fromEntityName(media_, "mali-c55 3a stats");
+ if (stats_->open() < 0)
+ return false;
+
+ params_ = V4L2VideoDevice::fromEntityName(media_, "mali-c55 3a params");
+ if (params_->open() < 0)
+ return false;
+
+ MaliC55Pipe *frPipe = &pipes_[MaliC55FR];
+ frPipe->resizer = V4L2Subdevice::fromEntityName(media_, "mali-c55 resizer fr");
+ if (frPipe->resizer->open() < 0)
+ return false;
+
+ frPipe->cap = V4L2VideoDevice::fromEntityName(media_, "mali-c55 fr");
+ if (frPipe->cap->open() < 0)
+ return false;
+
+ frPipe->link = media_->link("mali-c55 resizer fr", 1, "mali-c55 fr", 0);
+ if (!frPipe->link) {
+ LOG(MaliC55, Error) << "No link between fr resizer and video node";
+ return false;
+ }
+
+ frPipe->cap->bufferReady.connect(this, &PipelineHandlerMaliC55::imageBufferReady);
+
+ dsFitted_ = !!media_->getEntityByName("mali-c55 ds");
+ if (dsFitted_) {
+ LOG(MaliC55, Debug) << "Downscaler pipe is fitted";
+
+ MaliC55Pipe *dsPipe = &pipes_[MaliC55DS];
+
+ dsPipe->resizer = V4L2Subdevice::fromEntityName(media_, "mali-c55 resizer ds");
+ if (dsPipe->resizer->open() < 0)
+ return false;
+
+ dsPipe->cap = V4L2VideoDevice::fromEntityName(media_, "mali-c55 ds");
+ if (dsPipe->cap->open() < 0)
+ return false;
+
+ dsPipe->link = media_->link("mali-c55 resizer ds", 1,
+ "mali-c55 ds", 0);
+ if (!dsPipe->link) {
+ LOG(MaliC55, Error) << "No link between ds resizer and video node";
+ return false;
+ }
+
+ dsPipe->cap->bufferReady.connect(this, &PipelineHandlerMaliC55::imageBufferReady);
+ }
+
+ stats_->bufferReady.connect(this, &PipelineHandlerMaliC55::statsBufferReady);
+ params_->bufferReady.connect(this, &PipelineHandlerMaliC55::paramsBufferReady);
+
+ ispSink = isp_->entity()->getPadByIndex(0);
+ if (!ispSink || ispSink->links().empty()) {
+ LOG(MaliC55, Error) << "ISP sink pad error";
+ return false;
+ }
+
+ /*
+ * We could have several links pointing to the ISP's sink pad, which
+ * will be from entities with one of the following functions:
+ *
+ * MEDIA_ENT_F_CAM_SENSOR - The test pattern generator
+ * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver
+ * MEDIA_ENT_F_IO_V4L - An input device
+ *
+ * The last one will be unsupported for now. The TPG is relatively easy,
+ * we just register a Camera for it. If we have a CSI-2 receiver we need
+ * to check its sink pad and register Cameras for anything connected to
+ * it (probably...there are some complex situations in which that might
+ * not be true but let's pretend they don't exist until we come across
+ * them)
+ */
+ bool registered;
+ for (MediaLink *link : ispSink->links()) {
+ unsigned int function = link->source()->entity()->function();
+
+ switch (function) {
+ case MEDIA_ENT_F_CAM_SENSOR:
+ registered = registerTPGCamera(link);
+ if (!registered)
+ return registered;
+
+ break;
+ case MEDIA_ENT_F_VID_IF_BRIDGE:
+ registered = registerSensorCamera(link);
+ if (!registered)
+ return registered;
+
+ break;
+ case MEDIA_ENT_F_IO_V4L:
+ LOG(MaliC55, Warning) << "Memory input not yet supported";
+ break;
+ default:
+ LOG(MaliC55, Error) << "Unsupported entity function";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+REGISTER_PIPELINE_HANDLER(PipelineHandlerMaliC55, "mali-c55")
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/mali-c55/meson.build b/src/libcamera/pipeline/mali-c55/meson.build
new file mode 100644
index 00000000..eba8e5a3
--- /dev/null
+++ b/src/libcamera/pipeline/mali-c55/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_internal_sources += files([
+ 'mali-c55.cpp'
+])
diff --git a/src/libcamera/pipeline/rkisp1/meson.build b/src/libcamera/pipeline/rkisp1/meson.build
index cad66535..d21a6ef9 100644
--- a/src/libcamera/pipeline/rkisp1/meson.build
+++ b/src/libcamera/pipeline/rkisp1/meson.build
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'rkisp1.cpp',
'rkisp1_path.cpp',
])
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 586b46d6..675f0a74 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -2,15 +2,16 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * rkisp1.cpp - Pipeline handler for Rockchip ISP1
+ * Pipeline handler for Rockchip ISP1
*/
#include <algorithm>
-#include <array>
-#include <iomanip>
+#include <map>
#include <memory>
#include <numeric>
+#include <optional>
#include <queue>
+#include <vector>
#include <linux/media-bus-format.h>
#include <linux/rkisp1-config.h>
@@ -23,6 +24,7 @@
#include <libcamera/control_ids.h>
#include <libcamera/formats.h>
#include <libcamera/framebuffer.h>
+#include <libcamera/property_ids.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
#include <libcamera/transform.h>
@@ -33,11 +35,14 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/camera_sensor_properties.h"
+#include "libcamera/internal/converter/converter_v4l2_m2m.h"
#include "libcamera/internal/delayed_controls.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/media_pipeline.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
@@ -94,6 +99,7 @@ public:
}
PipelineHandlerRkISP1 *pipe();
+ const PipelineHandlerRkISP1 *pipe() const;
int loadIPA(unsigned int hwRevision);
Stream mainPathStream_;
@@ -109,8 +115,15 @@ public:
std::unique_ptr<ipa::rkisp1::IPAProxyRkISP1> ipa_;
+ ControlInfoMap ipaControls_;
+
+ /*
+ * All entities in the pipeline, from the camera sensor to the RKISP1.
+ */
+ MediaPipeline pipe_;
+
private:
- void paramFilled(unsigned int frame);
+ void paramsComputed(unsigned int frame, unsigned int bytesused);
void setSensorControls(unsigned int frame,
const ControlList &sensorControls);
@@ -170,25 +183,27 @@ private:
}
friend RkISP1CameraData;
+ friend RkISP1CameraConfiguration;
friend RkISP1Frames;
- int initLinks(Camera *camera, const CameraSensor *sensor,
- const RkISP1CameraConfiguration &config);
+ int initLinks(Camera *camera, const RkISP1CameraConfiguration &config);
int createCamera(MediaEntity *sensor);
void tryCompleteRequest(RkISP1FrameInfo *info);
- void bufferReady(FrameBuffer *buffer);
- void paramReady(FrameBuffer *buffer);
- void statReady(FrameBuffer *buffer);
+ void imageBufferReady(FrameBuffer *buffer);
+ void paramBufferReady(FrameBuffer *buffer);
+ void statBufferReady(FrameBuffer *buffer);
+ void dewarpBufferReady(FrameBuffer *buffer);
void frameStart(uint32_t sequence);
int allocateBuffers(Camera *camera);
int freeBuffers(Camera *camera);
+ int updateControls(RkISP1CameraData *data);
+
MediaDevice *media_;
std::unique_ptr<V4L2Subdevice> isp_;
std::unique_ptr<V4L2VideoDevice> param_;
std::unique_ptr<V4L2VideoDevice> stat_;
- std::unique_ptr<V4L2Subdevice> csi_;
bool hasSelfPath_;
bool isRaw_;
@@ -196,14 +211,22 @@ private:
RkISP1MainPath mainPath_;
RkISP1SelfPath selfPath_;
+ std::unique_ptr<V4L2M2MConverter> dewarper_;
+ Rectangle scalerMaxCrop_;
+ bool useDewarper_;
+
+ std::optional<Rectangle> activeCrop_;
+
+ /* Internal buffers used when dewarper is being used */
+ std::vector<std::unique_ptr<FrameBuffer>> mainPathBuffers_;
+ std::queue<FrameBuffer *> availableMainPathBuffers_;
+
std::vector<std::unique_ptr<FrameBuffer>> paramBuffers_;
std::vector<std::unique_ptr<FrameBuffer>> statBuffers_;
std::queue<FrameBuffer *> availableParamBuffers_;
std::queue<FrameBuffer *> availableStatBuffers_;
Camera *activeCamera_;
-
- const MediaPad *ispSink_;
};
RkISP1Frames::RkISP1Frames(PipelineHandler *pipe)
@@ -218,6 +241,8 @@ RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
FrameBuffer *paramBuffer = nullptr;
FrameBuffer *statBuffer = nullptr;
+ FrameBuffer *mainPathBuffer = nullptr;
+ FrameBuffer *selfPathBuffer = nullptr;
if (!isRaw) {
if (pipe_->availableParamBuffers_.empty()) {
@@ -235,10 +260,16 @@ RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
statBuffer = pipe_->availableStatBuffers_.front();
pipe_->availableStatBuffers_.pop();
+
+ if (pipe_->useDewarper_) {
+ mainPathBuffer = pipe_->availableMainPathBuffers_.front();
+ pipe_->availableMainPathBuffers_.pop();
+ }
}
- FrameBuffer *mainPathBuffer = request->findBuffer(&data->mainPathStream_);
- FrameBuffer *selfPathBuffer = request->findBuffer(&data->selfPathStream_);
+ if (!mainPathBuffer)
+ mainPathBuffer = request->findBuffer(&data->mainPathStream_);
+ selfPathBuffer = request->findBuffer(&data->selfPathStream_);
RkISP1FrameInfo *info = new RkISP1FrameInfo;
@@ -264,6 +295,7 @@ int RkISP1Frames::destroy(unsigned int frame)
pipe_->availableParamBuffers_.push(info->paramBuffer);
pipe_->availableStatBuffers_.push(info->statBuffer);
+ pipe_->availableMainPathBuffers_.push(info->mainPathBuffer);
frameInfo_.erase(info->frame);
@@ -279,6 +311,7 @@ void RkISP1Frames::clear()
pipe_->availableParamBuffers_.push(info->paramBuffer);
pipe_->availableStatBuffers_.push(info->statBuffer);
+ pipe_->availableMainPathBuffers_.push(info->mainPathBuffer);
delete info;
}
@@ -334,6 +367,11 @@ PipelineHandlerRkISP1 *RkISP1CameraData::pipe()
return static_cast<PipelineHandlerRkISP1 *>(Camera::Private::pipe());
}
+const PipelineHandlerRkISP1 *RkISP1CameraData::pipe() const
+{
+ return static_cast<const PipelineHandlerRkISP1 *>(Camera::Private::pipe());
+}
+
int RkISP1CameraData::loadIPA(unsigned int hwRevision)
{
ipa_ = IPAManager::createIPA<ipa::rkisp1::IPAProxyRkISP1>(pipe(), 1, 1);
@@ -341,26 +379,12 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision)
return -ENOENT;
ipa_->setSensorControls.connect(this, &RkISP1CameraData::setSensorControls);
- ipa_->paramsBufferReady.connect(this, &RkISP1CameraData::paramFilled);
+ ipa_->paramsComputed.connect(this, &RkISP1CameraData::paramsComputed);
ipa_->metadataReady.connect(this, &RkISP1CameraData::metadataReady);
- /*
- * The API tuning file is made from the sensor name unless the
- * environment variable overrides it.
- */
- std::string ipaTuningFile;
- char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RKISP1_TUNING_FILE");
- if (!configFromEnv || *configFromEnv == '\0') {
- ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml");
- /*
- * If the tuning file isn't found, fall back to the
- * 'uncalibrated' configuration file.
- */
- if (ipaTuningFile.empty())
- ipaTuningFile = ipa_->configurationFile("uncalibrated.yaml");
- } else {
- ipaTuningFile = std::string(configFromEnv);
- }
+ /* The IPA tuning file is made from the sensor name. */
+ std::string ipaTuningFile =
+ ipa_->configurationFile(sensor_->model() + ".yaml", "uncalibrated.yaml");
IPACameraSensorInfo sensorInfo{};
int ret = sensor_->sensorInfo(&sensorInfo);
@@ -370,7 +394,7 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision)
}
ret = ipa_->init({ ipaTuningFile, sensor_->model() }, hwRevision,
- sensorInfo, sensor_->controls(), &controlInfo_);
+ sensorInfo, sensor_->controls(), &ipaControls_);
if (ret < 0) {
LOG(RkISP1, Error) << "IPA initialization failure";
return ret;
@@ -379,15 +403,14 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision)
return 0;
}
-void RkISP1CameraData::paramFilled(unsigned int frame)
+void RkISP1CameraData::paramsComputed(unsigned int frame, unsigned int bytesused)
{
PipelineHandlerRkISP1 *pipe = RkISP1CameraData::pipe();
RkISP1FrameInfo *info = frameInfo_.find(frame);
if (!info)
return;
- info->paramBuffer->_d()->metadata().planes()[0].bytesused =
- sizeof(struct rkisp1_params_cfg);
+ info->paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused;
pipe->param_->queueBuffer(info->paramBuffer);
pipe->stat_->queueBuffer(info->statBuffer);
@@ -454,11 +477,12 @@ bool RkISP1CameraConfiguration::fitsAllPaths(const StreamConfiguration &cfg)
StreamConfiguration config;
config = cfg;
- if (data_->mainPath_->validate(sensor, &config) != Valid)
+ if (data_->mainPath_->validate(sensor, sensorConfig, &config) != Valid)
return false;
config = cfg;
- if (data_->selfPath_ && data_->selfPath_->validate(sensor, &config) != Valid)
+ if (data_->selfPath_ &&
+ data_->selfPath_->validate(sensor, sensorConfig, &config) != Valid)
return false;
return true;
@@ -466,6 +490,7 @@ bool RkISP1CameraConfiguration::fitsAllPaths(const StreamConfiguration &cfg)
CameraConfiguration::Status RkISP1CameraConfiguration::validate()
{
+ const PipelineHandlerRkISP1 *pipe = data_->pipe();
const CameraSensor *sensor = data_->sensor_.get();
unsigned int pathCount = data_->selfPath_ ? 2 : 1;
Status status;
@@ -475,6 +500,27 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
status = validateColorSpaces(ColorSpaceFlag::StreamsShareColorSpace);
+ /*
+ * Make sure that if a sensor configuration has been requested it
+ * is valid.
+ */
+ if (sensorConfig) {
+ if (!sensorConfig->isValid()) {
+ LOG(RkISP1, Error)
+ << "Invalid sensor configuration request";
+
+ return Invalid;
+ }
+
+ unsigned int bitDepth = sensorConfig->bitDepth;
+ if (bitDepth != 8 && bitDepth != 10 && bitDepth != 12) {
+ LOG(RkISP1, Error)
+ << "Invalid sensor configuration bit depth";
+
+ return Invalid;
+ }
+ }
+
/* Cap the number of entries to the available streams. */
if (config_.size() > pathCount) {
config_.resize(pathCount);
@@ -501,6 +547,18 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
}
}
+ bool useDewarper = false;
+ if (pipe->dewarper_) {
+ /*
+ * Platforms with dewarper support, such as i.MX8MP, support
+ * only a single stream. We can inspect config_[0] only here.
+ */
+ bool isRaw = PixelFormatInfo::info(config_[0].pixelFormat).colourEncoding ==
+ PixelFormatInfo::ColourEncodingRAW;
+ if (!isRaw)
+ useDewarper = true;
+ }
+
/*
* If there are more than one stream in the configuration figure out the
* order to evaluate the streams. The first stream has the highest
@@ -513,50 +571,72 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
if (config_.size() == 2 && fitsAllPaths(config_[0]))
std::reverse(order.begin(), order.end());
+ /*
+ * Validate the configuration against the desired path and, if the
+ * platform supports it, the dewarper.
+ */
+ auto validateConfig = [&](StreamConfiguration &cfg, RkISP1Path *path,
+ Stream *stream, Status expectedStatus) {
+ StreamConfiguration tryCfg = cfg;
+
+ Status ret = path->validate(sensor, sensorConfig, &tryCfg);
+ if (ret == Invalid)
+ return false;
+
+ if (!useDewarper &&
+ (expectedStatus == Valid && ret == Adjusted))
+ return false;
+
+ if (useDewarper) {
+ bool adjusted;
+
+ pipe->dewarper_->validateOutput(&tryCfg, &adjusted,
+ Converter::Alignment::Down);
+ if (expectedStatus == Valid && adjusted)
+ return false;
+ }
+
+ cfg = tryCfg;
+ cfg.setStream(stream);
+ return true;
+ };
+
bool mainPathAvailable = true;
bool selfPathAvailable = data_->selfPath_;
+ RkISP1Path *mainPath = data_->mainPath_;
+ RkISP1Path *selfPath = data_->selfPath_;
+ Stream *mainPathStream = const_cast<Stream *>(&data_->mainPathStream_);
+ Stream *selfPathStream = const_cast<Stream *>(&data_->selfPathStream_);
for (unsigned int index : order) {
StreamConfiguration &cfg = config_[index];
/* Try to match stream without adjusting configuration. */
if (mainPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->mainPath_->validate(sensor, &tryCfg) == Valid) {
+ if (validateConfig(cfg, mainPath, mainPathStream, Valid)) {
mainPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
continue;
}
}
if (selfPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->selfPath_->validate(sensor, &tryCfg) == Valid) {
+ if (validateConfig(cfg, selfPath, selfPathStream, Valid)) {
selfPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
continue;
}
}
/* Try to match stream allowing adjusting configuration. */
if (mainPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->mainPath_->validate(sensor, &tryCfg) == Adjusted) {
+ if (validateConfig(cfg, mainPath, mainPathStream, Adjusted)) {
mainPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
status = Adjusted;
continue;
}
}
if (selfPathAvailable) {
- StreamConfiguration tryCfg = cfg;
- if (data_->selfPath_->validate(sensor, &tryCfg) == Adjusted) {
+ if (validateConfig(cfg, selfPath, selfPathStream, Adjusted)) {
selfPathAvailable = false;
- cfg = tryCfg;
- cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
status = Adjusted;
continue;
}
@@ -590,7 +670,8 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
[](const auto &value) { return value.second; });
}
- sensorFormat_ = sensor->getFormat(mbusCodes, maxSize);
+ sensorFormat_ = sensor->getFormat(mbusCodes, maxSize,
+ mainPath->maxResolution());
if (sensorFormat_.size.isNull())
sensorFormat_.size = sensor->resolution();
@@ -603,7 +684,7 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
*/
PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
- : PipelineHandler(manager), hasSelfPath_(true)
+ : PipelineHandler(manager), hasSelfPath_(true), useDewarper_(false)
{
}
@@ -719,7 +800,7 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
CameraSensor *sensor = data->sensor_.get();
int ret;
- ret = initLinks(camera, sensor, *config);
+ ret = initLinks(camera, *config);
if (ret)
return ret;
@@ -730,44 +811,70 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
V4L2SubdeviceFormat format = config->sensorFormat();
LOG(RkISP1, Debug) << "Configuring sensor with " << format;
- ret = sensor->setFormat(&format, config->combinedTransform());
+ if (config->sensorConfig)
+ ret = sensor->applyConfiguration(*config->sensorConfig,
+ config->combinedTransform(),
+ &format);
+ else
+ ret = sensor->setFormat(&format, config->combinedTransform());
+
if (ret < 0)
return ret;
LOG(RkISP1, Debug) << "Sensor configured with " << format;
- if (csi_) {
- ret = csi_->setFormat(0, &format);
- if (ret < 0)
- return ret;
- }
+ /* Propagate format through the internal media pipeline up to the ISP */
+ ret = data->pipe_.configure(sensor, &format);
+ if (ret < 0)
+ return ret;
+ LOG(RkISP1, Debug) << "Configuring ISP with : " << format;
ret = isp_->setFormat(0, &format);
if (ret < 0)
return ret;
- Rectangle rect(0, 0, format.size);
- ret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &rect);
+ Rectangle inputCrop(0, 0, format.size);
+ ret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &inputCrop);
if (ret < 0)
return ret;
LOG(RkISP1, Debug)
<< "ISP input pad configured with " << format
- << " crop " << rect;
+ << " crop " << inputCrop;
+ Rectangle outputCrop = inputCrop;
const PixelFormat &streamFormat = config->at(0).pixelFormat;
const PixelFormatInfo &info = PixelFormatInfo::info(streamFormat);
isRaw_ = info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;
+ useDewarper_ = dewarper_ && !isRaw_;
/* YUYV8_2X8 is required on the ISP source path pad for YUV output. */
if (!isRaw_)
- format.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
+ format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+
+ /*
+ * On devices without DUAL_CROP (like the imx8mp) cropping needs to be
+ * done on the ISP/IS output.
+ */
+ if (media_->hwRevision() == RKISP1_V_IMX8MP) {
+ /* imx8mp has only a single path. */
+ const auto &cfg = config->at(0);
+ Size ispCrop = format.size.boundedToAspectRatio(cfg.size);
+ if (useDewarper_)
+ ispCrop = dewarper_->adjustInputSize(cfg.pixelFormat,
+ ispCrop);
+ else
+ ispCrop.alignUpTo(2, 2);
+
+ outputCrop = ispCrop.centeredTo(Rectangle(format.size).center());
+ format.size = ispCrop;
+ }
LOG(RkISP1, Debug)
<< "Configuring ISP output pad with " << format
- << " crop " << rect;
+ << " crop " << outputCrop;
- ret = isp_->setSelection(2, V4L2_SEL_TGT_CROP, &rect);
+ ret = isp_->setSelection(2, V4L2_SEL_TGT_CROP, &outputCrop);
if (ret < 0)
return ret;
@@ -778,15 +885,37 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
LOG(RkISP1, Debug)
<< "ISP output pad configured with " << format
- << " crop " << rect;
+ << " crop " << outputCrop;
+
+ IPACameraSensorInfo sensorInfo;
+ ret = data->sensor_->sensorInfo(&sensorInfo);
+ if (ret)
+ return ret;
std::map<unsigned int, IPAStream> streamConfig;
+ std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
for (const StreamConfiguration &cfg : *config) {
if (cfg.stream() == &data->mainPathStream_) {
ret = mainPath_.configure(cfg, format);
streamConfig[0] = IPAStream(cfg.pixelFormat,
cfg.size);
+ /* Configure dewarp */
+ if (dewarper_ && !isRaw_) {
+ outputCfgs.push_back(const_cast<StreamConfiguration &>(cfg));
+ ret = dewarper_->configure(cfg, outputCfgs);
+ if (ret)
+ return ret;
+
+ /*
+ * Calculate the crop rectangle of the data
+ * flowing into the dewarper in sensor
+ * coordinates.
+ */
+ scalerMaxCrop_ =
+ outputCrop.transformedBetween(inputCrop,
+ sensorInfo.analogCrop);
+ }
} else if (hasSelfPath_) {
ret = selfPath_.configure(cfg, format);
streamConfig[1] = IPAStream(cfg.pixelFormat,
@@ -800,7 +929,7 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
}
V4L2DeviceFormat paramFormat;
- paramFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RK_ISP1_PARAMS);
+ paramFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RK_ISP1_EXT_PARAMS);
ret = param_->setFormat(&paramFormat);
if (ret)
return ret;
@@ -812,20 +941,17 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
return ret;
/* Inform IPA of stream configuration and sensor controls. */
- ipa::rkisp1::IPAConfigInfo ipaConfig{};
+ ipa::rkisp1::IPAConfigInfo ipaConfig{ sensorInfo,
+ data->sensor_->controls(),
+ paramFormat.fourcc };
- ret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);
- if (ret)
- return ret;
-
- ipaConfig.sensorControls = data->sensor_->controls();
-
- ret = data->ipa_->configure(ipaConfig, streamConfig, &data->controlInfo_);
+ ret = data->ipa_->configure(ipaConfig, streamConfig, &data->ipaControls_);
if (ret) {
LOG(RkISP1, Error) << "failed configuring IPA (" << ret << ")";
return ret;
}
- return 0;
+
+ return updateControls(data);
}
int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
@@ -834,10 +960,19 @@ int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, S
RkISP1CameraData *data = cameraData(camera);
unsigned int count = stream->configuration().bufferCount;
- if (stream == &data->mainPathStream_)
- return mainPath_.exportBuffers(count, buffers);
- else if (hasSelfPath_ && stream == &data->selfPathStream_)
+ if (stream == &data->mainPathStream_) {
+ /*
+ * Currently, i.MX8MP is the only platform with DW100 dewarper.
+ * It has mainpath and no self path. Hence, export buffers from
+ * dewarper just for the main path stream, for now.
+ */
+ if (useDewarper_)
+ return dewarper_->exportBuffers(&data->mainPathStream_, count, buffers);
+ else
+ return mainPath_.exportBuffers(count, buffers);
+ } else if (hasSelfPath_ && stream == &data->selfPathStream_) {
return selfPath_.exportBuffers(count, buffers);
+ }
return -EINVAL;
}
@@ -863,6 +998,16 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)
goto error;
}
+ /* If the dewarper is being used, allocate internal buffers for ISP. */
+ if (useDewarper_) {
+ ret = mainPath_.exportBuffers(maxCount, &mainPathBuffers_);
+ if (ret < 0)
+ goto error;
+
+ for (std::unique_ptr<FrameBuffer> &buffer : mainPathBuffers_)
+ availableMainPathBuffers_.push(buffer.get());
+ }
+
for (std::unique_ptr<FrameBuffer> &buffer : paramBuffers_) {
buffer->setCookie(ipaBufferId++);
data->ipaBuffers_.emplace_back(buffer->cookie(),
@@ -884,6 +1029,7 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)
error:
paramBuffers_.clear();
statBuffers_.clear();
+ mainPathBuffers_.clear();
return ret;
}
@@ -898,8 +1044,12 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera)
while (!availableParamBuffers_.empty())
availableParamBuffers_.pop();
+ while (!availableMainPathBuffers_.empty())
+ availableMainPathBuffers_.pop();
+
paramBuffers_.clear();
statBuffers_.clear();
+ mainPathBuffers_.clear();
std::vector<unsigned int> ids;
for (IPABuffer &ipabuf : data->ipaBuffers_)
@@ -920,71 +1070,71 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera)
int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlList *controls)
{
RkISP1CameraData *data = cameraData(camera);
+ utils::ScopeExitActions actions;
int ret;
/* Allocate buffers for internal pipeline usage. */
ret = allocateBuffers(camera);
if (ret)
return ret;
+ actions += [&]() { freeBuffers(camera); };
ret = data->ipa_->start();
if (ret) {
- freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start IPA " << camera->id();
return ret;
}
+ actions += [&]() { data->ipa_->stop(); };
data->frame_ = 0;
if (!isRaw_) {
ret = param_->streamOn();
if (ret) {
- data->ipa_->stop();
- freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start parameters " << camera->id();
return ret;
}
+ actions += [&]() { param_->streamOff(); };
ret = stat_->streamOn();
if (ret) {
- param_->streamOff();
- data->ipa_->stop();
- freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start statistics " << camera->id();
return ret;
}
+ actions += [&]() { stat_->streamOff(); };
+
+ if (useDewarper_) {
+ ret = dewarper_->start();
+ if (ret) {
+ LOG(RkISP1, Error) << "Failed to start dewarper";
+ return ret;
+ }
+ actions += [&]() { dewarper_->stop(); };
+ }
}
if (data->mainPath_->isEnabled()) {
ret = mainPath_.start();
- if (ret) {
- param_->streamOff();
- stat_->streamOff();
- data->ipa_->stop();
- freeBuffers(camera);
+ if (ret)
return ret;
- }
+ actions += [&]() { mainPath_.stop(); };
}
if (hasSelfPath_ && data->selfPath_->isEnabled()) {
ret = selfPath_.start();
- if (ret) {
- mainPath_.stop();
- param_->streamOff();
- stat_->streamOff();
- data->ipa_->stop();
- freeBuffers(camera);
+ if (ret)
return ret;
- }
}
isp_->setFrameStartEnabled(true);
activeCamera_ = camera;
- return ret;
+
+ actions.release();
+ return 0;
}
void PipelineHandlerRkISP1::stopDevice(Camera *camera)
@@ -1010,6 +1160,9 @@ void PipelineHandlerRkISP1::stopDevice(Camera *camera)
if (ret)
LOG(RkISP1, Warning)
<< "Failed to stop parameters for " << camera->id();
+
+ if (useDewarper_)
+ dewarper_->stop();
}
ASSERT(data->queuedRequests_.empty());
@@ -1036,8 +1189,8 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
if (data->selfPath_ && info->selfPathBuffer)
data->selfPath_->queueBuffer(info->selfPathBuffer);
} else {
- data->ipa_->fillParamsBuffer(data->frame_,
- info->paramBuffer->cookie());
+ data->ipa_->computeParams(data->frame_,
+ info->paramBuffer->cookie());
}
data->frame_++;
@@ -1050,7 +1203,6 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
*/
int PipelineHandlerRkISP1::initLinks(Camera *camera,
- const CameraSensor *sensor,
const RkISP1CameraConfiguration &config)
{
RkISP1CameraData *data = cameraData(camera);
@@ -1061,31 +1213,16 @@ int PipelineHandlerRkISP1::initLinks(Camera *camera,
return ret;
/*
- * Configure the sensor links: enable the link corresponding to this
- * camera.
+ * Configure the sensor links: enable the links corresponding to this
+ * pipeline all the way up to the ISP, through any connected CSI receiver.
*/
- for (MediaLink *link : ispSink_->links()) {
- if (link->source()->entity() != sensor->entity())
- continue;
-
- LOG(RkISP1, Debug)
- << "Enabling link from sensor '"
- << link->source()->entity()->name()
- << "' to ISP";
-
- ret = link->setEnabled(true);
- if (ret < 0)
- return ret;
- }
-
- if (csi_) {
- MediaLink *link = isp_->entity()->getPadByIndex(0)->links().at(0);
-
- ret = link->setEnabled(true);
- if (ret < 0)
- return ret;
+ ret = data->pipe_.initLinks();
+ if (ret) {
+ LOG(RkISP1, Error) << "Failed to set up pipe links";
+ return ret;
}
+ /* Configure the paths after the ISP */
for (const StreamConfiguration &cfg : config) {
if (cfg.stream() == &data->mainPathStream_)
ret = data->mainPath_->setEnabled(true);
@@ -1101,6 +1238,58 @@ int PipelineHandlerRkISP1::initLinks(Camera *camera,
return 0;
}
+/**
+ * \brief Update the camera controls
+ * \param[in] data The camera data
+ *
+ * Compute the camera controls by calculating controls which the pipeline
+ * is reponsible for and merge them with the controls computed by the IPA.
+ *
+ * This function needs data->ipaControls_ to be refreshed when a new
+ * configuration is applied to the camera by the IPA configure() function.
+ *
+ * Always call this function after IPA configure() to make sure to have a
+ * properly refreshed IPA controls list.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int PipelineHandlerRkISP1::updateControls(RkISP1CameraData *data)
+{
+ ControlInfoMap::Map controls;
+
+ if (dewarper_) {
+ std::pair<Rectangle, Rectangle> cropLimits;
+ if (dewarper_->isConfigured(&data->mainPathStream_))
+ cropLimits = dewarper_->inputCropBounds(&data->mainPathStream_);
+ else
+ cropLimits = dewarper_->inputCropBounds();
+
+ /*
+ * ScalerCrop is specified to be in Sensor coordinates.
+ * So we need to transform the limits to sensor coordinates.
+ * We can safely assume that the maximum crop limit contains the
+ * full fov of the dewarper.
+ */
+ Rectangle min = cropLimits.first.transformedBetween(cropLimits.second,
+ scalerMaxCrop_);
+
+ controls[&controls::ScalerCrop] = ControlInfo(min,
+ scalerMaxCrop_,
+ scalerMaxCrop_);
+ data->properties_.set(properties::ScalerCropMaximum, scalerMaxCrop_);
+ activeCrop_ = scalerMaxCrop_;
+ }
+
+ /* Add the IPA registered controls to list of camera controls. */
+ for (const auto &ipaControl : data->ipaControls_)
+ controls[ipaControl.first] = ipaControl.second;
+
+ data->controlInfo_ = ControlInfoMap(std::move(controls),
+ controls::controls);
+
+ return 0;
+}
+
int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
{
int ret;
@@ -1109,22 +1298,27 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
std::make_unique<RkISP1CameraData>(this, &mainPath_,
hasSelfPath_ ? &selfPath_ : nullptr);
- data->sensor_ = std::make_unique<CameraSensor>(sensor);
- ret = data->sensor_->init();
- if (ret)
+ /* Identify the pipeline path between the sensor and the rkisp1_isp */
+ ret = data->pipe_.init(sensor, "rkisp1_isp");
+ if (ret) {
+ LOG(RkISP1, Error) << "Failed to identify path from sensor to sink";
return ret;
+ }
+
+ data->sensor_ = CameraSensorFactoryBase::create(sensor);
+ if (!data->sensor_)
+ return -ENODEV;
/* Initialize the camera properties. */
data->properties_ = data->sensor_->properties();
- /*
- * \todo Read dealy values from the sensor itself or from a
- * a sensor database. For now use generic values taken from
- * the Raspberry Pi and listed as generic values.
- */
+ scalerMaxCrop_ = Rectangle(data->sensor_->resolution());
+
+ const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
- { V4L2_CID_ANALOGUE_GAIN, { 1, false } },
- { V4L2_CID_EXPOSURE, { 2, false } },
+ { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+ { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
+ { V4L2_CID_VBLANK, { delays.vblankDelay, false } },
};
data->delayedCtrls_ =
@@ -1137,6 +1331,8 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
if (ret)
return ret;
+ updateControls(data.get());
+
std::set<Stream *> streams{
&data->mainPathStream_,
&data->selfPathStream_,
@@ -1144,6 +1340,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
const std::string &id = data->sensor_->id();
std::shared_ptr<Camera> camera =
Camera::create(std::move(data), id, streams);
+
registerCamera(std::move(camera));
return 0;
@@ -1151,8 +1348,6 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
{
- const MediaPad *pad;
-
DeviceMatch dm("rkisp1");
dm.add("rkisp1_isp");
dm.add("rkisp1_resizer_mainpath");
@@ -1177,22 +1372,6 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
if (isp_->open() < 0)
return false;
- /* Locate and open the optional CSI-2 receiver. */
- ispSink_ = isp_->entity()->getPadByIndex(0);
- if (!ispSink_ || ispSink_->links().empty())
- return false;
-
- pad = ispSink_->links().at(0)->source();
- if (pad->entity()->function() == MEDIA_ENT_F_VID_IF_BRIDGE) {
- csi_ = std::make_unique<V4L2Subdevice>(pad->entity());
- if (csi_->open() < 0)
- return false;
-
- ispSink_ = csi_->entity()->getPadByIndex(0);
- if (!ispSink_)
- return false;
- }
-
/* Locate and open the stats and params video nodes. */
stat_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1_stats");
if (stat_->open() < 0)
@@ -1209,19 +1388,44 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
if (hasSelfPath_ && !selfPath_.init(media_))
return false;
- mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
+ mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::imageBufferReady);
if (hasSelfPath_)
- selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
- stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
- param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
+ selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::imageBufferReady);
+ stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statBufferReady);
+ param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramBufferReady);
+
+ /* If dewarper is present, create its instance. */
+ DeviceMatch dwp("dw100");
+ dwp.add("dw100-source");
+ dwp.add("dw100-sink");
+
+ std::shared_ptr<MediaDevice> dwpMediaDevice = enumerator->search(dwp);
+ if (dwpMediaDevice) {
+ dewarper_ = std::make_unique<V4L2M2MConverter>(dwpMediaDevice.get());
+ if (dewarper_->isValid()) {
+ dewarper_->outputBufferReady.connect(
+ this, &PipelineHandlerRkISP1::dewarpBufferReady);
+
+ LOG(RkISP1, Info)
+ << "Using DW100 dewarper " << dewarper_->deviceNode();
+ } else {
+ LOG(RkISP1, Warning)
+ << "Found DW100 dewarper " << dewarper_->deviceNode()
+ << " but invalid";
+
+ dewarper_.reset();
+ }
+ }
/*
* Enumerate all sensors connected to the ISP and create one
* camera instance for each of them.
*/
bool registered = false;
- for (MediaLink *link : ispSink_->links()) {
- if (!createCamera(link->source()->entity()))
+
+ for (MediaEntity *entity : media_->locateEntities(MEDIA_ENT_F_CAM_SENSOR)) {
+ LOG(RkISP1, Debug) << "Identified " << entity->name();
+ if (!createCamera(entity))
registered = true;
}
@@ -1251,7 +1455,7 @@ void PipelineHandlerRkISP1::tryCompleteRequest(RkISP1FrameInfo *info)
completeRequest(request);
}
-void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)
+void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
@@ -1261,7 +1465,7 @@ void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)
return;
const FrameMetadata &metadata = buffer->metadata();
- Request *request = buffer->request();
+ Request *request = info->request;
if (metadata.status != FrameMetadata::FrameCancelled) {
/*
@@ -1276,18 +1480,102 @@ void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)
if (isRaw_) {
const ControlList &ctrls =
data->delayedCtrls_->get(metadata.sequence);
- data->ipa_->processStatsBuffer(info->frame, 0, ctrls);
+ data->ipa_->processStats(info->frame, 0, ctrls);
}
} else {
if (isRaw_)
info->metadataProcessed = true;
}
+ if (!useDewarper_) {
+ completeBuffer(request, buffer);
+ tryCompleteRequest(info);
+
+ return;
+ }
+
+ /* Do not queue cancelled frames to dewarper. */
+ if (metadata.status == FrameMetadata::FrameCancelled) {
+ /*
+ * i.MX8MP is the only known platform with dewarper. It has
+ * no self path. Hence, only main path buffer completion is
+ * required.
+ *
+ * Also, we cannot completeBuffer(request, buffer) as buffer
+ * here, is an internal buffer (between ISP and dewarper) and
+ * is not associated to the any specific request. The request
+ * buffer associated with main path stream is the one that
+ * is required to be completed (not the internal buffer).
+ */
+ for (auto it : request->buffers()) {
+ if (it.first == &data->mainPathStream_)
+ completeBuffer(request, it.second);
+ }
+
+ tryCompleteRequest(info);
+ return;
+ }
+
+ /* Handle scaler crop control. */
+ const auto &crop = request->controls().get(controls::ScalerCrop);
+ if (crop) {
+ Rectangle rect = crop.value();
+
+ /*
+ * ScalerCrop is specified to be in Sensor coordinates.
+ * So we need to transform it into dewarper coordinates.
+ * We can safely assume that the maximum crop limit contains the
+ * full fov of the dewarper.
+ */
+ std::pair<Rectangle, Rectangle> cropLimits =
+ dewarper_->inputCropBounds(&data->mainPathStream_);
+
+ rect = rect.transformedBetween(scalerMaxCrop_, cropLimits.second);
+ int ret = dewarper_->setInputCrop(&data->mainPathStream_,
+ &rect);
+ rect = rect.transformedBetween(cropLimits.second, scalerMaxCrop_);
+ if (!ret && rect != crop.value()) {
+ /*
+ * If the rectangle is changed by setInputCrop on the
+ * dewarper, log a debug message and cache the actual
+ * applied rectangle for metadata reporting.
+ */
+ LOG(RkISP1, Debug)
+ << "Applied rectangle " << rect.toString()
+ << " differs from requested " << crop.value().toString();
+ }
+
+ activeCrop_ = rect;
+ }
+
+ /*
+ * Queue input and output buffers to the dewarper. The output
+ * buffers for the dewarper are the buffers of the request, supplied
+ * by the application.
+ */
+ int ret = dewarper_->queueBuffers(buffer, request->buffers());
+ if (ret < 0)
+ LOG(RkISP1, Error) << "Cannot queue buffers to dewarper: "
+ << strerror(-ret);
+
+ request->metadata().set(controls::ScalerCrop, activeCrop_.value());
+}
+
+void PipelineHandlerRkISP1::dewarpBufferReady(FrameBuffer *buffer)
+{
+ ASSERT(activeCamera_);
+ RkISP1CameraData *data = cameraData(activeCamera_);
+ Request *request = buffer->request();
+
+ RkISP1FrameInfo *info = data->frameInfo_.find(buffer->request());
+ if (!info)
+ return;
+
completeBuffer(request, buffer);
tryCompleteRequest(info);
}
-void PipelineHandlerRkISP1::paramReady(FrameBuffer *buffer)
+void PipelineHandlerRkISP1::paramBufferReady(FrameBuffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
@@ -1300,7 +1588,7 @@ void PipelineHandlerRkISP1::paramReady(FrameBuffer *buffer)
tryCompleteRequest(info);
}
-void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
+void PipelineHandlerRkISP1::statBufferReady(FrameBuffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
@@ -1318,10 +1606,10 @@ void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
if (data->frame_ <= buffer->metadata().sequence)
data->frame_ = buffer->metadata().sequence + 1;
- data->ipa_->processStatsBuffer(info->frame, info->statBuffer->cookie(),
- data->delayedCtrls_->get(buffer->metadata().sequence));
+ data->ipa_->processStats(info->frame, info->statBuffer->cookie(),
+ data->delayedCtrls_->get(buffer->metadata().sequence));
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1, "rkisp1")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
index b62b645c..64018dc5 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1_path.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * rkisp1path.cpp - Rockchip ISP1 path helper
+ * Rockchip ISP1 path helper
*/
#include "rkisp1_path.h"
@@ -126,12 +126,50 @@ void RkISP1Path::populateFormats()
}
}
+/**
+ * \brief Filter the sensor resolutions that can be supported
+ * \param[in] sensor The camera sensor
+ *
+ * This function retrieves all the sizes supported by the sensor and
+ * filters all the resolutions that can be supported on the pipeline.
+ * It is possible that the sensor's maximum output resolution is higher
+ * than the ISP maximum input. In that case, this function filters out all
+ * the resolution incapable of being supported and returns the maximum
+ * sensor resolution that can be supported by the pipeline.
+ *
+ * \return Maximum sensor size supported on the pipeline
+ */
+Size RkISP1Path::filterSensorResolution(const CameraSensor *sensor)
+{
+ auto iter = sensorSizesMap_.find(sensor);
+ if (iter != sensorSizesMap_.end())
+ return iter->second.back();
+
+ std::vector<Size> &sizes = sensorSizesMap_[sensor];
+ for (unsigned int code : sensor->mbusCodes()) {
+ for (const Size &size : sensor->sizes(code)) {
+ if (size.width > maxResolution_.width ||
+ size.height > maxResolution_.height)
+ continue;
+
+ sizes.push_back(size);
+ }
+ }
+
+ /* Sort in increasing order and remove duplicates. */
+ std::sort(sizes.begin(), sizes.end());
+ auto last = std::unique(sizes.begin(), sizes.end());
+ sizes.erase(last, sizes.end());
+
+ return sizes.back();
+}
+
StreamConfiguration
RkISP1Path::generateConfiguration(const CameraSensor *sensor, const Size &size,
StreamRole role)
{
const std::vector<unsigned int> &mbusCodes = sensor->mbusCodes();
- const Size &resolution = sensor->resolution();
+ Size resolution = filterSensorResolution(sensor);
/* Min and max resolutions to populate the available stream formats. */
Size maxResolution = maxResolution_.boundedToAspectRatio(resolution)
@@ -216,11 +254,13 @@ RkISP1Path::generateConfiguration(const CameraSensor *sensor, const Size &size,
return cfg;
}
-CameraConfiguration::Status RkISP1Path::validate(const CameraSensor *sensor,
- StreamConfiguration *cfg)
+CameraConfiguration::Status
+RkISP1Path::validate(const CameraSensor *sensor,
+ const std::optional<SensorConfiguration> &sensorConfig,
+ StreamConfiguration *cfg)
{
const std::vector<unsigned int> &mbusCodes = sensor->mbusCodes();
- const Size &resolution = sensor->resolution();
+ Size resolution = filterSensorResolution(sensor);
const StreamConfiguration reqCfg = *cfg;
CameraConfiguration::Status status = CameraConfiguration::Valid;
@@ -247,9 +287,14 @@ CameraConfiguration::Status RkISP1Path::validate(const CameraSensor *sensor,
continue;
/*
- * Store the raw format with the highest bits per pixel
- * for later usage.
+ * If the bits per pixel is supplied from the sensor
+ * configuration, choose a raw format that complies with
+ * it. Otherwise, store the raw format with the highest
+ * bits per pixel for later usage.
*/
+ if (sensorConfig && info.bitsPerPixel != sensorConfig->bitDepth)
+ continue;
+
if (info.bitsPerPixel > rawBitsPerPixel) {
rawBitsPerPixel = info.bitsPerPixel;
rawFormat = format;
@@ -262,6 +307,9 @@ CameraConfiguration::Status RkISP1Path::validate(const CameraSensor *sensor,
}
}
+ if (sensorConfig && !rawFormat.isValid())
+ return CameraConfiguration::Invalid;
+
bool isRaw = PixelFormatInfo::info(cfg->pixelFormat).colourEncoding ==
PixelFormatInfo::ColourEncodingRAW;
@@ -281,14 +329,48 @@ CameraConfiguration::Status RkISP1Path::validate(const CameraSensor *sensor,
if (isRaw) {
/*
* Use the sensor output size closest to the requested stream
- * size.
+ * size while ensuring the output size doesn't exceed ISP limits.
+ *
+ * As 'resolution' is the largest sensor resolution
+ * supported by the ISP, CameraSensor::getFormat() will never
+ * return a V4L2SubdeviceFormat with a larger size.
*/
uint32_t mbusCode = formatToMediaBus.at(cfg->pixelFormat);
+ cfg->size.boundTo(resolution);
+
+ Size rawSize = sensorConfig ? sensorConfig->outputSize
+ : cfg->size;
+
V4L2SubdeviceFormat sensorFormat =
- sensor->getFormat({ mbusCode }, cfg->size);
+ sensor->getFormat({ mbusCode }, rawSize);
+
+ if (sensorConfig &&
+ sensorConfig->outputSize != sensorFormat.size)
+ return CameraConfiguration::Invalid;
minResolution = sensorFormat.size;
maxResolution = sensorFormat.size;
+ } else if (sensorConfig) {
+ /*
+ * We have already ensured 'rawFormat' has the matching bit
+ * depth with sensorConfig.bitDepth hence, only validate the
+ * sensorConfig's output size here.
+ */
+ Size sensorSize = sensorConfig->outputSize;
+
+ if (sensorSize > resolution)
+ return CameraConfiguration::Invalid;
+
+ uint32_t mbusCode = formatToMediaBus.at(rawFormat);
+ V4L2SubdeviceFormat sensorFormat =
+ sensor->getFormat({ mbusCode }, sensorSize);
+
+ if (sensorFormat.size != sensorSize)
+ return CameraConfiguration::Invalid;
+
+ minResolution = minResolution_.expandedToAspectRatio(sensorSize);
+ maxResolution = maxResolution_.boundedTo(sensorSize)
+ .boundedToAspectRatio(sensorSize);
} else {
/*
* Adjust the size based on the sensor resolution and absolute
@@ -338,11 +420,14 @@ int RkISP1Path::configure(const StreamConfiguration &config,
/*
* Crop on the resizer input to maintain FOV before downscaling.
*
- * \todo The alignment to a multiple of 2 pixels is required but may
- * change the aspect ratio very slightly. A more advanced algorithm to
- * compute the resizer input crop rectangle is needed, and it should
- * also take into account the need to crop away the edge pixels affected
- * by the ISP processing blocks.
+ * Note that this does not apply to devices without DUAL_CROP support
+ * (like imx8mp) , where the cropping needs to be done on the
+ * ImageStabilizer block on the ISP source pad and therefore is
+ * configured before this stage. For simplicity we still set the crop.
+ * This gets ignored by the kernel driver because the hardware is
+ * missing the capability.
+ *
+ * Alignment to a multiple of 2 pixels is required by the resizer.
*/
Size ispCrop = inputFormat.size.boundedToAspectRatio(config.size)
.alignedUpTo(2, 2);
@@ -365,7 +450,7 @@ int RkISP1Path::configure(const StreamConfiguration &config,
* The configuration has been validated, the pixel format is guaranteed
* to be supported and thus found in formatToMediaBus.
*/
- ispFormat.mbus_code = formatToMediaBus.at(config.pixelFormat);
+ ispFormat.code = formatToMediaBus.at(config.pixelFormat);
ret = resizer_->setFormat(1, &ispFormat);
if (ret < 0)
@@ -435,8 +520,8 @@ void RkISP1Path::stop()
}
/*
- * \todo Remove the hardcoded resolutions and formats once all users will have
- * migrated to a recent enough kernel.
+ * \todo Remove the hardcoded resolutions and formats once kernels older than
+ * v6.4 will stop receiving LTS support (scheduled for December 2027 for v6.1).
*/
namespace {
constexpr Size RKISP1_RSZ_MP_SRC_MIN{ 32, 16 };
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1_path.h b/src/libcamera/pipeline/rkisp1/rkisp1_path.h
index cd77957e..430181d3 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1_path.h
+++ b/src/libcamera/pipeline/rkisp1/rkisp1_path.h
@@ -2,11 +2,12 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * rkisp1path.h - Rockchip ISP1 path helper
+ * Rockchip ISP1 path helper
*/
#pragma once
+#include <map>
#include <memory>
#include <set>
#include <vector>
@@ -25,6 +26,7 @@ namespace libcamera {
class CameraSensor;
class MediaDevice;
+class SensorConfiguration;
class V4L2Subdevice;
struct StreamConfiguration;
struct V4L2SubdeviceFormat;
@@ -44,6 +46,7 @@ public:
const Size &resolution,
StreamRole role);
CameraConfiguration::Status validate(const CameraSensor *sensor,
+ const std::optional<SensorConfiguration> &sensorConfig,
StreamConfiguration *cfg);
int configure(const StreamConfiguration &config,
@@ -60,9 +63,11 @@ public:
int queueBuffer(FrameBuffer *buffer) { return video_->queueBuffer(buffer); }
Signal<FrameBuffer *> &bufferReady() { return video_->bufferReady; }
+ const Size &maxResolution() const { return maxResolution_; }
private:
void populateFormats();
+ Size filterSensorResolution(const CameraSensor *sensor);
static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
@@ -77,6 +82,12 @@ private:
std::unique_ptr<V4L2Subdevice> resizer_;
std::unique_ptr<V4L2VideoDevice> video_;
MediaLink *link_;
+
+ /*
+ * Map from camera sensors to the sizes (in increasing order),
+ * which are guaranteed to be supported by the pipeline.
+ */
+ std::map<const CameraSensor *, std::vector<Size>> sensorSizesMap_;
};
class RkISP1MainPath : public RkISP1Path
diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.cpp b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp
index 3db92e7d..ad50a7c8 100644
--- a/src/libcamera/pipeline/rpi/common/delayed_controls.cpp
+++ b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * delayed_controls.cpp - Helper to deal with controls that take effect with a delay
+ * Helper to deal with controls that take effect with a delay
*
* Note: This has been forked from the libcamera core implementation.
*/
diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.h b/src/libcamera/pipeline/rpi/common/delayed_controls.h
index 61f755f0..487b0057 100644
--- a/src/libcamera/pipeline/rpi/common/delayed_controls.h
+++ b/src/libcamera/pipeline/rpi/common/delayed_controls.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * delayed_controls.h - Helper to deal with controls that take effect with a delay
+ * Helper to deal with controls that take effect with a delay
*
* Note: This has been forked from the libcamera core implementation.
*/
diff --git a/src/libcamera/pipeline/rpi/common/meson.build b/src/libcamera/pipeline/rpi/common/meson.build
index 8fb7e823..b2b1a0a6 100644
--- a/src/libcamera/pipeline/rpi/common/meson.build
+++ b/src/libcamera/pipeline/rpi/common/meson.build
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'delayed_controls.cpp',
'pipeline_base.cpp',
'rpi_stream.cpp',
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index ee222d06..1f13e523 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2023, Raspberry Pi Ltd
*
- * pipeline_base.cpp - Pipeline handler base class for Raspberry Pi devices
+ * Pipeline handler base class for Raspberry Pi devices
*/
#include "pipeline_base.h"
@@ -37,10 +37,10 @@ namespace {
constexpr unsigned int defaultRawBitDepth = 12;
-PixelFormat mbusCodeToPixelFormat(unsigned int mbus_code,
+PixelFormat mbusCodeToPixelFormat(unsigned int code,
BayerFormat::Packing packingReq)
{
- BayerFormat bayer = BayerFormat::fromMbusCode(mbus_code);
+ BayerFormat bayer = BayerFormat::fromMbusCode(code);
ASSERT(bayer.isValid());
@@ -105,7 +105,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_
Status status = Valid;
yuvColorSpace_.reset();
- for (auto cfg : config_) {
+ for (auto &cfg : config_) {
/* First fix up raw streams to have the "raw" colour space. */
if (PipelineHandlerBase::isRaw(cfg.pixelFormat)) {
/* If there was no value here, that doesn't count as "adjusted". */
@@ -130,7 +130,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_
rgbColorSpace_->range = ColorSpace::Range::Full;
/* Go through the streams again and force everyone to the same colour space. */
- for (auto cfg : config_) {
+ for (auto &cfg : config_) {
if (cfg.colorSpace == ColorSpace::Raw)
continue;
@@ -181,12 +181,14 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()
rawStreams_.clear();
outStreams_.clear();
+ unsigned int rawStreamIndex = 0;
+ unsigned int outStreamIndex = 0;
- for (const auto &[index, cfg] : utils::enumerate(config_)) {
+ for (auto &cfg : config_) {
if (PipelineHandlerBase::isRaw(cfg.pixelFormat))
- rawStreams_.emplace_back(index, &cfg);
+ rawStreams_.emplace_back(rawStreamIndex++, &cfg);
else
- outStreams_.emplace_back(index, &cfg);
+ outStreams_.emplace_back(outStreamIndex++, &cfg);
}
/* Sort the streams so the highest resolution is first. */
@@ -221,7 +223,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()
* without modifications.
*/
if (sensorConfig) {
- BayerFormat bayer = BayerFormat::fromMbusCode(sensorFormat_.mbus_code);
+ BayerFormat bayer = BayerFormat::fromMbusCode(sensorFormat_.code);
if (bayer.bitDepth != sensorConfig->bitDepth ||
sensorFormat_.size != sensorConfig->outputSize) {
@@ -235,24 +237,16 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()
for (auto &raw : rawStreams_) {
StreamConfiguration *rawStream = raw.cfg;
- /* Adjust the RAW stream to match the computed sensor format. */
- BayerFormat sensorBayer = BayerFormat::fromMbusCode(sensorFormat_.mbus_code);
-
/*
- * 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!
+ * Some sensors change their Bayer order when they are
+ * h-flipped or v-flipped, according to the transform. Adjust
+ * the RAW stream to match the computed sensor format by
+ * applying the sensor Bayer order resulting from the transform
+ * to the user request.
*/
- if (data_->flipsAlterBayerOrder_) {
- sensorBayer.order = data_->nativeBayerOrder_;
- sensorBayer = sensorBayer.transform(combinedTransform_);
- }
- /* Apply the sensor adjusted Bayer order to the user request. */
BayerFormat cfgBayer = BayerFormat::fromPixelFormat(rawStream->pixelFormat);
- cfgBayer.order = sensorBayer.order;
+ cfgBayer.order = data_->sensor_->bayerOrder(combinedTransform_);
if (rawStream->pixelFormat != cfgBayer.toPixelFormat()) {
rawStream->pixelFormat = cfgBayer.toPixelFormat();
@@ -367,6 +361,7 @@ V4L2DeviceFormat PipelineHandlerBase::toV4L2DeviceFormat(const V4L2VideoDevice *
deviceFormat.planesCount = info.numPlanes();
deviceFormat.fourcc = dev->toV4L2PixelFormat(stream->pixelFormat);
deviceFormat.size = stream->size;
+ deviceFormat.planes[0].bpl = stream->stride;
deviceFormat.colorSpace = stream->colorSpace;
return deviceFormat;
@@ -376,8 +371,8 @@ V4L2DeviceFormat PipelineHandlerBase::toV4L2DeviceFormat(const V4L2VideoDevice *
const V4L2SubdeviceFormat &format,
BayerFormat::Packing packingReq)
{
- unsigned int mbus_code = format.mbus_code;
- const PixelFormat pix = mbusCodeToPixelFormat(mbus_code, packingReq);
+ unsigned int code = format.code;
+ const PixelFormat pix = mbusCodeToPixelFormat(code, packingReq);
V4L2DeviceFormat deviceFormat;
deviceFormat.fourcc = dev->toV4L2PixelFormat(pix);
@@ -408,7 +403,7 @@ PipelineHandlerBase::generateConfiguration(Camera *camera, Span<const StreamRole
case StreamRole::Raw:
size = sensorSize;
sensorFormat = data->findBestFormat(size, defaultRawBitDepth);
- pixelFormat = mbusCodeToPixelFormat(sensorFormat.mbus_code,
+ pixelFormat = mbusCodeToPixelFormat(sensorFormat.code,
BayerFormat::Packing::CSI2);
ASSERT(pixelFormat.isValid());
colorSpace = ColorSpace::Raw;
@@ -481,7 +476,11 @@ PipelineHandlerBase::generateConfiguration(Camera *camera, Span<const StreamRole
*/
for (const auto &format : fmts) {
PixelFormat pf = format.first.toPixelFormat();
- if (pf.isValid()) {
+ /*
+ * Some V4L2 formats translate to the same pixel format (e.g. YU12, YM12
+ * both give YUV420). We must avoid duplicating the range in this case.
+ */
+ if (pf.isValid() && deviceFormats.find(pf) == deviceFormats.end()) {
const SizeRange &ispSizes = format.second[0];
deviceFormats[pf].emplace_back(ispSizes.min, sensorSize,
ispSizes.hStep, ispSizes.vStep);
@@ -499,8 +498,6 @@ PipelineHandlerBase::generateConfiguration(Camera *camera, Span<const StreamRole
config->addConfiguration(cfg);
}
- config->validate();
-
return config;
}
@@ -538,6 +535,7 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
* Platform specific internal stream configuration. This also assigns
* external streams which get configured below.
*/
+ data->cropParams_.clear();
ret = data->platformConfigure(rpiConfig);
if (ret)
return ret;
@@ -550,12 +548,6 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
}
/*
- * Set the scaler crop to the value we are using (scaled to native sensor
- * coordinates).
- */
- data->scalerCrop_ = data->scaleIspCrop(data->ispCrop_);
-
- /*
* Update the ScalerCropMaximum to the correct value for this camera mode.
* For us, it's the same as the "analogue crop".
*
@@ -572,9 +564,28 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
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_));
- ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop, data->scalerCrop_);
+ const auto cropParamsIt = data->cropParams_.find(0);
+ if (cropParamsIt != data->cropParams_.end()) {
+ const CameraData::CropParams &cropParams = cropParamsIt->second;
+ /*
+ * Add the ScalerCrop control limits based on the current mode and
+ * the first configured stream.
+ */
+ Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize));
+ ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,
+ data->scaleIspCrop(cropParams.ispCrop));
+ if (data->cropParams_.size() == 2) {
+ /*
+ * The control map for rpi::ScalerCrops has the min value
+ * as the default crop for stream 0, max value as the default
+ * value for stream 1.
+ */
+ ctrlMap[&controls::rpi::ScalerCrops] =
+ ControlInfo(data->scaleIspCrop(data->cropParams_.at(0).ispCrop),
+ data->scaleIspCrop(data->cropParams_.at(1).ispCrop),
+ ctrlMap[&controls::ScalerCrop].def());
+ }
+ }
data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
@@ -731,7 +742,8 @@ int PipelineHandlerBase::queueRequestDevice(Camera *camera, Request *request)
if (!data->isRunning())
return -EINVAL;
- LOG(RPI, Debug) << "queueRequestDevice: New request.";
+ LOG(RPI, Debug) << "queueRequestDevice: New request sequence: "
+ << request->sequence();
/* Push all buffers supplied in the Request to the respective streams. */
for (auto stream : data->streams_) {
@@ -778,13 +790,10 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
CameraData *data = cameraData.get();
int ret;
- data->sensor_ = std::make_unique<CameraSensor>(sensorEntity);
+ data->sensor_ = CameraSensorFactoryBase::create(sensorEntity);
if (!data->sensor_)
return -EINVAL;
- if (data->sensor_->init())
- return -EINVAL;
-
/* Populate the map of sensor supported formats and sizes. */
for (auto const mbusCode : data->sensor_->mbusCodes())
data->sensorFormats_.emplace(mbusCode,
@@ -807,11 +816,12 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
* Setup our delayed control writer with the sensor default
* gain and exposure delays. Mark VBLANK for priority write.
*/
+ const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
std::unordered_map<uint32_t, RPi::DelayedControls::ControlParams> 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 } }
+ { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+ { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
+ { V4L2_CID_HBLANK, { delays.hblankDelay, false } },
+ { V4L2_CID_VBLANK, { delays.vblankDelay, true } }
};
data->delayedCtrls_ = std::make_unique<RPi::DelayedControls>(data->sensor_->device(), params);
data->sensorMetadata_ = result.sensorConfig.sensorMetadata;
@@ -838,40 +848,6 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
*/
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.
- *
- * If flips are supported verify 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;
-
ret = platformRegister(cameraData, frontend, backend);
if (ret)
return ret;
@@ -988,7 +964,7 @@ V4L2SubdeviceFormat CameraData::findBestFormat(const Size &req, unsigned int bit
if (score <= bestScore) {
bestScore = score;
- bestFormat.mbus_code = mbusCode;
+ bestFormat.code = mbusCode;
bestFormat.size = size;
}
@@ -1180,20 +1156,11 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)
if (!ipa_)
return -ENOENT;
- /*
- * 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);
- }
+ /* The configuration (tuning file) is made from the sensor name. */
+ std::string model = sensor_->model();
+ if (isMonoSensor(sensor_))
+ model += "_mono";
+ std::string configurationFile = ipa_->configurationFile(model + ".json");
IPASettings settings(configurationFile, sensor_->model());
ipa::RPi::InitParams params;
@@ -1214,7 +1181,6 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)
int CameraData::configureIPA(const CameraConfiguration *config, ipa::RPi::ConfigResult *result)
{
- std::map<unsigned int, ControlInfoMap> entityControls;
ipa::RPi::ConfigParams params;
int ret;
@@ -1334,9 +1300,30 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const
void CameraData::applyScalerCrop(const ControlList &controls)
{
- const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
- if (scalerCrop) {
- Rectangle nativeCrop = *scalerCrop;
+ const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);
+ const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);
+ std::vector<Rectangle> scalerCrops;
+
+ /*
+ * First thing to do is create a vector of crops to apply to each ISP output
+ * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if
+ * present.
+ *
+ * If controls::rpi::ScalerCrops is preset, apply the given crops to the
+ * ISP output streams, indexed by the same order in which they had been
+ * configured. This is not the same as the ISP output index. Otherwise
+ * if controls::ScalerCrop is present, apply the same crop to all ISP
+ * output streams.
+ */
+ for (unsigned int i = 0; i < cropParams_.size(); i++) {
+ if (scalerCropRPi && i < scalerCropRPi->size())
+ scalerCrops.push_back(scalerCropRPi->data()[i]);
+ else if (scalerCropCore)
+ scalerCrops.push_back(*scalerCropCore);
+ }
+
+ for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {
+ Rectangle nativeCrop = scalerCrop;
if (!nativeCrop.width || !nativeCrop.height)
nativeCrop = { 0, 0, 1, 1 };
@@ -1352,20 +1339,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)
* 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 minSize = cropParams_.at(i).ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
Size size = ispCrop.size().expandedTo(minSize);
ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));
- if (ispCrop != ispCrop_) {
- ispCrop_ = ispCrop;
- platformSetIspCrop();
-
- /*
- * 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_);
+ if (ispCrop != cropParams_.at(i).ispCrop) {
+ cropParams_.at(i).ispCrop = ispCrop;
+ platformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop);
}
}
}
@@ -1437,6 +1417,8 @@ void CameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream)
* Tag the buffer as completed, returning it to the
* application.
*/
+ LOG(RPI, Debug) << "Completing request buffer for stream "
+ << stream->name();
pipe()->completeBuffer(request, buffer);
} else {
/*
@@ -1445,6 +1427,8 @@ void CameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream)
* unconditionally for internal streams), or there is no pending
* request, so we can recycle it.
*/
+ LOG(RPI, Debug) << "Returning buffer to stream "
+ << stream->name();
stream->returnBuffer(buffer);
}
}
@@ -1488,6 +1472,9 @@ void CameraData::checkRequestCompleted()
if (state_ != State::IpaComplete)
return;
+ LOG(RPI, Debug) << "Completing request sequence: "
+ << request->sequence();
+
pipe()->completeRequest(request);
requestQueue_.pop();
requestCompleted = true;
@@ -1500,6 +1487,7 @@ void CameraData::checkRequestCompleted()
if (state_ == State::IpaComplete &&
((ispOutputCount_ == ispOutputTotal_ && dropFrameCount_) ||
requestCompleted)) {
+ LOG(RPI, Debug) << "Going into Idle state";
state_ = State::Idle;
if (dropFrameCount_) {
dropFrameCount_--;
@@ -1514,7 +1502,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
request->metadata().set(controls::SensorTimestamp,
bufferControls.get(controls::SensorTimestamp).value_or(0));
- request->metadata().set(controls::ScalerCrop, scalerCrop_);
+ if (cropParams_.size()) {
+ std::vector<Rectangle> crops;
+
+ for (auto const &[k, v] : cropParams_)
+ crops.push_back(scaleIspCrop(v.ispCrop));
+
+ request->metadata().set(controls::ScalerCrop, crops[0]);
+ if (crops.size() > 1) {
+ request->metadata().set(controls::rpi::ScalerCrops,
+ Span<const Rectangle>(crops.data(), crops.size()));
+ }
+ }
}
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h
index 267eef11..aae0c2f3 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2023, Raspberry Pi Ltd
*
- * pipeline_base.h - Pipeline handler base class for Raspberry Pi devices
+ * Pipeline handler base class for Raspberry Pi devices
*/
#include <map>
@@ -48,7 +48,7 @@ class CameraData : public Camera::Private
public:
CameraData(PipelineHandler *pipe)
: Camera::Private(pipe), state_(State::Stopped),
- flipsAlterBayerOrder_(false), dropFrameCount_(0), buffersAllocated_(false),
+ dropFrameCount_(0), buffersAllocated_(false),
ispOutputCount_(0), ispOutputTotal_(0)
{
}
@@ -83,7 +83,7 @@ public:
Rectangle scaleIspCrop(const Rectangle &ispCrop) const;
void applyScalerCrop(const ControlList &controls);
- virtual void platformSetIspCrop() = 0;
+ virtual void platformSetIspCrop(unsigned int index, const Rectangle &ispCrop) = 0;
void cameraTimeout();
void frameStarted(uint32_t sequence);
@@ -131,15 +131,25 @@ public:
std::queue<Request *> 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_;
+
+ struct CropParams {
+ CropParams(Rectangle ispCrop_, Size ispMinCropSize_, unsigned int ispIndex_)
+ : ispCrop(ispCrop_), ispMinCropSize(ispMinCropSize_), ispIndex(ispIndex_)
+ {
+ }
+
+ /* Crop in ISP (camera mode) pixels */
+ Rectangle ispCrop;
+ /* Minimum crop size in ISP output pixels */
+ Size ispMinCropSize;
+ /* Index of the ISP output channel for this crop */
+ unsigned int ispIndex;
+ };
+
+ /* Mapping of CropParams keyed by the output stream order in CameraConfiguration */
+ std::map<unsigned int, CropParams> cropParams_;
unsigned int dropFrameCount_;
diff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.cpp b/src/libcamera/pipeline/rpi/common/rpi_stream.cpp
index 70f115f1..accf59eb 100644
--- a/src/libcamera/pipeline/rpi/common/rpi_stream.cpp
+++ b/src/libcamera/pipeline/rpi/common/rpi_stream.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * rpi_stream.cpp - Raspberry Pi device stream abstraction class.
+ * Raspberry Pi device stream abstraction class.
*/
#include "rpi_stream.h"
diff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.h b/src/libcamera/pipeline/rpi/common/rpi_stream.h
index fc2bdfe2..0dba1296 100644
--- a/src/libcamera/pipeline/rpi/common/rpi_stream.h
+++ b/src/libcamera/pipeline/rpi/common/rpi_stream.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * rpi_stream.h - Raspberry Pi device stream abstraction class.
+ * Raspberry Pi device stream abstraction class.
*/
#pragma once
@@ -14,6 +14,7 @@
#include <vector>
#include <libcamera/base/flags.h>
+#include <libcamera/base/utils.h>
#include <libcamera/stream.h>
@@ -81,6 +82,16 @@ public:
* to be applied after ISP processing.
*/
Needs32bitConv = (1 << 4),
+ /*
+ * Indicates that the input stream needs a software 16-bit endian
+ * conversion to be applied before ISP processing.
+ */
+ Needs16bitEndianSwap = (1 << 5),
+ /*
+ * Indicates that the input stream needs a software 14-bit to
+ * 16-bit unpacking.
+ */
+ Needs14bitUnpack = (1 << 6),
};
using StreamFlags = Flags<StreamFlag>;
@@ -180,19 +191,14 @@ private:
template<typename E, std::size_t N>
class Device : public std::array<class Stream, N>
{
-private:
- constexpr auto index(E e) const noexcept
- {
- return static_cast<std::underlying_type_t<E>>(e);
- }
public:
Stream &operator[](E e)
{
- return std::array<class Stream, N>::operator[](index(e));
+ return std::array<class Stream, N>::operator[](utils::to_underlying(e));
}
const Stream &operator[](E e) const
{
- return std::array<class Stream, N>::operator[](index(e));
+ return std::array<class Stream, N>::operator[](utils::to_underlying(e));
}
};
diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/src/libcamera/pipeline/rpi/common/shared_mem_object.h
deleted file mode 100644
index aa56c220..00000000
--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2023, Raspberry Pi Ltd
- *
- * shared_mem_object.h - Helper class for shared memory allocations
- */
-#pragma once
-
-#include <cstddef>
-#include <fcntl.h>
-#include <string>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <utility>
-
-#include <libcamera/base/class.h>
-#include <libcamera/base/shared_fd.h>
-
-namespace libcamera {
-
-namespace RPi {
-
-template<class T>
-class SharedMemObject
-{
-public:
- static constexpr std::size_t SIZE = sizeof(T);
-
- SharedMemObject()
- : obj_(nullptr)
- {
- }
-
- template<class... Args>
- SharedMemObject(const std::string &name, Args &&...args)
- : name_(name), obj_(nullptr)
- {
- void *mem;
- int ret;
-
- ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
- if (ret < 0)
- return;
-
- fd_ = SharedFD(std::move(ret));
- if (!fd_.isValid())
- return;
-
- ret = ftruncate(fd_.get(), SIZE);
- if (ret < 0)
- return;
-
- mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
- fd_.get(), 0);
- if (mem == MAP_FAILED)
- return;
-
- obj_ = new (mem) T(std::forward<Args>(args)...);
- }
-
- SharedMemObject(SharedMemObject<T> &&rhs)
- {
- this->name_ = std::move(rhs.name_);
- this->fd_ = std::move(rhs.fd_);
- this->obj_ = rhs.obj_;
- rhs.obj_ = nullptr;
- }
-
- ~SharedMemObject()
- {
- if (obj_) {
- obj_->~T();
- munmap(obj_, SIZE);
- }
- }
-
- /* Make SharedMemObject non-copyable for now. */
- LIBCAMERA_DISABLE_COPY(SharedMemObject)
-
- SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
- {
- this->name_ = std::move(rhs.name_);
- this->fd_ = std::move(rhs.fd_);
- this->obj_ = rhs.obj_;
- rhs.obj_ = nullptr;
- return *this;
- }
-
- T *operator->()
- {
- return obj_;
- }
-
- const T *operator->() const
- {
- return obj_;
- }
-
- T &operator*()
- {
- return *obj_;
- }
-
- const T &operator*() const
- {
- return *obj_;
- }
-
- const SharedFD &fd() const
- {
- return fd_;
- }
-
- explicit operator bool() const
- {
- return !!obj_;
- }
-
-private:
- std::string name_;
- SharedFD fd_;
- T *obj_;
-};
-
-} /* namespace RPi */
-
-} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/rpi/pisp/data/example.yaml b/src/libcamera/pipeline/rpi/pisp/data/example.yaml
new file mode 100644
index 00000000..d67e654a
--- /dev/null
+++ b/src/libcamera/pipeline/rpi/pisp/data/example.yaml
@@ -0,0 +1,45 @@
+{
+ "version": 1.0,
+ "target": "pisp",
+
+ "pipeline_handler":
+ {
+ # Number of CFE config and stats buffers to allocate and use. A
+ # larger number minimises the possibility of dropping frames,
+ # but increases the latency for updating the HW configuration.
+ #
+ # "num_cfe_config_stats_buffers": 12,
+
+ # Number of jobs to queue ahead to the CFE on startup. A larger
+ # number will increase latency for 3A changes, but may reduce
+ # avoidable frame drops.
+ #
+ # "num_cfe_config_queue": 2,
+
+ # Override any request from the IPA to drop a number of startup
+ # frames.
+ #
+ # "disable_startup_frame_drops": false,
+
+ # Custom timeout value (in ms) for camera 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.
+ #
+ # "camera_timeout_value_ms": 0,
+
+ # Disables temporal denoise functionality in the ISP pipeline.
+ # Disabling temporal denoise avoids allocating 2 additional
+ # Bayer framebuffers required for its operation.
+ #
+ # "disable_tdn": false,
+
+ # Disables multiframe HDR functionality in the ISP pipeline.
+ # Disabling multiframe HDR avoids allocating 2 additional Bayer
+ # framebuffers required for its operation.
+ #
+ # "disable_hdr": false,
+ }
+}
diff --git a/src/libcamera/pipeline/rpi/pisp/data/meson.build b/src/libcamera/pipeline/rpi/pisp/data/meson.build
new file mode 100644
index 00000000..17dfc435
--- /dev/null
+++ b/src/libcamera/pipeline/rpi/pisp/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' / 'pisp')
diff --git a/src/libcamera/pipeline/rpi/pisp/meson.build b/src/libcamera/pipeline/rpi/pisp/meson.build
new file mode 100644
index 00000000..178df94c
--- /dev/null
+++ b/src/libcamera/pipeline/rpi/pisp/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_internal_sources += files([
+ 'pisp.cpp',
+])
+
+librt = cc.find_library('rt', required : true)
+libpisp_dep = dependency('libpisp', fallback : ['libpisp', 'libpisp_dep'])
+
+libcamera_deps += [libpisp_dep, librt]
+
+subdir('data')
diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
new file mode 100644
index 00000000..91e7f4c9
--- /dev/null
+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
@@ -0,0 +1,2372 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Raspberry Pi Ltd
+ *
+ * pisp.cpp - Pipeline handler for PiSP based Raspberry Pi devices
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <memory>
+#include <mutex>
+#include <numeric>
+#include <queue>
+#include <set>
+#include <sstream>
+#include <string>
+#include <sys/ioctl.h>
+#include <unordered_map>
+#include <vector>
+
+#include <linux/dma-buf.h>
+#include <linux/v4l2-controls.h>
+#include <linux/videodev2.h>
+
+#include <libcamera/base/shared_fd.h>
+#include <libcamera/formats.h>
+
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/shared_mem_object.h"
+
+#include "libpisp/backend/backend.hpp"
+#include "libpisp/common/logging.hpp"
+#include "libpisp/common/utils.hpp"
+#include "libpisp/common/version.hpp"
+#include "libpisp/frontend/frontend.hpp"
+#include "libpisp/variants/variant.hpp"
+
+#include "../common/pipeline_base.h"
+#include "../common/rpi_stream.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(RPI)
+
+using StreamFlag = RPi::Stream::StreamFlag;
+using StreamParams = RPi::RPiCameraConfiguration::StreamParams;
+
+namespace {
+
+enum class Cfe : unsigned int { Output0, Embedded, Stats, Config };
+enum class Isp : unsigned int { Input, Output0, Output1, TdnInput, TdnOutput,
+ StitchInput, StitchOutput, Config };
+
+/* Offset for all compressed buffers; mode for TDN and Stitch. */
+constexpr unsigned int DefaultCompressionOffset = 2048;
+constexpr unsigned int DefaultCompressionMode = 1;
+
+const std::vector<std::pair<BayerFormat, unsigned int>> BayerToMbusCodeMap{
+ { { BayerFormat::BGGR, 8, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SBGGR8_1X8, },
+ { { BayerFormat::GBRG, 8, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGBRG8_1X8, },
+ { { BayerFormat::GRBG, 8, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGRBG8_1X8, },
+ { { BayerFormat::RGGB, 8, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SRGGB8_1X8, },
+ { { BayerFormat::BGGR, 10, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SBGGR10_1X10, },
+ { { BayerFormat::GBRG, 10, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGBRG10_1X10, },
+ { { BayerFormat::GRBG, 10, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGRBG10_1X10, },
+ { { BayerFormat::RGGB, 10, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SRGGB10_1X10, },
+ { { BayerFormat::BGGR, 12, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SBGGR12_1X12, },
+ { { BayerFormat::GBRG, 12, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGBRG12_1X12, },
+ { { BayerFormat::GRBG, 12, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGRBG12_1X12, },
+ { { BayerFormat::RGGB, 12, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SRGGB12_1X12, },
+ { { BayerFormat::BGGR, 14, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SBGGR14_1X14, },
+ { { BayerFormat::GBRG, 14, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGBRG14_1X14, },
+ { { BayerFormat::GRBG, 14, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGRBG14_1X14, },
+ { { BayerFormat::RGGB, 14, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SRGGB14_1X14, },
+ { { BayerFormat::BGGR, 16, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SBGGR16_1X16, },
+ { { BayerFormat::GBRG, 16, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGBRG16_1X16, },
+ { { BayerFormat::GRBG, 16, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SGRBG16_1X16, },
+ { { BayerFormat::RGGB, 16, BayerFormat::Packing::None }, MEDIA_BUS_FMT_SRGGB16_1X16, },
+ { { BayerFormat::BGGR, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_SBGGR16_1X16, },
+ { { BayerFormat::GBRG, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_SGBRG16_1X16, },
+ { { BayerFormat::GRBG, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_SGRBG16_1X16, },
+ { { BayerFormat::RGGB, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_SRGGB16_1X16, },
+ { { BayerFormat::RGGB, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_SRGGB16_1X16, },
+ { { BayerFormat::MONO, 16, BayerFormat::Packing::None }, MEDIA_BUS_FMT_Y16_1X16, },
+ { { BayerFormat::MONO, 16, BayerFormat::Packing::PISP1 }, MEDIA_BUS_FMT_Y16_1X16, },
+};
+
+unsigned int bayerToMbusCode(const BayerFormat &bayer)
+{
+ const auto it = std::find_if(BayerToMbusCodeMap.begin(), BayerToMbusCodeMap.end(),
+ [bayer](const std::pair<BayerFormat, unsigned int> &match) {
+ return bayer == match.first;
+ });
+
+ if (it != BayerToMbusCodeMap.end())
+ return it->second;
+
+ return 0;
+}
+
+uint32_t mbusCodeUnpacked16(unsigned int code)
+{
+ BayerFormat bayer = BayerFormat::fromMbusCode(code);
+ BayerFormat bayer16(bayer.order, 16, BayerFormat::Packing::None);
+
+ return bayerToMbusCode(bayer16);
+}
+
+uint8_t toPiSPBayerOrder(V4L2PixelFormat format)
+{
+ BayerFormat bayer = BayerFormat::fromV4L2PixelFormat(format);
+
+ switch (bayer.order) {
+ case BayerFormat::Order::BGGR:
+ return PISP_BAYER_ORDER_BGGR;
+ case BayerFormat::Order::GBRG:
+ return PISP_BAYER_ORDER_GBRG;
+ case BayerFormat::Order::GRBG:
+ return PISP_BAYER_ORDER_GRBG;
+ case BayerFormat::Order::RGGB:
+ return PISP_BAYER_ORDER_RGGB;
+ case BayerFormat::Order::MONO:
+ return PISP_BAYER_ORDER_GREYSCALE;
+ default:
+ ASSERT(0);
+ return -1;
+ }
+}
+
+pisp_image_format_config toPiSPImageFormat(V4L2DeviceFormat &format)
+{
+ pisp_image_format_config image = {};
+
+ image.width = format.size.width;
+ image.height = format.size.height;
+ image.stride = format.planes[0].bpl;
+
+ PixelFormat pix = format.fourcc.toPixelFormat();
+
+ if (RPi::PipelineHandlerBase::isRaw(pix)) {
+ BayerFormat bayer = BayerFormat::fromPixelFormat(pix);
+ switch (bayer.packing) {
+ case BayerFormat::Packing::None:
+ image.format = PISP_IMAGE_FORMAT_BPS_16 +
+ PISP_IMAGE_FORMAT_UNCOMPRESSED;
+ break;
+ case BayerFormat::Packing::PISP1:
+ image.format = PISP_IMAGE_FORMAT_COMPRESSION_MODE_1;
+ break;
+ case BayerFormat::Packing::PISP2:
+ image.format = PISP_IMAGE_FORMAT_COMPRESSION_MODE_2;
+ break;
+ default:
+ ASSERT(0);
+ }
+ return image;
+ }
+
+ switch (pix) {
+ case formats::YUV420:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_420 +
+ PISP_IMAGE_FORMAT_PLANARITY_PLANAR;
+ image.stride2 = image.stride / 2;
+ break;
+ case formats::NV12:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_420 +
+ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR;
+ image.stride2 = image.stride;
+ break;
+ case formats::NV21:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_420 +
+ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR +
+ PISP_IMAGE_FORMAT_ORDER_SWAPPED;
+ image.stride2 = image.stride;
+ break;
+ case formats::YUYV:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_422 +
+ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED;
+ break;
+ case formats::UYVY:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_422 +
+ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED +
+ PISP_IMAGE_FORMAT_ORDER_SWAPPED;
+ break;
+ case formats::NV16:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_422 +
+ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR;
+ image.stride2 = image.stride;
+ break;
+ case formats::NV61:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL +
+ PISP_IMAGE_FORMAT_BPS_8 +
+ PISP_IMAGE_FORMAT_SAMPLING_422 +
+ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR +
+ PISP_IMAGE_FORMAT_ORDER_SWAPPED;
+ image.stride2 = image.stride;
+ break;
+ case formats::RGB888:
+ case formats::BGR888:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL;
+ break;
+ case formats::XRGB8888:
+ case formats::XBGR8888:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL + PISP_IMAGE_FORMAT_BPP_32;
+ break;
+ case formats::RGBX8888:
+ case formats::BGRX8888:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL + PISP_IMAGE_FORMAT_BPP_32 +
+ PISP_IMAGE_FORMAT_ORDER_SWAPPED;
+ break;
+ case formats::RGB161616:
+ case formats::BGR161616:
+ image.format = PISP_IMAGE_FORMAT_THREE_CHANNEL + PISP_IMAGE_FORMAT_BPS_16;
+ break;
+ default:
+ LOG(RPI, Error) << "Pixel format " << pix << " unsupported";
+ ASSERT(0);
+ }
+
+ return image;
+}
+
+void computeOptimalStride(V4L2DeviceFormat &format)
+{
+ pisp_image_format_config fmt = toPiSPImageFormat(format);
+
+ libpisp::compute_optimal_stride(fmt);
+
+ uint32_t fourcc = format.fourcc.fourcc();
+
+ /*
+ * For YUV420/422 non-multiplanar formats, double the U/V stride for the
+ * Y-plane to ensure we get the optimal alignment on all three planes.
+ */
+ if (fourcc == V4L2_PIX_FMT_YUV420 || fourcc == V4L2_PIX_FMT_YUV422P ||
+ fourcc == V4L2_PIX_FMT_YVU420)
+ fmt.stride = fmt.stride2 * 2;
+
+ format.planes[0].bpl = fmt.stride;
+ format.planes[1].bpl = fmt.stride2;
+ format.planes[2].bpl = fmt.stride2;
+
+ /*
+ * Need to set planesCount correctly so that V4L2VideoDevice::trySetFormatMultiplane()
+ * copies the bpl fields correctly.
+ */
+ const PixelFormat &pixFormat = format.fourcc.toPixelFormat();
+ const PixelFormatInfo &info = PixelFormatInfo::info(pixFormat);
+ format.planesCount = info.numPlanes();
+}
+
+void setupOutputClipping(const V4L2DeviceFormat &v4l2Format,
+ pisp_be_output_format_config &outputFormat)
+{
+ const PixelFormat &pixFormat = v4l2Format.fourcc.toPixelFormat();
+ const PixelFormatInfo &info = PixelFormatInfo::info(pixFormat);
+
+ if (info.colourEncoding != PixelFormatInfo::ColourEncodingYUV)
+ return;
+
+ if (v4l2Format.colorSpace == ColorSpace::Sycc) {
+ outputFormat.lo = 0;
+ outputFormat.hi = 65535;
+ outputFormat.lo2 = 0;
+ outputFormat.hi2 = 65535;
+ } else if (v4l2Format.colorSpace == ColorSpace::Smpte170m ||
+ v4l2Format.colorSpace == ColorSpace::Rec709) {
+ outputFormat.lo = 16 << 8;
+ outputFormat.hi = 235 << 8;
+ outputFormat.lo2 = 16 << 8;
+ outputFormat.hi2 = 240 << 8;
+ } else {
+ LOG(RPI, Warning)
+ << "Unrecognised colour space "
+ << ColorSpace::toString(v4l2Format.colorSpace)
+ << ", using full range";
+ outputFormat.lo = 0;
+ outputFormat.hi = 65535;
+ outputFormat.lo2 = 0;
+ outputFormat.hi2 = 65535;
+ }
+}
+
+int dmabufSyncStart(const SharedFD &fd)
+{
+ struct dma_buf_sync dma_sync {};
+ dma_sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;
+
+ int ret = ::ioctl(fd.get(), DMA_BUF_IOCTL_SYNC, &dma_sync);
+ if (ret)
+ LOG(RPI, Error) << "failed to lock-sync-write dma buf";
+
+ return ret;
+}
+
+int dmabufSyncEnd(const SharedFD &fd)
+{
+ struct dma_buf_sync dma_sync {};
+ dma_sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
+
+ int ret = ::ioctl(fd.get(), DMA_BUF_IOCTL_SYNC, &dma_sync);
+
+ if (ret)
+ LOG(RPI, Error) << "failed to unlock-sync-write dma buf";
+
+ return ret;
+}
+
+void do32BitConversion(void *mem, unsigned int width, unsigned int height,
+ unsigned int stride)
+{
+ /*
+ * The arm64 version is actually not that much quicker because the
+ * vast bulk of the time is spent waiting for memory.
+ */
+#if __aarch64__
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+ uint64_t count = (width + 15) / 16;
+ uint8_t *dest = ptr + count * 64;
+ uint8_t *src = ptr + count * 48;
+
+ /* Pre-decrement would have been nice. */
+ asm volatile("movi v3.16b, #255 \n"
+ "1: \n"
+ "sub %[src], %[src], #48 \n"
+ "sub %[dest], %[dest], #64 \n"
+ "subs %[count], %[count], #1 \n"
+ "ld3 {v0.16b, v1.16b, v2.16b}, [%[src]] \n"
+ "st4 {v0.16b, v1.16b, v2.16b, v3.16b}, [%[dest]] \n"
+ "b.gt 1b \n"
+ : [count]"+r" (count)
+ : [src]"r" (src), [dest]"r" (dest)
+ : "cc", "v1", "v2", "v3", "v4", "memory"
+ );
+ }
+#else
+ std::vector<uint8_t> incache(3 * width);
+ std::vector<uint8_t> outcache(4 * width);
+
+ memcpy(incache.data(), mem, 3 * width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *ptr3 = incache.data();
+ uint8_t *ptr4 = outcache.data();
+ for (unsigned int i = 0; i < width; i++) {
+ *(ptr4++) = *(ptr3++);
+ *(ptr4++) = *(ptr3++);
+ *(ptr4++) = *(ptr3++);
+ *(ptr4++) = 255;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 3 * width);
+ memcpy(ptr, outcache.data(), 4 * width);
+ }
+#endif
+}
+
+void do16BitEndianSwap([[maybe_unused]] void *mem, [[maybe_unused]] unsigned int width,
+ [[maybe_unused]] unsigned int height, [[maybe_unused]] unsigned int stride)
+{
+#if __aarch64__
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+ uint64_t count = (width + 7) / 8;
+
+ asm volatile("1: \n"
+ "ld1 {v1.16b}, [%[ptr]] \n"
+ "rev16 v1.16b, v1.16b \n"
+ "st1 {v1.16b}, [%[ptr]], #16 \n"
+ "subs %[count], %[count], #1 \n"
+ "b.gt 1b \n"
+ : [count]"+r" (count), [ptr]"+r" (ptr)
+ :
+ : "cc", "v1", "memory"
+ );
+ }
+#endif
+}
+
+void do14bitUnpack(void *mem, unsigned int width, unsigned int height,
+ unsigned int stride)
+{
+ std::vector<uint8_t> cache(stride);
+
+ for (unsigned int j = 0; j < height; j++) {
+ const uint8_t *in = ((uint8_t *)mem) + j * stride;
+ uint8_t *out = ((uint8_t *)mem) + j * stride;
+ uint8_t *p = cache.data();
+
+ std::memcpy(p, in, stride);
+ for (unsigned int i = 0; i < width; i += 4, p += 7) {
+ uint16_t p0 = (p[0] << 8) | ((p[4] & 0x3f) << 2);
+ uint16_t p1 = (p[1] << 8) | ((p[4] & 0xc0) >> 4) | ((p[5] & 0x0f) << 4);
+ uint16_t p2 = (p[2] << 8) | ((p[5] & 0xf0) >> 2) | ((p[6] & 0x03) << 6);
+ uint16_t p3 = (p[3] << 8) | (p[6] & 0xfc);
+
+ *(uint16_t *)(out + i * 2 + 0) = p0;
+ *(uint16_t *)(out + i * 2 + 2) = p1;
+ *(uint16_t *)(out + i * 2 + 4) = p2;
+ *(uint16_t *)(out + i * 2 + 6) = p3;
+ }
+ }
+}
+
+void downscaleInterleaved3(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride)
+{
+ std::vector<uint8_t> incache(3 * src_width);
+ unsigned int dst_width = src_width / 2;
+ std::vector<uint8_t> outcache(3 * dst_width);
+
+ memcpy(incache.data(), mem, 3 * src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 6, dst += 3) {
+ dst[0] = ((int)src[0] + (int)src[3] + 1) >> 1;
+ dst[1] = ((int)src[1] + (int)src[4] + 1) >> 1;
+ dst[2] = ((int)src[2] + (int)src[5] + 1) >> 1;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 3 * src_width);
+ memcpy(ptr, outcache.data(), 3 * dst_width);
+ }
+}
+
+void downscaleInterleaved4(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride)
+{
+ std::vector<uint8_t> incache(4 * src_width);
+ unsigned int dst_width = src_width / 2;
+ std::vector<uint8_t> outcache(4 * dst_width);
+
+ memcpy(incache.data(), mem, 4 * src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 8, dst += 4) {
+ dst[0] = ((int)src[0] + (int)src[4] + 1) >> 1;
+ dst[1] = ((int)src[1] + (int)src[5] + 1) >> 1;
+ dst[2] = ((int)src[2] + (int)src[6] + 1) >> 1;
+ dst[3] = ((int)src[3] + (int)src[7] + 1) >> 1;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 4 * src_width);
+ memcpy(ptr, outcache.data(), 4 * dst_width);
+ }
+}
+
+void downscalePlaneInternal(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride, std::vector<uint8_t> &incache,
+ std::vector<uint8_t> &outcache)
+{
+ unsigned int dst_width = src_width / 2;
+ memcpy(incache.data(), mem, src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 2, dst++)
+ *dst = ((int)src[0] + (int)src[1] + 1) >> 1;
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, src_width);
+ memcpy(ptr, outcache.data(), dst_width);
+ }
+}
+
+void downscalePlanar420(void *memY, void *memU, void *memV, unsigned int height,
+ unsigned int src_width, unsigned int stride)
+{
+ std::vector<uint8_t> incache(src_width);
+ std::vector<uint8_t> outcache(src_width / 2);
+
+ downscalePlaneInternal(memY, height, src_width, stride, incache, outcache);
+ downscalePlaneInternal(memU, height / 2, src_width / 2, stride / 2, incache, outcache);
+ downscalePlaneInternal(memV, height / 2, src_width / 2, stride / 2, incache, outcache);
+}
+
+void downscalePlanar422(void *memY, void *memU, void *memV,
+ unsigned int height, unsigned int src_width, unsigned int stride)
+{
+ std::vector<uint8_t> incache(src_width);
+ std::vector<uint8_t> outcache(src_width / 2);
+
+ downscalePlaneInternal(memY, height, src_width, stride, incache, outcache);
+ downscalePlaneInternal(memU, height, src_width / 2, stride / 2, incache, outcache);
+ downscalePlaneInternal(memV, height, src_width / 2, stride / 2, incache, outcache);
+}
+
+void downscaleInterleavedYuyv(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride)
+{
+ std::vector<uint8_t> incache(2 * src_width);
+ unsigned int dst_width = src_width / 2;
+ std::vector<uint8_t> outcache(2 * dst_width);
+
+ memcpy(incache.data(), mem, 2 * src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 8, dst += 4) {
+ dst[0] = ((int)src[0] + (int)src[2] + 1) >> 1;
+ dst[1] = ((int)src[1] + (int)src[5] + 1) >> 1;
+ dst[2] = ((int)src[4] + (int)src[6] + 1) >> 1;
+ dst[3] = ((int)src[3] + (int)src[7] + 1) >> 1;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 4 * src_width);
+ memcpy(ptr, outcache.data(), 2 * dst_width);
+ }
+}
+
+void downscaleInterleavedUyvy(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride)
+{
+ std::vector<uint8_t> incache(2 * src_width);
+ unsigned int dst_width = src_width / 2;
+ std::vector<uint8_t> outcache(2 * dst_width);
+
+ memcpy(incache.data(), mem, 2 * src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 8, dst += 4) {
+ dst[0] = ((int)src[0] + (int)src[4] + 1) >> 1;
+ dst[1] = ((int)src[1] + (int)src[3] + 1) >> 1;
+ dst[2] = ((int)src[2] + (int)src[6] + 1) >> 1;
+ dst[3] = ((int)src[5] + (int)src[7] + 1) >> 1;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 4 * src_width);
+ memcpy(ptr, outcache.data(), 2 * dst_width);
+ }
+}
+
+void downscaleInterleaved2Internal(void *mem, unsigned int height, unsigned int src_width,
+ unsigned int stride, std::vector<uint8_t> &incache,
+ std::vector<uint8_t> &outcache)
+{
+ unsigned int dst_width = src_width / 2;
+ memcpy(incache.data(), mem, 2 * src_width);
+ for (unsigned int j = 0; j < height; j++) {
+ uint8_t *ptr = (uint8_t *)mem + j * stride;
+
+ uint8_t *src = incache.data(), *dst = outcache.data();
+ for (unsigned int i = 0; i < dst_width; i++, src += 4, dst += 2) {
+ dst[0] = ((int)src[0] + (int)src[2] + 1) >> 1;
+ dst[1] = ((int)src[1] + (int)src[3] + 1) >> 1;
+ }
+
+ if (j < height - 1)
+ memcpy(incache.data(), ptr + stride, 2 * src_width);
+ memcpy(ptr, outcache.data(), 2 * dst_width);
+ }
+}
+
+void downscaleSemiPlanar420(void *memY, void *memUV, unsigned int height,
+ unsigned int src_width, unsigned int stride)
+{
+ std::vector<uint8_t> incache(src_width);
+ std::vector<uint8_t> outcache(src_width / 2);
+
+ downscalePlaneInternal(memY, height, src_width, stride, incache, outcache);
+ downscaleInterleaved2Internal(memUV, height / 2, src_width / 2, stride,
+ incache, outcache);
+}
+
+void downscaleStreamBuffer(RPi::Stream *stream, int index)
+{
+ unsigned int downscale = stream->swDownscale();
+ /* Must be a power of 2. */
+ ASSERT((downscale & (downscale - 1)) == 0);
+
+ unsigned int stride = stream->configuration().stride;
+ unsigned int dst_width = stream->configuration().size.width;
+ unsigned int height = stream->configuration().size.height;
+ const PixelFormat &pixFormat = stream->configuration().pixelFormat;
+ const RPi::BufferObject &b = stream->getBuffer(index);
+ void *mem = b.mapped->planes()[0].data();
+ ASSERT(b.mapped);
+
+ /* Do repeated downscale-by-2 in place until we're done. */
+ for (; downscale > 1; downscale >>= 1) {
+ unsigned int src_width = downscale * dst_width;
+
+ if (pixFormat == formats::RGB888 || pixFormat == formats::BGR888) {
+ downscaleInterleaved3(mem, height, src_width, stride);
+ } else if (pixFormat == formats::XRGB8888 || pixFormat == formats::XBGR8888) {
+ /* On some devices these may actually be 24bpp at this point. */
+ if (stream->getFlags() & StreamFlag::Needs32bitConv)
+ downscaleInterleaved3(mem, height, src_width, stride);
+ else
+ downscaleInterleaved4(mem, height, src_width, stride);
+ } else if (pixFormat == formats::YUV420 || pixFormat == formats::YVU420) {
+ /* These may look like either single or multi-planar buffers. */
+ void *mem1;
+ void *mem2;
+ if (b.mapped->planes().size() == 3) {
+ mem1 = b.mapped->planes()[1].data();
+ mem2 = b.mapped->planes()[2].data();
+ } else {
+ unsigned int ySize = height * stride;
+ mem1 = static_cast<uint8_t *>(mem) + ySize;
+ mem2 = static_cast<uint8_t *>(mem1) + ySize / 4;
+ }
+ downscalePlanar420(mem, mem1, mem2, height, src_width, stride);
+ } else if (pixFormat == formats::YUV422 || pixFormat == formats::YVU422) {
+ /* These may look like either single or multi-planar buffers. */
+ void *mem1;
+ void *mem2;
+ if (b.mapped->planes().size() == 3) {
+ mem1 = b.mapped->planes()[1].data();
+ mem2 = b.mapped->planes()[2].data();
+ } else {
+ unsigned int ySize = height * stride;
+ mem1 = static_cast<uint8_t *>(mem) + ySize;
+ mem2 = static_cast<uint8_t *>(mem1) + ySize / 2;
+ }
+ downscalePlanar422(mem, mem1, mem2, height, src_width, stride);
+ } else if (pixFormat == formats::YUYV || pixFormat == formats::YVYU) {
+ downscaleInterleavedYuyv(mem, height, src_width, stride);
+ } else if (pixFormat == formats::UYVY || pixFormat == formats::VYUY) {
+ downscaleInterleavedUyvy(mem, height, src_width, stride);
+ } else if (pixFormat == formats::NV12 || pixFormat == formats::NV21) {
+ /* These may look like either single or multi-planar buffers. */
+ void *mem1;
+ if (b.mapped->planes().size() == 2)
+ mem1 = b.mapped->planes()[1].data();
+ else
+ mem1 = static_cast<uint8_t *>(mem) + height * stride;
+ downscaleSemiPlanar420(mem, mem1, height, src_width, stride);
+ } else {
+ LOG(RPI, Error) << "Sw downscale unsupported for " << pixFormat;
+ ASSERT(0);
+ }
+ }
+}
+
+/* Return largest width of any of these streams (or of the camera input). */
+unsigned int getLargestWidth(const V4L2SubdeviceFormat &sensorFormat,
+ const std::vector<StreamParams> &outStreams)
+{
+ unsigned int largestWidth = sensorFormat.size.width;
+
+ for (const auto &stream : outStreams)
+ largestWidth = std::max(largestWidth, stream.cfg->size.width);
+
+ return largestWidth;
+}
+
+/* Return the minimum number of pixels required to write out multiples of 16 bytes. */
+unsigned int getFormatAlignment(const V4L2PixelFormat &fourcc)
+{
+ const PixelFormatInfo &info = PixelFormatInfo::info(fourcc);
+ unsigned int formatAlignment = 0;
+ for (const auto &plane : info.planes) {
+ if (plane.bytesPerGroup) {
+ /* How many pixels we need in this plane for a multiple of 16 bytes (??). */
+ unsigned int align = 16 * info.pixelsPerGroup /
+ std::gcd(16u, plane.bytesPerGroup);
+ formatAlignment = std::max(formatAlignment, align);
+ }
+ }
+
+ return formatAlignment;
+}
+
+/* Calculate the amount of software downscale required (which is a power of 2). */
+unsigned int calculateSwDownscale(const V4L2DeviceFormat &format, unsigned int largestWidth,
+ unsigned int platformMaxDownscale)
+{
+ unsigned int formatAlignment = getFormatAlignment(format.fourcc);
+ unsigned int maxDownscale = platformMaxDownscale * 16 / formatAlignment;
+ unsigned int limitWidth = largestWidth / maxDownscale;
+
+ unsigned int hwWidth = format.size.width;
+ unsigned int swDownscale = 1;
+ for (; hwWidth < limitWidth; hwWidth *= 2, swDownscale *= 2);
+
+ return swDownscale;
+}
+
+} /* namespace */
+
+using ::libpisp::BackEnd;
+using ::libpisp::FrontEnd;
+
+class PiSPCameraData final : public RPi::CameraData
+{
+public:
+ PiSPCameraData(PipelineHandler *pipe, const libpisp::PiSPVariant &variant)
+ : RPi::CameraData(pipe), pispVariant_(variant)
+ {
+ /* Initialise internal libpisp logging. */
+ ::libpisp::logging_init();
+ LOG(RPI, Info) << "libpisp version " << ::libpisp::version();
+ }
+
+ ~PiSPCameraData()
+ {
+ freeBuffers();
+ }
+
+ V4L2VideoDevice::Formats ispFormats() const override
+ {
+ return isp_[Isp::Output0].dev()->formats();
+ }
+
+ V4L2VideoDevice::Formats rawFormats() const override
+ {
+ return cfe_[Cfe::Output0].dev()->formats();
+ }
+
+ V4L2VideoDevice *frontendDevice() override
+ {
+ return cfe_[Cfe::Output0].dev();
+ }
+
+ CameraConfiguration::Status
+ platformValidate(RPi::RPiCameraConfiguration *rpiConfig) const override;
+
+ int platformPipelineConfigure(const std::unique_ptr<YamlObject> &root) override;
+
+ void platformStart() override;
+ void platformStop() override;
+ void platformFreeBuffers() override;
+
+ void cfeBufferDequeue(FrameBuffer *buffer);
+ void beInputDequeue(FrameBuffer *buffer);
+ void beOutputDequeue(FrameBuffer *buffer);
+
+ void processStatsComplete(const ipa::RPi::BufferIds &buffers);
+ void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);
+ void setCameraTimeout(uint32_t maxFrameLengthMs);
+
+ /* Array of CFE and ISP device streams and associated buffers/streams. */
+ RPi::Device<Cfe, 4> cfe_;
+ RPi::Device<Isp, 8> isp_;
+
+ const libpisp::PiSPVariant &pispVariant_;
+
+ /* Frontend/Backend objects shared with the IPA. */
+ SharedMemObject<FrontEnd> fe_;
+ SharedMemObject<BackEnd> be_;
+ bool beEnabled_;
+
+ std::unique_ptr<V4L2Subdevice> csi2Subdev_;
+ std::unique_ptr<V4L2Subdevice> feSubdev_;
+
+ std::vector<FrameBuffer *> tdnBuffers_;
+ std::vector<FrameBuffer *> stitchBuffers_;
+ unsigned int tdnInputIndex_;
+ unsigned int stitchInputIndex_;
+
+ struct Config {
+ /*
+ * Number of CFE config and stats buffers to allocate and use. A
+ * larger number minimises the possibility of dropping frames,
+ * but increases the latency for updating the HW configuration.
+ */
+ unsigned int numCfeConfigStatsBuffers;
+ /*
+ * Number of jobs to queue ahead to the CFE on startup.
+ * A larger number will increase latency for 3A changes.
+ */
+ unsigned int numCfeConfigQueue;
+ /* Don't use BE temporal denoise and free some memory resources. */
+ bool disableTdn;
+ /* Don't use BE HDR and free some memory resources. */
+ bool disableHdr;
+ };
+
+ Config config_;
+
+ bool adjustDeviceFormat(V4L2DeviceFormat &format) const;
+
+private:
+ int platformConfigure(const RPi::RPiCameraConfiguration *rpiConfig) override;
+
+ int platformConfigureIpa([[maybe_unused]] ipa::RPi::ConfigParams &params) override
+ {
+ return 0;
+ }
+
+ int platformInitIpa(ipa::RPi::InitParams &params) override;
+
+ int configureEntities(V4L2SubdeviceFormat sensorFormat,
+ V4L2SubdeviceFormat &embeddedFormat);
+ int configureCfe();
+ bool calculateCscConfiguration(const V4L2DeviceFormat &v4l2Format, pisp_be_ccm_config &csc);
+ int configureBe(const std::optional<ColorSpace> &yuvColorSpace);
+
+ void platformSetIspCrop(unsigned int index, const Rectangle &ispCrop) override;
+
+ void prepareCfe();
+ void prepareBe(uint32_t bufferId, bool stitchSwapBuffers);
+
+ void tryRunPipeline() override;
+
+ struct CfeJob {
+ ControlList sensorControls;
+ unsigned int delayContext;
+ std::unordered_map<const RPi::Stream *, FrameBuffer *> buffers;
+ };
+
+ std::queue<CfeJob> cfeJobQueue_;
+
+ bool cfeJobComplete() const
+ {
+ if (cfeJobQueue_.empty())
+ return false;
+
+ const CfeJob &job = cfeJobQueue_.back();
+ return job.buffers.count(&cfe_[Cfe::Output0]) &&
+ job.buffers.count(&cfe_[Cfe::Stats]) &&
+ (!sensorMetadata_ ||
+ job.buffers.count(&cfe_[Cfe::Embedded]));
+ }
+
+ std::string last_dump_file_;
+};
+
+class PipelineHandlerPiSP : public RPi::PipelineHandlerBase
+{
+public:
+ PipelineHandlerPiSP(CameraManager *manager)
+ : RPi::PipelineHandlerBase(manager)
+ {
+ }
+
+ ~PipelineHandlerPiSP()
+ {
+ }
+
+ bool match(DeviceEnumerator *enumerator) override;
+
+private:
+ PiSPCameraData *cameraData(Camera *camera)
+ {
+ return static_cast<PiSPCameraData *>(camera->_d());
+ }
+
+ int prepareBuffers(Camera *camera) override;
+ int platformRegister(std::unique_ptr<RPi::CameraData> &cameraData,
+ MediaDevice *cfe, MediaDevice *isp) override;
+};
+
+bool PipelineHandlerPiSP::match(DeviceEnumerator *enumerator)
+{
+ constexpr unsigned int numCfeDevices = 2;
+
+ /*
+ * Loop over all CFE instances, but return out once a match is found.
+ * This is to ensure we correctly enumerate the camera when an instance
+ * of the CFE has registered with media controller, but has not registered
+ * device nodes due to a sensor subdevice failure.
+ */
+ for (unsigned int i = 0; i < numCfeDevices; i++) {
+ DeviceMatch cfe("rp1-cfe");
+ cfe.add("rp1-cfe-fe-image0");
+ cfe.add("rp1-cfe-fe-stats");
+ cfe.add("rp1-cfe-fe-config");
+ MediaDevice *cfeDevice = acquireMediaDevice(enumerator, cfe);
+
+ if (!cfeDevice) {
+ LOG(RPI, Debug) << "Unable to acquire a CFE instance";
+ break;
+ }
+
+ DeviceMatch isp("pispbe");
+ isp.add("pispbe-input");
+ isp.add("pispbe-config");
+ isp.add("pispbe-output0");
+ isp.add("pispbe-output1");
+ isp.add("pispbe-tdn_output");
+ isp.add("pispbe-tdn_input");
+ isp.add("pispbe-stitch_output");
+ isp.add("pispbe-stitch_input");
+ MediaDevice *ispDevice = acquireMediaDevice(enumerator, isp);
+
+ if (!ispDevice) {
+ LOG(RPI, Debug) << "Unable to acquire ISP instance";
+ break;
+ }
+
+ /*
+ * The loop below is used to register multiple cameras behind
+ * one or more video mux devices that are attached to a
+ * particular CFE instance. Obviously these cameras cannot be
+ * used simultaneously.
+ */
+ unsigned int numCameras = 0;
+ for (MediaEntity *entity : cfeDevice->entities()) {
+ if (entity->function() != MEDIA_ENT_F_CAM_SENSOR)
+ continue;
+
+ const libpisp::PiSPVariant &variant =
+ libpisp::get_variant(cfeDevice->hwRevision(),
+ ispDevice->hwRevision());
+ if (!variant.NumFrontEnds() || !variant.NumBackEnds()) {
+ LOG(RPI, Error) << "Unsupported PiSP variant";
+ break;
+ }
+
+ std::unique_ptr<RPi::CameraData> cameraData =
+ std::make_unique<PiSPCameraData>(this, variant);
+ PiSPCameraData *pisp =
+ static_cast<PiSPCameraData *>(cameraData.get());
+
+ pisp->fe_ = SharedMemObject<FrontEnd>
+ ("pisp_frontend", true, pisp->pispVariant_);
+ pisp->be_ = SharedMemObject<BackEnd>
+ ("pisp_backend", BackEnd::Config({}), pisp->pispVariant_);
+
+ if (!pisp->fe_.fd().isValid() || !pisp->be_.fd().isValid()) {
+ LOG(RPI, Error) << "Failed to create ISP shared objects";
+ break;
+ }
+
+ int ret = registerCamera(cameraData, cfeDevice, "csi2",
+ ispDevice, entity);
+ if (ret)
+ LOG(RPI, Error) << "Failed to register camera "
+ << entity->name() << ": " << ret;
+ else
+ numCameras++;
+ }
+
+ if (numCameras)
+ return true;
+ }
+
+ return false;
+}
+
+int PipelineHandlerPiSP::prepareBuffers(Camera *camera)
+{
+ PiSPCameraData *data = cameraData(camera);
+ unsigned int numRawBuffers = 0;
+ int ret;
+
+ for (Stream *s : camera->streams()) {
+ if (PipelineHandlerBase::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 CFE, allocate a minimum of 4 buffers as we want
+ * to avoid any frame drops.
+ */
+ constexpr unsigned int minBuffers = 4;
+ if (stream == &data->cfe_[Cfe::Output0]) {
+ /*
+ * If an application has configured a RAW stream, allocate
+ * additional buffers to make up the minimum, but ensure
+ * we have at least 2 sets of internal buffers to use to
+ * minimise frame drops.
+ */
+ numBuffers = std::max<int>(2, minBuffers - numRawBuffers);
+ } else if (stream == &data->isp_[Isp::Input]) {
+ /*
+ * ISP input buffers are imported from the CFE, so follow
+ * similar logic as above to count all the RAW buffers
+ * available.
+ */
+ numBuffers = numRawBuffers +
+ std::max<int>(2, minBuffers - numRawBuffers);
+ } else if (stream == &data->cfe_[Cfe::Embedded]) {
+ /*
+ * Embedded data buffers are (currently) for internal use,
+ * so allocate a reasonably large amount.
+ */
+ numBuffers = 12;
+ } else if (stream == &data->cfe_[Cfe::Stats] ||
+ stream == &data->cfe_[Cfe::Config]) {
+ numBuffers = data->config_.numCfeConfigStatsBuffers;
+ } else if (!data->beEnabled_) {
+ /* Backend not enabled, we don't need to allocate buffers. */
+ numBuffers = 0;
+ } else if (stream == &data->isp_[Isp::TdnOutput] && data->config_.disableTdn) {
+ /* TDN is explicitly disabled. */
+ continue;
+ } else if (stream == &data->isp_[Isp::StitchOutput] && data->config_.disableHdr) {
+ /* Stitch/HDR is explicitly disabled. */
+ continue;
+ } else {
+ /* Allocate 2 sets of all other Backend buffers */
+ numBuffers = 2;
+ }
+
+ LOG(RPI, Debug) << "Preparing " << numBuffers
+ << " buffers for stream " << stream->name();
+
+ ret = stream->prepareBuffers(numBuffers);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Store the Framebuffer pointers for convenience as we will ping-pong
+ * these buffers between the input and output nodes for TDN and Stitch.
+ *
+ * The buffer size needs to be setup here as well. Conveniently this is
+ * the same for both TDN and stitch.
+ */
+ pisp_image_format_config tdn;
+ data->be_->GetTdnOutputFormat(tdn);
+ unsigned int size = tdn.stride * tdn.height;
+ for (auto const &buffer : data->isp_[Isp::TdnOutput].getBuffers()) {
+ FrameBuffer *b = buffer.second.buffer;
+ b->_d()->metadata().planes()[0].bytesused = size;
+ data->tdnBuffers_.push_back(b);
+ }
+ for (auto const &buffer : data->isp_[Isp::StitchOutput].getBuffers()) {
+ FrameBuffer *b = buffer.second.buffer;
+ b->_d()->metadata().planes()[0].bytesused = size;
+ data->stitchBuffers_.push_back(b);
+ }
+
+ /* Size up the config buffers as well. */
+ for (auto &b : data->isp_[Isp::Config].getBuffers()) {
+ FrameMetadata::Plane &plane = b.second.buffer->_d()->metadata().planes()[0];
+ plane.bytesused = sizeof(pisp_be_tiles_config);
+ }
+
+ /*
+ * Pass the stats and embedded data buffers to the IPA. No other
+ * buffers need to be passed.
+ */
+ mapBuffers(camera, data->cfe_[Cfe::Stats].getBuffers(), RPi::MaskStats);
+ if (data->sensorMetadata_)
+ mapBuffers(camera, data->cfe_[Cfe::Embedded].getBuffers(),
+ RPi::MaskEmbeddedData);
+
+ return 0;
+}
+
+int PipelineHandlerPiSP::platformRegister(std::unique_ptr<RPi::CameraData> &cameraData,
+ MediaDevice *cfe, MediaDevice *isp)
+{
+ PiSPCameraData *data = static_cast<PiSPCameraData *>(cameraData.get());
+ int ret;
+
+ MediaEntity *cfeImage = cfe->getEntityByName("rp1-cfe-fe-image0");
+ MediaEntity *cfeEmbedded = cfe->getEntityByName("rp1-cfe-csi2-ch1");
+ MediaEntity *cfeStats = cfe->getEntityByName("rp1-cfe-fe-stats");
+ MediaEntity *cfeConfig = cfe->getEntityByName("rp1-cfe-fe-config");
+ MediaEntity *ispInput = isp->getEntityByName("pispbe-input");
+ MediaEntity *IpaPrepare = isp->getEntityByName("pispbe-config");
+ MediaEntity *ispOutput0 = isp->getEntityByName("pispbe-output0");
+ MediaEntity *ispOutput1 = isp->getEntityByName("pispbe-output1");
+ MediaEntity *ispTdnOutput = isp->getEntityByName("pispbe-tdn_output");
+ MediaEntity *ispTdnInput = isp->getEntityByName("pispbe-tdn_input");
+ MediaEntity *ispStitchOutput = isp->getEntityByName("pispbe-stitch_output");
+ MediaEntity *ispStitchInput = isp->getEntityByName("pispbe-stitch_input");
+
+ /* Locate and open the cfe video streams. */
+ data->cfe_[Cfe::Output0] = RPi::Stream("CFE Image", cfeImage, StreamFlag::RequiresMmap);
+ data->cfe_[Cfe::Embedded] = RPi::Stream("CFE Embedded", cfeEmbedded);
+ data->cfe_[Cfe::Stats] = RPi::Stream("CFE Stats", cfeStats);
+ data->cfe_[Cfe::Config] = RPi::Stream("CFE Config", cfeConfig,
+ StreamFlag::Recurrent | StreamFlag::RequiresMmap);
+
+ /* Tag the ISP input stream as an import stream. */
+ data->isp_[Isp::Input] =
+ RPi::Stream("ISP Input", ispInput, StreamFlag::ImportOnly);
+ data->isp_[Isp::Config] =
+ RPi::Stream("ISP Config", IpaPrepare, StreamFlag::Recurrent |
+ StreamFlag::RequiresMmap);
+ data->isp_[Isp::Output0] =
+ RPi::Stream("ISP Output0", ispOutput0, StreamFlag::RequiresMmap);
+ data->isp_[Isp::Output1] =
+ RPi::Stream("ISP Output1", ispOutput1, StreamFlag::RequiresMmap);
+ data->isp_[Isp::TdnOutput] =
+ RPi::Stream("ISP TDN Output", ispTdnOutput, StreamFlag::Recurrent);
+ data->isp_[Isp::TdnInput] =
+ RPi::Stream("ISP TDN Input", ispTdnInput, StreamFlag::ImportOnly |
+ StreamFlag::Recurrent);
+ data->isp_[Isp::StitchOutput] =
+ RPi::Stream("ISP Stitch Output", ispStitchOutput, StreamFlag::Recurrent);
+ data->isp_[Isp::StitchInput] =
+ RPi::Stream("ISP Stitch Input", ispStitchInput, StreamFlag::ImportOnly |
+ StreamFlag::Recurrent);
+
+ /* Wire up all the buffer connections. */
+ data->cfe_[Cfe::Output0].dev()->bufferReady.connect(data, &PiSPCameraData::cfeBufferDequeue);
+ data->cfe_[Cfe::Stats].dev()->bufferReady.connect(data, &PiSPCameraData::cfeBufferDequeue);
+ data->cfe_[Cfe::Config].dev()->bufferReady.connect(data, &PiSPCameraData::cfeBufferDequeue);
+ data->isp_[Isp::Input].dev()->bufferReady.connect(data, &PiSPCameraData::beInputDequeue);
+ data->isp_[Isp::Config].dev()->bufferReady.connect(data, &PiSPCameraData::beOutputDequeue);
+ data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &PiSPCameraData::beOutputDequeue);
+ data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &PiSPCameraData::beOutputDequeue);
+ data->cfe_[Cfe::Embedded].dev()->bufferReady.connect(data, &PiSPCameraData::cfeBufferDequeue);
+
+ data->csi2Subdev_ = std::make_unique<V4L2Subdevice>(cfe->getEntityByName("csi2"));
+ data->feSubdev_ = std::make_unique<V4L2Subdevice>(cfe->getEntityByName("pisp-fe"));
+ data->csi2Subdev_->open();
+ data->feSubdev_->open();
+
+ /*
+ * Open all CFE 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->cfe_[Cfe::Output0]);
+ data->streams_.push_back(&data->cfe_[Cfe::Config]);
+ data->streams_.push_back(&data->cfe_[Cfe::Stats]);
+ if (data->sensorMetadata_)
+ data->streams_.push_back(&data->cfe_[Cfe::Embedded]);
+
+ data->streams_.push_back(&data->isp_[Isp::Input]);
+ data->streams_.push_back(&data->isp_[Isp::Output0]);
+ data->streams_.push_back(&data->isp_[Isp::Output1]);
+ data->streams_.push_back(&data->isp_[Isp::Config]);
+ data->streams_.push_back(&data->isp_[Isp::TdnInput]);
+ data->streams_.push_back(&data->isp_[Isp::TdnOutput]);
+ data->streams_.push_back(&data->isp_[Isp::StitchInput]);
+ data->streams_.push_back(&data->isp_[Isp::StitchOutput]);
+
+ for (auto stream : data->streams_) {
+ ret = stream->dev()->open();
+ if (ret)
+ return ret;
+ }
+
+ /* Write up all the IPA connections. */
+ data->ipa_->prepareIspComplete.connect(data, &PiSPCameraData::prepareIspComplete);
+ data->ipa_->processStatsComplete.connect(data, &PiSPCameraData::processStatsComplete);
+ data->ipa_->setCameraTimeout.connect(data, &PiSPCameraData::setCameraTimeout);
+
+ /*
+ * List the available streams an application may request. At present, we
+ * do not advertise CFE Embedded and ISP Statistics streams, as there
+ * is no mechanism for the application to request non-image buffer formats.
+ */
+ std::set<Stream *> streams;
+ streams.insert(&data->cfe_[Cfe::Output0]);
+ streams.insert(&data->isp_[Isp::Output0]);
+ streams.insert(&data->isp_[Isp::Output1]);
+
+ /* Create and register the camera. */
+ const std::string &id = data->sensor_->id();
+ std::shared_ptr<Camera> camera =
+ Camera::create(std::move(cameraData), id, streams);
+ PipelineHandler::registerCamera(std::move(camera));
+
+ LOG(RPI, Info) << "Registered camera " << id
+ << " to CFE device " << cfe->deviceNode()
+ << " and ISP device " << isp->deviceNode()
+ << " using PiSP variant " << data->pispVariant_.Name();
+
+ return 0;
+}
+
+CameraConfiguration::Status
+PiSPCameraData::platformValidate(RPi::RPiCameraConfiguration *rpiConfig) const
+{
+ std::vector<StreamParams> &rawStreams = rpiConfig->rawStreams_;
+ std::vector<StreamParams> &outStreams = rpiConfig->outStreams_;
+
+ CameraConfiguration::Status status = CameraConfiguration::Status::Valid;
+
+ /* Can only output 1 RAW stream and/or 2 YUV/RGB streams for now. */
+ if (rawStreams.size() > 1 || outStreams.size() > 2) {
+ LOG(RPI, Error) << "Invalid number of streams requested";
+ return CameraConfiguration::Status::Invalid;
+ }
+
+ if (!rawStreams.empty()) {
+ rawStreams[0].dev = cfe_[Cfe::Output0].dev();
+
+ StreamConfiguration *rawStream = rawStreams[0].cfg;
+ BayerFormat bayer = BayerFormat::fromPixelFormat(rawStream->pixelFormat);
+ /*
+ * We cannot output CSI2 packed or non 16-bit output from the frontend,
+ * so signal the output as unpacked 16-bits in these cases.
+ */
+ if (bayer.packing == BayerFormat::Packing::CSI2 || bayer.bitDepth != 16) {
+ bayer.packing = (bayer.packing == BayerFormat::Packing::CSI2) ?
+ BayerFormat::Packing::PISP1 : BayerFormat::Packing::None;
+ bayer.bitDepth = 16;
+ }
+
+ /* The RAW stream size cannot exceed the sensor frame output - for now. */
+ if (rawStream->size != rpiConfig->sensorFormat_.size ||
+ rawStream->pixelFormat != bayer.toPixelFormat()) {
+ rawStream->size = rpiConfig->sensorFormat_.size;
+ rawStream->pixelFormat = bayer.toPixelFormat();
+ status = CameraConfiguration::Adjusted;
+ }
+
+ rawStreams[0].format =
+ RPi::PipelineHandlerBase::toV4L2DeviceFormat(cfe_[Cfe::Output0].dev(), rawStream);
+
+ computeOptimalStride(rawStreams[0].format);
+ }
+
+ /*
+ * For the two ISP outputs, the lower resolution must be routed from
+ * Output 1
+ *
+ * Index 0 contains the largest requested resolution.
+ */
+ unsigned int largestWidth = getLargestWidth(rpiConfig->sensorFormat_,
+ rpiConfig->outStreams_);
+
+ for (unsigned int i = 0; i < outStreams.size(); i++) {
+ StreamConfiguration *cfg = outStreams[i].cfg;
+
+ /*
+ * Output 1 must be for the smallest resolution. We will
+ * have that fixed up in the code above.
+ */
+ auto ispOutput = i == 1 || outStreams.size() == 1 ? Isp::Output1
+ : Isp::Output0;
+ outStreams[i].dev = isp_[ispOutput].dev();
+
+ /*
+ * Don't let The output streams downscale by more than 64x when
+ * a downscaler block is available, or 16x when there's only the
+ * resampler.
+ */
+ Size rawSize = rpiConfig->sensorFormat_.size.boundedToAspectRatio(cfg->size);
+ unsigned int outputIndex = ispOutput == Isp::Output0 ? 0 : 1;
+ Size minSize;
+ if (pispVariant_.BackEndDownscalerAvailable(0, outputIndex)) {
+ /*
+ * Downscaler available. Allow up to 64x downscale. If not a multiple of
+ * 64, round up to the next integer, but also ensure the result is even.
+ */
+ const unsigned int downscale = 64;
+ minSize.width = (rawSize.width + downscale - 1) / downscale;
+ minSize.width = (minSize.width + 1) & ~1; /* ensure even */
+ minSize.height = (rawSize.height + downscale - 1) / downscale;
+ minSize.height = (minSize.height + 1) & ~1; /* ensure even */
+ } else {
+ /* No downscale. Resampler requires: (output_dim - 1) * 16 <= input_dim - 1 */
+ const unsigned int downscale = 16;
+ minSize.width = (rawSize.width - 1 + downscale - 1) / downscale + 1;
+ minSize.width = (minSize.width + 1) & ~1; /* ensure even */
+ minSize.height = (rawSize.height - 1 + downscale - 1) / downscale + 1;
+ minSize.height = (minSize.height + 1) & ~1; /* ensure even */
+ }
+ LOG(RPI, Debug) << "minSize: width " << minSize.width << " height " << minSize.height;
+
+ /* Bound the output size to minSize, preserve aspect ratio, and ensure even numbers. */
+ if (cfg->size.width < minSize.width) {
+ cfg->size.height = (cfg->size.height * minSize.width / cfg->size.width + 1) & ~1;
+ cfg->size.width = minSize.width;
+ status = CameraConfiguration::Status::Adjusted;
+ }
+
+ if (cfg->size.height < minSize.height) {
+ cfg->size.width = (cfg->size.width * minSize.height / cfg->size.height + 1) & ~1;
+ cfg->size.height = minSize.height;
+ status = CameraConfiguration::Status::Adjusted;
+ }
+
+ /* Make sure output1 is no larger than output 0. */
+ Size size = cfg->size.boundedTo(outStreams[0].cfg->size);
+
+ /* \todo Warn if upscaling: reduces image quality. */
+
+ if (cfg->size != size) {
+ cfg->size = size;
+ status = CameraConfiguration::Status::Adjusted;
+ }
+
+ outStreams[i].format =
+ RPi::PipelineHandlerBase::toV4L2DeviceFormat(outStreams[i].dev, outStreams[i].cfg);
+
+ /* Compute the optimal stride for the BE output buffers. */
+ computeOptimalStride(outStreams[i].format);
+
+ /*
+ * We need to check for software downscaling. This must happen
+ * after adjusting the device format so that we can choose the
+ * largest stride - which might have been the original
+ * unadjusted format, or the adjusted one (if software
+ * downscaling means it's larger).
+ */
+ V4L2DeviceFormat adjustedFormat = outStreams[i].format;
+ adjustDeviceFormat(adjustedFormat);
+
+ unsigned int swDownscale =
+ calculateSwDownscale(adjustedFormat, largestWidth,
+ be_->GetMaxDownscale());
+ LOG(RPI, Debug) << "For stream " << adjustedFormat
+ << " swDownscale is " << swDownscale;
+ if (swDownscale > 1) {
+ adjustedFormat.size.width *= swDownscale;
+ computeOptimalStride(adjustedFormat);
+ for (unsigned int p = 0; p < outStreams[i].format.planesCount; p++)
+ outStreams[i].format.planes[p].bpl =
+ std::max(outStreams[i].format.planes[p].bpl, adjustedFormat.planes[p].bpl);
+ }
+ }
+
+ return status;
+}
+
+int PiSPCameraData::platformPipelineConfigure(const std::unique_ptr<YamlObject> &root)
+{
+ config_ = {
+ .numCfeConfigStatsBuffers = 12,
+ .numCfeConfigQueue = 2,
+ .disableTdn = false,
+ .disableHdr = false,
+ };
+
+ if (!root)
+ return 0;
+
+ std::optional<double> ver = (*root)["version"].get<double>();
+ if (!ver || *ver != 1.0) {
+ LOG(RPI, Error) << "Unexpected configuration file version reported";
+ return -EINVAL;
+ }
+
+ std::optional<std::string> target = (*root)["target"].get<std::string>();
+ if (target != "pisp") {
+ LOG(RPI, Error) << "Unexpected target reported: expected \"pisp\", got "
+ << (target ? target->c_str() : "(unknown)");
+ return -EINVAL;
+ }
+
+ const YamlObject &phConfig = (*root)["pipeline_handler"];
+ config_.numCfeConfigStatsBuffers =
+ phConfig["num_cfe_config_stats_buffers"].get<unsigned int>(config_.numCfeConfigStatsBuffers);
+ config_.numCfeConfigQueue =
+ phConfig["num_cfe_config_queue"].get<unsigned int>(config_.numCfeConfigQueue);
+ config_.disableTdn = phConfig["disable_tdn"].get<bool>(config_.disableTdn);
+ config_.disableHdr = phConfig["disable_hdr"].get<bool>(config_.disableHdr);
+
+ if (config_.disableTdn) {
+ LOG(RPI, Info) << "TDN disabled by user config";
+ streams_.erase(std::remove_if(streams_.begin(), streams_.end(),
+ [this] (const RPi::Stream *s) { return s == &isp_[Isp::TdnInput] ||
+ s == &isp_[Isp::TdnInput]; }),
+ streams_.end());
+ }
+
+ if (config_.disableHdr) {
+ LOG(RPI, Info) << "HDR disabled by user config";
+ streams_.erase(std::remove_if(streams_.begin(), streams_.end(),
+ [this] (const RPi::Stream *s) { return s == &isp_[Isp::StitchInput] ||
+ s == &isp_[Isp::StitchOutput]; }),
+ streams_.end());
+ }
+
+ if (config_.numCfeConfigStatsBuffers < 1) {
+ LOG(RPI, Error)
+ << "Invalid configuration: num_cfe_config_stats_buffers must be >= 1";
+ return -EINVAL;
+ }
+
+ if (config_.numCfeConfigQueue < 1) {
+ LOG(RPI, Error)
+ << "Invalid configuration: numCfeConfigQueue must be >= 1";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+std::unordered_map<uint32_t, uint32_t> deviceAdjustTable = {
+ { V4L2_PIX_FMT_RGBX32, V4L2_PIX_FMT_RGB24 },
+ { V4L2_PIX_FMT_XBGR32, V4L2_PIX_FMT_BGR24 }
+};
+
+bool PiSPCameraData::adjustDeviceFormat(V4L2DeviceFormat &format) const
+{
+ auto it = deviceAdjustTable.find(format.fourcc.fourcc());
+
+ if (pispVariant_.BackendRGB32Supported(0))
+ return false;
+
+ if (it != deviceAdjustTable.end()) {
+ LOG(RPI, Debug) << "Swapping 32-bit for 24-bit format";
+ format.fourcc = V4L2PixelFormat(it->second);
+ return true;
+ }
+
+ return false;
+}
+
+int PiSPCameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfig)
+{
+ const std::vector<RPi::RPiCameraConfiguration::StreamParams> &rawStreams = rpiConfig->rawStreams_;
+ const std::vector<RPi::RPiCameraConfiguration::StreamParams> &outStreams = rpiConfig->outStreams_;
+ int ret;
+ V4L2VideoDevice *cfe = cfe_[Cfe::Output0].dev();
+ V4L2DeviceFormat cfeFormat;
+
+ /*
+ * See which streams are requested, and route the user
+ * StreamConfiguration appropriately.
+ */
+ if (rawStreams.empty()) {
+ /*
+ * The CFE Frontend output will always be 16-bits unpacked, so adjust the
+ * mbus code right at the start.
+ */
+ V4L2SubdeviceFormat sensorFormatMod = rpiConfig->sensorFormat_;
+ sensorFormatMod.code = mbusCodeUnpacked16(sensorFormatMod.code);
+ cfeFormat = RPi::PipelineHandlerBase::toV4L2DeviceFormat(cfe,
+ sensorFormatMod,
+ BayerFormat::Packing::PISP1);
+ computeOptimalStride(cfeFormat);
+ } else {
+ rawStreams[0].cfg->setStream(&cfe_[Cfe::Output0]);
+ cfe_[Cfe::Output0].setFlags(StreamFlag::External);
+ cfeFormat = rawStreams[0].format;
+ }
+
+ /*
+ * If the sensor output is 16-bits, we must endian swap the buffer
+ * contents to account for the HW missing this feature.
+ */
+ cfe_[Cfe::Output0].clearFlags(StreamFlag::Needs16bitEndianSwap);
+ if (MediaBusFormatInfo::info(rpiConfig->sensorFormat_.code).bitsPerPixel == 16) {
+ cfe_[Cfe::Output0].setFlags(StreamFlag::Needs16bitEndianSwap);
+ LOG(RPI, Warning)
+ << "The sensor is configured for a 16-bit output, statistics"
+ << " will not be correct. You must use manual camera settings.";
+ }
+
+ /* Ditto for the 14-bit unpacking. */
+ cfe_[Cfe::Output0].clearFlags(StreamFlag::Needs14bitUnpack);
+ if (MediaBusFormatInfo::info(rpiConfig->sensorFormat_.code).bitsPerPixel == 14) {
+ cfe_[Cfe::Output0].setFlags(StreamFlag::Needs14bitUnpack);
+ LOG(RPI, Warning)
+ << "The sensor is configured for a 14-bit output, statistics"
+ << " will not be correct. You must use manual camera settings.";
+ }
+
+ ret = cfe->setFormat(&cfeFormat);
+ if (ret)
+ return ret;
+
+ /* Set the TDN and Stitch node formats in case they are turned on. */
+ isp_[Isp::TdnOutput].dev()->setFormat(&cfeFormat);
+ isp_[Isp::TdnInput].dev()->setFormat(&cfeFormat);
+ isp_[Isp::StitchOutput].dev()->setFormat(&cfeFormat);
+ isp_[Isp::StitchInput].dev()->setFormat(&cfeFormat);
+
+ ret = isp_[Isp::Input].dev()->setFormat(&cfeFormat);
+ if (ret)
+ return ret;
+
+ LOG(RPI, Info) << "Sensor: " << sensor_->id()
+ << " - Selected sensor format: " << rpiConfig->sensorFormat_
+ << " - Selected CFE format: " << cfeFormat;
+
+ /*
+ * Find the largest width of any stream; we'll use it later to check for
+ * excessive downscaling.
+ */
+ unsigned int largestWidth = getLargestWidth(rpiConfig->sensorFormat_, outStreams);
+
+ unsigned int beEnables = 0;
+ V4L2DeviceFormat format;
+
+ /*
+ * First thing is to remove Isp::Output0 and Isp::Output1 from streams_
+ * as they may be unused depending on the configuration. Add them back
+ * only if needed.
+ */
+ streams_.erase(std::remove_if(streams_.begin(), streams_.end(),
+ [this] (const RPi::Stream *s) { return s == &isp_[Isp::Output0] ||
+ s == &isp_[Isp::Output1]; }),
+ streams_.end());
+
+ cropParams_.clear();
+ for (unsigned int i = 0; i < outStreams.size(); i++) {
+ StreamConfiguration *cfg = outStreams[i].cfg;
+ unsigned int ispIndex;
+
+ /*
+ * Output 1 must be for the smallest resolution. We will
+ * have that fixed up in the code above.
+ */
+ RPi::Stream *stream;
+ if (i == 1 || outStreams.size() == 1) {
+ stream = &isp_[Isp::Output1];
+ beEnables |= PISP_BE_RGB_ENABLE_OUTPUT1;
+ ispIndex = 1;
+ } else {
+ stream = &isp_[Isp::Output0];
+ beEnables |= PISP_BE_RGB_ENABLE_OUTPUT0;
+ ispIndex = 0;
+ }
+
+ format = outStreams[i].format;
+ bool needs32BitConversion = adjustDeviceFormat(format);
+
+ /*
+ * This pixel format may not be the same as the configured
+ * pixel format if adjustDeviceFormat() above has reqused a change.
+ */
+ PixelFormat pixFmt = format.fourcc.toPixelFormat();
+
+ /* If there's excessive downscaling we'll do some of it in software. */
+ unsigned int swDownscale = calculateSwDownscale(format, largestWidth,
+ be_->GetMaxDownscale());
+ unsigned int hwWidth = format.size.width * swDownscale;
+ format.size.width = hwWidth;
+
+ LOG(RPI, Debug) << "Setting " << stream->name() << " to "
+ << format << " (sw downscale " << swDownscale << ")";
+
+ ret = stream->dev()->setFormat(&format);
+ if (ret)
+ return -EINVAL;
+ LOG(RPI, Debug) << "After setFormat, stride " << format.planes[0].bpl;
+
+ if (format.size.height != cfg->size.height ||
+ format.size.width != hwWidth || format.fourcc.toPixelFormat() != pixFmt) {
+ 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);
+
+ libcamera::RPi::Stream::StreamFlags flags = StreamFlag::External;
+
+ stream->clearFlags(StreamFlag::Needs32bitConv);
+ if (needs32BitConversion)
+ flags |= StreamFlag::Needs32bitConv;
+
+ /* Set smallest selection the ISP will allow. */
+ Size minCrop{ 32, 32 };
+
+ /* Adjust aspect ratio by providing crops on the input image. */
+ Size size = cfeFormat.size.boundedToAspectRatio(outStreams[i].cfg->size);
+ Rectangle ispCrop = size.centeredTo(Rectangle(cfeFormat.size).center());
+
+ /*
+ * Calculate the minimum crop. The rule is that (output_dim - 1) / (input_dim - 1)
+ * must be strictly < 16. We add 2 after dividing because +1
+ * comes from the division that rounds down, and +1 because we
+ * had (input_dim - 1).
+ */
+ Size scalingMinSize = outStreams[i].cfg->size.shrunkBy({ 1, 1 }) / 16;
+ scalingMinSize.growBy({ 2, 2 });
+ minCrop.expandTo(scalingMinSize);
+
+ platformSetIspCrop(ispIndex, ispCrop);
+ /*
+ * Set the scaler crop to the value we are using (scaled to native sensor
+ * coordinates).
+ */
+ cropParams_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(outStreams[i].index),
+ std::forward_as_tuple(ispCrop, minCrop, ispIndex));
+
+ cfg->setStream(stream);
+ stream->setFlags(flags);
+ stream->setSwDownscale(swDownscale);
+ streams_.push_back(stream);
+ }
+
+ pisp_be_global_config global;
+ be_->GetGlobal(global);
+ global.rgb_enables &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 + PISP_BE_RGB_ENABLE_OUTPUT1);
+ global.rgb_enables |= beEnables;
+ be_->SetGlobal(global);
+
+ beEnabled_ = beEnables & (PISP_BE_RGB_ENABLE_OUTPUT0 | PISP_BE_RGB_ENABLE_OUTPUT1);
+
+ /* CFE statistics output format. */
+ format = {};
+ format.fourcc = V4L2PixelFormat(V4L2_META_FMT_RPI_FE_STATS);
+ ret = cfe_[Cfe::Stats].dev()->setFormat(&format);
+ if (ret) {
+ LOG(RPI, Error) << "Failed to set format on CFE stats stream: "
+ << format;
+ return ret;
+ }
+
+ /* CFE config format. */
+ format = {};
+ format.fourcc = V4L2PixelFormat(V4L2_META_FMT_RPI_FE_CFG);
+ ret = cfe_[Cfe::Config].dev()->setFormat(&format);
+ if (ret) {
+ LOG(RPI, Error) << "Failed to set format on CFE config stream: "
+ << format;
+ return ret;
+ }
+
+ /*
+ * Configure the CFE embedded data output format only if the sensor
+ * supports it.
+ */
+ V4L2SubdeviceFormat embeddedFormat = sensor_->embeddedDataFormat();
+ if (sensorMetadata_) {
+ static const std::map<uint32_t, V4L2PixelFormat> metaFormats{
+ { MEDIA_BUS_FMT_META_8, V4L2PixelFormat(V4L2_META_FMT_GENERIC_8) },
+ { MEDIA_BUS_FMT_META_10, V4L2PixelFormat(V4L2_META_FMT_GENERIC_CSI2_10) },
+ { MEDIA_BUS_FMT_META_12, V4L2PixelFormat(V4L2_META_FMT_GENERIC_CSI2_12) },
+ { MEDIA_BUS_FMT_META_14, V4L2PixelFormat(V4L2_META_FMT_GENERIC_CSI2_14) },
+ };
+
+ const auto metaFormat = metaFormats.find(embeddedFormat.code);
+ if (metaFormat == metaFormats.end()) {
+ LOG(RPI, Error)
+ << "Unsupported metadata format "
+ << utils::hex(embeddedFormat.code, 4);
+ return -EINVAL;
+ }
+
+ format = {};
+ format.fourcc = metaFormat->second;
+ format.size = embeddedFormat.size;
+
+ LOG(RPI, Debug) << "Setting embedded data format " << format;
+ ret = cfe_[Cfe::Embedded].dev()->setFormat(&format);
+ if (ret) {
+ LOG(RPI, Error) << "Failed to set format on CFE embedded: "
+ << format;
+ return ret;
+ }
+ }
+
+ configureEntities(rpiConfig->sensorFormat_, embeddedFormat);
+ configureCfe();
+
+ if (beEnabled_)
+ configureBe(rpiConfig->yuvColorSpace_);
+
+ return 0;
+}
+
+void PiSPCameraData::platformStart()
+{
+ /*
+ * We don't need to worry about dequeue events for the TDN and Stitch
+ * nodes as the buffers are simply ping-ponged every frame. But we do
+ * want to track the currently used input index.
+ */
+ tdnInputIndex_ = 0;
+ stitchInputIndex_ = 0;
+
+ cfeJobQueue_ = {};
+
+ for (unsigned int i = 0; i < config_.numCfeConfigQueue; i++)
+ prepareCfe();
+
+ /* Clear the debug dump file history. */
+ last_dump_file_.clear();
+}
+
+void PiSPCameraData::platformStop()
+{
+ cfeJobQueue_ = {};
+}
+
+void PiSPCameraData::platformFreeBuffers()
+{
+ tdnBuffers_.clear();
+ stitchBuffers_.clear();
+}
+
+void PiSPCameraData::cfeBufferDequeue(FrameBuffer *buffer)
+{
+ RPi::Stream *stream = nullptr;
+ int index = 0;
+
+ if (!isRunning())
+ return;
+
+ for (RPi::Stream &s : cfe_) {
+ index = s.getBufferId(buffer);
+ if (index) {
+ stream = &s;
+ break;
+ }
+ }
+
+ /* If the last CFE job has completed, we need a new job entry in the queue. */
+ if (cfeJobQueue_.empty() || cfeJobComplete())
+ cfeJobQueue_.push({});
+
+ CfeJob &job = cfeJobQueue_.back();
+
+ /* 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;
+
+ job.buffers[stream] = buffer;
+
+ if (stream == &cfe_[Cfe::Output0]) {
+ /* Do an endian swap or 14-bit unpacking if needed. */
+ if (stream->getFlags() & StreamFlag::Needs16bitEndianSwap ||
+ stream->getFlags() & StreamFlag::Needs14bitUnpack) {
+ const unsigned int stride = stream->configuration().stride;
+ const unsigned int width = stream->configuration().size.width;
+ const unsigned int height = stream->configuration().size.height;
+ const RPi::BufferObject &b = stream->getBuffer(index);
+
+ ASSERT(b.mapped);
+ void *mem = b.mapped->planes()[0].data();
+
+ dmabufSyncStart(buffer->planes()[0].fd);
+ if (stream->getFlags() & StreamFlag::Needs16bitEndianSwap)
+ do16BitEndianSwap(mem, width, height, stride);
+ else
+ do14bitUnpack(mem, width, height, stride);
+ dmabufSyncEnd(buffer->planes()[0].fd);
+ }
+
+ /*
+ * 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);
+ job.sensorControls = std::move(ctrl);
+ job.delayContext = delayContext;
+ } else if (stream == &cfe_[Cfe::Config]) {
+ /* The config buffer can be re-queued back straight away. */
+ handleStreamBuffer(buffer, &cfe_[Cfe::Config]);
+ prepareCfe();
+ }
+
+ handleState();
+}
+
+void PiSPCameraData::beInputDequeue(FrameBuffer *buffer)
+{
+ if (!isRunning())
+ return;
+
+ LOG(RPI, Debug) << "Stream ISP Input buffer complete"
+ << ", buffer id " << cfe_[Cfe::Output0].getBufferId(buffer)
+ << ", timestamp: " << buffer->metadata().timestamp;
+
+ /* The ISP input buffer gets re-queued into CFE. */
+ handleStreamBuffer(buffer, &cfe_[Cfe::Output0]);
+ handleState();
+}
+
+void PiSPCameraData::beOutputDequeue(FrameBuffer *buffer)
+{
+ RPi::Stream *stream = nullptr;
+ int index;
+
+ if (!isRunning())
+ return;
+
+ for (RPi::Stream &s : isp_) {
+ index = s.getBufferId(buffer);
+ if (index) {
+ 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;
+
+ bool downscale = stream->swDownscale() > 1;
+ bool needs32bitConv = !!(stream->getFlags() & StreamFlag::Needs32bitConv);
+
+ if (downscale || needs32bitConv)
+ dmabufSyncStart(buffer->planes()[0].fd);
+
+ if (downscale) {
+ /* Further software downscaling must be applied. */
+ downscaleStreamBuffer(stream, index);
+ }
+
+ /* Convert 24bpp outputs to 32bpp outputs where necessary. */
+ if (needs32bitConv) {
+ unsigned int stride = stream->configuration().stride;
+ unsigned int width = stream->configuration().size.width;
+ unsigned int height = stream->configuration().size.height;
+
+ const RPi::BufferObject &b = stream->getBuffer(index);
+
+ ASSERT(b.mapped);
+ void *mem = b.mapped->planes()[0].data();
+ do32BitConversion(mem, width, height, stride);
+ }
+
+ if (downscale || needs32bitConv)
+ dmabufSyncEnd(buffer->planes()[0].fd);
+
+ handleStreamBuffer(buffer, stream);
+
+ /*
+ * Increment the number of ISP outputs generated.
+ * This is needed to track dropped frames.
+ */
+ ispOutputCount_++;
+ handleState();
+}
+
+void PiSPCameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
+{
+ if (!isRunning())
+ return;
+
+ handleStreamBuffer(cfe_[Cfe::Stats].getBuffers().at(buffers.stats & RPi::MaskID).buffer,
+ &cfe_[Cfe::Stats]);
+}
+
+void PiSPCameraData::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<utils::Duration>(1s, 5 * maxFrameLengthMs * 1ms);
+
+ LOG(RPI, Debug) << "Setting CFE timeout to " << timeout;
+ cfe_[Cfe::Output0].dev()->setDequeueTimeout(timeout);
+}
+
+void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers)
+{
+ unsigned int embeddedId = buffers.embedded & RPi::MaskID;
+ unsigned int bayerId = buffers.bayer & RPi::MaskID;
+ FrameBuffer *buffer;
+
+ if (!isRunning())
+ return;
+
+ if (sensorMetadata_ && embeddedId) {
+ buffer = cfe_[Cfe::Embedded].getBuffers().at(embeddedId).buffer;
+ handleStreamBuffer(buffer, &cfe_[Cfe::Embedded]);
+ }
+
+ if (!beEnabled_) {
+ /*
+ * If there is no need to run the Backend, just signal that the
+ * input buffer is completed and all Backend outputs are ready.
+ */
+ ispOutputCount_ = ispOutputTotal_;
+ buffer = cfe_[Cfe::Output0].getBuffers().at(bayerId).buffer;
+ handleStreamBuffer(buffer, &cfe_[Cfe::Output0]);
+ } else
+ prepareBe(bayerId, stitchSwapBuffers);
+
+ state_ = State::IpaComplete;
+ handleState();
+}
+
+int PiSPCameraData::configureCfe()
+{
+ V4L2DeviceFormat cfeFormat;
+ cfe_[Cfe::Output0].dev()->getFormat(&cfeFormat);
+
+ std::scoped_lock<FrontEnd> l(*fe_);
+
+ pisp_fe_global_config global;
+ fe_->GetGlobal(global);
+ global.enables &= ~PISP_FE_ENABLE_COMPRESS0;
+
+ global.enables |= PISP_FE_ENABLE_OUTPUT0;
+ global.bayer_order = toPiSPBayerOrder(cfeFormat.fourcc);
+
+ pisp_image_format_config image = toPiSPImageFormat(cfeFormat);
+ pisp_fe_input_config input = {};
+
+ input.streaming = 1;
+ input.format = image;
+ input.format.format = PISP_IMAGE_FORMAT_BPS_16;
+
+ if (PISP_IMAGE_FORMAT_COMPRESSED(image.format)) {
+ pisp_compress_config compress;
+ compress.offset = DefaultCompressionOffset;
+ compress.mode = (image.format & PISP_IMAGE_FORMAT_COMPRESSION_MASK) /
+ PISP_IMAGE_FORMAT_COMPRESSION_MODE_1;
+ global.enables |= PISP_FE_ENABLE_COMPRESS0;
+ fe_->SetCompress(0, compress);
+ }
+
+ if (input.format.width > pispVariant_.FrontEndDownscalerMaxWidth(0, 0))
+ global.enables |= PISP_FE_ENABLE_DECIMATE;
+
+ fe_->SetGlobal(global);
+ fe_->SetInput(input);
+ fe_->SetOutputFormat(0, image);
+
+ return 0;
+}
+
+bool PiSPCameraData::calculateCscConfiguration(const V4L2DeviceFormat &v4l2Format, pisp_be_ccm_config &csc)
+{
+ const PixelFormat &pixFormat = v4l2Format.fourcc.toPixelFormat();
+ const PixelFormatInfo &info = PixelFormatInfo::info(pixFormat);
+ memset(&csc, 0, sizeof(csc));
+
+ if (info.colourEncoding == PixelFormatInfo::ColourEncodingYUV) {
+ /* Look up the correct YCbCr conversion matrix for this colour space. */
+ if (v4l2Format.colorSpace == ColorSpace::Sycc)
+ be_->InitialiseYcbcr(csc, "jpeg");
+ else if (v4l2Format.colorSpace == ColorSpace::Smpte170m)
+ be_->InitialiseYcbcr(csc, "smpte170m");
+ else if (v4l2Format.colorSpace == ColorSpace::Rec709)
+ be_->InitialiseYcbcr(csc, "rec709");
+ else {
+ LOG(RPI, Warning)
+ << "Unrecognised colour space "
+ << ColorSpace::toString(v4l2Format.colorSpace)
+ << ", defaulting to sYCC";
+ be_->InitialiseYcbcr(csc, "jpeg");
+ }
+ return true;
+ }
+ /* There will be more formats to check for in due course. */
+ else if (pixFormat == formats::RGB888 || pixFormat == formats::RGBX8888 ||
+ pixFormat == formats::XRGB8888 || pixFormat == formats::RGB161616) {
+ /* Identity matrix but with RB colour swap. */
+ csc.coeffs[2] = csc.coeffs[4] = csc.coeffs[6] = 1 << 10;
+ return true;
+ }
+
+ return false;
+}
+
+int PiSPCameraData::configureBe(const std::optional<ColorSpace> &yuvColorSpace)
+{
+ pisp_image_format_config inputFormat;
+ V4L2DeviceFormat cfeFormat;
+
+ isp_[Isp::Input].dev()->getFormat(&cfeFormat);
+ inputFormat = toPiSPImageFormat(cfeFormat);
+
+ pisp_be_global_config global;
+ be_->GetGlobal(global);
+ global.bayer_enables &= ~(PISP_BE_BAYER_ENABLE_DECOMPRESS +
+ PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS +
+ PISP_BE_BAYER_ENABLE_TDN_COMPRESS +
+ PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS +
+ PISP_BE_BAYER_ENABLE_STITCH_COMPRESS);
+ global.rgb_enables &= ~(PISP_BE_RGB_ENABLE_RESAMPLE0 +
+ PISP_BE_RGB_ENABLE_RESAMPLE1 +
+ PISP_BE_RGB_ENABLE_DOWNSCALE0 +
+ PISP_BE_RGB_ENABLE_DOWNSCALE1 +
+ PISP_BE_RGB_ENABLE_CSC0 +
+ PISP_BE_RGB_ENABLE_CSC1);
+
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_INPUT;
+ global.bayer_order = toPiSPBayerOrder(cfeFormat.fourcc);
+
+ ispOutputTotal_ = 1; /* Config buffer */
+ if (PISP_IMAGE_FORMAT_COMPRESSED(inputFormat.format)) {
+ pisp_decompress_config decompress;
+ decompress.offset = DefaultCompressionOffset;
+ decompress.mode = (inputFormat.format & PISP_IMAGE_FORMAT_COMPRESSION_MASK)
+ / PISP_IMAGE_FORMAT_COMPRESSION_MODE_1;
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_DECOMPRESS;
+ be_->SetDecompress(decompress);
+ }
+
+ if (global.rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT0) {
+ pisp_be_output_format_config outputFormat0 = {};
+ V4L2DeviceFormat ispFormat0 = {};
+
+ isp_[Isp::Output0].dev()->getFormat(&ispFormat0);
+ outputFormat0.image = toPiSPImageFormat(ispFormat0);
+
+ pisp_be_ccm_config csc;
+ if (calculateCscConfiguration(ispFormat0, csc)) {
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_CSC0;
+ be_->SetCsc(0, csc);
+ }
+
+ BackEnd::SmartResize resize = {};
+ resize.width = ispFormat0.size.width;
+ resize.height = ispFormat0.size.height;
+ be_->SetSmartResize(0, resize);
+
+ setupOutputClipping(ispFormat0, outputFormat0);
+
+ be_->SetOutputFormat(0, outputFormat0);
+ ispOutputTotal_++;
+ }
+
+ if (global.rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT1) {
+ pisp_be_output_format_config outputFormat1 = {};
+ V4L2DeviceFormat ispFormat1 = {};
+
+ isp_[Isp::Output1].dev()->getFormat(&ispFormat1);
+ outputFormat1.image = toPiSPImageFormat(ispFormat1);
+
+ pisp_be_ccm_config csc;
+ if (calculateCscConfiguration(ispFormat1, csc)) {
+ global.rgb_enables |= PISP_BE_RGB_ENABLE_CSC1;
+ be_->SetCsc(1, csc);
+ }
+
+ BackEnd::SmartResize resize = {};
+ resize.width = ispFormat1.size.width;
+ resize.height = ispFormat1.size.height;
+ be_->SetSmartResize(1, resize);
+
+ setupOutputClipping(ispFormat1, outputFormat1);
+
+ be_->SetOutputFormat(1, outputFormat1);
+ ispOutputTotal_++;
+ }
+
+ /* Setup the TDN I/O blocks in case TDN gets turned on later. */
+ V4L2DeviceFormat tdnV4L2Format;
+ isp_[Isp::TdnOutput].dev()->getFormat(&tdnV4L2Format);
+ pisp_image_format_config tdnFormat = toPiSPImageFormat(tdnV4L2Format);
+ be_->SetTdnOutputFormat(tdnFormat);
+ be_->SetTdnInputFormat(tdnFormat);
+
+ if (PISP_IMAGE_FORMAT_COMPRESSED(tdnFormat.format)) {
+ pisp_decompress_config tdnDecompress;
+ pisp_compress_config tdnCompress;
+
+ tdnDecompress.offset = tdnCompress.offset = DefaultCompressionOffset;
+ tdnDecompress.mode = tdnCompress.mode = DefaultCompressionMode;
+ be_->SetTdnDecompress(tdnDecompress);
+ be_->SetTdnCompress(tdnCompress);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS +
+ PISP_BE_BAYER_ENABLE_TDN_COMPRESS;
+ }
+
+ /* Likewise for the Stitch block. */
+ V4L2DeviceFormat stitchV4L2Format;
+ isp_[Isp::StitchOutput].dev()->getFormat(&stitchV4L2Format);
+ pisp_image_format_config stitchFormat = toPiSPImageFormat(stitchV4L2Format);
+ be_->SetStitchOutputFormat(stitchFormat);
+ be_->SetStitchInputFormat(stitchFormat);
+
+ if (PISP_IMAGE_FORMAT_COMPRESSED(stitchFormat.format)) {
+ pisp_decompress_config stitchDecompress;
+ pisp_compress_config stitchCompress;
+
+ /* Stitch block is after BLC, so compression offset should be 0. */
+ stitchDecompress.offset = stitchCompress.offset = 0;
+ stitchDecompress.mode = stitchCompress.mode = DefaultCompressionMode;
+ be_->SetStitchDecompress(stitchDecompress);
+ be_->SetStitchCompress(stitchCompress);
+ global.bayer_enables |= PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS +
+ PISP_BE_BAYER_ENABLE_STITCH_COMPRESS;
+ }
+
+ /*
+ * For the bit of the pipeline where we go temporarily into YCbCr, we'll use the
+ * same flavour of YCbCr as dictated by the headline colour space. But there's
+ * no benefit from compressing and shifting the range, so we'll stick with the
+ * full range version of whatever that colour space is.
+ */
+ if (yuvColorSpace) {
+ pisp_be_ccm_config ccm;
+ if (yuvColorSpace == ColorSpace::Sycc) {
+ be_->InitialiseYcbcr(ccm, "jpeg");
+ be_->SetYcbcr(ccm);
+ be_->InitialiseYcbcrInverse(ccm, "jpeg");
+ be_->SetYcbcrInverse(ccm);
+ } else if (yuvColorSpace == ColorSpace::Smpte170m) {
+ /* We want the full range version of smpte170m, aka. jpeg */
+ be_->InitialiseYcbcr(ccm, "jpeg");
+ be_->SetYcbcr(ccm);
+ be_->InitialiseYcbcrInverse(ccm, "jpeg");
+ be_->SetYcbcrInverse(ccm);
+ } else if (yuvColorSpace == ColorSpace::Rec709) {
+ be_->InitialiseYcbcr(ccm, "rec709_full");
+ be_->SetYcbcr(ccm);
+ be_->InitialiseYcbcrInverse(ccm, "rec709_full");
+ be_->SetYcbcrInverse(ccm);
+ } else {
+ /* Validation should have ensured this can't happen. */
+ LOG(RPI, Error)
+ << "Invalid colour space "
+ << ColorSpace::toString(yuvColorSpace);
+ ASSERT(0);
+ }
+ } else {
+ /* Again, validation should have prevented this. */
+ LOG(RPI, Error) << "No YUV colour space";
+ ASSERT(0);
+ }
+
+ be_->SetGlobal(global);
+ be_->SetInputFormat(inputFormat);
+
+ return 0;
+}
+
+void PiSPCameraData::platformSetIspCrop(unsigned int index, const Rectangle &ispCrop)
+{
+ pisp_be_crop_config beCrop = {
+ static_cast<uint16_t>(ispCrop.x),
+ static_cast<uint16_t>(ispCrop.y),
+ static_cast<uint16_t>(ispCrop.width),
+ static_cast<uint16_t>(ispCrop.height)
+ };
+
+ LOG(RPI, Debug) << "Output " << index << " " << ispCrop.toString();
+ be_->SetCrop(index, beCrop);
+}
+
+int PiSPCameraData::platformInitIpa(ipa::RPi::InitParams &params)
+{
+ params.fe = fe_.fd();
+ params.be = be_.fd();
+ return 0;
+}
+
+int PiSPCameraData::configureEntities(V4L2SubdeviceFormat sensorFormat,
+ V4L2SubdeviceFormat &embeddedFormat)
+{
+ int ret = 0;
+
+ constexpr unsigned int csiVideoSinkPad = 0;
+ constexpr unsigned int csiVideoSourcePad = 1;
+ constexpr unsigned int csiMetaSourcePad = 2;
+
+ constexpr unsigned int feVideoSinkPad = 0;
+ constexpr unsigned int feConfigSinkPad = 1;
+ constexpr unsigned int feVideo0SourcePad = 2;
+ constexpr unsigned int feVideo1SourcePad = 3;
+ constexpr unsigned int feStatsSourcePad = 4;
+
+ const MediaEntity *csi2 = csi2Subdev_->entity();
+ const MediaEntity *fe = feSubdev_->entity();
+
+ for (MediaLink *link : csi2->pads()[csiVideoSourcePad]->links()) {
+ if (link->sink()->entity()->name() == "rp1-cfe-csi2-ch0")
+ link->setEnabled(false);
+ else if (link->sink()->entity()->name() == "pisp-fe")
+ link->setEnabled(true);
+ }
+
+ csi2->pads()[csiMetaSourcePad]->links()[0]->setEnabled(sensorMetadata_);
+
+ fe->pads()[feConfigSinkPad]->links()[0]->setEnabled(true);
+ fe->pads()[feVideo0SourcePad]->links()[0]->setEnabled(true);
+ fe->pads()[feVideo1SourcePad]->links()[0]->setEnabled(false);
+ fe->pads()[feStatsSourcePad]->links()[0]->setEnabled(true);
+
+ const V4L2Subdevice::Stream imageStream{
+ csiVideoSinkPad,
+ sensor_->imageStream().stream
+ };
+ const V4L2Subdevice::Stream embeddedDataStream{
+ csiVideoSinkPad,
+ sensor_->embeddedDataStream().value_or(V4L2Subdevice::Stream{}).stream
+ };
+
+ V4L2Subdevice::Routing routing;
+ routing.emplace_back(imageStream, V4L2Subdevice::Stream{ csiVideoSourcePad, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+ if (sensorMetadata_)
+ routing.emplace_back(embeddedDataStream,
+ V4L2Subdevice::Stream{ csiMetaSourcePad, 0 },
+ V4L2_SUBDEV_ROUTE_FL_ACTIVE);
+
+ ret = csi2Subdev_->setRouting(&routing);
+ if (ret)
+ return ret;
+
+ ret = csi2Subdev_->setFormat(imageStream, &sensorFormat);
+ if (ret)
+ return ret;
+
+ if (sensorMetadata_) {
+ ret = csi2Subdev_->setFormat(embeddedDataStream, &embeddedFormat);
+ if (ret)
+ return ret;
+ }
+
+ V4L2SubdeviceFormat feFormat = sensorFormat;
+ feFormat.code = mbusCodeUnpacked16(sensorFormat.code);
+ ret = feSubdev_->setFormat(feVideoSinkPad, &feFormat);
+ if (ret)
+ return ret;
+
+ ret = csi2Subdev_->setFormat(csiVideoSourcePad, &feFormat);
+ if (ret)
+ return ret;
+
+ V4L2DeviceFormat feOutputFormat;
+ cfe_[Cfe::Output0].dev()->getFormat(&feOutputFormat);
+ BayerFormat feOutputBayer = BayerFormat::fromV4L2PixelFormat(feOutputFormat.fourcc);
+
+ feFormat.code = bayerToMbusCode(feOutputBayer);
+ ret = feSubdev_->setFormat(feVideo0SourcePad, &feFormat);
+
+ return ret;
+}
+
+void PiSPCameraData::prepareCfe()
+{
+ /* Fetch an unused config buffer from the stream .*/
+ const RPi::BufferObject &config = cfe_[Cfe::Config].acquireBuffer();
+ ASSERT(config.mapped);
+
+ {
+ std::scoped_lock<FrontEnd> l(*fe_);
+ Span<uint8_t> configBuffer = config.mapped->planes()[0];
+ fe_->Prepare(reinterpret_cast<pisp_fe_config *>(configBuffer.data()));
+ }
+
+ config.buffer->_d()->metadata().planes()[0].bytesused = sizeof(pisp_fe_config);
+ cfe_[Cfe::Config].queueBuffer(config.buffer);
+}
+
+void PiSPCameraData::prepareBe(uint32_t bufferId, bool stitchSwapBuffers)
+{
+ ispOutputCount_ = 0;
+
+ FrameBuffer *buffer = cfe_[Cfe::Output0].getBuffers().at(bufferId).buffer;
+
+ LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bufferId
+ << ", timestamp: " << buffer->metadata().timestamp;
+
+ isp_[Isp::Input].queueBuffer(buffer);
+
+ /* Ping-pong between input/output buffers for the TDN and Stitch nodes. */
+ if (!config_.disableTdn) {
+ isp_[Isp::TdnInput].queueBuffer(tdnBuffers_[tdnInputIndex_]);
+ isp_[Isp::TdnOutput].queueBuffer(tdnBuffers_[tdnInputIndex_ ^ 1]);
+ tdnInputIndex_ ^= 1;
+ }
+
+ if (!config_.disableHdr) {
+ if (stitchSwapBuffers)
+ stitchInputIndex_ ^= 1;
+ isp_[Isp::StitchInput].queueBuffer(stitchBuffers_[stitchInputIndex_]);
+ isp_[Isp::StitchOutput].queueBuffer(stitchBuffers_[stitchInputIndex_ ^ 1]);
+ }
+
+ /* Fetch an unused config buffer from the stream .*/
+ const RPi::BufferObject &config = isp_[Isp::Config].acquireBuffer();
+ ASSERT(config.mapped);
+
+ Span<uint8_t> configBufferSpan = config.mapped->planes()[0];
+ pisp_be_tiles_config *configBuffer = reinterpret_cast<pisp_be_tiles_config *>(configBufferSpan.data());
+ be_->Prepare(configBuffer);
+
+ /*
+ * If the LIBCAMERA_RPI_PISP_CONFIG_DUMP environment variable is set,
+ * dump the Backend config to the given file. This is a one-shot
+ * operation, so log the filename that was provided and allow the
+ * application to change the filename for multiple dumps in a single
+ * run.
+ *
+ * \todo Using an environment variable is only a temporary solution
+ * until we have support for vendor specific controls in libcamera.
+ */
+ const char *config_dump = utils::secure_getenv("LIBCAMERA_RPI_PISP_CONFIG_DUMP");
+ if (config_dump && last_dump_file_ != config_dump) {
+ std::ofstream of(config_dump);
+ if (of.is_open()) {
+ of << be_->GetJsonConfig(configBuffer);
+ last_dump_file_ = config_dump;
+ }
+ }
+
+ isp_[Isp::Config].queueBuffer(config.buffer);
+}
+
+void PiSPCameraData::tryRunPipeline()
+{
+ /* If any of our request or buffer queues are empty, we cannot proceed. */
+ if (state_ != State::Idle || requestQueue_.empty() || !cfeJobComplete())
+ return;
+
+ CfeJob &job = cfeJobQueue_.front();
+
+ /* 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(job.sensorControls, request);
+
+ /* Set our state to say the pipeline is active. */
+ state_ = State::Busy;
+
+ unsigned int bayerId = cfe_[Cfe::Output0].getBufferId(job.buffers[&cfe_[Cfe::Output0]]);
+ unsigned int statsId = cfe_[Cfe::Stats].getBufferId(job.buffers[&cfe_[Cfe::Stats]]);
+ ASSERT(bayerId && statsId);
+
+ std::stringstream ss;
+ ss << "Signalling IPA processStats and prepareIsp:"
+ << " Bayer buffer id: " << bayerId
+ << " Stats buffer id: " << statsId;
+
+ ipa::RPi::PrepareParams params;
+ params.buffers.bayer = RPi::MaskBayerData | bayerId;
+ params.buffers.stats = RPi::MaskStats | statsId;
+ params.buffers.embedded = 0;
+ params.ipaContext = requestQueue_.front()->sequence();
+ params.delayContext = job.delayContext;
+ params.sensorControls = std::move(job.sensorControls);
+ params.requestControls = request->controls();
+
+ if (sensorMetadata_) {
+ unsigned int embeddedId =
+ cfe_[Cfe::Embedded].getBufferId(job.buffers[&cfe_[Cfe::Embedded]]);
+
+ ASSERT(embeddedId);
+ params.buffers.embedded = RPi::MaskEmbeddedData | embeddedId;
+ ss << " Embedded buffer id: " << embeddedId;
+ }
+
+ LOG(RPI, Debug) << ss.str();
+
+ cfeJobQueue_.pop();
+ ipa_->prepareIsp(params);
+}
+
+REGISTER_PIPELINE_HANDLER(PipelineHandlerPiSP, "rpi/pisp")
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/rpi/vc4/data/meson.build b/src/libcamera/pipeline/rpi/vc4/data/meson.build
index cca5e388..179feebc 100644
--- a/src/libcamera/pipeline/rpi/vc4/data/meson.build
+++ b/src/libcamera/pipeline/rpi/vc4/data/meson.build
@@ -5,4 +5,5 @@ conf_files = files([
])
install_data(conf_files,
- install_dir : pipeline_data_dir / 'rpi' / 'vc4')
+ install_dir : pipeline_data_dir / 'rpi' / 'vc4',
+ install_tag : 'runtime')
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
deleted file mode 100644
index 317b1fc1..00000000
--- a/src/libcamera/pipeline/rpi/vc4/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 <array>
-#include <fcntl.h>
-#include <linux/dma-buf.h>
-#include <linux/dma-heap.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-#include <libcamera/base/log.h>
-
-/*
- * /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<const char *, 2> 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
deleted file mode 100644
index 0a4a8d86..00000000
--- a/src/libcamera/pipeline/rpi/vc4/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 <stddef.h>
-
-#include <libcamera/base/unique_fd.h>
-
-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
index cdb049c5..9b37c2f0 100644
--- a/src/libcamera/pipeline/rpi/vc4/meson.build
+++ b/src/libcamera/pipeline/rpi/vc4/meson.build
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
- 'dma_heaps.cpp',
+libcamera_internal_sources += files([
'vc4.cpp',
])
diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index 616e0bc9..fe910bdf 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019-2023, Raspberry Pi Ltd
*
- * vc4.cpp - Pipeline handler for VC4-based Raspberry Pi devices
+ * Pipeline handler for VC4-based Raspberry Pi devices
*/
#include <linux/bcm2835-isp.h>
@@ -12,12 +12,11 @@
#include <libcamera/formats.h>
#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/dma_buf_allocator.h"
#include "../common/pipeline_base.h"
#include "../common/rpi_stream.h"
-#include "dma_heaps.h"
-
using namespace std::chrono_literals;
namespace libcamera {
@@ -87,7 +86,7 @@ public:
RPi::Device<Isp, 4> isp_;
/* DMAHEAP allocation helper. */
- RPi::DmaHeap dmaHeap_;
+ DmaBufAllocator dmaHeap_;
SharedFD lsTable_;
struct Config {
@@ -110,9 +109,10 @@ public:
Config config_;
private:
- void platformSetIspCrop() override
+ void platformSetIspCrop([[maybe_unused]] unsigned int index, const Rectangle &ispCrop) override
{
- isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &ispCrop_);
+ Rectangle crop = ispCrop;
+ isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);
}
int platformConfigure(const RPi::RPiCameraConfiguration *rpiConfig) override;
@@ -283,6 +283,9 @@ int PipelineHandlerVc4::prepareBuffers(Camera *camera)
numBuffers = 1;
}
+ LOG(RPI, Debug) << "Preparing " << numBuffers
+ << " buffers for stream " << stream->name();
+
ret = stream->prepareBuffers(numBuffers);
if (ret < 0)
return ret;
@@ -423,7 +426,7 @@ CameraConfiguration::Status Vc4CameraData::platformValidate(RPi::RPiCameraConfig
BayerFormat rawBayer = BayerFormat::fromPixelFormat(rawStream->pixelFormat);
/* Apply the sensor bitdepth. */
- rawBayer.bitDepth = BayerFormat::fromMbusCode(rpiConfig->sensorFormat_.mbus_code).bitDepth;
+ rawBayer.bitDepth = BayerFormat::fromMbusCode(rpiConfig->sensorFormat_.code).bitDepth;
/* Default to CSI2 packing if the user request is unsupported. */
if (rawBayer.packing != BayerFormat::Packing::CSI2 &&
@@ -431,6 +434,17 @@ CameraConfiguration::Status Vc4CameraData::platformValidate(RPi::RPiCameraConfig
rawBayer.packing = BayerFormat::Packing::CSI2;
PixelFormat rawFormat = rawBayer.toPixelFormat();
+
+ /*
+ * Try for an unpacked format if a packed one wasn't available.
+ * This catches 8 (and 16) bit formats which would otherwise
+ * fail.
+ */
+ if (!rawFormat.isValid() && rawBayer.packing != BayerFormat::Packing::None) {
+ rawBayer.packing = BayerFormat::Packing::None;
+ rawFormat = rawBayer.toPixelFormat();
+ }
+
if (rawStream->pixelFormat != rawFormat ||
rawStream->size != rpiConfig->sensorFormat_.size) {
rawStream->pixelFormat = rawFormat;
@@ -496,9 +510,9 @@ int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr<YamlObject> &
}
std::optional<std::string> target = (*root)["target"].get<std::string>();
- if (!target || *target != "bcm2835") {
+ if (target != "bcm2835") {
LOG(RPI, Error) << "Unexpected target reported: expected \"bcm2835\", got "
- << *target;
+ << (target ? target->c_str() : "(unknown)");
return -EINVAL;
}
@@ -628,7 +642,7 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
* \todo If Output 1 format is not YUV420, Output 1 ought to be disabled as
* colour denoise will not run.
*/
- if (outStreams.size() == 1) {
+ if (outStreams.size() <= 1) {
V4L2VideoDevice *dev = isp_[Isp::Output1].dev();
V4L2DeviceFormat output1Format;
@@ -688,13 +702,19 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
/* Figure out the smallest selection the ISP will allow. */
Rectangle testCrop(0, 0, 1, 1);
isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop);
- ispMinCropSize_ = testCrop.size();
/* Adjust aspect ratio by providing crops on the input image. */
Size size = unicamFormat.size.boundedToAspectRatio(maxSize);
- ispCrop_ = size.centeredTo(Rectangle(unicamFormat.size).center());
+ Rectangle ispCrop = size.centeredTo(Rectangle(unicamFormat.size).center());
- platformSetIspCrop();
+ platformSetIspCrop(0, ispCrop);
+ /*
+ * Set the scaler crop to the value we are using (scaled to native sensor
+ * coordinates).
+ */
+ cropParams_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(0),
+ std::forward_as_tuple(ispCrop, testCrop.size(), 0));
return 0;
}
@@ -789,7 +809,7 @@ void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
{
RPi::Stream *stream = nullptr;
- unsigned int index;
+ unsigned int index = 0;
if (!isRunning())
return;
@@ -942,6 +962,7 @@ void Vc4CameraData::tryRunPipeline()
params.requestControls = request->controls();
params.ipaContext = request->sequence();
params.delayContext = bayerFrame.delayContext;
+ params.buffers.embedded = 0;
if (embeddedBuffer) {
unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);
@@ -1004,6 +1025,6 @@ bool Vc4CameraData::findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&em
return true;
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerVc4)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerVc4, "rpi/vc4")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build
index 42b0896d..dda3de97 100644
--- a/src/libcamera/pipeline/simple/meson.build
+++ b/src/libcamera/pipeline/simple/meson.build
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'simple.cpp',
])
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 911051b2..efb07051 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2020, Laurent Pinchart
* Copyright (C) 2019, Martijn Braam
*
- * simple.cpp - Pipeline handler for simple pipelines
+ * Pipeline handler for simple pipelines
*/
#include <algorithm>
@@ -13,8 +13,9 @@
#include <memory>
#include <queue>
#include <set>
-#include <string>
+#include <stdint.h>
#include <string.h>
+#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -30,14 +31,16 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/camera_sensor_properties.h"
#include "libcamera/internal/converter.h"
+#include "libcamera/internal/delayed_controls.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/software_isp/software_isp.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
-
namespace libcamera {
LOG_DEFINE_CATEGORY(SimplePipeline)
@@ -163,7 +166,7 @@ LOG_DEFINE_CATEGORY(SimplePipeline)
* handler has no a priori knowledge of. The pipeline handler thus implements a
* heuristic to handle sharing of hardware resources in a generic fashion.
*
- * Two cameras are considered to be mutually exclusive if their share common
+ * Two cameras are considered to be mutually exclusive if they share common
* pads along the pipeline from the camera sensor to the video node. An entity
* can thus be used concurrently by multiple cameras, as long as pads are
* distinct.
@@ -178,6 +181,56 @@ LOG_DEFINE_CATEGORY(SimplePipeline)
class SimplePipelineHandler;
+struct SimpleFrameInfo {
+ SimpleFrameInfo(uint32_t f, Request *r, bool m)
+ : frame(f), request(r), metadataRequired(m), metadataProcessed(false)
+ {
+ }
+
+ uint32_t frame;
+ Request *request;
+ bool metadataRequired;
+ bool metadataProcessed;
+};
+
+class SimpleFrames
+{
+public:
+ void create(Request *request, bool metadataRequested);
+ void destroy(uint32_t frame);
+ void clear();
+
+ SimpleFrameInfo *find(uint32_t frame);
+
+private:
+ std::map<uint32_t, SimpleFrameInfo> frameInfo_;
+};
+
+void SimpleFrames::create(Request *request, bool metadataRequired)
+{
+ const uint32_t frame = request->sequence();
+ auto [it, inserted] = frameInfo_.try_emplace(frame, frame, request, metadataRequired);
+ ASSERT(inserted);
+}
+
+void SimpleFrames::destroy(uint32_t frame)
+{
+ frameInfo_.erase(frame);
+}
+
+void SimpleFrames::clear()
+{
+ frameInfo_.clear();
+}
+
+SimpleFrameInfo *SimpleFrames::find(uint32_t frame)
+{
+ auto info = frameInfo_.find(frame);
+ if (info == frameInfo_.end())
+ return nullptr;
+ return &info->second;
+}
+
struct SimplePipelineInfo {
const char *driver;
/*
@@ -185,17 +238,25 @@ struct SimplePipelineInfo {
* and the number of streams it supports.
*/
std::vector<std::pair<const char *, unsigned int>> converters;
+ /*
+ * Using Software ISP is to be enabled per driver.
+ *
+ * The Software ISP can't be used together with the converters.
+ */
+ bool swIspEnabled;
};
namespace {
static const SimplePipelineInfo supportedDevices[] = {
- { "dcmipp", {} },
- { "imx7-csi", { { "pxp", 1 } } },
- { "j721e-csi2rx", {} },
- { "mxc-isi", {} },
- { "qcom-camss", {} },
- { "sun6i-csi", {} },
+ { "dcmipp", {}, false },
+ { "imx7-csi", { { "pxp", 1 } }, false },
+ { "intel-ipu6", {}, true },
+ { "j721e-csi2rx", {}, true },
+ { "mtk-seninf", { { "mtk-mdp", 3 } }, false },
+ { "mxc-isi", {}, false },
+ { "qcom-camss", {}, true },
+ { "sun6i-csi", {}, false },
};
} /* namespace */
@@ -215,7 +276,8 @@ public:
int setupFormats(V4L2SubdeviceFormat *format,
V4L2Subdevice::Whence whence,
Transform transform = Transform::Identity);
- void bufferReady(FrameBuffer *buffer);
+ void imageBufferReady(FrameBuffer *buffer);
+ void clearIncompleteRequests();
unsigned int streamIndex(const Stream *stream) const
{
@@ -265,21 +327,36 @@ public:
std::list<Entity> entities_;
std::unique_ptr<CameraSensor> sensor_;
V4L2VideoDevice *video_;
+ V4L2Subdevice *frameStartEmitter_;
std::vector<Configuration> configs_;
std::map<PixelFormat, std::vector<const Configuration *>> formats_;
+ std::unique_ptr<DelayedControls> delayedCtrls_;
+
+ std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_;
+ struct RequestOutputs {
+ Request *request;
+ std::map<const Stream *, FrameBuffer *> outputs;
+ };
+ std::queue<RequestOutputs> conversionQueue_;
+ bool useConversion_;
+
std::unique_ptr<Converter> converter_;
- std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
- bool useConverter_;
- std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;
+ std::unique_ptr<SoftwareIsp> swIsp_;
+ SimpleFrames frameInfo_;
private:
void tryPipeline(unsigned int code, const Size &size);
static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink);
- void converterInputDone(FrameBuffer *buffer);
- void converterOutputDone(FrameBuffer *buffer);
+ void tryCompleteRequest(Request *request);
+ void conversionInputDone(FrameBuffer *buffer);
+ void conversionOutputDone(FrameBuffer *buffer);
+
+ void ispStatsReady(uint32_t frame, uint32_t bufferId);
+ void metadataReady(uint32_t frame, const ControlList &metadata);
+ void setSensorControls(const ControlList &sensorControls);
};
class SimpleCameraConfiguration : public CameraConfiguration
@@ -331,6 +408,7 @@ public:
V4L2VideoDevice *video(const MediaEntity *entity);
V4L2Subdevice *subdev(const MediaEntity *entity);
MediaDevice *converter() { return converter_; }
+ bool swIspEnabled() const { return swIspEnabled_; }
protected:
int queueRequestDevice(Camera *camera, Request *request) override;
@@ -349,16 +427,16 @@ private:
return static_cast<SimpleCameraData *>(camera->_d());
}
- std::vector<MediaEntity *> locateSensors();
+ std::vector<MediaEntity *> locateSensors(MediaDevice *media);
static int resetRoutingTable(V4L2Subdevice *subdev);
const MediaPad *acquirePipeline(SimpleCameraData *data);
void releasePipeline(SimpleCameraData *data);
- MediaDevice *media_;
std::map<const MediaEntity *, EntityData> entities_;
MediaDevice *converter_;
+ bool swIspEnabled_;
};
/* -----------------------------------------------------------------------------
@@ -370,8 +448,6 @@ SimpleCameraData::SimpleCameraData(SimplePipelineHandler *pipe,
MediaEntity *sensor)
: Camera::Private(pipe), streams_(numStreams)
{
- int ret;
-
/*
* Find the shortest path from the camera sensor to a video capture
* device using the breadth-first search algorithm. This heuristic will
@@ -462,12 +538,16 @@ SimpleCameraData::SimpleCameraData(SimplePipelineHandler *pipe,
}
/* Finally also remember the sensor. */
- sensor_ = std::make_unique<CameraSensor>(sensor);
- ret = sensor_->init();
- if (ret) {
- sensor_.reset();
+ sensor_ = CameraSensorFactoryBase::create(sensor);
+ if (!sensor_)
return;
- }
+
+ const CameraSensorProperties::SensorDelays &delays = sensor_->sensorDelays();
+ std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
+ { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+ { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
+ };
+ delayedCtrls_ = std::make_unique<DelayedControls>(sensor_->device(), params);
LOG(SimplePipeline, Debug)
<< "Found pipeline: "
@@ -503,8 +583,26 @@ int SimpleCameraData::init()
<< "Failed to create converter, disabling format conversion";
converter_.reset();
} else {
- converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone);
- converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone);
+ converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
+ converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
+ }
+ }
+
+ /*
+ * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
+ */
+ if (!converter_ && pipe->swIspEnabled()) {
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get(), &controlInfo_);
+ if (!swIsp_->isValid()) {
+ LOG(SimplePipeline, Warning)
+ << "Failed to create software ISP, disabling software debayering";
+ swIsp_.reset();
+ } else {
+ swIsp_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
+ swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
+ swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
+ swIsp_->metadataReady.connect(this, &SimpleCameraData::metadataReady);
+ swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls);
}
}
@@ -543,6 +641,20 @@ int SimpleCameraData::init()
properties_ = sensor_->properties();
+ /* Find the first subdev that can generate a frame start signal, if any. */
+ frameStartEmitter_ = nullptr;
+ for (const Entity &entity : entities_) {
+ V4L2Subdevice *sd = pipe->subdev(entity.entity);
+ if (!sd || !sd->supportsFrameStartEvent())
+ continue;
+
+ LOG(SimplePipeline, Debug)
+ << "Using frameStart signal from '"
+ << entity.entity->name() << "'";
+ frameStartEmitter_ = sd;
+ break;
+ }
+
return 0;
}
@@ -562,13 +674,13 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
* corresponding possible V4L2 pixel formats on the video node.
*/
V4L2SubdeviceFormat format{};
- format.mbus_code = code;
+ format.code = code;
format.size = size;
int ret = setupFormats(&format, V4L2Subdevice::TryFormat);
if (ret < 0) {
/* Pipeline configuration failed, skip this configuration. */
- format.mbus_code = code;
+ format.code = code;
format.size = size;
LOG(SimplePipeline, Debug)
<< "Sensor format " << format
@@ -576,7 +688,7 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
return;
}
- V4L2VideoDevice::Formats videoFormats = video_->formats(format.mbus_code);
+ V4L2VideoDevice::Formats videoFormats = video_->formats(format.code);
LOG(SimplePipeline, Debug)
<< "Adding configuration for " << format.size
@@ -598,12 +710,20 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
config.captureFormat = pixelFormat;
config.captureSize = format.size;
- if (!converter_) {
- config.outputFormats = { pixelFormat };
- config.outputSizes = config.captureSize;
- } else {
+ if (converter_) {
config.outputFormats = converter_->formats(pixelFormat);
config.outputSizes = converter_->sizes(format.size);
+ } else if (swIsp_) {
+ config.outputFormats = swIsp_->formats(pixelFormat);
+ config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
+ if (config.outputFormats.empty()) {
+ /* Do not use swIsp for unsupported pixelFormat's. */
+ config.outputFormats = { pixelFormat };
+ config.outputSizes = config.captureSize;
+ }
+ } else {
+ config.outputFormats = { pixelFormat };
+ config.outputSizes = config.captureSize;
}
configs_.push_back(config);
@@ -706,7 +826,7 @@ int SimpleCameraData::setupFormats(V4L2SubdeviceFormat *format,
if (ret < 0)
return ret;
- if (format->mbus_code != sourceFormat.mbus_code ||
+ if (format->code != sourceFormat.code ||
format->size != sourceFormat.size) {
LOG(SimplePipeline, Debug)
<< "Source '" << source->entity()->name()
@@ -720,17 +840,14 @@ int SimpleCameraData::setupFormats(V4L2SubdeviceFormat *format,
}
LOG(SimplePipeline, Debug)
- << "Link '" << source->entity()->name()
- << "':" << source->index()
- << " -> '" << sink->entity()->name()
- << "':" << sink->index()
- << " configured with format " << *format;
+ << "Link " << *link << ": configured with format "
+ << *format;
}
return 0;
}
-void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+void SimpleCameraData::imageBufferReady(FrameBuffer *buffer)
{
SimplePipelineHandler *pipe = SimpleCameraData::pipe();
@@ -740,35 +857,34 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
* point converting an erroneous buffer.
*/
if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
- if (!useConverter_) {
+ if (!useConversion_) {
/* No conversion, just complete the request. */
Request *request = buffer->request();
pipe->completeBuffer(request, buffer);
- pipe->completeRequest(request);
+ tryCompleteRequest(request);
return;
}
/*
- * The converter is in use. Requeue the internal buffer for
- * capture (unless the stream is being stopped), and complete
- * the request with all the user-facing buffers.
+ * The converter or Software ISP is in use. Requeue the internal
+ * buffer for capture (unless the stream is being stopped), and
+ * complete the request with all the user-facing buffers.
*/
if (buffer->metadata().status != FrameMetadata::FrameCancelled)
video_->queueBuffer(buffer);
- if (converterQueue_.empty())
+ if (conversionQueue_.empty())
return;
- Request *request = nullptr;
- for (auto &item : converterQueue_.front()) {
- FrameBuffer *outputBuffer = item.second;
- request = outputBuffer->request();
- pipe->completeBuffer(request, outputBuffer);
- }
- converterQueue_.pop();
+ const RequestOutputs &outputs = conversionQueue_.front();
+ for (auto &[stream, buf] : outputs.outputs)
+ pipe->completeBuffer(outputs.request, buf);
+ SimpleFrameInfo *info = frameInfo_.find(outputs.request->sequence());
+ if (info)
+ info->metadataRequired = false;
+ tryCompleteRequest(outputs.request);
+ conversionQueue_.pop();
- if (request)
- pipe->completeRequest(request);
return;
}
@@ -782,9 +898,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
*/
Request *request = buffer->request();
- if (useConverter_ && !converterQueue_.empty()) {
- const std::map<unsigned int, FrameBuffer *> &outputs =
- converterQueue_.front();
+ if (useConversion_ && !conversionQueue_.empty()) {
+ const std::map<const Stream *, FrameBuffer *> &outputs =
+ conversionQueue_.front().outputs;
if (!outputs.empty()) {
FrameBuffer *outputBuffer = outputs.begin()->second;
if (outputBuffer)
@@ -797,40 +913,110 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
buffer->metadata().timestamp);
/*
- * Queue the captured and the request buffer to the converter if format
- * conversion is needed. If there's no queued request, just requeue the
- * captured buffer for capture.
+ * Queue the captured and the request buffer to the converter or Software
+ * ISP if format conversion is needed. If there's no queued request, just
+ * requeue the captured buffer for capture.
*/
- if (useConverter_) {
- if (converterQueue_.empty()) {
+ if (useConversion_) {
+ if (conversionQueue_.empty()) {
video_->queueBuffer(buffer);
return;
}
- converter_->queueBuffers(buffer, converterQueue_.front());
- converterQueue_.pop();
+ if (converter_)
+ converter_->queueBuffers(buffer, conversionQueue_.front().outputs);
+ else
+ /*
+ * request->sequence() cannot be retrieved from `buffer' inside
+ * queueBuffers because unique_ptr's make buffer->request() invalid
+ * already here.
+ */
+ swIsp_->queueBuffers(request->sequence(), buffer,
+ conversionQueue_.front().outputs);
+
+ conversionQueue_.pop();
return;
}
/* Otherwise simply complete the request. */
pipe->completeBuffer(request, buffer);
- pipe->completeRequest(request);
+ tryCompleteRequest(request);
+}
+
+void SimpleCameraData::clearIncompleteRequests()
+{
+ while (!conversionQueue_.empty()) {
+ pipe()->cancelRequest(conversionQueue_.front().request);
+ conversionQueue_.pop();
+ }
}
-void SimpleCameraData::converterInputDone(FrameBuffer *buffer)
+void SimpleCameraData::tryCompleteRequest(Request *request)
+{
+ if (request->hasPendingBuffers())
+ return;
+
+ SimpleFrameInfo *info = frameInfo_.find(request->sequence());
+ if (!info) {
+ /* Something is really wrong, let's return. */
+ return;
+ }
+
+ if (info->metadataRequired && !info->metadataProcessed)
+ return;
+
+ frameInfo_.destroy(info->frame);
+ pipe()->completeRequest(request);
+}
+
+void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)
{
/* Queue the input buffer back for capture. */
video_->queueBuffer(buffer);
}
-void SimpleCameraData::converterOutputDone(FrameBuffer *buffer)
+void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
{
SimplePipelineHandler *pipe = SimpleCameraData::pipe();
/* Complete the buffer and the request. */
Request *request = buffer->request();
if (pipe->completeBuffer(request, buffer))
- pipe->completeRequest(request);
+ tryCompleteRequest(request);
+}
+
+void SimpleCameraData::ispStatsReady(uint32_t frame, uint32_t bufferId)
+{
+ swIsp_->processStats(frame, bufferId,
+ delayedCtrls_->get(frame));
+}
+
+void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata)
+{
+ SimpleFrameInfo *info = frameInfo_.find(frame);
+ if (!info)
+ return;
+
+ info->request->metadata().merge(metadata);
+ info->metadataProcessed = true;
+ tryCompleteRequest(info->request);
+}
+
+void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
+{
+ delayedCtrls_->push(sensorControls);
+ /*
+ * Directly apply controls now if there is no frameStart signal.
+ *
+ * \todo Applying controls directly not only increases the risk of
+ * applying them to the wrong frame (or across a frame boundary),
+ * but it also bypasses delayedCtrls_, creating AGC regulation issues.
+ * Both problems should be fixed.
+ */
+ if (!frameStartEmitter_) {
+ ControlList ctrls(sensorControls);
+ sensor_->setControls(&ctrls);
+ }
}
/* Retrieve all source pads connected to a sink pad through active routes. */
@@ -851,17 +1037,17 @@ std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
std::vector<const MediaPad *> pads;
- for (const struct v4l2_subdev_route &route : routing) {
- if (sink->index() != route.sink_pad ||
+ for (const V4L2Subdevice::Route &route : routing) {
+ if (sink->index() != route.sink.pad ||
!(route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
continue;
- const MediaPad *pad = entity->getPadByIndex(route.source_pad);
+ const MediaPad *pad = entity->getPadByIndex(route.source.pad);
if (!pad) {
LOG(SimplePipeline, Warning)
<< "Entity " << entity->name()
<< " has invalid route source pad "
- << route.source_pad;
+ << route.source.pad;
}
pads.push_back(pad);
@@ -881,6 +1067,33 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
{
}
+namespace {
+
+static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
+{
+ ASSERT(supportedSizes.min <= supportedSizes.max);
+
+ if (supportedSizes.min == supportedSizes.max)
+ return supportedSizes.max;
+
+ unsigned int hStep = supportedSizes.hStep;
+ unsigned int vStep = supportedSizes.vStep;
+
+ if (hStep == 0)
+ hStep = supportedSizes.max.width - supportedSizes.min.width;
+ if (vStep == 0)
+ vStep = supportedSizes.max.height - supportedSizes.min.height;
+
+ Size adjusted = requestedSize.boundedTo(supportedSizes.max)
+ .expandedTo(supportedSizes.min);
+
+ return adjusted.shrunkBy(supportedSizes.min)
+ .alignedDownTo(hStep, vStep)
+ .grownBy(supportedSizes.min);
+}
+
+} /* namespace */
+
CameraConfiguration::Status SimpleCameraConfiguration::validate()
{
const CameraSensor *sensor = data_->sensor_.get();
@@ -997,10 +1210,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
}
if (!pipeConfig_->outputSizes.contains(cfg.size)) {
+ Size adjustedSize = pipeConfig_->captureSize;
+ /*
+ * The converter (when present) may not be able to output
+ * a size identical to its input size. The capture size is thus
+ * not guaranteed to be a valid output size. In such cases, use
+ * the smaller valid output size closest to the requested.
+ */
+ if (!pipeConfig_->outputSizes.contains(adjustedSize))
+ adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes);
LOG(SimplePipeline, Debug)
<< "Adjusting size from " << cfg.size
- << " to " << pipeConfig_->captureSize;
- cfg.size = pipeConfig_->captureSize;
+ << " to " << adjustedSize;
+ cfg.size = adjustedSize;
status = Adjusted;
}
@@ -1012,8 +1234,11 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
/* Set the stride, frameSize and bufferCount. */
if (needConversion_) {
std::tie(cfg.stride, cfg.frameSize) =
- data_->converter_->strideAndFrameSize(cfg.pixelFormat,
- cfg.size);
+ data_->converter_
+ ? data_->converter_->strideAndFrameSize(cfg.pixelFormat,
+ cfg.size)
+ : data_->swIsp_->strideAndFrameSize(cfg.pixelFormat,
+ cfg.size);
if (cfg.stride == 0)
return Invalid;
} else {
@@ -1029,7 +1254,7 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
cfg.frameSize = format.planes[0].size;
}
- cfg.bufferCount = 3;
+ cfg.bufferCount = 4;
}
return status;
@@ -1120,7 +1345,7 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
const SimpleCameraData::Configuration *pipeConfig = config->pipeConfig();
V4L2SubdeviceFormat format{};
- format.mbus_code = pipeConfig->code;
+ format.code = pipeConfig->code;
format.size = pipeConfig->sensorSize;
ret = data->setupFormats(&format, V4L2Subdevice::ActiveFormat,
@@ -1156,14 +1381,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
/* Configure the converter if needed. */
std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
- data->useConverter_ = config->needConversion();
+ data->useConversion_ = config->needConversion();
for (unsigned int i = 0; i < config->size(); ++i) {
StreamConfiguration &cfg = config->at(i);
cfg.setStream(&data->streams_[i]);
- if (data->useConverter_)
+ if (data->useConversion_)
outputCfgs.push_back(cfg);
}
@@ -1176,7 +1401,13 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
inputCfg.stride = captureFormat.planes[0].bpl;
inputCfg.bufferCount = kNumInternalBuffers;
- return data->converter_->configure(inputCfg, outputCfgs);
+ if (data->converter_) {
+ return data->converter_->configure(inputCfg, outputCfgs);
+ } else {
+ ipa::soft::IPAConfigInfo configInfo;
+ configInfo.sensorControls = data->sensor_->controls();
+ return data->swIsp_->configure(inputCfg, outputCfgs, configInfo);
+ }
}
int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
@@ -1189,9 +1420,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
* Export buffers on the converter or capture video node, depending on
* whether the converter is used or not.
*/
- if (data->useConverter_)
- return data->converter_->exportBuffers(data->streamIndex(stream),
- count, buffers);
+ if (data->useConversion_)
+ return data->converter_
+ ? data->converter_->exportBuffers(stream, count, buffers)
+ : data->swIsp_->exportBuffers(stream, count, buffers);
else
return data->video_->exportBuffers(count, buffers);
}
@@ -1200,6 +1432,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
{
SimpleCameraData *data = cameraData(camera);
V4L2VideoDevice *video = data->video_;
+ V4L2Subdevice *frameStartEmitter = data->frameStartEmitter_;
int ret;
const MediaPad *pad = acquirePipeline(data);
@@ -1210,13 +1443,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
return -EBUSY;
}
- if (data->useConverter_) {
+ if (data->useConversion_) {
/*
* When using the converter allocate a fixed number of internal
* buffers.
*/
ret = video->allocateBuffers(kNumInternalBuffers,
- &data->converterBuffers_);
+ &data->conversionBuffers_);
} else {
/* Otherwise, prepare for using buffers from the only stream. */
Stream *stream = &data->streams_[0];
@@ -1227,7 +1460,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
return ret;
}
- video->bufferReady.connect(data, &SimpleCameraData::bufferReady);
+ video->bufferReady.connect(data, &SimpleCameraData::imageBufferReady);
+
+ data->delayedCtrls_->reset();
+ if (frameStartEmitter) {
+ ret = frameStartEmitter->setFrameStartEnabled(true);
+ if (ret) {
+ stop(camera);
+ return ret;
+ }
+ frameStartEmitter->frameStart.connect(data->delayedCtrls_.get(),
+ &DelayedControls::applyControls);
+ }
ret = video->streamOn();
if (ret < 0) {
@@ -1235,15 +1479,21 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
return ret;
}
- if (data->useConverter_) {
- ret = data->converter_->start();
+ if (data->useConversion_) {
+ if (data->converter_)
+ ret = data->converter_->start();
+ else if (data->swIsp_)
+ ret = data->swIsp_->start();
+ else
+ ret = 0;
+
if (ret < 0) {
stop(camera);
return ret;
}
/* Queue all internal buffers for capture. */
- for (std::unique_ptr<FrameBuffer> &buffer : data->converterBuffers_)
+ for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
video->queueBuffer(buffer.get());
}
@@ -1254,16 +1504,29 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
{
SimpleCameraData *data = cameraData(camera);
V4L2VideoDevice *video = data->video_;
+ V4L2Subdevice *frameStartEmitter = data->frameStartEmitter_;
+
+ if (frameStartEmitter) {
+ frameStartEmitter->setFrameStartEnabled(false);
+ frameStartEmitter->frameStart.disconnect(data->delayedCtrls_.get(),
+ &DelayedControls::applyControls);
+ }
- if (data->useConverter_)
- data->converter_->stop();
+ if (data->useConversion_) {
+ if (data->converter_)
+ data->converter_->stop();
+ else if (data->swIsp_)
+ data->swIsp_->stop();
+ }
video->streamOff();
video->releaseBuffers();
- video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
+ video->bufferReady.disconnect(data, &SimpleCameraData::imageBufferReady);
- data->converterBuffers_.clear();
+ data->frameInfo_.clear();
+ data->clearIncompleteRequests();
+ data->conversionBuffers_.clear();
releasePipeline(data);
}
@@ -1273,7 +1536,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
SimpleCameraData *data = cameraData(camera);
int ret;
- std::map<unsigned int, FrameBuffer *> buffers;
+ std::map<const Stream *, FrameBuffer *> buffers;
for (auto &[stream, buffer] : request->buffers()) {
/*
@@ -1281,8 +1544,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
* queue, it will be handed to the converter in the capture
* completion handler.
*/
- if (data->useConverter_) {
- buffers.emplace(data->streamIndex(stream), buffer);
+ if (data->useConversion_) {
+ buffers.emplace(stream, buffer);
} else {
ret = data->video_->queueBuffer(buffer);
if (ret < 0)
@@ -1290,8 +1553,12 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
}
}
- if (data->useConverter_)
- data->converterQueue_.push(std::move(buffers));
+ data->frameInfo_.create(request, !!data->swIsp_);
+ if (data->useConversion_) {
+ data->conversionQueue_.push({ request, std::move(buffers) });
+ if (data->swIsp_)
+ data->swIsp_->queueRequest(request->sequence(), request->controls());
+ }
return 0;
}
@@ -1300,7 +1567,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
* Match and Setup
*/
-std::vector<MediaEntity *> SimplePipelineHandler::locateSensors()
+std::vector<MediaEntity *>
+SimplePipelineHandler::locateSensors(MediaDevice *media)
{
std::vector<MediaEntity *> entities;
@@ -1308,7 +1576,7 @@ std::vector<MediaEntity *> SimplePipelineHandler::locateSensors()
* Gather all the camera sensor entities based on the function they
* expose.
*/
- for (MediaEntity *entity : media_->entities()) {
+ for (MediaEntity *entity : media->entities()) {
if (entity->function() == MEDIA_ENT_F_CAM_SENSOR)
entities.push_back(entity);
}
@@ -1387,7 +1655,7 @@ int SimplePipelineHandler::resetRoutingTable(V4L2Subdevice *subdev)
LOG(SimplePipeline, Debug)
<< "Routing table of " << subdev->deviceNode()
- << " reset to " << routing.toString();
+ << " reset to " << routing;
return 0;
}
@@ -1396,17 +1664,18 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
{
const SimplePipelineInfo *info = nullptr;
unsigned int numStreams = 1;
+ MediaDevice *media;
for (const SimplePipelineInfo &inf : supportedDevices) {
DeviceMatch dm(inf.driver);
- media_ = acquireMediaDevice(enumerator, dm);
- if (media_) {
+ media = acquireMediaDevice(enumerator, dm);
+ if (media) {
info = &inf;
break;
}
}
- if (!media_)
+ if (!media)
return false;
for (const auto &[name, streams] : info->converters) {
@@ -1418,13 +1687,17 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
}
}
+ swIspEnabled_ = info->swIspEnabled;
+
/* Locate the sensors. */
- std::vector<MediaEntity *> sensors = locateSensors();
+ std::vector<MediaEntity *> sensors = locateSensors(media);
if (sensors.empty()) {
- LOG(SimplePipeline, Error) << "No sensor found";
+ LOG(SimplePipeline, Info) << "No sensor found for " << media->deviceNode();
return false;
}
+ LOG(SimplePipeline, Debug) << "Sensor found for " << media->deviceNode();
+
/*
* Create one camera data instance for each sensor and gather all
* entities in all pipelines.
@@ -1488,8 +1761,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
if (subdev->caps().hasStreams()) {
/*
* Reset the routing table to its default state
- * to make sure entities are enumerate according
- * to the defaul routing configuration.
+ * to make sure entities are enumerated according
+ * to the default routing configuration.
*/
ret = resetRoutingTable(subdev.get());
if (ret) {
@@ -1604,6 +1877,6 @@ void SimplePipelineHandler::releasePipeline(SimpleCameraData *data)
}
}
-REGISTER_PIPELINE_HANDLER(SimplePipelineHandler)
+REGISTER_PIPELINE_HANDLER(SimplePipelineHandler, "simple")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/uvcvideo/meson.build b/src/libcamera/pipeline/uvcvideo/meson.build
index a3c2efd4..a3a91074 100644
--- a/src/libcamera/pipeline/uvcvideo/meson.build
+++ b/src/libcamera/pipeline/uvcvideo/meson.build
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'uvcvideo.cpp',
])
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index ed9c7f88..58aa0eb4 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -2,17 +2,22 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * uvcvideo.cpp - Pipeline handler for uvcvideo devices
+ * Pipeline handler for uvcvideo devices
*/
#include <algorithm>
+#include <bitset>
+#include <cmath>
#include <fstream>
-#include <iomanip>
-#include <math.h>
+#include <map>
#include <memory>
-#include <tuple>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
#include <libcamera/base/log.h>
+#include <libcamera/base/mutex.h>
#include <libcamera/base/utils.h>
#include <libcamera/camera.h>
@@ -44,14 +49,18 @@ public:
int init(MediaDevice *media);
void addControl(uint32_t cid, const ControlInfo &v4l2info,
ControlInfoMap::Map *ctrls);
- void bufferReady(FrameBuffer *buffer);
+ void imageBufferReady(FrameBuffer *buffer);
const std::string &id() const { return id_; }
+ Mutex openLock_;
std::unique_ptr<V4L2VideoDevice> video_;
Stream stream_;
std::map<PixelFormat, std::vector<SizeRange>> formats_;
+ std::optional<v4l2_exposure_auto_type> autoExposureMode_;
+ std::optional<v4l2_exposure_auto_type> manualExposureMode_;
+
private:
bool generateId();
@@ -89,16 +98,39 @@ public:
bool match(DeviceEnumerator *enumerator) override;
private:
- int processControl(ControlList *controls, unsigned int id,
- const ControlValue &value);
+ int processControl(const UVCCameraData *data, ControlList *controls,
+ unsigned int id, const ControlValue &value);
int processControls(UVCCameraData *data, Request *request);
+ bool acquireDevice(Camera *camera) override;
+ void releaseDevice(Camera *camera) override;
+
UVCCameraData *cameraData(Camera *camera)
{
return static_cast<UVCCameraData *>(camera->_d());
}
};
+namespace {
+
+std::optional<controls::ExposureTimeModeEnum> v4l2ToExposureMode(int32_t x)
+{
+ using namespace controls;
+
+ switch (x) {
+ case V4L2_EXPOSURE_AUTO:
+ case V4L2_EXPOSURE_APERTURE_PRIORITY:
+ return ExposureTimeModeAuto;
+ case V4L2_EXPOSURE_MANUAL:
+ case V4L2_EXPOSURE_SHUTTER_PRIORITY:
+ return ExposureTimeModeManual;
+ default:
+ return {};
+ }
+}
+
+} /* namespace */
+
UVCCameraConfiguration::UVCCameraConfiguration(UVCCameraData *data)
: CameraConfiguration(), data_(data)
{
@@ -158,9 +190,29 @@ CameraConfiguration::Status UVCCameraConfiguration::validate()
format.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);
format.size = cfg.size;
- int ret = data_->video_->tryFormat(&format);
- if (ret)
- return Invalid;
+ /*
+ * For power-consumption reasons video_ is closed when the camera is not
+ * acquired. Open it here if necessary.
+ */
+ {
+ bool opened = false;
+
+ MutexLocker locker(data_->openLock_);
+
+ if (!data_->video_->isOpen()) {
+ int ret = data_->video_->open();
+ if (ret)
+ return Invalid;
+
+ opened = true;
+ }
+
+ int ret = data_->video_->tryFormat(&format);
+ if (opened)
+ data_->video_->close();
+ if (ret)
+ return Invalid;
+ }
cfg.stride = format.planes[0].bpl;
cfg.frameSize = format.planes[0].size;
@@ -260,8 +312,8 @@ void PipelineHandlerUVC::stopDevice(Camera *camera)
data->video_->releaseBuffers();
}
-int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
- const ControlValue &value)
+int PipelineHandlerUVC::processControl(const UVCCameraData *data, ControlList *controls,
+ unsigned int id, const ControlValue &value)
{
uint32_t cid;
@@ -271,12 +323,14 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
cid = V4L2_CID_CONTRAST;
else if (id == controls::Saturation)
cid = V4L2_CID_SATURATION;
- else if (id == controls::AeEnable)
+ else if (id == controls::ExposureTimeMode)
cid = V4L2_CID_EXPOSURE_AUTO;
else if (id == controls::ExposureTime)
cid = V4L2_CID_EXPOSURE_ABSOLUTE;
else if (id == controls::AnalogueGain)
cid = V4L2_CID_GAIN;
+ else if (id == controls::Gamma)
+ cid = V4L2_CID_GAMMA;
else
return -EINVAL;
@@ -293,22 +347,33 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
case V4L2_CID_BRIGHTNESS: {
float scale = std::max(max - def, def - min);
float fvalue = value.get<float>() * scale + def;
- controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));
+ controls->set(cid, static_cast<int32_t>(std::lround(fvalue)));
break;
}
case V4L2_CID_SATURATION: {
float scale = def - min;
float fvalue = value.get<float>() * scale + min;
- controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));
+ controls->set(cid, static_cast<int32_t>(std::lround(fvalue)));
break;
}
case V4L2_CID_EXPOSURE_AUTO: {
- int32_t ivalue = value.get<bool>()
- ? V4L2_EXPOSURE_APERTURE_PRIORITY
- : V4L2_EXPOSURE_MANUAL;
- controls->set(V4L2_CID_EXPOSURE_AUTO, ivalue);
+ std::optional<v4l2_exposure_auto_type> mode;
+
+ switch (value.get<int32_t>()) {
+ case controls::ExposureTimeModeAuto:
+ mode = data->autoExposureMode_;
+ break;
+ case controls::ExposureTimeModeManual:
+ mode = data->manualExposureMode_;
+ break;
+ }
+
+ if (!mode)
+ return -EINVAL;
+
+ controls->set(V4L2_CID_EXPOSURE_AUTO, static_cast<int32_t>(*mode));
break;
}
@@ -327,10 +392,14 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
}
float fvalue = (value.get<float>() - p) / m;
- controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));
+ controls->set(cid, static_cast<int32_t>(std::lround(fvalue)));
break;
}
+ case V4L2_CID_GAMMA:
+ controls->set(cid, static_cast<int32_t>(std::lround(value.get<float>() * 100)));
+ break;
+
default: {
int32_t ivalue = value.get<int32_t>();
controls->set(cid, ivalue);
@@ -346,7 +415,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)
ControlList controls(data->video_->controls());
for (const auto &[id, value] : request->controls())
- processControl(&controls, id, value);
+ processControl(data, &controls, id, value);
for (const auto &ctrl : controls)
LOG(UVC, Debug)
@@ -411,6 +480,23 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
return true;
}
+bool PipelineHandlerUVC::acquireDevice(Camera *camera)
+{
+ UVCCameraData *data = cameraData(camera);
+
+ MutexLocker locker(data->openLock_);
+
+ return data->video_->open() == 0;
+}
+
+void PipelineHandlerUVC::releaseDevice(Camera *camera)
+{
+ UVCCameraData *data = cameraData(camera);
+
+ MutexLocker locker(data->openLock_);
+ data->video_->close();
+}
+
int UVCCameraData::init(MediaDevice *media)
{
int ret;
@@ -432,7 +518,7 @@ int UVCCameraData::init(MediaDevice *media)
if (ret)
return ret;
- video_->bufferReady.connect(this, &UVCCameraData::bufferReady);
+ video_->bufferReady.connect(this, &UVCCameraData::imageBufferReady);
/* Generate the camera ID. */
if (!generateId()) {
@@ -510,8 +596,19 @@ int UVCCameraData::init(MediaDevice *media)
addControl(cid, info, &ctrls);
}
+ if (autoExposureMode_ && manualExposureMode_) {
+ /* \todo Move this to the Camera class */
+ ctrls[&controls::AeEnable] = ControlInfo(false, true, true);
+ }
+
controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
+ /*
+ * Close to allow camera to go into runtime-suspend, video_ will be
+ * re-opened from acquireDevice() and validate().
+ */
+ video_->close();
+
return 0;
}
@@ -597,7 +694,7 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
id = &controls::Saturation;
break;
case V4L2_CID_EXPOSURE_AUTO:
- id = &controls::AeEnable;
+ id = &controls::ExposureTimeMode;
break;
case V4L2_CID_EXPOSURE_ABSOLUTE:
id = &controls::ExposureTime;
@@ -605,11 +702,15 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
case V4L2_CID_GAIN:
id = &controls::AnalogueGain;
break;
+ case V4L2_CID_GAMMA:
+ id = &controls::Gamma;
+ break;
default:
return;
}
/* Map the control info. */
+ const std::vector<ControlValue> &v4l2Values = v4l2Info.values();
int32_t min = v4l2Info.min().get<int32_t>();
int32_t max = v4l2Info.max().get<int32_t>();
int32_t def = v4l2Info.def().get<int32_t>();
@@ -647,10 +748,79 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
};
break;
- case V4L2_CID_EXPOSURE_AUTO:
- info = ControlInfo{ false, true, true };
- break;
+ case V4L2_CID_EXPOSURE_AUTO: {
+ /*
+ * From the V4L2_CID_EXPOSURE_AUTO documentation:
+ *
+ * ------------------------------------------------------------
+ * V4L2_EXPOSURE_AUTO:
+ * Automatic exposure time, automatic iris aperture.
+ *
+ * V4L2_EXPOSURE_MANUAL:
+ * Manual exposure time, manual iris.
+ *
+ * V4L2_EXPOSURE_SHUTTER_PRIORITY:
+ * Manual exposure time, auto iris.
+ *
+ * V4L2_EXPOSURE_APERTURE_PRIORITY:
+ * Auto exposure time, manual iris.
+ *-------------------------------------------------------------
+ *
+ * ExposureTimeModeAuto = { V4L2_EXPOSURE_AUTO,
+ * V4L2_EXPOSURE_APERTURE_PRIORITY }
+ *
+ *
+ * ExposureTimeModeManual = { V4L2_EXPOSURE_MANUAL,
+ * V4L2_EXPOSURE_SHUTTER_PRIORITY }
+ */
+
+ std::bitset<
+ std::max(V4L2_EXPOSURE_AUTO,
+ std::max(V4L2_EXPOSURE_APERTURE_PRIORITY,
+ std::max(V4L2_EXPOSURE_MANUAL,
+ V4L2_EXPOSURE_SHUTTER_PRIORITY))) + 1
+ > exposureModes;
+ std::optional<controls::ExposureTimeModeEnum> lcDef;
+
+ for (const ControlValue &value : v4l2Values) {
+ const auto x = value.get<int32_t>();
+ if (0 <= x && static_cast<std::size_t>(x) < exposureModes.size()) {
+ exposureModes[x] = true;
+
+ if (x == def)
+ lcDef = v4l2ToExposureMode(x);
+ }
+ }
+
+ if (exposureModes[V4L2_EXPOSURE_AUTO])
+ autoExposureMode_ = V4L2_EXPOSURE_AUTO;
+ else if (exposureModes[V4L2_EXPOSURE_APERTURE_PRIORITY])
+ autoExposureMode_ = V4L2_EXPOSURE_APERTURE_PRIORITY;
+
+ if (exposureModes[V4L2_EXPOSURE_SHUTTER_PRIORITY])
+ manualExposureMode_ = V4L2_EXPOSURE_SHUTTER_PRIORITY;
+ else if (exposureModes[V4L2_EXPOSURE_MANUAL])
+ manualExposureMode_ = V4L2_EXPOSURE_MANUAL;
+
+ std::array<ControlValue, 2> values;
+ std::size_t count = 0;
+
+ if (autoExposureMode_)
+ values[count++] = controls::ExposureTimeModeAuto;
+
+ if (manualExposureMode_)
+ values[count++] = controls::ExposureTimeModeManual;
+
+ if (count == 0)
+ return;
+
+ info = ControlInfo{
+ Span<const ControlValue>{ values.data(), count },
+ !lcDef ? values.front() : *lcDef,
+ };
+ break;
+ }
case V4L2_CID_EXPOSURE_ABSOLUTE:
/*
* ExposureTime is in units of 1 µs, and UVC expects
@@ -689,6 +859,15 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
break;
}
+ case V4L2_CID_GAMMA:
+ /* UVC gamma is in units of 1/100 gamma. */
+ info = ControlInfo{
+ { min / 100.0f },
+ { max / 100.0f },
+ { def / 100.0f }
+ };
+ break;
+
default:
info = v4l2Info;
break;
@@ -697,7 +876,7 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
ctrls->emplace(id, info);
}
-void UVCCameraData::bufferReady(FrameBuffer *buffer)
+void UVCCameraData::imageBufferReady(FrameBuffer *buffer)
{
Request *request = buffer->request();
@@ -709,6 +888,6 @@ void UVCCameraData::bufferReady(FrameBuffer *buffer)
pipe()->completeRequest(request);
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerUVC)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerUVC, "uvcvideo")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/vimc/meson.build b/src/libcamera/pipeline/vimc/meson.build
index 290eefb5..868e2546 100644
--- a/src/libcamera/pipeline/vimc/meson.build
+++ b/src/libcamera/pipeline/vimc/meson.build
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-libcamera_sources += files([
+libcamera_internal_sources += files([
'vimc.cpp',
])
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index 5536941a..07273bd2 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -2,18 +2,19 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * vimc.cpp - Pipeline handler for the vimc device
+ * Pipeline handler for the vimc device
*/
#include <algorithm>
+#include <cmath>
#include <iomanip>
#include <map>
-#include <math.h>
#include <tuple>
#include <linux/media-bus-format.h>
#include <linux/version.h>
+#include <libcamera/base/flags.h>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
@@ -21,6 +22,8 @@
#include <libcamera/control_ids.h>
#include <libcamera/controls.h>
#include <libcamera/formats.h>
+#include <libcamera/framebuffer.h>
+#include <libcamera/geometry.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
@@ -53,8 +56,8 @@ public:
int init();
int allocateMockIPABuffers();
- void bufferReady(FrameBuffer *buffer);
- void paramsBufferReady(unsigned int id, const Flags<ipa::vimc::TestFlag> flags);
+ void imageBufferReady(FrameBuffer *buffer);
+ void paramsComputed(unsigned int id, const Flags<ipa::vimc::TestFlag> flags);
MediaDevice *media_;
std::unique_ptr<CameraSensor> sensor_;
@@ -114,6 +117,9 @@ static const std::map<PixelFormat, uint32_t> pixelformats{
{ formats::BGR888, MEDIA_BUS_FMT_RGB888_1X24 },
};
+static constexpr Size kMinSize{ 16, 16 };
+static constexpr Size kMaxSize{ 4096, 2160 };
+
} /* namespace */
VimcCameraConfiguration::VimcCameraConfiguration(VimcCameraData *data)
@@ -153,14 +159,20 @@ CameraConfiguration::Status VimcCameraConfiguration::validate()
const Size size = cfg.size;
/*
- * The scaler hardcodes a x3 scale-up ratio, and the sensor output size
- * is aligned to two pixels in both directions. The output width and
- * height thus have to be multiples of 6.
+ * The sensor output size is aligned to two pixels in both directions.
+ * Additionally, prior to v5.16, the scaler hardcodes a x3 scale-up
+ * ratio, requiring the output width and height to be multiples of 6.
*/
- cfg.size.width = std::max(48U, std::min(4096U, cfg.size.width));
- cfg.size.height = std::max(48U, std::min(2160U, cfg.size.height));
- cfg.size.width -= cfg.size.width % 6;
- cfg.size.height -= cfg.size.height % 6;
+ Size minSize{ kMinSize };
+ unsigned int alignment = 2;
+
+ if (data_->media_->version() < KERNEL_VERSION(5, 16, 0)) {
+ minSize *= 3;
+ alignment *= 3;
+ }
+
+ cfg.size.expandTo(minSize).boundTo(kMaxSize)
+ .alignDownTo(alignment, alignment);
if (cfg.size != size) {
LOG(VIMC, Debug)
@@ -216,10 +228,12 @@ PipelineHandlerVimc::generateConfiguration(Camera *camera,
}
}
- /* The scaler hardcodes a x3 scale-up ratio. */
- std::vector<SizeRange> sizes{
- SizeRange{ { 48, 48 }, { 4096, 2160 } }
- };
+ /* Prior to v5.16, the scaler hardcodes a x3 scale-up ratio. */
+ Size minSize{ kMinSize };
+ if (data->media_->version() < KERNEL_VERSION(5, 16, 0))
+ minSize *= 3;
+
+ std::vector<SizeRange> sizes{ { minSize, kMaxSize } };
formats[pixelformat.first] = sizes;
}
@@ -242,10 +256,18 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
StreamConfiguration &cfg = config->at(0);
int ret;
- /* The scaler hardcodes a x3 scale-up ratio. */
+ /*
+ * Prior to v5.16, the scaler hardcodes a x3 scale-up ratio. For newer
+ * kernels, use a sensor resolution of 1920x1080 and let the scaler
+ * produce the requested stream size.
+ */
+ Size sensorSize{ 1920, 1080 };
+ if (data->media_->version() < KERNEL_VERSION(5, 16, 0))
+ sensorSize = { cfg.size.width / 3, cfg.size.height / 3 };
+
V4L2SubdeviceFormat subformat = {};
- subformat.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8;
- subformat.size = { cfg.size.width / 3, cfg.size.height / 3 };
+ subformat.code = MEDIA_BUS_FMT_SGRBG8_1X8;
+ subformat.size = sensorSize;
ret = data->sensor_->setFormat(&subformat);
if (ret)
@@ -255,7 +277,7 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
if (ret)
return ret;
- subformat.mbus_code = pixelformats.find(cfg.pixelFormat)->second;
+ subformat.code = pixelformats.find(cfg.pixelFormat)->second;
ret = data->debayer_->setFormat(1, &subformat);
if (ret)
return ret;
@@ -293,7 +315,7 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
* vimc driver will fail pipeline validation.
*/
format.fourcc = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8);
- format.size = { cfg.size.width / 3, cfg.size.height / 3 };
+ format.size = sensorSize;
ret = data->raw_->setFormat(&format);
if (ret)
@@ -398,7 +420,7 @@ int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request)
continue;
}
- int32_t value = lroundf(it.second.get<float>() * 128 + offset);
+ int32_t value = std::lround(it.second.get<float>() * 128 + offset);
controls.set(cid, std::clamp(value, 0, 255));
}
@@ -470,7 +492,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
return false;
}
- data->ipa_->paramsBufferReady.connect(data.get(), &VimcCameraData::paramsBufferReady);
+ data->ipa_->paramsComputed.connect(data.get(), &VimcCameraData::paramsComputed);
std::string conf = data->ipa_->configurationFile("vimc.conf");
Flags<ipa::vimc::TestFlag> inFlags = ipa::vimc::TestFlag::Flag2;
@@ -510,10 +532,9 @@ int VimcCameraData::init()
return ret;
/* Create and open the camera sensor, debayer, scaler and video device. */
- sensor_ = std::make_unique<CameraSensor>(media_->getEntityByName("Sensor B"));
- ret = sensor_->init();
- if (ret)
- return ret;
+ sensor_ = CameraSensorFactoryBase::create(media_->getEntityByName("Sensor B"));
+ if (!sensor_)
+ return -ENODEV;
debayer_ = V4L2Subdevice::fromEntityName(media_, "Debayer B");
if (debayer_->open())
@@ -527,7 +548,7 @@ int VimcCameraData::init()
if (video_->open())
return -ENODEV;
- video_->bufferReady.connect(this, &VimcCameraData::bufferReady);
+ video_->bufferReady.connect(this, &VimcCameraData::imageBufferReady);
raw_ = V4L2VideoDevice::fromEntityName(media_, "Raw Capture 1");
if (raw_->open())
@@ -575,7 +596,7 @@ int VimcCameraData::init()
return 0;
}
-void VimcCameraData::bufferReady(FrameBuffer *buffer)
+void VimcCameraData::imageBufferReady(FrameBuffer *buffer)
{
PipelineHandlerVimc *pipe =
static_cast<PipelineHandlerVimc *>(this->pipe());
@@ -600,7 +621,7 @@ void VimcCameraData::bufferReady(FrameBuffer *buffer)
pipe->completeBuffer(request, buffer);
pipe->completeRequest(request);
- ipa_->fillParamsBuffer(request->sequence(), mockIPABufs_[0]->cookie());
+ ipa_->computeParams(request->sequence(), mockIPABufs_[0]->cookie());
}
int VimcCameraData::allocateMockIPABuffers()
@@ -618,11 +639,11 @@ int VimcCameraData::allocateMockIPABuffers()
return video_->exportBuffers(kBufCount, &mockIPABufs_);
}
-void VimcCameraData::paramsBufferReady([[maybe_unused]] unsigned int id,
- [[maybe_unused]] const Flags<ipa::vimc::TestFlag> flags)
+void VimcCameraData::paramsComputed([[maybe_unused]] unsigned int id,
+ [[maybe_unused]] const Flags<ipa::vimc::TestFlag> flags)
{
}
-REGISTER_PIPELINE_HANDLER(PipelineHandlerVimc)
+REGISTER_PIPELINE_HANDLER(PipelineHandlerVimc, "vimc")
} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/README.md b/src/libcamera/pipeline/virtual/README.md
new file mode 100644
index 00000000..a9f39c15
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/README.md
@@ -0,0 +1,65 @@
+# Virtual Pipeline Handler
+
+Virtual pipeline handler emulates fake external camera(s) for testing.
+
+## Parse config file and register cameras
+
+- A sample config file is located at `src/libcamera/pipeline/virtual/data/virtual.yaml`.
+- If libcamera is installed, the config file should be installed at
+ `share/libcamera/pipeline/virtual/virtual.yaml`.
+
+### Config File Format
+The config file contains the information about cameras' properties to register.
+The config file should be a yaml file with dictionary of the cameraIds
+associated with their properties as top level. The default value will be applied
+when any property is empty.
+
+Each camera block is a dictionary, containing the following keys:
+- `supported_formats` (list of `VirtualCameraData::Resolution`, optional):
+ List of supported resolution and frame rates of the emulated camera
+ - `width` (`unsigned int`, default=1920): Width of the window resolution.
+ This needs to be even.
+ - `height` (`unsigned int`, default=1080): Height of the window resolution.
+ - `frame_rates` (list of `int`, default=`[30,60]` ): Range of the frame
+ rate (per second). If the list contains one value, it's the lower bound
+ and the upper bound. If the list contains two values, the first is the
+ lower bound and the second is the upper bound. No other number of values
+ is allowed.
+- `test_pattern` (`string`): Which test pattern to use as frames. The options
+ are "bars", "lines". Cannot be set with `frames`.
+ - The test patterns are "bars" which means color bars, and "lines" which means
+ diagonal lines.
+- `frames` (dictionary):
+ - `path` (`string`): Path to an image, or path to a directory of a series of
+ images. Cannot be set with `test_pattern`.
+ - The path to an image has ".jpg" extension.
+ - The path to a directory ends with "/". The name of the images in the
+ directory are "{n}.jpg" with {n} is the sequence of images starting with 0.
+- `location` (`string`, default="front"): The location of the camera. Support
+ "CameraLocationFront", "CameraLocationBack", and "CameraLocationExternal".
+- `model` (`string`, default="Unknown"): The model name of the camera.
+
+Check `data/virtual.yaml` as the sample config file.
+
+### Implementation
+
+`Parser` class provides methods to parse the config file to register cameras
+in Virtual Pipeline Handler. `parseConfigFile()` is exposed to use in
+Virtual Pipeline Handler.
+
+This is the procedure of the Parser class:
+1. `parseConfigFile()` parses the config file to `YamlObject` using `YamlParser::parse()`.
+ - Parse the top level of config file which are the camera ids and look into
+ each camera properties.
+2. For each camera, `parseCameraConfigData()` returns a camera with the configuration.
+ - The methods in the next step fill the data with the pointer to the Camera object.
+ - If the config file contains invalid configuration, this method returns
+ nullptr. The camera will be skipped.
+3. Parse each property and register the data.
+ - `parseSupportedFormats()`: Parses `supported_formats` in the config, which
+ contains resolutions and frame rates.
+ - `parseFrameGenerator()`: Parses `test_pattern` or `frames` in the config.
+ - `parseLocation()`: Parses `location` in the config.
+ - `parseModel()`: Parses `model` in the config.
+4. Back to `parseConfigFile()` and append the camera configuration.
+5. Returns a list of camera configurations.
diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
new file mode 100644
index 00000000..1d3d9ba8
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/config_parser.cpp
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Virtual cameras helper to parse config file
+ */
+
+#include "config_parser.h"
+
+#include <string.h>
+#include <utility>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/property_ids.h>
+
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "virtual.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Virtual)
+
+std::vector<std::unique_ptr<VirtualCameraData>>
+ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe)
+{
+ std::vector<std::unique_ptr<VirtualCameraData>> configurations;
+
+ std::unique_ptr<YamlObject> cameras = YamlParser::parse(file);
+ if (!cameras) {
+ LOG(Virtual, Error) << "Failed to parse config file.";
+ return configurations;
+ }
+
+ if (!cameras->isDictionary()) {
+ LOG(Virtual, Error) << "Config file is not a dictionary at the top level.";
+ return configurations;
+ }
+
+ /* Look into the configuration of each camera */
+ for (const auto &[cameraId, cameraConfigData] : cameras->asDict()) {
+ std::unique_ptr<VirtualCameraData> data =
+ parseCameraConfigData(cameraConfigData, pipe);
+ /* Parse configData to data */
+ if (!data) {
+ /* Skip the camera if it has invalid config */
+ LOG(Virtual, Error) << "Failed to parse config of the camera: "
+ << cameraId;
+ continue;
+ }
+
+ data->config_.id = cameraId;
+ ControlInfoMap::Map controls;
+ /* \todo Check which resolution's frame rate to be reported */
+ controls[&controls::FrameDurationLimits] =
+ ControlInfo(1000000 / data->config_.resolutions[0].frameRates[1],
+ 1000000 / data->config_.resolutions[0].frameRates[0]);
+
+ std::vector<ControlValue> supportedFaceDetectModes{
+ static_cast<int32_t>(controls::draft::FaceDetectModeOff),
+ };
+ controls[&controls::draft::FaceDetectMode] = ControlInfo(supportedFaceDetectModes);
+
+ data->controlInfo_ = ControlInfoMap(std::move(controls), controls::controls);
+ configurations.push_back(std::move(data));
+ }
+
+ return configurations;
+}
+
+std::unique_ptr<VirtualCameraData>
+ConfigParser::parseCameraConfigData(const YamlObject &cameraConfigData,
+ PipelineHandler *pipe)
+{
+ std::vector<VirtualCameraData::Resolution> resolutions;
+ if (parseSupportedFormats(cameraConfigData, &resolutions))
+ return nullptr;
+
+ std::unique_ptr<VirtualCameraData> data =
+ std::make_unique<VirtualCameraData>(pipe, resolutions);
+
+ if (parseFrameGenerator(cameraConfigData, data.get()))
+ return nullptr;
+
+ if (parseLocation(cameraConfigData, data.get()))
+ return nullptr;
+
+ if (parseModel(cameraConfigData, data.get()))
+ return nullptr;
+
+ return data;
+}
+
+int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData,
+ std::vector<VirtualCameraData::Resolution> *resolutions)
+{
+ if (cameraConfigData.contains("supported_formats")) {
+ const YamlObject &supportedResolutions = cameraConfigData["supported_formats"];
+
+ for (const YamlObject &supportedResolution : supportedResolutions.asList()) {
+ unsigned int width = supportedResolution["width"].get<unsigned int>(1920);
+ unsigned int height = supportedResolution["height"].get<unsigned int>(1080);
+ if (width == 0 || height == 0) {
+ LOG(Virtual, Error) << "Invalid width or/and height";
+ return -EINVAL;
+ }
+ if (width % 2 != 0) {
+ LOG(Virtual, Error) << "Invalid width: width needs to be even";
+ return -EINVAL;
+ }
+
+ std::vector<int64_t> frameRates;
+ if (supportedResolution.contains("frame_rates")) {
+ auto frameRatesList =
+ supportedResolution["frame_rates"].getList<int>();
+ if (!frameRatesList || (frameRatesList->size() != 1 &&
+ frameRatesList->size() != 2)) {
+ LOG(Virtual, Error) << "Invalid frame_rates: either one or two values";
+ return -EINVAL;
+ }
+
+ if (frameRatesList->size() == 2 &&
+ frameRatesList.value()[0] > frameRatesList.value()[1]) {
+ LOG(Virtual, Error) << "frame_rates's first value(lower bound)"
+ << " is higher than the second value(upper bound)";
+ return -EINVAL;
+ }
+ /*
+ * Push the min and max framerates. A
+ * single rate is duplicated.
+ */
+ frameRates.push_back(frameRatesList.value().front());
+ frameRates.push_back(frameRatesList.value().back());
+ } else {
+ frameRates.push_back(30);
+ frameRates.push_back(60);
+ }
+
+ resolutions->emplace_back(
+ VirtualCameraData::Resolution{ Size{ width, height },
+ frameRates });
+ }
+ } else {
+ resolutions->emplace_back(
+ VirtualCameraData::Resolution{ Size{ 1920, 1080 },
+ { 30, 60 } });
+ }
+
+ return 0;
+}
+
+int ConfigParser::parseFrameGenerator(const YamlObject &cameraConfigData, VirtualCameraData *data)
+{
+ const std::string testPatternKey = "test_pattern";
+ const std::string framesKey = "frames";
+ if (cameraConfigData.contains(testPatternKey)) {
+ if (cameraConfigData.contains(framesKey)) {
+ LOG(Virtual, Error) << "A camera should use either "
+ << testPatternKey << " or " << framesKey;
+ return -EINVAL;
+ }
+
+ auto testPattern = cameraConfigData[testPatternKey].get<std::string>("");
+
+ if (testPattern == "bars") {
+ data->config_.frame = TestPattern::ColorBars;
+ } else if (testPattern == "lines") {
+ data->config_.frame = TestPattern::DiagonalLines;
+ } else {
+ LOG(Virtual, Debug) << "Test pattern: " << testPattern
+ << " is not supported";
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ const YamlObject &frames = cameraConfigData[framesKey];
+
+ /* When there is no frames provided in the config file, use color bar test pattern */
+ if (!frames) {
+ data->config_.frame = TestPattern::ColorBars;
+ return 0;
+ }
+
+ if (!frames.isDictionary()) {
+ LOG(Virtual, Error) << "'frames' is not a dictionary.";
+ return -EINVAL;
+ }
+
+ auto path = frames["path"].get<std::string>();
+
+ if (!path) {
+ LOG(Virtual, Error) << "Test pattern or path should be specified.";
+ return -EINVAL;
+ }
+
+ std::vector<std::filesystem::path> files;
+
+ switch (std::filesystem::symlink_status(*path).type()) {
+ case std::filesystem::file_type::regular:
+ files.push_back(*path);
+ break;
+
+ case std::filesystem::file_type::directory:
+ for (const auto &dentry : std::filesystem::directory_iterator{ *path }) {
+ if (dentry.is_regular_file())
+ files.push_back(dentry.path());
+ }
+
+ std::sort(files.begin(), files.end(), [](const auto &a, const auto &b) {
+ return ::strverscmp(a.c_str(), b.c_str()) < 0;
+ });
+
+ if (files.empty()) {
+ LOG(Virtual, Error) << "Directory has no files: " << *path;
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ LOG(Virtual, Error) << "Frame: " << *path << " is not supported";
+ return -EINVAL;
+ }
+
+ data->config_.frame = ImageFrames{ std::move(files) };
+
+ return 0;
+}
+
+int ConfigParser::parseLocation(const YamlObject &cameraConfigData, VirtualCameraData *data)
+{
+ /* Default value is properties::CameraLocationFront */
+ int32_t location = properties::CameraLocationFront;
+
+ if (auto l = cameraConfigData["location"].get<std::string>()) {
+ auto it = properties::LocationNameValueMap.find(*l);
+ if (it == properties::LocationNameValueMap.end()) {
+ LOG(Virtual, Error)
+ << "location: " << *l << " is not supported";
+ return -EINVAL;
+ }
+
+ location = it->second;
+ }
+
+ data->properties_.set(properties::Location, location);
+
+ return 0;
+}
+
+int ConfigParser::parseModel(const YamlObject &cameraConfigData, VirtualCameraData *data)
+{
+ std::string model = cameraConfigData["model"].get<std::string>("Unknown");
+
+ data->properties_.set(properties::Model, model);
+
+ return 0;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h
new file mode 100644
index 00000000..d2000de9
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/config_parser.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Virtual cameras helper to parse config file
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <libcamera/base/file.h>
+
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "virtual.h"
+
+namespace libcamera {
+
+class ConfigParser
+{
+public:
+ std::vector<std::unique_ptr<VirtualCameraData>>
+ parseConfigFile(File &file, PipelineHandler *pipe);
+
+private:
+ std::unique_ptr<VirtualCameraData>
+ parseCameraConfigData(const YamlObject &cameraConfigData, PipelineHandler *pipe);
+
+ int parseSupportedFormats(const YamlObject &cameraConfigData,
+ std::vector<VirtualCameraData::Resolution> *resolutions);
+ int parseFrameGenerator(const YamlObject &cameraConfigData, VirtualCameraData *data);
+ int parseLocation(const YamlObject &cameraConfigData, VirtualCameraData *data);
+ int parseModel(const YamlObject &cameraConfigData, VirtualCameraData *data);
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/data/meson.build b/src/libcamera/pipeline/virtual/data/meson.build
new file mode 100644
index 00000000..ce63f9a2
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/data/meson.build
@@ -0,0 +1,4 @@
+install_data('virtual.yaml',
+ install_dir : pipeline_data_dir / 'virtual',
+ install_tag : 'runtime',
+ rename: 'virtual.yaml.example')
diff --git a/src/libcamera/pipeline/virtual/data/virtual.yaml b/src/libcamera/pipeline/virtual/data/virtual.yaml
new file mode 100644
index 00000000..20471bb9
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/data/virtual.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: CC0-1.0
+%YAML 1.1
+---
+"Virtual0":
+ supported_formats:
+ - width: 1920
+ height: 1080
+ frame_rates:
+ - 30
+ - 60
+ - width: 1680
+ height: 1050
+ frame_rates:
+ - 70
+ - 80
+ test_pattern: "lines"
+ location: "CameraLocationFront"
+ model: "Virtual Video Device"
+"Virtual1":
+ supported_formats:
+ - width: 800
+ height: 600
+ frame_rates:
+ - 60
+ test_pattern: "bars"
+ location: "CameraLocationBack"
+ model: "Virtual Video Device1"
+"Virtual2":
+ supported_formats:
+ - width: 400
+ height: 300
+ test_pattern: "lines"
+ location: "CameraLocationFront"
+ model: "Virtual Video Device2"
+"Virtual3":
+ test_pattern: "bars"
diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h
new file mode 100644
index 00000000..a0658c45
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/frame_generator.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Virtual cameras helper to generate frames
+ */
+
+#pragma once
+
+#include <libcamera/framebuffer.h>
+#include <libcamera/geometry.h>
+
+namespace libcamera {
+
+class FrameGenerator
+{
+public:
+ virtual ~FrameGenerator() = default;
+
+ virtual void configure(const Size &size) = 0;
+
+ virtual int generateFrame(const Size &size,
+ const FrameBuffer *buffer) = 0;
+
+protected:
+ FrameGenerator() {}
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.cpp b/src/libcamera/pipeline/virtual/image_frame_generator.cpp
new file mode 100644
index 00000000..d1545b5d
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/image_frame_generator.cpp
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Derived class of FrameGenerator for generating frames from images
+ */
+
+#include "image_frame_generator.h"
+
+#include <string>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/framebuffer.h>
+
+#include "libcamera/internal/mapped_framebuffer.h"
+
+#include "libyuv/convert.h"
+#include "libyuv/scale.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Virtual)
+
+/*
+ * Factory function to create an ImageFrameGenerator object.
+ * Read the images and convert them to buffers in NV12 format.
+ * Store the pointers to the buffers to a list (imageFrameDatas)
+ */
+std::unique_ptr<ImageFrameGenerator>
+ImageFrameGenerator::create(ImageFrames &imageFrames)
+{
+ std::unique_ptr<ImageFrameGenerator> imageFrameGenerator =
+ std::make_unique<ImageFrameGenerator>();
+ imageFrameGenerator->imageFrames_ = &imageFrames;
+
+ /*
+ * For each file in the directory, load the image,
+ * convert it to NV12, and store the pointer.
+ */
+ for (const auto &path : imageFrames.files) {
+ File file(path);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ LOG(Virtual, Error) << "Failed to open image file " << file.fileName()
+ << ": " << strerror(file.error());
+ return nullptr;
+ }
+
+ /* Read the image file to data */
+ auto fileSize = file.size();
+ auto buffer = std::make_unique<uint8_t[]>(fileSize);
+ if (file.read({ buffer.get(), static_cast<size_t>(fileSize) }) != fileSize) {
+ LOG(Virtual, Error) << "Failed to read file " << file.fileName()
+ << ": " << strerror(file.error());
+ return nullptr;
+ }
+
+ /* Get the width and height of the image */
+ int width, height;
+ if (libyuv::MJPGSize(buffer.get(), fileSize, &width, &height)) {
+ LOG(Virtual, Error) << "Failed to get the size of the image file: "
+ << file.fileName();
+ return nullptr;
+ }
+
+ std::unique_ptr<uint8_t[]> dstY =
+ std::make_unique<uint8_t[]>(width * height);
+ std::unique_ptr<uint8_t[]> dstUV =
+ std::make_unique<uint8_t[]>(width * height / 2);
+ int ret = libyuv::MJPGToNV12(buffer.get(), fileSize,
+ dstY.get(), width, dstUV.get(),
+ width, width, height, width, height);
+ if (ret != 0)
+ LOG(Virtual, Error) << "MJPGToNV12() failed with " << ret;
+
+ imageFrameGenerator->imageFrameDatas_.emplace_back(
+ ImageFrameData{ std::move(dstY), std::move(dstUV),
+ Size(width, height) });
+ }
+
+ ASSERT(!imageFrameGenerator->imageFrameDatas_.empty());
+
+ return imageFrameGenerator;
+}
+
+/*
+ * \var ImageFrameGenerator::frameRepeat
+ * \brief Number of frames to repeat before proceeding to the next frame
+ */
+
+/* Scale the buffers for image frames. */
+void ImageFrameGenerator::configure(const Size &size)
+{
+ /* Reset the source images to prevent multiple configuration calls */
+ scaledFrameDatas_.clear();
+ frameIndex_ = 0;
+ parameter_ = 0;
+
+ for (unsigned int i = 0; i < imageFrameDatas_.size(); i++) {
+ /* Scale the imageFrameDatas_ to scaledY and scaledUV */
+ unsigned int halfSizeWidth = (size.width + 1) / 2;
+ unsigned int halfSizeHeight = (size.height + 1) / 2;
+ std::unique_ptr<uint8_t[]> scaledY =
+ std::make_unique<uint8_t[]>(size.width * size.height);
+ std::unique_ptr<uint8_t[]> scaledUV =
+ std::make_unique<uint8_t[]>(halfSizeWidth * halfSizeHeight * 2);
+ auto &src = imageFrameDatas_[i];
+
+ /*
+ * \todo Some platforms might enforce stride due to GPU.
+ * The width needs to be a multiple of the stride to work
+ * properly for now.
+ */
+ libyuv::NV12Scale(src.Y.get(), src.size.width,
+ src.UV.get(), src.size.width,
+ src.size.width, src.size.height,
+ scaledY.get(), size.width, scaledUV.get(), size.width,
+ size.width, size.height, libyuv::FilterMode::kFilterBilinear);
+
+ scaledFrameDatas_.emplace_back(
+ ImageFrameData{ std::move(scaledY), std::move(scaledUV), size });
+ }
+}
+
+int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buffer)
+{
+ ASSERT(!scaledFrameDatas_.empty());
+
+ MappedFrameBuffer mappedFrameBuffer(buffer, MappedFrameBuffer::MapFlag::Write);
+
+ const auto &planes = mappedFrameBuffer.planes();
+
+ /* Loop only around the number of images available */
+ frameIndex_ %= imageFrameDatas_.size();
+
+ /* Write the scaledY and scaledUV to the mapped frame buffer */
+ libyuv::NV12Copy(scaledFrameDatas_[frameIndex_].Y.get(), size.width,
+ scaledFrameDatas_[frameIndex_].UV.get(), size.width, planes[0].begin(),
+ size.width, planes[1].begin(), size.width,
+ size.width, size.height);
+
+ /* Proceed to the next image every 4 frames */
+ /* \todo Consider setting the frameRepeat in the config file */
+ parameter_++;
+ if (parameter_ % frameRepeat == 0)
+ frameIndex_++;
+
+ return 0;
+}
+
+/*
+ * \var ImageFrameGenerator::imageFrameDatas_
+ * \brief List of pointers to the not scaled image buffers
+ */
+
+/*
+ * \var ImageFrameGenerator::scaledFrameDatas_
+ * \brief List of pointers to the scaled image buffers
+ */
+
+/*
+ * \var ImageFrameGenerator::imageFrames_
+ * \brief Pointer to the imageFrames_ in VirtualCameraData
+ */
+
+/*
+ * \var ImageFrameGenerator::parameter_
+ * \brief Speed parameter. Change to the next image every parameter_ frames
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.h b/src/libcamera/pipeline/virtual/image_frame_generator.h
new file mode 100644
index 00000000..42a077ba
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/image_frame_generator.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Derived class of FrameGenerator for generating frames from images
+ */
+
+#pragma once
+
+#include <filesystem>
+#include <memory>
+#include <stdint.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "frame_generator.h"
+
+namespace libcamera {
+
+/* Frame configuration provided by the config file */
+struct ImageFrames {
+ std::vector<std::filesystem::path> files;
+};
+
+class ImageFrameGenerator : public FrameGenerator
+{
+public:
+ static std::unique_ptr<ImageFrameGenerator> create(ImageFrames &imageFrames);
+
+private:
+ static constexpr unsigned int frameRepeat = 4;
+
+ struct ImageFrameData {
+ std::unique_ptr<uint8_t[]> Y;
+ std::unique_ptr<uint8_t[]> UV;
+ Size size;
+ };
+
+ void configure(const Size &size) override;
+ int generateFrame(const Size &size, const FrameBuffer *buffer) override;
+
+ std::vector<ImageFrameData> imageFrameDatas_;
+ std::vector<ImageFrameData> scaledFrameDatas_;
+ ImageFrames *imageFrames_;
+ unsigned int frameIndex_;
+ unsigned int parameter_;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/meson.build b/src/libcamera/pipeline/virtual/meson.build
new file mode 100644
index 00000000..c8434593
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/meson.build
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_internal_sources += files([
+ 'config_parser.cpp',
+ 'image_frame_generator.cpp',
+ 'test_pattern_generator.cpp',
+ 'virtual.cpp',
+])
+
+libjpeg = dependency('libjpeg', required : true)
+
+libcamera_deps += [libyuv_dep]
+libcamera_deps += [libjpeg]
+
+subdir('data')
diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp
new file mode 100644
index 00000000..745be83b
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Derived class of FrameGenerator for generating test patterns
+ */
+
+#include "test_pattern_generator.h"
+
+#include <string.h>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/mapped_framebuffer.h"
+
+#include <libyuv/convert_from_argb.h>
+
+namespace {
+
+template<size_t SampleSize>
+void rotateLeft1Column(const libcamera::Size &size, uint8_t *image)
+{
+ if (size.width < 2)
+ return;
+
+ const size_t stride = size.width * SampleSize;
+ uint8_t first[SampleSize];
+
+ for (size_t i = 0; i < size.height; i++, image += stride) {
+ memcpy(first, &image[0], SampleSize);
+ memmove(&image[0], &image[SampleSize], stride - SampleSize);
+ memcpy(&image[stride - SampleSize], first, SampleSize);
+ }
+}
+
+} /* namespace */
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Virtual)
+
+static const unsigned int kARGBSize = 4;
+
+int TestPatternGenerator::generateFrame(const Size &size,
+ const FrameBuffer *buffer)
+{
+ MappedFrameBuffer mappedFrameBuffer(buffer,
+ MappedFrameBuffer::MapFlag::Write);
+
+ const auto &planes = mappedFrameBuffer.planes();
+
+ rotateLeft1Column<kARGBSize>(size, template_.get());
+
+ /* Convert the template_ to the frame buffer */
+ int ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize,
+ planes[0].begin(), size.width,
+ planes[1].begin(), size.width,
+ size.width, size.height);
+ if (ret != 0)
+ LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret;
+
+ return ret;
+}
+
+void ColorBarsGenerator::configure(const Size &size)
+{
+ constexpr uint8_t kColorBar[8][3] = {
+ /* R, G, B */
+ { 0xff, 0xff, 0xff }, /* White */
+ { 0xff, 0xff, 0x00 }, /* Yellow */
+ { 0x00, 0xff, 0xff }, /* Cyan */
+ { 0x00, 0xff, 0x00 }, /* Green */
+ { 0xff, 0x00, 0xff }, /* Magenta */
+ { 0xff, 0x00, 0x00 }, /* Red */
+ { 0x00, 0x00, 0xff }, /* Blue */
+ { 0x00, 0x00, 0x00 }, /* Black */
+ };
+
+ template_ = std::make_unique<uint8_t[]>(
+ size.width * size.height * kARGBSize);
+
+ unsigned int colorBarWidth = size.width / std::size(kColorBar);
+
+ uint8_t *buf = template_.get();
+ for (size_t h = 0; h < size.height; h++) {
+ for (size_t w = 0; w < size.width; w++) {
+ /* Repeat when the width is exceed */
+ unsigned int index = (w / colorBarWidth) % std::size(kColorBar);
+
+ *buf++ = kColorBar[index][2]; /* B */
+ *buf++ = kColorBar[index][1]; /* G */
+ *buf++ = kColorBar[index][0]; /* R */
+ *buf++ = 0x00; /* A */
+ }
+ }
+}
+
+void DiagonalLinesGenerator::configure(const Size &size)
+{
+ constexpr uint8_t kColorBar[2][3] = {
+ /* R, G, B */
+ { 0xff, 0xff, 0xff }, /* White */
+ { 0x00, 0x00, 0x00 }, /* Black */
+ };
+
+ template_ = std::make_unique<uint8_t[]>(
+ size.width * size.height * kARGBSize);
+
+ unsigned int lineWidth = size.width / 10;
+
+ uint8_t *buf = template_.get();
+ for (size_t h = 0; h < size.height; h++) {
+ for (size_t w = 0; w < size.width; w++) {
+ /* Repeat when the width is exceed */
+ int index = ((w + h) / lineWidth) % 2;
+
+ *buf++ = kColorBar[index][2]; /* B */
+ *buf++ = kColorBar[index][1]; /* G */
+ *buf++ = kColorBar[index][0]; /* R */
+ *buf++ = 0x00; /* A */
+ }
+ }
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.h b/src/libcamera/pipeline/virtual/test_pattern_generator.h
new file mode 100644
index 00000000..2a51bd31
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/test_pattern_generator.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Derived class of FrameGenerator for generating test patterns
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <libcamera/framebuffer.h>
+#include <libcamera/geometry.h>
+
+#include "frame_generator.h"
+
+namespace libcamera {
+
+enum class TestPattern : char {
+ ColorBars = 0,
+ DiagonalLines = 1,
+};
+
+class TestPatternGenerator : public FrameGenerator
+{
+public:
+ int generateFrame(const Size &size, const FrameBuffer *buffer) override;
+
+protected:
+ /* Buffer of test pattern template */
+ std::unique_ptr<uint8_t[]> template_;
+};
+
+class ColorBarsGenerator : public TestPatternGenerator
+{
+public:
+ /* Generate a template buffer of the color bar test pattern. */
+ void configure(const Size &size) override;
+};
+
+class DiagonalLinesGenerator : public TestPatternGenerator
+{
+public:
+ /* Generate a template buffer of the diagonal lines test pattern. */
+ void configure(const Size &size) override;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
new file mode 100644
index 00000000..049ebcba
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/virtual.cpp
@@ -0,0 +1,419 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Pipeline handler for virtual cameras
+ */
+
+#include "virtual.h"
+
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <errno.h>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <stdint.h>
+#include <string>
+#include <time.h>
+#include <utility>
+#include <vector>
+
+#include <libcamera/base/flags.h>
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/formats.h>
+#include <libcamera/pixel_format.h>
+#include <libcamera/property_ids.h>
+#include <libcamera/request.h>
+
+#include "libcamera/internal/camera.h"
+#include "libcamera/internal/dma_buf_allocator.h"
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/yaml_parser.h"
+
+#include "pipeline/virtual/config_parser.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Virtual)
+
+namespace {
+
+uint64_t currentTimestamp()
+{
+ const auto now = std::chrono::steady_clock::now();
+ auto nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ now.time_since_epoch());
+
+ return nsecs.count();
+}
+
+} /* namespace */
+
+template<class... Ts>
+struct overloaded : Ts... {
+ using Ts::operator()...;
+};
+template<class... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+class VirtualCameraConfiguration : public CameraConfiguration
+{
+public:
+ static constexpr unsigned int kBufferCount = 4;
+
+ VirtualCameraConfiguration(VirtualCameraData *data);
+
+ Status validate() override;
+
+private:
+ const VirtualCameraData *data_;
+};
+
+class PipelineHandlerVirtual : public PipelineHandler
+{
+public:
+ PipelineHandlerVirtual(CameraManager *manager);
+ ~PipelineHandlerVirtual();
+
+ std::unique_ptr<CameraConfiguration> generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles) override;
+ int configure(Camera *camera, CameraConfiguration *config) override;
+
+ int exportFrameBuffers(Camera *camera, Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *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;
+
+private:
+ static bool created_;
+
+ VirtualCameraData *cameraData(Camera *camera)
+ {
+ return static_cast<VirtualCameraData *>(camera->_d());
+ }
+
+ bool initFrameGenerator(Camera *camera);
+
+ DmaBufAllocator dmaBufAllocator_;
+
+ bool resetCreated_ = false;
+};
+
+VirtualCameraData::VirtualCameraData(PipelineHandler *pipe,
+ const std::vector<Resolution> &supportedResolutions)
+ : Camera::Private(pipe)
+{
+ config_.resolutions = supportedResolutions;
+ for (const auto &resolution : config_.resolutions) {
+ if (config_.minResolutionSize.isNull() || config_.minResolutionSize > resolution.size)
+ config_.minResolutionSize = resolution.size;
+
+ config_.maxResolutionSize = std::max(config_.maxResolutionSize, resolution.size);
+ }
+
+ properties_.set(properties::PixelArrayActiveAreas,
+ { Rectangle(config_.maxResolutionSize) });
+
+ /* \todo Support multiple streams and pass multi_stream_test */
+ streamConfigs_.resize(kMaxStream);
+}
+
+VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data)
+ : CameraConfiguration(), data_(data)
+{
+}
+
+CameraConfiguration::Status VirtualCameraConfiguration::validate()
+{
+ Status status = Valid;
+
+ if (config_.empty()) {
+ LOG(Virtual, Error) << "Empty config";
+ return Invalid;
+ }
+
+ /* Only one stream is supported */
+ if (config_.size() > VirtualCameraData::kMaxStream) {
+ config_.resize(VirtualCameraData::kMaxStream);
+ status = Adjusted;
+ }
+
+ for (StreamConfiguration &cfg : config_) {
+ bool adjusted = false;
+ bool found = false;
+ for (const auto &resolution : data_->config_.resolutions) {
+ if (resolution.size.width == cfg.size.width &&
+ resolution.size.height == cfg.size.height) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * \todo It's a pipeline's decision to choose a
+ * resolution when the exact one is not supported.
+ * Defining the default logic in PipelineHandler to
+ * find the closest resolution would be nice.
+ */
+ cfg.size = data_->config_.maxResolutionSize;
+ status = Adjusted;
+ adjusted = true;
+ }
+
+ if (cfg.pixelFormat != formats::NV12) {
+ cfg.pixelFormat = formats::NV12;
+ status = Adjusted;
+ adjusted = true;
+ }
+
+ if (adjusted)
+ LOG(Virtual, Info)
+ << "Stream configuration adjusted to " << cfg.toString();
+
+ const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat);
+ cfg.stride = info.stride(cfg.size.width, 0, 1);
+ cfg.frameSize = info.frameSize(cfg.size, 1);
+
+ cfg.bufferCount = VirtualCameraConfiguration::kBufferCount;
+ }
+
+ return status;
+}
+
+/* static */
+bool PipelineHandlerVirtual::created_ = false;
+
+PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
+ : PipelineHandler(manager),
+ dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
+ DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
+ DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
+{
+}
+
+PipelineHandlerVirtual::~PipelineHandlerVirtual()
+{
+ if (resetCreated_)
+ created_ = false;
+}
+
+std::unique_ptr<CameraConfiguration>
+PipelineHandlerVirtual::generateConfiguration(Camera *camera,
+ Span<const StreamRole> roles)
+{
+ VirtualCameraData *data = cameraData(camera);
+ auto config = std::make_unique<VirtualCameraConfiguration>(data);
+
+ if (roles.empty())
+ return config;
+
+ for (const StreamRole role : roles) {
+ switch (role) {
+ case StreamRole::StillCapture:
+ case StreamRole::VideoRecording:
+ case StreamRole::Viewfinder:
+ break;
+
+ case StreamRole::Raw:
+ default:
+ LOG(Virtual, Error)
+ << "Requested stream role not supported: " << role;
+ return {};
+ }
+
+ std::map<PixelFormat, std::vector<SizeRange>> streamFormats;
+ PixelFormat pixelFormat = formats::NV12;
+ streamFormats[pixelFormat] = { { data->config_.minResolutionSize,
+ data->config_.maxResolutionSize } };
+ StreamFormats formats(streamFormats);
+ StreamConfiguration cfg(formats);
+ cfg.pixelFormat = pixelFormat;
+ cfg.size = data->config_.maxResolutionSize;
+ cfg.bufferCount = VirtualCameraConfiguration::kBufferCount;
+
+ config->addConfiguration(cfg);
+ }
+
+ ASSERT(config->validate() != CameraConfiguration::Invalid);
+
+ return config;
+}
+
+int PipelineHandlerVirtual::configure(Camera *camera,
+ CameraConfiguration *config)
+{
+ VirtualCameraData *data = cameraData(camera);
+ for (auto [i, c] : utils::enumerate(*config)) {
+ c.setStream(&data->streamConfigs_[i].stream);
+ /* Start reading the images/generating test patterns */
+ data->streamConfigs_[i].frameGenerator->configure(c.size);
+ }
+
+ return 0;
+}
+
+int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera,
+ Stream *stream,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ if (!dmaBufAllocator_.isValid())
+ return -ENOBUFS;
+
+ const StreamConfiguration &config = stream->configuration();
+ const PixelFormatInfo &info = PixelFormatInfo::info(config.pixelFormat);
+
+ std::vector<unsigned int> planeSizes;
+ for (size_t i = 0; i < info.numPlanes(); ++i)
+ planeSizes.push_back(info.planeSize(config.size, i));
+
+ return dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, buffers);
+}
+
+int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera,
+ [[maybe_unused]] const ControlList *controls)
+{
+ VirtualCameraData *data = cameraData(camera);
+
+ for (auto &s : data->streamConfigs_)
+ s.seq = 0;
+
+ return 0;
+}
+
+void PipelineHandlerVirtual::stopDevice([[maybe_unused]] Camera *camera)
+{
+}
+
+int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,
+ Request *request)
+{
+ VirtualCameraData *data = cameraData(camera);
+ const auto timestamp = currentTimestamp();
+
+ for (auto const &[stream, buffer] : request->buffers()) {
+ bool found = false;
+ /* map buffer and fill test patterns */
+ for (auto &streamConfig : data->streamConfigs_) {
+ if (stream == &streamConfig.stream) {
+ FrameMetadata &fmd = buffer->_d()->metadata();
+
+ fmd.status = FrameMetadata::Status::FrameSuccess;
+ fmd.sequence = streamConfig.seq++;
+ fmd.timestamp = timestamp;
+
+ for (const auto [i, p] : utils::enumerate(buffer->planes()))
+ fmd.planes()[i].bytesused = p.length;
+
+ found = true;
+
+ if (streamConfig.frameGenerator->generateFrame(
+ stream->configuration().size, buffer))
+ fmd.status = FrameMetadata::Status::FrameError;
+
+ completeBuffer(request, buffer);
+ break;
+ }
+ }
+ ASSERT(found);
+ }
+
+ request->metadata().set(controls::SensorTimestamp, timestamp);
+ completeRequest(request);
+
+ return 0;
+}
+
+bool PipelineHandlerVirtual::match([[maybe_unused]] DeviceEnumerator *enumerator)
+{
+ if (created_)
+ return false;
+
+ created_ = true;
+
+ std::string configFile = configurationFile("virtual", "virtual.yaml", true);
+ if (configFile.empty()) {
+ LOG(Virtual, Debug)
+ << "Configuration file not found, skipping virtual cameras";
+ return false;
+ }
+
+ File file(configFile);
+ if (!file.open(File::OpenModeFlag::ReadOnly)) {
+ LOG(Virtual, Error)
+ << "Failed to open config file `" << file.fileName() << "`";
+ return false;
+ }
+
+ ConfigParser parser;
+ auto configData = parser.parseConfigFile(file, this);
+ if (configData.size() == 0) {
+ LOG(Virtual, Error) << "Failed to parse any cameras from the config file: "
+ << file.fileName();
+ return false;
+ }
+
+ /* Configure and register cameras with configData */
+ for (auto &data : configData) {
+ std::set<Stream *> streams;
+ for (auto &streamConfig : data->streamConfigs_)
+ streams.insert(&streamConfig.stream);
+ std::string id = data->config_.id;
+ std::shared_ptr<Camera> camera = Camera::create(std::move(data), id, streams);
+
+ if (!initFrameGenerator(camera.get())) {
+ LOG(Virtual, Error) << "Failed to initialize frame "
+ << "generator for camera: " << id;
+ continue;
+ }
+
+ registerCamera(std::move(camera));
+ }
+
+ resetCreated_ = true;
+
+ return true;
+}
+
+bool PipelineHandlerVirtual::initFrameGenerator(Camera *camera)
+{
+ auto data = cameraData(camera);
+ auto &frame = data->config_.frame;
+ std::visit(overloaded{
+ [&](TestPattern &testPattern) {
+ for (auto &streamConfig : data->streamConfigs_) {
+ if (testPattern == TestPattern::DiagonalLines)
+ streamConfig.frameGenerator = std::make_unique<DiagonalLinesGenerator>();
+ else
+ streamConfig.frameGenerator = std::make_unique<ColorBarsGenerator>();
+ }
+ },
+ [&](ImageFrames &imageFrames) {
+ for (auto &streamConfig : data->streamConfigs_)
+ streamConfig.frameGenerator = ImageFrameGenerator::create(imageFrames);
+ } },
+ frame);
+
+ for (auto &streamConfig : data->streamConfigs_)
+ if (!streamConfig.frameGenerator)
+ return false;
+
+ return true;
+}
+
+REGISTER_PIPELINE_HANDLER(PipelineHandlerVirtual, "virtual")
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/virtual/virtual.h b/src/libcamera/pipeline/virtual/virtual.h
new file mode 100644
index 00000000..683cb82b
--- /dev/null
+++ b/src/libcamera/pipeline/virtual/virtual.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * Pipeline handler for virtual cameras
+ */
+
+#pragma once
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <libcamera/geometry.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/camera.h"
+#include "libcamera/internal/pipeline_handler.h"
+
+#include "frame_generator.h"
+#include "image_frame_generator.h"
+#include "test_pattern_generator.h"
+
+namespace libcamera {
+
+using VirtualFrame = std::variant<TestPattern, ImageFrames>;
+
+class VirtualCameraData : public Camera::Private
+{
+public:
+ const static unsigned int kMaxStream = 3;
+
+ struct Resolution {
+ Size size;
+ std::vector<int64_t> frameRates;
+ };
+ struct StreamConfig {
+ Stream stream;
+ std::unique_ptr<FrameGenerator> frameGenerator;
+ unsigned int seq = 0;
+ };
+ /* The config file is parsed to the Configuration struct */
+ struct Configuration {
+ std::string id;
+ std::vector<Resolution> resolutions;
+ VirtualFrame frame;
+
+ Size maxResolutionSize;
+ Size minResolutionSize;
+ };
+
+ VirtualCameraData(PipelineHandler *pipe,
+ const std::vector<Resolution> &supportedResolutions);
+
+ ~VirtualCameraData() = default;
+
+ Configuration config_;
+
+ std::vector<StreamConfig> streamConfigs_;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 9c74c6cf..d84dff3c 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018, Google Inc.
*
- * pipeline_handler.cpp - Pipeline handler infrastructure
+ * Pipeline handler infrastructure
*/
#include "libcamera/internal/pipeline_handler.h"
@@ -22,7 +22,6 @@
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_manager.h"
#include "libcamera/internal/device_enumerator.h"
-#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/request.h"
#include "libcamera/internal/tracepoints.h"
@@ -75,7 +74,7 @@ PipelineHandler::PipelineHandler(CameraManager *manager)
PipelineHandler::~PipelineHandler()
{
- for (std::shared_ptr<MediaDevice> media : mediaDevices_)
+ for (std::shared_ptr<MediaDevice> &media : mediaDevices_)
media->release();
}
@@ -157,26 +156,28 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,
* Pipeline handlers shall not call this function directly as the Camera class
* handles access internally.
*
- * \context This function is \threadsafe.
+ * \context This function is called from the CameraManager thread.
*
* \return True if the pipeline handler was acquired, false if another process
* has already acquired it
* \sa release()
*/
-bool PipelineHandler::acquire()
+bool PipelineHandler::acquire(Camera *camera)
{
- MutexLocker locker(lock_);
-
- if (useCount_) {
- ++useCount_;
- return true;
+ if (useCount_ == 0) {
+ for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {
+ if (!media->lock()) {
+ unlockMediaDevices();
+ return false;
+ }
+ }
}
- for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {
- if (!media->lock()) {
+ if (!acquireDevice(camera)) {
+ if (useCount_ == 0)
unlockMediaDevices();
- return false;
- }
+
+ return false;
}
++useCount_;
@@ -195,29 +196,60 @@ bool PipelineHandler::acquire()
* Pipeline handlers shall not call this function directly as the Camera class
* handles access internally.
*
- * \context This function is \threadsafe.
+ * \context This function is called from the CameraManager thread.
*
* \sa acquire()
*/
void PipelineHandler::release(Camera *camera)
{
- MutexLocker locker(lock_);
-
ASSERT(useCount_);
- unlockMediaDevices();
-
releaseDevice(camera);
+ if (useCount_ == 1)
+ unlockMediaDevices();
+
--useCount_;
}
/**
+ * \brief Acquire resources associated with this camera
+ * \param[in] camera The camera for which to acquire resources
+ *
+ * Pipeline handlers may override this in order to get resources such as opening
+ * devices and allocating buffers when a camera is acquired.
+ *
+ * This is used by the uvcvideo pipeline handler to delay opening /dev/video#
+ * until the camera is acquired to avoid excess power consumption. The delayed
+ * opening of /dev/video# is a special case because the kernel uvcvideo driver
+ * powers on the USB device as soon as /dev/video# is opened. This behavior
+ * should *not* be copied by other pipeline handlers.
+ *
+ * \context This function is called from the CameraManager thread.
+ *
+ * \return True on success, false on failure
+ * \sa releaseDevice()
+ */
+bool PipelineHandler::acquireDevice([[maybe_unused]] Camera *camera)
+{
+ return true;
+}
+
+/**
* \brief Release resources associated with this camera
* \param[in] camera The camera for which to release resources
*
* Pipeline handlers may override this in order to perform cleanup operations
* when a camera is released, such as freeing memory.
+ *
+ * This is called once for every camera that is released. If there are resources
+ * shared by multiple cameras then the pipeline handler must take care to not
+ * release them until releaseDevice() has been called for all previously
+ * acquired cameras.
+ *
+ * \context This function is called from the CameraManager thread.
+ *
+ * \sa acquireDevice()
*/
void PipelineHandler::releaseDevice([[maybe_unused]] Camera *camera)
{
@@ -335,9 +367,7 @@ void PipelineHandler::stop(Camera *camera)
while (!waitingRequests_.empty()) {
Request *request = waitingRequests_.front();
waitingRequests_.pop();
-
- request->_d()->cancel();
- completeRequest(request);
+ cancelRequest(request);
}
/* Make sure no requests are pending. */
@@ -438,10 +468,8 @@ void PipelineHandler::doQueueRequest(Request *request)
}
int ret = queueRequestDevice(camera, request);
- if (ret) {
- request->_d()->cancel();
- completeRequest(request);
- }
+ if (ret)
+ cancelRequest(request);
}
/**
@@ -537,9 +565,23 @@ void PipelineHandler::completeRequest(Request *request)
}
/**
+ * \brief Cancel request and signal its completion
+ * \param[in] request The request to cancel
+ *
+ * This function cancels and completes the request. The same rules as for
+ * completeRequest() apply.
+ */
+void PipelineHandler::cancelRequest(Request *request)
+{
+ request->_d()->cancel();
+ completeRequest(request);
+}
+
+/**
* \brief Retrieve the absolute path to a platform configuration file
* \param[in] subdir The pipeline handler specific subdirectory name
* \param[in] name The configuration file name
+ * \param[in] silent Disable error messages
*
* This function locates a named platform configuration file and returns
* its absolute path to the pipeline handler. It searches the following
@@ -555,7 +597,8 @@ void PipelineHandler::completeRequest(Request *request)
* string if no configuration file can be found
*/
std::string PipelineHandler::configurationFile(const std::string &subdir,
- const std::string &name) const
+ const std::string &name,
+ bool silent) const
{
std::string confPath;
struct stat statbuf;
@@ -585,9 +628,11 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
if (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG)
return confPath;
- LOG(Pipeline, Error)
- << "Configuration file '" << confPath
- << "' not found for pipeline handler '" << PipelineHandler::name() << "'";
+ if (!silent)
+ LOG(Pipeline, Error)
+ << "Configuration file '" << confPath
+ << "' not found for pipeline handler '"
+ << PipelineHandler::name() << "'";
return std::string();
}
@@ -605,9 +650,14 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
{
cameras_.push_back(camera);
- if (mediaDevices_.empty())
- LOG(Pipeline, Fatal)
- << "Registering camera with no media devices!";
+ if (mediaDevices_.empty()) {
+ /*
+ * For virtual devices with no MediaDevice, there are no system
+ * devices to register.
+ */
+ manager_->_d()->addCamera(std::move(camera));
+ return;
+ }
/*
* Walk the entity list and map the devnums of all capture video nodes
@@ -649,7 +699,7 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
*/
void PipelineHandler::hotplugMediaDevice(MediaDevice *media)
{
- media->disconnected.connect(this, [=]() { mediaDeviceDisconnected(media); });
+ media->disconnected.connect(this, [this, media] { mediaDeviceDisconnected(media); });
}
/**
@@ -720,6 +770,13 @@ void PipelineHandler::disconnect()
*/
/**
+ * \fn PipelineHandler::cameraManager() const
+ * \brief Retrieve the CameraManager that this pipeline handler belongs to
+ * \context This function is \threadsafe.
+ * \return The CameraManager for this pipeline handler
+ */
+
+/**
* \class PipelineHandlerFactoryBase
* \brief Base class for pipeline handler factories
*
@@ -795,6 +852,28 @@ std::vector<PipelineHandlerFactoryBase *> &PipelineHandlerFactoryBase::factories
}
/**
+ * \brief Return the factory for the pipeline handler with name \a name
+ * \param[in] name The pipeline handler name
+ * \return The factory of the pipeline with name \a name, or nullptr if not found
+ */
+const PipelineHandlerFactoryBase *PipelineHandlerFactoryBase::getFactoryByName(const std::string &name)
+{
+ const std::vector<PipelineHandlerFactoryBase *> &factories =
+ PipelineHandlerFactoryBase::factories();
+
+ auto iter = std::find_if(factories.begin(),
+ factories.end(),
+ [&name](const PipelineHandlerFactoryBase *f) {
+ return f->name() == name;
+ });
+
+ if (iter != factories.end())
+ return *iter;
+
+ return nullptr;
+}
+
+/**
* \class PipelineHandlerFactory
* \brief Registration of PipelineHandler classes and creation of instances
* \tparam _PipelineHandler The pipeline handler class type for this factory
@@ -830,6 +909,8 @@ std::vector<PipelineHandlerFactoryBase *> &PipelineHandlerFactoryBase::factories
* \def REGISTER_PIPELINE_HANDLER
* \brief Register a pipeline handler with the pipeline handler factory
* \param[in] handler Class name of PipelineHandler derived class to register
+ * \param[in] name Name assigned to the pipeline handler, matching the pipeline
+ * subdirectory name in the source tree.
*
* Register a PipelineHandler subclass with the factory and make it available to
* try and match devices.
diff --git a/src/libcamera/pixel_format.cpp b/src/libcamera/pixel_format.cpp
index 80c22072..314179a8 100644
--- a/src/libcamera/pixel_format.cpp
+++ b/src/libcamera/pixel_format.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * pixel_format.cpp - libcamera Pixel Format
+ * libcamera Pixel Format
*/
#include <libcamera/formats.h>
diff --git a/src/libcamera/process.cpp b/src/libcamera/process.cpp
index 86a382fb..68fad327 100644
--- a/src/libcamera/process.cpp
+++ b/src/libcamera/process.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * process.cpp - Process object
+ * Process object
*/
#include "libcamera/internal/process.h"
@@ -10,7 +10,6 @@
#include <algorithm>
#include <dirent.h>
#include <fcntl.h>
-#include <iostream>
#include <list>
#include <signal.h>
#include <string.h>
@@ -76,7 +75,7 @@ void ProcessManager::sighandler()
return;
}
- for (auto it = processes_.begin(); it != processes_.end(); ) {
+ for (auto it = processes_.begin(); it != processes_.end();) {
Process *process = *it;
int wstatus;
@@ -189,7 +188,6 @@ const struct sigaction &ProcessManager::oldsa() const
return oldsa_;
}
-
/**
* \class Process
* \brief Process object
@@ -271,8 +269,8 @@ int Process::start(const std::string &path,
unsigned int len = args.size();
argv[0] = path.c_str();
for (unsigned int i = 0; i < len; i++)
- argv[i+1] = args[i].c_str();
- argv[len+1] = nullptr;
+ argv[i + 1] = args[i].c_str();
+ argv[len + 1] = nullptr;
execv(path.c_str(), (char **)argv);
diff --git a/src/libcamera/property_ids.cpp.in b/src/libcamera/property_ids.cpp.in
deleted file mode 100644
index f917e334..00000000
--- a/src/libcamera/property_ids.cpp.in
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * property_ids.cpp : Property ID list
- *
- * This file is auto-generated. Do not edit.
- */
-
-#include <libcamera/property_ids.h>
-
-/**
- * \file property_ids.h
- * \brief Camera property identifiers
- */
-
-namespace libcamera {
-
-/**
- * \brief Namespace for libcamera properties
- */
-namespace properties {
-
-${controls_doc}
-
-/**
- * \brief Namespace for libcamera draft properties
- */
-namespace draft {
-
-${draft_controls_doc}
-
-} /* namespace draft */
-
-#ifndef __DOXYGEN__
-/*
- * Keep the properties definitions hidden from doxygen as it incorrectly parses
- * them as functions.
- */
-${controls_def}
-
-namespace draft {
-
-${draft_controls_def}
-
-} /* namespace draft */
-#endif
-
-/**
- * \brief List of all supported libcamera properties
- */
-extern const ControlIdMap properties {
-${controls_map}
-};
-
-} /* namespace properties */
-
-} /* namespace libcamera */
diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids_core.yaml
index f3556384..834454a4 100644
--- a/src/libcamera/property_ids.yaml
+++ b/src/libcamera/property_ids_core.yaml
@@ -4,6 +4,7 @@
#
%YAML 1.1
---
+vendor: libcamera
controls:
- Location:
type: int32_t
@@ -700,37 +701,4 @@ controls:
Different cameras may report identical devices.
- # ----------------------------------------------------------------------------
- # Draft properties section
-
- - ColorFilterArrangement:
- type: int32_t
- draft: true
- description: |
- The arrangement of color filters on sensor; represents the colors in the
- top-left 2x2 section of the sensor, in reading order. Currently
- identical to ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT.
- enum:
- - name: RGGB
- value: 0
- description: RGGB Bayer pattern
- - name: GRBG
- value: 1
- description: GRBG Bayer pattern
- - name: GBRG
- value: 2
- description: GBRG Bayer pattern
- - name: BGGR
- value: 3
- description: BGGR Bayer pattern
- - name: RGB
- value: 4
- description: |
- Sensor is not Bayer; output has 3 16-bit values for each pixel,
- instead of just 1 16-bit value per pixel.
- - name: MONO
- value: 5
- description: |
- Sensor is not Bayer; output consists of a single colour channel.
-
...
diff --git a/src/libcamera/property_ids_draft.yaml b/src/libcamera/property_ids_draft.yaml
new file mode 100644
index 00000000..62f0e242
--- /dev/null
+++ b/src/libcamera/property_ids_draft.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2019, Google Inc.
+#
+%YAML 1.1
+---
+vendor: draft
+controls:
+ - ColorFilterArrangement:
+ type: int32_t
+ vendor: draft
+ description: |
+ The arrangement of color filters on sensor; represents the colors in the
+ top-left 2x2 section of the sensor, in reading order. Currently
+ identical to ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT.
+ enum:
+ - name: RGGB
+ value: 0
+ description: RGGB Bayer pattern
+ - name: GRBG
+ value: 1
+ description: GRBG Bayer pattern
+ - name: GBRG
+ value: 2
+ description: GBRG Bayer pattern
+ - name: BGGR
+ value: 3
+ description: BGGR Bayer pattern
+ - name: RGB
+ value: 4
+ description: |
+ Sensor is not Bayer; output has 3 16-bit values for each pixel,
+ instead of just 1 16-bit value per pixel.
+ - name: MONO
+ value: 5
+ description: |
+ Sensor is not Bayer; output consists of a single colour channel.
+
+...
diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build
index 00ae5a8f..8bd1b135 100644
--- a/src/libcamera/proxy/meson.build
+++ b/src/libcamera/proxy/meson.build
@@ -13,7 +13,8 @@ foreach mojom : ipa_mojoms
'--libcamera_generate_proxy_cpp',
'--libcamera_output_path=@OUTPUT@',
'./' + '@INPUT@'
- ])
+ ],
+ env : py_build_env)
- libcamera_sources += proxy
+ libcamera_internal_sources += proxy
endforeach
diff --git a/src/libcamera/proxy/worker/meson.build b/src/libcamera/proxy/worker/meson.build
index aa4d9cd7..8c54a2e2 100644
--- a/src/libcamera/proxy/worker/meson.build
+++ b/src/libcamera/proxy/worker/meson.build
@@ -15,10 +15,10 @@ foreach mojom : ipa_mojoms
'--libcamera_generate_proxy_worker',
'--libcamera_output_path=@OUTPUT@',
'./' + '@INPUT@'
- ])
+ ],
+ env : py_build_env)
- proxy = executable(mojom['name'] + '_ipa_proxy',
- [worker, libcamera_generated_ipa_headers],
+ proxy = executable(mojom['name'] + '_ipa_proxy', worker,
install : true,
install_dir : proxy_install_dir,
dependencies : libcamera_private)
diff --git a/src/libcamera/pub_key.cpp b/src/libcamera/pub_key.cpp
index 64dfa234..f1d73a5c 100644
--- a/src/libcamera/pub_key.cpp
+++ b/src/libcamera/pub_key.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * pub_key.cpp - Public key signature verification
+ * Public key signature verification
*/
#include "libcamera/internal/pub_key.h"
diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
index 949c556f..7f1e11e8 100644
--- a/src/libcamera/request.cpp
+++ b/src/libcamera/request.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * request.cpp - Capture request handling
+ * Capture request handling
*/
#include "libcamera/internal/request.h"
@@ -28,10 +28,17 @@
* \brief Describes a frame capture request to be processed by a camera
*/
+/**
+ * \internal
+ * \file libcamera/internal/request.h
+ * \brief Internal support for request handling
+ */
+
namespace libcamera {
LOG_DEFINE_CATEGORY(Request)
+#ifndef __DOXYGEN_PUBLIC__
/**
* \class Request::Private
* \brief Request private data
@@ -300,6 +307,7 @@ void Request::Private::timeout()
emitPrepareCompleted();
}
+#endif /* __DOXYGEN_PUBLIC__ */
/**
* \enum Request::Status
@@ -467,16 +475,6 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer,
return -EINVAL;
}
- auto it = bufferMap_.find(stream);
- if (it != bufferMap_.end()) {
- LOG(Request, Error) << "FrameBuffer already set for stream";
- return -EEXIST;
- }
-
- buffer->_d()->setRequest(this);
- _d()->pending_.insert(buffer);
- bufferMap_[stream] = buffer;
-
/*
* Make sure the fence has been extracted from the buffer
* to avoid waiting on a stale fence.
@@ -486,6 +484,15 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer,
return -EEXIST;
}
+ auto [it, inserted] = bufferMap_.try_emplace(stream, buffer);
+ if (!inserted) {
+ LOG(Request, Error) << "FrameBuffer already set for stream";
+ return -EEXIST;
+ }
+
+ buffer->_d()->setRequest(this);
+ _d()->pending_.insert(buffer);
+
if (fence && fence->isValid())
buffer->_d()->setFence(std::move(fence));
diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp
new file mode 100644
index 00000000..d19b5e2e
--- /dev/null
+++ b/src/libcamera/sensor/camera_sensor.cpp
@@ -0,0 +1,583 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * A camera sensor
+ */
+
+#include "libcamera/internal/camera_sensor.h"
+
+#include <memory>
+#include <vector>
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/media_object.h"
+
+/**
+ * \file camera_sensor.h
+ * \brief A camera sensor
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(CameraSensor)
+
+/**
+ * \class CameraSensor
+ * \brief A abstract camera sensor
+ *
+ * The CameraSensor class eases handling of sensors for pipeline handlers by
+ * hiding the details of the kernel API and caching sensor information.
+ */
+
+/**
+ * \brief Destroy a CameraSensor
+ */
+CameraSensor::~CameraSensor() = default;
+
+/**
+ * \fn CameraSensor::model()
+ * \brief Retrieve the sensor model name
+ *
+ * The sensor model name is a free-formed string that uniquely identifies the
+ * sensor model.
+ *
+ * \return The sensor model name
+ */
+
+/**
+ * \fn CameraSensor::id()
+ * \brief Retrieve the sensor ID
+ *
+ * The sensor ID is a free-form string that uniquely identifies the sensor in
+ * the system. The ID satisfies the requirements to be used as a camera ID.
+ *
+ * \return The sensor ID
+ */
+
+/**
+ * \fn CameraSensor::entity()
+ * \brief Retrieve the sensor media entity
+ * \return The sensor media entity
+ */
+
+/**
+ * \fn CameraSensor::device()
+ * \brief Retrieve the camera sensor device
+ * \todo Remove this function by integrating DelayedControl with CameraSensor
+ * \return The camera sensor device
+ */
+
+/**
+ * \fn CameraSensor::focusLens()
+ * \brief Retrieve the focus lens controller
+ *
+ * \return The focus lens controller. nullptr if no focus lens controller is
+ * connected to the sensor
+ */
+
+/**
+ * \fn CameraSensor::mbusCodes()
+ * \brief Retrieve the media bus codes supported by the camera sensor
+ *
+ * Any Bayer formats are listed using the sensor's native Bayer order,
+ * that is, with the effect of V4L2_CID_HFLIP and V4L2_CID_VFLIP undone
+ * (where these controls exist).
+ *
+ * \return The supported media bus codes sorted in increasing order
+ */
+
+/**
+ * \fn CameraSensor::sizes()
+ * \brief Retrieve the supported frame sizes for a media bus code
+ * \param[in] mbusCode The media bus code for which sizes are requested
+ *
+ * \return The supported frame sizes for \a mbusCode sorted in increasing order
+ */
+
+/**
+ * \fn CameraSensor::resolution()
+ * \brief Retrieve the camera sensor resolution
+ *
+ * The camera sensor resolution is the active pixel area size, clamped to the
+ * maximum frame size the sensor can produce if it is smaller than the active
+ * pixel area.
+ *
+ * \todo Consider if it desirable to distinguish between the maximum resolution
+ * the sensor can produce (also including upscaled ones) and the actual pixel
+ * array size by splitting this function in two.
+ *
+ * \return The camera sensor resolution in pixels
+ */
+
+/**
+ * \fn CameraSensor::getFormat()
+ * \brief Retrieve the best sensor format for a desired output
+ * \param[in] mbusCodes The list of acceptable media bus codes
+ * \param[in] size The desired size
+ * \param[in] maxSize The maximum size
+ *
+ * Media bus codes are selected from \a mbusCodes, which lists all acceptable
+ * codes in decreasing order of preference. Media bus codes supported by the
+ * sensor but not listed in \a mbusCodes are ignored. If none of the desired
+ * codes is supported, it returns an error.
+ *
+ * \a size indicates the desired size at the output of the sensor. This function
+ * selects the best media bus code and size supported by the sensor according
+ * to the following criteria.
+ *
+ * - The desired \a size shall fit in the sensor output size to avoid the need
+ * to up-scale.
+ * - The sensor output size shall match the desired aspect ratio to avoid the
+ * need to crop the field of view.
+ * - The sensor output size shall be as small as possible to lower the required
+ * bandwidth.
+ * - The desired \a size shall be supported by one of the media bus code listed
+ * in \a mbusCodes.
+ * - The desired \a size shall fit into the maximum size \a maxSize if it is not
+ * null.
+ *
+ * When multiple media bus codes can produce the same size, the code at the
+ * lowest position in \a mbusCodes is selected.
+ *
+ * The use of this function is optional, as the above criteria may not match the
+ * needs of all pipeline handlers. Pipeline handlers may implement custom
+ * sensor format selection when needed.
+ *
+ * The returned sensor output format is guaranteed to be acceptable by the
+ * setFormat() function without any modification.
+ *
+ * \return The best sensor output format matching the desired media bus codes
+ * and size on success, or an empty format otherwise.
+ */
+
+/**
+ * \fn CameraSensor::setFormat()
+ * \brief Set the sensor output format
+ * \param[in] format The desired sensor output format
+ * \param[in] transform The transform to be applied on the sensor.
+ * Defaults to Identity.
+ *
+ * If flips are writable they are configured according to the desired Transform.
+ * Transform::Identity always corresponds to H/V flip being disabled if the
+ * controls are writable. Flips are set before the new format is applied as
+ * they can effectively change the Bayer pattern ordering.
+ *
+ * The ranges of any controls associated with the sensor are also updated.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
+ * \fn CameraSensor::tryFormat()
+ * \brief Try the sensor output format
+ * \param[in] format The desired sensor output format
+ *
+ * The ranges of any controls associated with the sensor are not updated.
+ *
+ * \todo Add support for Transform by changing the format's Bayer ordering
+ * before calling subdev_->setFormat().
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
+ * \fn CameraSensor::applyConfiguration()
+ * \brief Apply a sensor configuration to the camera sensor
+ * \param[in] config The sensor configuration
+ * \param[in] transform The transform to be applied on the sensor.
+ * Defaults to Identity
+ * \param[out] sensorFormat Format applied to the sensor (optional)
+ *
+ * Apply to the camera sensor the configuration \a config.
+ *
+ * \todo The configuration shall be fully populated and if any of the fields
+ * specified cannot be applied exactly, an error code is returned.
+ *
+ * \return 0 if \a config is applied correctly to the camera sensor, a negative
+ * error code otherwise
+ */
+
+/**
+ * \brief Retrieve the image source stream
+ *
+ * Sensors that produce multiple streams do not guarantee that the image stream
+ * is always assigned number 0. This function allows callers to retrieve the
+ * image stream on the sensor's source pad, in order to configure the receiving
+ * side accordingly.
+ *
+ * \return The image source stream
+ */
+V4L2Subdevice::Stream CameraSensor::imageStream() const
+{
+ return { 0, 0 };
+}
+
+/**
+ * \brief Retrieve the embedded data source stream
+ *
+ * Some sensors produce embedded data in a stream separate from the image
+ * stream. This function indicates if the sensor supports this feature by
+ * returning the embedded data stream on the sensor's source pad if available,
+ * or an std::optional<> without a value otheriwse.
+ *
+ * \return The embedded data source stream
+ */
+std::optional<V4L2Subdevice::Stream> CameraSensor::embeddedDataStream() const
+{
+ return {};
+}
+
+/**
+ * \brief Retrieve the format on the embedded data stream
+ *
+ * When an embedded data stream is available, this function returns the
+ * corresponding format on the sensor's source pad. The format may vary with
+ * the image stream format, and should therefore be retrieved after configuring
+ * the image stream.
+ *
+ * If the sensor doesn't support embedded data, this function returns a
+ * default-constructed format.
+ *
+ * \return The format on the embedded data stream
+ */
+V4L2SubdeviceFormat CameraSensor::embeddedDataFormat() const
+{
+ return {};
+}
+
+/**
+ * \brief Enable or disable the embedded data stream
+ * \param[in] enable True to enable the embedded data stream, false to disable it
+ *
+ * For sensors that support embedded data, this function enables or disables
+ * generation of embedded data. Some of such sensors always produce embedded
+ * data, in which case this function return -EISCONN if the caller attempts to
+ * disable embedded data.
+ *
+ * If the sensor doesn't support embedded data, this function returns 0 when \a
+ * enable is false, and -ENOSTR otherwise.
+ *
+ * \return 0 on success, or a negative error code otherwise
+ */
+int CameraSensor::setEmbeddedDataEnabled(bool enable)
+{
+ return enable ? -ENOSTR : 0;
+}
+
+/**
+ * \fn CameraSensor::properties()
+ * \brief Retrieve the camera sensor properties
+ * \return The list of camera sensor properties
+ */
+
+/**
+ * \fn CameraSensor::sensorInfo()
+ * \brief Assemble and return the camera sensor info
+ * \param[out] info The camera sensor info
+ *
+ * This function fills \a info with information that describes the camera sensor
+ * and its current configuration. The information combines static data (such as
+ * the the sensor model or active pixel array size) and data specific to the
+ * current sensor configuration (such as the line length and pixel rate).
+ *
+ * Sensor information is only available for raw sensors. When called for a YUV
+ * sensor, this function returns -EINVAL.
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+
+/**
+ * \fn CameraSensor::computeTransform()
+ * \brief Compute the Transform that gives the requested \a orientation
+ * \param[inout] orientation The desired image orientation
+ *
+ * This function computes the Transform that the pipeline handler should apply
+ * to the CameraSensor to obtain the requested \a orientation.
+ *
+ * The intended caller of this function is the validate() implementation of
+ * pipeline handlers, that pass in the application requested
+ * CameraConfiguration::orientation and obtain a Transform to apply to the
+ * camera sensor, likely at configure() time.
+ *
+ * If the requested \a orientation cannot be obtained, the \a orientation
+ * parameter is adjusted to report the current image orientation and
+ * Transform::Identity is returned.
+ *
+ * If the requested \a orientation can be obtained, the function computes a
+ * Transform and does not adjust \a orientation.
+ *
+ * Pipeline handlers are expected to verify if \a orientation has been
+ * adjusted by this function and set the CameraConfiguration::status to
+ * Adjusted accordingly.
+ *
+ * \return A Transform instance that applied to the CameraSensor produces images
+ * with \a orientation
+ */
+
+/**
+ * \fn CameraSensor::bayerOrder()
+ * \brief Compute the Bayer order that results from the given Transform
+ * \param[in] t The Transform to apply to the sensor
+ *
+ * Some sensors change their Bayer order when they are h-flipped or v-flipped.
+ * This function computes and returns the Bayer order that would result from the
+ * given transform applied to the sensor.
+ *
+ * This function is valid only when the sensor produces raw Bayer formats.
+ *
+ * \return The Bayer order produced by the sensor when the Transform is applied
+ */
+
+/**
+ * \fn CameraSensor::controls()
+ * \brief Retrieve the supported V4L2 controls and their information
+ *
+ * Control information is updated automatically to reflect the current sensor
+ * configuration when the setFormat() function is called, without invalidating
+ * any iterator on the ControlInfoMap.
+ *
+ * \return A map of the V4L2 controls supported by the sensor
+ */
+
+/**
+ * \fn CameraSensor::getControls()
+ * \brief Read V4L2 controls from the sensor
+ * \param[in] ids The list of controls to read, specified by their ID
+ *
+ * This function reads the value of all controls contained in \a ids, and
+ * returns their values as a ControlList. The control identifiers are defined by
+ * the V4L2 specification (V4L2_CID_*).
+ *
+ * If any control in \a ids is not supported by the device, is disabled (i.e.
+ * has the V4L2_CTRL_FLAG_DISABLED flag set), or if any other error occurs
+ * during validation of the requested controls, no control is read and this
+ * function returns an empty control list.
+ *
+ * \sa V4L2Device::getControls()
+ *
+ * \return The control values in a ControlList on success, or an empty list on
+ * error
+ */
+
+/**
+ * \fn CameraSensor::setControls()
+ * \brief Write V4L2 controls to the sensor
+ * \param[in] ctrls The list of controls to write
+ *
+ * This function writes the value of all controls contained in \a ctrls, and
+ * stores the values actually applied to the device in the corresponding \a
+ * ctrls entry. The control identifiers are defined by the V4L2 specification
+ * (V4L2_CID_*).
+ *
+ * If any control in \a ctrls is not supported by the device, is disabled (i.e.
+ * has the V4L2_CTRL_FLAG_DISABLED flag set), is read-only, or if any other
+ * error occurs during validation of the requested controls, no control is
+ * written and this function returns -EINVAL.
+ *
+ * If an error occurs while writing the controls, the index of the first
+ * control that couldn't be written is returned. All controls below that index
+ * are written and their values are updated in \a ctrls, while all other
+ * controls are not written and their values are not changed.
+ *
+ * \sa V4L2Device::setControls()
+ *
+ * \return 0 on success or an error code otherwise
+ * \retval -EINVAL One of the control is not supported or not accessible
+ * \retval i The index of the control that failed
+ */
+
+/**
+ * \fn CameraSensor::testPatternModes()
+ * \brief Retrieve all the supported test pattern modes of the camera sensor
+ * The test pattern mode values correspond to the controls::TestPattern control.
+ *
+ * \return The list of test pattern modes
+ */
+
+/**
+ * \fn CameraSensor::setTestPatternMode()
+ * \brief Set the test pattern mode for the camera sensor
+ * \param[in] mode The test pattern mode
+ *
+ * The new \a mode is applied to the sensor if it differs from the active test
+ * pattern mode. Otherwise, this function is a no-op. Setting the same test
+ * pattern mode for every frame thus incurs no performance penalty.
+ */
+
+/**
+ * \fn CameraSensor::sensorDelays()
+ * \brief Fetch the sensor delay values
+ *
+ * This function retrieves the delays that the sensor applies to controls. If
+ * the static properties database doesn't specifiy control delay values for the
+ * sensor, default delays that may be suitable are returned and a warning is
+ * logged.
+ *
+ * \return The sensor delay values
+ */
+
+/**
+ * \class CameraSensorFactoryBase
+ * \brief Base class for camera sensor factories
+ *
+ * The CameraSensorFactoryBase class is the base of all specializations of
+ * the CameraSensorFactory class template. It implements the factory
+ * registration, maintains a registry of factories, and provides access to the
+ * registered factories.
+ */
+
+/**
+ * \brief Construct a camera sensor factory base
+ * \param[in] name The camera sensor factory name
+ * \param[in] priority Priority order for factory selection
+ *
+ * Creating an instance of the factory base registers it with the global list of
+ * factories, accessible through the factories() function.
+ */
+CameraSensorFactoryBase::CameraSensorFactoryBase(const char *name, int priority)
+ : name_(name), priority_(priority)
+{
+ registerFactory(this);
+}
+
+/**
+ * \brief Create an instance of the CameraSensor corresponding to a media entity
+ * \param[in] entity The media entity on the source end of the sensor
+ *
+ * When multiple factories match the same \a entity, this function selects the
+ * matching factory with the highest priority as specified to the
+ * REGISTER_CAMERA_SENSOR() macro at factory registration time. If multiple
+ * matching factories have the same highest priority value, which factory gets
+ * selected is undefined and may vary between runs.
+ *
+ * \return A unique pointer to a new instance of the CameraSensor subclass
+ * matching the entity, or a null pointer if no such factory exists
+ */
+std::unique_ptr<CameraSensor> CameraSensorFactoryBase::create(MediaEntity *entity)
+{
+ const std::vector<CameraSensorFactoryBase *> &factories =
+ CameraSensorFactoryBase::factories();
+
+ for (const CameraSensorFactoryBase *factory : factories) {
+ std::variant<std::unique_ptr<CameraSensor>, int> result =
+ factory->match(entity);
+
+ if (std::holds_alternative<std::unique_ptr<CameraSensor>>(result)) {
+ LOG(CameraSensor, Debug)
+ << "Entity '" << entity->name() << "' matched by "
+ << factory->name();
+ return std::get<std::unique_ptr<CameraSensor>>(std::move(result));
+ }
+
+ if (std::get<int>(result)) {
+ LOG(CameraSensor, Error)
+ << "Failed to create sensor for '"
+ << entity->name() << ": " << std::get<int>(result);
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+/**
+ * \fn CameraSensorFactoryBase::name()
+ * \brief Retrieve the camera sensor factory name
+ * \return The name of the factory
+ */
+
+/**
+ * \fn CameraSensorFactoryBase::priority()
+ * \brief Retrieve the priority value for the factory
+ * \return The priority value for the factory
+ */
+
+/**
+ * \brief Retrieve the list of all camera sensor factories
+ *
+ * The factories are sorted in decreasing priority order.
+ *
+ * \return The list of camera sensor factories
+ */
+std::vector<CameraSensorFactoryBase *> &CameraSensorFactoryBase::factories()
+{
+ /*
+ * The static factories map is defined inside the function to ensure
+ * it gets initialized on first use, without any dependency on link
+ * order.
+ */
+ static std::vector<CameraSensorFactoryBase *> factories;
+ return factories;
+}
+
+/**
+ * \brief Add a camera sensor class to the registry
+ * \param[in] factory Factory to use to construct the camera sensor
+ */
+void CameraSensorFactoryBase::registerFactory(CameraSensorFactoryBase *factory)
+{
+ std::vector<CameraSensorFactoryBase *> &factories =
+ CameraSensorFactoryBase::factories();
+
+ auto pos = std::upper_bound(factories.begin(), factories.end(), factory,
+ [](const CameraSensorFactoryBase *value,
+ const CameraSensorFactoryBase *elem) {
+ return value->priority() > elem->priority();
+ });
+ factories.insert(pos, factory);
+}
+
+/**
+ * \class CameraSensorFactory
+ * \brief Registration of CameraSensorFactory classes and creation of instances
+ * \tparam _CameraSensor The camera sensor class type for this factory
+ *
+ * To facilitate discovery and instantiation of CameraSensor classes, the
+ * CameraSensorFactory class implements auto-registration of camera sensors.
+ * Each CameraSensor subclass shall register itself using the
+ * REGISTER_CAMERA_SENSOR() macro, which will create a corresponding instance
+ * of a CameraSensorFactory subclass and register it with the static list of
+ * factories.
+ */
+
+/**
+ * \fn CameraSensorFactory::CameraSensorFactory()
+ * \brief Construct a camera sensor factory
+ *
+ * Creating an instance of the factory registers it with the global list of
+ * factories, accessible through the CameraSensorFactoryBase::factories()
+ * function.
+ */
+
+/**
+ * \def REGISTER_CAMERA_SENSOR(sensor, priority)
+ * \brief Register a camera sensor type to the sensor factory
+ * \param[in] sensor Class name of the CameraSensor derived class to register
+ * \param[in] priority Priority order for factory selection
+ *
+ * Register a CameraSensor subclass with the factory and make it available to
+ * try and match sensors. The subclass needs to implement a static match
+ * function:
+ *
+ * \code{.cpp}
+ * static std::variant<std::unique_ptr<CameraSensor>, int> match(MediaEntity *entity);
+ * \endcode
+ *
+ * The function tests if the sensor class supports the camera sensor identified
+ * by a MediaEntity. If so, it creates a new instance of the sensor class. The
+ * return value is a variant that contains
+ *
+ * - A new instance of the camera sensor class if the entity matched and
+ * creation succeeded ;
+ * - A non-zero error code if the entity matched and the creation failed ; or
+ * - A zero error code if the entity didn't match.
+ *
+ * When multiple factories can support the same MediaEntity (as in the match()
+ * function of multiple factories returning true for the same entity), the \a
+ * priority argument selects which factory will be used. See
+ * CameraSensorFactoryBase::create() for more information.
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp
index d9261672..32989c19 100644
--- a/src/libcamera/camera_sensor.cpp
+++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
@@ -2,82 +2,177 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * camera_sensor.cpp - A camera sensor
+ * camera_sensor_legacy.cpp - A V4L2-backed camera sensor
*/
-#include "libcamera/internal/camera_sensor.h"
-#include "libcamera/internal/media_device.h"
-
#include <algorithm>
+#include <cmath>
#include <float.h>
#include <iomanip>
#include <limits.h>
-#include <math.h>
+#include <map>
+#include <memory>
#include <string.h>
+#include <string>
+#include <vector>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
#include <libcamera/camera.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
#include <libcamera/orientation.h>
#include <libcamera/property_ids.h>
+#include <libcamera/transform.h>
-#include <libcamera/base/utils.h>
+#include <libcamera/ipa/core_ipa_interface.h>
#include "libcamera/internal/bayer_format.h"
#include "libcamera/internal/camera_lens.h"
+#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/camera_sensor_properties.h"
#include "libcamera/internal/formats.h"
+#include "libcamera/internal/media_device.h"
#include "libcamera/internal/sysfs.h"
-
-/**
- * \file camera_sensor.h
- * \brief A camera sensor
- */
+#include "libcamera/internal/v4l2_subdevice.h"
namespace libcamera {
-LOG_DEFINE_CATEGORY(CameraSensor)
+class BayerFormat;
+class CameraLens;
+class MediaEntity;
+class SensorConfiguration;
+
+struct CameraSensorProperties;
+
+enum class Orientation;
+
+LOG_DECLARE_CATEGORY(CameraSensor)
+
+class CameraSensorLegacy : public CameraSensor, protected Loggable
+{
+public:
+ CameraSensorLegacy(const MediaEntity *entity);
+ ~CameraSensorLegacy();
+
+ static std::variant<std::unique_ptr<CameraSensor>, int>
+ match(MediaEntity *entity);
+
+ const std::string &model() const override { return model_; }
+ const std::string &id() const override { return id_; }
+
+ const MediaEntity *entity() const override { return entity_; }
+ V4L2Subdevice *device() override { return subdev_.get(); }
+
+ CameraLens *focusLens() override { return focusLens_.get(); }
+
+ const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
+ std::vector<Size> sizes(unsigned int mbusCode) const override;
+ Size resolution() const override;
+
+ V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
+ const Size &size,
+ const Size maxSize) const override;
+ int setFormat(V4L2SubdeviceFormat *format,
+ Transform transform = Transform::Identity) override;
+ int tryFormat(V4L2SubdeviceFormat *format) const override;
+
+ int applyConfiguration(const SensorConfiguration &config,
+ Transform transform = Transform::Identity,
+ V4L2SubdeviceFormat *sensorFormat = nullptr) override;
+
+ const ControlList &properties() const override { return properties_; }
+ int sensorInfo(IPACameraSensorInfo *info) const override;
+ Transform computeTransform(Orientation *orientation) const override;
+ BayerFormat::Order bayerOrder(Transform t) const override;
+
+ const ControlInfoMap &controls() const override;
+ ControlList getControls(const std::vector<uint32_t> &ids) override;
+ int setControls(ControlList *ctrls) override;
+
+ const std::vector<controls::draft::TestPatternModeEnum> &
+ testPatternModes() const override { return testPatternModes_; }
+ int setTestPatternMode(controls::draft::TestPatternModeEnum mode) override;
+ const CameraSensorProperties::SensorDelays &sensorDelays() override;
+
+protected:
+ std::string logPrefix() const override;
+
+private:
+ LIBCAMERA_DISABLE_COPY(CameraSensorLegacy)
+
+ int init();
+ int generateId();
+ int validateSensorDriver();
+ void initVimcDefaultProperties();
+ void initStaticProperties();
+ void initTestPatternModes();
+ int initProperties();
+ int applyTestPatternMode(controls::draft::TestPatternModeEnum mode);
+ int discoverAncillaryDevices();
+
+ const MediaEntity *entity_;
+ std::unique_ptr<V4L2Subdevice> subdev_;
+ unsigned int pad_;
+
+ const CameraSensorProperties *staticProps_;
+
+ std::string model_;
+ std::string id_;
+
+ V4L2Subdevice::Formats formats_;
+ std::vector<unsigned int> mbusCodes_;
+ std::vector<Size> sizes_;
+ std::vector<controls::draft::TestPatternModeEnum> testPatternModes_;
+ controls::draft::TestPatternModeEnum testPatternMode_;
+
+ Size pixelArraySize_;
+ Rectangle activeArea_;
+ const BayerFormat *bayerFormat_;
+ bool supportFlips_;
+ bool flipsAlterBayerOrder_;
+ Orientation mountingOrientation_;
+
+ ControlList properties_;
+
+ std::unique_ptr<CameraLens> focusLens_;
+};
/**
- * \class CameraSensor
+ * \class CameraSensorLegacy
* \brief A camera sensor based on V4L2 subdevices
*
- * The CameraSensor class eases handling of sensors for pipeline handlers by
- * hiding the details of the V4L2 subdevice kernel API and caching sensor
- * information.
- *
* The implementation is currently limited to sensors that expose a single V4L2
* subdevice with a single pad. It will be extended to support more complex
* devices as the needs arise.
*/
-/**
- * \brief Construct a CameraSensor
- * \param[in] entity The media entity backing the camera sensor
- *
- * Once constructed the instance must be initialized with init().
- */
-CameraSensor::CameraSensor(const MediaEntity *entity)
+CameraSensorLegacy::CameraSensorLegacy(const MediaEntity *entity)
: entity_(entity), pad_(UINT_MAX), staticProps_(nullptr),
bayerFormat_(nullptr), supportFlips_(false),
- properties_(properties::properties)
+ flipsAlterBayerOrder_(false), properties_(properties::properties)
{
}
-/**
- * \brief Destroy a CameraSensor
- */
-CameraSensor::~CameraSensor()
+CameraSensorLegacy::~CameraSensorLegacy() = default;
+
+std::variant<std::unique_ptr<CameraSensor>, int>
+CameraSensorLegacy::match(MediaEntity *entity)
{
+ std::unique_ptr<CameraSensorLegacy> sensor =
+ std::make_unique<CameraSensorLegacy>(entity);
+
+ int ret = sensor->init();
+ if (ret)
+ return { ret };
+
+ return { std::move(sensor) };
}
-/**
- * \brief Initialize the camera sensor instance
- *
- * This function performs the initialisation steps of the CameraSensor that may
- * fail. It shall be called once and only once after constructing the instance.
- *
- * \return 0 on success or a negative error code otherwise
- */
-int CameraSensor::init()
+int CameraSensorLegacy::init()
{
for (const MediaPad *pad : entity_->pads()) {
if (pad->flags() & MEDIA_PAD_FL_SOURCE) {
@@ -188,23 +283,12 @@ int CameraSensor::init()
* Set HBLANK to the minimum to start with a well-defined line length,
* allowing IPA modules that do not modify HBLANK to use the sensor
* minimum line length in their calculations.
- *
- * 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.
- *
- * \todo The control API ought to have a flag to specify if a control
- * is read-only which could be used below.
*/
- const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK);
- const int32_t hblankMin = hblank.min().get<int32_t>();
- const int32_t hblankMax = hblank.max().get<int32_t>();
-
- if (hblankMin != hblankMax) {
+ const struct v4l2_query_ext_ctrl *hblankInfo = subdev_->controlInfo(V4L2_CID_HBLANK);
+ if (hblankInfo && !(hblankInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
ControlList ctrl(subdev_->controls());
- ctrl.set(V4L2_CID_HBLANK, hblankMin);
+ ctrl.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblankInfo->minimum));
ret = subdev_->setControls(&ctrl);
if (ret)
return ret;
@@ -213,7 +297,31 @@ int CameraSensor::init()
return applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff);
}
-int CameraSensor::validateSensorDriver()
+int CameraSensorLegacy::generateId()
+{
+ const std::string devPath = subdev_->devicePath();
+
+ /* Try to get ID from firmware description. */
+ id_ = sysfs::firmwareNodePath(devPath);
+ if (!id_.empty())
+ return 0;
+
+ /*
+ * Virtual sensors not described in firmware
+ *
+ * Verify it's a platform device and construct ID from the device path
+ * and model of sensor.
+ */
+ if (devPath.find("/sys/devices/platform/", 0) == 0) {
+ id_ = devPath.substr(strlen("/sys/devices/")) + " " + model();
+ return 0;
+ }
+
+ LOG(CameraSensor, Error) << "Can't generate sensor ID";
+ return -EINVAL;
+}
+
+int CameraSensorLegacy::validateSensorDriver()
{
int err = 0;
@@ -258,9 +366,14 @@ int CameraSensor::validateSensorDriver()
const struct v4l2_query_ext_ctrl *hflipInfo = subdev_->controlInfo(V4L2_CID_HFLIP);
const struct v4l2_query_ext_ctrl *vflipInfo = subdev_->controlInfo(V4L2_CID_VFLIP);
if (hflipInfo && !(hflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY) &&
- vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
supportFlips_ = true;
+ if (hflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT ||
+ vflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT)
+ flipsAlterBayerOrder_ = true;
+ }
+
if (!supportFlips_)
LOG(CameraSensor, Debug)
<< "Camera sensor does not support horizontal/vertical flip";
@@ -352,18 +465,14 @@ int CameraSensor::validateSensorDriver()
return 0;
}
-/*
- * \brief Initialize properties that cannot be intialized by the
- * regular initProperties() function for VIMC
- */
-void CameraSensor::initVimcDefaultProperties()
+void CameraSensorLegacy::initVimcDefaultProperties()
{
/* Use the largest supported size. */
pixelArraySize_ = sizes_.back();
activeArea_ = Rectangle(pixelArraySize_);
}
-void CameraSensor::initStaticProperties()
+void CameraSensorLegacy::initStaticProperties()
{
staticProps_ = CameraSensorProperties::get(model_);
if (!staticProps_)
@@ -375,7 +484,31 @@ void CameraSensor::initStaticProperties()
initTestPatternModes();
}
-void CameraSensor::initTestPatternModes()
+const CameraSensorProperties::SensorDelays &CameraSensorLegacy::sensorDelays()
+{
+ static constexpr CameraSensorProperties::SensorDelays defaultSensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 1,
+ .vblankDelay = 2,
+ .hblankDelay = 2,
+ };
+
+ if (!staticProps_ ||
+ (!staticProps_->sensorDelays.exposureDelay &&
+ !staticProps_->sensorDelays.gainDelay &&
+ !staticProps_->sensorDelays.vblankDelay &&
+ !staticProps_->sensorDelays.hblankDelay)) {
+ LOG(CameraSensor, Warning)
+ << "No sensor delays found in static properties. "
+ "Assuming unverified defaults.";
+
+ return defaultSensorDelays;
+ }
+
+ return staticProps_->sensorDelays;
+}
+
+void CameraSensorLegacy::initTestPatternModes()
{
const auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN);
if (v4l2TestPattern == controls().end()) {
@@ -419,7 +552,7 @@ void CameraSensor::initTestPatternModes()
}
}
-int CameraSensor::initProperties()
+int CameraSensorLegacy::initProperties()
{
model_ = subdev_->model();
properties_.set(properties::Model, utils::toAscii(model_));
@@ -516,16 +649,7 @@ int CameraSensor::initProperties()
return 0;
}
-/**
- * \brief Check for and initialise any ancillary devices
- *
- * Sensors sometimes have ancillary devices such as a Lens or Flash that could
- * be linked to their MediaEntity by the kernel. Search for and handle any
- * such device.
- *
- * \todo Handle MEDIA_ENT_F_FLASH too.
- */
-int CameraSensor::discoverAncillaryDevices()
+int CameraSensorLegacy::discoverAncillaryDevices()
{
int ret;
@@ -552,50 +676,7 @@ int CameraSensor::discoverAncillaryDevices()
return 0;
}
-/**
- * \fn CameraSensor::model()
- * \brief Retrieve the sensor model name
- *
- * The sensor model name is a free-formed string that uniquely identifies the
- * sensor model.
- *
- * \return The sensor model name
- */
-
-/**
- * \fn CameraSensor::id()
- * \brief Retrieve the sensor ID
- *
- * The sensor ID is a free-form string that uniquely identifies the sensor in
- * the system. The ID satisfies the requirements to be used as a camera ID.
- *
- * \return The sensor ID
- */
-
-/**
- * \fn CameraSensor::entity()
- * \brief Retrieve the sensor media entity
- * \return The sensor media entity
- */
-
-/**
- * \fn CameraSensor::mbusCodes()
- * \brief Retrieve the media bus codes supported by the camera sensor
- *
- * Any Bayer formats are listed using the sensor's native Bayer order,
- * that is, with the effect of V4L2_CID_HFLIP and V4L2_CID_VFLIP undone
- * (where these controls exist).
- *
- * \return The supported media bus codes sorted in increasing order
- */
-
-/**
- * \brief Retrieve the supported frame sizes for a media bus code
- * \param[in] mbusCode The media bus code for which sizes are requested
- *
- * \return The supported frame sizes for \a mbusCode sorted in increasing order
- */
-std::vector<Size> CameraSensor::sizes(unsigned int mbusCode) const
+std::vector<Size> CameraSensorLegacy::sizes(unsigned int mbusCode) const
{
std::vector<Size> sizes;
@@ -612,120 +693,14 @@ std::vector<Size> CameraSensor::sizes(unsigned int mbusCode) const
return sizes;
}
-/**
- * \brief Retrieve the camera sensor resolution
- *
- * The camera sensor resolution is the active pixel area size, clamped to the
- * maximum frame size the sensor can produce if it is smaller than the active
- * pixel area.
- *
- * \todo Consider if it desirable to distinguish between the maximum resolution
- * the sensor can produce (also including upscaled ones) and the actual pixel
- * array size by splitting this function in two.
- *
- * \return The camera sensor resolution in pixels
- */
-Size CameraSensor::resolution() const
+Size CameraSensorLegacy::resolution() const
{
return std::min(sizes_.back(), activeArea_.size());
}
-/**
- * \fn CameraSensor::testPatternModes()
- * \brief Retrieve all the supported test pattern modes of the camera sensor
- * The test pattern mode values correspond to the controls::TestPattern control.
- *
- * \return The list of test pattern modes
- */
-
-/**
- * \brief Set the test pattern mode for the camera sensor
- * \param[in] mode The test pattern mode
- *
- * The new \a mode is applied to the sensor if it differs from the active test
- * pattern mode. Otherwise, this function is a no-op. Setting the same test
- * pattern mode for every frame thus incurs no performance penalty.
- */
-int CameraSensor::setTestPatternMode(controls::draft::TestPatternModeEnum mode)
-{
- if (testPatternMode_ == mode)
- return 0;
-
- if (testPatternModes_.empty()) {
- LOG(CameraSensor, Error)
- << "Camera sensor does not support test pattern modes.";
- return -EINVAL;
- }
-
- return applyTestPatternMode(mode);
-}
-
-int CameraSensor::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)
-{
- if (testPatternModes_.empty())
- return 0;
-
- auto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),
- mode);
- if (it == testPatternModes_.end()) {
- LOG(CameraSensor, Error) << "Unsupported test pattern mode "
- << mode;
- return -EINVAL;
- }
-
- LOG(CameraSensor, Debug) << "Apply test pattern mode " << mode;
-
- int32_t index = staticProps_->testPatternModes.at(mode);
- ControlList ctrls{ controls() };
- ctrls.set(V4L2_CID_TEST_PATTERN, index);
-
- int ret = setControls(&ctrls);
- if (ret)
- return ret;
-
- testPatternMode_ = mode;
-
- return 0;
-}
-
-/**
- * \brief Retrieve the best sensor format for a desired output
- * \param[in] mbusCodes The list of acceptable media bus codes
- * \param[in] size The desired size
- *
- * Media bus codes are selected from \a mbusCodes, which lists all acceptable
- * codes in decreasing order of preference. Media bus codes supported by the
- * sensor but not listed in \a mbusCodes are ignored. If none of the desired
- * codes is supported, it returns an error.
- *
- * \a size indicates the desired size at the output of the sensor. This function
- * selects the best media bus code and size supported by the sensor according
- * to the following criteria.
- *
- * - The desired \a size shall fit in the sensor output size to avoid the need
- * to up-scale.
- * - The sensor output size shall match the desired aspect ratio to avoid the
- * need to crop the field of view.
- * - The sensor output size shall be as small as possible to lower the required
- * bandwidth.
- * - The desired \a size shall be supported by one of the media bus code listed
- * in \a mbusCodes.
- *
- * When multiple media bus codes can produce the same size, the code at the
- * lowest position in \a mbusCodes is selected.
- *
- * The use of this function is optional, as the above criteria may not match the
- * needs of all pipeline handlers. Pipeline handlers may implement custom
- * sensor format selection when needed.
- *
- * The returned sensor output format is guaranteed to be acceptable by the
- * setFormat() function without any modification.
- *
- * \return The best sensor output format matching the desired media bus codes
- * and size on success, or an empty format otherwise.
- */
-V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbusCodes,
- const Size &size) const
+V4L2SubdeviceFormat
+CameraSensorLegacy::getFormat(const std::vector<unsigned int> &mbusCodes,
+ const Size &size, Size maxSize) const
{
unsigned int desiredArea = size.width * size.height;
unsigned int bestArea = UINT_MAX;
@@ -742,11 +717,15 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
for (const SizeRange &range : formats->second) {
const Size &sz = range.max;
+ if (!maxSize.isNull() &&
+ (sz.width > maxSize.width || sz.height > maxSize.height))
+ continue;
+
if (sz.width < size.width || sz.height < size.height)
continue;
float ratio = static_cast<float>(sz.width) / sz.height;
- float ratioDiff = fabsf(ratio - desiredRatio);
+ float ratioDiff = std::abs(ratio - desiredRatio);
unsigned int area = sz.width * sz.height;
unsigned int areaDiff = area - desiredArea;
@@ -768,7 +747,7 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
}
V4L2SubdeviceFormat format{
- .mbus_code = bestCode,
+ .code = bestCode,
.size = *bestSize,
.colorSpace = ColorSpace::Raw,
};
@@ -776,22 +755,7 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
return format;
}
-/**
- * \brief Set the sensor output format
- * \param[in] format The desired sensor output format
- * \param[in] transform The transform to be applied on the sensor.
- * Defaults to Identity.
- *
- * If flips are writable they are configured according to the desired Transform.
- * Transform::Identity always corresponds to H/V flip being disabled if the
- * controls are writable. Flips are set before the new format is applied as
- * they can effectively change the Bayer pattern ordering.
- *
- * The ranges of any controls associated with the sensor are also updated.
- *
- * \return 0 on success or a negative error code otherwise
- */
-int CameraSensor::setFormat(V4L2SubdeviceFormat *format, Transform transform)
+int CameraSensorLegacy::setFormat(V4L2SubdeviceFormat *format, Transform transform)
{
/* Configure flips if the sensor supports that. */
if (supportFlips_) {
@@ -812,45 +776,19 @@ int CameraSensor::setFormat(V4L2SubdeviceFormat *format, Transform transform)
if (ret)
return ret;
- updateControlInfo();
+ subdev_->updateControlInfo();
return 0;
}
-/**
- * \brief Try the sensor output format
- * \param[in] format The desired sensor output format
- *
- * The ranges of any controls associated with the sensor are not updated.
- *
- * \todo Add support for Transform by changing the format's Bayer ordering
- * before calling subdev_->setFormat().
- *
- * \return 0 on success or a negative error code otherwise
- */
-int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const
+int CameraSensorLegacy::tryFormat(V4L2SubdeviceFormat *format) const
{
return subdev_->setFormat(pad_, format,
V4L2Subdevice::Whence::TryFormat);
}
-/**
- * \brief Apply a sensor configuration to the camera sensor
- * \param[in] config The sensor configuration
- * \param[in] transform The transform to be applied on the sensor.
- * Defaults to Identity
- * \param[out] sensorFormat Format applied to the sensor (optional)
- *
- * Apply to the camera sensor the configuration \a config.
- *
- * \todo The configuration shall be fully populated and if any of the fields
- * specified cannot be applied exactly, an error code is returned.
- *
- * \return 0 if \a config is applied correctly to the camera sensor, a negative
- * error code otherwise
- */
-int CameraSensor::applyConfiguration(const SensorConfiguration &config,
- Transform transform,
- V4L2SubdeviceFormat *sensorFormat)
+int CameraSensorLegacy::applyConfiguration(const SensorConfiguration &config,
+ Transform transform,
+ V4L2SubdeviceFormat *sensorFormat)
{
if (!config.isValid()) {
LOG(CameraSensor, Error) << "Invalid sensor configuration";
@@ -890,12 +828,12 @@ int CameraSensor::applyConfiguration(const SensorConfiguration &config,
size.height != config.outputSize.height)
continue;
- subdevFormat.mbus_code = code;
+ subdevFormat.code = code;
subdevFormat.size = size;
break;
}
}
- if (!subdevFormat.mbus_code) {
+ if (!subdevFormat.code) {
LOG(CameraSensor, Error) << "Invalid output size in sensor configuration";
return -EINVAL;
}
@@ -917,107 +855,7 @@ int CameraSensor::applyConfiguration(const SensorConfiguration &config,
return 0;
}
-/**
- * \brief Retrieve the supported V4L2 controls and their information
- *
- * Control information is updated automatically to reflect the current sensor
- * configuration when the setFormat() function is called, without invalidating
- * any iterator on the ControlInfoMap. A manual update can also be forced by
- * calling the updateControlInfo() function for pipeline handlers that change
- * the sensor configuration wihtout using setFormat().
- *
- * \return A map of the V4L2 controls supported by the sensor
- */
-const ControlInfoMap &CameraSensor::controls() const
-{
- return subdev_->controls();
-}
-
-/**
- * \brief Read V4L2 controls from the sensor
- * \param[in] ids The list of controls to read, specified by their ID
- *
- * This function reads the value of all controls contained in \a ids, and
- * returns their values as a ControlList. The control identifiers are defined by
- * the V4L2 specification (V4L2_CID_*).
- *
- * If any control in \a ids is not supported by the device, is disabled (i.e.
- * has the V4L2_CTRL_FLAG_DISABLED flag set), or if any other error occurs
- * during validation of the requested controls, no control is read and this
- * function returns an empty control list.
- *
- * \sa V4L2Device::getControls()
- *
- * \return The control values in a ControlList on success, or an empty list on
- * error
- */
-ControlList CameraSensor::getControls(const std::vector<uint32_t> &ids)
-{
- return subdev_->getControls(ids);
-}
-
-/**
- * \brief Write V4L2 controls to the sensor
- * \param[in] ctrls The list of controls to write
- *
- * This function writes the value of all controls contained in \a ctrls, and
- * stores the values actually applied to the device in the corresponding \a
- * ctrls entry. The control identifiers are defined by the V4L2 specification
- * (V4L2_CID_*).
- *
- * If any control in \a ctrls is not supported by the device, is disabled (i.e.
- * has the V4L2_CTRL_FLAG_DISABLED flag set), is read-only, or if any other
- * error occurs during validation of the requested controls, no control is
- * written and this function returns -EINVAL.
- *
- * If an error occurs while writing the controls, the index of the first
- * control that couldn't be written is returned. All controls below that index
- * are written and their values are updated in \a ctrls, while all other
- * controls are not written and their values are not changed.
- *
- * \sa V4L2Device::setControls()
- *
- * \return 0 on success or an error code otherwise
- * \retval -EINVAL One of the control is not supported or not accessible
- * \retval i The index of the control that failed
- */
-int CameraSensor::setControls(ControlList *ctrls)
-{
- return subdev_->setControls(ctrls);
-}
-
-/**
- * \fn CameraSensor::device()
- * \brief Retrieve the camera sensor device
- * \todo Remove this function by integrating DelayedControl with CameraSensor
- * \return The camera sensor device
- */
-
-/**
- * \fn CameraSensor::properties()
- * \brief Retrieve the camera sensor properties
- * \return The list of camera sensor properties
- */
-
-/**
- * \brief Assemble and return the camera sensor info
- * \param[out] info The camera sensor info
- *
- * This function fills \a info with information that describes the camera sensor
- * and its current configuration. The information combines static data (such as
- * the the sensor model or active pixel array size) and data specific to the
- * current sensor configuration (such as the line length and pixel rate).
- *
- * Sensor information is only available for raw sensors. When called for a YUV
- * sensor, this function returns -EINVAL.
- *
- * Pipeline handlers that do not change the sensor format using the setFormat()
- * function may need to call updateControlInfo() beforehand, to ensure all the
- * control ranges are up to date.
- *
- * \return 0 on success, a negative error code otherwise
- */
-int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const
+int CameraSensorLegacy::sensorInfo(IPACameraSensorInfo *info) const
{
if (!bayerFormat_)
return -EINVAL;
@@ -1058,7 +896,7 @@ int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const
ret = subdev_->getFormat(pad_, &format);
if (ret)
return ret;
- info->bitsPerPixel = format.bitsPerPixel();
+ info->bitsPerPixel = MediaBusFormatInfo::info(format.code).bitsPerPixel;
info->outputSize = format.size;
std::optional<int32_t> cfa = properties_.get(properties::draft::ColorFilterArrangement);
@@ -1091,51 +929,7 @@ int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const
return 0;
}
-/**
- * \fn void CameraSensor::updateControlInfo()
- * \brief Update the sensor's ControlInfoMap in case they have changed
- * \sa V4L2Device::updateControlInfo()
- */
-void CameraSensor::updateControlInfo()
-{
- subdev_->updateControlInfo();
-}
-
-/**
- * \fn CameraSensor::focusLens()
- * \brief Retrieve the focus lens controller
- *
- * \return The focus lens controller. nullptr if no focus lens controller is
- * connected to the sensor
- */
-
-/**
- * \brief Compute the Transform that gives the requested \a orientation
- * \param[inout] orientation The desired image orientation
- *
- * This function computes the Transform that the pipeline handler should apply
- * to the CameraSensor to obtain the requested \a orientation.
- *
- * The intended caller of this function is the validate() implementation of
- * pipeline handlers, that pass in the application requested
- * CameraConfiguration::orientation and obtain a Transform to apply to the
- * camera sensor, likely at configure() time.
- *
- * If the requested \a orientation cannot be obtained, the \a orientation
- * parameter is adjusted to report the current image orientation and
- * Transform::Identity is returned.
- *
- * If the requested \a orientation can be obtained, the function computes a
- * Transform and does not adjust \a orientation.
- *
- * Pipeline handlers are expected to verify if \a orientation has been
- * adjusted by this function and set the CameraConfiguration::status to
- * Adjusted accordingly.
- *
- * \return A Transform instance that applied to the CameraSensor produces images
- * with \a orientation
- */
-Transform CameraSensor::computeTransform(Orientation *orientation) const
+Transform CameraSensorLegacy::computeTransform(Orientation *orientation) const
{
/*
* If we cannot do any flips we cannot change the native camera mounting
@@ -1168,33 +962,84 @@ Transform CameraSensor::computeTransform(Orientation *orientation) const
return transform;
}
-std::string CameraSensor::logPrefix() const
+BayerFormat::Order CameraSensorLegacy::bayerOrder(Transform t) const
{
- return "'" + entity_->name() + "'";
+ /* Return a defined by meaningless value for non-Bayer sensors. */
+ if (!bayerFormat_)
+ return BayerFormat::Order::BGGR;
+
+ if (!flipsAlterBayerOrder_)
+ return bayerFormat_->order;
+
+ /*
+ * Apply the transform to the native (i.e. untransformed) Bayer order,
+ * using the rest of the Bayer format supplied by the caller.
+ */
+ return bayerFormat_->transform(t).order;
}
-int CameraSensor::generateId()
+const ControlInfoMap &CameraSensorLegacy::controls() const
{
- const std::string devPath = subdev_->devicePath();
+ return subdev_->controls();
+}
- /* Try to get ID from firmware description. */
- id_ = sysfs::firmwareNodePath(devPath);
- if (!id_.empty())
+ControlList CameraSensorLegacy::getControls(const std::vector<uint32_t> &ids)
+{
+ return subdev_->getControls(ids);
+}
+
+int CameraSensorLegacy::setControls(ControlList *ctrls)
+{
+ return subdev_->setControls(ctrls);
+}
+
+int CameraSensorLegacy::setTestPatternMode(controls::draft::TestPatternModeEnum mode)
+{
+ if (testPatternMode_ == mode)
return 0;
- /*
- * Virtual sensors not described in firmware
- *
- * Verify it's a platform device and construct ID from the device path
- * and model of sensor.
- */
- if (devPath.find("/sys/devices/platform/", 0) == 0) {
- id_ = devPath.substr(strlen("/sys/devices/")) + " " + model();
+ if (testPatternModes_.empty()) {
+ LOG(CameraSensor, Error)
+ << "Camera sensor does not support test pattern modes.";
+ return -EINVAL;
+ }
+
+ return applyTestPatternMode(mode);
+}
+
+int CameraSensorLegacy::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)
+{
+ if (testPatternModes_.empty())
return 0;
+
+ auto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),
+ mode);
+ if (it == testPatternModes_.end()) {
+ LOG(CameraSensor, Error) << "Unsupported test pattern mode "
+ << mode;
+ return -EINVAL;
}
- LOG(CameraSensor, Error) << "Can't generate sensor ID";
- return -EINVAL;
+ LOG(CameraSensor, Debug) << "Apply test pattern mode " << mode;
+
+ int32_t index = staticProps_->testPatternModes.at(mode);
+ ControlList ctrls{ controls() };
+ ctrls.set(V4L2_CID_TEST_PATTERN, index);
+
+ int ret = setControls(&ctrls);
+ if (ret)
+ return ret;
+
+ testPatternMode_ = mode;
+
+ return 0;
}
+std::string CameraSensorLegacy::logPrefix() const
+{
+ return "'" + entity_->name() + "'";
+}
+
+REGISTER_CAMERA_SENSOR(CameraSensorLegacy, -100)
+
} /* namespace libcamera */
diff --git a/src/libcamera/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp
index 27d6799a..c9e9e148 100644
--- a/src/libcamera/camera_sensor_properties.cpp
+++ b/src/libcamera/sensor/camera_sensor_properties.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * camera_sensor_properties.cpp - Database of camera sensor properties
+ * Database of camera sensor properties
*/
#include "libcamera/internal/camera_sensor_properties.h"
@@ -41,6 +41,35 @@ LOG_DEFINE_CATEGORY(CameraSensorProperties)
* \brief Map that associates the TestPattern control value with the indexes of
* the corresponding sensor test pattern modes as returned by
* V4L2_CID_TEST_PATTERN.
+ *
+ * \var CameraSensorProperties::sensorDelays
+ * \brief Sensor control application delays
+ *
+ * This structure may be defined as empty if the actual sensor delays are not
+ * available or have not been measured.
+ */
+
+/**
+ * \struct CameraSensorProperties::SensorDelays
+ * \brief Sensor control application delay values
+ *
+ * This structure holds delay values, expressed in number of frames, between the
+ * time a control value is applied to the sensor and the time that value is
+ * reflected in the output. For example "2 frames delay" means that parameters
+ * set during frame N will take effect for frame N+2 (and by extension a delay
+ * of 0 would mean the parameter is applied immediately to the current frame).
+ *
+ * \var CameraSensorProperties::SensorDelays::exposureDelay
+ * \brief Number of frames between application of exposure control and effect
+ *
+ * \var CameraSensorProperties::SensorDelays::gainDelay
+ * \brief Number of frames between application of analogue gain control and effect
+ *
+ * \var CameraSensorProperties::SensorDelays::vblankDelay
+ * \brief Number of frames between application of vblank control and effect
+ *
+ * \var CameraSensorProperties::SensorDelays::hblankDelay
+ * \brief Number of frames between application of hblank control and effect
*/
/**
@@ -52,6 +81,21 @@ LOG_DEFINE_CATEGORY(CameraSensorProperties)
const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor)
{
static const std::map<std::string, const CameraSensorProperties> sensorProps = {
+ { "ar0144", {
+ .unitCellSize = { 3000, 3000 },
+ .testPatternModes = {
+ { controls::draft::TestPatternModeOff, 0 },
+ { controls::draft::TestPatternModeSolidColor, 1 },
+ { controls::draft::TestPatternModeColorBars, 2 },
+ { controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
+ },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
+ } },
{ "ar0521", {
.unitCellSize = { 2200, 2200 },
.testPatternModes = {
@@ -60,6 +104,33 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeColorBars, 2 },
{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
},
+ .sensorDelays = { },
+ } },
+ { "gc05a2", {
+ .unitCellSize = { 1120, 1120 },
+ .testPatternModes = {
+ { controls::draft::TestPatternModeOff, 0 },
+ { controls::draft::TestPatternModeColorBars, 1 },
+ },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
+ } },
+ { "gc08a3", {
+ .unitCellSize = { 1120, 1120 },
+ .testPatternModes = {
+ { controls::draft::TestPatternModeOff, 0 },
+ { controls::draft::TestPatternModeColorBars, 2 },
+ },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "hi846", {
.unitCellSize = { 1120, 1120 },
@@ -78,6 +149,18 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* 9: "Resolution Pattern"
*/
},
+ .sensorDelays = { },
+ } },
+ { "imx214", {
+ .unitCellSize = { 1120, 1120 },
+ .testPatternModes = {
+ { controls::draft::TestPatternModeOff, 0 },
+ { controls::draft::TestPatternModeColorBars, 1 },
+ { controls::draft::TestPatternModeSolidColor, 2 },
+ { controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
+ { controls::draft::TestPatternModePn9, 4 },
+ },
+ .sensorDelays = { },
} },
{ "imx219", {
.unitCellSize = { 1120, 1120 },
@@ -88,6 +171,12 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
{ controls::draft::TestPatternModePn9, 4 },
},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 1,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "imx258", {
.unitCellSize = { 1120, 1120 },
@@ -98,22 +187,72 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
{ controls::draft::TestPatternModePn9, 4 },
},
+ .sensorDelays = { },
+ } },
+ { "imx283", {
+ .unitCellSize = { 2400, 2400 },
+ .testPatternModes = {},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 1,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "imx290", {
.unitCellSize = { 2900, 2900 },
.testPatternModes = {},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "imx296", {
.unitCellSize = { 3450, 3450 },
.testPatternModes = {},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "imx327", {
.unitCellSize = { 2900, 2900 },
.testPatternModes = {},
+ .sensorDelays = { },
+ } },
+ { "imx335", {
+ .unitCellSize = { 2000, 2000 },
+ .testPatternModes = {},
+ .sensorDelays = { },
+ } },
+ { "imx415", {
+ .unitCellSize = { 1450, 1450 },
+ .testPatternModes = {},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
+ } },
+ { "imx462", {
+ .unitCellSize = { 2900, 2900 },
+ .testPatternModes = {},
+ .sensorDelays = { },
} },
{ "imx477", {
.unitCellSize = { 1550, 1550 },
.testPatternModes = {},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 3,
+ .hblankDelay = 3
+ },
} },
{ "imx519", {
.unitCellSize = { 1220, 1220 },
@@ -126,6 +265,12 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* these two patterns do not comply with MIPI CCS v1.1 (Section 10.1).
*/
},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 3,
+ .hblankDelay = 3
+ },
} },
{ "imx708", {
.unitCellSize = { 1400, 1400 },
@@ -136,6 +281,12 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeColorBarsFadeToGray, 3 },
{ controls::draft::TestPatternModePn9, 4 },
},
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 3,
+ .hblankDelay = 3
+ },
} },
{ "ov2685", {
.unitCellSize = { 1750, 1750 },
@@ -150,6 +301,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* 5: "Color Square"
*/
},
+ .sensorDelays = { },
} },
{ "ov2740", {
.unitCellSize = { 1400, 1400 },
@@ -157,6 +309,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeOff, 0 },
{ controls::draft::TestPatternModeColorBars, 1},
},
+ .sensorDelays = { },
} },
{ "ov4689", {
.unitCellSize = { 2000, 2000 },
@@ -170,6 +323,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* colorBarType2 and colorBarType3.
*/
},
+ .sensorDelays = { },
} },
{ "ov5640", {
.unitCellSize = { 1400, 1400 },
@@ -177,10 +331,25 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeOff, 0 },
{ controls::draft::TestPatternModeColorBars, 1 },
},
+ .sensorDelays = { },
} },
{ "ov5647", {
.unitCellSize = { 1400, 1400 },
.testPatternModes = {},
+ /*
+ * 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".
+ *
+ * \todo Verify these delays properly, as the upstream
+ * driver appears to configure _no_ delay.
+ */
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "ov5670", {
.unitCellSize = { 1120, 1120 },
@@ -188,6 +357,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeOff, 0 },
{ controls::draft::TestPatternModeColorBars, 1 },
},
+ .sensorDelays = { },
} },
{ "ov5675", {
.unitCellSize = { 1120, 1120 },
@@ -195,6 +365,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeOff, 0 },
{ controls::draft::TestPatternModeColorBars, 1 },
},
+ .sensorDelays = { },
} },
{ "ov5693", {
.unitCellSize = { 1400, 1400 },
@@ -207,6 +378,36 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* Rolling Bar".
*/
},
+ .sensorDelays = { },
+ } },
+ { "ov64a40", {
+ .unitCellSize = { 1008, 1008 },
+ .testPatternModes = {
+ { controls::draft::TestPatternModeOff, 0 },
+ { controls::draft::TestPatternModeColorBars, 1 },
+ { controls::draft::TestPatternModeColorBarsFadeToGray, 2 },
+ /*
+ * No corresponding test patter mode
+ * 3: "Vertical Color Bar Type 3",
+ * 4: "Vertical Color Bar Type 4"
+ */
+ },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
+ } },
+ { "ov7251", {
+ .unitCellSize = { 3000, 3000 },
+ .testPatternModes = { },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "ov8858", {
.unitCellSize = { 1120, 1120 },
@@ -220,6 +421,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* 4: "Vertical Color Bar Type 4"
*/
},
+ .sensorDelays = { },
} },
{ "ov8865", {
.unitCellSize = { 1400, 1400 },
@@ -234,6 +436,17 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
* 5: "Color squares with rolling bar"
*/
},
+ .sensorDelays = { },
+ } },
+ { "ov9281", {
+ .unitCellSize = { 3000, 3000 },
+ .testPatternModes = { },
+ .sensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 2,
+ .vblankDelay = 2,
+ .hblankDelay = 2
+ },
} },
{ "ov13858", {
.unitCellSize = { 1120, 1120 },
@@ -241,6 +454,7 @@ const CameraSensorProperties *CameraSensorProperties::get(const std::string &sen
{ controls::draft::TestPatternModeOff, 0 },
{ controls::draft::TestPatternModeColorBars, 1 },
},
+ .sensorDelays = { },
} },
};
diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp
new file mode 100644
index 00000000..ab75b1f8
--- /dev/null
+++ b/src/libcamera/sensor/camera_sensor_raw.cpp
@@ -0,0 +1,1157 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas on Board Oy.
+ *
+ * camera_sensor_raw.cpp - A raw camera sensor using the V4L2 streams API
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <float.h>
+#include <iomanip>
+#include <limits.h>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string.h>
+#include <string>
+#include <vector>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
+#include <libcamera/camera.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+#include <libcamera/orientation.h>
+#include <libcamera/property_ids.h>
+#include <libcamera/transform.h>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_lens.h"
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/camera_sensor_properties.h"
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/media_device.h"
+#include "libcamera/internal/sysfs.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+
+namespace libcamera {
+
+class BayerFormat;
+class CameraLens;
+class MediaEntity;
+class SensorConfiguration;
+
+struct CameraSensorProperties;
+
+enum class Orientation;
+
+LOG_DECLARE_CATEGORY(CameraSensor)
+
+class CameraSensorRaw : public CameraSensor, protected Loggable
+{
+public:
+ CameraSensorRaw(const MediaEntity *entity);
+ ~CameraSensorRaw();
+
+ static std::variant<std::unique_ptr<CameraSensor>, int>
+ match(MediaEntity *entity);
+
+ const std::string &model() const override { return model_; }
+ const std::string &id() const override { return id_; }
+
+ const MediaEntity *entity() const override { return entity_; }
+ V4L2Subdevice *device() override { return subdev_.get(); }
+
+ CameraLens *focusLens() override { return focusLens_.get(); }
+
+ const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
+ std::vector<Size> sizes(unsigned int mbusCode) const override;
+ Size resolution() const override;
+
+ V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
+ const Size &size,
+ const Size maxSize) const override;
+ int setFormat(V4L2SubdeviceFormat *format,
+ Transform transform = Transform::Identity) override;
+ int tryFormat(V4L2SubdeviceFormat *format) const override;
+
+ int applyConfiguration(const SensorConfiguration &config,
+ Transform transform = Transform::Identity,
+ V4L2SubdeviceFormat *sensorFormat = nullptr) override;
+
+ V4L2Subdevice::Stream imageStream() const override;
+ std::optional<V4L2Subdevice::Stream> embeddedDataStream() const override;
+ V4L2SubdeviceFormat embeddedDataFormat() const override;
+ int setEmbeddedDataEnabled(bool enable) override;
+
+ const ControlList &properties() const override { return properties_; }
+ int sensorInfo(IPACameraSensorInfo *info) const override;
+ Transform computeTransform(Orientation *orientation) const override;
+ BayerFormat::Order bayerOrder(Transform t) const override;
+
+ const ControlInfoMap &controls() const override;
+ ControlList getControls(const std::vector<uint32_t> &ids) override;
+ int setControls(ControlList *ctrls) override;
+
+ const std::vector<controls::draft::TestPatternModeEnum> &
+ testPatternModes() const override { return testPatternModes_; }
+ int setTestPatternMode(controls::draft::TestPatternModeEnum mode) override;
+ const CameraSensorProperties::SensorDelays &sensorDelays() override;
+
+protected:
+ std::string logPrefix() const override;
+
+private:
+ LIBCAMERA_DISABLE_COPY(CameraSensorRaw)
+
+ std::optional<int> init();
+ int initProperties();
+ void initStaticProperties();
+ void initTestPatternModes();
+ int applyTestPatternMode(controls::draft::TestPatternModeEnum mode);
+
+ const MediaEntity *entity_;
+ std::unique_ptr<V4L2Subdevice> subdev_;
+
+ struct Streams {
+ V4L2Subdevice::Stream sink;
+ V4L2Subdevice::Stream source;
+ };
+
+ struct {
+ Streams image;
+ std::optional<Streams> edata;
+ } streams_;
+
+ const CameraSensorProperties *staticProps_;
+
+ std::string model_;
+ std::string id_;
+
+ V4L2Subdevice::Formats formats_;
+ std::vector<unsigned int> mbusCodes_;
+ std::vector<Size> sizes_;
+ std::vector<controls::draft::TestPatternModeEnum> testPatternModes_;
+ controls::draft::TestPatternModeEnum testPatternMode_;
+
+ Size pixelArraySize_;
+ Rectangle activeArea_;
+ BayerFormat::Order cfaPattern_;
+ bool supportFlips_;
+ bool flipsAlterBayerOrder_;
+ Orientation mountingOrientation_;
+
+ ControlList properties_;
+
+ std::unique_ptr<CameraLens> focusLens_;
+};
+
+/**
+ * \class CameraSensorRaw
+ * \brief A camera sensor based on V4L2 subdevices
+ *
+ * This class supports single-subdev sensors with a single source pad and one
+ * or two internal sink pads (for the image and embedded data streams).
+ */
+
+CameraSensorRaw::CameraSensorRaw(const MediaEntity *entity)
+ : entity_(entity), staticProps_(nullptr), supportFlips_(false),
+ flipsAlterBayerOrder_(false), properties_(properties::properties)
+{
+}
+
+CameraSensorRaw::~CameraSensorRaw() = default;
+
+std::variant<std::unique_ptr<CameraSensor>, int>
+CameraSensorRaw::match(MediaEntity *entity)
+{
+ /* Check the entity type. */
+ if (entity->type() != MediaEntity::Type::V4L2Subdevice ||
+ entity->function() != MEDIA_ENT_F_CAM_SENSOR) {
+ libcamera::LOG(CameraSensor, Debug)
+ << entity->name() << ": unsupported entity type ("
+ << utils::to_underlying(entity->type())
+ << ") or function (" << utils::hex(entity->function()) << ")";
+ return { 0 };
+ }
+
+ /* Count and check the number of pads. */
+ static constexpr uint32_t kPadFlagsMask = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_SOURCE
+ | MEDIA_PAD_FL_INTERNAL;
+ unsigned int numSinks = 0;
+ unsigned int numSources = 0;
+
+ for (const MediaPad *pad : entity->pads()) {
+ switch (pad->flags() & kPadFlagsMask) {
+ case MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL:
+ numSinks++;
+ break;
+
+ case MEDIA_PAD_FL_SOURCE:
+ numSources++;
+ break;
+
+ default:
+ libcamera::LOG(CameraSensor, Debug)
+ << entity->name() << ": unsupported pad " << pad->index()
+ << " type " << utils::hex(pad->flags());
+ return { 0 };
+ }
+ }
+
+ if (numSinks < 1 || numSinks > 2 || numSources != 1) {
+ libcamera::LOG(CameraSensor, Debug)
+ << entity->name() << ": unsupported number of sinks ("
+ << numSinks << ") or sources (" << numSources << ")";
+ return { 0 };
+ }
+
+ /*
+ * The entity matches. Create the camera sensor and initialize it. The
+ * init() function will perform further match checks.
+ */
+ std::unique_ptr<CameraSensorRaw> sensor =
+ std::make_unique<CameraSensorRaw>(entity);
+
+ std::optional<int> err = sensor->init();
+ if (err)
+ return { *err };
+
+ return { std::move(sensor) };
+}
+
+std::optional<int> CameraSensorRaw::init()
+{
+ /* Create and open the subdev. */
+ subdev_ = std::make_unique<V4L2Subdevice>(entity_);
+ int ret = subdev_->open();
+ if (ret)
+ return { ret };
+
+ /*
+ * 1. Identify the pads.
+ */
+
+ /*
+ * First locate the source pad. The match() function guarantees there
+ * is one and only one source pad.
+ */
+ unsigned int sourcePad = UINT_MAX;
+
+ for (const MediaPad *pad : entity_->pads()) {
+ if (pad->flags() & MEDIA_PAD_FL_SOURCE) {
+ sourcePad = pad->index();
+ break;
+ }
+ }
+
+ /*
+ * Iterate over the routes to identify the streams on the source pad,
+ * and the internal sink pads.
+ */
+ V4L2Subdevice::Routing routing = {};
+ ret = subdev_->getRouting(&routing, V4L2Subdevice::TryFormat);
+ if (ret)
+ return { ret };
+
+ bool imageStreamFound = false;
+
+ for (const V4L2Subdevice::Route &route : routing) {
+ if (route.source.pad != sourcePad) {
+ LOG(CameraSensor, Error) << "Invalid route " << route;
+ return { -EINVAL };
+ }
+
+ /* Identify the stream type based on the supported formats. */
+ V4L2Subdevice::Formats formats = subdev_->formats(route.source);
+
+ std::optional<MediaBusFormatInfo::Type> type;
+
+ for (const auto &[code, sizes] : formats) {
+ const MediaBusFormatInfo &info =
+ MediaBusFormatInfo::info(code);
+ if (info.isValid()) {
+ type = info.type;
+ break;
+ }
+ }
+
+ if (!type) {
+ LOG(CameraSensor, Warning)
+ << "No known format on pad " << route.source;
+ continue;
+ }
+
+ switch (*type) {
+ case MediaBusFormatInfo::Type::Image:
+ if (imageStreamFound) {
+ LOG(CameraSensor, Error)
+ << "Multiple internal image streams ("
+ << streams_.image.sink << " and "
+ << route.sink << ")";
+ return { -EINVAL };
+ }
+
+ imageStreamFound = true;
+ streams_.image.sink = route.sink;
+ streams_.image.source = route.source;
+ break;
+
+ case MediaBusFormatInfo::Type::Metadata:
+ /*
+ * Skip metadata streams that are not sensor embedded
+ * data. The source stream reports a generic metadata
+ * format, check the sink stream for the exact format.
+ */
+ formats = subdev_->formats(route.sink);
+ if (formats.size() != 1)
+ continue;
+
+ if (MediaBusFormatInfo::info(formats.cbegin()->first).type !=
+ MediaBusFormatInfo::Type::EmbeddedData)
+ continue;
+
+ if (streams_.edata) {
+ LOG(CameraSensor, Error)
+ << "Multiple internal embedded data streams ("
+ << streams_.edata->sink << " and "
+ << route.sink << ")";
+ return { -EINVAL };
+ }
+
+ streams_.edata = { route.sink, route.source };
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!imageStreamFound) {
+ LOG(CameraSensor, Error) << "No image stream found";
+ return { -EINVAL };
+ }
+
+ LOG(CameraSensor, Debug)
+ << "Found image stream " << streams_.image.sink
+ << " -> " << streams_.image.source;
+
+ if (streams_.edata)
+ LOG(CameraSensor, Debug)
+ << "Found embedded data stream " << streams_.edata->sink
+ << " -> " << streams_.edata->source;
+
+ /*
+ * 2. Enumerate and cache the media bus codes, sizes and colour filter
+ * array order for the image stream.
+ */
+
+ /*
+ * Get the native sensor CFA pattern. It is simpler to retrieve it from
+ * the internal image sink pad as it is guaranteed to expose a single
+ * format, and is not affected by flips.
+ */
+ V4L2Subdevice::Formats formats = subdev_->formats(streams_.image.sink);
+ if (formats.size() != 1) {
+ LOG(CameraSensor, Error)
+ << "Image pad has " << formats.size()
+ << " formats, expected 1";
+ return { -EINVAL };
+ }
+
+ uint32_t nativeFormat = formats.cbegin()->first;
+ const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(nativeFormat);
+ if (!bayerFormat.isValid()) {
+ LOG(CameraSensor, Error)
+ << "Invalid native format " << nativeFormat;
+ return { 0 };
+ }
+
+ cfaPattern_ = bayerFormat.order;
+
+ /*
+ * Retrieve and cache the media bus codes and sizes on the source image
+ * stream.
+ */
+ formats_ = subdev_->formats(streams_.image.source);
+ if (formats_.empty()) {
+ LOG(CameraSensor, Error) << "No image format found";
+ return { -EINVAL };
+ }
+
+ /* Populate and sort the media bus codes and the sizes. */
+ for (const auto &[code, ranges] : formats_) {
+ /* Drop non-raw formats (in case we have a hybrid sensor). */
+ const MediaBusFormatInfo &info = MediaBusFormatInfo::info(code);
+ if (info.colourEncoding != PixelFormatInfo::ColourEncodingRAW)
+ continue;
+
+ mbusCodes_.push_back(code);
+ std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes_),
+ [](const SizeRange &range) { return range.max; });
+ }
+
+ if (mbusCodes_.empty()) {
+ LOG(CameraSensor, Debug) << "No raw image formats found";
+ return { 0 };
+ }
+
+ std::sort(mbusCodes_.begin(), mbusCodes_.end());
+ std::sort(sizes_.begin(), sizes_.end());
+
+ /*
+ * Remove duplicate sizes. There are no duplicate media bus codes as
+ * they are the keys in the formats map.
+ */
+ auto last = std::unique(sizes_.begin(), sizes_.end());
+ sizes_.erase(last, sizes_.end());
+
+ /*
+ * 3. Query selection rectangles. Retrieve properties, and verify that
+ * all the expected selection rectangles are supported.
+ */
+
+ Rectangle rect;
+ ret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP_BOUNDS,
+ &rect);
+ if (ret) {
+ LOG(CameraSensor, Error) << "No pixel array crop bounds";
+ return { ret };
+ }
+
+ pixelArraySize_ = rect.size();
+
+ ret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP_DEFAULT,
+ &activeArea_);
+ if (ret) {
+ LOG(CameraSensor, Error) << "No pixel array crop default";
+ return { ret };
+ }
+
+ ret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP,
+ &rect);
+ if (ret) {
+ LOG(CameraSensor, Error) << "No pixel array crop rectangle";
+ return { ret };
+ }
+
+ /*
+ * 4. Verify that all required controls are present.
+ */
+
+ const ControlIdMap &controls = subdev_->controls().idmap();
+
+ static constexpr uint32_t mandatoryControls[] = {
+ V4L2_CID_ANALOGUE_GAIN,
+ V4L2_CID_CAMERA_ORIENTATION,
+ V4L2_CID_EXPOSURE,
+ V4L2_CID_HBLANK,
+ V4L2_CID_PIXEL_RATE,
+ V4L2_CID_VBLANK,
+ };
+
+ ret = 0;
+
+ for (uint32_t ctrl : mandatoryControls) {
+ if (!controls.count(ctrl)) {
+ LOG(CameraSensor, Error)
+ << "Mandatory V4L2 control " << utils::hex(ctrl)
+ << " not available";
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret) {
+ LOG(CameraSensor, Error)
+ << "The sensor kernel driver needs to be fixed";
+ LOG(CameraSensor, Error)
+ << "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information";
+ return { ret };
+ }
+
+ /*
+ * Verify if sensor supports horizontal/vertical flips
+ *
+ * \todo Handle horizontal and vertical flips independently.
+ */
+ const struct v4l2_query_ext_ctrl *hflipInfo = subdev_->controlInfo(V4L2_CID_HFLIP);
+ const struct v4l2_query_ext_ctrl *vflipInfo = subdev_->controlInfo(V4L2_CID_VFLIP);
+ if (hflipInfo && !(hflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY) &&
+ vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+ supportFlips_ = true;
+
+ if (hflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT ||
+ vflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT)
+ flipsAlterBayerOrder_ = true;
+ }
+
+ if (!supportFlips_)
+ LOG(CameraSensor, Debug)
+ << "Camera sensor does not support horizontal/vertical flip";
+
+ /*
+ * 5. Discover ancillary devices.
+ *
+ * \todo This code may be shared by different V4L2 sensor classes.
+ */
+ for (MediaEntity *ancillary : entity_->ancillaryEntities()) {
+ switch (ancillary->function()) {
+ case MEDIA_ENT_F_LENS:
+ focusLens_ = std::make_unique<CameraLens>(ancillary);
+ ret = focusLens_->init();
+ if (ret) {
+ LOG(CameraSensor, Error)
+ << "Lens initialisation failed, lens disabled";
+ focusLens_.reset();
+ }
+ break;
+
+ default:
+ LOG(CameraSensor, Warning)
+ << "Unsupported ancillary entity function "
+ << ancillary->function();
+ break;
+ }
+ }
+
+ /*
+ * 6. Initialize properties.
+ */
+
+ ret = initProperties();
+ if (ret)
+ return { ret };
+
+ /*
+ * 7. Initialize controls.
+ */
+
+ /*
+ * Set HBLANK to the minimum to start with a well-defined line length,
+ * allowing IPA modules that do not modify HBLANK to use the sensor
+ * minimum line length in their calculations.
+ */
+ const struct v4l2_query_ext_ctrl *hblankInfo = subdev_->controlInfo(V4L2_CID_HBLANK);
+ if (hblankInfo && !(hblankInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+ ControlList ctrl(subdev_->controls());
+
+ ctrl.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblankInfo->minimum));
+ ret = subdev_->setControls(&ctrl);
+ if (ret)
+ return ret;
+ }
+
+ ret = applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff);
+ if (ret)
+ return { ret };
+
+ return {};
+}
+
+int CameraSensorRaw::initProperties()
+{
+ model_ = subdev_->model();
+ properties_.set(properties::Model, utils::toAscii(model_));
+
+ /* Generate a unique ID for the sensor. */
+ id_ = sysfs::firmwareNodePath(subdev_->devicePath());
+ if (id_.empty()) {
+ LOG(CameraSensor, Error) << "Can't generate sensor ID";
+ return -EINVAL;
+ }
+
+ /* Initialize the static properties from the sensor database. */
+ initStaticProperties();
+
+ /* Retrieve and register properties from the kernel interface. */
+ const ControlInfoMap &controls = subdev_->controls();
+
+ const auto &orientation = controls.find(V4L2_CID_CAMERA_ORIENTATION);
+ if (orientation != controls.end()) {
+ int32_t v4l2Orientation = orientation->second.def().get<int32_t>();
+ int32_t propertyValue;
+
+ switch (v4l2Orientation) {
+ default:
+ LOG(CameraSensor, Warning)
+ << "Unsupported camera location "
+ << v4l2Orientation << ", setting to External";
+ [[fallthrough]];
+ case V4L2_CAMERA_ORIENTATION_EXTERNAL:
+ propertyValue = properties::CameraLocationExternal;
+ break;
+ case V4L2_CAMERA_ORIENTATION_FRONT:
+ propertyValue = properties::CameraLocationFront;
+ break;
+ case V4L2_CAMERA_ORIENTATION_BACK:
+ propertyValue = properties::CameraLocationBack;
+ break;
+ }
+ properties_.set(properties::Location, propertyValue);
+ } else {
+ LOG(CameraSensor, Warning) << "Failed to retrieve the camera location";
+ }
+
+ const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION);
+ if (rotationControl != controls.end()) {
+ int32_t propertyValue = rotationControl->second.def().get<int32_t>();
+
+ /*
+ * Cache the Transform associated with the camera mounting
+ * rotation for later use in computeTransform().
+ */
+ bool success;
+ mountingOrientation_ = orientationFromRotation(propertyValue, &success);
+ if (!success) {
+ LOG(CameraSensor, Warning)
+ << "Invalid rotation of " << propertyValue
+ << " degrees - ignoring";
+ mountingOrientation_ = Orientation::Rotate0;
+ }
+
+ properties_.set(properties::Rotation, propertyValue);
+ } else {
+ LOG(CameraSensor, Warning)
+ << "Rotation control not available, default to 0 degrees";
+ properties_.set(properties::Rotation, 0);
+ mountingOrientation_ = Orientation::Rotate0;
+ }
+
+ properties_.set(properties::PixelArraySize, pixelArraySize_);
+ properties_.set(properties::PixelArrayActiveAreas, { activeArea_ });
+
+ /* Color filter array pattern. */
+ uint32_t cfa;
+
+ switch (cfaPattern_) {
+ case BayerFormat::BGGR:
+ cfa = properties::draft::BGGR;
+ break;
+ case BayerFormat::GBRG:
+ cfa = properties::draft::GBRG;
+ break;
+ case BayerFormat::GRBG:
+ cfa = properties::draft::GRBG;
+ break;
+ case BayerFormat::RGGB:
+ cfa = properties::draft::RGGB;
+ break;
+ case BayerFormat::MONO:
+ default:
+ cfa = properties::draft::MONO;
+ break;
+ }
+
+ properties_.set(properties::draft::ColorFilterArrangement, cfa);
+
+ return 0;
+}
+
+void CameraSensorRaw::initStaticProperties()
+{
+ staticProps_ = CameraSensorProperties::get(model_);
+ if (!staticProps_)
+ return;
+
+ /* Register the properties retrieved from the sensor database. */
+ properties_.set(properties::UnitCellSize, staticProps_->unitCellSize);
+
+ initTestPatternModes();
+}
+
+const CameraSensorProperties::SensorDelays &CameraSensorRaw::sensorDelays()
+{
+ static constexpr CameraSensorProperties::SensorDelays defaultSensorDelays = {
+ .exposureDelay = 2,
+ .gainDelay = 1,
+ .vblankDelay = 2,
+ .hblankDelay = 2,
+ };
+
+ if (!staticProps_ ||
+ (!staticProps_->sensorDelays.exposureDelay &&
+ !staticProps_->sensorDelays.gainDelay &&
+ !staticProps_->sensorDelays.vblankDelay &&
+ !staticProps_->sensorDelays.hblankDelay)) {
+ LOG(CameraSensor, Warning)
+ << "No sensor delays found in static properties. "
+ "Assuming unverified defaults.";
+
+ return defaultSensorDelays;
+ }
+
+ return staticProps_->sensorDelays;
+}
+
+void CameraSensorRaw::initTestPatternModes()
+{
+ const auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN);
+ if (v4l2TestPattern == controls().end()) {
+ LOG(CameraSensor, Debug) << "V4L2_CID_TEST_PATTERN is not supported";
+ return;
+ }
+
+ const auto &testPatternModes = staticProps_->testPatternModes;
+ if (testPatternModes.empty()) {
+ /*
+ * The camera sensor supports test patterns but we don't know
+ * how to map them so this should be fixed.
+ */
+ LOG(CameraSensor, Debug) << "No static test pattern map for \'"
+ << model() << "\'";
+ return;
+ }
+
+ /*
+ * Create a map that associates the V4L2 control index to the test
+ * pattern mode by reversing the testPatternModes map provided by the
+ * camera sensor properties. This makes it easier to verify if the
+ * control index is supported in the below for loop that creates the
+ * list of supported test patterns.
+ */
+ std::map<int32_t, controls::draft::TestPatternModeEnum> indexToTestPatternMode;
+ for (const auto &it : testPatternModes)
+ indexToTestPatternMode[it.second] = it.first;
+
+ for (const ControlValue &value : v4l2TestPattern->second.values()) {
+ const int32_t index = value.get<int32_t>();
+
+ const auto it = indexToTestPatternMode.find(index);
+ if (it == indexToTestPatternMode.end()) {
+ LOG(CameraSensor, Debug)
+ << "Test pattern mode " << index << " ignored";
+ continue;
+ }
+
+ testPatternModes_.push_back(it->second);
+ }
+}
+
+std::vector<Size> CameraSensorRaw::sizes(unsigned int mbusCode) const
+{
+ std::vector<Size> sizes;
+
+ const auto &format = formats_.find(mbusCode);
+ if (format == formats_.end())
+ return sizes;
+
+ const std::vector<SizeRange> &ranges = format->second;
+ std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
+ [](const SizeRange &range) { return range.max; });
+
+ std::sort(sizes.begin(), sizes.end());
+
+ return sizes;
+}
+
+Size CameraSensorRaw::resolution() const
+{
+ return std::min(sizes_.back(), activeArea_.size());
+}
+
+V4L2SubdeviceFormat
+CameraSensorRaw::getFormat(const std::vector<unsigned int> &mbusCodes,
+ const Size &size, Size maxSize) const
+{
+ unsigned int desiredArea = size.width * size.height;
+ unsigned int bestArea = UINT_MAX;
+ float desiredRatio = static_cast<float>(size.width) / size.height;
+ float bestRatio = FLT_MAX;
+ const Size *bestSize = nullptr;
+ uint32_t bestCode = 0;
+
+ for (unsigned int code : mbusCodes) {
+ const auto formats = formats_.find(code);
+ if (formats == formats_.end())
+ continue;
+
+ for (const SizeRange &range : formats->second) {
+ const Size &sz = range.max;
+
+ if (!maxSize.isNull() &&
+ (sz.width > maxSize.width || sz.height > maxSize.height))
+ continue;
+
+ if (sz.width < size.width || sz.height < size.height)
+ continue;
+
+ float ratio = static_cast<float>(sz.width) / sz.height;
+ float ratioDiff = std::abs(ratio - desiredRatio);
+ unsigned int area = sz.width * sz.height;
+ unsigned int areaDiff = area - desiredArea;
+
+ if (ratioDiff > bestRatio)
+ continue;
+
+ if (ratioDiff < bestRatio || areaDiff < bestArea) {
+ bestRatio = ratioDiff;
+ bestArea = areaDiff;
+ bestSize = &sz;
+ bestCode = code;
+ }
+ }
+ }
+
+ if (!bestSize) {
+ LOG(CameraSensor, Debug) << "No supported format or size found";
+ return {};
+ }
+
+ V4L2SubdeviceFormat format{
+ .code = bestCode,
+ .size = *bestSize,
+ .colorSpace = ColorSpace::Raw,
+ };
+
+ return format;
+}
+
+int CameraSensorRaw::setFormat(V4L2SubdeviceFormat *format, Transform transform)
+{
+ /* Configure flips if the sensor supports that. */
+ if (supportFlips_) {
+ ControlList flipCtrls(subdev_->controls());
+
+ flipCtrls.set(V4L2_CID_HFLIP,
+ static_cast<int32_t>(!!(transform & Transform::HFlip)));
+ flipCtrls.set(V4L2_CID_VFLIP,
+ static_cast<int32_t>(!!(transform & Transform::VFlip)));
+
+ int ret = subdev_->setControls(&flipCtrls);
+ if (ret)
+ return ret;
+ }
+
+ /* Apply format on the subdev. */
+ int ret = subdev_->setFormat(streams_.image.source, format);
+ if (ret)
+ return ret;
+
+ subdev_->updateControlInfo();
+ return 0;
+}
+
+int CameraSensorRaw::tryFormat(V4L2SubdeviceFormat *format) const
+{
+ return subdev_->setFormat(streams_.image.source, format,
+ V4L2Subdevice::Whence::TryFormat);
+}
+
+int CameraSensorRaw::applyConfiguration(const SensorConfiguration &config,
+ Transform transform,
+ V4L2SubdeviceFormat *sensorFormat)
+{
+ if (!config.isValid()) {
+ LOG(CameraSensor, Error) << "Invalid sensor configuration";
+ return -EINVAL;
+ }
+
+ std::vector<unsigned int> filteredCodes;
+ std::copy_if(mbusCodes_.begin(), mbusCodes_.end(),
+ std::back_inserter(filteredCodes),
+ [&config](unsigned int mbusCode) {
+ BayerFormat bayer = BayerFormat::fromMbusCode(mbusCode);
+ if (bayer.bitDepth == config.bitDepth)
+ return true;
+ return false;
+ });
+ if (filteredCodes.empty()) {
+ LOG(CameraSensor, Error)
+ << "Cannot find any format with bit depth "
+ << config.bitDepth;
+ return -EINVAL;
+ }
+
+ /*
+ * Compute the sensor's data frame size by applying the cropping
+ * rectangle, subsampling and output crop to the sensor's pixel array
+ * size.
+ *
+ * \todo The actual size computation is for now ignored and only the
+ * output size is considered. This implies that resolutions obtained
+ * with two different cropping/subsampling will look identical and
+ * only the first found one will be considered.
+ */
+ V4L2SubdeviceFormat subdevFormat = {};
+ for (unsigned int code : filteredCodes) {
+ for (const Size &size : sizes(code)) {
+ if (size.width != config.outputSize.width ||
+ size.height != config.outputSize.height)
+ continue;
+
+ subdevFormat.code = code;
+ subdevFormat.size = size;
+ break;
+ }
+ }
+ if (!subdevFormat.code) {
+ LOG(CameraSensor, Error) << "Invalid output size in sensor configuration";
+ return -EINVAL;
+ }
+
+ int ret = setFormat(&subdevFormat, transform);
+ if (ret)
+ return ret;
+
+ /*
+ * Return to the caller the format actually applied to the sensor.
+ * This is relevant if transform has changed the bayer pattern order.
+ */
+ if (sensorFormat)
+ *sensorFormat = subdevFormat;
+
+ /* \todo Handle AnalogCrop. Most sensors do not support set_selection */
+ /* \todo Handle scaling in the digital domain. */
+
+ return 0;
+}
+
+V4L2Subdevice::Stream CameraSensorRaw::imageStream() const
+{
+ return streams_.image.source;
+}
+
+std::optional<V4L2Subdevice::Stream> CameraSensorRaw::embeddedDataStream() const
+{
+ if (!streams_.edata)
+ return {};
+
+ return { streams_.edata->source };
+}
+
+V4L2SubdeviceFormat CameraSensorRaw::embeddedDataFormat() const
+{
+ if (!streams_.edata)
+ return {};
+
+ V4L2SubdeviceFormat format;
+ int ret = subdev_->getFormat(streams_.edata->source, &format);
+ if (ret)
+ return {};
+
+ return format;
+}
+
+int CameraSensorRaw::setEmbeddedDataEnabled(bool enable)
+{
+ if (!streams_.edata)
+ return enable ? -ENOSTR : 0;
+
+ V4L2Subdevice::Routing routing{ 2 };
+
+ routing[0].sink = streams_.image.sink;
+ routing[0].source = streams_.image.source;
+ routing[0].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+
+ routing[1].sink = streams_.edata->sink;
+ routing[1].source = streams_.edata->source;
+ routing[1].flags = enable ? V4L2_SUBDEV_ROUTE_FL_ACTIVE : 0;
+
+ int ret = subdev_->setRouting(&routing);
+ if (ret)
+ return ret;
+
+ /*
+ * Check if the embedded data stream has been enabled or disabled
+ * correctly. Assume at least one route will match the embedded data
+ * source stream, as there would be something seriously wrong
+ * otherwise.
+ */
+ bool enabled = false;
+
+ for (const V4L2Subdevice::Route &route : routing) {
+ if (route.source != streams_.edata->source)
+ continue;
+
+ enabled = route.flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+ break;
+ }
+
+ if (enabled != enable)
+ return enabled ? -EISCONN : -ENOSTR;
+
+ return 0;
+}
+
+int CameraSensorRaw::sensorInfo(IPACameraSensorInfo *info) const
+{
+ info->model = model();
+
+ /*
+ * The active area size is a static property, while the crop
+ * rectangle needs to be re-read as it depends on the sensor
+ * configuration.
+ */
+ info->activeAreaSize = { activeArea_.width, activeArea_.height };
+
+ int ret = subdev_->getSelection(streams_.image.sink, V4L2_SEL_TGT_CROP,
+ &info->analogCrop);
+ if (ret)
+ return ret;
+
+ /*
+ * IPACameraSensorInfo::analogCrop::x and IPACameraSensorInfo::analogCrop::y
+ * are defined relatively to the active pixel area, while V4L2's
+ * TGT_CROP target is defined in respect to the full pixel array.
+ *
+ * Compensate it by subtracting the active area offset.
+ */
+ info->analogCrop.x -= activeArea_.x;
+ info->analogCrop.y -= activeArea_.y;
+
+ /* The bit depth and image size depend on the currently applied format. */
+ V4L2SubdeviceFormat format{};
+ ret = subdev_->getFormat(streams_.image.source, &format);
+ if (ret)
+ return ret;
+ info->bitsPerPixel = MediaBusFormatInfo::info(format.code).bitsPerPixel;
+ info->outputSize = format.size;
+
+ std::optional<int32_t> cfa = properties_.get(properties::draft::ColorFilterArrangement);
+ info->cfaPattern = cfa ? *cfa : properties::draft::RGB;
+
+ /*
+ * Retrieve the pixel rate, line length and minimum/maximum frame
+ * duration through V4L2 controls. Support for the V4L2_CID_PIXEL_RATE,
+ * V4L2_CID_HBLANK and V4L2_CID_VBLANK controls is mandatory.
+ */
+ ControlList ctrls = subdev_->getControls({ V4L2_CID_PIXEL_RATE,
+ V4L2_CID_HBLANK,
+ V4L2_CID_VBLANK });
+ if (ctrls.empty()) {
+ LOG(CameraSensor, Error)
+ << "Failed to retrieve camera info controls";
+ return -EINVAL;
+ }
+
+ info->pixelRate = ctrls.get(V4L2_CID_PIXEL_RATE).get<int64_t>();
+
+ const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK);
+ info->minLineLength = info->outputSize.width + hblank.min().get<int32_t>();
+ info->maxLineLength = info->outputSize.width + hblank.max().get<int32_t>();
+
+ const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK);
+ info->minFrameLength = info->outputSize.height + vblank.min().get<int32_t>();
+ info->maxFrameLength = info->outputSize.height + vblank.max().get<int32_t>();
+
+ return 0;
+}
+
+Transform CameraSensorRaw::computeTransform(Orientation *orientation) const
+{
+ /*
+ * If we cannot do any flips we cannot change the native camera mounting
+ * orientation.
+ */
+ if (!supportFlips_) {
+ *orientation = mountingOrientation_;
+ return Transform::Identity;
+ }
+
+ /*
+ * Now compute the required transform to obtain 'orientation' starting
+ * from the mounting rotation.
+ *
+ * As a note:
+ * orientation / mountingOrientation_ = transform
+ * mountingOrientation_ * transform = orientation
+ */
+ Transform transform = *orientation / mountingOrientation_;
+
+ /*
+ * If transform contains any Transpose we cannot do it, so adjust
+ * 'orientation' to report the image native orientation and return Identity.
+ */
+ if (!!(transform & Transform::Transpose)) {
+ *orientation = mountingOrientation_;
+ return Transform::Identity;
+ }
+
+ return transform;
+}
+
+BayerFormat::Order CameraSensorRaw::bayerOrder(Transform t) const
+{
+ if (!flipsAlterBayerOrder_)
+ return cfaPattern_;
+
+ /*
+ * Apply the transform to the native (i.e. untransformed) Bayer order,
+ * using the rest of the Bayer format supplied by the caller.
+ */
+ BayerFormat format{ cfaPattern_, 8, BayerFormat::Packing::None };
+ return format.transform(t).order;
+}
+
+const ControlInfoMap &CameraSensorRaw::controls() const
+{
+ return subdev_->controls();
+}
+
+ControlList CameraSensorRaw::getControls(const std::vector<uint32_t> &ids)
+{
+ return subdev_->getControls(ids);
+}
+
+int CameraSensorRaw::setControls(ControlList *ctrls)
+{
+ return subdev_->setControls(ctrls);
+}
+
+int CameraSensorRaw::setTestPatternMode(controls::draft::TestPatternModeEnum mode)
+{
+ if (testPatternMode_ == mode)
+ return 0;
+
+ if (testPatternModes_.empty()) {
+ LOG(CameraSensor, Error)
+ << "Camera sensor does not support test pattern modes.";
+ return -EINVAL;
+ }
+
+ return applyTestPatternMode(mode);
+}
+
+int CameraSensorRaw::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)
+{
+ if (testPatternModes_.empty())
+ return 0;
+
+ auto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),
+ mode);
+ if (it == testPatternModes_.end()) {
+ LOG(CameraSensor, Error) << "Unsupported test pattern mode "
+ << mode;
+ return -EINVAL;
+ }
+
+ LOG(CameraSensor, Debug) << "Apply test pattern mode " << mode;
+
+ int32_t index = staticProps_->testPatternModes.at(mode);
+ ControlList ctrls{ controls() };
+ ctrls.set(V4L2_CID_TEST_PATTERN, index);
+
+ int ret = setControls(&ctrls);
+ if (ret)
+ return ret;
+
+ testPatternMode_ = mode;
+
+ return 0;
+}
+
+std::string CameraSensorRaw::logPrefix() const
+{
+ return "'" + entity_->name() + "'";
+}
+
+REGISTER_CAMERA_SENSOR(CameraSensorRaw, 0)
+
+} /* namespace libcamera */
diff --git a/src/libcamera/sensor/meson.build b/src/libcamera/sensor/meson.build
new file mode 100644
index 00000000..dce74ed6
--- /dev/null
+++ b/src/libcamera/sensor/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+
+libcamera_internal_sources += files([
+ 'camera_sensor.cpp',
+ 'camera_sensor_legacy.cpp',
+ 'camera_sensor_properties.cpp',
+ 'camera_sensor_raw.cpp',
+])
diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp
new file mode 100644
index 00000000..d9b61d37
--- /dev/null
+++ b/src/libcamera/shared_mem_object.cpp
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023 Raspberry Pi Ltd
+ * Copyright (C) 2024 Andrei Konovalov
+ * Copyright (C) 2024 Dennis Bonke
+ * Copyright (C) 2024 Ideas on Board Oy
+ *
+ * Helpers for shared memory allocations
+ */
+
+#include "libcamera/internal/shared_mem_object.h"
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <libcamera/base/memfd.h>
+
+/**
+ * \file shared_mem_object.cpp
+ * \brief Helpers for shared memory allocations
+ */
+
+namespace libcamera {
+
+/**
+ * \class SharedMem
+ * \brief Helper class to allocate and manage memory shareable between processes
+ *
+ * SharedMem manages memory suitable for sharing between processes. When an
+ * instance is constructed, it allocates a memory buffer of the requested size
+ * backed by an anonymous file, using the memfd API.
+ *
+ * The allocated memory is exposed by the mem() function. If memory allocation
+ * fails, the function returns an empty Span. This can be also checked using the
+ * bool() operator.
+ *
+ * The file descriptor for the backing file is exposed as a SharedFD by the fd()
+ * function. It can be shared with other processes across IPC boundaries, which
+ * can then map the memory with mmap().
+ *
+ * A single memfd is created for every SharedMem. If there is a need to allocate
+ * a large number of objects in shared memory, these objects should be grouped
+ * together and use the shared memory allocated by a single SharedMem object if
+ * possible. This will help to minimize the number of created memfd's.
+ */
+
+SharedMem::SharedMem() = default;
+
+/**
+ * \brief Construct a SharedMem with memory of the given \a size
+ * \param[in] name Name of the SharedMem
+ * \param[in] size Size of the shared memory to allocate and map
+ *
+ * The \a name is used for debugging purpose only. Multiple SharedMem instances
+ * can have the same name.
+ */
+SharedMem::SharedMem(const std::string &name, std::size_t size)
+{
+ UniqueFD memfd = MemFd::create(name.c_str(), size,
+ MemFd::Seal::Shrink | MemFd::Seal::Grow);
+ if (!memfd.isValid())
+ return;
+
+ fd_ = SharedFD(std::move(memfd));
+
+ void *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd_.get(), 0);
+ if (mem == MAP_FAILED) {
+ fd_ = SharedFD();
+ return;
+ }
+
+ mem_ = { static_cast<uint8_t *>(mem), size };
+}
+
+/**
+ * \brief Move constructor for SharedMem
+ * \param[in] rhs The object to move
+ */
+SharedMem::SharedMem(SharedMem &&rhs)
+{
+ this->fd_ = std::move(rhs.fd_);
+ this->mem_ = rhs.mem_;
+ rhs.mem_ = {};
+}
+
+/**
+ * \brief Destroy the SharedMem instance
+ *
+ * Destroying an instance invalidates the memory mapping exposed with mem().
+ * Other mappings of the backing file, created in this or other processes with
+ * mmap(), remain valid.
+ *
+ * Similarly, other references to the backing file descriptor created by copying
+ * the SharedFD returned by fd() remain valid. The underlying memory will be
+ * freed only when all file descriptors that reference the anonymous file get
+ * closed.
+ */
+SharedMem::~SharedMem()
+{
+ if (!mem_.empty())
+ munmap(mem_.data(), mem_.size_bytes());
+}
+
+/**
+ * \brief Move assignment operator for SharedMem
+ * \param[in] rhs The object to move
+ */
+SharedMem &SharedMem::operator=(SharedMem &&rhs)
+{
+ this->fd_ = std::move(rhs.fd_);
+ this->mem_ = rhs.mem_;
+ rhs.mem_ = {};
+ return *this;
+}
+
+/**
+ * \fn const SharedFD &SharedMem::fd() const
+ * \brief Retrieve the file descriptor for the underlying shared memory
+ * \return The file descriptor, or an invalid SharedFD if allocation failed
+ */
+
+/**
+ * \fn Span<uint8_t> SharedMem::mem() const
+ * \brief Retrieve the underlying shared memory
+ * \return The memory buffer, or an empty Span if allocation failed
+ */
+
+/**
+ * \fn SharedMem::operator bool()
+ * \brief Check if the shared memory allocation succeeded
+ * \return True if allocation of the shared memory succeeded, false otherwise
+ */
+
+/**
+ * \class SharedMemObject
+ * \brief Helper class to allocate an object in shareable memory
+ * \tparam The object type
+ *
+ * The SharedMemObject class is a specialization of the SharedMem class that
+ * wraps an object of type \a T and constructs it in shareable memory. It uses
+ * the same underlying memory allocation and sharing mechanism as the SharedMem
+ * class.
+ *
+ * The wrapped object is constructed at the same time as the SharedMemObject
+ * instance, by forwarding the arguments passed to the SharedMemObject
+ * constructor. The underlying memory allocation is sized to the object \a T
+ * size. The bool() operator should be used to check the allocation was
+ * successful. The object can be accessed using the dereference operators
+ * operator*() and operator->().
+ *
+ * While no restriction on the type \a T is enforced, not all types are suitable
+ * for sharing between multiple processes. Most notably, any object type that
+ * contains pointer or reference members will likely cause issues. Even if those
+ * members refer to other members of the same object, the shared memory will be
+ * mapped at different addresses in different processes, and the pointers will
+ * not be valid.
+ *
+ * A new anonymous file is created for every SharedMemObject instance. If there
+ * is a need to share a large number of small objects, these objects should be
+ * grouped into a single larger object to limit the number of file descriptors.
+ *
+ * To share the object with other processes, see the SharedMem documentation.
+ */
+
+/**
+ * \var SharedMemObject::kSize
+ * \brief The size of the object stored in shared memory
+ */
+
+/**
+ * \fn SharedMemObject::SharedMemObject(const std::string &name, Args &&...args)
+ * \brief Construct a SharedMemObject
+ * \param[in] name Name of the SharedMemObject
+ * \param[in] args Arguments to pass to the constructor of the object T
+ *
+ * The \a name is used for debugging purpose only. Multiple SharedMem instances
+ * can have the same name.
+ */
+
+/**
+ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
+ * \brief Move constructor for SharedMemObject
+ * \param[in] rhs The object to move
+ */
+
+/**
+ * \fn SharedMemObject::~SharedMemObject()
+ * \brief Destroy the SharedMemObject instance
+ *
+ * Destroying a SharedMemObject calls the wrapped T object's destructor. While
+ * the underlying memory may not be freed immediately if other mappings have
+ * been created manually (see SharedMem::~SharedMem() for more information), the
+ * stored object may be modified. Depending on the ~T() destructor, accessing
+ * the object after destruction of the SharedMemObject causes undefined
+ * behaviour. It is the responsibility of the user of this class to synchronize
+ * with other users who have access to the shared object.
+ */
+
+/**
+ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
+ * \brief Move assignment operator for SharedMemObject
+ * \param[in] rhs The SharedMemObject object to take the data from
+ *
+ * Moving a SharedMemObject does not affect the stored object.
+ */
+
+/**
+ * \fn SharedMemObject::operator->()
+ * \brief Dereference the stored object
+ * \return Pointer to the stored object
+ */
+
+/**
+ * \fn const T *SharedMemObject::operator->() const
+ * \copydoc SharedMemObject::operator->
+ */
+
+/**
+ * \fn SharedMemObject::operator*()
+ * \brief Dereference the stored object
+ * \return Reference to the stored object
+ */
+
+/**
+ * \fn const T &SharedMemObject::operator*() const
+ * \copydoc SharedMemObject::operator*
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/TODO b/src/libcamera/software_isp/TODO
new file mode 100644
index 00000000..a50db668
--- /dev/null
+++ b/src/libcamera/software_isp/TODO
@@ -0,0 +1,208 @@
+2. Reconsider stats sharing
+
+>>> +void SwStatsCpu::finishFrame(void)
+>>> +{
+>>> + *sharedStats_ = stats_;
+>>
+>> Is it more efficient to copy the stats instead of operating directly on
+>> the shared memory ?
+>
+> I inherited doing things this way from Andrey. I kept this because
+> we don't really have any synchronization with the IPA reading this.
+>
+> So the idea is to only touch this when the next set of statistics
+> is ready since we don't know when the IPA is done with accessing
+> the previous set of statistics ...
+>
+> This is both something which seems mostly a theoretic problem,
+> yet also definitely something which I think we need to fix.
+>
+> Maybe use a ringbuffer of stats buffers and pass the index into
+> the ringbuffer to the emit signal ?
+
+That would match how we deal with hardware ISPs, and I think that's a
+good idea. It will help decoupling the processing side from the IPA.
+
+---
+
+3. Remove statsReady signal
+
+> class SwStatsCpu
+> {
+> /**
+> * \brief Signals that the statistics are ready
+> */
+> Signal<> statsReady;
+
+But better, I wonder if the signal could be dropped completely. The
+SwStatsCpu class does not operate asynchronously. Shouldn't whoever
+calls the finishFrame() function then handle emitting the signal ?
+
+Now, the trouble is that this would be the DebayerCpu class, whose name
+doesn't indicate as a prime candidate to handle stats. However, it
+already exposes a getStatsFD() function, so we're already calling for
+trouble :-) Either that should be moved to somewhere else, or the class
+should be renamed. Considering that the class applies colour gains in
+addition to performing the interpolation, it may be more of a naming
+issue.
+
+Removing the signal and refactoring those classes doesn't have to be
+addressed now, I think it would be part of a larger refactoring
+(possibly also considering platforms that have no ISP but can produce
+stats in hardware, such as the i.MX7), but please keep it on your radar.
+
+---
+
+5. Store ISP parameters in per-frame buffers
+
+> /**
+> * \fn void Debayer::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+> * \brief Process the bayer data into the requested format.
+> * \param[in] input The input buffer.
+> * \param[in] output The output buffer.
+> * \param[in] params The parameters to be used in debayering.
+> *
+> * \note DebayerParams is passed by value deliberately so that a copy is passed
+> * when this is run in another thread by invokeMethod().
+> */
+
+Possibly something to address later, by storing ISP parameters in
+per-frame buffers like we do for hardware ISPs.
+
+---
+
+6. Input buffer copying configuration
+
+> DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
+> : stats_(std::move(stats)), gammaCorrection_(1.0)
+> {
+> enableInputMemcpy_ = true;
+
+Set this appropriately and/or make it configurable.
+
+---
+
+7. Performance measurement configuration
+
+> void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+> /* Measure before emitting signals */
+> if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
+> ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
+> timespec frameEndTime = {};
+> clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
+> frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
+> if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
+> const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
+> DebayerCpu::kFramesToSkip;
+> LOG(Debayer, Info)
+> << "Processed " << measuredFrames
+> << " frames in " << frameProcessTime_ / 1000 << "us, "
+> << frameProcessTime_ / (1000 * measuredFrames)
+> << " us/frame";
+> }
+> }
+
+I wonder if there would be a way to control at runtime when/how to
+perform those measurements. Maybe that's a bit overkill.
+
+---
+
+8. DebayerCpu cleanups
+
+> >> class DebayerCpu : public Debayer, public Object
+> >> const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
+> >
+> > This,
+>
+> Note the statistics pass-through stuff is sort of a necessary evil
+> since we want one main loop going over the data line by line and
+> doing both debayering as well as stats while the line is still
+> hot in the l2 cache. And things like the process2() and process4()
+> loops are highly CPU debayering specific so I don't think we should
+> move those out of the CpuDebayer code.
+
+Yes, that I understood from the review. "necessary evil" is indeed the
+right term :-) I expect it will take quite some design skills to balance
+the need for performances and the need for a maintainable architecture.
+
+> > plus the fact that this class handles colour gains and gamma,
+> > makes me thing we have either a naming issue, or an architecture issue.
+>
+> I agree that this does a bit more then debayering, although
+> the debayering really is the main thing it does.
+>
+> I guess the calculation of the rgb lookup tables which do the
+> color gains and gamma could be moved outside of this class,
+> that might even be beneficial for GPU based debayering assuming
+> that that is going to use rgb lookup tables too (it could
+> implement actual color gains + gamma correction in some different
+> way).
+>
+> I think this falls under the lets wait until we have a GPU
+> based SoftISP MVP/POC and then do some refactoring to see which
+> bits should go where.
+
+---
+
+8. Decouple pipeline and IPA naming
+
+> The current src/ipa/meson.build assumes the IPA name to match the
+> pipeline name. For this reason "-Dipas=simple" is used for the
+> Soft IPA module.
+
+This should be addressed.
+
+---
+
+9. Doxyfile cleanup
+
+>> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
+>> index a86ea6c1..2be8d47b 100644
+>> --- a/Documentation/Doxyfile.in
+>> +++ b/Documentation/Doxyfile.in
+>> @@ -44,6 +44,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \
+>> @TOP_SRCDIR@/src/libcamera/pipeline/ \
+>> @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
+>> @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
+>> + @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
+> Why is this needed ?
+>
+>> @TOP_BUILDDIR@/src/libcamera/proxy/
+>> EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
+>> diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
+>> index f3b4881c..3352d08f 100644
+>> --- a/include/libcamera/ipa/meson.build
+>> +++ b/include/libcamera/ipa/meson.build
+>> @@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
+>> 'ipu3': 'ipu3.mojom',
+>> 'rkisp1': 'rkisp1.mojom',
+>> 'rpi/vc4': 'raspberrypi.mojom',
+>> + 'simple': 'soft.mojom',
+>> 'vimc': 'vimc.mojom',
+>> }
+>> diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
+>> new file mode 100644
+>> index 00000000..c249bd75
+>> --- /dev/null
+>> +++ b/include/libcamera/ipa/soft.mojom
+>> @@ -0,0 +1,28 @@
+>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
+>> +
+>> +/*
+>> + * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
+> Ah that's why.
+
+Yes, because, well... all the other IPAs were doing that...
+
+> It doesn't have to be done before merging, but could you
+> address this sooner than later ?
+
+---
+
+13. Improve black level and colour gains application
+
+I think the black level should eventually be moved before debayering, and
+ideally the colour gains as well. I understand the need for optimizations to
+lower the CPU consumption, but at the same time I don't feel comfortable
+building up on top of an implementation that may work a bit more by chance than
+by correctness, as that's not very maintainable.
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
new file mode 100644
index 00000000..e9e18c48
--- /dev/null
+++ b/src/libcamera/software_isp/debayer.cpp
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023-2025 Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * debayer base class
+ */
+
+#include "debayer.h"
+
+namespace libcamera {
+
+/**
+ * \struct DebayerParams
+ * \brief Struct to hold the debayer parameters.
+ */
+
+/**
+ * \var DebayerParams::kRGBLookupSize
+ * \brief Size of a color lookup table
+ */
+
+/**
+ * \struct DebayerParams::CcmColumn
+ * \brief Type of a single column of a color correction matrix (CCM)
+ *
+ * When multiplying an input pixel, columns in the CCM correspond to the red,
+ * green or blue component of input pixel values, while rows correspond to the
+ * red, green or blue components of the output pixel values. The members of the
+ * CcmColumn structure are named after the colour components of the output pixel
+ * values they correspond to.
+ */
+
+/**
+ * \var DebayerParams::CcmColumn::r
+ * \brief Red (first) component of a CCM column
+ */
+
+/**
+ * \var DebayerParams::CcmColumn::g
+ * \brief Green (second) component of a CCM column
+ */
+
+/**
+ * \var DebayerParams::CcmColumn::b
+ * \brief Blue (third) component of a CCM column
+ */
+
+/**
+ * \typedef DebayerParams::LookupTable
+ * \brief Type of the lookup tables for single lookup values
+ */
+
+/**
+ * \typedef DebayerParams::CcmLookupTable
+ * \brief Type of the CCM lookup tables for red, green, blue values
+ */
+
+/**
+ * \var DebayerParams::red
+ * \brief Lookup table for red color, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::green
+ * \brief Lookup table for green color, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::blue
+ * \brief Lookup table for blue color, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::redCcm
+ * \brief Lookup table for the CCM red column, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::greenCcm
+ * \brief Lookup table for the CCM green column, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::blueCcm
+ * \brief Lookup table for the CCM blue column, mapping input values to output values
+ */
+
+/**
+ * \var DebayerParams::gammaLut
+ * \brief Gamma lookup table used with color correction matrix
+ */
+
+/**
+ * \class Debayer
+ * \brief Base debayering class
+ *
+ * Base class that provides functions for setting up the debayering process.
+ */
+
+LOG_DEFINE_CATEGORY(Debayer)
+
+Debayer::~Debayer()
+{
+}
+
+/**
+ * \fn int Debayer::configure()
+ * \brief Configure the debayer object according to the passed in parameters
+ * \param[in] inputCfg The input configuration
+ * \param[in] outputCfgs The output configurations
+ * \param[in] ccmEnabled Whether a color correction matrix is applied
+ *
+ * \return 0 on success, a negative errno on failure
+ */
+
+/**
+ * \fn Size Debayer::patternSize(PixelFormat inputFormat)
+ * \brief Get the width and height at which the bayer pattern repeats
+ * \param[in] inputFormat The input format
+ *
+ * Valid sizes are: 2x2, 4x2 or 4x4.
+ *
+ * \return Pattern size or an empty size for unsupported inputFormats
+ */
+
+/**
+ * \fn std::vector<PixelFormat> Debayer::formats(PixelFormat inputFormat)
+ * \brief Get the supported output formats
+ * \param[in] inputFormat The input format
+ *
+ * \return All supported output formats or an empty vector if there are none
+ */
+
+/**
+ * \fn std::tuple<unsigned int, unsigned int> Debayer::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+ * \brief Get the stride and the frame size
+ * \param[in] outputFormat The output format
+ * \param[in] size The output size
+ *
+ * \return A tuple of the stride and the frame size, or a tuple with 0,0 if
+ * there is no valid output config
+ */
+
+/**
+ * \fn void Debayer::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+ * \brief Process the bayer data into the requested format
+ * \param[in] frame The frame number
+ * \param[in] input The input buffer
+ * \param[in] output The output buffer
+ * \param[in] params The parameters to be used in debayering
+ *
+ * \note DebayerParams is passed by value deliberately so that a copy is passed
+ * when this is run in another thread by invokeMethod().
+ */
+
+/**
+ * \fn virtual SizeRange Debayer::sizes(PixelFormat inputFormat, const Size &inputSize)
+ * \brief Get the supported output sizes for the given input format and size
+ * \param[in] inputFormat The input format
+ * \param[in] inputSize The input size
+ *
+ * \return The valid size ranges or an empty range if there are none
+ */
+
+/**
+ * \var Signal<FrameBuffer *> Debayer::inputBufferReady
+ * \brief Signals when the input buffer is ready
+ */
+
+/**
+ * \var Signal<FrameBuffer *> Debayer::outputBufferReady
+ * \brief Signals when the output buffer is ready
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
new file mode 100644
index 00000000..ba033d44
--- /dev/null
+++ b/src/libcamera/software_isp/debayer.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023, Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * debayering base class
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/signal.h>
+
+#include <libcamera/geometry.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/software_isp/debayer_params.h"
+
+namespace libcamera {
+
+class FrameBuffer;
+
+LOG_DECLARE_CATEGORY(Debayer)
+
+class Debayer
+{
+public:
+ virtual ~Debayer() = 0;
+
+ virtual int configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ bool ccmEnabled) = 0;
+
+ virtual std::vector<PixelFormat> formats(PixelFormat inputFormat) = 0;
+
+ virtual std::tuple<unsigned int, unsigned int>
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
+
+ virtual void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0;
+
+ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
+
+ Signal<FrameBuffer *> inputBufferReady;
+ Signal<FrameBuffer *> outputBufferReady;
+
+private:
+ virtual Size patternSize(PixelFormat inputFormat) = 0;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
new file mode 100644
index 00000000..66f6038c
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -0,0 +1,875 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023-2025 Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * CPU based debayering class
+ */
+
+#include "debayer_cpu.h"
+
+#include <algorithm>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <utility>
+
+#include <linux/dma-buf.h>
+
+#include <libcamera/formats.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/dma_buf_allocator.h"
+#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/mapped_framebuffer.h"
+
+namespace libcamera {
+
+/**
+ * \class DebayerCpu
+ * \brief Class for debayering on the CPU
+ *
+ * Implementation for CPU based debayering
+ */
+
+/**
+ * \brief Constructs a DebayerCpu object
+ * \param[in] stats Pointer to the stats object to use
+ */
+DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
+ : stats_(std::move(stats))
+{
+ /*
+ * Reading from uncached buffers may be very slow.
+ * In such a case, it's better to copy input buffer data to normal memory.
+ * But in case of cached buffers, copying the data is unnecessary overhead.
+ * enable_input_memcpy_ makes this behavior configurable. At the moment, we
+ * always set it to true as the safer choice but this should be changed in
+ * future.
+ */
+ enableInputMemcpy_ = true;
+
+ /* Initialize color lookup tables */
+ for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
+ red_[i] = green_[i] = blue_[i] = i;
+ redCcm_[i] = { static_cast<int16_t>(i), 0, 0 };
+ greenCcm_[i] = { 0, static_cast<int16_t>(i), 0 };
+ blueCcm_[i] = { 0, 0, static_cast<int16_t>(i) };
+ }
+}
+
+DebayerCpu::~DebayerCpu() = default;
+
+#define DECLARE_SRC_POINTERS(pixel_t) \
+ const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \
+ const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \
+ const pixel_t *next = (const pixel_t *)src[2] + xShift_;
+
+#define GAMMA(value) \
+ *dst++ = gammaLut_[std::clamp(value, 0, static_cast<int>(gammaLut_.size()) - 1)]
+
+#define STORE_PIXEL(b_, g_, r_) \
+ if constexpr (ccmEnabled) { \
+ const DebayerParams::CcmColumn &blue = blueCcm_[b_]; \
+ const DebayerParams::CcmColumn &green = greenCcm_[g_]; \
+ const DebayerParams::CcmColumn &red = redCcm_[r_]; \
+ GAMMA(blue.b + green.b + red.b); \
+ GAMMA(blue.g + green.g + red.g); \
+ GAMMA(blue.r + green.r + red.r); \
+ } else { \
+ *dst++ = blue_[b_]; \
+ *dst++ = green_[g_]; \
+ *dst++ = red_[r_]; \
+ } \
+ if constexpr (addAlphaByte) \
+ *dst++ = 255; \
+ x++;
+
+/*
+ * RGR
+ * GBG
+ * RGR
+ */
+#define BGGR_BGR888(p, n, div) \
+ STORE_PIXEL( \
+ curr[x] / (div), \
+ (prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div)), \
+ (prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div)))
+
+/*
+ * GBG
+ * RGR
+ * GBG
+ */
+#define GRBG_BGR888(p, n, div) \
+ STORE_PIXEL( \
+ (prev[x] + next[x]) / (2 * (div)), \
+ curr[x] / (div), \
+ (curr[x - p] + curr[x + n]) / (2 * (div)))
+
+/*
+ * GRG
+ * BGB
+ * GRG
+ */
+#define GBRG_BGR888(p, n, div) \
+ STORE_PIXEL( \
+ (curr[x - p] + curr[x + n]) / (2 * (div)), \
+ curr[x] / (div), \
+ (prev[x] + next[x]) / (2 * (div)))
+
+/*
+ * BGB
+ * GRG
+ * BGB
+ */
+#define RGGB_BGR888(p, n, div) \
+ STORE_PIXEL( \
+ (prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div)), \
+ (prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div)), \
+ curr[x] / (div))
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint8_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ BGGR_BGR888(1, 1, 1)
+ GBRG_BGR888(1, 1, 1)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint8_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ GRBG_BGR888(1, 1, 1)
+ RGGB_BGR888(1, 1, 1)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint16_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ /* divide values by 4 for 10 -> 8 bpp value */
+ BGGR_BGR888(1, 1, 4)
+ GBRG_BGR888(1, 1, 4)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint16_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ /* divide values by 4 for 10 -> 8 bpp value */
+ GRBG_BGR888(1, 1, 4)
+ RGGB_BGR888(1, 1, 4)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint16_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ /* divide values by 16 for 12 -> 8 bpp value */
+ BGGR_BGR888(1, 1, 16)
+ GBRG_BGR888(1, 1, 16)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ DECLARE_SRC_POINTERS(uint16_t)
+
+ for (int x = 0; x < (int)window_.width;) {
+ /* divide values by 16 for 12 -> 8 bpp value */
+ GRBG_BGR888(1, 1, 16)
+ RGGB_BGR888(1, 1, 16)
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 5 / 4;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ /*
+ * For the first pixel getting a pixel from the previous column uses
+ * x - 2 to skip the 5th byte with least-significant bits for 4 pixels.
+ * Same for last pixel (uses x + 2) and looking at the next column.
+ */
+ for (int x = 0; x < widthInBytes;) {
+ /* First pixel */
+ BGGR_BGR888(2, 1, 1)
+ /* Second pixel BGGR -> GBRG */
+ GBRG_BGR888(1, 1, 1)
+ /* Same thing for third and fourth pixels */
+ BGGR_BGR888(1, 1, 1)
+ GBRG_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 5 / 4;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* First pixel */
+ GRBG_BGR888(2, 1, 1)
+ /* Second pixel GRBG -> RGGB */
+ RGGB_BGR888(1, 1, 1)
+ /* Same thing for third and fourth pixels */
+ GRBG_BGR888(1, 1, 1)
+ RGGB_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 5 / 4;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ GBRG_BGR888(2, 1, 1)
+ /* Odd pixel GBGR -> BGGR */
+ BGGR_BGR888(1, 1, 1)
+ /* Same thing for next 2 pixels */
+ GBRG_BGR888(1, 1, 1)
+ BGGR_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 5 / 4;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ RGGB_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG */
+ GRBG_BGR888(1, 1, 1)
+ /* Same thing for next 2 pixels */
+ RGGB_BGR888(1, 1, 1)
+ GRBG_BGR888(1, 2, 1)
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
+ x++;
+ }
+}
+
+static bool isStandardBayerOrder(BayerFormat::Order order)
+{
+ return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
+ order == BayerFormat::GRBG || order == BayerFormat::RGGB;
+}
+
+/*
+ * Setup the Debayer object according to the passed in parameters.
+ * Return 0 on success, a negative errno value on failure
+ * (unsupported parameters).
+ */
+int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
+{
+ BayerFormat bayerFormat =
+ BayerFormat::fromPixelFormat(inputFormat);
+
+ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ bayerFormat.packing == BayerFormat::Packing::None &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+ config.patternSize.width = 2;
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::RGB888,
+ formats::XRGB8888,
+ formats::ARGB8888,
+ formats::BGR888,
+ formats::XBGR8888,
+ formats::ABGR8888 });
+ return 0;
+ }
+
+ if (bayerFormat.bitDepth == 10 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = 10;
+ config.patternSize.width = 4; /* 5 bytes per *4* pixels */
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::RGB888,
+ formats::XRGB8888,
+ formats::ARGB8888,
+ formats::BGR888,
+ formats::XBGR8888,
+ formats::ABGR8888 });
+ return 0;
+ }
+
+ LOG(Debayer, Info)
+ << "Unsupported input format " << inputFormat.toString();
+ return -EINVAL;
+}
+
+int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
+{
+ if (outputFormat == formats::RGB888 || outputFormat == formats::BGR888) {
+ config.bpp = 24;
+ return 0;
+ }
+
+ if (outputFormat == formats::XRGB8888 || outputFormat == formats::ARGB8888 ||
+ outputFormat == formats::XBGR8888 || outputFormat == formats::ABGR8888) {
+ config.bpp = 32;
+ return 0;
+ }
+
+ LOG(Debayer, Info)
+ << "Unsupported output format " << outputFormat.toString();
+ return -EINVAL;
+}
+
+/*
+ * Check for standard Bayer orders and set xShift_ and swap debayer0/1, so that
+ * a single pair of BGGR debayer functions can be used for all 4 standard orders.
+ */
+int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
+{
+ switch (order) {
+ case BayerFormat::BGGR:
+ break;
+ case BayerFormat::GBRG:
+ xShift_ = 1; /* BGGR -> GBRG */
+ break;
+ case BayerFormat::GRBG:
+ std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
+ break;
+ case BayerFormat::RGGB:
+ xShift_ = 1; /* BGGR -> GBRG */
+ std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define SET_DEBAYER_METHODS(method0, method1) \
+ debayer0_ = addAlphaByte \
+ ? (ccmEnabled ? &DebayerCpu::method0<true, true> : &DebayerCpu::method0<true, false>) \
+ : (ccmEnabled ? &DebayerCpu::method0<false, true> : &DebayerCpu::method0<false, false>); \
+ debayer1_ = addAlphaByte \
+ ? (ccmEnabled ? &DebayerCpu::method1<true, true> : &DebayerCpu::method1<true, false>) \
+ : (ccmEnabled ? &DebayerCpu::method1<false, true> : &DebayerCpu::method1<false, false>);
+
+int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat,
+ PixelFormat outputFormat,
+ bool ccmEnabled)
+{
+ BayerFormat bayerFormat =
+ BayerFormat::fromPixelFormat(inputFormat);
+ bool addAlphaByte = false;
+
+ xShift_ = 0;
+ swapRedBlueGains_ = false;
+
+ auto invalidFmt = []() -> int {
+ LOG(Debayer, Error) << "Unsupported input output format combination";
+ return -EINVAL;
+ };
+
+ switch (outputFormat) {
+ case formats::XRGB8888:
+ case formats::ARGB8888:
+ addAlphaByte = true;
+ [[fallthrough]];
+ case formats::RGB888:
+ break;
+ case formats::XBGR8888:
+ case formats::ABGR8888:
+ addAlphaByte = true;
+ [[fallthrough]];
+ case formats::BGR888:
+ /* Swap R and B in bayer order to generate BGR888 instead of RGB888 */
+ swapRedBlueGains_ = true;
+
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ bayerFormat.order = BayerFormat::RGGB;
+ break;
+ case BayerFormat::GBRG:
+ bayerFormat.order = BayerFormat::GRBG;
+ break;
+ case BayerFormat::GRBG:
+ bayerFormat.order = BayerFormat::GBRG;
+ break;
+ case BayerFormat::RGGB:
+ bayerFormat.order = BayerFormat::BGGR;
+ break;
+ default:
+ return invalidFmt();
+ }
+ break;
+ default:
+ return invalidFmt();
+ }
+
+ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ bayerFormat.packing == BayerFormat::Packing::None &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ switch (bayerFormat.bitDepth) {
+ case 8:
+ SET_DEBAYER_METHODS(debayer8_BGBG_BGR888, debayer8_GRGR_BGR888)
+ break;
+ case 10:
+ SET_DEBAYER_METHODS(debayer10_BGBG_BGR888, debayer10_GRGR_BGR888)
+ break;
+ case 12:
+ SET_DEBAYER_METHODS(debayer12_BGBG_BGR888, debayer12_GRGR_BGR888)
+ break;
+ }
+ setupStandardBayerOrder(bayerFormat.order);
+ return 0;
+ }
+
+ if (bayerFormat.bitDepth == 10 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ SET_DEBAYER_METHODS(debayer10P_BGBG_BGR888, debayer10P_GRGR_BGR888)
+ return 0;
+ case BayerFormat::GBRG:
+ SET_DEBAYER_METHODS(debayer10P_GBGB_BGR888, debayer10P_RGRG_BGR888)
+ return 0;
+ case BayerFormat::GRBG:
+ SET_DEBAYER_METHODS(debayer10P_GRGR_BGR888, debayer10P_BGBG_BGR888)
+ return 0;
+ case BayerFormat::RGGB:
+ SET_DEBAYER_METHODS(debayer10P_RGRG_BGR888, debayer10P_GBGB_BGR888)
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ return invalidFmt();
+}
+
+int DebayerCpu::configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ bool ccmEnabled)
+{
+ if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)
+ return -EINVAL;
+
+ if (stats_->configure(inputCfg) != 0)
+ return -EINVAL;
+
+ const Size &statsPatternSize = stats_->patternSize();
+ if (inputConfig_.patternSize.width != statsPatternSize.width ||
+ inputConfig_.patternSize.height != statsPatternSize.height) {
+ LOG(Debayer, Error)
+ << "mismatching stats and debayer pattern sizes for "
+ << inputCfg.pixelFormat.toString();
+ return -EINVAL;
+ }
+
+ inputConfig_.stride = inputCfg.stride;
+
+ if (outputCfgs.size() != 1) {
+ LOG(Debayer, Error)
+ << "Unsupported number of output streams: "
+ << outputCfgs.size();
+ return -EINVAL;
+ }
+
+ const StreamConfiguration &outputCfg = outputCfgs[0];
+ SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
+ std::tie(outputConfig_.stride, outputConfig_.frameSize) =
+ strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
+
+ if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
+ LOG(Debayer, Error)
+ << "Invalid output size/stride: "
+ << "\n " << outputCfg.size << " (" << outSizeRange << ")"
+ << "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")";
+ return -EINVAL;
+ }
+
+ int ret = setDebayerFunctions(inputCfg.pixelFormat,
+ outputCfg.pixelFormat,
+ ccmEnabled);
+ if (ret != 0)
+ return -EINVAL;
+
+ window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
+ ~(inputConfig_.patternSize.width - 1);
+ window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
+ ~(inputConfig_.patternSize.height - 1);
+ window_.width = outputCfg.size.width;
+ window_.height = outputCfg.size.height;
+
+ /* Don't pass x,y since process() already adjusts src before passing it */
+ stats_->setWindow(Rectangle(window_.size()));
+
+ /* pad with patternSize.Width on both left and right side */
+ lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
+ lineBufferLength_ = window_.width * inputConfig_.bpp / 8 +
+ 2 * lineBufferPadding_;
+
+ if (enableInputMemcpy_) {
+ for (unsigned int i = 0; i <= inputConfig_.patternSize.height; i++)
+ lineBuffers_[i].resize(lineBufferLength_);
+ }
+
+ measuredFrames_ = 0;
+ frameProcessTime_ = 0;
+
+ return 0;
+}
+
+/*
+ * Get width and height at which the bayer-pattern repeats.
+ * Return pattern-size or an empty Size for an unsupported inputFormat.
+ */
+Size DebayerCpu::patternSize(PixelFormat inputFormat)
+{
+ DebayerCpu::DebayerInputConfig config;
+
+ if (getInputConfig(inputFormat, config) != 0)
+ return {};
+
+ return config.patternSize;
+}
+
+std::vector<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat)
+{
+ DebayerCpu::DebayerInputConfig config;
+
+ if (getInputConfig(inputFormat, config) != 0)
+ return std::vector<PixelFormat>();
+
+ return config.outputFormats;
+}
+
+std::tuple<unsigned int, unsigned int>
+DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+{
+ DebayerCpu::DebayerOutputConfig config;
+
+ if (getOutputConfig(outputFormat, config) != 0)
+ return std::make_tuple(0, 0);
+
+ /* round up to multiple of 8 for 64 bits alignment */
+ unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7;
+
+ return std::make_tuple(stride, stride * size.height);
+}
+
+void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[])
+{
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ if (!enableInputMemcpy_)
+ return;
+
+ for (unsigned int i = 0; i < patternHeight; i++) {
+ memcpy(lineBuffers_[i].data(),
+ linePointers[i + 1] - lineBufferPadding_,
+ lineBufferLength_);
+ linePointers[i + 1] = lineBuffers_[i].data() + lineBufferPadding_;
+ }
+
+ /* Point lineBufferIndex_ to first unused lineBuffer */
+ lineBufferIndex_ = patternHeight;
+}
+
+void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
+{
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ for (unsigned int i = 0; i < patternHeight; i++)
+ linePointers[i] = linePointers[i + 1];
+
+ linePointers[patternHeight] = src +
+ (patternHeight / 2) * (int)inputConfig_.stride;
+}
+
+void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[])
+{
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
+
+ if (!enableInputMemcpy_)
+ return;
+
+ memcpy(lineBuffers_[lineBufferIndex_].data(),
+ linePointers[patternHeight] - lineBufferPadding_,
+ lineBufferLength_);
+ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_].data() + lineBufferPadding_;
+
+ lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
+}
+
+void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
+{
+ unsigned int yEnd = window_.y + window_.height;
+ /* Holds [0] previous- [1] current- [2] next-line */
+ const uint8_t *linePointers[3];
+
+ /* Adjust src to top left corner of the window */
+ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
+
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
+ if (window_.y) {
+ linePointers[1] = src - inputConfig_.stride; /* previous-line */
+ linePointers[2] = src;
+ } else {
+ /* window_.y == 0, use the next line as prev line */
+ linePointers[1] = src + inputConfig_.stride;
+ linePointers[2] = src;
+ /* Last 2 lines also need special handling */
+ yEnd -= 2;
+ }
+
+ setupInputMemcpy(linePointers);
+
+ for (unsigned int y = window_.y; y < yEnd; y += 2) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(y, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+ }
+
+ if (window_.y == 0) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(yEnd, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ /* next line may point outside of src, use prev. */
+ linePointers[2] = linePointers[0];
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+ }
+}
+
+void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
+{
+ const unsigned int yEnd = window_.y + window_.height;
+ /*
+ * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line
+ * [3] 1-line-down [4] 2-lines-down.
+ */
+ const uint8_t *linePointers[5];
+
+ /* Adjust src to top left corner of the window */
+ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
+
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
+ linePointers[1] = src - 2 * inputConfig_.stride;
+ linePointers[2] = src - inputConfig_.stride;
+ linePointers[3] = src;
+ linePointers[4] = src + inputConfig_.stride;
+
+ setupInputMemcpy(linePointers);
+
+ for (unsigned int y = window_.y; y < yEnd; y += 4) {
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine0(y, linePointers);
+ (this->*debayer0_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer1_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ stats_->processLine2(y, linePointers);
+ (this->*debayer2_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+
+ shiftLinePointers(linePointers, src);
+ memcpyNextLine(linePointers);
+ (this->*debayer3_)(dst, linePointers);
+ src += inputConfig_.stride;
+ dst += outputConfig_.stride;
+ }
+}
+
+namespace {
+
+inline int64_t timeDiff(timespec &after, timespec &before)
+{
+ return (after.tv_sec - before.tv_sec) * 1000000000LL +
+ (int64_t)after.tv_nsec - (int64_t)before.tv_nsec;
+}
+
+} /* namespace */
+
+void DebayerCpu::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+{
+ timespec frameStartTime;
+
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) {
+ frameStartTime = {};
+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
+ }
+
+ std::vector<DmaSyncer> dmaSyncers;
+ for (const FrameBuffer::Plane &plane : input->planes())
+ dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Read);
+
+ for (const FrameBuffer::Plane &plane : output->planes())
+ dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write);
+
+ green_ = params.green;
+ greenCcm_ = params.greenCcm;
+ if (swapRedBlueGains_) {
+ red_ = params.blue;
+ blue_ = params.red;
+ redCcm_ = params.blueCcm;
+ blueCcm_ = params.redCcm;
+ for (unsigned int i = 0; i < 256; i++) {
+ std::swap(redCcm_[i].r, redCcm_[i].b);
+ std::swap(blueCcm_[i].r, blueCcm_[i].b);
+ }
+ } else {
+ red_ = params.red;
+ blue_ = params.blue;
+ redCcm_ = params.redCcm;
+ blueCcm_ = params.blueCcm;
+ }
+ gammaLut_ = params.gammaLut;
+
+ /* Copy metadata from the input buffer */
+ FrameMetadata &metadata = output->_d()->metadata();
+ metadata.status = input->metadata().status;
+ metadata.sequence = input->metadata().sequence;
+ metadata.timestamp = input->metadata().timestamp;
+
+ MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
+ MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);
+ if (!in.isValid() || !out.isValid()) {
+ LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
+ metadata.status = FrameMetadata::FrameError;
+ return;
+ }
+
+ stats_->startFrame();
+
+ if (inputConfig_.patternSize.height == 2)
+ process2(in.planes()[0].data(), out.planes()[0].data());
+ else
+ process4(in.planes()[0].data(), out.planes()[0].data());
+
+ metadata.planes()[0].bytesused = out.planes()[0].size();
+
+ dmaSyncers.clear();
+
+ /* Measure before emitting signals */
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
+ ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
+ timespec frameEndTime = {};
+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
+ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
+ if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
+ const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
+ DebayerCpu::kFramesToSkip;
+ LOG(Debayer, Info)
+ << "Processed " << measuredFrames
+ << " frames in " << frameProcessTime_ / 1000 << "us, "
+ << frameProcessTime_ / (1000 * measuredFrames)
+ << " us/frame";
+ }
+ }
+
+ /*
+ * Buffer ids are currently not used, so pass zeros as its parameter.
+ *
+ * \todo Pass real bufferId once stats buffer passing is changed.
+ */
+ stats_->finishFrame(frame, 0);
+ outputBufferReady.emit(output);
+ inputBufferReady.emit(input);
+}
+
+SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+ Size patternSize = this->patternSize(inputFormat);
+ unsigned int borderHeight = patternSize.height;
+
+ if (patternSize.isNull())
+ return {};
+
+ /* No need for top/bottom border with a pattern height of 2 */
+ if (patternSize.height == 2)
+ borderHeight = 0;
+
+ /*
+ * For debayer interpolation a border is kept around the entire image
+ * and the minimum output size is pattern-height x pattern-width.
+ */
+ if (inputSize.width < (3 * patternSize.width) ||
+ inputSize.height < (2 * borderHeight + patternSize.height)) {
+ LOG(Debayer, Warning)
+ << "Input format size too small: " << inputSize.toString();
+ return {};
+ }
+
+ return SizeRange(Size(patternSize.width, patternSize.height),
+ Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),
+ (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),
+ patternSize.width, patternSize.height);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
new file mode 100644
index 00000000..926195e9
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023-2025 Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * CPU based debayering header
+ */
+
+#pragma once
+
+#include <memory>
+#include <stdint.h>
+#include <vector>
+
+#include <libcamera/base/object.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+#include "debayer.h"
+#include "swstats_cpu.h"
+
+namespace libcamera {
+
+class DebayerCpu : public Debayer, public Object
+{
+public:
+ DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
+ ~DebayerCpu();
+
+ int configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ bool ccmEnabled);
+ Size patternSize(PixelFormat inputFormat);
+ std::vector<PixelFormat> formats(PixelFormat input);
+ std::tuple<unsigned int, unsigned int>
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
+ void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params);
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
+
+ /**
+ * \brief Get the file descriptor for the statistics
+ *
+ * \return the file descriptor pointing to the statistics
+ */
+ const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
+
+ /**
+ * \brief Get the output frame size
+ *
+ * \return The output frame size
+ */
+ unsigned int frameSize() { return outputConfig_.frameSize; }
+
+private:
+ /**
+ * \brief Called to debayer 1 line of Bayer input data to output format
+ * \param[out] dst Pointer to the start of the output line to write
+ * \param[in] src The input data
+ *
+ * Input data is an array of (patternSize_.height + 1) src
+ * pointers each pointing to a line in the Bayer source. The middle
+ * element of the array will point to the actual line being processed.
+ * Earlier element(s) will point to the previous line(s) and later
+ * element(s) to the next line(s).
+ *
+ * These functions take an array of src pointers, rather than
+ * a single src pointer + a stride for the source, so that when the src
+ * is slow uncached memory it can be copied to faster memory before
+ * debayering. Debayering a standard 2x2 Bayer pattern requires access
+ * to the previous and next src lines for interpolating the missing
+ * colors. To allow copying the src lines only once 3 temporary buffers
+ * each holding a single line are used, re-using the oldest buffer for
+ * the next line and the pointers are swizzled so that:
+ * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
+ * This way the 3 pointers passed to the debayer functions form
+ * a sliding window over the src avoiding the need to copy each
+ * line more than once.
+ *
+ * Similarly for bayer patterns which repeat every 4 lines, 5 src
+ * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up
+ * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down.
+ */
+ using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
+
+ /* 8-bit raw bayer format */
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ /* unpacked 10-bit raw bayer format */
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ /* unpacked 12-bit raw bayer format */
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
+
+ struct DebayerInputConfig {
+ Size patternSize;
+ unsigned int bpp; /* Memory used per pixel, not precision */
+ unsigned int stride;
+ std::vector<PixelFormat> outputFormats;
+ };
+
+ struct DebayerOutputConfig {
+ unsigned int bpp; /* Memory used per pixel, not precision */
+ unsigned int stride;
+ unsigned int frameSize;
+ };
+
+ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
+ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
+ int setupStandardBayerOrder(BayerFormat::Order order);
+ int setDebayerFunctions(PixelFormat inputFormat,
+ PixelFormat outputFormat,
+ bool ccmEnabled);
+ void setupInputMemcpy(const uint8_t *linePointers[]);
+ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
+ void memcpyNextLine(const uint8_t *linePointers[]);
+ void process2(const uint8_t *src, uint8_t *dst);
+ void process4(const uint8_t *src, uint8_t *dst);
+
+ /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
+ static constexpr unsigned int kMaxLineBuffers = 5;
+
+ DebayerParams::LookupTable red_;
+ DebayerParams::LookupTable green_;
+ DebayerParams::LookupTable blue_;
+ DebayerParams::CcmLookupTable redCcm_;
+ DebayerParams::CcmLookupTable greenCcm_;
+ DebayerParams::CcmLookupTable blueCcm_;
+ DebayerParams::LookupTable gammaLut_;
+ debayerFn debayer0_;
+ debayerFn debayer1_;
+ debayerFn debayer2_;
+ debayerFn debayer3_;
+ Rectangle window_;
+ DebayerInputConfig inputConfig_;
+ DebayerOutputConfig outputConfig_;
+ std::unique_ptr<SwStatsCpu> stats_;
+ std::vector<uint8_t> lineBuffers_[kMaxLineBuffers];
+ unsigned int lineBufferLength_;
+ unsigned int lineBufferPadding_;
+ unsigned int lineBufferIndex_;
+ unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
+ bool enableInputMemcpy_;
+ bool swapRedBlueGains_;
+ unsigned int measuredFrames_;
+ int64_t frameProcessTime_;
+ /* Skip 30 frames for things to stabilize then measure 30 frames */
+ static constexpr unsigned int kFramesToSkip = 30;
+ static constexpr unsigned int kLastFrameToMeasure = 60;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
new file mode 100644
index 00000000..aac7eda7
--- /dev/null
+++ b/src/libcamera/software_isp/meson.build
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: CC0-1.0
+
+softisp_enabled = pipelines.contains('simple')
+summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')
+
+if not softisp_enabled
+ subdir_done()
+endif
+
+libcamera_internal_sources += files([
+ 'debayer.cpp',
+ 'debayer_cpu.cpp',
+ 'software_isp.cpp',
+ 'swstats_cpu.cpp',
+])
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
new file mode 100644
index 00000000..28e2a360
--- /dev/null
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -0,0 +1,422 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ *
+ * Simple software ISP implementation
+ */
+
+#include "libcamera/internal/software_isp/software_isp.h"
+
+#include <cmath>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/thread.h>
+
+#include <libcamera/controls.h>
+#include <libcamera/formats.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/software_isp/debayer_params.h"
+
+#include "debayer_cpu.h"
+
+/**
+ * \file software_isp.cpp
+ * \brief Simple software ISP implementation
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(SoftwareIsp)
+
+/**
+ * \class SoftwareIsp
+ * \brief Class for the Software ISP
+ */
+
+/**
+ * \var SoftwareIsp::inputBufferReady
+ * \brief A signal emitted when the input frame buffer completes
+ */
+
+/**
+ * \var SoftwareIsp::outputBufferReady
+ * \brief A signal emitted when the output frame buffer completes
+ */
+
+/**
+ * \var SoftwareIsp::ispStatsReady
+ * \brief A signal emitted when the statistics for IPA are ready
+ */
+
+/**
+ * \var SoftwareIsp::metadataReady
+ * \brief A signal emitted when the metadata for IPA is ready
+ */
+
+/**
+ * \var SoftwareIsp::setSensorControls
+ * \brief A signal emitted when the values to write to the sensor controls are
+ * ready
+ */
+
+/**
+ * \brief Constructs SoftwareIsp object
+ * \param[in] pipe The pipeline handler in use
+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
+ * \param[out] ipaControls The IPA controls to update
+ * handler
+ */
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
+ ControlInfoMap *ipaControls)
+ : dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
+ DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
+ DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
+{
+ /*
+ * debayerParams_ must be initialized because the initial value is used for
+ * the first two frames, i.e. until stats processing starts providing its
+ * own parameters.
+ *
+ * \todo This should be handled in the same place as the related
+ * operations, in the IPA module.
+ */
+ std::array<uint8_t, 256> gammaTable;
+ for (unsigned int i = 0; i < 256; i++)
+ gammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5);
+ for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
+ debayerParams_.red[i] = gammaTable[i];
+ debayerParams_.green[i] = gammaTable[i];
+ debayerParams_.blue[i] = gammaTable[i];
+ }
+
+ if (!dmaHeap_.isValid()) {
+ LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
+ return;
+ }
+
+ sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
+ if (!sharedParams_) {
+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
+ return;
+ }
+
+ auto stats = std::make_unique<SwStatsCpu>();
+ if (!stats->isValid()) {
+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
+ return;
+ }
+ stats->statsReady.connect(this, &SoftwareIsp::statsReady);
+
+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
+ debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
+ debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
+
+ ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
+ if (!ipa_) {
+ LOG(SoftwareIsp, Error)
+ << "Creating IPA for software ISP failed";
+ debayer_.reset();
+ return;
+ }
+
+ /*
+ * The API tuning file is made from the sensor name. If the tuning file
+ * isn't found, fall back to the 'uncalibrated' file.
+ */
+ std::string ipaTuningFile =
+ ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
+
+ IPACameraSensorInfo sensorInfo{};
+ int ret = sensor->sensorInfo(&sensorInfo);
+ if (ret) {
+ LOG(SoftwareIsp, Error) << "Camera sensor information not available";
+ return;
+ }
+
+ ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
+ debayer_->getStatsFD(),
+ sharedParams_.fd(),
+ sensorInfo,
+ sensor->controls(),
+ ipaControls,
+ &ccmEnabled_);
+ if (ret) {
+ LOG(SoftwareIsp, Error) << "IPA init failed";
+ debayer_.reset();
+ return;
+ }
+
+ ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
+ ipa_->metadataReady.connect(this,
+ [this](uint32_t frame, const ControlList &metadata) {
+ metadataReady.emit(frame, metadata);
+ });
+ ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
+
+ debayer_->moveToThread(&ispWorkerThread_);
+}
+
+SoftwareIsp::~SoftwareIsp()
+{
+ /* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
+ debayer_.reset();
+}
+
+/**
+ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
+ * \brief Load a configuration from a file
+ * \param[in] filename The file to load the configuration data from
+ *
+ * Currently is a stub doing nothing and always returning "success".
+ *
+ * \return 0 on success
+ */
+
+/**
+ * \brief Process the statistics gathered
+ * \param[in] frame The frame number
+ * \param[in] bufferId ID of the statistics buffer
+ * \param[in] sensorControls The sensor controls
+ *
+ * Requests the IPA to calculate new parameters for ISP and new control
+ * values for the sensor.
+ */
+void SoftwareIsp::processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls)
+{
+ ASSERT(ipa_);
+ ipa_->processStats(frame, bufferId, sensorControls);
+}
+
+/**
+ * \brief Check the validity of Software Isp object
+ * \return True if Software Isp is valid, false otherwise
+ */
+bool SoftwareIsp::isValid() const
+{
+ return !!debayer_;
+}
+
+/**
+ * \brief Get the output formats supported for the given input format
+ * \param[in] inputFormat The input format
+ * \return All the supported output formats or an empty vector if there are none
+ */
+std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
+{
+ ASSERT(debayer_);
+
+ return debayer_->formats(inputFormat);
+}
+
+/**
+ * \brief Get the supported output sizes for the given input format and size
+ * \param[in] inputFormat The input format
+ * \param[in] inputSize The input frame size
+ * \return The valid size range or an empty range if there are none
+ */
+SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+ ASSERT(debayer_);
+
+ return debayer_->sizes(inputFormat, inputSize);
+}
+
+/**
+ * Get the output stride and the frame size in bytes for the given output format and size
+ * \param[in] outputFormat The output format
+ * \param[in] size The output size (width and height in pixels)
+ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
+ * if there is no valid output config
+ */
+std::tuple<unsigned int, unsigned int>
+SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+{
+ ASSERT(debayer_);
+
+ return debayer_->strideAndFrameSize(outputFormat, size);
+}
+
+/**
+ * \brief Configure the SoftwareIsp object according to the passed in parameters
+ * \param[in] inputCfg The input configuration
+ * \param[in] outputCfgs The output configurations
+ * \param[in] configInfo The IPA configuration data, received from the pipeline
+ * handler
+ * \return 0 on success, a negative errno on failure
+ */
+int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ const ipa::soft::IPAConfigInfo &configInfo)
+{
+ ASSERT(ipa_ && debayer_);
+
+ int ret = ipa_->configure(configInfo);
+ if (ret < 0)
+ return ret;
+
+ return debayer_->configure(inputCfg, outputCfgs, ccmEnabled_);
+}
+
+/**
+ * \brief Export the buffers from the Software ISP
+ * \param[in] stream Output stream exporting the buffers
+ * \param[in] count Number of buffers to allocate
+ * \param[out] buffers Vector to store the allocated buffers
+ * \return The number of allocated buffers on success or a negative error code
+ * otherwise
+ */
+int SoftwareIsp::exportBuffers(const Stream *stream, unsigned int count,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ ASSERT(debayer_ != nullptr);
+
+ /* single output for now */
+ if (stream == nullptr)
+ return -EINVAL;
+
+ return dmaHeap_.exportBuffers(count, { debayer_->frameSize() }, buffers);
+}
+
+/**
+ * \brief Queue a request and process the control list from the application
+ * \param[in] frame The number of the frame which will be processed next
+ * \param[in] controls The controls for the \a frame
+ */
+void SoftwareIsp::queueRequest(const uint32_t frame, const ControlList &controls)
+{
+ ipa_->queueRequest(frame, controls);
+}
+
+/**
+ * \brief Queue buffers to Software ISP
+ * \param[in] frame The frame number
+ * \param[in] input The input framebuffer
+ * \param[in] outputs The container holding the output stream pointers and
+ * their respective frame buffer outputs
+ * \return 0 on success, a negative errno on failure
+ */
+int SoftwareIsp::queueBuffers(uint32_t frame, FrameBuffer *input,
+ const std::map<const Stream *, FrameBuffer *> &outputs)
+{
+ /*
+ * Validate the outputs as a sanity check: at least one output is
+ * required, all outputs must reference a valid stream.
+ */
+ if (outputs.empty())
+ return -EINVAL;
+
+ /* We only support a single stream for now. */
+ if (outputs.size() != 1)
+ return -EINVAL;
+
+ for (auto [stream, buffer] : outputs) {
+ if (!buffer)
+ return -EINVAL;
+ }
+
+ queuedInputBuffers_.push_back(input);
+
+ for (auto iter = outputs.begin(); iter != outputs.end(); iter++) {
+ FrameBuffer *const buffer = iter->second;
+ queuedOutputBuffers_.push_back(buffer);
+ process(frame, input, buffer);
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Starts the Software ISP streaming operation
+ * \return 0 on success, any other value indicates an error
+ */
+int SoftwareIsp::start()
+{
+ int ret = ipa_->start();
+ if (ret)
+ return ret;
+
+ ispWorkerThread_.start();
+ return 0;
+}
+
+/**
+ * \brief Stops the Software ISP streaming operation
+ *
+ * All pending buffers are returned back as canceled before this function
+ * returns.
+ */
+void SoftwareIsp::stop()
+{
+ ispWorkerThread_.exit();
+ ispWorkerThread_.wait();
+
+ Thread::current()->dispatchMessages(Message::Type::InvokeMessage, this);
+
+ ipa_->stop();
+
+ for (auto buffer : queuedOutputBuffers_) {
+ FrameMetadata &metadata = buffer->_d()->metadata();
+ metadata.status = FrameMetadata::FrameCancelled;
+ outputBufferReady.emit(buffer);
+ }
+ queuedOutputBuffers_.clear();
+
+ for (auto buffer : queuedInputBuffers_) {
+ FrameMetadata &metadata = buffer->_d()->metadata();
+ metadata.status = FrameMetadata::FrameCancelled;
+ inputBufferReady.emit(buffer);
+ }
+ queuedInputBuffers_.clear();
+}
+
+/**
+ * \brief Passes the input framebuffer to the ISP worker to process
+ * \param[in] frame The frame number
+ * \param[in] input The input framebuffer
+ * \param[out] output The framebuffer to write the processed frame to
+ */
+void SoftwareIsp::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output)
+{
+ ipa_->computeParams(frame);
+ debayer_->invokeMethod(&DebayerCpu::process,
+ ConnectionTypeQueued, frame, input, output, debayerParams_);
+}
+
+void SoftwareIsp::saveIspParams()
+{
+ debayerParams_ = *sharedParams_;
+}
+
+void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
+{
+ setSensorControls.emit(sensorControls);
+}
+
+void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)
+{
+ ispStatsReady.emit(frame, bufferId);
+}
+
+void SoftwareIsp::inputReady(FrameBuffer *input)
+{
+ ASSERT(queuedInputBuffers_.front() == input);
+ queuedInputBuffers_.pop_front();
+ inputBufferReady.emit(input);
+}
+
+void SoftwareIsp::outputReady(FrameBuffer *output)
+{
+ ASSERT(queuedOutputBuffers_.front() == output);
+ queuedOutputBuffers_.pop_front();
+ outputBufferReady.emit(output);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
new file mode 100644
index 00000000..c520c806
--- /dev/null
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
@@ -0,0 +1,434 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023, Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * CPU based software statistics implementation
+ */
+
+#include "swstats_cpu.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/bayer_format.h"
+
+namespace libcamera {
+
+/**
+ * \class SwStatsCpu
+ * \brief Class for gathering statistics on the CPU
+ *
+ * CPU based software ISP statistics implementation.
+ *
+ * This class offers a configure function + functions to gather statistics on a
+ * line by line basis. This allows CPU based software debayering to interleave
+ * debayering and statistics gathering on a line by line basis while the input
+ * data is still hot in the cache.
+ *
+ * It is also possible to specify a window over which to gather statistics
+ * instead of processing the whole frame.
+ */
+
+/**
+ * \fn bool SwStatsCpu::isValid() const
+ * \brief Gets whether the statistics object is valid
+ *
+ * \return True if it's valid, false otherwise
+ */
+
+/**
+ * \fn const SharedFD &SwStatsCpu::getStatsFD()
+ * \brief Get the file descriptor for the statistics
+ *
+ * \return The file descriptor
+ */
+
+/**
+ * \fn const Size &SwStatsCpu::patternSize()
+ * \brief Get the pattern size
+ *
+ * For some input-formats, e.g. Bayer data, processing is done multiple lines
+ * and/or columns at a time. Get width and height at which the (bayer) pattern
+ * repeats. Window values are rounded down to a multiple of this and the height
+ * also indicates if processLine2() should be called or not.
+ * This may only be called after a successful configure() call.
+ *
+ * \return The pattern size
+ */
+
+/**
+ * \fn void SwStatsCpu::processLine0(unsigned int y, const uint8_t *src[])
+ * \brief Process line 0
+ * \param[in] y The y coordinate.
+ * \param[in] src The input data.
+ *
+ * This function processes line 0 for input formats with
+ * patternSize height == 1.
+ * It'll process line 0 and 1 for input formats with patternSize height >= 2.
+ * This function may only be called after a successful setWindow() call.
+ */
+
+/**
+ * \fn void SwStatsCpu::processLine2(unsigned int y, const uint8_t *src[])
+ * \brief Process line 2 and 3
+ * \param[in] y The y coordinate.
+ * \param[in] src The input data.
+ *
+ * This function processes line 2 and 3 for input formats with
+ * patternSize height == 4.
+ * This function may only be called after a successful setWindow() call.
+ */
+
+/**
+ * \var Signal<> SwStatsCpu::statsReady
+ * \brief Signals that the statistics are ready
+ */
+
+/**
+ * \typedef SwStatsCpu::statsProcessFn
+ * \brief Called when there is data to get statistics from
+ * \param[in] src The input data
+ *
+ * These functions take an array of (patternSize_.height + 1) src
+ * pointers each pointing to a line in the source image. The middle
+ * element of the array will point to the actual line being processed.
+ * Earlier element(s) will point to the previous line(s) and later
+ * element(s) to the next line(s).
+ *
+ * See the documentation of DebayerCpu::debayerFn for more details.
+ */
+
+/**
+ * \var unsigned int SwStatsCpu::ySkipMask_
+ * \brief Skip lines where this bitmask is set in y
+ */
+
+/**
+ * \var Rectangle SwStatsCpu::window_
+ * \brief Statistics window, set by setWindow(), used every line
+ */
+
+/**
+ * \var Size SwStatsCpu::patternSize_
+ * \brief The size of the bayer pattern
+ *
+ * Valid sizes are: 2x2, 4x2 or 4x4.
+ */
+
+/**
+ * \var unsigned int SwStatsCpu::xShift_
+ * \brief The offset of x, applied to window_.x for bayer variants
+ *
+ * This can either be 0 or 1.
+ */
+
+LOG_DEFINE_CATEGORY(SwStatsCpu)
+
+SwStatsCpu::SwStatsCpu()
+ : sharedStats_("softIsp_stats")
+{
+ if (!sharedStats_)
+ LOG(SwStatsCpu, Error)
+ << "Failed to create shared memory for statistics";
+}
+
+static constexpr unsigned int kRedYMul = 77; /* 0.299 * 256 */
+static constexpr unsigned int kGreenYMul = 150; /* 0.587 * 256 */
+static constexpr unsigned int kBlueYMul = 29; /* 0.114 * 256 */
+
+#define SWSTATS_START_LINE_STATS(pixel_t) \
+ pixel_t r, g, g2, b; \
+ uint64_t yVal; \
+ \
+ uint64_t sumR = 0; \
+ uint64_t sumG = 0; \
+ uint64_t sumB = 0;
+
+#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
+ sumR += r; \
+ sumG += g; \
+ sumB += b; \
+ \
+ yVal = r * kRedYMul; \
+ yVal += g * kGreenYMul; \
+ yVal += b * kBlueYMul; \
+ stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
+
+#define SWSTATS_FINISH_LINE_STATS() \
+ stats_.sumR_ += sumR; \
+ stats_.sumG_ += sumG; \
+ stats_.sumB_ += sumB;
+
+void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
+{
+ const uint8_t *src0 = src[1] + window_.x;
+ const uint8_t *src1 = src[2] + window_.x;
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
+ for (int x = 0; x < (int)window_.width; x += 4) {
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+
+ g = (g + g2) / 2;
+
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
+{
+ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
+ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
+
+ SWSTATS_START_LINE_STATS(uint16_t)
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
+ for (int x = 0; x < (int)window_.width; x += 4) {
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+
+ g = (g + g2) / 2;
+
+ /* divide Y by 4 for 10 -> 8 bpp value */
+ SWSTATS_ACCUMULATE_LINE_STATS(4)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
+{
+ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
+ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
+
+ SWSTATS_START_LINE_STATS(uint16_t)
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 4 sample every other 2x2 block */
+ for (int x = 0; x < (int)window_.width; x += 4) {
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+
+ g = (g + g2) / 2;
+
+ /* divide Y by 16 for 12 -> 8 bpp value */
+ SWSTATS_ACCUMULATE_LINE_STATS(16)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
+{
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
+ const int widthInBytes = window_.width * 5 / 4;
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ /* x += 5 sample every other 2x2 block */
+ for (int x = 0; x < widthInBytes; x += 5) {
+ /* BGGR */
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+ g = (g + g2) / 2;
+ /* Data is already 8 bits, divide by 1 */
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
+{
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
+ const int widthInBytes = window_.width * 5 / 4;
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ /* x += 5 sample every other 2x2 block */
+ for (int x = 0; x < widthInBytes; x += 5) {
+ /* GBRG */
+ g = src0[x];
+ b = src0[x + 1];
+ r = src1[x];
+ g2 = src1[x + 1];
+ g = (g + g2) / 2;
+ /* Data is already 8 bits, divide by 1 */
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+/**
+ * \brief Reset state to start statistics gathering for a new frame
+ *
+ * This may only be called after a successful setWindow() call.
+ */
+void SwStatsCpu::startFrame(void)
+{
+ if (window_.width == 0)
+ LOG(SwStatsCpu, Error) << "Calling startFrame() without setWindow()";
+
+ stats_.sumR_ = 0;
+ stats_.sumB_ = 0;
+ stats_.sumG_ = 0;
+ stats_.yHistogram.fill(0);
+}
+
+/**
+ * \brief Finish statistics calculation for the current frame
+ * \param[in] frame The frame number
+ * \param[in] bufferId ID of the statistics buffer
+ *
+ * This may only be called after a successful setWindow() call.
+ */
+void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId)
+{
+ *sharedStats_ = stats_;
+ statsReady.emit(frame, bufferId);
+}
+
+/**
+ * \brief Setup SwStatsCpu object for standard Bayer orders
+ * \param[in] order The Bayer order
+ *
+ * Check if order is a standard Bayer order and setup xShift_ and swapLines_
+ * so that a single BGGR stats function can be used for all 4 standard orders.
+ */
+int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order)
+{
+ switch (order) {
+ case BayerFormat::BGGR:
+ xShift_ = 0;
+ swapLines_ = false;
+ break;
+ case BayerFormat::GBRG:
+ xShift_ = 1; /* BGGR -> GBRG */
+ swapLines_ = false;
+ break;
+ case BayerFormat::GRBG:
+ xShift_ = 0;
+ swapLines_ = true; /* BGGR -> GRBG */
+ break;
+ case BayerFormat::RGGB:
+ xShift_ = 1; /* BGGR -> GBRG */
+ swapLines_ = true; /* GBRG -> RGGB */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ patternSize_.height = 2;
+ patternSize_.width = 2;
+ ySkipMask_ = 0x02; /* Skip every 3th and 4th line */
+ return 0;
+}
+
+/**
+ * \brief Configure the statistics object for the passed in input format
+ * \param[in] inputCfg The input format
+ *
+ * \return 0 on success, a negative errno value on failure
+ */
+int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+{
+ BayerFormat bayerFormat =
+ BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+
+ if (bayerFormat.packing == BayerFormat::Packing::None &&
+ setupStandardBayerOrder(bayerFormat.order) == 0) {
+ switch (bayerFormat.bitDepth) {
+ case 8:
+ stats0_ = &SwStatsCpu::statsBGGR8Line0;
+ return 0;
+ case 10:
+ stats0_ = &SwStatsCpu::statsBGGR10Line0;
+ return 0;
+ case 12:
+ stats0_ = &SwStatsCpu::statsBGGR12Line0;
+ return 0;
+ }
+ }
+
+ if (bayerFormat.bitDepth == 10 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ patternSize_.height = 2;
+ patternSize_.width = 4; /* 5 bytes per *4* pixels */
+ /* Skip every 3th and 4th line, sample every other 2x2 block */
+ ySkipMask_ = 0x02;
+ xShift_ = 0;
+
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ case BayerFormat::GRBG:
+ stats0_ = &SwStatsCpu::statsBGGR10PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::GRBG;
+ return 0;
+ case BayerFormat::GBRG:
+ case BayerFormat::RGGB:
+ stats0_ = &SwStatsCpu::statsGBRG10PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::RGGB;
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ LOG(SwStatsCpu, Info)
+ << "Unsupported input format " << inputCfg.pixelFormat.toString();
+ return -EINVAL;
+}
+
+/**
+ * \brief Specify window coordinates over which to gather statistics
+ * \param[in] window The window object.
+ */
+void SwStatsCpu::setWindow(const Rectangle &window)
+{
+ window_ = window;
+
+ window_.x &= ~(patternSize_.width - 1);
+ window_.x += xShift_;
+ window_.y &= ~(patternSize_.height - 1);
+
+ /* width_ - xShift_ to make sure the window fits */
+ window_.width -= xShift_;
+ window_.width &= ~(patternSize_.width - 1);
+ window_.height &= ~(patternSize_.height - 1);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
new file mode 100644
index 00000000..26a2f462
--- /dev/null
+++ b/src/libcamera/software_isp/swstats_cpu.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Linaro Ltd
+ * Copyright (C) 2023, Red Hat Inc.
+ *
+ * Authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * CPU based software statistics implementation
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <libcamera/base/signal.h>
+
+#include <libcamera/geometry.h>
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/shared_mem_object.h"
+#include "libcamera/internal/software_isp/swisp_stats.h"
+
+namespace libcamera {
+
+class PixelFormat;
+struct StreamConfiguration;
+
+class SwStatsCpu
+{
+public:
+ SwStatsCpu();
+ ~SwStatsCpu() = default;
+
+ bool isValid() const { return sharedStats_.fd().isValid(); }
+
+ const SharedFD &getStatsFD() { return sharedStats_.fd(); }
+
+ const Size &patternSize() { return patternSize_; }
+
+ int configure(const StreamConfiguration &inputCfg);
+ void setWindow(const Rectangle &window);
+ void startFrame();
+ void finishFrame(uint32_t frame, uint32_t bufferId);
+
+ void processLine0(unsigned int y, const uint8_t *src[])
+ {
+ if ((y & ySkipMask_) || y < static_cast<unsigned int>(window_.y) ||
+ y >= (window_.y + window_.height))
+ return;
+
+ (this->*stats0_)(src);
+ }
+
+ void processLine2(unsigned int y, const uint8_t *src[])
+ {
+ if ((y & ySkipMask_) || y < static_cast<unsigned int>(window_.y) ||
+ y >= (window_.y + window_.height))
+ return;
+
+ (this->*stats2_)(src);
+ }
+
+ Signal<uint32_t, uint32_t> statsReady;
+
+private:
+ using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
+
+ int setupStandardBayerOrder(BayerFormat::Order order);
+ /* Bayer 8 bpp unpacked */
+ void statsBGGR8Line0(const uint8_t *src[]);
+ /* Bayer 10 bpp unpacked */
+ void statsBGGR10Line0(const uint8_t *src[]);
+ /* Bayer 12 bpp unpacked */
+ void statsBGGR12Line0(const uint8_t *src[]);
+ /* Bayer 10 bpp packed */
+ void statsBGGR10PLine0(const uint8_t *src[]);
+ void statsGBRG10PLine0(const uint8_t *src[]);
+
+ /* Variables set by configure(), used every line */
+ statsProcessFn stats0_;
+ statsProcessFn stats2_;
+ bool swapLines_;
+
+ unsigned int ySkipMask_;
+
+ Rectangle window_;
+
+ Size patternSize_;
+
+ unsigned int xShift_;
+
+ SharedMemObject<SwIspStats> sharedStats_;
+ SwIspStats stats_;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/source_paths.cpp b/src/libcamera/source_paths.cpp
index 19689585..1af5386a 100644
--- a/src/libcamera/source_paths.cpp
+++ b/src/libcamera/source_paths.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2021, Google Inc.
*
- * source_paths.cpp - Identify libcamera source and build paths
+ * Identify libcamera source and build paths
*/
#include "libcamera/internal/source_paths.h"
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index f3e00ead..f091487c 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -2,22 +2,22 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * stream.cpp - Video stream for a Camera
+ * Video stream for a Camera
*/
#include <libcamera/stream.h>
#include <algorithm>
#include <array>
-#include <iomanip>
#include <limits.h>
-#include <sstream>
-
-#include <libcamera/request.h>
+#include <ostream>
+#include <string>
+#include <vector>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
+#include <libcamera/request.h>
/**
* \file stream.h
@@ -392,7 +392,24 @@ StreamConfiguration::StreamConfiguration(const StreamFormats &formats)
*/
std::string StreamConfiguration::toString() const
{
- return size.toString() + "-" + pixelFormat.toString();
+ std::stringstream ss;
+ ss << *this;
+
+ return ss.str();
+}
+
+/**
+ * \brief Insert a text representation of a StreamConfiguration into an output
+ * stream
+ * \param[in] out The output stream
+ * \param[in] cfg The StreamConfiguration
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const StreamConfiguration &cfg)
+{
+ out << cfg.size << "-" << cfg.pixelFormat << "/"
+ << ColorSpace::toString(cfg.colorSpace);
+ return out;
}
/**
@@ -433,7 +450,7 @@ std::ostream &operator<<(std::ostream &out, StreamRole role)
"Viewfinder",
};
- out << names[static_cast<std::underlying_type_t<StreamRole>>(role)];
+ out << names[utils::to_underlying(role)];
return out;
}
diff --git a/src/libcamera/sysfs.cpp b/src/libcamera/sysfs.cpp
index 44c3331b..3d9885b0 100644
--- a/src/libcamera/sysfs.cpp
+++ b/src/libcamera/sysfs.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * sysfs.cpp - Miscellaneous utility functions to access sysfs
+ * Miscellaneous utility functions to access sysfs
*/
#include "libcamera/internal/sysfs.h"
diff --git a/src/libcamera/tracepoints.cpp b/src/libcamera/tracepoints.cpp
index 0173b75a..90662d12 100644
--- a/src/libcamera/tracepoints.cpp
+++ b/src/libcamera/tracepoints.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * tracepoints.cpp - Tracepoints with lttng
+ * Tracepoints with lttng
*/
#define TRACEPOINT_CREATE_PROBES
#define TRACEPOINT_DEFINE
diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp
index fb2d55ac..9fe8b562 100644
--- a/src/libcamera/transform.cpp
+++ b/src/libcamera/transform.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * transform.cpp - 2D plane transforms.
+ * 2D plane transforms.
*/
#include <libcamera/transform.h>
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index 24d208ef..0db92c19 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -2,15 +2,14 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_device.cpp - Common base for V4L2 video devices and subdevices
+ * Common base for V4L2 video devices and subdevices
*/
#include "libcamera/internal/v4l2_device.h"
#include <fcntl.h>
-#include <iomanip>
-#include <limits.h>
#include <map>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
@@ -206,10 +205,29 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
ControlType type;
+ ControlValue &value = ctrl.second;
+ Span<uint8_t> data;
switch (info.type) {
case V4L2_CTRL_TYPE_U8:
type = ControlTypeByte;
+ value.reserve(type, true, info.elems);
+ data = value.data();
+ v4l2Ctrl.p_u8 = data.data();
+ break;
+
+ case V4L2_CTRL_TYPE_U16:
+ type = ControlTypeUnsigned16;
+ value.reserve(type, true, info.elems);
+ data = value.data();
+ v4l2Ctrl.p_u16 = reinterpret_cast<uint16_t *>(data.data());
+ break;
+
+ case V4L2_CTRL_TYPE_U32:
+ type = ControlTypeUnsigned32;
+ value.reserve(type, true, info.elems);
+ data = value.data();
+ v4l2Ctrl.p_u32 = reinterpret_cast<uint32_t *>(data.data());
break;
default:
@@ -219,11 +237,6 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
return {};
}
- ControlValue &value = ctrl.second;
- value.reserve(type, true, info.elems);
- Span<uint8_t> data = value.data();
-
- v4l2Ctrl.p_u8 = data.data();
v4l2Ctrl.size = data.size();
}
}
@@ -301,6 +314,30 @@ int V4L2Device::setControls(ControlList *ctrls)
/* Set the v4l2_ext_control value for the write operation. */
ControlValue &value = ctrl->second;
switch (iter->first->type()) {
+ case ControlTypeUnsigned16: {
+ if (value.isArray()) {
+ Span<uint8_t> data = value.data();
+ v4l2Ctrl.p_u16 = reinterpret_cast<uint16_t *>(data.data());
+ v4l2Ctrl.size = data.size();
+ } else {
+ v4l2Ctrl.value = value.get<uint16_t>();
+ }
+
+ break;
+ }
+
+ case ControlTypeUnsigned32: {
+ if (value.isArray()) {
+ Span<uint8_t> data = value.data();
+ v4l2Ctrl.p_u32 = reinterpret_cast<uint32_t *>(data.data());
+ v4l2Ctrl.size = data.size();
+ } else {
+ v4l2Ctrl.value = value.get<uint32_t>();
+ }
+
+ break;
+ }
+
case ControlTypeInteger32: {
if (value.isArray()) {
Span<uint8_t> data = value.data();
@@ -413,6 +450,28 @@ std::string V4L2Device::devicePath() const
}
/**
+ * \brief Check if frame start event is supported
+ *
+ * Due to limitations in the kernel API, this function may disable the frame
+ * start event as a side effect. It should only be called during initialization,
+ * before enabling the frame start event with setFrameStartEnabled().
+ *
+ * \return True if frame start event is supported, false otherwise
+ */
+bool V4L2Device::supportsFrameStartEvent()
+{
+ struct v4l2_event_subscription event{};
+ event.type = V4L2_EVENT_FRAME_SYNC;
+
+ int ret = ioctl(VIDIOC_SUBSCRIBE_EVENT, &event);
+ if (ret)
+ return false;
+
+ ioctl(VIDIOC_UNSUBSCRIBE_EVENT, &event);
+ return true;
+}
+
+/**
* \brief Enable or disable frame start event notification
* \param[in] enable True to enable frame start events, false to disable them
*
@@ -490,6 +549,12 @@ ControlType V4L2Device::v4l2CtrlType(uint32_t ctrlType)
case V4L2_CTRL_TYPE_BOOLEAN:
return ControlTypeBool;
+ case V4L2_CTRL_TYPE_U16:
+ return ControlTypeUnsigned16;
+
+ case V4L2_CTRL_TYPE_U32:
+ return ControlTypeUnsigned32;
+
case V4L2_CTRL_TYPE_INTEGER:
return ControlTypeInteger32;
@@ -522,7 +587,15 @@ std::unique_ptr<ControlId> V4L2Device::v4l2ControlId(const v4l2_query_ext_ctrl &
const std::string name(static_cast<const char *>(ctrl.name), len);
const ControlType type = v4l2CtrlType(ctrl.type);
- return std::make_unique<ControlId>(ctrl.id, name, type);
+ ControlId::DirectionFlags flags;
+ if (ctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)
+ flags = ControlId::Direction::Out;
+ else if (ctrl.flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ flags = ControlId::Direction::In;
+ else
+ flags = ControlId::Direction::In | ControlId::Direction::Out;
+
+ return std::make_unique<ControlId>(ctrl.id, name, "v4l2", type, flags);
}
/**
@@ -538,6 +611,16 @@ std::optional<ControlInfo> V4L2Device::v4l2ControlInfo(const v4l2_query_ext_ctrl
static_cast<uint8_t>(ctrl.maximum),
static_cast<uint8_t>(ctrl.default_value));
+ case V4L2_CTRL_TYPE_U16:
+ return ControlInfo(static_cast<uint16_t>(ctrl.minimum),
+ static_cast<uint16_t>(ctrl.maximum),
+ static_cast<uint16_t>(ctrl.default_value));
+
+ case V4L2_CTRL_TYPE_U32:
+ return ControlInfo(static_cast<uint32_t>(ctrl.minimum),
+ static_cast<uint32_t>(ctrl.maximum),
+ static_cast<uint32_t>(ctrl.default_value));
+
case V4L2_CTRL_TYPE_BOOLEAN:
return ControlInfo(static_cast<bool>(ctrl.minimum),
static_cast<bool>(ctrl.maximum),
@@ -624,6 +707,8 @@ void V4L2Device::listControls()
case V4L2_CTRL_TYPE_BITMASK:
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_U8:
+ case V4L2_CTRL_TYPE_U16:
+ case V4L2_CTRL_TYPE_U32:
break;
/* \todo Support other control types. */
default:
diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
index 5551c62e..e8b3eb9c 100644
--- a/src/libcamera/v4l2_pixelformat.cpp
+++ b/src/libcamera/v4l2_pixelformat.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2019, Google Inc.
* Copyright (C) 2020, Raspberry Pi Ltd
*
- * v4l2_pixelformat.cpp - V4L2 Pixel Format
+ * V4L2 Pixel Format
*/
#include "libcamera/internal/v4l2_pixelformat.h"
@@ -71,6 +71,10 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
{ formats::BGRA8888, "32-bit ARGB 8-8-8-8" } },
{ V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),
{ formats::RGBA8888, "32-bit ABGR 8-8-8-8" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_RGB48),
+ { formats::BGR161616, "48-bit RGB 16-16-16" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_BGR48),
+ { formats::RGB161616, "48-bit BGR 16-16-16" } },
/* YUV packed formats. */
{ V4L2PixelFormat(V4L2_PIX_FMT_YUYV),
@@ -135,6 +139,10 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
{ formats::R10_CSI2P, "10-bit Greyscale Packed" } },
{ V4L2PixelFormat(V4L2_PIX_FMT_Y12),
{ formats::R12, "12-bit Greyscale" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_Y12P),
+ { formats::R12_CSI2P, "12-bit Greyscale Packed" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_Y16),
+ { formats::R16, "16-bit Greyscale" } },
/* Bayer formats. */
{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),
@@ -201,6 +209,16 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
{ formats::SGRBG16, "16-bit Bayer GRGR/BGBG" } },
{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),
{ formats::SRGGB16, "16-bit Bayer RGRG/GBGB" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_BGGR),
+ { formats::BGGR_PISP_COMP1, "16-bit Bayer BGBG/GRGR PiSP Compress Mode 1" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GBRG),
+ { formats::GBRG_PISP_COMP1, "16-bit Bayer GBGB/RGRG PiSP Compress Mode 1" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_GRBG),
+ { formats::GRBG_PISP_COMP1, "16-bit Bayer GRGR/BGBG PiSP Compress Mode 1" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_RGGB),
+ { formats::RGGB_PISP_COMP1, "16-bit Bayer RGRG/GBGB PiSP Compress Mode 1" } },
+ { V4L2PixelFormat(V4L2_PIX_FMT_PISP_COMP1_MONO),
+ { formats::MONO_PISP_COMP1, "16-bit Mono PiSP Compress Mode 1" } },
/* Compressed formats. */
{ V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),
@@ -355,6 +373,40 @@ V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat)
}
/**
+ * \brief Test if a V4L2PixelFormat is one of the line based generic metadata
+ * formats
+ *
+ * A limited number of metadata formats, the ones that represents generic
+ * line-based metadata buffers, need to have their width, height and
+ * bytesperline set by userspace.
+ *
+ * This function tests if the current V4L2PixelFormat is one of those.
+ *
+ * Note: It would have been nicer to store this information in a
+ * V4L2PixelFormat::Info instance, but as metadata format are not exposed to
+ * applications, there are no PixelFormat and DRM fourcc codes associated to
+ * them.
+ *
+ * \return True if the V4L2PixelFormat() is a generic line based format, false
+ * otherwise
+ */
+bool V4L2PixelFormat::isGenericLineBasedMetadata() const
+{
+ switch (fourcc_) {
+ case V4L2_META_FMT_GENERIC_8:
+ case V4L2_META_FMT_GENERIC_CSI2_10:
+ case V4L2_META_FMT_GENERIC_CSI2_12:
+ case V4L2_META_FMT_GENERIC_CSI2_14:
+ case V4L2_META_FMT_GENERIC_CSI2_16:
+ case V4L2_META_FMT_GENERIC_CSI2_20:
+ case V4L2_META_FMT_GENERIC_CSI2_24:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
* \brief Insert a text representation of a V4L2PixelFormat into an output
* stream
* \param[in] out The output stream
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
index 15e8206a..33279654 100644
--- a/src/libcamera/v4l2_subdevice.cpp
+++ b/src/libcamera/v4l2_subdevice.cpp
@@ -2,27 +2,32 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_subdevice.cpp - V4L2 Subdevice
+ * V4L2 Subdevice
*/
#include "libcamera/internal/v4l2_subdevice.h"
#include <fcntl.h>
-#include <iomanip>
-#include <regex>
#include <sstream>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
+#pragma GCC diagnostic push
+#if defined __SANITIZE_ADDRESS__ && defined __OPTIMIZE__
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#include <regex>
+#pragma GCC diagnostic pop
+
#include <linux/media-bus-format.h>
#include <linux/v4l2-subdev.h>
-#include <libcamera/geometry.h>
-
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
+#include <libcamera/geometry.h>
+
#include "libcamera/internal/formats.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/media_object.h"
@@ -36,110 +41,808 @@ namespace libcamera {
LOG_DECLARE_CATEGORY(V4L2)
-namespace {
-
-/*
- * \struct V4L2SubdeviceFormatInfo
+/**
+ * \class MediaBusFormatInfo
* \brief Information about media bus formats
- * \param bitsPerPixel Bits per pixel
- * \param name Name of MBUS format
- * \param colourEncoding Type of colour encoding
+ *
+ * The MediaBusFormatInfo class groups together information describing a media
+ * bus format. It facilitates handling of media bus formats by providing data
+ * commonly used in pipeline handlers.
+ *
+ * \var MediaBusFormatInfo::name
+ * \brief The format name as a human-readable string, used as the text
+ * representation of the format
+ *
+ * \var MediaBusFormatInfo::code
+ * \brief The media bus format code described by this instance (MEDIA_BUS_FMT_*)
+ *
+ * \var MediaBusFormatInfo::type
+ * \brief The media bus format type
+ *
+ * \var MediaBusFormatInfo::bitsPerPixel
+ * \brief The average number of bits per pixel
+ *
+ * The number of bits per pixel averages the total number of bits for all
+ * colour components over the whole image, excluding any padding bits or
+ * padding pixels.
+ *
+ * For formats that transmit multiple or fractional pixels per sample, the
+ * value will differ from the bus width.
+ *
+ * Formats that don't have a fixed number of bits per pixel, such as compressed
+ * formats, or device-specific embedded data formats, report 0 in this field.
+ *
+ * \var MediaBusFormatInfo::colourEncoding
+ * \brief The colour encoding type
+ *
+ * This field is valid for Type::Image formats only.
*/
-struct V4L2SubdeviceFormatInfo {
- unsigned int bitsPerPixel;
- const char *name;
- PixelFormatInfo::ColourEncoding colourEncoding;
-};
-/*
- * \var formatInfoMap
- * \brief A map that associates V4L2SubdeviceFormatInfo struct to V4L2 media
- * bus codes
- */
-const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = {
- { MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, { 16, "RGB444_2X8_PADHI_BE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, { 16, "RGB444_2X8_PADHI_LE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, { 16, "RGB555_2X8_PADHI_BE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, { 16, "RGB555_2X8_PADHI_LE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB565_1X16, { 16, "RGB565_1X16", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_BGR565_2X8_BE, { 16, "BGR565_2X8_BE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_BGR565_2X8_LE, { 16, "BGR565_2X8_LE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB565_2X8_BE, { 16, "RGB565_2X8_BE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB565_2X8_LE, { 16, "RGB565_2X8_LE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB666_1X18, { 18, "RGB666_1X18", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_BGR888_1X24, { 24, "BGR888_1X24", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB888_1X24, { 24, "RGB888_1X24", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB888_2X12_BE, { 24, "RGB888_2X12_BE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_RGB888_2X12_LE, { 24, "RGB888_2X12_LE", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_ARGB8888_1X32, { 32, "ARGB8888_1X32", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_Y8_1X8, { 8, "Y8_1X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UV8_1X8, { 8, "UV8_1X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY8_1_5X8, { 12, "UYVY8_1_5X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY8_1_5X8, { 12, "VYUY8_1_5X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV8_1_5X8, { 12, "YUYV8_1_5X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU8_1_5X8, { 12, "YVYU8_1_5X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY8_2X8, { 16, "UYVY8_2X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY8_2X8, { 16, "VYUY8_2X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV8_2X8, { 16, "YUYV8_2X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU8_2X8, { 16, "YVYU8_2X8", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_Y10_1X10, { 10, "Y10_1X10", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY10_2X10, { 20, "UYVY10_2X10", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY10_2X10, { 20, "VYUY10_2X10", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV10_2X10, { 20, "YUYV10_2X10", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU10_2X10, { 20, "YVYU10_2X10", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_Y12_1X12, { 12, "Y12_1X12", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY8_1X16, { 16, "UYVY8_1X16", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY8_1X16, { 16, "VYUY8_1X16", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV8_1X16, { 16, "YUYV8_1X16", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU8_1X16, { 16, "YVYU8_1X16", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YDYUYDYV8_1X16, { 16, "YDYUYDYV8_1X16", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY10_1X20, { 20, "UYVY10_1X20", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY10_1X20, { 20, "VYUY10_1X20", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV10_1X20, { 20, "YUYV10_1X20", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU10_1X20, { 20, "YVYU10_1X20", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUV8_1X24, { 24, "YUV8_1X24", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUV10_1X30, { 30, "YUV10_1X30", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_AYUV8_1X32, { 32, "AYUV8_1X32", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY12_2X12, { 24, "UYVY12_2X12", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY12_2X12, { 24, "VYUY12_2X12", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV12_2X12, { 24, "YUYV12_2X12", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU12_2X12, { 24, "YVYU12_2X12", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_UYVY12_1X24, { 24, "UYVY12_1X24", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_VYUY12_1X24, { 24, "VYUY12_1X24", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YUYV12_1X24, { 24, "YUYV12_1X24", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_YVYU12_1X24, { 24, "YVYU12_1X24", PixelFormatInfo::ColourEncodingYUV } },
- { MEDIA_BUS_FMT_SBGGR8_1X8, { 8, "SBGGR8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGBRG8_1X8, { 8, "SGBRG8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGRBG8_1X8, { 8, "SGRBG8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SRGGB8_1X8, { 8, "SRGGB8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, { 8, "SBGGR10_ALAW8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, { 8, "SGBRG10_ALAW8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, { 8, "SGRBG10_ALAW8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, { 8, "SRGGB10_ALAW8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, { 8, "SBGGR10_DPCM8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, { 8, "SGBRG10_DPCM8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, { 8, "SGRBG10_DPCM8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, { 8, "SRGGB10_DPCM8_1X8", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, { 16, "SBGGR10_2X8_PADHI_BE", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, { 16, "SBGGR10_2X8_PADHI_LE", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, { 16, "SBGGR10_2X8_PADLO_BE", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, { 16, "SBGGR10_2X8_PADLO_LE", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR10_1X10, { 10, "SBGGR10_1X10", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
- { MEDIA_BUS_FMT_SRGGB12_1X12, { 12, "SRGGB12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+/**
+ * \enum MediaBusFormatInfo::Type
+ * \brief The format type
+ *
+ * \var MediaBusFormatInfo::Type::Image
+ * \brief The format describes image data
+ *
+ * \var MediaBusFormatInfo::Type::Metadata
+ * \brief The format describes generic metadata
+ *
+ * \var MediaBusFormatInfo::Type::EmbeddedData
+ * \brief The format describes sensor embedded data
+ */
+
+namespace {
+
+const std::map<uint32_t, MediaBusFormatInfo> mediaBusFormatInfo{
+ /* This table is sorted to match the order in linux/media-bus-format.h */
+ { MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, {
+ .name = "RGB444_2X8_PADHI_BE",
+ .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, {
+ .name = "RGB444_2X8_PADHI_LE",
+ .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, {
+ .name = "RGB555_2X8_PADHI_BE",
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, {
+ .name = "RGB555_2X8_PADHI_LE",
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB565_1X16, {
+ .name = "RGB565_1X16",
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_BGR565_2X8_BE, {
+ .name = "BGR565_2X8_BE",
+ .code = MEDIA_BUS_FMT_BGR565_2X8_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_BGR565_2X8_LE, {
+ .name = "BGR565_2X8_LE",
+ .code = MEDIA_BUS_FMT_BGR565_2X8_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB565_2X8_BE, {
+ .name = "RGB565_2X8_BE",
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB565_2X8_LE, {
+ .name = "RGB565_2X8_LE",
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB666_1X18, {
+ .name = "RGB666_1X18",
+ .code = MEDIA_BUS_FMT_RGB666_1X18,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 18,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_BGR888_1X24, {
+ .name = "BGR888_1X24",
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB888_1X24, {
+ .name = "RGB888_1X24",
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB888_2X12_BE, {
+ .name = "RGB888_2X12_BE",
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB888_2X12_LE, {
+ .name = "RGB888_2X12_LE",
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB121212_1X36, {
+ .name = "RGB121212_1X36",
+ .code = MEDIA_BUS_FMT_RGB121212_1X36,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 36,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_RGB202020_1X60, {
+ .name = "RGB202020_1X60",
+ .code = MEDIA_BUS_FMT_RGB202020_1X60,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 60,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_ARGB8888_1X32, {
+ .name = "ARGB8888_1X32",
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 32,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_Y8_1X8, {
+ .name = "Y8_1X8",
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UV8_1X8, {
+ .name = "UV8_1X8",
+ .code = MEDIA_BUS_FMT_UV8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, {
+ .name = "UYVY8_1_5X8",
+ .code = MEDIA_BUS_FMT_UYVY8_1_5X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, {
+ .name = "VYUY8_1_5X8",
+ .code = MEDIA_BUS_FMT_VYUY8_1_5X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, {
+ .name = "YUYV8_1_5X8",
+ .code = MEDIA_BUS_FMT_YUYV8_1_5X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, {
+ .name = "YVYU8_1_5X8",
+ .code = MEDIA_BUS_FMT_YVYU8_1_5X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY8_2X8, {
+ .name = "UYVY8_2X8",
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, {
+ .name = "VYUY8_2X8",
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, {
+ .name = "YUYV8_2X8",
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, {
+ .name = "YVYU8_2X8",
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_Y10_1X10, {
+ .name = "Y10_1X10",
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY10_2X10, {
+ .name = "UYVY10_2X10",
+ .code = MEDIA_BUS_FMT_UYVY10_2X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY10_2X10, {
+ .name = "VYUY10_2X10",
+ .code = MEDIA_BUS_FMT_VYUY10_2X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV10_2X10, {
+ .name = "YUYV10_2X10",
+ .code = MEDIA_BUS_FMT_YUYV10_2X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU10_2X10, {
+ .name = "YVYU10_2X10",
+ .code = MEDIA_BUS_FMT_YVYU10_2X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_Y12_1X12, {
+ .name = "Y12_1X12",
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_Y16_1X16, {
+ .name = "Y16_1X16",
+ .code = MEDIA_BUS_FMT_Y16_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY8_1X16, {
+ .name = "UYVY8_1X16",
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY8_1X16, {
+ .name = "VYUY8_1X16",
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV8_1X16, {
+ .name = "YUYV8_1X16",
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU8_1X16, {
+ .name = "YVYU8_1X16",
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YDYUYDYV8_1X16, {
+ .name = "YDYUYDYV8_1X16",
+ .code = MEDIA_BUS_FMT_YDYUYDYV8_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY10_1X20, {
+ .name = "UYVY10_1X20",
+ .code = MEDIA_BUS_FMT_UYVY10_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY10_1X20, {
+ .name = "VYUY10_1X20",
+ .code = MEDIA_BUS_FMT_VYUY10_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV10_1X20, {
+ .name = "YUYV10_1X20",
+ .code = MEDIA_BUS_FMT_YUYV10_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU10_1X20, {
+ .name = "YVYU10_1X20",
+ .code = MEDIA_BUS_FMT_YVYU10_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUV8_1X24, {
+ .name = "YUV8_1X24",
+ .code = MEDIA_BUS_FMT_YUV8_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUV10_1X30, {
+ .name = "YUV10_1X30",
+ .code = MEDIA_BUS_FMT_YUV10_1X30,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 30,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_AYUV8_1X32, {
+ .name = "AYUV8_1X32",
+ .code = MEDIA_BUS_FMT_AYUV8_1X32,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 32,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY12_2X12, {
+ .name = "UYVY12_2X12",
+ .code = MEDIA_BUS_FMT_UYVY12_2X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY12_2X12, {
+ .name = "VYUY12_2X12",
+ .code = MEDIA_BUS_FMT_VYUY12_2X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV12_2X12, {
+ .name = "YUYV12_2X12",
+ .code = MEDIA_BUS_FMT_YUYV12_2X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU12_2X12, {
+ .name = "YVYU12_2X12",
+ .code = MEDIA_BUS_FMT_YVYU12_2X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_UYVY12_1X24, {
+ .name = "UYVY12_1X24",
+ .code = MEDIA_BUS_FMT_UYVY12_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_VYUY12_1X24, {
+ .name = "VYUY12_1X24",
+ .code = MEDIA_BUS_FMT_VYUY12_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YUYV12_1X24, {
+ .name = "YUYV12_1X24",
+ .code = MEDIA_BUS_FMT_YUYV12_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_YVYU12_1X24, {
+ .name = "YVYU12_1X24",
+ .code = MEDIA_BUS_FMT_YVYU12_1X24,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, {
+ .name = "SBGGR8_1X8",
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, {
+ .name = "SGBRG8_1X8",
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, {
+ .name = "SGRBG8_1X8",
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, {
+ .name = "SRGGB8_1X8",
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, {
+ .name = "SBGGR10_ALAW8_1X8",
+ .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, {
+ .name = "SGBRG10_ALAW8_1X8",
+ .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, {
+ .name = "SGRBG10_ALAW8_1X8",
+ .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, {
+ .name = "SRGGB10_ALAW8_1X8",
+ .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, {
+ .name = "SBGGR10_DPCM8_1X8",
+ .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, {
+ .name = "SGBRG10_DPCM8_1X8",
+ .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, {
+ .name = "SGRBG10_DPCM8_1X8",
+ .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, {
+ .name = "SRGGB10_DPCM8_1X8",
+ .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, {
+ .name = "SBGGR10_2X8_PADHI_BE",
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, {
+ .name = "SBGGR10_2X8_PADHI_LE",
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, {
+ .name = "SBGGR10_2X8_PADLO_BE",
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, {
+ .name = "SBGGR10_2X8_PADLO_LE",
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, {
+ .name = "SBGGR10_1X10",
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, {
+ .name = "SGBRG10_1X10",
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, {
+ .name = "SGRBG10_1X10",
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, {
+ .name = "SRGGB10_1X10",
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, {
+ .name = "SBGGR12_1X12",
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, {
+ .name = "SGBRG12_1X12",
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, {
+ .name = "SGRBG12_1X12",
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, {
+ .name = "SRGGB12_1X12",
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, {
+ .name = "SBGGR14_1X14",
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 14,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, {
+ .name = "SGBRG14_1X14",
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 14,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, {
+ .name = "SGRBG14_1X14",
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 14,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, {
+ .name = "SRGGB14_1X14",
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 14,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_SBGGR16_1X16, {
+ .name = "SBGGR16_1X16",
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SGBRG16_1X16, {
+ .name = "SGBRG16_1X16",
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SGRBG16_1X16, {
+ .name = "SGRBG16_1X16",
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SRGGB16_1X16, {
+ .name = "SRGGB16_1X16",
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SBGGR20_1X20, {
+ .name = "SBGGR20_1X20",
+ .code = MEDIA_BUS_FMT_SBGGR20_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SGBRG20_1X20, {
+ .name = "SGBRG20_1X20",
+ .code = MEDIA_BUS_FMT_SGBRG20_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SGRBG20_1X20, {
+ .name = "SGRBG20_1X20",
+ .code = MEDIA_BUS_FMT_SGRBG20_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
+ { MEDIA_BUS_FMT_SRGGB20_1X20, {
+ .name = "SRGGB20_1X20",
+ .code = MEDIA_BUS_FMT_SRGGB20_1X20,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW
+ } },
/* \todo Clarify colour encoding for HSV formats */
- { MEDIA_BUS_FMT_AHSV8888_1X32, { 32, "AHSV8888_1X32", PixelFormatInfo::ColourEncodingRGB } },
- { MEDIA_BUS_FMT_JPEG_1X8, { 8, "JPEG_1X8", PixelFormatInfo::ColourEncodingYUV } },
+ { MEDIA_BUS_FMT_AHSV8888_1X32, {
+ .name = "AHSV8888_1X32",
+ .code = MEDIA_BUS_FMT_AHSV8888_1X32,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 32,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+ } },
+ { MEDIA_BUS_FMT_JPEG_1X8, {
+ .name = "JPEG_1X8",
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
+ .type = MediaBusFormatInfo::Type::Image,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+ } },
+ { MEDIA_BUS_FMT_METADATA_FIXED, {
+ .name = "METADATA_FIXED",
+ .code = MEDIA_BUS_FMT_METADATA_FIXED,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 0,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_8, {
+ .name = "META_8",
+ .code = MEDIA_BUS_FMT_META_8,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 8,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_10, {
+ .name = "META_10",
+ .code = MEDIA_BUS_FMT_META_10,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 10,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_12, {
+ .name = "META_12",
+ .code = MEDIA_BUS_FMT_META_12,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 12,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_14, {
+ .name = "META_14",
+ .code = MEDIA_BUS_FMT_META_14,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 14,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_16, {
+ .name = "META_16",
+ .code = MEDIA_BUS_FMT_META_16,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 16,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_20, {
+ .name = "META_20",
+ .code = MEDIA_BUS_FMT_META_20,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 20,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_META_24, {
+ .name = "META_24",
+ .code = MEDIA_BUS_FMT_META_24,
+ .type = MediaBusFormatInfo::Type::Metadata,
+ .bitsPerPixel = 24,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_CCS_EMBEDDED, {
+ .name = "CCS_EMBEDDED",
+ .code = MEDIA_BUS_FMT_CCS_EMBEDDED,
+ .type = MediaBusFormatInfo::Type::EmbeddedData,
+ .bitsPerPixel = 0,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
+ { MEDIA_BUS_FMT_OV2740_EMBEDDED, {
+ .name = "OV2740_EMBEDDED",
+ .code = MEDIA_BUS_FMT_CCS_EMBEDDED,
+ .type = MediaBusFormatInfo::Type::EmbeddedData,
+ .bitsPerPixel = 0,
+ .colourEncoding = PixelFormatInfo::ColourEncodingRAW,
+ } },
};
} /* namespace */
/**
+ * \fn bool MediaBusFormatInfo::isValid() const
+ * \brief Check if the media bus format info is valid
+ * \return True if the media bus format info is valid, false otherwise
+ */
+
+/**
+ * \brief Retrieve information about a media bus format
+ * \param[in] code The media bus format code
+ * \return The MediaBusFormatInfo describing the \a code if known, or an invalid
+ * MediaBusFormatInfo otherwise
+ */
+const MediaBusFormatInfo &MediaBusFormatInfo::info(uint32_t code)
+{
+ static const MediaBusFormatInfo invalid{};
+
+ const auto it = mediaBusFormatInfo.find(code);
+ if (it == mediaBusFormatInfo.end()) {
+ LOG(V4L2, Warning)
+ << "Unsupported media bus format "
+ << utils::hex(code, 4);
+ return invalid;
+ }
+
+ return it->second;
+}
+
+/**
* \struct V4L2SubdeviceCapability
* \brief struct v4l2_subdev_capability object wrapper and helpers
*
@@ -192,7 +895,7 @@ const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = {
*/
/**
- * \var V4L2SubdeviceFormat::mbus_code
+ * \var V4L2SubdeviceFormat::code
* \brief The image format bus code
*/
@@ -229,23 +932,6 @@ const std::string V4L2SubdeviceFormat::toString() const
}
/**
- * \brief Retrieve the number of bits per pixel for the V4L2 subdevice format
- * \return The number of bits per pixel for the format, or 0 if the format is
- * not supported
- */
-uint8_t V4L2SubdeviceFormat::bitsPerPixel() const
-{
- const auto it = formatInfoMap.find(mbus_code);
- if (it == formatInfoMap.end()) {
- LOG(V4L2, Error) << "No information available for format '"
- << *this << "'";
- return 0;
- }
-
- return it->second.bitsPerPixel;
-}
-
-/**
* \brief Insert a text representation of a V4L2SubdeviceFormat into an output
* stream
* \param[in] out The output stream
@@ -256,10 +942,10 @@ std::ostream &operator<<(std::ostream &out, const V4L2SubdeviceFormat &f)
{
out << f.size << "-";
- const auto it = formatInfoMap.find(f.mbus_code);
+ const auto it = mediaBusFormatInfo.find(f.code);
- if (it == formatInfoMap.end())
- out << utils::hex(f.mbus_code, 4);
+ if (it == mediaBusFormatInfo.end())
+ out << utils::hex(f.code, 4);
else
out << it->second.name;
@@ -295,30 +981,131 @@ std::ostream &operator<<(std::ostream &out, const V4L2SubdeviceFormat &f)
*/
/**
- * \class V4L2Subdevice::Routing
+ * \class V4L2Subdevice::Stream
+ * \brief V4L2 subdevice stream
+ *
+ * This class identifies a subdev stream, by bundling the pad number with the
+ * stream number. It is used in all stream-aware functions of the V4L2Subdevice
+ * class to identify the stream the functions operate on.
+ *
+ * \var V4L2Subdevice::Stream::pad
+ * \brief The 0-indexed pad number
+ *
+ * \var V4L2Subdevice::Stream::stream
+ * \brief The stream number
+ */
+
+/**
+ * \fn V4L2Subdevice::Stream::Stream()
+ * \brief Construct a Stream with pad and stream set to 0
+ */
+
+/**
+ * \fn V4L2Subdevice::Stream::Stream(unsigned int pad, unsigned int stream)
+ * \brief Construct a Stream with a given \a pad and \a stream number
+ * \param[in] pad The indexed pad number
+ * \param[in] stream The stream number
+ */
+
+/**
+ * \brief Compare streams for equality
+ * \return True if the two streams are equal, false otherwise
+ */
+bool operator==(const V4L2Subdevice::Stream &lhs, const V4L2Subdevice::Stream &rhs)
+{
+ return lhs.pad == rhs.pad && lhs.stream == rhs.stream;
+}
+
+/**
+ * \fn bool operator!=(const V4L2Subdevice::Stream &lhs, const V4L2Subdevice::Stream &rhs)
+ * \brief Compare streams for inequality
+ * \return True if the two streams are not equal, false otherwise
+ */
+
+/**
+ * \brief Insert a text representation of a V4L2Subdevice::Stream into an
+ * output stream
+ * \param[in] out The output stream
+ * \param[in] stream The V4L2Subdevice::Stream
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Stream &stream)
+{
+ out << stream.pad << "/" << stream.stream;
+
+ return out;
+}
+
+/**
+ * \class V4L2Subdevice::Route
+ * \brief V4L2 subdevice routing table entry
+ *
+ * This class models a route in the subdevice routing table. It is similar to
+ * the v4l2_subdev_route structure, but uses the V4L2Subdevice::Stream class
+ * for easier usage with the V4L2Subdevice stream-aware functions.
+ *
+ * \var V4L2Subdevice::Route::sink
+ * \brief The sink stream of the route
+ *
+ * \var V4L2Subdevice::Route::source
+ * \brief The source stream of the route
+ *
+ * \var V4L2Subdevice::Route::flags
+ * \brief The route flags (V4L2_SUBDEV_ROUTE_FL_*)
+ */
+
+/**
+ * \fn V4L2Subdevice::Route::Route()
+ * \brief Construct a Route with default streams
+ */
+
+/**
+ * \fn V4L2Subdevice::Route::Route(const Stream &sink, const Stream &source,
+ * uint32_t flags)
+ * \brief Construct a Route from \a sink to \a source
+ * \param[in] sink The sink stream
+ * \param[in] source The source stream
+ * \param[in] flags The route flags
+ */
+
+/**
+ * \brief Insert a text representation of a V4L2Subdevice::Route into an
+ * output stream
+ * \param[in] out The output stream
+ * \param[in] route The V4L2Subdevice::Route
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Route &route)
+{
+ out << route.sink << " -> " << route.source
+ << " (" << utils::hex(route.flags) << ")";
+
+ return out;
+}
+
+/**
+ * \typedef V4L2Subdevice::Routing
* \brief V4L2 subdevice routing table
*
* This class stores a subdevice routing table as a vector of routes.
*/
/**
- * \brief Assemble and return a string describing the routing table
- * \return A string describing the routing table
+ * \brief Insert a text representation of a V4L2Subdevice::Routing into an
+ * output stream
+ * \param[in] out The output stream
+ * \param[in] routing The V4L2Subdevice::Routing
+ * \return The output stream \a out
*/
-std::string V4L2Subdevice::Routing::toString() const
+std::ostream &operator<<(std::ostream &out, const V4L2Subdevice::Routing &routing)
{
- std::stringstream routing;
-
- for (const auto &[i, route] : utils::enumerate(*this)) {
- routing << "[" << i << "] "
- << route.sink_pad << "/" << route.sink_stream << " -> "
- << route.source_pad << "/" << route.source_stream
- << " (" << utils::hex(route.flags) << ")";
- if (i != size() - 1)
- routing << ", ";
+ for (const auto &[i, route] : utils::enumerate(routing)) {
+ out << "[" << i << "] " << route;
+ if (i != routing.size() - 1)
+ out << ", ";
}
- return routing.str();
+ return out;
}
/**
@@ -359,6 +1146,21 @@ int V4L2Subdevice::open()
return ret;
}
+ /* If the subdev supports streams, enable the streams API. */
+ if (caps_.hasStreams()) {
+ struct v4l2_subdev_client_capability clientCaps{};
+ clientCaps.capabilities = V4L2_SUBDEV_CLIENT_CAP_STREAMS;
+
+ ret = ioctl(VIDIOC_SUBDEV_S_CLIENT_CAP, &clientCaps);
+ if (ret < 0) {
+ ret = -errno;
+ LOG(V4L2, Error)
+ << "Unable to set client capabilities: "
+ << strerror(-ret);
+ return ret;
+ }
+ }
+
return 0;
}
@@ -370,7 +1172,7 @@ int V4L2Subdevice::open()
/**
* \brief Get selection rectangle \a rect for \a target
- * \param[in] pad The 0-indexed pad number the rectangle is retrieved from
+ * \param[in] stream The stream the rectangle is retrieved from
* \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
* \param[out] rect The retrieved selection rectangle
*
@@ -378,13 +1180,14 @@ int V4L2Subdevice::open()
*
* \return 0 on success or a negative error code otherwise
*/
-int V4L2Subdevice::getSelection(unsigned int pad, unsigned int target,
+int V4L2Subdevice::getSelection(const Stream &stream, unsigned int target,
Rectangle *rect)
{
struct v4l2_subdev_selection sel = {};
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sel.pad = pad;
+ sel.pad = stream.pad;
+ sel.stream = stream.stream;
sel.target = target;
sel.flags = 0;
@@ -392,7 +1195,7 @@ int V4L2Subdevice::getSelection(unsigned int pad, unsigned int target,
if (ret < 0) {
LOG(V4L2, Error)
<< "Unable to get rectangle " << target << " on pad "
- << pad << ": " << strerror(-ret);
+ << stream << ": " << strerror(-ret);
return ret;
}
@@ -405,8 +1208,19 @@ int V4L2Subdevice::getSelection(unsigned int pad, unsigned int target,
}
/**
+ * \fn V4L2Subdevice::getSelection(unsigned int pad, unsigned int target,
+ * Rectangle *rect)
+ * \brief Get selection rectangle \a rect for \a target
+ * \param[in] pad The 0-indexed pad number the rectangle is retrieved from
+ * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
+ * \param[out] rect The retrieved selection rectangle
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
* \brief Set selection rectangle \a rect for \a target
- * \param[in] pad The 0-indexed pad number the rectangle is to be applied to
+ * \param[in] stream The stream the rectangle is to be applied to
* \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
* \param[inout] rect The selection rectangle to be applied
*
@@ -414,13 +1228,14 @@ int V4L2Subdevice::getSelection(unsigned int pad, unsigned int target,
*
* \return 0 on success or a negative error code otherwise
*/
-int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
+int V4L2Subdevice::setSelection(const Stream &stream, unsigned int target,
Rectangle *rect)
{
struct v4l2_subdev_selection sel = {};
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sel.pad = pad;
+ sel.pad = stream.pad;
+ sel.stream = stream.stream;
sel.target = target;
sel.flags = 0;
@@ -433,7 +1248,7 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
if (ret < 0) {
LOG(V4L2, Error)
<< "Unable to set rectangle " << target << " on pad "
- << pad << ": " << strerror(-ret);
+ << stream << ": " << strerror(-ret);
return ret;
}
@@ -444,26 +1259,40 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
return 0;
}
+
/**
- * \brief Enumerate all media bus codes and frame sizes on a \a pad
- * \param[in] pad The 0-indexed pad number to enumerate formats on
+ * \fn V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
+ * Rectangle *rect)
+ * \brief Set selection rectangle \a rect for \a target
+ * \param[in] pad The 0-indexed pad number the rectangle is to be applied to
+ * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
+ * \param[inout] rect The selection rectangle to be applied
+ *
+ * \todo Define a V4L2SelectionTarget enum for the selection target
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
+ * \brief Enumerate all media bus codes and frame sizes on a \a stream
+ * \param[in] stream The stream to enumerate formats for
*
* Enumerate all media bus codes and frame sizes supported by the subdevice on
- * a \a pad.
+ * a \a stream.
*
* \return A list of the supported device formats
*/
-V4L2Subdevice::Formats V4L2Subdevice::formats(unsigned int pad)
+V4L2Subdevice::Formats V4L2Subdevice::formats(const Stream &stream)
{
Formats formats;
- if (pad >= entity_->pads().size()) {
- LOG(V4L2, Error) << "Invalid pad: " << pad;
+ if (stream.pad >= entity_->pads().size()) {
+ LOG(V4L2, Error) << "Invalid pad: " << stream.pad;
return {};
}
- for (unsigned int code : enumPadCodes(pad)) {
- std::vector<SizeRange> sizes = enumPadSizes(pad, code);
+ for (unsigned int code : enumPadCodes(stream)) {
+ std::vector<SizeRange> sizes = enumPadSizes(stream, code);
if (sizes.empty())
return {};
@@ -471,7 +1300,7 @@ V4L2Subdevice::Formats V4L2Subdevice::formats(unsigned int pad)
if (!inserted.second) {
LOG(V4L2, Error)
<< "Could not add sizes for media bus code "
- << code << " on pad " << pad;
+ << code << " on pad " << stream.pad;
return {};
}
}
@@ -479,6 +1308,17 @@ V4L2Subdevice::Formats V4L2Subdevice::formats(unsigned int pad)
return formats;
}
+/**
+ * \fn V4L2Subdevice::formats(unsigned int pad)
+ * \brief Enumerate all media bus codes and frame sizes on a \a pad
+ * \param[in] pad The 0-indexed pad number to enumerate formats on
+ *
+ * Enumerate all media bus codes and frame sizes supported by the subdevice on
+ * a \a pad
+ *
+ * \return A list of the supported device formats
+ */
+
std::optional<ColorSpace> V4L2Subdevice::toColorSpace(const v4l2_mbus_framefmt &format) const
{
/*
@@ -494,9 +1334,9 @@ std::optional<ColorSpace> V4L2Subdevice::toColorSpace(const v4l2_mbus_framefmt &
return std::nullopt;
PixelFormatInfo::ColourEncoding colourEncoding;
- auto iter = formatInfoMap.find(format.code);
- if (iter != formatInfoMap.end()) {
- colourEncoding = iter->second.colourEncoding;
+ const MediaBusFormatInfo &info = MediaBusFormatInfo::info(format.code);
+ if (info.isValid()) {
+ colourEncoding = info.colourEncoding;
} else {
LOG(V4L2, Warning)
<< "Unknown subdev format "
@@ -510,83 +1350,189 @@ std::optional<ColorSpace> V4L2Subdevice::toColorSpace(const v4l2_mbus_framefmt &
}
/**
- * \brief Retrieve the image format set on one of the V4L2 subdevice pads
- * \param[in] pad The 0-indexed pad number the format is to be retrieved from
+ * \brief Retrieve the image format set on one of the V4L2 subdevice streams
+ * \param[in] stream The stream the format is to be retrieved from
* \param[out] format The image bus format
* \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat
* "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat"
* \return 0 on success or a negative error code otherwise
*/
-int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format,
+int V4L2Subdevice::getFormat(const Stream &stream, V4L2SubdeviceFormat *format,
Whence whence)
{
struct v4l2_subdev_format subdevFmt = {};
subdevFmt.which = whence;
- subdevFmt.pad = pad;
+ subdevFmt.pad = stream.pad;
+ subdevFmt.stream = stream.stream;
int ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);
if (ret) {
LOG(V4L2, Error)
- << "Unable to get format on pad " << pad
- << ": " << strerror(-ret);
+ << "Unable to get format on pad " << stream << ": "
+ << strerror(-ret);
return ret;
}
format->size.width = subdevFmt.format.width;
format->size.height = subdevFmt.format.height;
- format->mbus_code = subdevFmt.format.code;
+ format->code = subdevFmt.format.code;
format->colorSpace = toColorSpace(subdevFmt.format);
return 0;
}
/**
+ * \fn V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format,
+ * Whence whence)
+ * \brief Retrieve the image format set on one of the V4L2 subdevice pads
+ * \param[in] pad The 0-indexed pad number the format is to be retrieved from
+ * \param[out] format The image bus format
+ * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat
+ * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat"
+ * \return 0 on success or a negative error code otherwise
+ */
+
+/**
* \brief Set an image format on one of the V4L2 subdevice pads
- * \param[in] pad The 0-indexed pad number the format is to be applied to
- * \param[inout] format The image bus format to apply to the subdevice's pad
+ * \param[in] stream The stream the format is to be applied to
+ * \param[inout] format The image bus format to apply to the stream
* \param[in] whence The format to set, \ref V4L2Subdevice::ActiveFormat
* "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat"
*
- * Apply the requested image format to the desired media pad and return the
+ * Apply the requested image format to the desired stream and return the
* actually applied format parameters, as getFormat() would do.
*
* \return 0 on success or a negative error code otherwise
*/
-int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format,
+int V4L2Subdevice::setFormat(const Stream &stream, V4L2SubdeviceFormat *format,
Whence whence)
{
struct v4l2_subdev_format subdevFmt = {};
subdevFmt.which = whence;
- subdevFmt.pad = pad;
+ subdevFmt.pad = stream.pad;
+ subdevFmt.stream = stream.stream;
subdevFmt.format.width = format->size.width;
subdevFmt.format.height = format->size.height;
- subdevFmt.format.code = format->mbus_code;
+ subdevFmt.format.code = format->code;
subdevFmt.format.field = V4L2_FIELD_NONE;
if (format->colorSpace) {
fromColorSpace(format->colorSpace, subdevFmt.format);
/* The CSC flag is only applicable to source pads. */
- if (entity_->pads()[pad]->flags() & MEDIA_PAD_FL_SOURCE)
+ if (entity_->pads()[stream.pad]->flags() & MEDIA_PAD_FL_SOURCE)
subdevFmt.format.flags |= V4L2_MBUS_FRAMEFMT_SET_CSC;
}
int ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);
if (ret) {
LOG(V4L2, Error)
- << "Unable to set format on pad " << pad
- << ": " << strerror(-ret);
+ << "Unable to set format on pad " << stream << ": "
+ << strerror(-ret);
return ret;
}
format->size.width = subdevFmt.format.width;
format->size.height = subdevFmt.format.height;
- format->mbus_code = subdevFmt.format.code;
+ format->code = subdevFmt.format.code;
format->colorSpace = toColorSpace(subdevFmt.format);
return 0;
}
/**
+ * \fn V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format,
+ * Whence whence)
+ * \brief Set an image format on one of the V4L2 subdevice pads
+ * \param[in] pad The 0-indexed pad number the format is to be applied to
+ * \param[inout] format The image bus format to apply to the subdevice's pad
+ * \param[in] whence The format to set, \ref V4L2Subdevice::ActiveFormat
+ * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat"
+ *
+ * Apply the requested image format to the desired media pad and return the
+ * actually applied format parameters, as getFormat() would do.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+
+namespace {
+
+void routeFromKernel(V4L2Subdevice::Route &route,
+ const struct v4l2_subdev_route &kroute)
+{
+ route.sink.pad = kroute.sink_pad;
+ route.sink.stream = kroute.sink_stream;
+ route.source.pad = kroute.source_pad;
+ route.source.stream = kroute.source_stream;
+ route.flags = kroute.flags;
+}
+
+void routeToKernel(const V4L2Subdevice::Route &route,
+ struct v4l2_subdev_route &kroute)
+{
+ kroute.sink_pad = route.sink.pad;
+ kroute.sink_stream = route.sink.stream;
+ kroute.source_pad = route.source.pad;
+ kroute.source_stream = route.source.stream;
+ kroute.flags = route.flags;
+}
+
+/*
+ * Legacy routing support for pre-v6.10-rc1 kernels. Drop when v6.12-rc1 gets
+ * released.
+ */
+struct v4l2_subdev_routing_legacy {
+ __u32 which;
+ __u32 num_routes;
+ __u64 routes;
+ __u32 reserved[6];
+};
+
+#define VIDIOC_SUBDEV_G_ROUTING_LEGACY _IOWR('V', 38, struct v4l2_subdev_routing_legacy)
+#define VIDIOC_SUBDEV_S_ROUTING_LEGACY _IOWR('V', 39, struct v4l2_subdev_routing_legacy)
+
+} /* namespace */
+
+int V4L2Subdevice::getRoutingLegacy(Routing *routing, Whence whence)
+{
+ struct v4l2_subdev_routing_legacy rt = {};
+
+ rt.which = whence;
+
+ int ret = ioctl(VIDIOC_SUBDEV_G_ROUTING_LEGACY, &rt);
+ if (ret == 0 || ret == -ENOTTY)
+ return ret;
+
+ if (ret != -ENOSPC) {
+ LOG(V4L2, Error)
+ << "Failed to retrieve number of routes: "
+ << strerror(-ret);
+ return ret;
+ }
+
+ std::vector<struct v4l2_subdev_route> routes{ rt.num_routes };
+ rt.routes = reinterpret_cast<uintptr_t>(routes.data());
+
+ ret = ioctl(VIDIOC_SUBDEV_G_ROUTING_LEGACY, &rt);
+ if (ret) {
+ LOG(V4L2, Error)
+ << "Failed to retrieve routes: " << strerror(-ret);
+ return ret;
+ }
+
+ if (rt.num_routes != routes.size()) {
+ LOG(V4L2, Error) << "Invalid number of routes";
+ return -EINVAL;
+ }
+
+ routing->resize(rt.num_routes);
+
+ for (const auto &[i, route] : utils::enumerate(routes))
+ routeFromKernel((*routing)[i], route);
+
+ return 0;
+}
+
+/**
* \brief Retrieve the subdevice's internal routing table
* \param[out] routing The routing table
* \param[in] whence The routing table to get, \ref V4L2Subdevice::ActiveFormat
@@ -596,6 +1542,8 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format,
*/
int V4L2Subdevice::getRouting(Routing *routing, Whence whence)
{
+ routing->clear();
+
if (!caps_.hasStreams())
return 0;
@@ -604,18 +1552,24 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence)
rt.which = whence;
int ret = ioctl(VIDIOC_SUBDEV_G_ROUTING, &rt);
- if (ret == 0 || ret == -ENOTTY)
- return ret;
+ if (ret == -ENOTTY)
+ return V4L2Subdevice::getRoutingLegacy(routing, whence);
- if (ret != -ENOSPC) {
+ if (ret) {
LOG(V4L2, Error)
<< "Failed to retrieve number of routes: "
<< strerror(-ret);
return ret;
}
- routing->resize(rt.num_routes);
- rt.routes = reinterpret_cast<uintptr_t>(routing->data());
+ if (!rt.num_routes)
+ return 0;
+
+ std::vector<struct v4l2_subdev_route> routes{ rt.num_routes };
+ rt.routes = reinterpret_cast<uintptr_t>(routes.data());
+
+ rt.len_routes = rt.num_routes;
+ rt.num_routes = 0;
ret = ioctl(VIDIOC_SUBDEV_G_ROUTING, &rt);
if (ret) {
@@ -624,11 +1578,43 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence)
return ret;
}
- if (rt.num_routes != routing->size()) {
+ if (rt.num_routes != routes.size()) {
LOG(V4L2, Error) << "Invalid number of routes";
return -EINVAL;
}
+ routing->resize(rt.num_routes);
+
+ for (const auto &[i, route] : utils::enumerate(routes))
+ routeFromKernel((*routing)[i], route);
+
+ return 0;
+}
+
+int V4L2Subdevice::setRoutingLegacy(Routing *routing, Whence whence)
+{
+ std::vector<struct v4l2_subdev_route> routes{ routing->size() };
+
+ for (const auto &[i, route] : utils::enumerate(*routing))
+ routeToKernel(route, routes[i]);
+
+ struct v4l2_subdev_routing_legacy rt = {};
+ rt.which = whence;
+ rt.num_routes = routes.size();
+ rt.routes = reinterpret_cast<uintptr_t>(routes.data());
+
+ int ret = ioctl(VIDIOC_SUBDEV_S_ROUTING_LEGACY, &rt);
+ if (ret) {
+ LOG(V4L2, Error) << "Failed to set routes: " << strerror(-ret);
+ return ret;
+ }
+
+ routes.resize(rt.num_routes);
+ routing->resize(rt.num_routes);
+
+ for (const auto &[i, route] : utils::enumerate(routes))
+ routeFromKernel((*routing)[i], route);
+
return 0;
}
@@ -646,22 +1632,60 @@ int V4L2Subdevice::getRouting(Routing *routing, Whence whence)
*/
int V4L2Subdevice::setRouting(Routing *routing, Whence whence)
{
- if (!caps_.hasStreams())
+ if (!caps_.hasStreams()) {
+ routing->clear();
return 0;
+ }
+
+ std::vector<struct v4l2_subdev_route> routes{ routing->size() };
+
+ for (const auto &[i, route] : utils::enumerate(*routing))
+ routeToKernel(route, routes[i]);
struct v4l2_subdev_routing rt = {};
rt.which = whence;
- rt.num_routes = routing->size();
- rt.routes = reinterpret_cast<uintptr_t>(routing->data());
+ rt.len_routes = routes.size();
+ rt.num_routes = routes.size();
+ rt.routes = reinterpret_cast<uintptr_t>(routes.data());
int ret = ioctl(VIDIOC_SUBDEV_S_ROUTING, &rt);
+ if (ret == -ENOTTY)
+ return setRoutingLegacy(routing, whence);
+
if (ret) {
LOG(V4L2, Error) << "Failed to set routes: " << strerror(-ret);
return ret;
}
+ /*
+ * The kernel may want to return more routes than we have space for. In
+ * that event, we must issue a VIDIOC_SUBDEV_G_ROUTING call to retrieve
+ * the additional routes.
+ */
+ if (rt.num_routes > routes.size()) {
+ routes.resize(rt.num_routes);
+
+ rt.len_routes = rt.num_routes;
+ rt.num_routes = 0;
+
+ ret = ioctl(VIDIOC_SUBDEV_G_ROUTING, &rt);
+ if (ret) {
+ LOG(V4L2, Error)
+ << "Failed to retrieve routes: " << strerror(-ret);
+ return ret;
+ }
+ }
+
+ if (rt.num_routes != routes.size()) {
+ LOG(V4L2, Error) << "Invalid number of routes";
+ return -EINVAL;
+ }
+
routing->resize(rt.num_routes);
+ for (const auto &[i, route] : utils::enumerate(routes))
+ routeFromKernel((*routing)[i], route);
+
return 0;
}
@@ -747,14 +1771,15 @@ std::string V4L2Subdevice::logPrefix() const
return "'" + entity_->name() + "'";
}
-std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)
+std::vector<unsigned int> V4L2Subdevice::enumPadCodes(const Stream &stream)
{
std::vector<unsigned int> codes;
int ret;
for (unsigned int index = 0; ; index++) {
struct v4l2_subdev_mbus_code_enum mbusEnum = {};
- mbusEnum.pad = pad;
+ mbusEnum.pad = stream.pad;
+ mbusEnum.stream = stream.stream;
mbusEnum.index = index;
mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -767,7 +1792,7 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)
if (ret < 0 && ret != -EINVAL) {
LOG(V4L2, Error)
- << "Unable to enumerate formats on pad " << pad
+ << "Unable to enumerate formats on pad " << stream
<< ": " << strerror(-ret);
return {};
}
@@ -775,7 +1800,7 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)
return codes;
}
-std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,
+std::vector<SizeRange> V4L2Subdevice::enumPadSizes(const Stream &stream,
unsigned int code)
{
std::vector<SizeRange> sizes;
@@ -784,7 +1809,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,
for (unsigned int index = 0;; index++) {
struct v4l2_subdev_frame_size_enum sizeEnum = {};
sizeEnum.index = index;
- sizeEnum.pad = pad;
+ sizeEnum.pad = stream.pad;
+ sizeEnum.stream = stream.stream;
sizeEnum.code = code;
sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -798,7 +1824,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,
if (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {
LOG(V4L2, Error)
- << "Unable to enumerate sizes on pad " << pad
+ << "Unable to enumerate sizes on pad " << stream
<< ": " << strerror(-ret);
return {};
}
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index a72ef64d..d53aa2d3 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_videodevice.cpp - V4L2 Video Device
+ * V4L2 Video Device
*/
#include "libcamera/internal/v4l2_videodevice.h"
@@ -10,7 +10,6 @@
#include <algorithm>
#include <array>
#include <fcntl.h>
-#include <iomanip>
#include <sstream>
#include <string.h>
#include <sys/ioctl.h>
@@ -191,7 +190,7 @@ V4L2BufferCache::V4L2BufferCache(const std::vector<std::unique_ptr<FrameBuffer>>
{
for (const std::unique_ptr<FrameBuffer> &buffer : buffers)
cache_.emplace_back(true,
- lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel),
+ lastUsedCounter_++,
*buffer.get());
}
@@ -259,7 +258,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
return -ENOENT;
cache_[use] = Entry(false,
- lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel),
+ lastUsedCounter_++,
buffer);
return use;
@@ -803,12 +802,19 @@ std::string V4L2VideoDevice::logPrefix() const
*/
int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format)
{
- if (caps_.isMeta())
- return getFormatMeta(format);
- else if (caps_.isMultiplanar())
- return getFormatMultiplane(format);
- else
+ switch (bufferType_) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return getFormatSingleplane(format);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return getFormatMultiplane(format);
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ return getFormatMeta(format);
+ default:
+ return -EINVAL;
+ }
}
/**
@@ -823,12 +829,19 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format)
*/
int V4L2VideoDevice::tryFormat(V4L2DeviceFormat *format)
{
- if (caps_.isMeta())
- return trySetFormatMeta(format, false);
- else if (caps_.isMultiplanar())
- return trySetFormatMultiplane(format, false);
- else
+ switch (bufferType_) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return trySetFormatSingleplane(format, false);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return trySetFormatMultiplane(format, false);
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ return trySetFormatMeta(format, false);
+ default:
+ return -EINVAL;
+ }
}
/**
@@ -842,13 +855,25 @@ int V4L2VideoDevice::tryFormat(V4L2DeviceFormat *format)
*/
int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)
{
- int ret = 0;
- if (caps_.isMeta())
- ret = trySetFormatMeta(format, true);
- else if (caps_.isMultiplanar())
- ret = trySetFormatMultiplane(format, true);
- else
+ int ret;
+
+ switch (bufferType_) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
ret = trySetFormatSingleplane(format, true);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ ret = trySetFormatMultiplane(format, true);
+ break;
+ case V4L2_BUF_TYPE_META_CAPTURE:
+ case V4L2_BUF_TYPE_META_OUTPUT:
+ ret = trySetFormatMeta(format, true);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
/* Cache the set format on success. */
if (ret)
@@ -863,7 +888,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)
int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)
{
struct v4l2_format v4l2Format = {};
- struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
+ struct v4l2_meta_format *meta = &v4l2Format.fmt.meta;
int ret;
v4l2Format.type = bufferType_;
@@ -873,25 +898,42 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)
return ret;
}
- format->size.width = 0;
- format->size.height = 0;
- format->fourcc = V4L2PixelFormat(pix->dataformat);
+ format->fourcc = V4L2PixelFormat(meta->dataformat);
+ format->planes[0].size = meta->buffersize;
format->planesCount = 1;
- format->planes[0].bpl = pix->buffersize;
- format->planes[0].size = pix->buffersize;
+
+ bool genericLineBased = caps_.isMetaCapture() &&
+ format->fourcc.isGenericLineBasedMetadata();
+
+ if (genericLineBased) {
+ format->size.width = meta->width;
+ format->size.height = meta->height;
+ format->planes[0].bpl = meta->bytesperline;
+ } else {
+ format->size.width = 0;
+ format->size.height = 0;
+ format->planes[0].bpl = meta->buffersize;
+ }
return 0;
}
int V4L2VideoDevice::trySetFormatMeta(V4L2DeviceFormat *format, bool set)
{
+ bool genericLineBased = caps_.isMetaCapture() &&
+ format->fourcc.isGenericLineBasedMetadata();
struct v4l2_format v4l2Format = {};
- struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
+ struct v4l2_meta_format *meta = &v4l2Format.fmt.meta;
int ret;
v4l2Format.type = bufferType_;
- pix->dataformat = format->fourcc;
- pix->buffersize = format->planes[0].size;
+ meta->dataformat = format->fourcc;
+ meta->buffersize = format->planes[0].size;
+ if (genericLineBased) {
+ meta->width = format->size.width;
+ meta->height = format->size.height;
+ meta->bytesperline = format->planes[0].bpl;
+ }
ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format);
if (ret) {
LOG(V4L2, Error)
@@ -904,12 +946,18 @@ int V4L2VideoDevice::trySetFormatMeta(V4L2DeviceFormat *format, bool set)
* Return to caller the format actually applied on the video device,
* which might differ from the requested one.
*/
- format->size.width = 0;
- format->size.height = 0;
- format->fourcc = V4L2PixelFormat(pix->dataformat);
+ format->fourcc = V4L2PixelFormat(meta->dataformat);
format->planesCount = 1;
- format->planes[0].bpl = pix->buffersize;
- format->planes[0].size = pix->buffersize;
+ format->planes[0].size = meta->buffersize;
+ if (genericLineBased) {
+ format->size.width = meta->width;
+ format->size.height = meta->height;
+ format->planes[0].bpl = meta->bytesperline;
+ } else {
+ format->size.width = 0;
+ format->size.height = 0;
+ format->planes[0].bpl = meta->buffersize;
+ }
return 0;
}
@@ -1190,6 +1238,38 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(V4L2PixelFormat pixelFormat)
}
/**
+ * \brief Get the selection rectangle for \a target
+ * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
+ * \param[out] rect The selection rectangle to retrieve
+ *
+ * \todo Define a V4L2SelectionTarget enum for the selection target
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int V4L2VideoDevice::getSelection(unsigned int target, Rectangle *rect)
+{
+ struct v4l2_selection sel = {};
+
+ sel.type = bufferType_;
+ sel.target = target;
+ sel.flags = 0;
+
+ int ret = ioctl(VIDIOC_G_SELECTION, &sel);
+ if (ret < 0) {
+ LOG(V4L2, Error) << "Unable to get rectangle " << target
+ << ": " << strerror(-ret);
+ return ret;
+ }
+
+ rect->x = sel.r.left;
+ rect->y = sel.r.top;
+ rect->width = sel.r.width;
+ rect->height = sel.r.height;
+
+ return 0;
+}
+
+/**
* \brief Set a selection rectangle \a rect for \a target
* \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags
* \param[inout] rect The selection rectangle to be applied
@@ -1246,7 +1326,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count,
if (rb.count < count) {
LOG(V4L2, Error)
- << "Not enough buffers provided by V4L2VideoDevice";
+ << "Not enough buffers provided by V4L2VideoDevice. Wanted "
+ << count << ", got " << rb.count;
requestBuffers(0, memoryType);
return -ENOMEM;
}
@@ -1815,7 +1896,7 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()
* Detect kernel drivers which do not reset the sequence number to zero
* on stream start.
*/
- if (!firstFrame_) {
+ if (!firstFrame_.has_value()) {
if (buf.sequence)
LOG(V4L2, Info)
<< "Zero sequence expected for first frame (got "
@@ -2067,15 +2148,24 @@ V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelForma
* \class V4L2M2MDevice
* \brief Memory-to-Memory video device
*
+ * Memory to Memory devices in the kernel using the V4L2 M2M API can
+ * operate with multiple contexts for parallel operations on a single
+ * device. Each instance of a V4L2M2MDevice represents a single context.
+ *
* The V4L2M2MDevice manages two V4L2VideoDevice instances on the same
* deviceNode which operate together using two queues to implement the V4L2
* Memory to Memory API.
*
- * The two devices should be opened by calling open() on the V4L2M2MDevice, and
- * can be closed by calling close on the V4L2M2MDevice.
+ * Users of this class should create a new instance of the V4L2M2MDevice for
+ * each desired execution context and then open it by calling open() on the
+ * V4L2M2MDevice and close it by calling close() on the V4L2M2MDevice.
*
* Calling V4L2VideoDevice::open() and V4L2VideoDevice::close() on the capture
* or output V4L2VideoDevice is not permitted.
+ *
+ * Once the M2M device is open, users can operate on the output and capture
+ * queues represented by the V4L2VideoDevice returned by the output() and
+ * capture() functions.
*/
/**
diff --git a/src/libcamera/vector.cpp b/src/libcamera/vector.cpp
new file mode 100644
index 00000000..4dad1b90
--- /dev/null
+++ b/src/libcamera/vector.cpp
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+ *
+ * Vector and related operations
+ */
+
+#include "libcamera/internal/vector.h"
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file vector.h
+ * \brief Vector class
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Vector)
+
+/**
+ * \class Vector
+ * \brief Vector class
+ * \tparam T Type of numerical values to be stored in the vector
+ * \tparam Rows Number of dimension of the vector (= number of elements)
+ */
+
+/**
+ * \fn Vector::Vector()
+ * \brief Construct an uninitialized vector
+ */
+
+/**
+ * \fn Vector::Vector(T scalar)
+ * \brief Construct a vector filled with a \a scalar value
+ * \param[in] scalar The scalar value
+ */
+
+/**
+ * \fn Vector::Vector(const std::array<T, Rows> &data)
+ * \brief Construct vector from supplied data
+ * \param data Data from which to construct a vector
+ *
+ * The size of \a data must be equal to the dimension size Rows of the vector.
+ */
+
+/**
+ * \fn Vector::Vector(const Span<const T, Rows> data)
+ * \brief Construct vector from supplied data
+ * \param data Data from which to construct a vector
+ *
+ * The size of \a data must be equal to the dimension size Rows of the vector.
+ */
+
+/**
+ * \fn T Vector::operator[](size_t i) const
+ * \brief Index to an element in the vector
+ * \param i Index of element to retrieve
+ * \return Element at index \a i from the vector
+ */
+
+/**
+ * \fn T &Vector::operator[](size_t i)
+ * \copydoc Vector::operator[](size_t i) const
+ */
+
+/**
+ * \fn Vector::operator-() const
+ * \brief Negate a Vector by negating both all of its coordinates
+ * \return The negated vector
+ */
+
+/**
+ * \fn Vector::operator+(Vector const &other) const
+ * \brief Calculate the sum of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise sum of this vector and \a other
+ */
+
+/**
+ * \fn Vector::operator+(T scalar) const
+ * \brief Calculate the sum of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise sum of this vector and \a other
+ */
+
+/**
+ * \fn Vector::operator-(Vector const &other) const
+ * \brief Calculate the difference of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise subtraction of \a other from this vector
+ */
+
+/**
+ * \fn Vector::operator-(T scalar) const
+ * \brief Calculate the difference of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise subtraction of \a scalar from this vector
+ */
+
+/**
+ * \fn Vector::operator*(const Vector &other) const
+ * \brief Calculate the product of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise product of this vector and \a other
+ */
+
+/**
+ * \fn Vector::operator*(T scalar) const
+ * \brief Calculate the product of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise product of this vector and \a scalar
+ */
+
+/**
+ * \fn Vector::operator/(const Vector &other) const
+ * \brief Calculate the quotient of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise division of this vector by \a other
+ */
+
+/**
+ * \fn Vector::operator/(T scalar) const
+ * \brief Calculate the quotient of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise division of this vector by \a scalar
+ */
+
+/**
+ * \fn Vector::operator+=(Vector const &other)
+ * \brief Add \a other element-wise to this vector
+ * \param[in] other The other vector
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator+=(T scalar)
+ * \brief Add \a scalar element-wise to this vector
+ * \param[in] scalar The scalar
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator-=(Vector const &other)
+ * \brief Subtract \a other element-wise from this vector
+ * \param[in] other The other vector
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator-=(T scalar)
+ * \brief Subtract \a scalar element-wise from this vector
+ * \param[in] scalar The scalar
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator*=(const Vector &other)
+ * \brief Multiply this vector by \a other element-wise
+ * \param[in] other The other vector
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator*=(T scalar)
+ * \brief Multiply this vector by \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator/=(const Vector &other)
+ * \brief Divide this vector by \a other element-wise
+ * \param[in] other The other vector
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::operator/=(T scalar)
+ * \brief Divide this vector by \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return This vector
+ */
+
+/**
+ * \fn Vector::min(const Vector &other) const
+ * \brief Calculate the minimum of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise minimum of this vector and \a other
+ */
+
+/**
+ * \fn Vector::min(T scalar) const
+ * \brief Calculate the minimum of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise minimum of this vector and \a scalar
+ */
+
+/**
+ * \fn Vector::max(const Vector &other) const
+ * \brief Calculate the maximum of this vector and \a other element-wise
+ * \param[in] other The other vector
+ * \return The element-wise maximum of this vector and \a other
+ */
+
+/**
+ * \fn Vector::max(T scalar) const
+ * \brief Calculate the maximum of this vector and \a scalar element-wise
+ * \param[in] scalar The scalar
+ * \return The element-wise maximum of this vector and \a scalar
+ */
+
+/**
+ * \fn Vector::dot(const Vector<T, Rows> &other) const
+ * \brief Compute the dot product
+ * \param[in] other The other vector
+ * \return The dot product of the two vectors
+ */
+
+/**
+ * \fn constexpr T &Vector::x()
+ * \brief Convenience function to access the first element of the vector
+ * \return The first element of the vector
+ */
+
+/**
+ * \fn constexpr T &Vector::y()
+ * \brief Convenience function to access the second element of the vector
+ * \return The second element of the vector
+ */
+
+/**
+ * \fn constexpr T &Vector::z()
+ * \brief Convenience function to access the third element of the vector
+ * \return The third element of the vector
+ */
+
+/**
+ * \fn constexpr const T &Vector::x() const
+ * \copydoc Vector::x()
+ */
+
+/**
+ * \fn constexpr const T &Vector::y() const
+ * \copydoc Vector::y()
+ */
+
+/**
+ * \fn constexpr const T &Vector::z() const
+ * \copydoc Vector::z()
+ */
+
+/**
+ * \fn constexpr T &Vector::r()
+ * \brief Convenience function to access the first element of the vector
+ * \return The first element of the vector
+ */
+
+/**
+ * \fn constexpr T &Vector::g()
+ * \brief Convenience function to access the second element of the vector
+ * \return The second element of the vector
+ */
+
+/**
+ * \fn constexpr T &Vector::b()
+ * \brief Convenience function to access the third element of the vector
+ * \return The third element of the vector
+ */
+
+/**
+ * \fn constexpr const T &Vector::r() const
+ * \copydoc Vector::r()
+ */
+
+/**
+ * \fn constexpr const T &Vector::g() const
+ * \copydoc Vector::g()
+ */
+
+/**
+ * \fn constexpr const T &Vector::b() const
+ * \copydoc Vector::b()
+ */
+
+/**
+ * \fn Vector::length2()
+ * \brief Get the squared length of the vector
+ * \return The squared length of the vector
+ */
+
+/**
+ * \fn Vector::length()
+ * \brief Get the length of the vector
+ * \return The length of the vector
+ */
+
+/**
+ * \fn Vector::sum() const
+ * \brief Calculate the sum of all the vector elements
+ * \tparam R The type of the sum
+ *
+ * The type R of the sum defaults to the type T of the elements, but can be set
+ * explicitly to use a different type in case the type T would risk
+ * overflowing.
+ *
+ * \return The sum of all the vector elements
+ */
+
+/**
+ * \fn operator*(const Matrix<T, Rows, Cols> &m, const Vector<U, Cols> &v)
+ * \brief Multiply a matrix by a vector
+ * \tparam T Numerical type of the contents of the matrix
+ * \tparam U Numerical type of the contents of the vector
+ * \tparam Rows The number of rows in the matrix
+ * \tparam Cols The number of columns in the matrix (= rows in the vector)
+ * \param m The matrix
+ * \param v The vector
+ * \return Product of matrix \a m and vector \a v
+ */
+
+/**
+ * \typedef RGB
+ * \brief A Vector of 3 elements representing an RGB pixel value
+ */
+
+/**
+ * \fn bool operator==(const Vector<T, Rows> &lhs, const Vector<T, Rows> &rhs)
+ * \brief Compare vectors for equality
+ * \return True if the two vectors are equal, false otherwise
+ */
+
+/**
+ * \fn bool operator!=(const Vector<T, Rows> &lhs, const Vector<T, Rows> &rhs)
+ * \brief Compare vectors for inequality
+ * \return True if the two vectors are not equal, false otherwise
+ */
+
+#ifndef __DOXYGEN__
+bool vectorValidateYaml(const YamlObject &obj, unsigned int size)
+{
+ if (!obj.isList())
+ return false;
+
+ if (obj.size() != size) {
+ LOG(Vector, Error)
+ << "Wrong number of values in YAML vector: expected "
+ << size << ", got " << obj.size();
+ return false;
+ }
+
+ return true;
+}
+#endif /* __DOXYGEN__ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/version.cpp.in b/src/libcamera/version.cpp.in
index 5aec08a1..bf5a2c30 100644
--- a/src/libcamera/version.cpp.in
+++ b/src/libcamera/version.cpp.in
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * version.cpp - libcamera version
+ * libcamera version
*
* This file is auto-generated. Do not edit.
*/
diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
index bf21141e..a5e42461 100644
--- a/src/libcamera/yaml_parser.cpp
+++ b/src/libcamera/yaml_parser.cpp
@@ -2,15 +2,16 @@
/*
* Copyright (C) 2022, Google Inc.
*
- * yaml_parser.cpp - libcamera YAML parsing helper
+ * libcamera YAML parsing helper
*/
#include "libcamera/internal/yaml_parser.h"
-#include <cstdlib>
+#include <charconv>
#include <errno.h>
#include <functional>
#include <limits>
+#include <stdlib.h>
#include <libcamera/base/file.h>
#include <libcamera/base/log.h>
@@ -18,7 +19,7 @@
#include <yaml.h>
/**
- * \file libcamera/internal/yaml_parser.h
+ * \file yaml_parser.h
* \brief A YAML parser helper
*/
@@ -38,12 +39,12 @@ static const YamlObject empty;
* \brief A class representing the tree structure of the YAML content
*
* The YamlObject class represents the tree structure of YAML content. A
- * YamlObject can be a dictionary or list of YamlObjects or a value if a tree
- * leaf.
+ * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a
+ * tree leaf.
*/
YamlObject::YamlObject()
- : type_(Type::Value)
+ : type_(Type::Empty)
{
}
@@ -71,6 +72,20 @@ YamlObject::~YamlObject() = default;
*/
/**
+ * \fn YamlObject::isEmpty()
+ * \brief Return whether the YamlObject is an empty
+ *
+ * \return True if the YamlObject is empty, false otherwise
+ */
+
+/**
+ * \fn YamlObject::operator bool()
+ * \brief Return whether the YamlObject is a non-empty
+ *
+ * \return False if the YamlObject is empty, true otherwise
+ */
+
+/**
* \fn YamlObject::size()
* \brief Retrieve the number of elements in a dictionary or list YamlObject
*
@@ -104,7 +119,7 @@ std::size_t YamlObject::size() const
*/
/**
- * \fn template<typename T> YamlObject::get<T>(const T &defaultValue) const
+ * \fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const
* \brief Parse the YamlObject as a \a T value
* \param[in] defaultValue The default value when failing to parse
*
@@ -118,172 +133,74 @@ std::size_t YamlObject::size() const
#ifndef __DOXYGEN__
template<>
-std::optional<bool> YamlObject::get() const
+std::optional<bool>
+YamlObject::Getter<bool>::get(const YamlObject &obj) const
{
- if (type_ != Type::Value)
+ if (obj.type_ != Type::Value)
return std::nullopt;
- if (value_ == "true")
+ if (obj.value_ == "true")
return true;
- else if (value_ == "false")
+ else if (obj.value_ == "false")
return false;
return std::nullopt;
}
-namespace {
-
-bool parseSignedInteger(const std::string &str, long min, long max,
- long *result)
-{
- if (str == "")
- return false;
-
- char *end;
-
- errno = 0;
- long value = std::strtol(str.c_str(), &end, 10);
-
- if ('\0' != *end || errno == ERANGE || value < min || value > max)
- return false;
-
- *result = value;
- return true;
-}
-
-bool parseUnsignedInteger(const std::string &str, unsigned long max,
- unsigned long *result)
-{
- if (str == "")
- return false;
-
- /*
- * strtoul() accepts strings representing a negative number, in which
- * case it negates the converted value. We don't want to silently accept
- * negative values and return a large positive number, so check for a
- * minus sign (after optional whitespace) and return an error.
- */
- std::size_t found = str.find_first_not_of(" \t");
- if (found != std::string::npos && str[found] == '-')
- return false;
-
- char *end;
-
- errno = 0;
- unsigned long value = std::strtoul(str.c_str(), &end, 10);
-
- if ('\0' != *end || errno == ERANGE || value > max)
- return false;
-
- *result = value;
- return true;
-}
-
-} /* namespace */
-
-template<>
-std::optional<int8_t> YamlObject::get() const
-{
- if (type_ != Type::Value)
- return std::nullopt;
-
- long value;
-
- if (!parseSignedInteger(value_, std::numeric_limits<int8_t>::min(),
- std::numeric_limits<int8_t>::max(), &value))
- return std::nullopt;
-
- return value;
-}
-
-template<>
-std::optional<uint8_t> YamlObject::get() const
-{
- if (type_ != Type::Value)
- return std::nullopt;
-
- unsigned long value;
-
- if (!parseUnsignedInteger(value_, std::numeric_limits<uint8_t>::max(),
- &value))
- return std::nullopt;
-
- return value;
-}
-
-template<>
-std::optional<int16_t> YamlObject::get() const
-{
- if (type_ != Type::Value)
- return std::nullopt;
-
- long value;
-
- if (!parseSignedInteger(value_, std::numeric_limits<int16_t>::min(),
- std::numeric_limits<int16_t>::max(), &value))
- return std::nullopt;
-
- return value;
-}
-
-template<>
-std::optional<uint16_t> YamlObject::get() const
+template<typename T>
+struct YamlObject::Getter<T, std::enable_if_t<
+ std::is_same_v<int8_t, T> ||
+ std::is_same_v<uint8_t, T> ||
+ std::is_same_v<int16_t, T> ||
+ std::is_same_v<uint16_t, T> ||
+ std::is_same_v<int32_t, T> ||
+ std::is_same_v<uint32_t, T>>>
{
- if (type_ != Type::Value)
- return std::nullopt;
-
- unsigned long value;
-
- if (!parseUnsignedInteger(value_, std::numeric_limits<uint16_t>::max(),
- &value))
- return std::nullopt;
+ std::optional<T> get(const YamlObject &obj) const
+ {
+ if (obj.type_ != Type::Value)
+ return std::nullopt;
- return value;
-}
+ const std::string &str = obj.value_;
+ T value;
-template<>
-std::optional<int32_t> YamlObject::get() const
-{
- if (type_ != Type::Value)
- return std::nullopt;
+ auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),
+ value);
+ if (ptr != str.data() + str.size() || ec != std::errc())
+ return std::nullopt;
- long value;
+ return value;
+ }
+};
- if (!parseSignedInteger(value_, std::numeric_limits<int32_t>::min(),
- std::numeric_limits<int32_t>::max(), &value))
- return std::nullopt;
-
- return value;
-}
+template struct YamlObject::Getter<int8_t>;
+template struct YamlObject::Getter<uint8_t>;
+template struct YamlObject::Getter<int16_t>;
+template struct YamlObject::Getter<uint16_t>;
+template struct YamlObject::Getter<int32_t>;
+template struct YamlObject::Getter<uint32_t>;
template<>
-std::optional<uint32_t> YamlObject::get() const
+std::optional<float>
+YamlObject::Getter<float>::get(const YamlObject &obj) const
{
- if (type_ != Type::Value)
- return std::nullopt;
-
- unsigned long value;
-
- if (!parseUnsignedInteger(value_, std::numeric_limits<uint32_t>::max(),
- &value))
- return std::nullopt;
-
- return value;
+ return obj.get<double>();
}
template<>
-std::optional<double> YamlObject::get() const
+std::optional<double>
+YamlObject::Getter<double>::get(const YamlObject &obj) const
{
- if (type_ != Type::Value)
+ if (obj.type_ != Type::Value)
return std::nullopt;
- if (value_ == "")
+ if (obj.value_.empty())
return std::nullopt;
char *end;
errno = 0;
- double value = utils::strtod(value_.c_str(), &end);
+ double value = utils::strtod(obj.value_.c_str(), &end);
if ('\0' != *end || errno == ERANGE)
return std::nullopt;
@@ -292,28 +209,30 @@ std::optional<double> YamlObject::get() const
}
template<>
-std::optional<std::string> YamlObject::get() const
+std::optional<std::string>
+YamlObject::Getter<std::string>::get(const YamlObject &obj) const
{
- if (type_ != Type::Value)
+ if (obj.type_ != Type::Value)
return std::nullopt;
- return value_;
+ return obj.value_;
}
template<>
-std::optional<Size> YamlObject::get() const
+std::optional<Size>
+YamlObject::Getter<Size>::get(const YamlObject &obj) const
{
- if (type_ != Type::List)
+ if (obj.type_ != Type::List)
return std::nullopt;
- if (list_.size() != 2)
+ if (obj.list_.size() != 2)
return std::nullopt;
- auto width = list_[0].value->get<uint32_t>();
+ auto width = obj.list_[0].value->get<uint32_t>();
if (!width)
return std::nullopt;
- auto height = list_[1].value->get<uint32_t>();
+ auto height = obj.list_[1].value->get<uint32_t>();
if (!height)
return std::nullopt;
@@ -339,6 +258,7 @@ std::optional<Size> YamlObject::get() const
template<typename T,
std::enable_if_t<
std::is_same_v<bool, T> ||
+ std::is_same_v<float, T> ||
std::is_same_v<double, T> ||
std::is_same_v<int8_t, T> ||
std::is_same_v<uint8_t, T> ||
@@ -367,6 +287,7 @@ std::optional<std::vector<T>> YamlObject::getList() const
}
template std::optional<std::vector<bool>> YamlObject::getList<bool>() const;
+template std::optional<std::vector<float>> YamlObject::getList<float>() const;
template std::optional<std::vector<double>> YamlObject::getList<double>() const;
template std::optional<std::vector<int8_t>> YamlObject::getList<int8_t>() const;
template std::optional<std::vector<uint8_t>> YamlObject::getList<uint8_t>() const;
@@ -424,7 +345,8 @@ template std::optional<std::vector<Size>> YamlObject::getList<Size>() const;
*
* This function retrieves an element of the YamlObject. Only YamlObject
* instances of List type associate elements with index, calling this function
- * on other types of instances is invalid and results in undefined behaviour.
+ * on other types of instances or with an invalid index results in an empty
+ * object.
*
* \return The YamlObject as an element of the list
*/
@@ -447,31 +369,31 @@ const YamlObject &YamlObject::operator[](std::size_t index) const
*
* \return True if an element exists, false otherwise
*/
-bool YamlObject::contains(const std::string &key) const
+bool YamlObject::contains(std::string_view key) const
{
- if (dictionary_.find(std::ref(key)) == dictionary_.end())
- return false;
-
- return true;
+ return dictionary_.find(key) != dictionary_.end();
}
/**
- * \fn YamlObject::operator[](const std::string &key) const
+ * \fn YamlObject::operator[](std::string_view key) const
* \brief Retrieve a member by name from the dictionary
*
* This function retrieve a member of a YamlObject by name. Only YamlObject
* instances of Dictionary type associate elements with names, calling this
- * function on other types of instances is invalid and results in undefined
- * behaviour.
+ * function on other types of instances or with a nonexistent key results in an
+ * empty object.
*
* \return The YamlObject corresponding to the \a key member
*/
-const YamlObject &YamlObject::operator[](const std::string &key) const
+const YamlObject &YamlObject::operator[](std::string_view key) const
{
- if (type_ != Type::Dictionary || !contains(key))
+ if (type_ != Type::Dictionary)
return empty;
auto iter = dictionary_.find(key);
+ if (iter == dictionary_.end())
+ return empty;
+
return *iter->second;
}
@@ -587,8 +509,17 @@ YamlParserContext::EventPtr YamlParserContext::nextEvent()
EventPtr event(new yaml_event_t);
/* yaml_parser_parse returns 1 when it succeeds */
- if (!yaml_parser_parse(&parser_, event.get()))
+ if (!yaml_parser_parse(&parser_, event.get())) {
+ File *file = static_cast<File *>(parser_.read_handler_data);
+
+ LOG(YamlParser, Error) << file->fileName() << ":"
+ << parser_.problem_mark.line << ":"
+ << parser_.problem_mark.column << " "
+ << parser_.problem << " "
+ << parser_.context;
+
return nullptr;
+ }
return event;
}
diff --git a/src/meson.build b/src/meson.build
index 165a77bb..8eb8f05b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -27,6 +27,37 @@ else
ipa_sign_module = false
endif
+# libyuv, used by the Android adaptation layer and the virtual pipeline handler.
+# Fallback to a subproject if libyuv isn't found, as it's typically not provided
+# by distributions. Where libyuv is provided by a distribution, it may not
+# always supply a pkg-config implementation, requiring cxx.find_library() to
+# search for it.
+if not get_option('force_fallback_for').contains('libyuv')
+ libyuv_dep = dependency('libyuv', required : false)
+ if not libyuv_dep.found()
+ libyuv_dep = cxx.find_library('yuv', has_headers : 'libyuv.h',
+ required : false)
+ endif
+else
+ libyuv_dep = dependency('', required : false)
+endif
+
+if (pipelines.contains('virtual') or get_option('android').allowed()) and \
+ not libyuv_dep.found()
+ cmake = import('cmake')
+
+ libyuv_vars = cmake.subproject_options()
+ libyuv_vars.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': 'ON'})
+ libyuv_vars.set_override_option('cpp_std', 'c++17')
+ libyuv_vars.append_compile_args('cpp',
+ '-Wno-sign-compare',
+ '-Wno-unused-variable',
+ '-Wno-unused-parameter')
+ libyuv_vars.append_link_args('-ljpeg')
+ libyuv = cmake.subproject('libyuv', options : libyuv_vars)
+ libyuv_dep = libyuv.dependency('yuv')
+endif
+
# libcamera must be built first as a dependency to the other components.
subdir('libcamera')
diff --git a/src/py/cam/cam_qt.py b/src/py/cam/cam_qt.py
index c1723b44..22d8c4da 100644
--- a/src/py/cam/cam_qt.py
+++ b/src/py/cam/cam_qt.py
@@ -2,7 +2,7 @@
# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
from helpers import mfb_to_rgb
-from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt6 import QtCore, QtGui, QtWidgets
import libcamera as libcam
import libcamera.utils
import sys
@@ -63,10 +63,10 @@ class QtRenderer:
self.buf_mmap_map = buf_mmap_map
def run(self):
- camnotif = QtCore.QSocketNotifier(self.cm.event_fd, QtCore.QSocketNotifier.Read)
+ camnotif = QtCore.QSocketNotifier(self.cm.event_fd, QtCore.QSocketNotifier.Type.Read)
camnotif.activated.connect(lambda _: self.readcam())
- keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Read)
+ keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Type.Read)
keynotif.activated.connect(lambda _: self.readkey())
print('Capturing...')
diff --git a/src/py/cam/cam_qtgl.py b/src/py/cam/cam_qtgl.py
index 6cfbd347..35b4b06b 100644
--- a/src/py/cam/cam_qtgl.py
+++ b/src/py/cam/cam_qtgl.py
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
-from PyQt5 import QtCore, QtWidgets
-from PyQt5.QtCore import Qt
+from PyQt6 import QtCore, QtWidgets
+from PyQt6.QtCore import Qt
import math
import os
@@ -142,10 +142,10 @@ class QtRenderer:
self.window = window
def run(self):
- camnotif = QtCore.QSocketNotifier(self.state.cm.event_fd, QtCore.QSocketNotifier.Read)
+ camnotif = QtCore.QSocketNotifier(self.state.cm.event_fd, QtCore.QSocketNotifier.Type.Read)
camnotif.activated.connect(lambda _: self.readcam())
- keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Read)
+ keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Type.Read)
keynotif.activated.connect(lambda _: self.readkey())
print('Capturing...')
@@ -175,8 +175,8 @@ class MainWindow(QtWidgets.QWidget):
def __init__(self, state):
super().__init__()
- self.setAttribute(Qt.WA_PaintOnScreen)
- self.setAttribute(Qt.WA_NativeWindow)
+ self.setAttribute(Qt.WidgetAttribute.WA_PaintOnScreen)
+ self.setAttribute(Qt.WidgetAttribute.WA_NativeWindow)
self.state = state
diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py
index 9948c41e..d43a7c1c 100755
--- a/src/py/libcamera/gen-py-controls.py
+++ b/src/py/libcamera/gen-py-controls.py
@@ -4,10 +4,12 @@
# Generate Python bindings controls from YAML
import argparse
-import string
+import jinja2
import sys
import yaml
+from controls import Control
+
def find_common_prefix(strings):
prefix = strings[0]
@@ -21,87 +23,86 @@ def find_common_prefix(strings):
return prefix
-def generate_py(controls, mode):
- out = ''
-
- for ctrl in controls:
- name, ctrl = ctrl.popitem()
-
- if ctrl.get('draft'):
- ns = 'libcamera::{}::draft::'.format(mode)
- container = 'draft'
- else:
- ns = 'libcamera::{}::'.format(mode)
- container = 'controls'
-
- out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{ns}{name}));\n\n'
-
- enum = ctrl.get('enum')
- if not enum:
- continue
-
- cpp_enum = name + 'Enum'
+def extend_control(ctrl, mode):
+ if ctrl.vendor != 'libcamera':
+ ctrl.klass = ctrl.vendor
+ ctrl.namespace = f'{ctrl.vendor}::'
+ else:
+ ctrl.klass = mode
+ ctrl.namespace = ''
- out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
+ if not ctrl.is_enum:
+ return ctrl
- if mode == 'controls':
- # Adjustments for controls
- if name == 'LensShadingMapMode':
- prefix = 'LensShadingMapMode'
- else:
- prefix = find_common_prefix([e['name'] for e in enum])
+ if mode == 'controls':
+ # Adjustments for controls
+ if ctrl.name == 'LensShadingMapMode':
+ prefix = 'LensShadingMapMode'
else:
- # Adjustments for properties
- prefix = find_common_prefix([e['name'] for e in enum])
-
- for entry in enum:
- cpp_enum = entry['name']
- py_enum = entry['name'][len(prefix):]
-
- out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum)
-
- out += '\t;\n\n'
-
- return {'controls': out}
+ prefix = find_common_prefix([e.name for e in ctrl.enum_values])
+ else:
+ # Adjustments for properties
+ prefix = find_common_prefix([e.name for e in ctrl.enum_values])
+ for enum in ctrl.enum_values:
+ enum.py_name = enum.name[len(prefix):]
-def fill_template(template, data):
- template = open(template, 'rb').read()
- template = template.decode('utf-8')
- template = string.Template(template)
- return template.substitute(data)
+ return ctrl
def main(argv):
+ headers = {
+ 'controls': 'control_ids.h',
+ 'properties': 'property_ids.h',
+ }
+
# Parse command line arguments
parser = argparse.ArgumentParser()
- parser.add_argument('-o', dest='output', metavar='file', type=str,
+ parser.add_argument('--mode', '-m', type=str, required=True,
+ help='Mode is either "controls" or "properties"')
+ parser.add_argument('--output', '-o', metavar='file', type=str,
help='Output file name. Defaults to standard output if not specified.')
- parser.add_argument('input', type=str,
- help='Input file name.')
- parser.add_argument('template', type=str,
+ parser.add_argument('--template', '-t', type=str, required=True,
help='Template file name.')
- parser.add_argument('--mode', type=str, required=True,
- help='Mode is either "controls" or "properties"')
+ parser.add_argument('input', type=str, nargs='+',
+ help='Input file name.')
args = parser.parse_args(argv[1:])
- if args.mode not in ['controls', 'properties']:
+ if not headers.get(args.mode):
print(f'Invalid mode option "{args.mode}"', file=sys.stderr)
return -1
- data = open(args.input, 'rb').read()
- controls = yaml.safe_load(data)['controls']
+ controls = []
+ vendors = []
+
+ for input in args.input:
+ data = yaml.safe_load(open(input, 'rb').read())
+
+ vendor = data['vendor']
+ if vendor != 'libcamera':
+ vendors.append(vendor)
+
+ for ctrl in data['controls']:
+ ctrl = Control(*ctrl.popitem(), vendor, args.mode)
+ controls.append(extend_control(ctrl, args.mode))
- data = generate_py(controls, args.mode)
+ data = {
+ 'mode': args.mode,
+ 'header': headers[args.mode],
+ 'vendors': vendors,
+ 'controls': controls,
+ }
- data = fill_template(args.template, data)
+ env = jinja2.Environment()
+ template = env.from_string(open(args.template, 'r', encoding='utf-8').read())
+ string = template.render(data)
if args.output:
- output = open(args.output, 'wb')
- output.write(data.encode('utf-8'))
+ output = open(args.output, 'w', encoding='utf-8')
+ output.write(string)
output.close()
else:
- sys.stdout.write(data)
+ sys.stdout.write(string)
return 0
diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build
index f58c7198..33ab6579 100644
--- a/src/py/libcamera/meson.build
+++ b/src/py/libcamera/meson.build
@@ -1,21 +1,5 @@
# SPDX-License-Identifier: CC0-1.0
-py3_dep = dependency('python3', required : get_option('pycamera'))
-
-if not py3_dep.found()
- pycamera_enabled = false
- subdir_done()
-endif
-
-pybind11_dep = dependency('pybind11', required : get_option('pycamera'))
-
-if not pybind11_dep.found()
- pycamera_enabled = false
- subdir_done()
-endif
-
-pycamera_enabled = true
-
pycamera_sources = files([
'py_camera_manager.cpp',
'py_color_space.cpp',
@@ -26,31 +10,26 @@ pycamera_sources = files([
'py_transform.cpp',
])
-# Generate controls
-
-gen_py_controls_input_files = files([
- '../../libcamera/control_ids.yaml',
- 'py_controls_generated.cpp.in',
-])
+# Generate controls and properties
+gen_py_controls_template = files('py_controls_generated.cpp.in')
gen_py_controls = files('gen-py-controls.py')
pycamera_sources += custom_target('py_gen_controls',
- input : gen_py_controls_input_files,
+ input : controls_files,
output : ['py_controls_generated.cpp'],
- command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@'])
-
-# Generate properties
-
-gen_py_property_enums_input_files = files([
- '../../libcamera/property_ids.yaml',
- 'py_properties_generated.cpp.in',
-])
+ command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@',
+ '-t', gen_py_controls_template, '@INPUT@'],
+ depend_files : [py_mod_controls],
+ env : py_build_env)
pycamera_sources += custom_target('py_gen_properties',
- input : gen_py_property_enums_input_files,
+ input : properties_files,
output : ['py_properties_generated.cpp'],
- command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@'])
+ command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@',
+ '-t', gen_py_controls_template, '@INPUT@'],
+ depend_files : [py_mod_controls],
+ env : py_build_env)
# Generate formats
@@ -84,6 +63,7 @@ pycamera = shared_module('_libcamera',
pycamera_sources,
install : true,
install_dir : destdir,
+ install_tag : 'python-runtime',
name_prefix : '',
dependencies : pycamera_deps,
cpp_args : pycamera_args)
@@ -99,7 +79,9 @@ run_command('ln', '-fsrT', meson.current_source_dir() / 'utils',
meson.current_build_dir() / 'utils',
check : true)
-install_data(['__init__.py'], install_dir : destdir)
+install_data(['__init__.py'],
+ install_dir : destdir,
+ install_tag : 'python-runtime')
# \todo Generate stubs when building. See https://peps.python.org/pep-0484/#stub-files
# Note: Depends on pybind11-stubgen. To generate pylibcamera stubs:
diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h
index 3574db23..af69b915 100644
--- a/src/py/libcamera/py_camera_manager.h
+++ b/src/py/libcamera/py_camera_manager.h
@@ -20,7 +20,7 @@ public:
~PyCameraManager();
pybind11::list cameras();
- std::shared_ptr<Camera> get(const std::string &name) { return cameraManager_->get(name); }
+ std::shared_ptr<Camera> get(std::string_view name) { return cameraManager_->get(name); }
static const std::string &version() { return CameraManager::version(); }
diff --git a/src/py/libcamera/py_color_space.cpp b/src/py/libcamera/py_color_space.cpp
index 5201121a..fd5a5dab 100644
--- a/src/py/libcamera/py_color_space.cpp
+++ b/src/py/libcamera/py_color_space.cpp
@@ -12,6 +12,8 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
+#include "py_main.h"
+
namespace py = pybind11;
using namespace libcamera;
diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in
index 18fa57d9..22a132d1 100644
--- a/src/py/libcamera/py_controls_generated.cpp.in
+++ b/src/py/libcamera/py_controls_generated.cpp.in
@@ -2,29 +2,46 @@
/*
* Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
*
- * Python bindings - Auto-generated controls
+ * Python bindings - Auto-generated {{mode}}
*
* This file is auto-generated. Do not edit.
*/
-#include <libcamera/control_ids.h>
+#include <libcamera/{{header}}>
#include <pybind11/pybind11.h>
+#include "py_main.h"
+
namespace py = pybind11;
-class PyControls
+class Py{{mode|capitalize}}
{
};
-class PyDraftControls
+{% for vendor in vendors -%}
+class Py{{vendor|capitalize}}{{mode|capitalize}}
{
};
-void init_py_controls_generated(py::module& m)
+{% endfor -%}
+
+void init_py_{{mode}}_generated(py::module& m)
{
- auto controls = py::class_<PyControls>(m, "controls");
- auto draft = py::class_<PyDraftControls>(controls, "draft");
+ auto {{mode}} = py::class_<Py{{mode|capitalize}}>(m, "{{mode}}");
+{%- for vendor in vendors %}
+ auto {{vendor}} = py::class_<Py{{vendor|capitalize}}{{mode|capitalize}}>({{mode}}, "{{vendor}}");
+{%- endfor %}
+
+{% for ctrl in controls %}
+ {{ctrl.klass}}.def_readonly_static("{{ctrl.name}}", static_cast<const libcamera::ControlId *>(&libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}));
+{%- if ctrl.is_enum %}
-${controls}
+ py::enum_<libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}Enum>({{ctrl.klass}}, "{{ctrl.name}}Enum")
+{%- for enum in ctrl.enum_values %}
+ .value("{{enum.py_name}}", libcamera::{{mode}}::{{ctrl.namespace}}{{enum.name}})
+{%- endfor %}
+ ;
+{%- endif %}
+{% endfor -%}
}
diff --git a/src/py/libcamera/py_enums.cpp b/src/py/libcamera/py_enums.cpp
index e25689c6..9e75ec1a 100644
--- a/src/py/libcamera/py_enums.cpp
+++ b/src/py/libcamera/py_enums.cpp
@@ -9,6 +9,8 @@
#include <pybind11/pybind11.h>
+#include "py_main.h"
+
namespace py = pybind11;
using namespace libcamera;
@@ -30,7 +32,8 @@ void init_py_enums(py::module &m)
.value("Float", ControlType::ControlTypeFloat)
.value("String", ControlType::ControlTypeString)
.value("Rectangle", ControlType::ControlTypeRectangle)
- .value("Size", ControlType::ControlTypeSize);
+ .value("Size", ControlType::ControlTypeSize)
+ .value("Point", ControlType::ControlTypePoint);
py::enum_<Orientation>(m, "Orientation")
.value("Rotate0", Orientation::Rotate0)
diff --git a/src/py/libcamera/py_formats_generated.cpp.in b/src/py/libcamera/py_formats_generated.cpp.in
index a3f7f94d..c5fb9063 100644
--- a/src/py/libcamera/py_formats_generated.cpp.in
+++ b/src/py/libcamera/py_formats_generated.cpp.in
@@ -11,6 +11,8 @@
#include <pybind11/pybind11.h>
+#include "py_main.h"
+
namespace py = pybind11;
class PyFormats
diff --git a/src/py/libcamera/py_geometry.cpp b/src/py/libcamera/py_geometry.cpp
index 5c2aeac4..c7e30360 100644
--- a/src/py/libcamera/py_geometry.cpp
+++ b/src/py/libcamera/py_geometry.cpp
@@ -14,6 +14,8 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
+#include "py_main.h"
+
namespace py = pybind11;
using namespace libcamera;
diff --git a/src/py/libcamera/py_helpers.cpp b/src/py/libcamera/py_helpers.cpp
index 79891ab6..1ad1d4c1 100644
--- a/src/py/libcamera/py_helpers.cpp
+++ b/src/py/libcamera/py_helpers.cpp
@@ -34,6 +34,8 @@ static py::object valueOrTuple(const ControlValue &cv)
py::object controlValueToPy(const ControlValue &cv)
{
switch (cv.type()) {
+ case ControlTypeNone:
+ return py::none();
case ControlTypeBool:
return valueOrTuple<bool>(cv);
case ControlTypeByte:
@@ -46,14 +48,14 @@ py::object controlValueToPy(const ControlValue &cv)
return valueOrTuple<float>(cv);
case ControlTypeString:
return py::cast(cv.get<std::string>());
- case ControlTypeRectangle:
- return valueOrTuple<Rectangle>(cv);
case ControlTypeSize: {
const Size *v = reinterpret_cast<const Size *>(cv.data().data());
return py::cast(v);
}
- case ControlTypeNone:
- return py::none();
+ case ControlTypeRectangle:
+ return valueOrTuple<Rectangle>(cv);
+ case ControlTypePoint:
+ return valueOrTuple<Point>(cv);
default:
throw std::runtime_error("Unsupported ControlValue type");
}
@@ -73,6 +75,8 @@ static ControlValue controlValueMaybeArray(const py::object &ob)
ControlValue pyToControlValue(const py::object &ob, ControlType type)
{
switch (type) {
+ case ControlTypeNone:
+ return ControlValue();
case ControlTypeBool:
return ControlValue(ob.cast<bool>());
case ControlTypeByte:
@@ -89,8 +93,8 @@ ControlValue pyToControlValue(const py::object &ob, ControlType type)
return controlValueMaybeArray<Rectangle>(ob);
case ControlTypeSize:
return ControlValue(ob.cast<Size>());
- case ControlTypeNone:
- return ControlValue();
+ case ControlTypePoint:
+ return controlValueMaybeArray<Point>(ob);
default:
throw std::runtime_error("Control type not implemented");
}
diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
index bce08218..441a70ab 100644
--- a/src/py/libcamera/py_main.cpp
+++ b/src/py/libcamera/py_main.cpp
@@ -7,6 +7,7 @@
#include "py_main.h"
+#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
@@ -85,14 +86,6 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, PyCameraSmartPtr<T>)
*/
static std::weak_ptr<PyCameraManager> gCameraManager;
-void init_py_color_space(py::module &m);
-void init_py_controls_generated(py::module &m);
-void init_py_enums(py::module &m);
-void init_py_formats_generated(py::module &m);
-void init_py_geometry(py::module &m);
-void init_py_properties_generated(py::module &m);
-void init_py_transform(py::module &m);
-
PYBIND11_MODULE(_libcamera, m)
{
init_py_enums(m);
@@ -407,12 +400,26 @@ PYBIND11_MODULE(_libcamera, m)
pyControlId
.def_property_readonly("id", &ControlId::id)
.def_property_readonly("name", &ControlId::name)
+ .def_property_readonly("vendor", &ControlId::vendor)
.def_property_readonly("type", &ControlId::type)
+ .def_property_readonly("isArray", &ControlId::isArray)
+ .def_property_readonly("size", &ControlId::size)
.def("__str__", [](const ControlId &self) { return self.name(); })
.def("__repr__", [](const ControlId &self) {
- return py::str("libcamera.ControlId({}, {}, {})")
- .format(self.id(), self.name(), self.type());
- });
+ std::string sizeStr = "";
+ if (self.isArray()) {
+ sizeStr = "[";
+ size_t size = self.size();
+ if (size == std::numeric_limits<size_t>::max())
+ sizeStr += "n";
+ else
+ sizeStr += std::to_string(size);
+ sizeStr += "]";
+ }
+ return py::str("libcamera.ControlId({}, {}.{}{}, {})")
+ .format(self.id(), self.vendor(), self.name(), sizeStr, self.type());
+ })
+ .def("enumerators", &ControlId::enumerators);
pyControlInfo
.def_property_readonly("min", [](const ControlInfo &self) {
diff --git a/src/py/libcamera/py_main.h b/src/py/libcamera/py_main.h
index 5bb5f2d1..4d594326 100644
--- a/src/py/libcamera/py_main.h
+++ b/src/py/libcamera/py_main.h
@@ -7,8 +7,18 @@
#include <libcamera/base/log.h>
+#include <pybind11/pybind11.h>
+
namespace libcamera {
LOG_DECLARE_CATEGORY(Python)
}
+
+void init_py_color_space(pybind11::module &m);
+void init_py_controls_generated(pybind11::module &m);
+void init_py_enums(pybind11::module &m);
+void init_py_formats_generated(pybind11::module &m);
+void init_py_geometry(pybind11::module &m);
+void init_py_properties_generated(pybind11::module &m);
+void init_py_transform(pybind11::module &m);
diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in
deleted file mode 100644
index e49b6e91..00000000
--- a/src/py/libcamera/py_properties_generated.cpp.in
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
- *
- * Python bindings - Auto-generated properties
- *
- * This file is auto-generated. Do not edit.
- */
-
-#include <libcamera/property_ids.h>
-
-#include <pybind11/pybind11.h>
-
-namespace py = pybind11;
-
-class PyProperties
-{
-};
-
-class PyDraftProperties
-{
-};
-
-void init_py_properties_generated(py::module& m)
-{
- auto controls = py::class_<PyProperties>(m, "properties");
- auto draft = py::class_<PyDraftProperties>(controls, "draft");
-
-${controls}
-}
diff --git a/src/py/libcamera/py_transform.cpp b/src/py/libcamera/py_transform.cpp
index f3a0bfaf..768260ff 100644
--- a/src/py/libcamera/py_transform.cpp
+++ b/src/py/libcamera/py_transform.cpp
@@ -12,6 +12,8 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
+#include "py_main.h"
+
namespace py = pybind11;
using namespace libcamera;
diff --git a/src/py/meson.build b/src/py/meson.build
index a4586b4a..92280697 100644
--- a/src/py/meson.build
+++ b/src/py/meson.build
@@ -1,3 +1,15 @@
# SPDX-License-Identifier: CC0-1.0
+py3_dep = dependency('python3', required : get_option('pycamera'))
+pybind11_dep = dependency('pybind11', required : get_option('pycamera'))
+
+pycamera_enabled = py3_dep.found() and pybind11_dep.found()
+if not pycamera_enabled
+ subdir_done()
+endif
+
subdir('libcamera')
+
+pycamera_devenv = environment()
+pycamera_devenv.prepend('PYTHONPATH', meson.current_build_dir())
+meson.add_devenv(pycamera_devenv)
diff --git a/src/v4l2/meson.build b/src/v4l2/meson.build
index ab4b35dd..2c040414 100644
--- a/src/v4l2/meson.build
+++ b/src/v4l2/meson.build
@@ -1,12 +1,11 @@
# SPDX-License-Identifier: CC0-1.0
-if not get_option('v4l2')
- v4l2_enabled = false
+v4l2_enabled = get_option('v4l2').allowed()
+
+if not v4l2_enabled
subdir_done()
endif
-v4l2_enabled = true
-
v4l2_compat_sources = files([
'v4l2_camera.cpp',
'v4l2_camera_file.cpp',
@@ -24,6 +23,7 @@ v4l2_compat_cpp_args = [
'-U_FILE_OFFSET_BITS',
'-D_FILE_OFFSET_BITS=32',
'-D_LARGEFILE64_SOURCE',
+ '-U_TIME_BITS',
'-fvisibility=hidden',
]
@@ -44,4 +44,5 @@ cdata.set('LIBCAMERA_V4L2_SO', get_option('prefix') / libcamera_libexecdir / 'v4
configure_file(input : 'libcamerify.in',
output : 'libcamerify',
configuration : cdata,
- install_dir : get_option('bindir'))
+ install_dir : get_option('bindir'),
+ install_tag : 'bin')
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index 7b97c2d5..94d138cd 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_camera.cpp - V4L2 compatibility camera
+ * V4L2 compatibility camera
*/
#include "v4l2_camera.h"
@@ -12,13 +12,15 @@
#include <libcamera/base/log.h>
+#include <libcamera/control_ids.h>
+
using namespace libcamera;
LOG_DECLARE_CATEGORY(V4L2Compat)
V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
- : camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
- efd_(-1), bufferAvailableCount_(0)
+ : camera_(camera), controls_(controls::controls), isRunning_(false),
+ bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
{
camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
}
@@ -202,10 +204,12 @@ int V4L2Camera::streamOn()
if (isRunning_)
return 0;
- int ret = camera_->start();
+ int ret = camera_->start(&controls_);
if (ret < 0)
return ret == -EACCES ? -EBUSY : ret;
+ controls_.clear();
+
isRunning_ = true;
for (Request *req : pendingRequests_) {
@@ -265,6 +269,8 @@ int V4L2Camera::qbuf(unsigned int index)
return 0;
}
+ request->controls().merge(std::move(controls_));
+
ret = camera_->queueRequest(request);
if (ret < 0) {
LOG(V4L2Compat, Error) << "Can't queue request";
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index d3483444..9bd161b9 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -2,19 +2,21 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_camera.h - V4L2 compatibility camera
+ * V4L2 compatibility camera
*/
#pragma once
#include <deque>
-#include <utility>
+#include <memory>
+#include <vector>
#include <libcamera/base/mutex.h>
#include <libcamera/base/semaphore.h>
#include <libcamera/base/shared_fd.h>
#include <libcamera/camera.h>
+#include <libcamera/controls.h>
#include <libcamera/framebuffer.h>
#include <libcamera/framebuffer_allocator.h>
@@ -49,6 +51,9 @@ public:
const libcamera::Size &size,
libcamera::StreamConfiguration *streamConfigOut);
+ libcamera::ControlList &controls() { return controls_; }
+ const libcamera::ControlInfoMap &controlInfo() { return camera_->controls(); }
+
int allocBuffers(unsigned int count);
void freeBuffers();
int getBufferFd(unsigned int index);
@@ -70,6 +75,8 @@ private:
std::shared_ptr<libcamera::Camera> camera_;
std::unique_ptr<libcamera::CameraConfiguration> config_;
+ libcamera::ControlList controls_;
+
bool isRunning_;
libcamera::Mutex bufferLock_;
diff --git a/src/v4l2/v4l2_camera_file.cpp b/src/v4l2/v4l2_camera_file.cpp
index 0a41587c..d8fe854b 100644
--- a/src/v4l2/v4l2_camera_file.cpp
+++ b/src/v4l2/v4l2_camera_file.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * v4l2_camera_file.h - V4L2 compatibility camera file information
+ * V4L2 compatibility camera file information
*/
#include "v4l2_camera_file.h"
diff --git a/src/v4l2/v4l2_camera_file.h b/src/v4l2/v4l2_camera_file.h
index 1a7b6a63..1212989e 100644
--- a/src/v4l2/v4l2_camera_file.h
+++ b/src/v4l2/v4l2_camera_file.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2020, Google Inc.
*
- * v4l2_camera_file.h - V4L2 compatibility camera file information
+ * V4L2 compatibility camera file information
*/
#pragma once
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index 341f7902..559ffc61 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -2,13 +2,12 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_camera_proxy.cpp - Proxy to V4L2 compatibility camera
+ * Proxy to V4L2 compatibility camera
*/
#include "v4l2_camera_proxy.h"
#include <algorithm>
-#include <array>
#include <errno.h>
#include <numeric>
#include <set>
@@ -23,9 +22,11 @@
#include <libcamera/base/utils.h>
#include <libcamera/camera.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
#include <libcamera/formats.h>
-#include "libcamera/internal/formats.h"
+#include "libcamera/internal/v4l2_pixelformat.h"
#include "v4l2_camera.h"
#include "v4l2_camera_file.h"
@@ -34,6 +35,7 @@
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
using namespace libcamera;
+using namespace std::literals::chrono_literals;
LOG_DECLARE_CATEGORY(V4L2Compat)
@@ -193,6 +195,29 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)
v4l2PixFormat_.xfer_func = V4L2_XFER_FUNC_DEFAULT;
sizeimage_ = streamConfig.frameSize;
+
+ const ControlInfoMap &controls = vcam_->controlInfo();
+ const auto &it = controls.find(&controls::FrameDurationLimits);
+
+ if (it != controls.end()) {
+ const int64_t duration = it->second.def().get<int64_t>();
+
+ v4l2TimePerFrame_.numerator = duration;
+ v4l2TimePerFrame_.denominator = 1000000;
+ } else {
+ /*
+ * Default to 30fps if the camera doesn't expose the
+ * FrameDurationLimits control.
+ *
+ * \todo Remove this once all pipeline handlers implement the
+ * control
+ */
+ LOG(V4L2Compat, Warning)
+ << "Camera does not support FrameDurationLimits";
+
+ v4l2TimePerFrame_.numerator = 333333;
+ v4l2TimePerFrame_.denominator = 1000000;
+ }
}
void V4L2CameraProxy::querycap(std::shared_ptr<Camera> camera)
@@ -756,6 +781,55 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
return ret;
}
+int V4L2CameraProxy::vidioc_g_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg)
+{
+ LOG(V4L2Compat, Debug)
+ << "[" << file->description() << "] " << __func__ << "()";
+
+ if (!validateBufferType(arg->type))
+ return -EINVAL;
+
+ memset(&arg->parm, 0, sizeof(arg->parm));
+
+ arg->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ arg->parm.capture.timeperframe = v4l2TimePerFrame_;
+
+ return 0;
+}
+
+int V4L2CameraProxy::vidioc_s_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg)
+{
+ LOG(V4L2Compat, Debug)
+ << "[" << file->description() << "] " << __func__ << "()";
+
+ if (!validateBufferType(arg->type))
+ return -EINVAL;
+
+ /*
+ * Store the frame duration if it is valid, otherwise keep the current
+ * value.
+ *
+ * \todo The provided value should be adjusted based on the camera
+ * capabilities.
+ */
+ if (arg->parm.capture.timeperframe.numerator &&
+ arg->parm.capture.timeperframe.denominator)
+ v4l2TimePerFrame_ = arg->parm.capture.timeperframe;
+
+ memset(&arg->parm, 0, sizeof(arg->parm));
+
+ arg->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ arg->parm.capture.timeperframe = v4l2TimePerFrame_;
+
+ /* Apply the frame duration. */
+ utils::Duration frameDuration = 1.0s * v4l2TimePerFrame_.numerator
+ / v4l2TimePerFrame_.denominator;
+ int64_t uDuration = frameDuration.get<std::micro>();
+ vcam_->controls().set(controls::FrameDurationLimits, { uDuration, uDuration });
+
+ return 0;
+}
+
const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
VIDIOC_QUERYCAP,
VIDIOC_ENUM_FRAMESIZES,
@@ -776,6 +850,8 @@ const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
VIDIOC_EXPBUF,
VIDIOC_STREAMON,
VIDIOC_STREAMOFF,
+ VIDIOC_G_PARM,
+ VIDIOC_S_PARM,
};
int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long longRequest, void *arg)
@@ -863,6 +939,12 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long longRequest, void
case VIDIOC_STREAMOFF:
ret = vidioc_streamoff(file, static_cast<int *>(arg));
break;
+ case VIDIOC_G_PARM:
+ ret = vidioc_g_parm(file, static_cast<struct v4l2_streamparm *>(arg));
+ break;
+ case VIDIOC_S_PARM:
+ ret = vidioc_s_parm(file, static_cast<struct v4l2_streamparm *>(arg));
+ break;
default:
ret = -ENOTTY;
break;
diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
index 8a0195e1..5aa352c3 100644
--- a/src/v4l2/v4l2_camera_proxy.h
+++ b/src/v4l2/v4l2_camera_proxy.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera
+ * Proxy to V4L2 compatibility camera
*/
#pragma once
@@ -67,6 +67,8 @@ private:
int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer *arg);
int vidioc_streamon(V4L2CameraFile *file, int *arg);
int vidioc_streamoff(V4L2CameraFile *file, int *arg);
+ int vidioc_g_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg);
+ int vidioc_s_parm(V4L2CameraFile *file, struct v4l2_streamparm *arg);
bool hasOwnership(V4L2CameraFile *file);
int acquire(V4L2CameraFile *file);
@@ -84,6 +86,7 @@ private:
struct v4l2_capability capabilities_;
struct v4l2_pix_format v4l2PixFormat_;
+ struct v4l2_fract v4l2TimePerFrame_;
std::vector<struct v4l2_buffer> buffers_;
std::map<void *, unsigned int> mmaps_;
diff --git a/src/v4l2/v4l2_compat.cpp b/src/v4l2/v4l2_compat.cpp
index 1765fb5d..ff833f57 100644
--- a/src/v4l2/v4l2_compat.cpp
+++ b/src/v4l2/v4l2_compat.cpp
@@ -2,17 +2,19 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_compat.cpp - V4L2 compatibility layer
+ * V4L2 compatibility layer
*/
#include "v4l2_compat_manager.h"
-#include <errno.h>
+#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <libcamera/base/utils.h>
@@ -28,71 +30,97 @@ using namespace libcamera;
va_end(ap); \
}
+namespace {
+
+/*
+ * Determine if the flags require a further mode arguments that needs to be
+ * parsed from va_args.
+ */
+bool needs_mode(int flags)
+{
+ return (flags & O_CREAT) || ((flags & O_TMPFILE) == O_TMPFILE);
+}
+
+} /* namespace */
+
extern "C" {
LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...)
{
mode_t mode = 0;
- if (oflag & O_CREAT || oflag & O_TMPFILE)
+ if (needs_mode(oflag))
extract_va_arg(mode_t, mode, oflag);
return V4L2CompatManager::instance()->openat(AT_FDCWD, path,
oflag, mode);
}
-/* _FORTIFY_SOURCE redirects open to __open_2 */
-LIBCAMERA_PUBLIC int __open_2(const char *path, int oflag)
-{
- return open(path, oflag);
-}
-
#ifndef open64
LIBCAMERA_PUBLIC int open64(const char *path, int oflag, ...)
{
mode_t mode = 0;
- if (oflag & O_CREAT || oflag & O_TMPFILE)
+ if (needs_mode(oflag))
extract_va_arg(mode_t, mode, oflag);
return V4L2CompatManager::instance()->openat(AT_FDCWD, path,
oflag | O_LARGEFILE, mode);
}
-
-LIBCAMERA_PUBLIC int __open64_2(const char *path, int oflag)
-{
- return open(path, oflag);
-}
#endif
LIBCAMERA_PUBLIC int openat(int dirfd, const char *path, int oflag, ...)
{
mode_t mode = 0;
- if (oflag & O_CREAT || oflag & O_TMPFILE)
+ if (needs_mode(oflag))
extract_va_arg(mode_t, mode, oflag);
return V4L2CompatManager::instance()->openat(dirfd, path, oflag, mode);
}
-LIBCAMERA_PUBLIC int __openat_2(int dirfd, const char *path, int oflag)
-{
- return openat(dirfd, path, oflag);
-}
-
#ifndef openat64
LIBCAMERA_PUBLIC int openat64(int dirfd, const char *path, int oflag, ...)
{
mode_t mode = 0;
- if (oflag & O_CREAT || oflag & O_TMPFILE)
+ if (needs_mode(oflag))
extract_va_arg(mode_t, mode, oflag);
return V4L2CompatManager::instance()->openat(dirfd, path,
oflag | O_LARGEFILE, mode);
}
+#endif
-LIBCAMERA_PUBLIC int __openat64_2(int dirfd, const char *path, int oflag)
+/*
+ * _FORTIFY_SOURCE redirects open* to __open*_2. Disable the
+ * -Wmissing-declarations warnings, as the functions won't be declared if
+ * _FORTIFY_SOURCE is not in use.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+
+LIBCAMERA_PUBLIC int __open_2(const char *path, int oflag)
+{
+ assert(!needs_mode(oflag));
+ return open(path, oflag);
+}
+
+LIBCAMERA_PUBLIC int __open64_2(const char *path, int oflag)
{
+ assert(!needs_mode(oflag));
+ return open64(path, oflag);
+}
+
+LIBCAMERA_PUBLIC int __openat_2(int dirfd, const char *path, int oflag)
+{
+ assert(!needs_mode(oflag));
return openat(dirfd, path, oflag);
}
-#endif
+
+LIBCAMERA_PUBLIC int __openat64_2(int dirfd, const char *path, int oflag)
+{
+ assert(!needs_mode(oflag));
+ return openat64(dirfd, path, oflag);
+}
+
+#pragma GCC diagnostic pop
LIBCAMERA_PUBLIC int dup(int oldfd)
{
@@ -125,7 +153,11 @@ LIBCAMERA_PUBLIC int munmap(void *addr, size_t length)
return V4L2CompatManager::instance()->munmap(addr, length);
}
+#if HAVE_POSIX_IOCTL
+LIBCAMERA_PUBLIC int ioctl(int fd, int request, ...)
+#else
LIBCAMERA_PUBLIC int ioctl(int fd, unsigned long request, ...)
+#endif
{
void *arg;
extract_va_arg(void *, arg, request);
diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp
index 5e8cdb4f..f53fb300 100644
--- a/src/v4l2/v4l2_compat_manager.cpp
+++ b/src/v4l2/v4l2_compat_manager.cpp
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_compat_manager.cpp - V4L2 compatibility manager
+ * V4L2 compatibility manager
*/
#include "v4l2_compat_manager.h"
@@ -10,7 +10,6 @@
#include <dlfcn.h>
#include <fcntl.h>
#include <map>
-#include <stdarg.h>
#include <string.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h
index 64af9a8c..f7c6f122 100644
--- a/src/v4l2/v4l2_compat_manager.h
+++ b/src/v4l2/v4l2_compat_manager.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2019, Google Inc.
*
- * v4l2_compat_manager.h - V4L2 compatibility manager
+ * V4L2 compatibility manager
*/
#pragma once