I made a minimum filter function that should work for PGM P2 images. The problem is that the output is wrong. Everything is described below.
Algorithm: http://www.roborealm.com/help/Min.php and https://www.youtube.com/watch?v=Y_QF0Xq8zGM
Debugging sample:
The beginning part of my image:
matrixSize = 3
offset = 1
First loop iteration:
j = 1, i = 1
neighboursNumbers = Count = 9
neighboursNumbers
values: (note that this is before sorting)
Second loop iteration:
j = 1, i = 2
neighboursNumbers = Count = 9
neighboursNumbers
values: (again before sorting)
Code:
// Properties
public string Format { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int MaxGrayLevel { get; set; }
public int[] Pixels { get; set; }
// Minimum Filter Code
int matrixSize = 3;
int offset = (matrixSize - 1) / 2;
for (int j = offset; j < image.Height - offset; j++)
{
for (int i = offset; i < image.Width - offset; i++)
{
List<int> neighboursNumbers = (from x in Enumerable.Range(i - offset, matrixSize)
from y in Enumerable.Range(j - offset, matrixSize)
where (x >= 0) && (x < image.Width) && (y >= 0) && (y < image.Height)
select image.Pixels[y * Width + x]).ToList();
neighboursNumbers.Sort();
int minIndex = neighboursNumbers[0];
image.Pixels[j * image.Width + i] = minIndex;
}
}
Result:
Expected (this result is using radius 7.0 in ImageJ):
You are replacing the data of the source image Pixels
in the loop with the output of the filter. You should not do that, because the filter must be applied to the entire source image.
To see the problem, imagine you are applying the filter to the pixel (X,Y)
and get an output of M
. The next step in your algorithm is to apply the filter to (X+1,Y)
. The neighborhood of that pixel includes (X,Y)
, but you replaced its value with M
in the earlier step. So a local minimum value will persist, until a new minimum is found. That creates the structure of your resulting image.
In order to fix it, simply create a new image in which you put the filter output, and don't modify the filter input.