-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes and improvements for StereoDepth, ColorCamera #82
Changes from 17 commits
db02740
ea9a98b
0431b74
5f2f312
e04b257
2f9ad0a
f22ca12
4fc1433
974dc98
4ec7f1f
ca3784b
69726b3
f809bb6
d4338e2
8c3a8e6
29ca1c5
757d830
3964a7e
fefb4b0
878e4a6
b72bb07
7df7402
df43775
b0dd5d6
6e11c1f
1e05f8c
3f2fb9d
6d84ac9
f62304d
2e25a1e
bf1cbdd
bffa915
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include <cstdio> | ||
#include <iostream> | ||
|
||
#include "utility.hpp" | ||
|
||
// Includes common necessary includes for development using depthai library | ||
#include "depthai/depthai.hpp" | ||
|
||
// Optional. If set (true), the ColorCamera is downscaled from 1080p to 720p. | ||
// Otherwise (false), the aligned depth is automatically upscaled to 1080p | ||
static constexpr bool downscaleColor = true; | ||
|
||
int main() { | ||
using namespace std; | ||
|
||
dai::Pipeline p; | ||
std::vector<std::string> queueNames; | ||
|
||
auto cam = p.create<dai::node::ColorCamera>(); | ||
cam->setBoardSocket(dai::CameraBoardSocket::RGB); | ||
cam->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1080_P); | ||
if(downscaleColor) cam->setIspScale(2, 3); | ||
// For now, RGB needs fixed focus to properly align with depth. | ||
// This value was used during calibration | ||
cam->initialControl.setManualFocus(130); | ||
|
||
auto rgbOut = p.create<dai::node::XLinkOut>(); | ||
rgbOut->setStreamName("rgb"); | ||
queueNames.push_back("rgb"); | ||
cam->isp.link(rgbOut->input); | ||
|
||
auto left = p.create<dai::node::MonoCamera>(); | ||
left->setResolution(dai::MonoCameraProperties::SensorResolution::THE_720_P); | ||
left->setBoardSocket(dai::CameraBoardSocket::LEFT); | ||
|
||
auto right = p.create<dai::node::MonoCamera>(); | ||
right->setResolution(dai::MonoCameraProperties::SensorResolution::THE_720_P); | ||
right->setBoardSocket(dai::CameraBoardSocket::RIGHT); | ||
|
||
auto stereo = p.create<dai::node::StereoDepth>(); | ||
stereo->setConfidenceThreshold(200); | ||
// LR-check is required for depth alignment | ||
stereo->setLeftRightCheck(true); | ||
stereo->setDepthAlign(dai::CameraBoardSocket::RGB); | ||
left->out.link(stereo->left); | ||
right->out.link(stereo->right); | ||
|
||
auto depthOut = p.create<dai::node::XLinkOut>(); | ||
depthOut->setStreamName("depth"); | ||
queueNames.push_back("depth"); | ||
// Currently we use the 'disparity' output. TODO 'depth' | ||
stereo->disparity.link(depthOut->input); | ||
|
||
// Connect to device | ||
dai::Device d(p); | ||
d.startPipeline(); | ||
|
||
// Sets queues size and behavior | ||
for(const auto& name : queueNames) { | ||
d.getOutputQueue(name, 4, false); | ||
} | ||
|
||
std::unordered_map<std::string, cv::Mat> frame; | ||
|
||
while(1) { | ||
std::unordered_map<std::string, std::shared_ptr<dai::ImgFrame>> latestPacket; | ||
|
||
auto queueEvents = d.getQueueEvents(queueNames); | ||
for(const auto& name : queueEvents) { | ||
auto packets = d.getOutputQueue(name)->tryGetAll<dai::ImgFrame>(); | ||
auto count = packets.size(); | ||
if(count > 0) { | ||
latestPacket[name] = packets[count - 1]; | ||
} | ||
} | ||
|
||
for(const auto& name : queueNames) { | ||
if(latestPacket.find(name) != latestPacket.end()) { | ||
if(name == "depth") { | ||
frame[name] = latestPacket[name]->getFrame(); | ||
// Optional, extend range 0..95 -> 0..255, for a better visualisation | ||
if(1) frame[name].convertTo(frame[name], CV_8UC1, 255. / 95); | ||
// Optional, apply false colorization | ||
if(1) cv::applyColorMap(frame[name], frame[name], cv::COLORMAP_HOT); | ||
} else { | ||
frame[name] = latestPacket[name]->getCvFrame(); | ||
} | ||
|
||
cv::imshow(name, frame[name]); | ||
} | ||
} | ||
|
||
// Blend when both received | ||
if(frame.find("rgb") != frame.end() && frame.find("depth") != frame.end()) { | ||
// Need to have both frames in BGR format before blending | ||
if(frame["depth"].channels() < 3) { | ||
cv::cvtColor(frame["depth"], frame["depth"], cv::COLOR_GRAY2BGR); | ||
} | ||
cv::Mat blended; | ||
cv::addWeighted(frame["rgb"], 0.6, frame["depth"], 0.4, 0, blended); | ||
cv::imshow("rgb-depth", blended); | ||
frame.clear(); | ||
} | ||
|
||
int key = cv::waitKey(1); | ||
if(key == 'q') { | ||
return 0; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,7 +118,7 @@ cv::Mat ImgFrame::getCvFrame() { | |
} break; | ||
|
||
case Type::YUV420p: | ||
cv::cvtColor(frame, output, cv::ColorConversionCodes::COLOR_YUV420p2BGR); | ||
cv::cvtColor(frame, output, cv::ColorConversionCodes::COLOR_YUV2BGR_IYUV); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. About the change here, it may look counter-intuitive, but the YUV420p frames we receive from device (currently from In OpenCV the format While |
||
break; | ||
|
||
case Type::NV12: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use
CameraBoardSocket
to which to align to, instead of newDepthAlign
enum?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently
DepthAlign
contains:{ AUTO = -1, RIGHT, LEFT, CENTER, RGB }
RIGHT
(default) andLEFT
would map to the respective 'rectified' streams.CENTER
would just displace the disparity map (each pixel by its disparity_value / 2), whileRGB
(not yet implemented, defaulting with a warning to CENTER) would need an additional 'warp' step (extrinsics alignment / resolution scaling). For boards like BW1092 where the RGB camera is not centered, the disparity displacement would need to be done by a different amount.Mapping to the real LEFT and RIGHT (from
CameraBoardSocket
), we would need to do and inverse homography transform, which should be easily doable with the mechanism for RGB in place. But it incurs the additional warp overhead, and in many cases it's not needed (as could be used directly with the rectified streams).Should then we extend
DepthAlign
as:{ AUTO = -1, RECTIFIED_RIGHT, RECTIFIED_LEFT, CENTER, RGB, LEFT, RIGHT }
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about if we separate this two to:
I think we'd cover all cases with this, without too much repetition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely adding a separate config with
CameraBoardSocket
is good, I will add it.But one case where combining these two might be confusing is with e.g. CameraBoardSocket::RGB and mode: RECTIFIED.
Or with images from host, or running Stereo on OAK-1: one input from the RGB camera, the other from host (actually forwarded by another OAK-1), as one customer wants to use. Actually I think here we should have a way to pass a minimal StereoCalibration structure to StereoDepth, that won't refer to CameraBoardSocket entries, but simply to the 'left' and 'right' inputs to the node. We can discuss further on luxonis/depthai-shared#19
So I was thinking to reference based on the I/Os of the StereoDepth node. Something like
DepthAlign: RECTIFIED_RIGHT, RECTIFIED_LEFT, RECTIFIED_CENTER, INPUT_LEFT, INPUT_RIGHT, OTHER_CAMERA
.When
OTHER_CAMERA
is set, will specify it byCameraBoardSocket
. And for example running standard stereo on OAK-D,INPUT_RIGHT
would be equivalent toOTHER_CAMERA
+CameraBoardSocket::RIGHT
.Thoughts on that? Also how to have the API, two separate functions, one with two parameters, or an overloaded function (pass either DepthAlign or CameraBoardSocket, and then remove
OTHER_CAMERA
)?CC @saching13
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What martin suggested works best if we ignore the concept that left, and right are our two global shutter cameras.
In stereo node we set left camera and right camera using
CameraBoardSocket
and we haveIn DepthAlign mode let's have only RECTIFIED_LEFT, RECTIFIED_RIGHT and CENTER.
And
setAlignment(cameraBoardSocket)
will make sure to align to any camera's that we have.And another overload function or something like
setExtendedAlignment
takesDepthAlign
mode. But in this caseRECTIFIED_LEFT
will be therectified_left
frame of the camera/image passed to the stereo left. andRECTIFIED_RIGHT
will be therectified_right
frame of the camera/image passed as the right image to the stereo node.One such case is where rgb camera can be stereo left and right camera can be stereo right.
And CENTER would not be the center of the device but the center of the stereo baseline.
Thoughts ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any resolution here? I think I've understood, and a lot of these are sounding good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed as an overloaded function, as the options (
DepthAlign
andCameraBoardSocket
) cannot be combined, and would be confusing to set both.