Thanks. This is the third test using synthetic images. Here is the methodology:
- I created a synthetic, 4K-sized uniform JPEG image with Pillow library. (image A)
- I introduced Gaussian noise and Poisson noise to the above image, which simulates read noise and photon shot noise, respectively . (image B)
- I downsampled image B to 1920x1080 pixels for viewing on an FHD monitor. (image C)
- I cropped image B to 960x540 pixels. (image D)
- I upsampled image D to 1920x1080 pixels for viewing on an FHD monitor. (image E)
- I measured the mean, standard deviation, and SNR of all the images above.
I found that the SNR of the downsampled image (image C) is higher than that of the cropped-then-upsampled one (image E). The downsampled image is also cleaner when viewed on my 24-inch FHD monitor.
Here are the downsampled and upsampled images (image C and image E, respectively):
Image C
https://LeoCDNendpoint01.azureedge.n...ownsampled.jpg
Image E
https://leostorage02.blob.core.windo..._upsampled.jpg
Here are the measurement results:
Code:
Image A
Mean: 118.0
StdDev: 0.0
SNR: inf
Image B
Mean: 118.0
StdDev: 10.87
SNR: 20.72
Image C
Mean: 117.5
StdDev: 4.87
SNR: 27.65
Image D
Mean: 117.49
StdDev: 10.88
SNR: 20.67
Image E
Mean: 117.49
StdDev: 9.73
SNR: 21.64
And finally here is the code.
Code:
from PIL import Image
from numpy import asarray
import numpy as np
import math
import cv2
""" Add photon shot noise (poisson noise) and read noise (gaussian noise)
to the image
"""
def noisy(noise_typ,image):
if noise_typ == "gauss":
row,col,ch= image.shape
mean = 0
var = 0.1
sigma = var**0.5
gauss = np.random.normal(mean,sigma,(row,col,ch))
gauss = gauss.reshape(row,col,ch)
noisy = image + gauss
return noisy
elif noise_typ == "poisson":
vals = len(np.unique(image))
vals = 2 ** np.ceil(np.log2(vals))
noisy = np.random.poisson(image * vals) / float(vals)
return noisy
""" Calculate image's mean, standard deviation, and SNR """
def mean_std_snr(img):
img_int = asarray(img)
img_float32 = img_int.astype('float32')
img_mean, img_std = img_float32.mean(), img_float32.std()
img_snr = 20*math.log10(img_mean/img_std)
return({'mean': img_mean, 'std': img_std, 'snr': img_snr})
files = [{'name': 'mgrey3840x2160_noiseless.jpg'},
{'name': 'mgrey3840x2160_poisson_gauss.jpg'},
{'name': 'mgrey1920x1080_poisson_gauss_downsampled.jpg'},
{'name': 'mgrey960x540_poisson_gauss_cropped.jpg'},
{'name': 'mgrey1920x1080_poisson_gauss_upsampled.jpg'}]
# Generate a 4K-size JPEG image
img0 = Image.new('RGB', (3840, 2160), color = (118, 118, 118))
img0_pix_float32 = asarray(img0).astype('float32')
files[0].update(mean_std_snr(img0))
img0.save(files[0]['name'], format='JPEG', quality=100)
# Add photon shot noise and read noise to the generated image
# and calculate the mean, stdev and SNR
img1_pix_poisson = noisy('poisson', img0_pix_float32)
img1_pix_poisson_gauss = noisy('gauss', img1_pix_poisson)
files[1].update(mean_std_snr(img1_pix_poisson_gauss))
img1_pix_int = img1_pix_poisson_gauss.astype('uint8')
img1 = Image.fromarray(img1_pix_int)
img1.save(files[1]['name'], format='JPEG', quality=100)
# Downsample image to FHD-size image for viewing on monitor
# and calculate the mean, stdev and SNR
img2 = img1.resize((1920, 1080), resample=Image.LANCZOS)
files[2].update(mean_std_snr(img2))
img2.save(files[2]['name'], format='JPEG', quality=100)
# Crop the original, noise-added image to 960x540 pixels
img3 = img1.crop((1440, 810, 2400, 1350))
files[3].update(mean_std_snr(img3))
img3.save(files[3]['name'], format='JPEG', quality=100)
# Upsample the cropped image to FHD-size for viewing on monitor
# and calculate the mean, stdev and SNR
img4 = img3.resize((1920, 1080), resample=Image.LANCZOS)
files[4].update(mean_std_snr(img4))
img4.save(files[4]['name'], format='JPEG', quality=100)
# Print the result
for file in files:
print(f'Filename: {file["name"]}\r\n' +
f'Mean: {str(round(file["mean"], 2))}\r\n' +
f'StdDev: {str(round(file["std"], 2))}\r\n' +
f'SNR: {str(round(file["snr"], 2))}\r\n\r\n')