ericmjonas / pybm3d

Python wrapper around bm3d
GNU General Public License v3.0
133 stars 29 forks source link

Clip the output of run_bm3d_wrap before the type conversion. #13

Open fvdnabee opened 5 years ago

fvdnabee commented 5 years ago

The type conversion of the output of run_bm3d_wrap from np.float32 to the input dtype leads to numeric overflow in the output when the input dtype is uint8. It appears the author has anticipated this overflow, but the clipping happens after the conversion (i.e. when the overflow has already happened).

I included two images of the denoised output for the following script before and after the patch to bm3d.pyx.

import numpy as np
import skimage.data
from skimage.measure import compare_psnr
from skimage import io

import pybm3d

noise_std_dev = 20
img = skimage.data.astronaut()
noise = np.random.normal(scale=noise_std_dev,
                         size=img.shape).astype(np.int8)

assert img.dtype == np.uint8
noisy_img = (img.astype(np.int16) + noise).clip(0, 255).astype(np.uint8)

out = pybm3d.bm3d.bm3d(noisy_img, noise_std_dev)

noise_psnr = compare_psnr(img, noisy_img)
out_psnr = compare_psnr(img, out)

print("PSNR of noisy image: ", noise_psnr)
print("PSNR of reconstructed image: ", out_psnr)
io.imsave(fname='/tmp/astronaut_pybm3d_{}-denoised.png'.format(noise_std_dev), arr=out)
io.imsave(fname='/tmp/astronaut_pybm3d_{}.png'.format(noise_std_dev), arr=noisy_img)

Before patch: astronaut_pybm3d_20-denoised-beforepatch After patch: astronaut_pybm3d_20-denoised-afterpatch

zaccharieramzi commented 5 years ago

Isn't it a bit weird to clip the values after adding the noise?

I understand it's because you want to keep it in the RGB space of [0, 255] but it then makes the noise not gaussian anymore.