OpenCV cv::Mat set if
Asked Answered
A

3

13

Is there a simple way to set all values in a cv::Mat to a given value if they fulfill some condition. For instance, I have CV_32FC1, and I want set all values which are 0 to 20. In MATLAB I would have simply done this:

M(M == 0) = 20;
Arabia answered 9/5, 2014 at 20:20 Comment(0)
M
21

You can use

cv::Mat mask = M == 0;
M.setTo(0.5, mask);

However, it includes using additional memory for creating mask, but is a solution using opencv API therefore can be applied to all matrix types. If you consider performance issues, you can always refer directly to Mat::data to optimize this solution for concrete matrix type.

Mohair answered 9/5, 2014 at 20:29 Comment(3)
This is so powerful! It can be used for thresholding, etc by just making the second part of the expression more complex. It seems to be impossible to find this in the documentation or tutorials. We should promote this post so that people know to use it! Unfortunately it doesn't seem to work with compound expression - i.e. this works: cv::Mat newMask = mask <= 2; but this does not cv::Mat newMask = mask > 2 && mask < 5;Zuckerman
@DavidDoria only what comes to my mind now is to use two additional temporary masks, ie cv::Mat maskGreaterThan2 = M > 2; cv::Mat maskLessThan5 = M < 5; cv::Mat mask = massGreaterThan2 & maskLessThan5. Unfortunately this is really memory and time expensive comparing to productivity gain we achieve here.Mohair
@DavidDoria try this instead: (mask > 2) & (mask < 5)Adamant
Y
3

This is a classic case for look-up table. It is fast, simple, and can remap multiple values at same time.

Yetac answered 9/5, 2014 at 20:54 Comment(0)
A
2

Thanks to @marol 's comments, I settled for the implementation below. I am using C++11 lambda functions to condition which values need to be changed. To demonstrate its power, my condition is to set to DEFAULT_VAL when the value is out of the range [MIN_VAL, MAX_VAL]:

#include <functional>

#define MatType float
#define MatCmpFunc std::function<bool(const MatType&)>
.
.
.
// function which accepts lambda function to condition values which need to
// be changed
void MatSetIf(cv::Mat& inputmat, const MatType& newval, MatCmpFunc func) {
  float* pmat = (float*)inputmat.data;
  // iterate and set only values which fulfill the criteria
  for (int idx = 0; idx < inputmat.total(); ++idx) {
    if (func(pmat[idx])) {
      pmat[idx] = newval;
    }
  }
}
.
.
.
void main() {
  cv::Mat mymat(100,100,CV_32FC1);
  const float MIN_VAL = 10;
  const float MAX_VAL = 1000;
  const float DEFAULT_VAL = -1;
  .
  .
  .
  // declare lambda function which returns true when mat value out of range
  MatCmpFunc func = [&](const DepthMatType& val) -> bool {
    return (val < MIN_VAL || val > MAX_VAL) ? true : false;
  };
  // use lambda func above to set all out of range values to 50
  Mat32FSetIf(mymat, DEFAULT_VAL, func);
  .
  .
  .
}
Arabia answered 9/5, 2014 at 20:32 Comment(2)
This is partially what I refer to as direct Mat::data access. Using at() at debug mode is definitely slower than setTo(), so if you decide to use that kind of solution and you have particular matrix type, I would not recommend making a general template function, rather than concrete function which operates directly on Mat::data (as opencv docs says, "when coming to terms of optimization, nothing can beat C array operator[]")Mohair
Thanks @Mohair ... both comments were really helpful :)Arabia

© 2022 - 2024 — McMap. All rights reserved.