0

What is the most generic way of assigning an element from one source matrix to a destination matrix in OpenCV? I always find myself coding something like this, which is not exactly elegant:

cv::Mat source; // CV_32FC3
cv::Mat dest;   // CV_32FC3

// ...

switch( source.channels() )
{
case 1:
    {
        dest.at<float>( y, x ) = source.at<float>( j, i );
        break;
    }
case 2:
    {
        dest.at<cv::Vec2f>( y, x ) = source.at<cv::Vec2f>( j, i );
        break;
    }
case 3:
    {
        dest.at<cv::Vec3f>( y, x ) = source.at<cv::Vec3f>( j, i );
        break; 
    }
case 4:
    {
        dest.at<cv::Vec4f>( y, x ) = source.at<cv::Vec4f>( j, i );
        break;
    }
}

I am wondering what the recommended way for this would be, there must be some generic one-liner for this, right?

Bonus points for a solution working across different data types (e.g. assigning n-channel float elements to n-channel double or short elements)

4
  • 1
    Would it be possible for you to use the function remap as provided in opencv? It takes an input matrix, and output matrix, and a mapping between them. The input matrix and output matrix must be the same type, but the function otherwise doesn't care for their type. Commented May 28, 2019 at 15:14
  • 1
    Use 1x1 size ROIs instead? dest(cv::Rect(x,y,1,1)) = source(cv::Rect(i,j,1,1)) Probably a lot of overhead if its in a loop, but then you might as well make the ROI larger instead. Use convertTo instead of the assignment if you need to change type. Or switch to using explicitly typed Mats (like Mat3f) so if you make a templated function like on of the answers suggests, it can determine the parameters automagically. Commented May 28, 2019 at 15:47
  • @TheBarrometer thanks for pointing out remap, in my current scenario it's indeed the best solution. But I was also thinking of other cases. Commented Jun 6, 2019 at 6:55
  • @DanMašek yes, I think it's too much overhead. But I wasn't really aware of the typed Mats, thanks! Commented Jun 6, 2019 at 6:56

1 Answer 1

1

There is a templated version for cv::Vec<T, n> so you could make a template function taking source and destination types, as well as number of channels like so:

template<typename TSrc, typename TDst, int N>
void assign(const cv::Mat& src, cv::Mat& dst, int x, int y)
{
    assert(src.channels() == N);
    assert(dst.channels() == N);
    assert(src.size() == dst.size());

    const cv::Vec<TSrc, N>& srcVec = src.at<cv::Vec<TSrc, N>>(x, y);
    cv::Vec<TDst, N>& dstVec = dst.at<cv::Vec<TDst, N>>(x, y);
    for (int i = 0; i < N; ++i)
        dstVec[i] = static_cast<TDst>(srcVec[i]);
}
int main(int argc, char* argv[])
{   //sample usage:
    cv::Mat src(5, 5, CV_8UC3, cv::Scalar::all(255)), dst(5, 5, CV_32FC3, cv::Scalar::all(1));
    assign<unsigned char, float, 3>(src, dst, 3, 3);
}
Sign up to request clarification or add additional context in comments.

1 Comment

I'd consider using saturate_cast instead of static_cast, especially for integer types, so you avoid overflow (and to make this closer to how a convertTo would behave).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.