Skip to content

Commit

Permalink
Merge pull request luxonis#38 from luxonis/gen2_imagemanip_rotate
Browse files Browse the repository at this point in the history
ImageManip: rotated/arbitrary cropping, add camera controls
  • Loading branch information
alex-luxonis committed Feb 12, 2021
2 parents 1ca39ac + 58033d6 commit ad670be
Show file tree
Hide file tree
Showing 18 changed files with 532 additions and 52 deletions.
2 changes: 1 addition & 1 deletion cmake/Depthai/DepthaiDeviceSideConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot")

# "full commit hash of device side binary"
set(DEPTHAI_DEVICE_SIDE_COMMIT "4f9c559d826a1238f91f5b72c5178c4a3be76c2b")
set(DEPTHAI_DEVICE_SIDE_COMMIT "d49ac887074adf18024c21e33ce071ea48b0c014")

# "version if applicable"
set(DEPTHAI_DEVICE_SIDE_VERSION "")
3 changes: 2 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ dai_add_example(mjpeg_encoding src/mjpeg_encoding_example.cpp)
# StereoDepth example
dai_add_example(stereo src/stereo_example.cpp)

# Image Manip node example
# Image Manip node examples
dai_add_example(image_manip src/image_manip_example.cpp)
dai_add_example(image_manip_warp src/image_manip_warp_example.cpp)

# Color Camera config example
dai_add_example(color_camera_control src/color_camera_control_example.cpp)
Expand Down
108 changes: 83 additions & 25 deletions examples/src/color_camera_control_example.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// This example shows usage of Camera Control message as well as ColorCamera configInput to change crop x and y

/**
* This example shows usage of Camera Control message as well as ColorCamera configInput to change crop x and y
* Uses 'WASD' controls to move the crop window, 'C' to capture a still image, 'T' to trigger autofocus, 'IOKL,.'
* for manual exposure/focus:
* Control: key[dec/inc] min..max
* exposure time: I O 1..33000 [us]
* sensitivity iso: K L 100..1600
* focus: , . 0..255 [far..near]
* To go back to auto controls:
* 'E' - autoexposure
* 'F' - autofocus (continuous)
*/
#include <iostream>
#include <cstdio>

Expand All @@ -12,6 +22,15 @@
// Step size ('W','A','S','D' controls)
static constexpr int STEP_SIZE = 16;

// Manual exposure/focus set step
static constexpr int EXP_STEP = 500; // us
static constexpr int ISO_STEP = 50;
static constexpr int LENS_STEP = 3;

static int clamp(int num, int v0, int v1) {
return std::max(v0, std::min(num, v1));
}

