Ask Your Question
0

Calculate Mean: different result for masked image vs ROI

asked 2018-05-20 11:32:11 -0600

sazr gravatar image

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?

edit retag flag offensive close merge delete

1 answer

Sort by ยป oldest newest most voted
0

answered 2018-05-20 17:13:29 -0600

Tetragramm gravatar image

What's happening is that your borders are different. In technique 1, Sobel uses BORDER_REFLECT101 to reflect the edges over the boundary. In technique 2, you replace the surroundings by zeros. That produces a lot of big gradients at the edge of your ROI, which are then inside the masked area.

The two ways of fixing this are to change the border options for technique 1 to BORDER_CONSTANT, or to provide some context for technique 2. Either by copying over slightly more information, performing the derivative on the original image, (Neither of these is quite exact, as the extra info does not exactly match the reflection) or manually reflecting the image (Which is exact, and just what T1 does now).

edit flag offensive delete link more

Comments

@Tetragramm thanks for your help :) Are you able to explain the part where you mention manually reflecting the image (Which is exact, and just what T1 does now)? What do you mean by reflecting? Do you mean just copying the image?

sazr gravatar imagesazr ( 2018-05-21 09:52:33 -0600 )edit

The Sobel gradients are calculated over a 3x3 area. For the pixels in the corners and along the edges, that 3x3 area extends past the real image. There are a few different methods for dealing with this, the default being Reflecting the image, according to the pattern gfedcb|abcdefgh|gfedcba

You can do this manually, which is exactly what T1 does automatically.

Tetragramm gravatar imageTetragramm ( 2018-05-23 16:44:36 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2018-05-20 11:32:11 -0600

Seen: 1,777 times

Last updated: May 20 '18