# Nonuniform Background Subtraction

I have multiple images like that shown below in which there are essentially two different brightness levels. I've looked into histogram equalization techniques in scikit-image as well as several background subtraction techniques in ImageJ, neither of which seem to be effective over the parameter ranges I've tested. I've thought of using edge detection to process the two regions separately, but my concern is that it will result in unnatural edge effects at the boundaries. There's also a lot of features in the image, making the simple edge detection techniques I've tried so far not work so well.

Are there better techniques for doing this sort of processing? I'm working in python with scikit-image, but would welcome any solution.

If it's not clear what kind of result I'm looking for, consider the below image that has an evenly balanced background.

• Is the rectangular shadow at the same place for all of your images? – A_A Feb 16 '18 at 15:55
• @lanery , hstogram equalization is indeed a standard technique, and it should yield better results. See points from here: mathworks.com/matlabcentral/answers/… – VladP Feb 16 '18 at 15:58
• @A_A No but it is always roughly the same size and shape. – lanery Feb 16 '18 at 15:59
• Can I please ask if this was resolved? – A_A Feb 27 '18 at 11:01
• @A_A Sorry it's taken me so long to get back to you. I tried modifying your approach to basically use Poisson smoothing in the narrow band of high intensity change. When it correctly finds the region of high contrast change, it works well. But overall the method does not work on my full dataset and often finds false positives. I think I'll have to take a different approach moving forward but am not sure yet what or how. Thank you for your answer, though --- it provided a good starting point. – lanery Mar 8 '18 at 11:49

Histogram equalisation (on its own) is not likely to work here because it corrects the brightness levels of the image as a whole.

What you are dealing with here is a uniform reduction in brightness akin to the effect of an optical filter.

What is really required is the correction of the mean brightness level over the "patch" that appears darker. Therefore, the problem now is to find the patch and correct it.

I am going to use Octave for the rest of this post but as you will see too it is not too difficult to transpose the idea to Python.

Let's have a look at the damage:

%Load the image and plot its average values by column.
imshow(a); %This should simply depict the image
%Let's take the cross section of the image by "squashing" all of its columns into their average.
q = mean(a);
plot(q);


This comes out like:

The most striking feature in this image is the sharp drop of the brightness level, about a third of the image width from the left.

Let's now look at the vertical cross section by "squashing" all of the image's rows into a mean column. All you have to add is:

q2=mean(a');
plot(q2);


And this comes out like:

In fact, if you have MATLAB somewhere handy, you might want to try to surf(a) and that will show you the "basin(s)" very clearly. For some reason, Octave's surf is getting its triangles tangled when I try to do it.

Right, so, what do we observe from this?

1. The image on either side of the discontinuities is predictable. It looks as if you simply took part of the waveform and slide it downwards...similar to the effect of a neutral density filter. There's simply a dip in the brightness that has a rectangular shape.

2. The problem on the first image is worse than the problem on the second image. On the second image we observe a sharp discontinuity which is approximately 4 pixels wide but it is very very sharp. On the first image, the discontinuity is about 8 pixels wide. Whatever data there was in those pixels has been "swallowed" by the gradient.

3. Working in 1 dimension (at first) you can see that just by either adding a value to the dip or subtracting a value from the "step", you can more or less match the means of the two regions on either side of the discontinuity. But for those pixels in the transition band, you are going to have to do something else (e.g. interpolation) to remedy the problem. The data in that region has been lost.

Where is the (big) patch? It starts approximately at 85,78 and extends all the way to the margins of the image. The X point is found by squashing all columns into one row and then looking at where the discontinuity starts and the Y point is found by doing the same but this time squashing all rows into one column.

Let's try to correct the X direction. First of all, we are going to get rid of the transition gradient. This is between pixels 85-93 (not across the whole image though, remember that this was a patch and that here we are trying to correct for the one dimensional case).

Q = q;
Q(85:93) = 0;
plot(Q);


Now, correct for the brightness. The difference is approximately 36. All I am doing here is to add a "step" (a constant value) from pixel 93 onwards, like so:

m=93;
Q2 = Q+36*[zeros(1,m),ones(1,length(Q)-m)];
plot(Q2);


This now looks like this:

What can you do for that gap of 8 values? You can fill it in with a constant value but that is going to show up as a "band" of 8 pixels and it will definitely be visible. The other thing you can do is to fill it with some Gaussian noise that has approximately pixel value 36 mean and enough variance so that you cannot distinguish it from the surrounding pixels. Unfortunately, because in this case the noise would be uncorrelated that would show up as banding again but this time much more "fuzzy". And finally, the other thing you could do is to train a model that would "generate" realistic values from the surrounding areas so that you "heal" the bands gracefully.

This is the basic idea. To automatically find the gradients you can either extract the gradient of the image with imgradient or do a similar calculation in a loop with a threshold on the local slope.

Your problems are not over here. As you can see in all images, there are more than 1 "basins" in the image. Your first image has a light band on the top right part which goes right into the darkness right below it.

Hopefully they are all rectangular so you can simply repeat the exact same process as many times as it is required until the image contains "no basins".

Hope this helps.