从给定随机种子的高斯噪声中恢复图像

Recovering an image from Gaussian Noise given random seed

我一直在尝试通过固定随机种子、保存图像、读取图像并重新生成高斯噪声来添加可重现的高斯噪声,然后 'subtracting' 它恢复原始图像。这是我迄今为止尝试过的(伪)代码:

SEED = 1234
np.random.seed(SEED)

img = cv2.imread(path, -1) # (32,32,3)
noise = np.random.normal(loc=0, scale=20, size=(32,32,3)).astype(np.uint8)

temp = img + noise

# ignore the noise if value exceeds 255 or is below 0
temp = np.where(temp<255, temp, img)
temp = np.where(temp>0, temp, img)

cv2.imwrite(some_path_and_file_name, temp)

然后,我用同样的方法读取带有高斯噪声的图像文件。当我 'ignore' 噪音时,我会跟踪 'ignoring' 发生时的矩阵索引,并使用此信息恢复原始数据:

img = cv2.imread(path_to_noise_img, -1)

SEED = 1234
np.random.seed(SEED)
noise = np.random.normal(loc=0, scale=20, size=(32,32,3)).astype(np.uint8)

temp = img - noise

# flag is the matrix with the indices of 'ignoring'
recovered_img = np.where(flag == 0, temp, img)

cv2.imwrite(some_path_and_file_name, recovered_img)

但是,当我打开两个图像时,它们是不同的。我检查过Gaussian Noise一直都是一样的,感觉读或者写图片文件的时候好像哪里出了问题(发生了某种不可逆的转换)。

但是,由于我是 Python 的新手,所以我无法调试它。

如有任何帮助,我们将不胜感激。谢谢!

编辑
我尝试在不修改任何内容的情况下使用 OpenCV 函数调用保存图像和加载图像,并比较这些值。据我所知,读入的值是不同的。我应该在我的代码中修复什么以防止这种情况发生?

解决方案
下面的代码就像一个魅力(感谢所有评论和答案)。

np.random.seed(SEED)

original = cv2.imread("C:/Users/user/project/1.png", cv2.IMREAD_UNCHANGED)
n = np.random.normal(loc=0, scale=20, size=original.shape).astype(np.int32)

n = original.astype(np.int32) + n.astype(np.int32)


floor = np.zeros_like(n)
ceil = np.zeros_like(n)

floor = np.where(n<255, 0, 1)

t = np.where(n < 255, n, original)

# record when rounding occurs
ceil = np.where(t>0, 0, 1)
arr = np.where(t>0, t, original)

flag = floor + ceil
arr = arr.astype(np.uint8)

cv2.imwrite("C:/Users/user/project/1-1.png", arr.astype(np.uint8))
np.random.seed(SEED)

recover = cv2.imread("C:/Users/user/project/1-1.png", cv2.IMREAD_UNCHANGED).astype(np.int32)
noise = np.random.normal(loc=0, scale=20, size=recover.shape).astype(np.int32)

ans = np.where(flag==0, recover-noise, recover)

assert(ans.astype(np.uint8) == original.astype(np.uint8)).all()

多个问题...

压缩

JPEG 是一种有损压缩,这意味着您肯定不会得到与压缩前完全相同的值。

PNG 是无损的,因此它会为您提供压缩前的准确值。

整数数学

将两个 uint8 值相加会再次得到一个 uint8。

这意味着您的数学计算结果始终在 0..255 范围内。 uint8 值可以 永远不会 为 <0 或 >255。

您的 np.where 检查没有用,因为值 uint8,即使在添加之后它们仍然是 uint8,并且它们可以 never <0 或 >255.

此外,每当您 add/subtract 值时,如果结果超出范围,则必须以某种方式 处理 。 Numpy 只是 将值包裹在 周围,就像通常的整数数学一样。另一个选项是饱和,意思是剪辑。 OpenCV 函数倾向于这样做。您可以使用任一库生成其中任何一个,但要小心。

如果你的代码中有任何饱和数学,你肯定无法减去噪声并恢复原始图像。如果您的代码中只有包装数学,您可以可以通过减去噪声来恢复原始图像。

调试

您丢弃了太多信息,减少了对“ 它们是否完全相等?”的回答。

你应该使用一个小图像,3x3 像素或其他东西,然后 查看 值,当你 打印 那些 numpy 数组时.

演示

import numpy as np
import cv2 as cv

img = cv.imread("image.png", cv.IMREAD_UNCHANGED)

SEED = 1234
np.random.seed(SEED)
noise = np.random.normal(loc=0, scale=20, size=img.shape).astype(np.uint8)

img_with_noise = img + noise # this will wrap around

cv.imwrite("image_with_noise.png", img_with_noise)
import numpy as np
import cv2 as cv

img = cv.imread("image.png", cv.IMREAD_UNCHANGED)
img_with_noise = cv.imread("image_with_noise.png", cv.IMREAD_UNCHANGED)

# generate noise the exact same way
SEED = 1234
np.random.seed(SEED)
noise = np.random.normal(loc=0, scale=20, size=img.shape).astype(np.uint8)

img_recovered = img_with_noise - noise # wrapping around again, backwards

cv.imwrite("image_recovered.png", img_recovered)

# images should be equal in all values of all pixels
assert (img_recovered == img).all()