Yongwei Xing
Yongwei Xing

Reputation: 13451

Opencv matrix per-element operation

I have a cv::Mat and I want to do some per-element operation. For example, I have a float matrix like cv::Mat m(100,100, CV_64F);. I have another function like below:

double fun(double elem);

How to apply this function to a matrix, which can do per-element operation.

Upvotes: 1

Views: 2163

Answers (4)

StereoMatching
StereoMatching

Reputation: 5019

Design a generic for loop for the matrix

/**
 *@brief apply stl like for_each algorithm on every channels
 *
 * @param T : the type of the channel(ex, uchar, float, double and so on)
 * @param func : Unary function that accepts an element in the range as argument
 *
 *@return :
 *  return func
 */
template<typename T, typename UnaryFunc, typename Mat>
UnaryFunc for_each_channels(Mat &&input, UnaryFunc func)
{
    int rows = input.rows;
    int cols = input.cols;

    if(input.isContinuous()){
        cols = input.total() * input.channels();
        rows = 1;
    }

    for(int row = 0; row != rows; ++row){
        auto begin = input.template ptr<T>(row);
        auto end = begin + cols;
        while(begin != end){
            func(*begin);
            ++begin;
        }
    }

    return func;
}

Use it as following

cv::Mat m(100, 100, CV_64F);
//......initialize m
for_each_channels<double>(m, [](double &a)
{
    a = std::sin(a) * 2.14168;;
});

You could extend the api to support vector type too

for_each<cv::vec3d>(m, [](cv::vec3d& a)
{
    a = ..... //do something
});

I already design some generic api to use, I put them at github

Upvotes: 0

remi
remi

Reputation: 3988

1)Check that your mat is continuous

if(mat.isContinuous())

2)Access row data pointer and cast it to double

double* buffer = (double*)mat.data; 
// or
double* buffer = mat.ptr<double>(0);
double *bufferEnd = (double*)mat.end;

3)Call your function on each elem of the buffer, e.g.

 for (; buffer != bufferEnd; ++buffer)
        *buffer = fun(*buffer)

Upvotes: 2

Andrey  Smorodov
Andrey Smorodov

Reputation: 10850

You can do it like this:

double func(double x)
{
    return(sin(x));
}

int main(void)
{
    Mat M=Mat::eye(3,3,CV_64FC1);
    std::transform(M.begin<double>(),M.end<double>(),M.begin<double>(),func);
    cout << M;
    getchar();
    return 0;
}

Upvotes: 5

marol
marol

Reputation: 4074

What about simply loop?

cv::Mat m(100, 100, CV_64F);
for(int x=0;x<m.cols;x++)
    for(int y=0;y<m.rows;y++)
        m.at<double>(y,x) = fun(m.at<double>(y,x));

This is pretty fast if you use release version of opencv libraries. If you can change source of fun function that it changes the argument itself instead of returning it form function:

void fun(double &elem);

You can then omit one at() call:

cv::Mat m(100, 100, CV_64F);
for(int x=0;x<m.cols;x++)
    for(int y=0;y<m.rows;y++)
        fun(m.at<double>(y,x));

Upvotes: 1

Related Questions