DepthAI v2 has been superseded by DepthAI v3. You are viewing legacy documentation.
DepthAI Tutorials
DepthAI API References

ON THIS PAGE

  • Setup
  • Demo
  • Source code
  • Pipeline

RGB Rotate Warp

This example shows usage of ImageManip to crop a rotated rectangle area on a frame, or perform various image transforms: rotate, mirror, flip, perspective transform.

Setup

Please run the install script to download all required dependencies. Please note that this script must be ran from git context, so you have to download the depthai-python repository first and then run the script
Command Line
1git clone https://github.com/luxonis/depthai-python.git
2cd depthai-python/examples
3python3 install_requirements.py
For additional information, please follow the installation guide.

Demo

https://user-images.githubusercontent.com/18037362/152208899-461fa163-42ec-4922-84b5-5cd09332ea32.png
Command Line
1Controls:
2z -rotated rectangle crop, decrease rate
3x -rotated rectangle crop, increase rate
4c -warp 4-point transform, cycle through modes
5v -resize cropped region, or disable resize
6h -print controls (help)

Source code

Python

Python
GitHub
1#!/usr/bin/env python3
2
3"""
4This example shows usage of ImageManip to crop a rotated rectangle area on a frame,
5or perform various image transforms: rotate, mirror, flip, perspective transform.
6"""
7
8import depthai as dai
9import cv2
10import numpy as np
11
12keyRotateDecr = 'z'
13keyRotateIncr = 'x'
14keyResizeInc = 'v'
15keyWarpTestCycle = 'c'
16
17def printControls():
18    print("=== Controls:")
19    print(keyRotateDecr, "-rotated rectangle crop, decrease rate")
20    print(keyRotateIncr, "-rotated rectangle crop, increase rate")
21    print(keyWarpTestCycle, "-warp 4-point transform, cycle through modes")
22    print(keyResizeInc, "-resize cropped region, or disable resize")
23    print("h -print controls (help)")
24
25rotateRateMax = 5.0
26rotateRateInc = 0.1
27
28resizeMaxW = 800
29resizeMaxH = 600
30resizeFactorMax = 5
31
32'''
33The crop points are specified in clockwise order,
34with first point mapped to output top-left, as:
35    P0  ->  P1
36     ^       v
37    P3  <-  P2
38'''
39P0 = [0, 0]  # top-left
40P1 = [1, 0]  # top-right
41P2 = [1, 1]  # bottom-right
42P3 = [0, 1]  # bottom-left
43
44warpList = [
45    # points order, normalized cordinates, description
46    # [[[0, 0], [1, 0], [1, 1], [0, 1]], True, "passthrough"],
47    # [[[0, 0], [639, 0], [639, 479], [0, 479]], False, "passthrough (pixels)"],
48    [[P0, P1, P2, P3], True, "1. passthrough"],
49    [[P3, P0, P1, P2], True, "2. rotate 90"],
50    [[P2, P3, P0, P1], True, "3. rotate 180"],
51    [[P1, P2, P3, P0], True, "4. rotate 270"],
52    [[P1, P0, P3, P2], True, "5. horizontal mirror"],
53    [[P3, P2, P1, P0], True, "6. vertical flip"],
54    [[[-0.1, -0.1], [1.1, -0.1], [1.1, 1.1], [-0.1, 1.1]], True, "7. add black borders"],
55    [[[-0.3, 0], [1, 0], [1.3, 1], [0, 1]], True, "8. parallelogram transform"],
56    [[[-0.2, 0], [1.8, 0], [1, 1], [0, 1]], True, "9. trapezoid transform"],
57]
58
59# Create pipeline
60pipeline = dai.Pipeline()
61
62# Define sources and outputs
63camRgb = pipeline.create(dai.node.ColorCamera)
64manip = pipeline.create(dai.node.ImageManip)
65
66camOut = pipeline.create(dai.node.XLinkOut)
67manipOut = pipeline.create(dai.node.XLinkOut)
68manipCfg = pipeline.create(dai.node.XLinkIn)
69
70camOut.setStreamName("preview")
71manipOut.setStreamName("manip")
72manipCfg.setStreamName("manipCfg")
73
74# Properties
75camRgb.setPreviewSize(640, 480)
76camRgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
77camRgb.setInterleaved(False)
78camRgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
79manip.setMaxOutputFrameSize(2000 * 1500 * 3)
80
81# Linking
82camRgb.preview.link(camOut.input)
83camRgb.preview.link(manip.inputImage)
84manip.out.link(manipOut.input)
85manipCfg.out.link(manip.inputConfig)
86
87# Connect to device and start pipeline
88with dai.Device(pipeline) as device:
89
90    # Create input & output queues
91    qPreview = device.getOutputQueue(name="preview", maxSize=4)
92    qManip = device.getOutputQueue(name="manip", maxSize=4)
93    qManipCfg = device.getInputQueue(name="manipCfg")
94
95    key = -1
96    angleDeg = 0
97    rotateRate = 1.0
98    resizeFactor = 0
99    resizeX = 0
100    resizeY = 0
101    testFourPt = False
102    warpIdx = -1
103
104    printControls()
105
106    while key != ord('q'):
107        if key > 0:
108            print("Pressed: ", key)
109            if key == ord(keyRotateDecr) or key == ord(keyRotateIncr):
110                if key == ord(keyRotateDecr):
111                    if rotateRate > -rotateRateMax:
112                        rotateRate -= rotateRateInc
113                if key == ord(keyRotateIncr):
114                    if rotateRate < rotateRateMax:
115                        rotateRate += rotateRateInc
116                testFourPt = False
117                print("Crop rotated rectangle, rate per frame: {:.1f} degrees".format(rotateRate))
118            elif key == ord(keyResizeInc):
119                resizeFactor += 1
120                if resizeFactor > resizeFactorMax:
121                    resizeFactor = 0
122                    print("Crop region not resized")
123                else:
124                    resizeX = resizeMaxW // resizeFactor
125                    resizeY = resizeMaxH // resizeFactor
126                    print("Crop region resized to: ", resizeX, 'x', resizeY)
127            elif key == ord(keyWarpTestCycle):
128                # Disable resizing initially
129                resizeFactor = 0
130                warpIdx = (warpIdx + 1) % len(warpList)
131                testFourPt = True
132                testDescription = warpList[warpIdx][2]
133                print("Warp 4-point transform: ", testDescription)
134            elif key == ord('h'):
135                printControls()
136
137        # Send an updated config with continuous rotate, or after a key press
138        if key >= 0 or (not testFourPt and abs(rotateRate) > 0.0001):
139            cfg = dai.ImageManipConfig()
140            if testFourPt:
141                test = warpList[warpIdx]
142                points, normalized = test[0], test[1]
143                point2fList = []
144                for p in points:
145                    pt = dai.Point2f()
146                    pt.x, pt.y = p[0], p[1]
147                    point2fList.append(pt)
148                cfg.setWarpTransformFourPoints(point2fList, normalized)
149            else:
150                angleDeg += rotateRate
151                rotatedRect = ((320, 240), (400, 400), angleDeg)
152                rr = dai.RotatedRect()
153                rr.center.x, rr.center.y = rotatedRect[0]
154                rr.size.width, rr.size.height = rotatedRect[1]
155                rr.angle = rotatedRect[2]
156                cfg.setCropRotatedRect(rr, False)
157            if resizeFactor > 0:
158                cfg.setResize(resizeX, resizeY)
159            # cfg.setWarpBorderFillColor(255, 0, 0)
160            # cfg.setWarpBorderReplicatePixels()
161            qManipCfg.send(cfg)
162
163        for q in [qPreview, qManip]:
164            pkt = q.get()
165            name = q.getName()
166            shape = (3, pkt.getHeight(), pkt.getWidth())
167            frame = pkt.getCvFrame()
168            if name == "preview" and not testFourPt:
169                # Draw RotatedRect cropped area on input frame
170                points = np.int0(cv2.boxPoints(rotatedRect))
171                cv2.drawContours(frame, [points], 0, (255, 0, 0), 1)
172                # Mark top-left corner
173                cv2.circle(frame, tuple(points[1]), 10, (255, 0, 0), 2)
174            cv2.imshow(name, frame)
175        key = cv2.waitKey(1)