int main(){

dai::Pipeline pipeline;
Expand Down Expand Up @@ -67,8 +86,18 @@ int main(){
float crop_x = 0;
float crop_y = 0;

float prev_crop_x = crop_x;
float prev_crop_y = crop_y;
// Defaults and limits for manual focus/exposure controls
int lens_pos = 150;
int lens_min = 0;
int lens_max = 255;

int exp_time = 20000;
int exp_min = 1;
int exp_max = 33000;

int sens_iso = 800;
int sens_min = 100;
int sens_max = 1600;

while(true){

Expand Down Expand Up @@ -105,35 +134,64 @@ int main(){
dai::CameraControl ctrl;
ctrl.setCaptureStill(true);
controlQueue->send(ctrl);
} else if(key == 'a'){
crop_x -= (max_crop_x / colorCam->getResolutionWidth()) * STEP_SIZE;
if (crop_x < 0) crop_x = max_crop_x;
} else if(key == 'd'){
crop_x += (max_crop_x / colorCam->getResolutionWidth()) * STEP_SIZE;
if (crop_x > max_crop_x) crop_x = 0.0f;
} else if(key == 'w'){
crop_y -= (max_crop_y / colorCam->getResolutionHeight()) * STEP_SIZE;
if (crop_y < 0) crop_y = max_crop_y;
} else if(key == 's'){
crop_y += (max_crop_y / colorCam->getResolutionHeight()) * STEP_SIZE;
if (crop_y > max_crop_y) crop_y = 0.0f;
}

} else if (key == 't') {
printf("Autofocus trigger (and disable continuous)\n");
dai::CameraControl ctrl;
ctrl.setAutoFocusMode(dai::CameraControl::AutoFocusMode::AUTO);
ctrl.setAutoFocusTrigger();
controlQueue->send(ctrl);
} else if (key == 'f') {
printf("Autofocus enable, continuous\n");
dai::CameraControl ctrl;
ctrl.setAutoFocusMode(dai::CameraControl::AutoFocusMode::CONTINUOUS_VIDEO);
controlQueue->send(ctrl);
} else if (key == 'e') {
printf("Autoexposure enable\n");
dai::CameraControl ctrl;
ctrl.setAutoExposureEnable();
controlQueue->send(ctrl);
} else if (key == ',' || key == '.') {
if (key == ',') lens_pos -= LENS_STEP;
if (key == '.') lens_pos += LENS_STEP;
lens_pos = clamp(lens_pos, lens_min, lens_max);
printf("Setting manual focus, lens position: %d\n", lens_pos);
dai::CameraControl ctrl;
ctrl.setManualFocus(lens_pos);
controlQueue->send(ctrl);
} else if (key == 'i' || key == 'o' || key == 'k' || key == 'l') {
if (key == 'i') exp_time -= EXP_STEP;
if (key == 'o') exp_time += EXP_STEP;
if (key == 'k') sens_iso -= ISO_STEP;
if (key == 'l') sens_iso += ISO_STEP;
exp_time = clamp(exp_time, exp_min, exp_max);
sens_iso = clamp(sens_iso, sens_min, sens_max);
printf("Setting manual exposure, time %d us, iso %d\n", exp_time, sens_iso);
dai::CameraControl ctrl;
ctrl.setManualExposure(exp_time, sens_iso);
controlQueue->send(ctrl);
} else if (key == 'w' || key == 'a' || key == 's' || key == 'd') {
if(key == 'a'){
crop_x -= (max_crop_x / colorCam->getResolutionWidth()) * STEP_SIZE;
if (crop_x < 0) crop_x = max_crop_x;
} else if(key == 'd'){
crop_x += (max_crop_x / colorCam->getResolutionWidth()) * STEP_SIZE;
if (crop_x > max_crop_x) crop_x = 0.0f;
} else if(key == 'w'){
crop_y -= (max_crop_y / colorCam->getResolutionHeight()) * STEP_SIZE;
if (crop_y < 0) crop_y = max_crop_y;
} else if(key == 's'){
crop_y += (max_crop_y / colorCam->getResolutionHeight()) * STEP_SIZE;
if (crop_y > max_crop_y) crop_y = 0.0f;
}

// if crop changed
if(prev_crop_x != crop_x || prev_crop_y != crop_y){
// Send new cfg to camera
dai::ImageManipConfig cfg;
cfg.setCropRect(crop_x, crop_y, 0, 0);
configQueue->send(cfg);
printf("Sending new crop - x: %f, y: %f\n", crop_x, crop_y);
}

// set prev crop
prev_crop_x = crop_x;
prev_crop_y = crop_y;

}


}
}
8 changes: 4 additions & 4 deletions examples/src/image_manip_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ int main(){
colorCam->setColorOrder(dai::ColorCameraProperties::ColorOrder::BGR);

// Create a center crop image manipulation
imageManip->setCenterCrop(0.7f);
imageManip->setResizeThumbnail(300, 400);
imageManip->initialConfig.setCenterCrop(0.7f);
imageManip->initialConfig.setResizeThumbnail(300, 400);

// Second image manipulator - Create a off center crop
imageManip2->setCropRect(0.1, 0.1, 0.3, 0.3);
imageManip2->initialConfig.setCropRect(0.1, 0.1, 0.3, 0.3);
imageManip2->setWaitForConfigInput(true);


Expand Down Expand Up @@ -108,4 +108,4 @@ int main(){

}

}
}
172 changes: 172 additions & 0 deletions examples/src/image_manip_warp_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#include <iostream>
#include <cstdio>

#include "utility.hpp"

#include "depthai/depthai.hpp"

struct warpFourPointTest {
std::vector<dai::Point2f> points;
bool normalizedCoords;
const char *description;
};

#define ROTATE_RATE_MAX 5.0
#define ROTATE_RATE_INC 0.1
#define KEY_ROTATE_DECR 'z'
#define KEY_ROTATE_INCR 'x'

#define RESIZE_MAX_W 800
#define RESIZE_MAX_H 600
#define RESIZE_FACTOR_MAX 5
#define KEY_RESIZE_INC 'v'

/* The crop points are specified in clockwise order,
* with first point mapped to output top-left, as:
* P0 -> P1
* ^ v
* P3 <- P2
*/
#define P0 {0, 0} // top-left
#define P1 {1, 0} // top-right
#define P2 {1, 1} // bottom-right
#define P3 {0, 1} // bottom-left
std::vector<warpFourPointTest> warpList = {
//{{{ 0, 0},{ 1, 0},{ 1, 1},{ 0, 1}}, true, "passthrough"},
//{{{ 0, 0},{639, 0},{639,479},{ 0,479}}, false,"passthrough (pixels)"},
{{P0, P1, P2, P3}, true, "1.passthrough"},
{{P3, P0, P1, P2}, true, "2.rotate 90"},
{{P2, P3, P0, P1}, true, "3.rotate 180"},
{{P1, P2, P3, P0}, true, "4.rotate 270"},
{{P1, P0, P3, P2}, true, "5.horizontal mirror"},
{{P3, P2, P1, P0}, true, "6.vertical flip"},
{{{-0.1,-0.1},{1.1,-0.1},{1.1,1.1},{-0.1,1.1}}, true, "7.add black borders"},
{{{-0.3, 0},{1, 0},{1.3, 1},{0, 1}}, true, "8.parallelogram transform"},
{{{-0.2, 0},{1.8, 0},{1, 1},{0, 1}}, true, "9.trapezoid transform"},
};
#define KEY_WARP_TEST_CYCLE 'c'

