YangYouji's WebSite

Jetson nano 获取CSI相机RAW图片并转换为Opencv Mat

树莓派可以通过加入 参数 “bayer=True” ,来获取CSI相机的raw图片,raw数据会紧跟在jpg图片的末尾,具体提取的方法不在累述,可以方便的google到。而Nvidia的 Jetson nano要获取raw图片的方法网上比较零散,故整理了一下。

获取raw图片的方法其实比较简单,使用的是jetson nano中自带的Libargus api。其api介绍可以参考https://docs.nvidia.com/jetson/l4t-multimedia/group__LibargusAPI.html,api架构可以参考https://docs.nvidia.com/jetson/archives/l4t-archived/l4t-3231/index.html#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/jetson_xavier_camera_soft_archi.html

相关代码在 Libargus api demo中的 argus\samples\oneShot 简单修改而来。并增加了转换为opencvmat的代码

主要更改的地方
1. iEGLStreamSettings->setPixelFormat(Argus::PIXEL_FMT_YCbCr_420_888); 变更为
iEGLStreamSettings->setPixelFormat(Argus::PIXEL_FMT_RAW16);
2. EGLStream::IImageJPEG *iImageJPEG = Argus::interface_cast(image);变更为
EGLStream::IImage *iImage = Argus::interface_cast(image);
3. 转换的代码看代码里的注释

#include <stdio.h>
#include <stdlib.h>
#include <Argus/Argus.h>
#include <EGLStream/EGLStream.h>
#include "ArgusHelpers.h"
#include "CommonOptions.h"
#include <opencv2/imgcodecs.hpp>

#define EXIT_IF_NULL(val,msg)   \
        {if (!val) {printf("%s\n",msg); return EXIT_FAILURE;}}
#define EXIT_IF_NOT_OK(val,msg) \
        {if (val!=Argus::STATUS_OK) {printf("%s\n",msg); return EXIT_FAILURE;}}

#ifdef ANDROID
#define FILE_PREFIX "/sdcard/DCIM/"
#else
#define FILE_PREFIX ""
#endif

