【Vega Prime】-001 读取渲染结果并用OpenCV保存

【Vega Prime】-001 读取渲染结果并用OpenCV保存

  最近遇到一个问题,在使用VP进行渲染之后,需要把多个channel中的画面读取出来,并保存成图片和视频。问题不难,权当一个笔记吧。

1、VP渲染

  这里我对VP的渲染没做改动,从vpApp派生出我自己的app类之后,全部按照默认流程干就行。

2、使用OpenGL获取渲染结果

2.1 让VP在完成帧渲染时发出通知

  为了能够让VP的完成帧渲染之后发送完成渲染的通知,需要为响应的通道订阅EVENT_POST_DRAW事件。m_pRightEyeChannel->addSubscriber(vsChannel::EVENT_POST_DRAW, this);通过这句来订阅事件。需要注意为了成功添加事件订阅,app类需要增加从vsChannel::Subscriber类的派生,并重写相对应的notify函数。

class CErgonomicTestViewApp: public vpApp, vsChannel::Subscriber
{
    /**
     * inherited pre / post draw notification method. Since we're only
     * subscribed to post draw events we don't need to bother checking for the
     * pre draw events.
     */
    virtual void notify(vsChannel::Event, const vsChannel *channel,
        vrDrawContext *context) ;
};

  由于我们只订阅了EVENT_POST_DRAW事件,所以在notify函数中我们并不需要进行事件类型的检查。如果为单个通道订阅了多个事件,在处理之前需要判断通道并判断事件类型做出相应的处理。
  notify函数的第一个参数vsChannel::Event是当前通知的事件,第二个参数const vsChannel *channel是当前通知的channel,最后一个参数vrDrawContext *context是VP维护的一个绘制上下文。

2.2 获取buffer

  获取VP渲染buffer的主要代码如下:

    int ox, oy, sx, sy;
    pChannel->getVrChannel()->getViewport(&ox, &oy, &sx, &sy);//读取当前视口的参数
    //获取当前buffer内容,并保存到m_data中
    glReadPixels(ox, oy, sx, sy, GL_BGR, GL_UNSIGNED_BYTE, m_data);

这里m_data是用户定义的unsigned char *m_data;,该指针在VP执行configure函数是完成初始化,分配足够大小的内存空间。对于不改变Viewport大小的应用,在VP执行完vp自己的configure之后,各个通道的viewport大小此时已经是已知的,所以对于视口所对应的窗口大小已经是固定的,因此,可以在configure中为m_data申请空间。
m_data = vuAllocArray<uchar >::malloc(MAX_WINDOW_WIDTH*MAX_WINDOW_HEIGHT*3);,注意,由于m_data是用户自己申请的内存,在程序退出或异常时,需要手动释放。

    if(m_data != NULL)
        vuAllocArray<uchar >::free(m_data);

3、使用OpenCV保存渲染结果

  此时,渲染结果已经被保存到m_data中,只需要将这个缓冲区保存下来即可。对于这个缓冲区的保存,方法很多,个人根据自己熟悉的方法做就行。
  由于之前使用FreeImage做图像加载和保存已经完成,但在使用freeimage时,发现将图像保存成avi以及MP4文件时比较吃力,所以转而求助于OpenCV。

3.1 将buffer保存到FIBITMAP

  在完成了FIBITMAP对象的创建时,需要指定图像的宽度和高度,可以直接使用viewport的大小。

    //创建FIBITMAP对象
    m_pLeftImg = FreeImage_Allocate(nWidth,nHeight,24,8,8,8);
    //获取FIBITMAP对象的数据指针
    BYTE* pData  = FreeImage_GetBits(pBitmap);
    //将图像数据拷贝从m_data缓冲区拷贝到FIBITMAP对象中
    memcpy(pData,m_data,sx*sy*3);

3.2 FIBITMAP转存到cv::Mat

  这里准备了一个从FIBITMAP结构转换到cv::Mat结构的函数。实际上从m_data直接创建cv::Mat的方法更好,并且肯定能够实现,不过我暂时没有时间去折腾,所以就拿来主义吧。

