OpenCV源码解读:Mat与GpuMat的内存拷贝(copyTo和clone)

17 篇文章 1 订阅
15 篇文章 0 订阅

前言

OpenCV基础类型Mat以及GPU上的GpuMat是整个OpenCV工程体系中两种非常基础常用的类型。通常情况下Mat拥有的方法,GpuMat都会有对应的方法。对于内存拷贝,有两个常用的方法copyTo()和clone(),在使用的时候需要特别注意。

在这里插入图片描述
在这里插入图片描述

Mat

clone

Mat Mat::clone() const
{
    Mat m;
    copyTo(m);
    return m;
}

copyTo

void Mat::copyTo( OutputArray _dst ) const
{
    CV_INSTRUMENT_REGION();

#ifdef HAVE_CUDA
    if (_dst.isGpuMat())
    {
        _dst.getGpuMat().upload(*this);
        return;
    }
#endif

    int dtype = _dst.type();
    if( _dst.fixedType() && dtype != type() )
    {
        CV_Assert( channels() == CV_MAT_CN(dtype) );
        convertTo( _dst, dtype );
        return;
    }

    if( empty() )
    {
        _dst.release();
        return;
    }

    if( _dst.isUMat() )
    {
        _dst.create( dims, size.p, type() );
        UMat dst = _dst.getUMat();
        CV_Assert(dst.u != NULL);
        size_t i, sz[CV_MAX_DIM] = {0}, dstofs[CV_MAX_DIM], esz = elemSize();
        CV_Assert(dims > 0 && dims < CV_MAX_DIM);
        for( i = 0; i < (size_t)dims; i++ )
            sz[i] = size.p[i];
        sz[dims-1] *= esz;
        dst.ndoffset(dstofs);
        dstofs[dims-1] *= esz;
        dst.u->currAllocator->upload(dst.u, data, dims, sz, dstofs, dst.step.p, step.p);
        return;
    }

    if( dims <= 2 )
    {
        _dst.create( rows, cols, type() );
        Mat dst = _dst.getMat();
        if( data == dst.data )
            return;

        if( rows > 0 && cols > 0 )
        {
            Mat src = *this;
            Size sz = getContinuousSize2D(src, dst, (int)elemSize());
            CV_CheckGE(sz.width, 0, "");

            const uchar* sptr = src.data;
            uchar* dptr = dst.data;

#if IPP_VERSION_X100 >= 201700
            CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippiCopy_8u_C1R_L, sptr, (int)src.step, dptr, (int)dst.step, ippiSizeL(sz.width, sz.height)) >= 0)
#endif

            for (; sz.height--; sptr += src.step, dptr += dst.step)
                memcpy(dptr, sptr, sz.width);
        }
        return;
    }

    _dst.create( dims, size, type() );
    Mat dst = _dst.getMat();
    if( data == dst.data )
        return;

    if( total() != 0 )
    {
        const Mat* arrays[] = { this, &dst };
        uchar* ptrs[2] = {};
        NAryMatIterator it(arrays, ptrs, 2);
        size_t sz = it.size*elemSize();

        for( size_t i = 0; i < it.nplanes; i++, ++it )
            memcpy(ptrs[1], ptrs[0], sz);
    }
}

clone vs. copyTo

可以看到,使用clone方法会新创建一个Mat,然后调用copyTo方法将原Mat中的内容拷贝到新创建的那个Mat中。

GpuMat

前面说到,GpuMat 一般都有Mat对应的方法,对于内存拷贝,GpuMat也有clone和copyTo两个方法:

clone

    inline GpuMat GpuMat::clone() const
    {
        GpuMat m;
        copyTo(m);
        return m;
    }

copyTo

    ...
    class CudaFuncTable : public GpuFuncTable
    {
    public:
        void copy(const Mat& src, GpuMat& dst) const
        {
            cudaSafeCall( cudaMemcpy2D(dst.data, dst.step, src.data, src.step, src.cols * src.elemSize(), src.rows, cudaMemcpyHostToDevice) );
        }
        ...
    }
    ...
    
void cv::gpu::GpuMat::copyTo(GpuMat& m) const
{
    CV_DbgAssert(!empty());

    m.create(size(), type());

    gpuFuncTable()->copy(*this, m);
}

clone vs. copyTo

可以看到,GpuMat使用clone方法同样会新创建一个GpuMat,然后调用copyTo方法将原GpuMat中的内容拷贝到新创建的那个GpuMat中。最后返回新的GpuMat的指针。

这里需要特别注意的是,如果利用OpenCV+CUDA编程,需要特别注意GpuMat的生成是比较费时的,不像Mat那样容易。在需要高性能的时候需要谨慎使用clone方法,而是提前创建好满足大小需求的GpuMat,然后使用copyTo方法拷贝。

在这里插入图片描述

参考资料

[1] cv::Mat Class Reference
[2] cv::cuda::GpuMat Class Reference
[3] void Mat::copyTo( OutputArray _dst ) const
[4] Mat Mat::clone() const
[5] void cv::gpu::GpuMat::copyTo(GpuMat& m) const
[6] inline GpuMat GpuMat::clone() const
[7] stackoverflow - What’s the difference between Mat::clone and Mat::copyTo?
[8] cnblogs - opencv之Mat数据类型
[9] OpenCV: cv::cuda::HostMem Class Reference
[10] opencv/cuda_host_mem.cpp · opencv/opencv
[11] opencv/cuda_gpu_mat.cpp · opencv/opencv
[12] opencv/cuda.inl.hpp · opencv/opencv

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中,可以使用模板方法来实现cv::Mat和std::vector之间的相互转换。具体的实现代码如下: ``` template<typename _Tp> std::vector<_Tp> convert_mat_to_vector(const cv::Mat& mat) { // 通道数不变,按行转为一行 return (std::vector<_Tp>)(mat.reshape(1, 1)); } template<typename _Tp> cv::Mat convert_vector_to_mat(std::vector<_Tp> v, int channels, int rows) { // 将vector变成单列的mat,这里需要clone(),因为这里的赋值操作是浅拷贝 cv::Mat mat = cv::Mat(v).clone(); cv::Mat dest = mat.reshape(channels, rows); return dest; } ``` 上述代码定义了两个模板方法,分别为convert_mat_to_vector和convert_vector_to_mat。convert_mat_to_vector方法将cv::Mat转换为std::vector,而convert_vector_to_mat方法将std::vector转换为cv::Mat。使用这些方法可以实现cv::Mat和std::vector之间的相互转换。 注意:在转换过程中,需要确保通道数和行数的正确性。在转换cv::Mat为std::vector时,使用reshape方法将多通道的cv::Mat转为一行;在转换std::vector为cv::Mat时,使用reshape方法将一行的std::vector转为指定的通道数和行数。 希望对你有帮助!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [OpenCV - cv::Mat与unsigned char*数组或者float*数组相互转换,cv::Mat与std::vector的相互转换](https://blog.csdn.net/HW140701/article/details/131064164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [opencv mat与std::vector互转](https://blog.csdn.net/qq_61862078/article/details/128883907)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracelessLe

❀点个赞加个关注再走吧❀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值