# how to balance the illumination( Correction of Intensity Inhomogeneity) in itk Montage

**URL:** https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811
**Category:** Beginner Questions
**Tags:** python, simpleitk
**Created:** [February 17, 2022, 12:54pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811 "2022-02-17T12:54:24Z")
**Posts on this page:** 18
**Page:** 1

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 17, 2022, 12:54pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/1 "2022-02-17T12:54:24Z")

</div>

hello, i use itk stitch microscope tiles to whole slide image, it works really good, yet there are some grid shadow in the blending area like this  
 ![image](https://discourse.itk.org/uploads/default/original/2X/3/30909472e257170421242f618be650952ff33a02.jpeg)  
i looked into it, it’s caused by the uneven illumination, this [article](https://link.springer.com/article/10.1007/s40192-021-00202-x#:~:text=N4BiasFieldCorrectionImageFilter) mention that N4BiasFieldCorrectionImageFilter can fix this  
according to N4BiasFieldCorrectionImageFilter [doc](https://simpleitk.readthedocs.io/en/master/link_N4BiasFieldCorrection_docs.html)  
here is my code

```auto
import SimpleITK as sitk
import sys
import os

inputImage = sitk.ReadImage(image_path, sitk.sitkFloat32)
image = inputImage

maskImage = sitk.OtsuThreshold(inputImage, 0, 1, 200)
image = sitk.Shrink(inputImage,
                         [int(4)] * inputImage.GetDimension())
maskImage = sitk.Shrink(maskImage,
                        [int(4)] * inputImage.GetDimension())

corrector = sitk.N4BiasFieldCorrectionImageFilter()

numberFittingLevels = 4

corrector.SetMaximumNumberOfIterations([int(4)]
                                       * numberFittingLevels)

corrected_image = corrector.Execute(image, maskImage)
log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage)

corrected_image_full_resolution = inputImage / sitk.Exp( log_bias_field )
corrected_image_full_resolution = sitk.Cast(corrected_image_full_resolution, sitk.sitkUInt8)

output_file = "005_015_corrected.jpg"
sitk.WriteImage(corrected_image_full_resolution, output_file)

```

the filter worked, yet the output image is gray, i want to cast the image to RGB, should i recompile SimpleITK to support cast the image to RGB? or is there any steps i was missing, it does not need a cast actually  
here is my output image

 ![image](https://discourse.itk.org/uploads/default/original/2X/d/d8acda6708b186cfcc00cad3172e8ed74774aec4.jpeg)

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [February 17, 2022, 2:19pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/2 "2022-02-17T14:19:03Z")

</div>

Hello @allen,

The reason the image is gray is that when you read it you forced it to be gray by selecting the pixel type to be sitkFloat32.

The bias field correction works on a scalar (gray) image, so estimate it on the gray image as you’ve done and then apply to the color image (I’m not sure the bias field model holds in this case, but worth a try):

```auto
color_input_image = sitk.ReadImage(image_path)
bias_field = sitk.Exp( log_bias_field )
corrected_image_full_resolution = sitk.Compose([sitk.VectorIndexSelectionCast(color_input_image, i, sitk.sitkFloat32)/bias_field for i in range(3)])
corrected_image_full_resolution = sitk.Cast(corrected_image_full_resolution, sitk.sitkVectorUInt8)
output_file = "005_015_corrected.jpg"
sitk.WriteImage(corrected_image_full_resolution, output_file)

```

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 17, 2022, 2:58pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/3 "2022-02-17T14:58:50Z")

</div>

The similar trick is used in the [C++ example](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/master/examples/CompleteMontage.cxx) as well: compute bias field on the grayscale image, and apply it to color image.

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 18, 2022, 3:01am UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/4 "2022-02-18T03:01:46Z")

</div>

thanks zivy, the output image is RGB now, yet some of their color changed seriously.  
the input images  
 ![image](https://discourse.itk.org/uploads/default/original/2X/8/8b064a3ac060d3153afb49748fd3e83dcf09204e.png)  
output images  
 ![image](https://discourse.itk.org/uploads/default/original/2X/e/e861e8040a6b81fd3f54605dc86207c3b2968f05.png)  
increase the MaximumNumberOfIterations will makes it worse  
 ![image](https://discourse.itk.org/uploads/default/original/2X/b/b70f4b5d2f1b16000bdcd051e26ac6f0a7df7c96.png)  
yet it still happen even if the MaximumNumberOfIterations is set to [1, 1, 1, 1]  
i think it is caused by the bias\_field, if the split bias\_field removed, the compose return a normal image same as the input  
is there anyway to fix this in N4BiasFieldCorrectionImageFilter  
here is the code

```auto
def N4BiasFieldCorrection(image_path, output_file):
    inputImage = sitk.ReadImage(image_path, sitk.sitkFloat32)
    image = inputImage
    color_input_image = sitk.ReadImage(image_path)
    maskImage = sitk.OtsuThreshold(inputImage, 0, 1, 200)
    image = sitk.Shrink(inputImage,
                             [int(16)] * inputImage.GetDimension())
    maskImage = sitk.Shrink(maskImage,
                            [int(16)] * inputImage.GetDimension())

    corrector = sitk.N4BiasFieldCorrectionImageFilter()
    corrector.SetMaximumNumberOfIterations([1, 1, 1, 1])
    corrected_image = corrector.Execute(image, maskImage)
# corrected_image = corrector.Execute(image)

    log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage)
    bias_field = sitk.Exp(log_bias_field)
    corrected_image_full_resolution = sitk.Compose([sitk.VectorIndexSelectionCast(color_input_image, i, sitk.sitkFloat32)/bias_field for i in range(3)])
    corrected_image_full_resolution = sitk.Cast(corrected_image_full_resolution, sitk.sitkVectorUInt8)

    sitk.WriteImage(corrected_image_full_resolution, output_file)

```

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 18, 2022, 7:00am UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/5 "2022-02-18T07:00:29Z")

</div>

sir, i wonder if there is a CompleteMontage.py or py version CorrectBias, i try to translate it

```auto
image = itk.imread(image_path)
  
region = image.GetLargestPossibleRegion()
bspliner = itk.BSplineControlPointImageFilter.New()
bspliner.SetInput(log_bias_field)
bspliner.SetSplineOrder(splineOrder)
bspliner.SetSize(region.GetSize())
bspliner.SetOrigin(image.GetOrigin())
bspliner.SetDirection(image.GetDirection())
bspliner.SetSpacing(image.GetSpacing())
bspliner.Update()

```

its hard for me to fix the error, like itk does not have BSplineControlPointImageFilter attribute

 ![image](https://discourse.itk.org/uploads/default/original/2X/4/416587e3d91cb5ca09bda02e9857c6139d027d67.png)

---

<div class="post-metadata">

### Author: ![zivy](https://discourse.itk.org/user_avatar/discourse.itk.org/zivy/32/1726_2.png) [@zivy](https://discourse.itk.org/u/zivy)
#### Post date: [February 18, 2022, 2:15pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/6 "2022-02-18T14:15:13Z")

</div>

Hello @allen,

When you read the color image the code is implicitly forcing it to be single channel:

```auto
inputImage = sitk.ReadImage(image_path, sitk.sitkFloat32)

```

Instead you need to read it as color:

```auto
inputImage = sitk.ReadImage(image_path)

```

Then convert it to grayscale based on your knowledge of the color representation. Then do the bias field correction on this grayscale image and apply it to the original three channel image.

For details on various color to grayscale conversion options [see wikipedia](https://en.wikipedia.org/wiki/Grayscale). Code for converting from sRGB with gamma correction is available in [this Jupyter notebook](https://github.com/InsightSoftwareConsortium/SimpleITK-Notebooks/blob/6879e6ad035d92b98b8b93aa407262b6675e574c/Python/03_Image_Details.ipynb), function is called `srgb2gray`.

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 18, 2022, 2:16pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/7 "2022-02-18T14:16:37Z")

</div>

> [@allen](#):
>
> BSplineControlPointImageFilter

This is rarely used, so it is no surprise to me that it is not wrapped. You could take a look at the [wrappings of the other filters](https://github.com/InsightSoftwareConsortium/ITK/tree/master/Modules/Filtering/ImageGrid/wrapping) in that module, and add one for `BSplineControlPointImageFilter` yourself.

> [@allen](#):
>
> if there is a CompleteMontage.py

No, there isn’t. [SimpleMontage.py](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/master/examples/SimpleMontage.py) is the only Python example we have for it.

Does C++ version work for you? I think it is easier to check that than write Python version of it. You need to point CMake at the [examples’ CMakeLists](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/master/examples/CMakeLists.txt) to configure and compile it.

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 20, 2022, 2:24pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/8 "2022-02-20T14:24:05Z")

</div>

hello @zivy sor for the idiot question, does “apply it to the original three channel image” means that apply the correction steps to all the 3 channels like below  
I = (  
I \* sitk.Cast(I \<= 0.0031308, sitk.sitkFloat32) \* 12.92  
+ I \*\* (1 / 2.4) \* sitk.Cast(I \> 0.0031308, sitk.sitkFloat32) \* 1.055  
- 0.055  
)

```auto
def gamma_correction(I):
    # nonlinear gamma correction
    I = (
        I * sitk.Cast(I <= 0.0031308, sitk.sitkFloat32) * 12.92
        + I ** (1 / 2.4) * sitk.Cast(I > 0.0031308, sitk.sitkFloat32) * 1.055
        - 0.055
    )
    return sitk.Cast(sitk.RescaleIntensity(I), sitk.sitkUInt8)

channels = [
    sitk.VectorIndexSelectionCast(color_input_image, i, sitk.sitkFloat32)
    for i in range(color_input_image.GetNumberOfComponentsPerPixel())
]

res = [gamma_correction(c) for c in channels]

```

and then compose 3 channel to rgb

```auto
corrected_image_full_resolution = sitk.Compose(res)
corrected_image_full_resolution = sitk.Cast(corrected_image_full_resolution, sitk.sitkVectorUInt8)

```

this did not work to even the illumination

or it means that just replace the sitk.ReadImage(image\_path, sitk.sitkFloat32) to srgb2gray result  
like this below

```auto
def srgb2gray(image):
    # Convert sRGB image to gray scale and rescale results to [0,255]
    channels = [
        sitk.VectorIndexSelectionCast(image, i, sitk.sitkFloat32)
        for i in range(image.GetNumberOfComponentsPerPixel())
    ]
    # linear mapping
    I = 1 / 255.0 * (0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2])
    # nonlinear gamma correction
    I = (
        I * sitk.Cast(I <= 0.0031308, sitk.sitkFloat32) * 12.92
        + I ** (1 / 2.4) * sitk.Cast(I > 0.0031308, sitk.sitkFloat32) * 1.055
        - 0.055
    )
    # i change sitkUInt8 to sitkFloat32 as the N4BiasFieldCorrectionImageFilter didn't support sitkUInt8 
    return sitk.Cast(sitk.RescaleIntensity(I), sitk.sitkFloat32)

def N4BiasFieldCorrection(image_path, output_file):
    color_input_image = sitk.ReadImage(image_path)
    inputImage = srgb2gray(color_input_image)
    image = inputImage
    maskImage = sitk.OtsuThreshold(inputImage, 0, 1, 200)
    image = sitk.Shrink(inputImage,
                             [int(16)] * inputImage.GetDimension())
    maskImage = sitk.Shrink(maskImage,
                            [int(16)] * inputImage.GetDimension())

    corrector = sitk.N4BiasFieldCorrectionImageFilter()
    corrector.SetSplineOrder(3)
    corrector.SetWienerFilterNoise(0.01)
    corrector.SetBiasFieldFullWidthAtHalfMaximum(0.15)
    corrector.SetConvergenceThreshold(0.00001)
    corrector.SetMaximumNumberOfIterations([1, 1, 1, 1])
    corrected_image = corrector.Execute(image , maskImage)

    log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage)
    bias_field = sitk.Exp(log_bias_field)
    corrected_image_full_resolution = sitk.Compose([sitk.VectorIndexSelectionCast(color_input_image, i, sitk.sitkFloat32)/bias_field for i in range(3)])
    corrected_image_full_resolution = sitk.Cast(corrected_image_full_resolution, sitk.sitkVectorUInt8)

    sitk.WriteImage(corrected_image_full_resolution, output_file)

```

the color changed in its result too, and it does gamma correction twice i think

if both the 2 version above not what you mean, how to apply it to the original three channel image, i know rgb image can trans to hsv image, the v channel represent the intensity, but grey image only has one channel, how can i extract its intensity features and apply it to the original three channel

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 21, 2022, 6:13am UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/9 "2022-02-21T06:13:15Z")

</div>

hello sir, i build CompleteMontage.cxx to exe file by cmake

 ![image](https://discourse.itk.org/uploads/default/original/2X/e/eac8b06ddca4cb06000002d631fa344c98ef06f5.png)  
here is its output  
 ![image](https://discourse.itk.org/uploads/default/original/2X/2/2b69a50c8cd3da736d669482d8544d31e479b968.jpeg)  
the left one is gen set doBiasCorrection doDenoising to false

```auto
CompleteMontage.exe E:\\projects\\ITKMontage\\examples\\SampleData_Balance E:\\projects\\ITKMontage\\examples\\output_balance montage.tiff 0 0

```

right one is gen set doBiasCorrection doDenoising to true

```auto
CompleteMontage.exe E:\\projects\\ITKMontage\\examples\\SampleData_Balance E:\\projects\\ITKMontage\\examples\\output_balance montage.tiff

```

the uneven intensity is much better, but the color changed(distortion?) in some tiles just like the sitk.N4BiasFieldCorrectionImageFilter result as upload above  
logs: [CompleteMontage.log](https://discourse.itk.org/uploads/short-url/6TLNtkIvCHYT9h05mSUafMiVmV.log) (27.2 KB)

one of the intensity flat result

 ![image](https://discourse.itk.org/uploads/default/original/2X/8/88dc4efc330ea3ca50cb3d4ce53b4b2b439f6cde.jpeg)

The total size of tiles is relatively large, i will upload the original data and config file if needed to reproduce

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 21, 2022, 1:28pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/10 "2022-02-21T13:28:04Z")

</div>

At first I thought, since the tiles are large, the intensities might be overflowing the chosen types. But the accumulate types are big enough.:

> <https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L560>

Can you examine these files, and see whether it is the bias correction which corrupts/saturates the intensities:

> <https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L418-L420>

If not, it must be invoked from here:

> <https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L472>

and the problem being in:

> <https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/include/itkTileMergeImageFilter.hxx#L562-L573>

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 21, 2022, 4:41pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/11 "2022-02-21T16:41:15Z")

</div>

i would like to examine it sir, yet don’t know how.  
it’s the same result if set doBiasCorrection to true and doDenoising to false, is this enough to make sure bias correction corrupts/saturates the intensities

 ![image](https://discourse.itk.org/uploads/default/original/2X/4/43f4f475fb843deb8fdcfb50704f71e0cbef5af2.jpeg)  
left is only bias correction, right is bias correction and denoise  
if its problem in itkTileMergeImageFilter, do you have any idea how to fix this

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 21, 2022, 4:43pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/12 "2022-02-21T16:43:17Z")

</div>

I meant look at the files named `baseFileName + "-flat" + fileNameExt` in the `outputPath`. Do these files have the problem, or is the problem present only in the final, stitched image?

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 21, 2022, 4:50pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/13 "2022-02-21T16:50:17Z")

</div>

the -flat file have a problem it self before stitch

 ![image](https://discourse.itk.org/uploads/default/original/2X/b/be125faf8b672043329fbddff64c6be21b93c73c.jpeg)

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 21, 2022, 4:55pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/14 "2022-02-21T16:55:42Z")

</div>

here is some original files can reproduce it

> **[captures.zip](https://drive.google.com/file/d/1XIhghDuJXewh8CuMcIFiB7Y5JSgyucqk/view?usp=sharing)**
>
> Google Drive file.

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 21, 2022, 5:02pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/15 "2022-02-21T17:02:57Z")

</div>

The problem is in the `CorrectBias` function:

> <https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L242-L319>

Probably just a few formulas need to be adjusted there. I don’t have time to work on it this week (paper deadline March 1st). Lines you should pay attention to, and experiment with: [269](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L269), [297](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L297), [305](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L305) and [312](https://github.com/InsightSoftwareConsortium/ITKMontage/blob/v0.7.3/examples/CompleteMontage.cxx#L312).

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 21, 2022, 5:07pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/16 "2022-02-21T17:07:46Z")

</div>

it’s all right. thanks for replying so many out of your busy schedule. I will try it tommorrow 😀

---

<div class="post-metadata">

### Author: ![allen](https://discourse.itk.org/letter_avatar_proxy/v4/letter/a/bbce88/32.png) [@allen](https://discourse.itk.org/u/allen)
#### Post date: [February 25, 2022, 1:19pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/17 "2022-02-25T13:19:22Z")

</div>

i tried to change the bias weight, it didn’t help. i found [another way](https://github.com/libvips/pyvips/issues/110) to balance the illumination by pyvips:

1、capture a white background with the camera slightly defocussed  
2、scale pixels by 1/white

```auto
white = pyvips.Image.new_from_file(white_path)
vignette = (white.max() * 0.7 + white.min() * 0.3) / white

cur_frame = pyvips.Image.new_from_file(filename)
cur_frame = (cur_frame * vignette).cast("uchar")

```

this method is really fast, less than 10ms per frame, and the result is acceptable for me  
the test output

 ![63497ca843489caa88317d365566cb3](https://discourse.itk.org/uploads/default/original/2X/9/9f506c87ef377bc9017c7462c436c01afce5b13e.jpeg)  
left is processed by adjust\_gamma, its better than the original images but grid shadow are still obvious

```auto
def adjust_gamma(image, gamma=1.2):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")

    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

```

right one is processed by pyvips vignette

thank you for all your help

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [February 25, 2022, 2:27pm UTC](https://discourse.itk.org/t/how-to-balance-the-illumination-correction-of-intensity-inhomogeneity-in-itk-montage/4811/18 "2022-02-25T14:27:00Z")

</div>

The generic inhomogeneity correction is a better-than-nothing cure when when other calibration methods are unavailable. Your solution seems good and fast.
