Filter OpenCV Mat for NAN values

tmlen picture tmlen · Jan 20, 2017 · Viewed 7.5k times · Source

Is there a direct way to create a mask where values in a cv::Mat_<double> are compared against NAN?

cv::Mat_<real> mat = ...
cv::Mat_<uchar> mask = (mat == NAN);

does not work because f == NAN is always false, even if f was assigned NAN. And there seems to be no overload of isnan() for matrices.

Answer

rayryeng picture rayryeng · Jan 20, 2017

As noted by user pSoLT, if you want to determine which values are NaN, simply compare the matrix with itself. For those elements that are not equal, those would be considered as NaN by the standard definition. You can create a new mask using that logic:

cv::Mat mask = cv::Mat(mat != mat);

mat here would be a matrix that contains NaN values and mask will be a CV_8UC1 (i.e. uchar) type matrix with each element being 0xFF if a value is NaN and 0x00 otherwise.

This post on OpenCV forums may also help: http://answers.opencv.org/question/2221/create-a-mask-for-nan-cells/


Edit (as of April 23, 2020)

As mentioned in the comments as well as one of the answers in this post, the above Boolean expression is buggy and can result in inconsistent behaviour. This is due to certain optimisation decisions that OpenCV makes. Please see this Github issue: https://github.com/opencv/opencv/issues/16465

The solution to this is to use cv::patchNaNs() to address this problem, which converts values that are NaN to a specific number.

To replicate creating the mask in the question, note that patchNaNs performs an in-place replacement of the values, so you would have to make two copies of the image, use the patchNaNs to set the NaN values to a distinct value per image then check to see the occurrence of both values at the same locations at the same time. In other words:

cv::Mat mat1 = mat.clone();
cv::Mat mat2 = mat.clone();
cv::patchNaNs(mat1, 128);
cv::patchNaNs(mat2, 200);
cv::Mat mask = mat1 == 128 & mat2 == 200;

mask will give you the results as expected in the original version of this answer.