Reputation: 13451
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
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
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
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
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