N4 Bias Field Correction
Overview
The N4 bias field correction algorithm is a popular method for correcting low frequency intensity non-uniformity present in MRI image data known as a bias or gain field. The method has also been successfully applied as flat-field correction in microscopy data. This method assumes a simple parametric model and does not require tissue classification.
This example demonstrates how to use the SimpleITK N4BiasFieldCorrectionImageFilter class. This filter has one required input image which is affected by a bias field we wish to correct. The primary input is required to have a “real” pixel type of either sitkFloat32 or sitkFloat64. Additionally, there is an optional “MaskImage” input which specifies which pixels are used to estimate the bias-field and suppress pixels close to zero. It is recommended that the mask image follows the common conventions for masked images, which is being of pixel type sitkUint8 and having values of 0 and 1 representing the mask. Additionally, the mask image and the main input image must occupy the same physical space to ensure pixel to pixel correspondence.
Some basic parameters for using this algorithm are parsed from the command line in this example. The shrink factor is used to reduce the size and complexity of the image. The N4 algorithm uses a multi-scale optimization approach to compute the bias field. The SetMaximumNumberOfIterations method takes an array used to set the limit of iterations per resolution level, thereby setting both the iterations and the number of scales ( from the length of the array ). The output of the filter is the bias corrected input image.
Example Run
Running the Python code with the following inputs:
main("BrainProtonDensitySlice.png", "N4_corrected.nrrd", 1)
produces the images below.
Input Images
Output Images
Code
#include <SimpleITK.h>
#include <iostream>
#include <stdlib.h>
namespace sitk = itk::simple;
int
main(int argc, char * argv[])
{
if (argc < 2)
{
std::cerr << "Usage: N4BiasFieldCorrection inputImage outputImage";
std::cerr << " [shrinkFactor] [maskImage] [numberOfIterations]";
std::cerr << " [numberOfFittingLevels]\n";
return 1;
}
sitk::Image inputImage = sitk::ReadImage(argv[1], sitk::sitkFloat32);
sitk::Image image = inputImage;
sitk::Image maskImage;
if (argc > 4)
{
maskImage = sitk::ReadImage(argv[4], sitk::sitkUInt8);
}
else
{
maskImage = sitk::OtsuThreshold(image, 0, 1, 200);
}
unsigned int shrinkFactor = 1;
if (argc > 3)
{
shrinkFactor = atoi(argv[3]);
std::vector<unsigned int> shrink(inputImage.GetDimension(), shrinkFactor);
image = sitk::Shrink(inputImage, shrink);
maskImage = sitk::Shrink(maskImage, shrink);
}
sitk::N4BiasFieldCorrectionImageFilter corrector;
unsigned int numFittingLevels = 4;
if (argc > 6)
{
numFittingLevels = atoi(argv[6]);
}
if (argc > 5)
{
unsigned int it = atoi(argv[5]);
std::vector<unsigned int> iterations(numFittingLevels, it);
corrector.SetMaximumNumberOfIterations(iterations);
}
sitk::Image corrected_image = corrector.Execute(image, maskImage);
sitk::Image log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage);
sitk::Image corrected_image_full_resolution = sitk::Divide(inputImage, sitk::Exp(log_bias_field));
sitk::WriteImage(corrected_image_full_resolution, argv[2]);
if (shrinkFactor > 1)
{
sitk::WriteImage(corrected_image, "CXX-Example-N4BiasFieldCorrection-shrunk.nrrd");
}
if (getenv("SITK_NOSHOW") == NULL)
sitk::Show(corrected_image, "N4 Corrected");
return 0;
}
using System;
using itk.simple;
using sitk = itk.simple.SimpleITK;
namespace itk.simple.examples {
class N4BiasFieldCorrection {
static void Main(string[] args) {
try {
if (args.Length < 2) {
Console.WriteLine("Usage: N4BiasFieldCorrection inputImage outputImage"
+ " [shrinkFactor] [maskImage] [numberOfIterations]"
+ " [numberOfFittingLevels]\n");
return;
}
// Read input image
Image inputImage = sitk.ReadImage(args[0], PixelIDValueEnum.sitkFloat32);
Image image = inputImage;
Image maskImage;
if (args.Length>3) {
maskImage = sitk.ReadImage(args[3], PixelIDValueEnum.sitkUInt8);
} else {
maskImage = sitk.OtsuThreshold(image, 0, 1, 200);
}
uint shrinkFactor = 1;
if (args.Length>2) {
shrinkFactor = UInt32.Parse(args[2]);
uint[] s_array = new uint[image.GetDimension()];
for (uint i=0; i<image.GetDimension(); i++) {
s_array[i] = shrinkFactor;
}
VectorUInt32 shrink = new VectorUInt32(s_array);
image = sitk.Shrink(inputImage, shrink);
maskImage = sitk.Shrink(maskImage, shrink);
}
N4BiasFieldCorrectionImageFilter corrector
= new N4BiasFieldCorrectionImageFilter();
uint numFittingLevels = 4;
if (args.Length>5) {
numFittingLevels = UInt32.Parse(args[5]);
}
if (args.Length>4) {
uint it = UInt32.Parse(args[4]);
uint[] it_array = new uint[numFittingLevels];
for (uint i=0; i<numFittingLevels; i++) {
it_array[i] = it;
}
VectorUInt32 iterations = new VectorUInt32(it_array);
corrector.SetMaximumNumberOfIterations(iterations);
}
Image corrected_image = corrector.Execute(image, maskImage);
Image log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage);
Image corrected_image_full_resolution = sitk.Divide(inputImage, sitk.Exp(log_bias_field));
sitk.WriteImage(corrected_image_full_resolution, args[1]);
if (shrinkFactor>1) {
sitk.WriteImage(corrected_image, "CSharp-Example-N4BiasFieldCorrection-shrunk.nrrd");
}
if (Environment.GetEnvironmentVariable("SITK_NOSHOW") == null)
SimpleITK.Show(corrected_image, "N4 Corrected");
} catch (Exception ex) {
Console.WriteLine(ex);
}
}
}
}
#!/usr/bin/env python
""" A SimpleITK example that demonstrates how to apply N4BiasFieldImageFilter
to an image. """
import sys
import os
import SimpleITK as sitk
def main(args):
""" A SimpleITK example that demonstrates how to apply N4BiasFieldImageFilter
to an image. """
if len(args) < 2:
print(
"Usage: N4BiasFieldCorrection inputImage "
+ "outputImage [shrinkFactor] [maskImage] [numberOfIterations] "
+ "[numberOfFittingLevels]"
)
sys.exit(1)
inputImage = sitk.ReadImage(args[1], sitk.sitkFloat32)
image = inputImage
if len(args) > 4:
maskImage = sitk.ReadImage(args[4], sitk.sitkUInt8)
else:
maskImage = sitk.OtsuThreshold(inputImage, 0, 1, 200)
shrinkFactor = 1
if len(args) > 3:
shrinkFactor = int(args[3])
if shrinkFactor > 1:
image = sitk.Shrink(inputImage, [shrinkFactor] * inputImage.GetDimension())
maskImage = sitk.Shrink(
maskImage, [shrinkFactor] * inputImage.GetDimension()
)
corrector = sitk.N4BiasFieldCorrectionImageFilter()
numberFittingLevels = 4
if len(args) > 6:
numberFittingLevels = int(args[6])
if len(args) > 5:
corrector.SetMaximumNumberOfIterations([int(args[5])] * numberFittingLevels)
corrected_image = corrector.Execute(image, maskImage)
log_bias_field = corrector.GetLogBiasFieldAsImage(inputImage)
corrected_image_full_resolution = inputImage / sitk.Exp(log_bias_field)
sitk.WriteImage(corrected_image_full_resolution, args[2])
if shrinkFactor > 1:
sitk.WriteImage(
corrected_image, "Python-Example-N4BiasFieldCorrection-shrunk.nrrd"
)
return_images = {
"input_image": inputImage,
"mask_image": maskImage,
"log_bias_field": log_bias_field,
"corrected_image": corrected_image,
}
# Run with:
#
# Rscript --vanilla N4BiasFieldCorrection.R inputImage, outputImage, shrinkFactor, maskImage, numberOfIterations, numberOfFittingLevels
#
library(SimpleITK)
args <- commandArgs( TRUE )
if (length(args) < 2) {
stop("At least 2 arguments expected - inputImage, outputImage, [shrinkFactor], ",
"[maskImage], [numberOfIterations], [numberOfFittingLevels]")
}
inputImage <- ReadImage(args[[1]], 'sitkFloat32')
image <- inputImage
if (length( args ) > 4) {
maskImage <- ReadImage( args[[4]], 'sitkUint8' )
} else {
maskImage <- OtsuThreshold( inputImage, 0, 1, 200 )
}
shrinkFactor = 1
if (length( args ) > 3) {
shrinkFactor = strtoi(args[3])
image <- Shrink( inputImage, rep(shrinkFactor, inputImage$GetDimension()) )
maskImage <- Shrink( maskImage, rep(shrinkFactor, inputImage$GetDimension()) )
}
inputImage <- Cast( inputImage, 'sitkFloat32' )
corrector <- N4BiasFieldCorrectionImageFilter()
numberFittingLevels <- 4
if (length ( args ) > 6) {
numberFittingLevels <- strtoi( args[[6]] )
}
if (length ( args ) > 5) {
corrector$SetMaximumNumberOfIterations( rep(strtoi( args[[5]], numberFittingLevels)) )
}
correctedImage <- corrector$Execute( image, maskImage )
logBiasField <- corrector$GetLogBiasFieldAsImage(inputImage)
correctedImageFullResolution <- inputImage / Exp( logBiasField )
WriteImage(correctedImageFullResolution, args[[2]])
if (shrinkFactor > 1) {
WriteImage(correctedImage, "R-Example-N4BiasFieldCorrection-shrunk.nrrd")
}
if(Sys.getenv("SITK_NOSHOW") == "") {
Show(correctedImage, "N4 Corrected")
}