How Shading Corrections Create Image Rings
Introduction
This web page is a technical discussion of why a shading correction similar to Nikon's hardcoded image correction creates rings and steps in an image.For further background here's a link to a separate page discussing the Nikon hardcoded correction issue.
The Short Answer
The short answer is that a smooth function is being applied to integer data and then the result is rounded or truncated back to an integer representation since a raw file from a camera can only contain integer data.
This integer rounding/truncation is what creates the problem. If instead, the result of the operation is stored as floating point then no rings/steps are produced.
Quadratic function of my Nikon Z6
I was able to approximately "reverse engineer" the smooth function applied to my Nikon Z6 raw files by examining where the steps occured in my raw image files and then fitting the smooth function that caused them.
A simple 2-dimensional quadratic function fitted the positions of the steps in the image surprisingly well:
- X=a*((x-xc)*cos(theta)-(y-yc)*sin(theta));
- Y=b*((x-xc)*sin(theta)+(y-yc)*cos(theta));
- pixel_multiplier=1+(X^2+Y^2)/c +adj
- (xc,yc) is the centre of the pattern (not the centre of the sensor)
- theta is the rotation angle of the pattern
- a, b, c are more scalars to be fitted
- adj is a small constant, very close to 0
What I discovered for my Nikon Z6 was the following:
- The main effect of the "correction" in the blue channel is to dim the corners by a multiplicative factor of around 0.97
- The main effect of the "correction" in the red channel is to brighten the corners by a multiplicative factor of around 1.01
How does this quadratic function create steps?
Let's take the blue channel for example where my Z6 applies a smooth multiplicative function that reaches approximately 0.97 in the corners.
When this function is applied to image data with a constant integer value 100 across the entire frame then it becomes a series of integer steps after rounding back to integer: 100, 99, 98, 97
This can be clearly seen in the following example where I did exactly that:
    

Click on image for larger version
What may not be totally obvious is the same steps appear even in the presence of image noise. In the following example I applied the same shading correction to integer data with a mean of 100 and standard deviation of 10:
    

Click on image for larger version
The main difference is that the steps have been slightly blurred because of the image noise.
A real example
Here is a real example of an image I took with my Nikon Z6. This was an underexposed image pushed very hard in post-processing:
    

Click on image for larger version
The rings become quite obvious when the blue channel is divided by the green channel:
    

Click on image for larger version
The positions and shapes of the rings do not match the synthetic example above because the sky background in this real example is not a constant level but is affected by optical vignetting.
It was relatively easy to make the same rings appear by opening the image using Photoshop (Adobe Camera Raw) by pushing the exposure then using the white balance tool to click on the background sky and pushing the Vibrance and Saturation sliders. This is further proof that the rings are embedded in the raw image data:
    

Click on image for larger version
The raw Nikon Z6 NEF file can be downloaded here.
Can the rings be repaired?
In theory, if the functional form of the shading correction is known with sufficent accuracy, it's possible to apply its inverse
(i.e. dividing the raw data by the smooth function instead of multiplying the raw data by the function).
This introduces an equal and opposite set of steps in the image that cancel out the original ones.
Using the functional form I derived for my Nikon Z6, I was able to do with with the real world image above. See the "before" and "after" here:
    

Click on image for larger version
However this is not a practical solution unless the parameters of the shading correction are reported somewhere, for example in the EXIF header of the raw file. To the best of my knowledge Nikon does not report the necessary parameters in the EXIF header.
Other Useful Links
DPReview Forum - Reverse Engineering the Rings
Last updated by Mark Shelley: 15 March 2023