int main(int argc, char** argv)
{
    ArgusSamples::CommonOptions options(basename(argv[0]),
                                        ArgusSamples::CommonOptions::Option_D_CameraDevice |
                                        ArgusSamples::CommonOptions::Option_M_SensorMode);
    if (!options.parse(argc, argv))
        return EXIT_FAILURE;
    if (options.requestedExit())
        return EXIT_SUCCESS;

    const uint64_t FIVE_SECONDS_IN_NANOSECONDS = 5000000000;

    Argus::UniqueObj<Argus::CameraProvider> cameraProvider(Argus::CameraProvider::create());

    Argus::ICameraProvider *iCameraProvider =
        Argus::interface_cast<Argus::ICameraProvider>(cameraProvider);
    EXIT_IF_NULL(iCameraProvider, "Cannot get core camera provider interface");
    printf("Argus Version: %s\n", iCameraProvider->getVersion().c_str());

    Argus::CameraDevice *device = ArgusSamples::ArgusHelpers::getCameraDevice(
            cameraProvider.get(), options.cameraDeviceIndex());
    Argus::ICameraProperties *iCameraProperties =
        Argus::interface_cast<Argus::ICameraProperties>(device);
    if (!iCameraProperties)
    {
        REPORT_ERROR("Failed to get ICameraProperties interface");
        return EXIT_FAILURE;
    }

    Argus::SensorMode* sensorMode = ArgusSamples::ArgusHelpers::getSensorMode(
            device, options.sensorModeIndex());
    Argus::ISensorMode *iSensorMode =
        Argus::interface_cast<Argus::ISensorMode>(sensorMode);
    if (!iSensorMode)
    {
        REPORT_ERROR("Failed to get sensor mode interface");
        return EXIT_FAILURE;
    }

    printf("Capturing from device %d using sensor mode %d (%dx%d)\n",
           options.cameraDeviceIndex(), options.sensorModeIndex(),
           iSensorMode->getResolution().width(), iSensorMode->getResolution().height());

    Argus::Status status;
    Argus::UniqueObj<Argus::CaptureSession> captureSession(
        iCameraProvider->createCaptureSession(device, &status));
    EXIT_IF_NOT_OK(status, "Failed to create capture session");

    Argus::ICaptureSession *iSession =
        Argus::interface_cast<Argus::ICaptureSession>(captureSession);
    EXIT_IF_NULL(iSession, "Cannot get Capture Session Interface");

    Argus::UniqueObj<Argus::OutputStreamSettings> streamSettings(
        iSession->createOutputStreamSettings(Argus::STREAM_TYPE_EGL));

    Argus::IEGLOutputStreamSettings *iEGLStreamSettings =
        Argus::interface_cast<Argus::IEGLOutputStreamSettings>(streamSettings);
    EXIT_IF_NULL(iEGLStreamSettings, "Cannot get IEGLOutputStreamSettings Interface");
    iEGLStreamSettings->setPixelFormat(Argus::PIXEL_FMT_RAW16);
    iEGLStreamSettings->setResolution(iSensorMode->getResolution());
    iEGLStreamSettings->setMetadataEnable(true);

    Argus::UniqueObj<Argus::OutputStream> stream(
        iSession->createOutputStream(streamSettings.get()));
    EXIT_IF_NULL(stream, "Failed to create EGLOutputStream");

    Argus::UniqueObj<EGLStream::FrameConsumer> consumer(
        EGLStream::FrameConsumer::create(stream.get()));

    EGLStream::IFrameConsumer *iFrameConsumer =
        Argus::interface_cast<EGLStream::IFrameConsumer>(consumer);
    EXIT_IF_NULL(iFrameConsumer, "Failed to initialize Consumer");

    Argus::UniqueObj<Argus::Request> request(
        iSession->createRequest(Argus::CAPTURE_INTENT_STILL_CAPTURE));

    Argus::IRequest *iRequest = Argus::interface_cast<Argus::IRequest>(request);
    EXIT_IF_NULL(iRequest, "Failed to get capture request interface");

    status = iRequest->enableOutputStream(stream.get());
    EXIT_IF_NOT_OK(status, "Failed to enable stream in capture request");

    Argus::ISourceSettings *iSourceSettings =
        Argus::interface_cast<Argus::ISourceSettings>(request);
    EXIT_IF_NULL(iSourceSettings, "Failed to get source settings request interface");
    iSourceSettings->setSensorMode(sensorMode);

    uint32_t requestId = iSession->capture(request.get());
    EXIT_IF_NULL(requestId, "Failed to submit capture request");

    Argus::UniqueObj<EGLStream::Frame> frame(
        iFrameConsumer->acquireFrame(FIVE_SECONDS_IN_NANOSECONDS, &status));

    EGLStream::IFrame *iFrame = Argus::interface_cast<EGLStream::IFrame>(frame);
    EXIT_IF_NULL(iFrame, "Failed to get IFrame interface");

    EGLStream::Image *image = iFrame->getImage();
    EXIT_IF_NULL(image, "Failed to get Image from iFrame->getImage()");

    EGLStream::IImage *iImage = Argus::interface_cast<EGLStream::IImage>(image);
    EXIT_IF_NULL(iImage, "Failed to get iImage Interface");    
//转换为opencvmat,我使用的是12.3m的相机,分辨率为4320*3040,每行的pitch为8192
//pitch大小可以在通过sensorMode读出,我就不在写代码了。
    Mat _img(3040,4032,CV_16UC1); //初始化一个mat
    uint16_t* p_s = (uint16_t*)iImage->mapBuffer(); //相机buffer指针
    uint16_t* p_d = (uint16_t*)_img.data; //Mat指针
    p_d+=(3040*4032 - 1);//由于相机buffer图像是翻转了180度的,我在赋值到Mat时顺便翻转回去了

    for(int y=0;y<3040;y++){
        for(int x =0;x<4032;x++){
// 12.3m相机是12bit深度的图片,相机buffer内是14bit的,所以进行了移位运算,不知是不是驱动有bug的原因
            *p_d = *p_s>>2; 
            if(y%2==0 && x%2==0){
 //这一步是12.3m相机red通道raw值不仅低,故赋值时增加了1倍,这样转换后的彩色图更加接近真实一点
               *p_d = *p_d *2;
            }
            p_d--;
            p_s++;
        }
        p_s+=64;
    }
//这里保存的是bayer图,如果想看转换后的图,可以通过opencv里的cvtcolor函数进行bayer格式的转换
    imwrite("bayer".tif,_img);
    // Shut down Argus.
    cameraProvider.reset();
    return EXIT_SUCCESS;
}
退出移动版