Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Calculate Mean: different result for masked image vs ROI

I have a weird problem where my average gradient magnitude result is different if I use a mask as opposed to creating a new Mat of that small ROI. I'll explain the 2 different ways I do this and 2 different average gradient magnitude results I get. I thought I should get the same average gradient magnitude result?

Scenario: Image A is my source/original image of a landscape. I want to get the average gradient magnitude in the region A (10,100), (100,100), (100,150), (10,150).

Technique 1:
- Create a ROI Mat that just shows region A. So its dimensions are 90 by 50.
- Perform cv::Sobel(), cv::magnitude() then cv::meanStdDev()
- My average gradient magnitude result is 11.34.

Technique 2:
- Create a new Mat that is a mask. The mat is the same dimensions as Image A and has a white area where Region A is. Then create a new Mat that just shows that region of Image A and the rest of the Mat is black - hopefully this makes sense.
- Perform cv::Sobel(), cv::magnitude() (but use the mask) then cv::meanStdDev()
- My average gradient magnitude result is 43.76.

Why the different result?

Below is my code:

static Mat backupSrc;
static Mat curSrc;

// Technique 1
void inspectRegion(const Point& strt, const Point& end) {

    curSrc = Mat(backupSrc.size(), CV_8UC3);
    cvtColor(backupSrc, curSrc, CV_GRAY2RGB);

    Rect region = Rect(strt, end);
    Mat regionImg = Mat(curSrc, region);

    // Calculate the average gradient magnitude/strength across the image
    Mat dX, dY, mag;
    Sobel(regionImg, dX, CV_32F, 1, 0);
    Sobel(regionImg, dY, CV_32F, 0, 1);
    magnitude(dX, dY, mag);

    Scalar sMMean, sMStdDev;
    meanStdDev(mag, sMMean, sMStdDev);
    double magnitudeMean = sMMean[0];
    double magnitudeStdDev = sMStdDev[0];

    rectangle(curSrc, region, { 0 }, 1);

    printf("[Gradient Magnitude Mean: %.3f, Gradient Magnitude Std Dev: %.3f]\n", magnitudeMean, magnitudeStdDev);
}

// Technique 2
void inspectRegion(const std::vector<Point>& pnts) {

    curSrc = Mat(backupSrc.size(), CV_8UC3);
    cvtColor(backupSrc, curSrc, CV_GRAY2RGB);

    std::vector<std::vector<Point>> cPnts;
    cPnts.push_back(pnts);

    Mat mask = Mat::zeros(curSrc.rows, curSrc.cols, CV_8UC1);
    fillPoly(mask, cPnts, { 255 });
    Mat regionImg;
    curSrc.copyTo(regionImg, mask);


    // Calculate the average gradient magnitude/strength across the image
    Mat dX, dY, mag;
    Sobel(regionImg, dX, CV_32F, 1, 0);
    Sobel(regionImg, dY, CV_32F, 0, 1);
    magnitude(dX, dY, mag);

    Scalar sMMean, sMStdDev;
    meanStdDev(mag, sMMean, sMStdDev, mask);
    double magnitudeMean = sMMean[0];
    double magnitudeStdDev = sMStdDev[0];

    polylines(curSrc, pnts, true, { 255 }, 3);

    printf("[Gradient Magnitude Mean: %.3f, Gradient Magnitude Std Dev: %.3f]\n", magnitudeMean, magnitudeStdDev);
}

Edit: someone suggested to me to erode the mask before calling meanStdDev (with a kernel of 3x3). Doing this brings technique 2 result much closer - 11.97. But is there a way to make this exactly accurate? Ie, produce the same result as technique 1?