Skip to content

Commit

Permalink
Merge pull request #498 from luxonis/camera_control
Browse files Browse the repository at this point in the history
Add the ability to control camera properties from GUI and from cmd arguments
  • Loading branch information
VanDavv committed Oct 7, 2021
2 parents 1b0b513 + 4872aef commit 2d13ccf
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 6 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ usage: depthai_demo.py [-h] [-cam {left,right,color}] [-vid VIDEO] [-dd] [-dnn]
[-s {nnInput,color,left,right,depth,depthRaw,disparity,disparityColor,rectifiedLeft,rectifiedRight} [{nnInput,color,left,right,depth,depthRaw,disparity,disparityColor,rectifiedLeft,rectifiedRight} ...]]
[--report {temp,cpu,memory} [{temp,cpu,memory} ...]] [--reportFile REPORTFILE] [-sync] [-monor {400,720,800}] [-monof MONOFPS] [-cb CALLBACK]
[--openvinoVersion {2020_3,2020_4,2021_1,2021_2,2021_3,2021_4}] [--count COUNTLABEL] [-dev DEVICEID] [-bandw {auto,low,high}] [-usbs {usb2,usb3}]
[-enc ENCODE [ENCODE ...]] [-encout ENCODEOUTPUT] [-xls XLINKCHUNKSIZE] [-camo CAMERAORIENTATION [CAMERAORIENTATION ...]]
[-enc ENCODE [ENCODE ...]] [-encout ENCODEOUTPUT] [-xls XLINKCHUNKSIZE] [-camo CAMERAORIENTATION [CAMERAORIENTATION ...]] [--cameraControlls]
[--cameraExposure CAMERAEXPOSURE] [--cameraSensitivity CAMERASENSITIVITY] [--cameraSaturation CAMERASATURATION] [--cameraContrast CAMERACONTRAST]
[--cameraBrightness CAMERABRIGHTNESS] [--cameraSharpness CAMERASHARPNESS]
optional arguments:
-h, --help show this help message and exit
Expand Down Expand Up @@ -126,6 +129,19 @@ optional arguments:
Define cameras orientation (available: AUTO, NORMAL, HORIZONTAL_MIRROR, VERTICAL_FLIP, ROTATE_180_DEG)
Format: camera_name,camera_orientation
Example: -camo color,ROTATE_180_DEG right,ROTATE_180_DEG left,ROTATE_180_DEG
--cameraControlls Show camera configuration options in GUI and controll them using keyboard
--cameraExposure CAMERAEXPOSURE
Specify camera saturation
--cameraSensitivity CAMERASENSITIVITY
Specify camera sensitivity
--cameraSaturation CAMERASATURATION
Specify image saturation
--cameraContrast CAMERACONTRAST
Specify image contrast
--cameraBrightness CAMERABRIGHTNESS
Specify image brightness
--cameraSharpness CAMERASHARPNESS
Specify image sharpness
```


Expand Down
93 changes: 90 additions & 3 deletions depthai_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,26 @@ def createQueueCallback(queueName):
cameras = device.getConnectedCameras()
if dai.CameraBoardSocket.LEFT in cameras and dai.CameraBoardSocket.RIGHT in cameras:
pv.collectCalibData(device)

cameraConfig = {
"exposure": conf.args.cameraExposure,
"sensitivity": conf.args.cameraSensitivity,
"saturation": conf.args.cameraSaturation,
"contrast": conf.args.cameraContrast,
"brightness": conf.args.cameraBrightness,
"sharpness": conf.args.cameraSharpness
}
def updateCameraConfigs():
if conf.leftCameraEnabled:
pm.updateLeftCamConfig(device, **cameraConfig)
if conf.rightCameraEnabled:
pm.updateRightCamConfig(device, **cameraConfig)
if conf.rgbCameraEnabled:
pm.updateColorCamConfig(device, **cameraConfig)

if any(cameraConfig.values()):
updateCameraConfigs()

pv.createQueues(device, createQueueCallback)
if encManager is not None:
encManager.createDefaultQueues(device)
Expand Down Expand Up @@ -295,13 +315,38 @@ def createQueueCallback(queueName):

def showFramesCallback(frame, name):
fps.drawFps(frame, name)
h, w = frame.shape[:2]
if name in [Previews.disparityColor.name, Previews.disparity.name, Previews.depth.name, Previews.depthRaw.name]:
h, w = frame.shape[:2]
text = "Median filter: {} [M]".format(pm._depthConfig.getMedianFilter().name.lstrip("KERNEL_").lstrip("MEDIAN_"))
cv2.putText(frame, text, (10, h - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, 0, 4)
cv2.putText(frame, text, (10, h - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, 255, 1)
returnFrame = callbacks.onShowFrame(frame, name)
return returnFrame if returnFrame is not None else frame
elif conf.args.cameraControlls and name in [Previews.color.name, Previews.left.name, Previews.right.name]:
text = "Exposure: {} T [+] [-] G".format(cameraConfig["exposure"] if cameraConfig["exposure"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 110), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 110), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
text = "Sensitivity: {} Y [+] [-] H".format(cameraConfig["sensitivity"] if cameraConfig["sensitivity"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 90), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 90), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
text = "Saturation: {} U [+] [-] J".format(cameraConfig["saturation"] if cameraConfig["saturation"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 70), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 70), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
text = "Contrast: {} I [+] [-] K".format(cameraConfig["contrast"] if cameraConfig["contrast"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 50), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 50), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
text = "Brightness: {} O [+] [-] L".format(cameraConfig["brightness"] if cameraConfig["brightness"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 30), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 30), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
text = "Sharpness: {} P [+] [-] ;".format(cameraConfig["sharpness"] if cameraConfig["sharpness"] is not None else "auto")
label_width = cv2.getTextSize(text, cv2.FONT_HERSHEY_TRIPLEX, 0.5, 4)[0][0]
cv2.putText(frame, text, (w - label_width, h - 10), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0, 0, 0), 4)
cv2.putText(frame, text, (w - label_width, h - 10), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (255, 255, 255), 1)
returnFrame = callbacks.onShowFrame(frame, name)
return returnFrame if returnFrame is not None else frame
pv.showFrames(callback=showFramesCallback)
elif hostFrame is not None:
debugHostFrame = hostFrame.copy()
Expand All @@ -321,6 +366,48 @@ def showFramesCallback(frame, name):
elif key == ord('m'):
nextFilter = next(medianFilters)
pm.updateDepthConfig(device, median=nextFilter)

if conf.args.cameraControlls:
update = True

if key == ord('t'):
cameraConfig["exposure"] = 10000 if cameraConfig["exposure"] is None else 500 if cameraConfig["exposure"] == 1 else min(cameraConfig["exposure"] + 500, 33000)
if cameraConfig["sensitivity"] is None:
cameraConfig["sensitivity"] = 800
elif key == ord('g'):
cameraConfig["exposure"] = 10000 if cameraConfig["exposure"] is None else max(cameraConfig["exposure"] - 500, 1)
if cameraConfig["sensitivity"] is None:
cameraConfig["sensitivity"] = 800
elif key == ord('y'):
cameraConfig["sensitivity"] = 800 if cameraConfig["sensitivity"] is None else min(cameraConfig["sensitivity"] + 50, 1600)
if cameraConfig["exposure"] is None:
cameraConfig["exposure"] = 10000
elif key == ord('h'):
cameraConfig["sensitivity"] = 800 if cameraConfig["sensitivity"] is None else max(cameraConfig["sensitivity"] - 50, 100)
if cameraConfig["exposure"] is None:
cameraConfig["exposure"] = 10000
elif key == ord('u'):
cameraConfig["saturation"] = 0 if cameraConfig["saturation"] is None else min(cameraConfig["saturation"] + 1, 10)
elif key == ord('j'):
cameraConfig["saturation"] = 0 if cameraConfig["saturation"] is None else max(cameraConfig["saturation"] - 1, -10)
elif key == ord('i'):
cameraConfig["contrast"] = 0 if cameraConfig["contrast"] is None else min(cameraConfig["contrast"] + 1, 10)
elif key == ord('k'):
cameraConfig["contrast"] = 0 if cameraConfig["contrast"] is None else max(cameraConfig["contrast"] - 1, -10)
elif key == ord('o'):
cameraConfig["brightness"] = 0 if cameraConfig["brightness"] is None else min(cameraConfig["brightness"] + 1, 10)
elif key == ord('l'):
cameraConfig["brightness"] = 0 if cameraConfig["brightness"] is None else max(cameraConfig["brightness"] - 1, -10)
elif key == ord('p'):
cameraConfig["sharpness"] = 0 if cameraConfig["sharpness"] is None else min(cameraConfig["sharpness"] + 1, 4)
elif key == ord(';'):
cameraConfig["sharpness"] = 0 if cameraConfig["sharpness"] is None else max(cameraConfig["sharpness"] - 1, 0)
else:
update = False

if update:
updateCameraConfigs()

finally:
if conf.useCamera and encManager is not None:
encManager.close()
Expand Down
10 changes: 9 additions & 1 deletion depthai_helpers/arg_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,18 @@ def parseArgs():
"Example: -enc left color \n"
"Example: -enc color right,10 left,10")
parser.add_argument('-encout', '--encodeOutput', type=Path, default=projectRoot, help="Path to directory where to store encoded files. Default: %(default)s")
parser.add_argument('-xls', '--xlinkChunkSize', type=int, default = None, help="Specify XLink chunk size")
parser.add_argument('-xls', '--xlinkChunkSize', type=int, help="Specify XLink chunk size")
parser.add_argument('-camo', '--cameraOrientation', type=_comaSeparated(default="AUTO", cast=orientationCast), nargs="+", default=[],
help=("Define cameras orientation (available: {}) \n"
"Format: camera_name,camera_orientation \n"
"Example: -camo color,ROTATE_180_DEG right,ROTATE_180_DEG left,ROTATE_180_DEG").format(', '.join(orientationChoices))
)
parser.add_argument("--cameraControlls", action="store_true", help="Show camera configuration options in GUI and control them using keyboard")
parser.add_argument("--cameraExposure", type=int, help="Specify camera saturation")
parser.add_argument("--cameraSensitivity", type=int, help="Specify camera sensitivity")
parser.add_argument("--cameraSaturation", type=checkRange(-10, 10), help="Specify image saturation")
parser.add_argument("--cameraContrast", type=checkRange(-10, 10), help="Specify image contrast")
parser.add_argument("--cameraBrightness", type=checkRange(-10, 10), help="Specify image brightness")
parser.add_argument("--cameraSharpness", type=checkRange(0, 4), help="Specify image sharpness")

return parser.parse_args()
2 changes: 1 addition & 1 deletion depthai_sdk/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name='depthai-sdk',
version='1.0.1',
version='1.1.0',
description='This package contains convenience classes and functions that help in most common tasks while using DepthAI API',
long_description=io.open("README.md", encoding="utf-8").read(),
long_description_content_type="text/markdown",
Expand Down
73 changes: 73 additions & 0 deletions depthai_sdk/src/depthai_sdk/managers/pipeline_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def __init__(self, openvinoVersion=None):
nodes = SimpleNamespace()

_depthConfig = dai.StereoDepthConfig()
_rgbConfig = dai.CameraControl()
_leftConfig = dai.CameraControl()
_rightConfig = dai.CameraControl()

def setNnManager(self, nnManager):
"""
Expand Down Expand Up @@ -130,6 +133,9 @@ def createColorCam(self, previewSize=None, res=dai.ColorCameraProperties.SensorR
self._mjpegLink(self.nodes.camRgb, self.nodes.xoutRgb, self.nodes.camRgb.video)
else:
self.nodes.camRgb.video.link(self.nodes.xoutRgb.input)
self.nodes.xinRgbControl = self.pipeline.createXLinkIn()
self.nodes.xinRgbControl.setStreamName(Previews.color.name + "_control")
self.nodes.xinRgbControl.out.link(self.nodes.camRgb.inputControl)


def createLeftCam(self, res=dai.MonoCameraProperties.SensorResolution.THE_720_P, fps=30, orientation: dai.CameraImageOrientation=None, xout=False):
Expand All @@ -156,6 +162,9 @@ def createLeftCam(self, res=dai.MonoCameraProperties.SensorResolution.THE_720_P,
self._mjpegLink(self.nodes.monoLeft, self.nodes.xoutLeft, self.nodes.monoLeft.out)
else:
self.nodes.monoLeft.out.link(self.nodes.xoutLeft.input)
self.nodes.xinLeftControl = self.pipeline.createXLinkIn()
self.nodes.xinLeftControl.setStreamName(Previews.left.name + "_control")
self.nodes.xinLeftControl.out.link(self.nodes.monoLeft.inputControl)

def createRightCam(self, res=dai.MonoCameraProperties.SensorResolution.THE_720_P, fps=30, orientation: dai.CameraImageOrientation=None, xout=False):
"""
Expand All @@ -181,6 +190,9 @@ def createRightCam(self, res=dai.MonoCameraProperties.SensorResolution.THE_720_P
self._mjpegLink(self.nodes.monoRight, self.nodes.xoutRight, self.nodes.monoRight.out)
else:
self.nodes.monoRight.out.link(self.nodes.xoutRight.input)
self.nodes.xinRightControl = self.pipeline.createXLinkIn()
self.nodes.xinRightControl.setStreamName(Previews.right.name + "_control")
self.nodes.xinRightControl.out.link(self.nodes.monoRight.inputControl)

def createDepth(self, dct=245, median=dai.MedianFilter.KERNEL_7x7, sigma=0, lr=False, lrcThreshold=4, extended=False, subpixel=False, useDisparity=False, useDepth=False, useRectifiedLeft=False, useRectifiedRight=False):
"""
Expand Down Expand Up @@ -264,6 +276,67 @@ def createDepth(self, dct=245, median=dai.MedianFilter.KERNEL_7x7, sigma=0, lr=F
else:
self.nodes.stereo.rectifiedRight.link(self.nodes.xoutRectRight.input)

def _updateCamConfig(self, configRef: dai.CameraControl, cameraName, device, exposure=None, sensitivity=None, saturation=None, contrast=None, brightness=None, sharpness=None):
if any([exposure, sensitivity]):
if not all([exposure, sensitivity]):
raise RuntimeError("Both \"exposure\" and \"sensitivity\" arguments must be provided")
configRef.setManualExposure(exposure, sensitivity)
if saturation is not None:
configRef.setSaturation(saturation)
if sharpness is not None:
configRef.setSharpness(sharpness)
if contrast is not None:
configRef.setContrast(contrast)
if brightness is not None:
configRef.setBrightness(brightness)

device.getInputQueue(cameraName + "_control").send(configRef)

def updateColorCamConfig(self, device, exposure=None, sensitivity=None, saturation=None, contrast=None, brightness=None, sharpness=None):
"""
Updates :obj:`depthai.node.ColorCamera` node config
Args:
device (depthai.Device): Running device instance
exposure (int, Optional): Exposure time in microseconds. Has to be set together with :obj:`sensitivity` (Usual range: 1..33000)
sensitivity (int, Optional): Sensivity as ISO value. Has to be set together with :obj:`exposure` (Usual range: 100..1600)
saturation (int, Optional): Image saturation (Allowed range: -10..10)
contrast (int, Optional): Image contrast (Allowed range: -10..10)
brightness (int, Optional): Image brightness (Allowed range: -10..10)
sharpness (int, Optional): Image sharpness (Allowed range: 0..4)
"""
self._updateCamConfig(self._rgbConfig, Previews.color.name, device, exposure, sensitivity, saturation, contrast, brightness, sharpness)

def updateLeftCamConfig(self, device, exposure=None, sensitivity=None, saturation=None, contrast=None, brightness=None, sharpness=None):
"""
Updates left :obj:`depthai.node.MonoCamera` node config
Args:
device (depthai.Device): Running device instance
exposure (int, Optional): Exposure time in microseconds. Has to be set together with :obj:`sensitivity` (Usual range: 1..33000)
sensitivity (int, Optional): Sensivity as ISO value. Has to be set together with :obj:`exposure` (Usual range: 100..1600)
saturation (int, Optional): Image saturation (Allowed range: -10..10)
contrast (int, Optional): Image contrast (Allowed range: -10..10)
brightness (int, Optional): Image brightness (Allowed range: -10..10)
sharpness (int, Optional): Image sharpness (Allowed range: 0..4)
"""
self._updateCamConfig(self._leftConfig, Previews.left.name, device, exposure, sensitivity, saturation, contrast, brightness, sharpness)

def updateRightCamConfig(self, device, exposure=None, sensitivity=None, saturation=None, contrast=None, brightness=None, sharpness=None):
"""
Updates right :obj:`depthai.node.MonoCamera` node config
Args:
device (depthai.Device): Running device instance
exposure (int, Optional): Exposure time in microseconds. Has to be set together with :obj:`sensitivity` (Usual range: 1..33000)
sensitivity (int, Optional): Sensivity as ISO value. Has to be set together with :obj:`exposure` (Usual range: 100..1600)
saturation (int, Optional): Image saturation (Allowed range: -10..10)
contrast (int, Optional): Image contrast (Allowed range: -10..10)
brightness (int, Optional): Image brightness (Allowed range: -10..10)
sharpness (int, Optional): Image sharpness (Allowed range: 0..4)
"""
self._updateCamConfig(self._rightConfig, Previews.right.name, device, exposure, sensitivity, saturation, contrast, brightness, sharpness)

def updateDepthConfig(self, device, dct=None, sigma=None, median=None, lrcThreshold=None):
"""
Updates :obj:`depthai.node.StereoDepth` node config
Expand Down

0 comments on commit 2d13ccf

Please sign in to comment.