Background
I’m following the OpenCV HDR tutorial here. I was getting NaN values when performing step 3 (tonemapping) so I had a look at the source for Tonemap.process()
to see if I could figure out why this was happening. I’m now confused as to how this works at all.
The source is:
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(_src.dims() == 2 && _src.type() == CV_32FC3);
_dst.create(src.size(), CV_32FC3);
Mat dst = _dst.getMat();
double min, max;
minMaxLoc(src, &min, &max);
if(max - min > DBL_EPSILON) {
dst = (src - min) / (max - min);
} else {
src.copyTo(dst);
}
pow(dst, 1.0f / gamma, dst);
}
(See here, Line 63.) I tried to write my own implementation of this in Python and realised that minMaxLoc()
doesn’t work for multi-channel arrays.
Question(s)
How does the above implementation work? It doesn’t look like it should.
The process()
method asserts that the source image is a 3-channel 32-bit floating point matrix, then calls minMaxLoc
with that image. The docs for minMaxLoc
state that it doesn’t work for multi-channel images. If I pass a multi-channel matrix (from Python), then I get:
cv2.error: OpenCV(4.9.0) /io/opencv/modules/core/src/minmax.cpp:1505: error: (-215:Assertion failed) (cn == 1 && (_mask.empty() || _mask.type() == CV_8U)) || (cn > 1 && _mask.empty() && !minIdx && !maxIdx) in function 'minMaxIdx'
The code which checks this assertion is:
void cv::minMaxIdx(InputArray _src, double* minVal,
double* maxVal, int* minIdx, int* maxIdx,
InputArray _mask)
{
CV_INSTRUMENT_REGION();
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert( (cn == 1 && (_mask.empty() || _mask.type() == CV_8U)) ||
(cn > 1 && _mask.empty() && !minIdx && !maxIdx) );
. . .
I can only assume that mask
, minIdx
, and maxIdx
aren’t affecting the outcome (as they aren’t passed in, so my best guess is that the _mask.empty()
and ![min|max]Idx
checks come back true) so the above expression reduces to
CV_Assert(cn == 1 || cn > 1)
I wasn’t sure what CV_MAT_CN(type)
was returning here, my guess was the number of channels in the image. The macro (and related macros) are documented here. If I re-implement the macro:
/* macro_check.c */
#include <stdio.h>
#define MAX 512
#define SHIFT 3
#define MASK ((MAX - 1) << SHIFT)
#define CN(flags) ((((flags) & MASK) >> SHIFT) + 1)
int main(){
int res = CN(21); // CV_32FC3 == 21
printf("CN == %dn", res);
return 0;
}
This prints out:
CN == 3
The value for flags
was chosen based on this table. Choosing random values from the table gives CN corresponding to the ‘C’ term of the type (i.e. it is the number of channels in the image).
While the documentation specifies that minMaxLoc
doesn’t work for multi-channel images, I’m actually now not sure why this assertion fails.
What am I missing here?