Search code examples
cimageimage-processingmathematical-morphologyimage-morphology

Unwanted Noise element during dilation


I'm applying the dilation operation on a binary ricegrain picture. When the structuring element used has size 9x9, the noise particles get enlarged, which is logical. But when I use a structuring element of size 15x15, i get new noise particles adjacent to the original noise particles. This is vry confusing as i can't why this is happening. The stucturing element is a nxn matrix with all 1's. The images have been attached for reference.

Reference image:
enter image description here

Dilated image (9x9 SE):
enter image description here

Dilated image (15x15 SE):
enter image description here Please help!!

Code:

void DilateBinary(Mat Image, Mat& op, double* SE, int m){
op=Image.clone();
double pixelvalue;

int limit=(m-1)/2;
for(int y = limit; y < (Image.rows - limit); y++)
            {
                for(int x = limit; x < (Image.cols - limit); x++)
                {
                    double max=0.0;
                    //printf("Input pixel: %u \n",Image.at<unsigned char>(y,x));
                    for(int j = -limit; j <=limit ; j++)
                    {
                        for(int i = -limit; i <=limit ; i++)
                        {
                            if(*(SE + (j+1)*m + (i+1) ) > 0)
                            {
                                pixelvalue = (*(SE + (j+1)*m + ((2*limit)-(i+1)) )) * (Image.at<unsigned char>(y+j,x+i));
                                //printf("%f",pixelvalue);
                                if (pixelvalue>127)
                                    max=255.0;
                            }
                        }
                    }
                    op.at<uchar>(y,x) = max;
                    //printf("Output pixel: %u \n",op.at<unsigned char>(y,x));
                    //printf("Modified pixel= %f %d\n",pixelvalue,cvRound(pixelvalue));

                }
            }

Note: SE is the pointer to the mask/SE i'm using. & m is 9 for 9x9 SE, 15 for 15x15 SE.........


Solution

  • The culprit is your if statement inside the second nested for loop blocks:

    for(int j = -limit; j <=limit ; j++) {
        for(int i = -limit; i <=limit ; i++) {
            if(*(SE + (j+1)*m + (i+1) ) > 0) {
                pixelvalue = (*(SE + (j+1)*m + ((2*limit)-(i+1)) ))  (Image.at<unsigned char>(y+j,x+i));
                if (pixelvalue>127)
                    max=255.0;
            }
        }
    }
    

    The way you are indexing into your structuring element assumes that it is a 3 x 3 mask, because you are expecting that i and j hover from -1 to 1. Mark Ransom is correct in his assessment in that you would have indices with negative values. Try setting m = 15, which is where your problem lies. This means that limit = 7 and then start with the beginning of this loop see what row and column you are accessing as you are trying to access the mask with row-major linear indices.

    If limit = 7, that means at the beginning, j = -7, i = -7 and so (j+1)*m = (-7+1)*15 = -90. Also (2*limit) - (i+1) = (2*7) - (-7+1) = 20. This ultimately means that you are trying to access the memory location of SE - 90 + 20 = SE - 70 so you're actually assessing the memory location 70 bytes (assuming unsigned char) away from the origin of your structuring element, and not the top left corner of the structuring element... so are you sure you wrote that code right? I'm surprised you haven't gotten any segmentation faults yet. You may have lucked out with the 9 x 9 because the row offset is negative, but the column offset will be positive and they may be equal in magnitude, so you probably lucked out with accessing all of the structuring elements then, but this certainly won't be true for higher mask sizes.

    In order to ensure that you cover all of the mask coefficients, you need to change the offset to +limit, not +1. I also don't understand the 2*limit inside the if statement. You can just access the mask element directly. Therefore, you need to do this:

    for(int j = -limit; j <=limit ; j++) {
        for(int i = -limit; i <=limit ; i++) {
            if(*(SE + (j+limit)*m + (i+limit) ) > 0) {
                pixelvalue = (*(SE + (j+limit)*m + (i+limit) )) * (Image.at<unsigned char>(y+j,x+i));
                if (pixelvalue>127)
                    max=255.0;
            }
        }
    }
    

    However, if I can suggest something, because you are doing morphological binary dilation, you only have to check to see if any pixels in your structuring element are touching a white pixel. Because this is a square structuring element, this will be even easier. Essentially, in a m x m pixel neighbourhood, if any pixel is non-zero, then you'd set the output to 255 and break out of the loop. A small optimization to your code could be:

    for(int j = -limit; j <=limit ; j++) {
        for(int i = -limit; i <=limit ; i++) {
            pixelValue = (*(SE + (j+limit)*m + (i+limit) )); // Get structuring element value
            pixelValue *= (Image.at<unsigned char>(y+j,x+i)); // Obtain corresponding intensity
            if (pixelValue != 0) { // If non-zero, set output to max and break
                max = 255.0;
                i = limit + 1; j = limit + 1; // break out of loop
            }
        }
    }
    

    This obviously won't save anything when you have areas of complete darkness, but when it comes to areas that are completely white, the double nested for loops that run through the structuring element will only iterate once. It may save some clock cycles, but always remember that brute-force morphology has already terrible computational complexity as it is.