Removing outlier pixels from a small binary image
-
20-06-2021 - |
Pergunta
I am currently implementing an algorithm for identifying the axis of minimum inertia of a colored mass (provided by the second moments). In order to do so, I need to acquire the centre of mass, as given by the first moments.
The weighted averaging function works well, but due to outlier pixels, I am receiving undesired results.
Here is the averaging function:
(e.g. x's weighted average)
for (i = 0, i < rows, i++) {
for (j = 0, j < cols, j++) {
if (colorAt(i,j).isForeground()) {
tempSumX++;
totalForeground++;
}
}
x_ += i*tempSumX;
tempSumX = 0;
}
x_ /= totalForeground; //where x_ represents the x coordinate of the weighted center of mass.
Given an image such as this, which is represented by exclusively two colors (background and foreground), how can I remove outlying pixels? Note: Outlying pixels refers to anything not part of the big color-mass. The white dot is the calculated center of mass, which is incorrect.
Much appreciated.
Solução
There are a lot of flood fill algorithms that would identify all the connected pixels given a starting point.
Alternatively a common way to remove small outliars like these that come from noise is to erode the image, then dilate it to return to the same size - although if you are purely doing CoG you don't necessarily need the dilate step
Outras dicas
How about, in pseudo code:
for( y = 0; y < rows; y++ )
{
for ( x = 0; x < cols; x++ )
{
if ( pixel( x, y ).isColor() )
{
int sum = 0;
// forgetting about edge cases for clarity...
if ( !pixel( x-1, y-1 ).isColor() ) sum++;
if ( !pixel( x, y-1 ).isColor() ) sum++;
if ( !pixel( x+1, y-1 ).isColor() ) sum++;
if ( !pixel( x-1, y ).isColor() ) sum++;
if ( !pixel( x+1, y ).isColor() ) sum++;
if ( !pixel( x-1, y+1 ).isColor() ) sum++;
if ( !pixel( x, y+1 ).isColor() ) sum++;
if ( !pixel( x+1, y+1 ).isColor() ) sum++;
if ( sum >= 7 )
{
pixel( x, y ).setBackground();
x -= 1;
y -= 1;
}
}
}
}
That is remove any pixel surrounded by 7 background pixels. If you change the color of a pixel back up to the earliest pixel which may now be affected.
Your measure of "outlier" could change - e.g. you could count diagonal pixels as being worth 1/2 as much. E.g. pixels directly above, below, left and right count for 2. And then use a different number for the sum.
You can increase accuracy by increasing the size of the filter - like to a 5x5 instead of a 3x3. In that case pixels 2 away should count for even less.
I believe your algorithm is incorrect. m10 (sorry, I couldn't figure out how to do subscripts) in a binary image is the summation of the x-coordinates of the foreground pixels, so your code should be something like:
for (i = 0, i < rows, i++) {
for (j = 0, j < cols, j++) {
if (colorAt(i,j).isForeground()) {
tempSumX += i;
totalForeground++;
}
}
}
x_ = tempSumX/totalForeground; //where x_ represents the x coordinate of the weighted center of mass.
Assuming this is related to your previous post Algorithm for Finding Longest Stretch of a Value at any Angle in a 2D Matrix, you should compute the other first and second order moments in the same loop:
m01 += j;
m20 += i*i;
m02 += j*j;
m11 += i*j;
(tempSumX in your algorithm is just m10 and totalForeground is m00)
Give this a try and if you're still having trouble with the outliers you can use Connected Component Labeling http://en.wikipedia.org/wiki/Connected_component_labeling to find the largest mass. (Available in matlab using bwlabel
or bwconncomp
.)