void FI2MAT(FIBITMAP* src, cv::Mat& dst)
{
    //FIT_BITMAP    //standard image : 1 - , 4 - , 8 - , 16 - , 24 - , 32 - bit
    //FIT_UINT16    //array of unsigned short : unsigned 16 - bit
    //FIT_INT16     //array of short : signed 16 - bit
    //FIT_UINT32    //array of unsigned long : unsigned 32 - bit
    //FIT_INT32     //array of long : signed 32 - bit
    //FIT_FLOAT     //array of float : 32 - bit IEEE floating point
    //FIT_DOUBLE    //array of double : 64 - bit IEEE floating point
    //FIT_COMPLEX   //array of FICOMPLEX : 2 x 64 - bit IEEE floating point
    //FIT_RGB16     //48 - bit RGB image : 3 x 16 - bit
    //FIT_RGBA16    //64 - bit RGBA image : 4 x 16 - bit
    //FIT_RGBF      //96 - bit RGB float image : 3 x 32 - bit IEEE floating point
    //FIT_RGBAF     //128 - bit RGBA float image : 4 x 32 - bit IEEE floating point

    int bpp = FreeImage_GetBPP(src);
    FREE_IMAGE_TYPE fit = FreeImage_GetImageType(src);

    int cv_type = -1;
    int cv_cvt = -1;

    switch (fit)
    {
    case FIT_UINT16: cv_type = cv::DataType<ushort>::type; break;
    case FIT_INT16: cv_type = cv::DataType<short>::type; break;
    case FIT_UINT32: cv_type = cv::DataType<unsigned>::type; break;
    case FIT_INT32: cv_type = cv::DataType<int>::type; break;
    case FIT_FLOAT: cv_type = cv::DataType<float>::type; break;
    case FIT_DOUBLE: cv_type = cv::DataType<double>::type; break;
    case FIT_COMPLEX: cv_type =cv::DataType<cv::Complex<double>>::type; break;
    case FIT_RGB16: cv_type = cv::DataType<cv::Vec<ushort, 3>>::type; cv_cvt = cv::COLOR_RGB2BGR; break;
    case FIT_RGBA16: cv_type = cv::DataType<cv::Vec<ushort, 4>>::type; cv_cvt = cv::COLOR_RGBA2BGRA; break;
    case FIT_RGBF: cv_type = cv::DataType<cv::Vec<float, 3>>::type; cv_cvt = cv::COLOR_RGB2BGR; break;
    case FIT_RGBAF: cv_type = cv::DataType<cv::Vec<float, 4>>::type; cv_cvt = cv::COLOR_RGBA2BGRA; break;
    case FIT_BITMAP:
        switch (bpp) {
    case 8: cv_type = cv::DataType<cv::Vec<uchar, 1>>::type; break;
    case 16: cv_type = cv::DataType<cv::Vec<uchar, 2>>::type; break;
    case 24: cv_type = cv::DataType<cv::Vec<uchar, 3>>::type; break;
    case 32: cv_type = cv::DataType<cv::Vec<uchar, 4>>::type; break;
    default:
        // 1, 4 // Unsupported natively
        cv_type = -1;
        }
        break;
    default:
        // FIT_UNKNOWN // unknown type
        dst = cv::Mat(); // return empty Mat
        return;
    }

    int width = FreeImage_GetWidth(src);
    int height = FreeImage_GetHeight(src);
    int step = FreeImage_GetPitch(src);

    if (cv_type >= 0) {
        dst = cv::Mat(height, width, cv_type, FreeImage_GetBits(src), step);
        if (cv_cvt > 0)
        {
            cv::cvtColor(dst, dst, cv_cvt);
        }
    }
    else {

        std::vector<uchar> lut;
        int n = (long)pow((long double)2,(int) bpp);
        for (int i = 0; i < n; ++i)
        {
            lut.push_back(static_cast<uchar>((255 / (n - 1))*i));
        }

        FIBITMAP* palletized = FreeImage_ConvertTo8Bits(src);
        BYTE* data = FreeImage_GetBits(src);
        for (int r = 0; r < height; ++r) {
            for (int c = 0; c < width; ++c) {
                dst.at<uchar>(r, c) = cv::saturate_cast<uchar>(lut[data[r*step + c]]);
            }
        }
    }

    flip(dst, dst, 0);
}

3.3 保存图像

  至此图像数据已经到OpenCV的cv::Mat里,之后的图像保存对OpenCV来说实在是很简单的事。通过cv::imwrite即可完成。

cv::imwrite(sFileName.c_str(),img_merge);

3.4 保存视频

  OpenCV使用cv::VideoWriter进行视频的写入。

    if (!m_pVideoWriter)
    {
        cv::Size sz(img_merge.cols,img_merge.rows);
        std::string sAviName = sImgRootPath + sAVI+sScensName+".avi";
        m_pVideoWriter = new cv::VideoWriter(sAviName.c_str(), CV_FOURCC('M', 'J', 'P', 'G'), 10, sz);
    }
    m_pVideoWriter->write(img_merge);

4、注意

  在过程中遇到一个很坑爹的坑。我的平台是win10+VS2005,由于平常都使用win10+VS2015开发,所以之前的OpenCV2.4.13是用VS2015编译的。到VS2005上用的时候,发现死活不能使用imwrite和imshow之类的函数。于是试图使用VS2005重新编译。结果重新下载了2.4.13的源代码之后,发现用VS2005编译不过去,说不支持_cpuidex命令。由于这次任务比较急,所以我没有深入去研究了,再向下切换到OpenCV2.3.1之后用VS2005重新编译生成了所有库。结果发现include目录需要自己手动把各个模块拷贝进去,真是醉了。估计是我折腾CMake生成解决方案时没折腾明白。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值