CopyTo loding data

asked 2017-12-28 09:23:25 -0500

Boogaloo gravatar image

updated 2017-12-28 10:14:15 -0500

Eduardo gravatar image

I've stepped through the code, through each OpenCV 3.4.0 function, and I just can't understand why data is being lost or destroyed on my faster systems.

When I use CMat::copyTo to send data to a CMat that has already had its frame buffer allocated (I checked, 8MB allocated), and the scope of the receiving CMat is always in range, why should there be a memory problem?

Using clone solves the problem but allocates memory again for the receiving CMat.

CopyTo just memcpy's to the target CMat's data, so it should work. I stepped through the destructors in my copy procedure, and the target CMat receiving the data is not freed.

If I call ShowImg() on the returned data after the copy call, then the memory problem goes away.

Appreciate any advice on this.

My test code to copy a D3D9 surface to a OpenCV CMat:-

void main()
{
while (input() != _ESC)
{
    if (!m_pInputLeft)
    {
        m_pInputLeftRight = new cv::Mat(pSBM->pTexture->getSize().Height, pSBM->pTexture->getSize().Width, CV_8UC4);
        // I stepped thro and memory gets allocated, some 8 MBS for an FHD sized frame
    }

    // *** Copy video frame (DShow RGBA) that was input to OpenCV BGR to LR Mat
    if (CopyTextureToMat(pSBM->pTexture, m_pInputLeftRight), 1)
    {
        DisplayFrame(m_pInputLeftRight);    // Frame data does not get displayed if copy mode is copyTo (clone works!)
    }
}
}


bool CopyTextureToMat(CD3D9Texture *pD9Tex , cv::Mat *pDest, SBM_P *pParams)
{
    IDirect3DSurface9 *pSurf;
    if (!pD9Tex || !pDest)
        return false;
    HRESULT r = pD9Tex->Texture->GetSurfaceLevel(0, &pSurf);    // Get DX9 surface from Viva3D texture
    if (r != S_OK)
        return false;
    r = pSurf->LockRect(&memDesc, NULL, D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE);    // D3DLOCK_READONLY
    if (FAILED(r))
    {
        if (r == D3DERR_WASSTILLDRAWING)
        {
            bool b = true;  // Debug point only
        }
        else
        if (r == D3DERR_INVALIDCALL)
        {
            bool b = true;
        }
        return false;
    }

    pMatOut = new cv::Mat(iHeight, iWidth, CV_8UC4, memDesc.pBits, memDesc.Pitch);  // Create Mat using same memory as DX9 surface

    static bool bCopyMode = 0;

    if (bCopyMode)
        pMatOut->copyTo(*pDest);    // On higher speed systems the memory is being destroyed (no exception occurs on use though)
    else
    {
        *pDest = pMatOut->clone();  // This duplicates the data and works on the higher speed systems
    }

    r = pSurf->UnlockRect();

    delete pMatOut;

    if (FAILED(r))
        return false;
    return true;
}
edit retag flag offensive close merge delete

Comments

please do NOT use raw pointers and new with cv::Mat. it already has refcounts, and your pointers defeat that purpose. please pass it by reference, or even by value (it's just a 60bytes header)

then, clone() is the same as copyTo(), don't start to believe in voodoo ;)

refactor, start with removing the pointers / new with cv::Mat

berak gravatar imageberak ( 2017-12-28 10:39:24 -0500 )edit
1

Right! Sorry, that makes sense! Funny about the voodoo, when I get confused I do feel like that at times, I guess we all do :)

So I can output the mat like this directly:-

DestMat = cv::Mat(iHeight, iWidth, CV_8UC4, memDesc.pBits, memDesc.Pitch);

DestMat replacing pDest

This would be correct?:- If DestMat has an allocated buffer the same size and type, the local mat will copy the data to that, otherwise it will be allocated in the dest mat, or share the memory from the source, the latter right? Then when the local proc cv::Mat is destroyed, it will not free the memory buffer because it is in use by pDest.

Boogaloo gravatar imageBoogaloo ( 2017-12-29 07:35:05 -0500 )edit

if you construct a Mat like:

Mat(w,h,type, some_pointer);

it will NOT take ownership of the data in any way. no memory will get allocated, some_pointer will not be destroyed. in other words, this one just makes a Mat header around existing data. since your pointer from the dx texture will get invalid, once you leave that function, you'll have to clone (or copy) it, so:

DestMat = cv::Mat(iHeight, iWidth, CV_8UC4, memDesc.pBits, memDesc.Pitch).clone();
berak gravatar imageberak ( 2017-12-29 07:53:34 -0500 )edit

This is where I am confused because stepping through the copyTo() code it does do a memcpy to the destination texture's buffer, so although the source will be lost, the data is copied, as the destination Mat has had a buffer allocated during create. What have I missed???

Boogaloo gravatar imageBoogaloo ( 2017-12-29 08:21:13 -0500 )edit

I converted to using &refs, and all is working but only using the clone to copy. I stepped through the code, and I don't see why copyTo fails. On line 288/9 of copyTo() in OCV 3.4.0, it creates a local Mat called Dst by calling _dst.create and then _dst.getMat(); create allocates a new databuffer to _dst as it is empty at start-off. It then copies the src data to that new allocated dst data. On return, the ~mat is called, but as the ref is now 2, it DOES NOT deallocate the dst memory! But that memory, after the return, cannot be used. Something is wrong, because this should work!

Boogaloo gravatar imageBoogaloo ( 2017-12-29 11:07:06 -0500 )edit