diff --git a/README.md b/README.md index e2ae450ac..24008cc30 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ``` diff --git a/depthai_demo.py b/depthai_demo.py index 8922dc283..f5452320e 100755 --- a/depthai_demo.py +++ b/depthai_demo.py @@ -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) @@ -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() @@ -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() diff --git a/depthai_helpers/arg_manager.py b/depthai_helpers/arg_manager.py index 21a24286d..f398bdb48 100644 --- a/depthai_helpers/arg_manager.py +++ b/depthai_helpers/arg_manager.py @@ -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() diff --git a/depthai_sdk/setup.py b/depthai_sdk/setup.py index e8306fcde..0cbbc995e 100644 --- a/depthai_sdk/setup.py +++ b/depthai_sdk/setup.py @@ -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", diff --git a/depthai_sdk/src/depthai_sdk/managers/pipeline_manager.py b/depthai_sdk/src/depthai_sdk/managers/pipeline_manager.py index 6ad8675db..cad2d21f3 100644 --- a/depthai_sdk/src/depthai_sdk/managers/pipeline_manager.py +++ b/depthai_sdk/src/depthai_sdk/managers/pipeline_manager.py @@ -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): """ @@ -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): @@ -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): """ @@ -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): """ @@ -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