This report demonstrates the
antspymm.blind_image_assessment
function for performing
automated, no-reference Quality Control on medical images. We will first
simulate a set of images with varying characteristics (noise,
smoothness, etc.) and then run the QC analysis on them. The results are
presented in interactive and static tables.
This script is designed to be fully portable and self-contained. It creates temporary files in a system-appropriate location and cleans up after itself upon completion.
First, we set up a Python environment and generate several test images. A temporary directory is created to store these images, ensuring the script does not leave files on the system.
# --- Python Environment and Image Simulation ---
import siq
import ants
import antspymm
import os
import tempfile
import shutil
import pandas as pd
# Create a single, system-agnostic temporary directory for all output files.
# This makes the script portable (works on Linux, macOS, Windows).
temp_dir = tempfile.mkdtemp()
print(f"Using temporary directory: {temp_dir}")
## Using temporary directory: /tmp/tmp0lt08_m8
# List to hold the paths of all generated files for processing.
generated_files = []
# --- Image 1: Baseline Simulated Image ---
print("Simulating baseline image...")
## Simulating baseline image...
eximg = siq.simulate_image()
exfn_base = os.path.join(temp_dir, 'baseline.nii.gz')
ants.image_write(eximg, exfn_base)
ants.plot( eximg )
generated_files.append(exfn_base)
# --- Image 2: Additive Gaussian Noise ---
print("Simulating image with Gaussian noise...")
## Simulating image with Gaussian noise...
img_gauss = siq.simulate_image().add_noise_to_image('additivegaussian', [0, 1])
exfn_gauss = os.path.join(temp_dir, 'noise_gaussian.nii.gz')
ants.image_write(img_gauss, exfn_gauss)
ants.plot( img_gauss )
generated_files.append(exfn_gauss)
# --- Image 3: Salt & Pepper Noise ---
print("Simulating image with Salt & Pepper noise...")
## Simulating image with Salt & Pepper noise...
img_sp = siq.simulate_image()
img_sp = ants.add_noise_to_image(img_sp, 'saltandpepper', [0.2, -1, 1])
exfn_sp = os.path.join(temp_dir, 'noise_saltpepper.nii.gz')
ants.image_write(img_sp, exfn_sp)
ants.plot( img_sp )
generated_files.append(exfn_sp)
# --- Image 4: Smoothed Image ---
print("Simulating a smoothed image...")
## Simulating a smoothed image...
img_smooth = siq.simulate_image().smooth_image(3)
exfn_smooth = os.path.join(temp_dir, 'smooth.nii.gz')
ants.image_write(img_smooth, exfn_smooth)
ants.plot( img_smooth )
generated_files.append(exfn_smooth)
# --- Image 5: Time Series Image ---
print("Simulating a 4D time-series image...")
## Simulating a 4D time-series image...
img_ts = siq.simulate_image([16, 16, 16, 4])
exfn_ts = os.path.join(temp_dir, 'timeseries.nii.gz')
ants.image_write(img_ts, exfn_ts)
generated_files.append(exfn_ts)
print(f"\nGenerated {len(generated_files)} files for analysis.")
##
## Generated 5 files for analysis.
Now we loop through the generated images and run
blind_image_assessment
on each. The results are collected
into a single Pandas DataFrame.
# --- Batch QC Processing ---
# A more efficient way to build a DataFrame is to append dictionaries to a
# list and then call the DataFrame constructor once.
qc_results = []
for f in generated_files:
print(f"Running QC on: {os.path.basename(f)}...")
myqc = antspymm.blind_image_assessment(f)
qc_results.append(myqc)
## Running QC on: baseline.nii.gz...
## Running QC on: noise_gaussian.nii.gz...
## Running QC on: noise_saltpepper.nii.gz...
## Running QC on: smooth.nii.gz...
## Running QC on: timeseries.nii.gz...
# Concatenate all results into a single DataFrame.
qcdf = pd.concat(qc_results, axis=0)
# The 'qcdf' DataFrame is now automatically available to R via reticulate as py$qcdf
# No need to write to a temporary CSV file.
The following tables display the QC metrics for all simulated images.
This table is sortable and searchable, which is useful for exploring the results dynamically.
## Warning in py_to_r.pandas.core.frame.DataFrame(x): index contains duplicated
## values: row names not set
This is a clean, static view of the same data, suitable for printing or including in a non-interactive document.
filename | dimensionality | noise | snr | cnr | psnr | ssim | mi | reflection_err | EVR | msk_vol | spc0 | spc1 | spc2 | org0 | org1 | org2 | dimx | dimy | dimz | dimt | slice | modality | mriseries | mrimfg | mrimodel | mriMagneticFieldStrength | mriSAR | mriPixelBandwidth | mriPixelBandwidthPE | dti_bvalueMax | dti_bvecnorm |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
baseline | 3 | 0.03687 | 25.65 | 20.98 | 23.16 | 0.9421 | -1.023 | 0.1575 | 0.6699 | 32494 | 1 | 1 | 1 | 0 | 0 | 0 | 32 | 32 | 32 | 1 | 0 | unknown | NA | NA | NA | NA | NA | NA | NA | NA | NA |
noise_gaussian | 3 | 0.09391 | NA | NA | 17.13 | 0.6415 | -0.2906 | 0.137 | NA | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 32 | 32 | 32 | 1 | 0 | unknown | NA | NA | NA | NA | NA | NA | NA | NA | NA |
noise_saltpepper | 3 | 0.07676 | Inf | Inf | 14.12 | 0.5467 | -0.7279 | 0.2088 | 0.834 | 32767 | 1 | 1 | 1 | 0 | 0 | 0 | 32 | 32 | 32 | 1 | 0 | unknown | NA | NA | NA | NA | NA | NA | NA | NA | NA |
smooth | 3 | 0.03274 | 13.09 | 11.16 | 34.76 | 0.9956 | -1.893 | 0.2581 | 0.02344 | 31356 | 1 | 1 | 1 | 0 | 0 | 0 | 32 | 32 | 32 | 1 | 0 | unknown | NA | NA | NA | NA | NA | NA | NA | NA | NA |
timeseries | 4 | 0.08569 | 32222380 | 22635400 | 21.9 | 0.9365 | -1.184 | 0.1029 | 0.6025 | 4052 | 1 | 1 | 1 | 0 | 0 | 0 | 16 | 16 | 16 | 4 | 0 | unknown | NA | NA | NA | NA | NA | NA | NA | NA | NA |
The metrics reported in the tables above are defined as follows:
fn
noise
snr
mean(fg) / std(bg)
.
cnr
(mean(fg) - mean(bg)) / std(bg)
.
psnr
ssim
mi
reflection_error
EVR
msk_vol
spc*
spcx
, spcy
,
spcz
).
org*
dim*
slice
modality
mriseries
, mrimfg
,
mrimodel