Skip to content

Commit ba0e78b

Browse files
authored
Merge pull request #986 from luxonis/camera_controls_misc
Miscellaneous camera controls, IMX582/IMX586 on-sensor HDR
2 parents 7629453 + 21bea7e commit ba0e78b

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

src/pipeline/datatype/CameraControlBindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,13 @@ std::vector<const char *> camCtrlAttr;
242242
.def("setEffectMode", &CameraControl::setEffectMode, py::arg("mode"), DOC(dai, CameraControl, setEffectMode))
243243
.def("setControlMode", &CameraControl::setControlMode, py::arg("mode"), DOC(dai, CameraControl, setControlMode))
244244
.def("setCaptureIntent", &CameraControl::setCaptureIntent, py::arg("mode"), DOC(dai, CameraControl, setCaptureIntent))
245+
.def("setMisc", py::overload_cast<std::string, std::string>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc))
246+
.def("setMisc", py::overload_cast<std::string, int>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc, 2))
247+
.def("setMisc", py::overload_cast<std::string, float>(&CameraControl::setMisc), py::arg("control"), py::arg("value"), DOC(dai, CameraControl, setMisc, 3))
248+
.def("clearMiscControls", &CameraControl::clearMiscControls, DOC(dai, CameraControl, clearMiscControls))
245249
.def("set", &CameraControl::set, py::arg("config"), DOC(dai, CameraControl, set))
246250
// getters
251+
.def("getMiscControls", &CameraControl::getMiscControls, DOC(dai, CameraControl, getMiscControls))
247252
.def("getCaptureStill", &CameraControl::getCaptureStill, DOC(dai, CameraControl, getCaptureStill))
248253
.def("getExposureTime", &CameraControl::getExposureTime, DOC(dai, CameraControl, getExposureTime))
249254
.def("getSensitivity", &CameraControl::getSensitivity, DOC(dai, CameraControl, getSensitivity))

utilities/cam_test.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from pathlib import Path
5151
import sys
5252
import signal
53+
import math
5354
from stress_test import stress_test, YOLO_LABELS, create_yolo
5455

5556

@@ -66,6 +67,8 @@ def socket_type_pair(arg):
6667
is_thermal = True if type in ['th', 'thermal'] else False
6768
return [socket, is_color, is_tof, is_thermal]
6869

70+
def string_pair(arg):
71+
return arg.split('=')
6972

7073
parser = argparse.ArgumentParser(add_help=False)
7174
parser.add_argument('-cams', '--cameras', type=socket_type_pair, nargs='+',
@@ -104,6 +107,10 @@ def socket_type_pair(arg):
104107
help="Show RGB `preview` stream instead of full size `isp`")
105108
parser.add_argument('-show', '--show-meta', action='store_true',
106109
help="List frame metadata (seqno, timestamp, exp, iso etc). Can also toggle with `\`")
110+
parser.add_argument('-misc', '--misc-controls', type=string_pair, nargs='+',
111+
default=[],
112+
help="List of miscellaneous camera controls to set initially, "
113+
"as pairs: key1=value1 key2=value2 ...")
107114

108115
parser.add_argument('-d', '--device', default="", type=str,
109116
help="Optional MX ID of the device to connect to.")
@@ -198,8 +205,6 @@ def clamp(num, v0, v1):
198205
return max(v0, min(num, v1))
199206

