# Camera still image at max resolution

This example shows how to capture a max resolution `still` image while streaming lower resolution frames to the host.

By default, camera will stream (1333x1000) frames to the host computer, and when the user presses `c` key, it will capture a
`still` image at max resolution (for OAK4 cameras that's 48MP) and save it to the `.png` file in the current directory.

> **OAK4 OS version**
> If you're using OAK4 cameras, make sure to use the latest version of Luxonis OS (1.6 or newer) to get the full 48MP resolution.

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
import numpy as np

# Create pipeline
with dai.Pipeline() as pipeline:
    # Define source and output
    cam = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A)
    cam_q_in = cam.inputControl.createInputQueue()

    cam_input_q = cam.inputControl.createInputQueue()
    # In some cases (IMX586), this requires an 8k screen to be able to see the full resolution at once
    stream_highest_res = cam.requestFullResolutionOutput(useHighestResolution=True)

    script = pipeline.create(dai.node.Script)
    stream_highest_res.link(script.inputs["in"])
    # Current workaround for OAK4 cameras, as Camera node doesn't yet support "still" frame capture
    script.setScript(
        """
        while True:
            message = node.inputs["in"].get()
            trigger = node.inputs["trigger"].tryGet()
            if trigger is not None:
                node.warn("Trigger received!")
                node.io["highest_res"].send(message)
        """)

    # If 8k, we can only have 1 output stream, so we need to use ImageManip to downscale
    imgManip = pipeline.create(dai.node.ImageManip)
    stream_highest_res.link(imgManip.inputImage)
    imgManip.initialConfig.setOutputSize(1333, 1000)
    imgManip.setMaxOutputFrameSize(1333*1000*3)
    downscaled_res_q = imgManip.out.createOutputQueue()

    highest_res_q = script.outputs["highest_res"].createOutputQueue()
    q_trigger = script.inputs["trigger"].createInputQueue()

    # Connect to device and start pipeline
    ctrl = dai.CameraControl()
    pipeline.start()
    print("To capture an image, press 'c'")
    while pipeline.isRunning():
        img_hd: dai.ImgFrame = downscaled_res_q.get()
        frame = img_hd.getCvFrame()
        cv2.imshow("video", frame)

        key = cv2.waitKey(1)
        if key == ord("q"):
            break
        if key == ord('c'):
            # Send a trigger message to the Script node
            q_trigger.send(dai.Buffer())

        if highest_res_q.has():
            highres_img = highest_res_q.get()
            frame = highres_img.getCvFrame()
            # Save the full image
            cv2.imwrite("full_image.png", frame)
```

#### C++

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

#include "depthai/depthai.hpp"

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

void signalHandler(int) {
    quitEvent = true;
}

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();

    // In some cases (IMX586), this requires an 8k screen to be able to see the full resolution at once
    auto streamHighestRes = cam->requestFullResolutionOutput();

    // Create script node for still capture
    auto script = pipeline.create<dai::node::Script>();
    streamHighestRes->link(script->inputs["in"]);

    // Current workaround for OAK4 cameras, as Camera node doesn't yet support "still" frame capture
    script->setScript(R"(
        while True:
            message = node.inputs["in"].get()
            trigger = node.inputs["trigger"].tryGet()
            if trigger is not None:
                node.warn("Trigger received!")
                node.io["highest_res"].send(message)
    )");

    // If 8k, we can only have 1 output stream, so we need to use ImageManip to downscale
    auto imgManip = pipeline.create<dai::node::ImageManip>();
    streamHighestRes->link(imgManip->inputImage);
    imgManip->initialConfig->setOutputSize(1333, 1000);
    imgManip->setMaxOutputFrameSize(1333 * 1000 * 3);
    auto downscaledResQ = imgManip->out.createOutputQueue();

    auto highestResQ = script->outputs["highest_res"].createOutputQueue();
    auto qTrigger = script->inputs["trigger"].createInputQueue();

    // Start pipeline
    pipeline.start();
    std::cout << "To capture an image, press 'c'" << std::endl;

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

        cv::Mat frame = imgHd->getCvFrame();
        cv::imshow("video", frame);

        int key = cv::waitKey(1);
        if(key == 'q') {
            break;
        }
        if(key == 'c') {
            // Send a trigger message to the Script node
            qTrigger->send(std::make_shared<dai::Buffer>());
        }

        if(highestResQ->has()) {
            auto highresImg = highestResQ->get<dai::ImgFrame>();
            if(highresImg != nullptr) {
                cv::Mat frame = highresImg->getCvFrame();
                // Save the full image
                cv::imwrite("full_image.png", frame);
            }
        }
    }

    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.