void printControls() {
printf("\n=== Controls:\n");
printf(" %c -rotated rectangle crop, decrease rate\n", KEY_ROTATE_DECR);
printf(" %c -rotated rectangle crop, increase rate\n", KEY_ROTATE_INCR);
printf(" %c -warp 4-point transform, cycle through modes\n", KEY_WARP_TEST_CYCLE);
printf(" %c -resize cropped region, or disable resize\n", KEY_RESIZE_INC);
printf(" h -print controls (help)\n");
}

int main(){
dai::Pipeline pipeline;

auto colorCam = pipeline.create<dai::node::ColorCamera>();
auto camOut = pipeline.create<dai::node::XLinkOut>();
auto manip = pipeline.create<dai::node::ImageManip>();
auto manipCfg = pipeline.create<dai::node::XLinkIn>();
auto manipOut = pipeline.create<dai::node::XLinkOut>();

camOut->setStreamName("preview");
manipOut->setStreamName("manip");
manipCfg->setStreamName("manipCfg");

colorCam->setPreviewSize(640, 480);
colorCam->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1080_P);
colorCam->setInterleaved(false);
colorCam->setColorOrder(dai::ColorCameraProperties::ColorOrder::BGR);
manip->setMaxOutputFrameSize(2000*1500*3);

/* Link nodes: CAM --> XLINK(preview)
* \--> manip -> XLINK(manipOut)
* manipCfg ---/
*/
colorCam->preview.link(camOut->input);
colorCam->preview.link(manip->inputImage);
manip->out.link(manipOut->input);
manipCfg->out.link(manip->inputConfig);

// Connect to device and start pipeline
dai::Device device(pipeline);
device.startPipeline();

// Create input & output queues
auto previewQueue = device.getOutputQueue("preview", 8, false);
auto manipQueue = device.getOutputQueue("manip", 8, false);
auto manipInQueue = device.getInputQueue("manipCfg");

std::vector<decltype(previewQueue)> frameQueues {previewQueue, manipQueue};

// keep processing data
int key = -1;
float angleDeg = 0;
float rotateRate = 1.0;
int resizeFactor = 0, resizeX, resizeY;
bool testFourPt = false;
int warpIdx = -1;

printControls();

while (key != 'q') {
if (key >= 0) {
printf("Pressed: %c | ", key);
if (key == KEY_ROTATE_DECR || key == KEY_ROTATE_INCR) {
if (key == KEY_ROTATE_DECR) {
if (rotateRate > -ROTATE_RATE_MAX)
rotateRate -= ROTATE_RATE_INC;
} else if (key == KEY_ROTATE_INCR) {
if (rotateRate < ROTATE_RATE_MAX)
rotateRate += ROTATE_RATE_INC;
}
testFourPt = false;
printf("Crop rotated rectangle, rate: %g degrees", rotateRate);
} else if (key == KEY_RESIZE_INC) {
resizeFactor++;
if (resizeFactor > RESIZE_FACTOR_MAX) {
resizeFactor = 0;
printf("Crop region not resized");
} else {
resizeX = RESIZE_MAX_W / resizeFactor;
resizeY = RESIZE_MAX_H / resizeFactor;
printf("Crop region resized to: %d x %d", resizeX, resizeY);
}
} else if (key == KEY_WARP_TEST_CYCLE) {
resizeFactor = 0; // Disable resizing initially
warpIdx = (warpIdx + 1) % warpList.size();
printf("Warp 4-point transform, %s", warpList[warpIdx].description);
testFourPt = true;
} else if (key == 'h') {
printControls();
}
printf("\n");
}

// Send an updated config with continuous rotate, or after a key press
if (key >= 0 || (!testFourPt && std::abs(rotateRate) > 0.0001)) {
dai::ImageManipConfig cfg;
if (testFourPt) {
cfg.setWarpTransformFourPoints(warpList[warpIdx].points,
warpList[warpIdx].normalizedCoords);
} else {
angleDeg += rotateRate;
dai::RotatedRect rr = {
{320, 240}, // center
{640, 480}, //{400, 400}, // size
angleDeg
};
bool normalized = false;
cfg.setCropRotatedRect(rr, normalized);
}
if (resizeFactor > 0) {
cfg.setResize(resizeX, resizeY);
}
//cfg.setWarpBorderFillColor(255, 0, 0);
//cfg.setWarpBorderReplicatePixels();
manipInQueue->send(cfg);
}

for (const auto& q : frameQueues) {
auto img = q->get<dai::ImgFrame>();
auto mat = toMat(img->getData(), img->getWidth(), img->getHeight(), 3, 1);
cv::imshow(q->getName(), mat);
}
key = cv::waitKey(1);
}
}

0 comments on commit ad670be

Please sign in to comment.