# Camera ROI-based exposure and focus

This example lets user select a region of interest (ROI) on the frame (on the host computer), and after selection is complete, it
will set the auto-exposure (AE) and auto-focus (AF) of the camera to the selected ROI.

Similar application could be to set AF/AE based on object detection results, so the camera would automatically refocus and adjust
exposure when a specific object is detected.

> **OAK4 OS version**
> If you're using OAK4 cameras, make sure to use the latest version of Luxonis OS (1.6 or newer), otherwise ROI won't be mapped correctly.

## Demo

This example requires the DepthAI v3 API, see [installation instructions](https://docs.luxonis.com/software-v3/depthai.md).

## Pipeline

## Source code

#### Python

```python
#!/usr/bin/env python3

import cv2
import depthai as dai

# Create pipeline
with dai.Pipeline() as pipeline:
    # Define source and output
    cam = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A)
    cam_input_q = cam.inputControl.createInputQueue()
    stream_q = cam.requestOutput((1920, 1080)).createOutputQueue()

    cam_q_in = cam.inputControl.createInputQueue()

    # Connect to device and start pipeline
    pipeline.start()

    # ROI selection variables
    start_points = []
    roi_rect = None
    scale_factors = None
    # Mouse callback function for ROI selection
    def select_roi(event, x, y, flags, param):
        global start_points, roi_rect
        def set_roi_rect():
            global roi_rect
            x1, y1 = start_points
            x2, y2 = (x, y)
            roi_rect = (min(x1, x2), min(y1, y2), abs(x2-x1), abs(y2-y1))

        if event == cv2.EVENT_LBUTTONDOWN:
            roi_rect = None
            start_points = (x, y)
        elif event == cv2.EVENT_MOUSEMOVE and start_points:
            set_roi_rect()
        elif event == cv2.EVENT_LBUTTONUP and start_points:
            set_roi_rect()
            roi_rect_scaled = (
                int(roi_rect[0] * scale_factors[0]),
                int(roi_rect[1] * scale_factors[1]),
                int(roi_rect[2] * scale_factors[0]),
                int(roi_rect[3] * scale_factors[1])
            )
            print(f"ROI selected: {roi_rect}")
            ctrl = dai.CameraControl()
            print(f"Scaled ROI selected: {roi_rect_scaled}. Setting exposure and focus to this region.")
            ctrl.setAutoExposureRegion(*roi_rect_scaled)
            ctrl.setAutoFocusRegion(*roi_rect_scaled)
            cam_q_in.send(ctrl)
            start_points = None

    # Create a window and set the mouse callback
    cv2.namedWindow("video")
    cv2.setMouseCallback("video", select_roi)

    while pipeline.isRunning():
        img_hd: dai.ImgFrame = stream_q.get()
        if scale_factors is None:
            print(img_hd.getTransformation().getSourceSize(), img_hd.getTransformation().getSize())
            scale_factors = (img_hd.getTransformation().getSourceSize()[0] / img_hd.getTransformation().getSize()[0],
                            img_hd.getTransformation().getSourceSize()[1] / img_hd.getTransformation().getSize()[1])
        frame = img_hd.getCvFrame()

        # Draw the ROI rectangle if it exists
        if roi_rect is not None:
            x, y, w, h = roi_rect
            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

        cv2.imshow("video", frame)

        key = cv2.waitKey(1)
        if key == ord("q"):
            break
```

#### C++

```cpp
#include <atomic>
#include <csignal>
#include <iostream>
#include <memory>
#include <opencv2/opencv.hpp>
#include <tuple>

#include "depthai/depthai.hpp"

std::atomic<bool> quitEvent(false);

void signalHandler(int) {
    quitEvent = true;
}

// Global variables for ROI selection
std::vector<cv::Point> startPoints;
cv::Rect roiRect;
std::pair<float, float> scaleFactors;

// Mouse callback function for ROI selection
void selectRoi(int event, int x, int y, int flags, void* userdata) {
    auto* camQIn = static_cast<std::shared_ptr<dai::InputQueue>*>(userdata);

    if(event == cv::EVENT_LBUTTONDOWN) {
        roiRect = cv::Rect();
        startPoints.clear();
        startPoints.push_back(cv::Point(x, y));
    } else if(event == cv::EVENT_MOUSEMOVE && !startPoints.empty()) {
        cv::Point start = startPoints[0];
        roiRect = cv::Rect(std::min(start.x, x), std::min(start.y, y), std::abs(x - start.x), std::abs(y - start.y));
    } else if(event == cv::EVENT_LBUTTONUP && !startPoints.empty()) {
        cv::Point start = startPoints[0];
        roiRect = cv::Rect(std::min(start.x, x), std::min(start.y, y), std::abs(x - start.x), std::abs(y - start.y));

        // Scale ROI to original resolution
        cv::Rect roiRectScaled(static_cast<int>(roiRect.x * scaleFactors.first),
                               static_cast<int>(roiRect.y * scaleFactors.second),
                               static_cast<int>(roiRect.width * scaleFactors.first),
                               static_cast<int>(roiRect.height * scaleFactors.second));

        std::cout << "ROI selected: " << roiRect << std::endl;
        std::cout << "Scaled ROI selected: " << roiRectScaled << ". Setting exposure and focus to this region." << std::endl;

        // Create and send control message
        auto ctrl = std::make_shared<dai::CameraControl>();
        ctrl->setAutoExposureRegion(roiRectScaled.x, roiRectScaled.y, roiRectScaled.width, roiRectScaled.height);
        ctrl->setAutoFocusRegion(roiRectScaled.x, roiRectScaled.y, roiRectScaled.width, roiRectScaled.height);
        (*camQIn)->send(ctrl);

        startPoints.clear();
    }
}

int main() {
    signal(SIGTERM, signalHandler);
    signal(SIGINT, signalHandler);

    // Create device
    std::shared_ptr<dai::Device> device = std::make_shared<dai::Device>();

    // Create pipeline
    dai::Pipeline pipeline(device);

    // Create nodes
    auto cam = pipeline.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_A);
    auto camQIn = cam->inputControl.createInputQueue();
    auto streamQ = cam->requestOutput(std::make_pair(1920, 1080))->createOutputQueue();

    // Start pipeline
    pipeline.start();

    // Create window and set mouse callback
    cv::namedWindow("video");
    cv::setMouseCallback("video", selectRoi, &camQIn);

    while(pipeline.isRunning() && !quitEvent) {
        auto imgHd = streamQ->get<dai::ImgFrame>();
        if(imgHd == nullptr) continue;

        // Calculate scale factors if not set
        if(scaleFactors.first == 0.0f) {
            auto sourceSize = imgHd->transformation.getSourceSize();
            auto targetSize = imgHd->transformation.getSize();
            scaleFactors = std::make_pair(static_cast<float>(sourceSize.first) / targetSize.first, static_cast<float>(sourceSize.second) / targetSize.second);
            std::cout << "Source size: " << sourceSize.first << "x" << sourceSize.second << std::endl;
            std::cout << "Target size: " << targetSize.first << "x" << targetSize.second << std::endl;
        }

        cv::Mat frame = imgHd->getCvFrame();

        // Draw ROI rectangle if it exists
        if(roiRect.width > 0 && roiRect.height > 0) {
            cv::rectangle(frame, roiRect, cv::Scalar(0, 255, 0), 2);
        }

        cv::imshow("video", frame);

        if(cv::waitKey(1) == 'q') {
            break;
        }
    }

    pipeline.stop();
    pipeline.wait();

    return 0;
}
```

### Need assistance?

Head over to [Discussion Forum](https://discuss.luxonis.com/) for technical support or any other questions you might have.