C++

1#include <iostream>
2
3#include "depthai/depthai.hpp"
4#include "utility.hpp"
5
6static constexpr auto keyRotateDecr = 'z';
7static constexpr auto keyRotateIncr = 'x';
8static constexpr auto keyResizeInc = 'v';
9static constexpr auto keyWarpTestCycle = 'c';
10
11void printControls() {
12    printf("\n=== Controls:\n");
13    printf(" %c -rotated rectangle crop, decrease rate\n", keyRotateDecr);
14    printf(" %c -rotated rectangle crop, increase rate\n", keyRotateIncr);
15    printf(" %c -warp 4-point transform, cycle through modes\n", keyWarpTestCycle);
16    printf(" %c -resize cropped region, or disable resize\n", keyResizeInc);
17    printf(" h -print controls (help)\n");
18}
19
20static constexpr auto ROTATE_RATE_MAX = 5.0f;
21static constexpr auto ROTATE_RATE_INC = 0.1f;
22
23static constexpr auto RESIZE_MAX_W = 800;
24static constexpr auto RESIZE_MAX_H = 600;
25static constexpr auto RESIZE_FACTOR_MAX = 5;
26
27/* The crop points are specified in clockwise order,
28 * with first point mapped to output top-left, as:
29 *   P0  ->  P1
30 *    ^       v
31 *   P3  <-  P2
32 */
33static const dai::Point2f P0 = {0, 0};  // top-left
34static const dai::Point2f P1 = {1, 0};  // top-right
35static const dai::Point2f P2 = {1, 1};  // bottom-right
36static const dai::Point2f P3 = {0, 1};  // bottom-left
37struct warpFourPointTest {
38    std::vector<dai::Point2f> points;
39    bool normalizedCoords;
40    const char* description;
41};
42
43std::vector<warpFourPointTest> warpList = {
44    //{{{  0,  0},{  1,  0},{  1,  1},{  0,  1}}, true, "passthrough"},
45    //{{{  0,  0},{639,  0},{639,479},{  0,479}}, false,"passthrough (pixels)"},
46    {{P0, P1, P2, P3}, true, "1. passthrough"},
47    {{P3, P0, P1, P2}, true, "2. rotate 90"},
48    {{P2, P3, P0, P1}, true, "3. rotate 180"},
49    {{P1, P2, P3, P0}, true, "4. rotate 270"},
50    {{P1, P0, P3, P2}, true, "5. horizontal mirror"},
51    {{P3, P2, P1, P0}, true, "6. vertical flip"},
52    {{{-0.1f, -0.1f}, {1.1f, -0.1f}, {1.1f, 1.1f}, {-0.1f, 1.1f}}, true, "7. add black borders"},
53    {{{-0.3f, 0}, {1, 0}, {1.3f, 1}, {0, 1}}, true, "8. parallelogram transform"},
54    {{{-0.2f, 0}, {1.8f, 0}, {1, 1}, {0, 1}}, true, "9. trapezoid transform"},
55};
56
57int main() {
58    // Create pipeline
59    dai::Pipeline pipeline;
60
61    // Define sources and outputs
62    auto camRgb = pipeline.create<dai::node::ColorCamera>();
63    auto manip = pipeline.create<dai::node::ImageManip>();
64
65    auto camOut = pipeline.create<dai::node::XLinkOut>();
66    auto manipOut = pipeline.create<dai::node::XLinkOut>();
67    auto manipCfg = pipeline.create<dai::node::XLinkIn>();
68
69    camOut->setStreamName("preview");
70    manipOut->setStreamName("manip");
71    manipCfg->setStreamName("manipCfg");
72
73    // Properties
74    camRgb->setPreviewSize(640, 480);
75    camRgb->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1080_P);
76    camRgb->setInterleaved(false);
77    camRgb->setColorOrder(dai::ColorCameraProperties::ColorOrder::BGR);
78    manip->setMaxOutputFrameSize(2000 * 1500 * 3);
79
80    // Linking
81    camRgb->preview.link(camOut->input);
82    camRgb->preview.link(manip->inputImage);
83    manip->out.link(manipOut->input);
84    manipCfg->out.link(manip->inputConfig);
85
86    // Connect to device and start pipeline
87    dai::Device device(pipeline);
88
89    // Create input & output queues
90    auto qPreview = device.getOutputQueue("preview", 8, false);
91    auto qManip = device.getOutputQueue("manip", 8, false);
92    auto qManipCfg = device.getInputQueue("manipCfg");
93
94    std::vector<decltype(qPreview)> frameQueues{qPreview, qManip};
95
96    // keep processing data
97    int key = -1;
98    float angleDeg = 0;
99    float rotateRate = 1.0;
100    int resizeFactor = 0;
101    int resizeX = 0;
102    int resizeY = 0;
103    bool testFourPt = false;
104    int warpIdx = -1;
105
106    printControls();
107
108    while(key != 'q') {
109        if(key >= 0) {
110            printf("Pressed: %c | ", key);
111            if(key == keyRotateDecr || key == keyRotateIncr) {
112                if(key == keyRotateDecr) {
113                    if(rotateRate > -ROTATE_RATE_MAX) rotateRate -= ROTATE_RATE_INC;
114                } else if(key == keyRotateIncr) {
115                    if(rotateRate < ROTATE_RATE_MAX) rotateRate += ROTATE_RATE_INC;
116                }
117                testFourPt = false;
118                printf("Crop rotated rectangle, rate: %g degrees", rotateRate);
119            } else if(key == keyResizeInc) {
120                resizeFactor++;
121                if(resizeFactor > RESIZE_FACTOR_MAX) {
122                    resizeFactor = 0;
123                    printf("Crop region not resized");
124                } else {
125                    resizeX = RESIZE_MAX_W / resizeFactor;
126                    resizeY = RESIZE_MAX_H / resizeFactor;
127                    printf("Crop region resized to: %d x %d", resizeX, resizeY);
128                }
129            } else if(key == keyWarpTestCycle) {
130                resizeFactor = 0;  // Disable resizing initially
131                warpIdx = (warpIdx + 1) % warpList.size();
132                printf("Warp 4-point transform: %s", warpList[warpIdx].description);
133                testFourPt = true;
134            } else if(key == 'h') {
135                printControls();
136            }
137            printf("\n");
138        }
139
140        // Send an updated config with continuous rotate, or after a key press
141        if(key >= 0 || (!testFourPt && std::abs(rotateRate) > 0.0001)) {
142            dai::ImageManipConfig cfg;
143            if(testFourPt) {
144                cfg.setWarpTransformFourPoints(warpList[warpIdx].points, warpList[warpIdx].normalizedCoords);
145            } else {
146                angleDeg += rotateRate;
147                dai::RotatedRect rr = {{320, 240},  // center
148                                       {640, 480},  //{400, 400}, // size
149                                       angleDeg};
150                cfg.setCropRotatedRect(rr, false);
151            }
152            if(resizeFactor > 0) {
153                cfg.setResize(resizeX, resizeY);
154            }
155            // cfg.setWarpBorderFillColor(255, 0, 0);
156            // cfg.setWarpBorderReplicatePixels();
157            qManipCfg->send(cfg);
158        }
159
160        for(const auto& q : frameQueues) {
161            auto img = q->get<dai::ImgFrame>();
162            auto mat = toMat(img->getData(), img->getWidth(), img->getHeight(), 3, 1);
163            cv::imshow(q->getName(), mat);
164        }
165        key = cv::waitKey(1);
166    }
167    return 0;
168}

Pipeline

Need assistance?

Head over to Discussion Forum for technical support or any other questions you might have.