1 | initial version |
You could use what I call an oriented high-pass filter (don't know the official name). I pasted the code below. It is in Emgu though. The basic idea is that you take a high pass filter kernel like:
k = [-1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1]
and convolve it with the image. Then rotate the kernel by N degrees (e.g. 15) and convolve it again. You do this up to 180 degrees and take the max of all the convoluted images. Scratches will have a high response to this kernel when aligned with the rotated filter. Your grains will too though. But you could experiment with leaving out the direction of the grains... BTW, smoothing the image beforehand gave me better results.
private Image<Gray, float> OrientedHighPass(Image<Gray, byte> image)
{
Image<Gray, float> filtered = new Image<Gray, float>(image.Size);
Image<Gray, float> final = new Image<Gray, float>(image.Size);
// import parameters
int smoothOpSize = 5;
int filterLength = 16;
int angleStep = 15;
// construct the kernel
float[,,] k3 = new float[1, 5, 1]
{
{ { -1 }, { -1 }, { 4 }, { -1 }, { -1 } },
};
Image<Gray, float> kRow = new Image<Gray, float>(k3);
Image<Gray, float> kImageOriginal = kRow;
for (int l = 0; l < filterLength - 1; ++l)
kImageOriginal = kImageOriginal.ConcateVertical(kRow);
// first step, smooth image
image._SmoothGaussian(smoothOpSize);
for (int angle = 0; angle < 180; angle += angleStep)
{
// Create convolution kernel
Image<Gray, float> kImage = kImageOriginal.Rotate(angle, new Gray(0), false);
// make sure the average of the kernel stays zero
kImage = kImage - (kImage.GetSum().Intensity / (kImage.Width * kImage.Height));
float[,] k = new float[kImage.Height, kImage.Width];
Buffer.BlockCopy(kImage.Data, 0, k, 0, k.Length * sizeof(float));
ConvolutionKernelF kernel = new ConvolutionKernelF(k);
filtered = image.Convolution(kernel);
final = filtered.Max(final);
}
return final;
}
2 | Added .Net comment |
You could use what I call an oriented high-pass filter (don't know the official name). I pasted the code below. It is in Emgu though. though (.Net wrapper for OpenCV). The basic idea is that you take a high pass filter kernel like:
k = [-1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1 -1 -1 4 -1 -1]
and convolve it with the image. Then rotate the kernel by N degrees (e.g. 15) and convolve it again. You do this up to 180 degrees and take the max of all the convoluted images. Scratches will have a high response to this kernel when aligned with the rotated filter. Your grains will too though. But you could experiment with leaving out the direction of the grains... BTW, smoothing the image beforehand gave me better results.
private Image<Gray, float> OrientedHighPass(Image<Gray, byte> image)
{
Image<Gray, float> filtered = new Image<Gray, float>(image.Size);
Image<Gray, float> final = new Image<Gray, float>(image.Size);
// import parameters
int smoothOpSize = 5;
int filterLength = 16;
int angleStep = 15;
// construct the kernel
float[,,] k3 = new float[1, 5, 1]
{
{ { -1 }, { -1 }, { 4 }, { -1 }, { -1 } },
};
Image<Gray, float> kRow = new Image<Gray, float>(k3);
Image<Gray, float> kImageOriginal = kRow;
for (int l = 0; l < filterLength - 1; ++l)
kImageOriginal = kImageOriginal.ConcateVertical(kRow);
// first step, smooth image
image._SmoothGaussian(smoothOpSize);
for (int angle = 0; angle < 180; angle += angleStep)
{
// Create convolution kernel
Image<Gray, float> kImage = kImageOriginal.Rotate(angle, new Gray(0), false);
// make sure the average of the kernel stays zero
kImage = kImage - (kImage.GetSum().Intensity / (kImage.Width * kImage.Height));
float[,] k = new float[kImage.Height, kImage.Width];
Buffer.BlockCopy(kImage.Data, 0, k, 0, k.Length * sizeof(float));
ConvolutionKernelF kernel = new ConvolutionKernelF(k);
filtered = image.Convolution(kernel);
final = filtered.Max(final);
}
return final;
}