# Removing horizontal noise artefacts from a (SEM) image

I've been trying to analyse some SEM images to determine grain areas and sizes. Some of my images (I've attached an example below) appear to have horizontal bands that manifest as regions with different brightness, which leads to discontinuities when detecting via MorphologicalComponents (code snippet below).

I've tried applying a number of filters (Meanfilter, Medianfilter, Gaussianfilter etc.) but haven't been able to come up with a solution for this. Could anyone please help? Thanks.

Original image: Results after colorising components An example of what I hope to get:

data = Import["https://i.stack.imgur.com/uVv8l.jpg"]
snip = ImageTake[data, 880];
ImageHistogram[snip]
regions = ColorNegate[Binarize[snip, .283]];
inverse = ColorNegate[%];
medianF = MedianFilter[inverse, 1];
ImageHistogram[medianF]
components = MorphologicalComponents[medianF, 0.5, CornerNeighbors -> False];
Colorize[components]
areas = (ComponentMeasurements[masks[[#, 2]], "Area"][[1]][[2]]) & /@Range[1, Length[masks]];
coords = (ComponentMeasurements[masks[[#, 2]],"Centroid"][[1]][[2]]) & /@ Range[1, Length[masks]];

• Does the noise manifest when you take a blank image? If so, you could take a blank image of the noise and subtract that from your images. – Carl Lange Jun 7 '19 at 11:01
• Nope, sadly. I took a few images during that run. The last image I've attached in the question is an example of this - and there were no artefacts there. There must have been some fluctuations during the scanning process, maybe because the sample was (somewhat) magnetic. – Zhao Jun 7 '19 at 11:03
• Probably overthinking it, but you could transfer learn this U-Net on your working data to segment the image. But more than likely you can just solve this using some smart filtering (have a look at ImagePyramid) – Carl Lange Jun 7 '19 at 12:45
• Just a thought, but you can attempt to model the noise with something like MedianFilter[i, {0, 50}], (across 0 vertical, 50 horizontal pixels) which you could then try to subtract from the image. – Carl Lange Jun 8 '19 at 18:42

One thing to look at is to level-adjust each line separately by dividing (or subtracting) the mean of the line's gray value:

img = ImageCrop[ColorConvert[Import["https://i.stack.imgur.com/uVv8l.jpg"],
"Grayscale"], {Full, 850}, {Center, Bottom}];

calib = Rescale[
With[{mean = Mean[#]}, # - mean] & /@ ImageData[img, "Real"]
] // Image


This removes a good portion of the fluctuations

After that, you can try your smoothing and segmentation again

bin = DeleteSmallComponents[
ColorNegate[MedianFilter[calib, 5]] // Binarize,
100
];
HighlightImage[calib, bin]


I like what @halirutan did and had a slight riff on it that appears to yield a better result.

Rescale[With[{meanFiltered = MeanFilter[#, 50]}, # - meanFiltered] & /@
ImageData[img, "Real"]] // Image


The result of this:

If I get time, I might experiment a bit with the "radius" of the MeanFilter to see if that improves the results.

Update: Using Manipulate, it appears that the best results occur between about 50 and 70, after which the banding starts reappearing and is more significant at about 120.

Manipulate[calib = Rescale[With[{meanFiltered = MeanFilter[#, radius]}, # - meanFiltered] & /@ImageData[img, "Real"]] // Image, {{radius, 50}, 10, 250, 5}]


Update2: I decided to let a program find the optimal by doing this:

initialImageData = ImageData[img, "Real"];
allSortedImages =
With[ {choices =
Table[Rescale[
With[ {meanFiltered = MeanFilter[#, radius]},
# -
meanFiltered
] & /@ initialImageData], {radius, 30, 100,
1}]},
SortBy[choices, Total[initialImageData - #] &]
];


That gives this as the best image.

allSortedImages[[1]] // Image


I modified this slightly just to more easily be able to find what was the optimal radius.

initialImageData = ImageData[img, "Real"];
Table[Rescale[
With[ {meanFiltered = MeanFilter[#, radius]},
# - meanFiltered
] & /@
initialImageData], {radius, 30, 100, 1}];
allSortedImages =
SortBy[allAdjustedImages, Total[initialImageData - #] &];