200207
# Calculates FPS over a moving window, configurable
201-
202-
203208
class FPS:
204209
def __init__(self, window_size=30):
205210
self.dq = collections.deque(maxlen=window_size)
@@ -360,6 +365,15 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
360365
# cam[c].initialControl.setManualExposure(15000, 400) # exposure [us], iso
361366
# When set, takes effect after the first 2 frames
362367
# cam[c].initialControl.setManualWhiteBalance(4000) # light temperature in K, 1000..12000
368+
# cam[c].initialControl.setAutoExposureLimit(5000) # can also be updated at runtime
369+
# cam[c].initialControl.setMisc("downsampling-mode", "binning") # default: "scaling"
370+
# cam[c].initialControl.setMisc("binning-mode", "sum") # default: "avg"
371+
# cam[c].initialControl.setMisc("manual-exposure-handling", "fast") # default: "default"
372+
# cam[c].initialControl.setMisc("hdr-exposure-ratio", 4) # enables HDR when set `> 1`, current options: 2, 4, 8
373+
# cam[c].initialControl.setMisc("hdr-local-tone-weight", 75) # default 75, range 0..100
374+
# cam[c].initialControl.setMisc("high-conversion-gain", 0) # 1 to enable (default on supported sensors)
375+
for kvPair in args.misc_controls:
376+
cam[c].initialControl.setMisc(*kvPair)
363377
control.out.link(cam[c].inputControl)
364378
if rotate[c]:
365379
cam[c].setImageOrientation(
@@ -534,6 +548,13 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
534548
chroma_denoise = 0
535549
control = 'none'
536550
show = args.show_meta
551+
high_conversion_gain = 1
552+
print(args.misc_controls)
553+
args_misc_dict = dict(args.misc_controls)
554+
555+
hdr_exp_ratio = int(math.log2(float(args_misc_dict.get('hdr-exposure-ratio', 1))))
556+
hdr_local_tone_weight = int(32 * float(args_misc_dict.get('hdr-local-tone-weight', 0.75)))
557+
hdr_on = (hdr_exp_ratio > 0)
537558

538559
jet_custom = cv2.applyColorMap(
539560
np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET)
@@ -664,6 +685,12 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
664685
elif key == ord('c'):
665686
capture_list = streams.copy()
666687
capture_time = time.strftime('%Y%m%d_%H%M%S')
688+
elif key == ord('h'):
689+
high_conversion_gain = 1 - high_conversion_gain
690+
print("High conversion gain:", high_conversion_gain)
691+
ctrl = dai.CameraControl()
692+
ctrl.setMisc("high-conversion-gain", high_conversion_gain)
693+
controlQueue.send(ctrl)
667694
elif key == ord('t'):
668695
print("Autofocus trigger (and disable continuous)")
669696
ctrl = dai.CameraControl()
@@ -742,7 +769,7 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
742769
floodIntensity = 0
743770
device.setIrFloodLightIntensity(floodIntensity)
744771
print(f'IR Flood intensity:', floodIntensity)
745-
elif key >= 0 and chr(key) in '34567890[]p\\;\'':
772+
elif key >= 0 and chr(key) in '34567890[]\\;\'rg':
746773
if key == ord('3'):
747774
control = 'awb_mode'
748775
elif key == ord('4'):
@@ -771,6 +798,11 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
771798
control = 'chroma_denoise'
772799
elif key == ord('p'):
773800
control = 'tof_amplitude_min'
801+
elif key == ord('r') or key == ord('g'):
802+
if hdr_on:
803+
control = 'hdr_exp_ratio' if key == ord('r') else 'hdr_local_tone_weight'
804+
else:
805+
print("HDR was not enabled, start with `-misc hdr-exposure-ratio=2` or higher to enable")
774806
print("Selected control:", control)
775807
elif key in [ord('-'), ord('_'), ord('+'), ord('=')]:
776808
change = 0
@@ -780,7 +812,7 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
780812
change = 1
781813
ctrl = dai.CameraControl()
782814
if control == 'none':
783-
print("Please select a control first using keys 3..9 0 [ ] \\ ; \'")
815+
print("Please select a control first using keys 3..9 0 [ ] \\ ; \' r g")
784816
elif control == 'ae_comp':
785817
ae_comp = clamp(ae_comp + change, -9, 9)
786818
print("Auto exposure compensation:", ae_comp)
@@ -833,6 +865,16 @@ def socket_to_socket_opt(socket: dai.CameraBoardSocket) -> str:
833865
chroma_denoise = clamp(chroma_denoise + change, 0, 4)
834866
print("Chroma denoise:", chroma_denoise)
835867
ctrl.setChromaDenoise(chroma_denoise)
868+
elif control == 'hdr_exp_ratio':
869+
hdr_exp_ratio = clamp(hdr_exp_ratio + change, 0, 3)
870+
value = pow(2, hdr_exp_ratio)
871+
print("HDR exposure ratio:", value)
872+
ctrl.setMisc("hdr-exposure-ratio", value)
873+
elif control == 'hdr_local_tone_weight':
874+
hdr_local_tone_weight = clamp(hdr_local_tone_weight + change, 0, 32)
875+
value = hdr_local_tone_weight / 32
876+
print(f"HDR local tone weight (normalized): {value:.2f}")
877+
ctrl.setMisc("hdr-local-tone-weight", value)
836878
controlQueue.send(ctrl)
837879

838880
print()

0 commit comments

Comments
 (0)