# 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

### examples/camera_still_max_res.pipeline.json

```json
{
  "pipeline": {
    "connections": [
      {
        "node1Id": 9,
        "node1Output": "out",
        "node1OutputGroup": "",
        "node2Id": 0,
        "node2Input": "inputControl",
        "node2InputGroup": ""
      },
      {
        "node1Id": 7,
        "node1Output": "out",
        "node1OutputGroup": "",
        "node2Id": 3,
        "node2Input": "trigger",
        "node2InputGroup": "io"
      },
      {
        "node1Id": 4,
        "node1Output": "out",
        "node1OutputGroup": "",
        "node2Id": 12,
        "node2Input": "in",
        "node2InputGroup": ""
      },
      {
        "node1Id": 3,
        "node1Output": "highest_res",
        "node1OutputGroup": "io",
        "node2Id": 10,
        "node2Input": "in",
        "node2InputGroup": ""
      },
      {
        "node1Id": 0,
        "node1Output": "0",
        "node1OutputGroup": "dynamicOutputs",
        "node2Id": 4,
        "node2Input": "inputImage",
        "node2InputGroup": ""
      },
      {
        "node1Id": 0,
        "node1Output": "0",
        "node1OutputGroup": "dynamicOutputs",
        "node2Id": 3,
        "node2Input": "in",
        "node2InputGroup": "io"
      }
    ],
    "globalProperties": {
      "calibData": null,
      "cameraTuningBlobSize": null,
      "cameraTuningBlobUri": "",
      "leonCssFrequencyHz": 700000000.0,
      "leonMssFrequencyHz": 700000000.0,
      "pipelineName": null,
      "pipelineVersion": null,
      "sippBufferSize": 18432,
      "sippDmaBufferSize": 16384,
      "xlinkChunkSize": -1
    },
    "nodes": [
      [
        12,
        {
          "alias": "",
          "id": 12,
          "ioInfo": [
            [
              [
                "",
                "in"
              ],
              {
                "blocking": true,
                "group": "",
                "id": 12,
                "name": "in",
                "queueSize": 3,
                "type": 3,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "XLinkOut",
          "parentId": -1,
          "properties": {
            "maxFpsLimit": -1.0,
            "metadataOnly": false,
            "streamName": "__x_4_out"
          }
        }
      ],
      [
        10,
        {
          "alias": "",
          "id": 10,
          "ioInfo": [
            [
              [
                "",
                "in"
              ],
              {
                "blocking": true,
                "group": "",
                "id": 11,
                "name": "in",
                "queueSize": 3,
                "type": 3,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "XLinkOut",
          "parentId": -1,
          "properties": {
            "maxFpsLimit": -1.0,
            "metadataOnly": false,
            "streamName": "__x_3_highest_res"
          }
        }
      ],
      [
        7,
        {
          "alias": "",
          "id": 7,
          "ioInfo": [
            [
              [
                "",
                "out"
              ],
              {
                "blocking": false,
                "group": "",
                "id": 9,
                "name": "out",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "XLinkIn",
          "parentId": -1,
          "properties": {
            "maxDataSize": 5242880,
            "numFrames": 8,
            "streamName": "__x_3_io_trigger"
          }
        }
      ],
      [
        9,
        {
          "alias": "",
          "id": 9,
          "ioInfo": [
            [
              [
                "",
                "out"
              ],
              {
                "blocking": false,
                "group": "",
                "id": 10,
                "name": "out",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "XLinkIn",
          "parentId": -1,
          "properties": {
            "maxDataSize": 5242880,
            "numFrames": 8,
            "streamName": "__x_0__inputControl"
          }
        }
      ],
      [
        4,
        {
          "alias": "",
          "id": 4,
          "ioInfo": [
            [
              [
                "",
                "inputConfig"
              ],
              {
                "blocking": true,
                "group": "",
                "id": 6,
                "name": "inputConfig",
                "queueSize": 3,
                "type": 3,
                "waitForMessage": false
              }
            ],
            [
              [
                "",
                "inputImage"
              ],
              {
                "blocking": true,
                "group": "",
                "id": 7,
                "name": "inputImage",
                "queueSize": 3,
                "type": 3,
                "waitForMessage": false
              }
            ],
            [
              [
                "",
                "out"
              ],
              {
                "blocking": false,
                "group": "",
                "id": 8,
                "name": "out",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "ImageManip",
          "parentId": -1,
          "properties": {
            "initialConfig": {
              "base": {
                "background": 0,
                "backgroundB": 0,
                "backgroundG": 0,
                "backgroundR": 0,
                "center": true,
                "colormap": 0,
                "operations": [],
                "outputHeight": 1000,
                "outputWidth": 1333,
                "resizeMode": 1,
                "undistort": false
              },
              "outputFrameType": 33,
              "reusePreviousImage": false,
              "skipCurrentImage": false
            },
            "numFramesPool": 4,
            "outputFrameSize": 3999000
          }
        }
      ],
      [
        3,
        {
          "alias": "",
          "id": 3,
          "ioInfo": [
            [
              [
                "io",
                "trigger"
              ],
              {
                "blocking": true,
                "group": "io",
                "id": 3,
                "name": "trigger",
                "queueSize": 8,
                "type": 3,
                "waitForMessage": false
              }
            ],
            [
              [
                "io",
                "in"
              ],
              {
                "blocking": true,
                "group": "io",
                "id": 4,
                "name": "in",
                "queueSize": 8,
                "type": 3,
                "waitForMessage": false
              }
            ],
            [
              [
                "io",
                "highest_res"
              ],
              {
                "blocking": false,
                "group": "io",
                "id": 5,
                "name": "highest_res",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "Script",
          "parentId": -1,
          "properties": {
            "processor": 1,
            "scriptName": "<script>",
            "scriptUri": "asset:__script"
          }
        }
      ],
      [
        0,
        {
          "alias": "",
          "id": 0,
          "ioInfo": [
            [
              [
                "",
                "raw"
              ],
              {
                "blocking": false,
                "group": "",
                "id": 1,
                "name": "raw",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ],
            [
              [
                "dynamicOutputs",
                "0"
              ],
              {
                "blocking": false,
                "group": "dynamicOutputs",
                "id": 2,
                "name": "0",
                "queueSize": 8,
                "type": 0,
                "waitForMessage": false
              }
            ],
            [
              [
                "",
                "inputControl"
              ],
              {
                "blocking": true,
                "group": "",
                "id": 0,
                "name": "inputControl",
                "queueSize": 3,
                "type": 3,
                "waitForMessage": false
              }
            ]
          ],
          "logLevel": 3,
          "name": "Camera",
          "parentId": -1,
          "properties": {
            "boardSocket": 0,
            "calibAlpha": null,
            "cameraName": "",
            "colorOrder": 0,
            "fp16": false,
            "fps": -1.0,
            "imageOrientation": -1,
            "initialControl": {
              "aeLockMode": false,
              "aeMaxExposureTimeUs": 0,
              "aeRegion": {
                "height": 0,
                "priority": 0,
                "width": 0,
                "x": 0,
                "y": 0
              },
              "afRegion": {
                "height": 0,
                "priority": 0,
                "width": 0,
                "x": 0,
                "y": 0
              },
              "antiBandingMode": 0,
              "autoFocusMode": 3,
              "awbLockMode": false,
              "awbMode": 0,
              "brightness": 0,
              "captureIntent": 0,
              "chromaDenoise": 0,
              "cmdMask": 0,
              "contrast": 0,
              "controlMode": 0,
              "effectMode": 0,
              "expCompensation": 0,
              "expManual": {
                "exposureTimeUs": 0,
                "frameDurationUs": 0,
                "sensitivityIso": 0
              },
              "frameSyncMode": 0,
              "lensPosAutoInfinity": 0,
              "lensPosAutoMacro": 0,
              "lensPosition": 0,
              "lensPositionRaw": 0.0,
              "lowPowerNumFramesBurst": 0,
              "lowPowerNumFramesDiscard": 0,
              "lumaDenoise": 0,
              "miscControls": [],
              "saturation": 0,
              "sceneMode": 0,
              "sharpness": 0,
              "strobeConfig": {
                "activeLevel": 0,
                "enable": 0,
                "gpioNumber": 0
              },
              "strobeTimings": {
                "durationUs": 0,
                "exposureBeginOffsetUs": 0,
                "exposureEndOffsetUs": 0
              },
              "wbColorTemp": 0
            },
            "interleaved": true,
            "isp3aFps": 0,
            "ispScale": {
              "horizDenominator": 0,
              "horizNumerator": 0,
              "vertDenominator": 0,
              "vertNumerator": 0
            },
            "mockIspHeight": -1,
            "mockIspWidth": -1,
            "numFramesPoolIsp": 3,
            "numFramesPoolPreview": 4,
            "numFramesPoolRaw": 3,
            "numFramesPoolStill": 4,
            "numFramesPoolVideo": 4,
            "outputRequests": [
              {
                "enableUndistortion": null,
                "fps": {
                  "value": null
                },
                "resizeMode": 0,
                "size": {
                  "value": {
                    "index": 0,
                    "value": [
                      4000,
                      3000
                    ]
                  }
                },
                "type": null
              }
            ],
            "previewHeight": 300,
            "previewKeepAspectRatio": false,
            "previewWidth": 300,
            "rawPacked": null,
            "resolutionHeight": -1,
            "resolutionWidth": -1,
            "sensorCropX": -1.0,
            "sensorCropY": -1.0,
            "sensorType": -1,
            "stillHeight": -1,
            "stillWidth": -1,
            "videoHeight": -1,
            "videoWidth": -1,
            "warpMeshHeight": 0,
            "warpMeshSource": -1,
            "warpMeshStepHeight": 32,
            "warpMeshStepWidth": 32,
            "warpMeshUri": "",
            "warpMeshWidth": 0
          }
        }
      ]
    ]
  }
}
```

## 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.
