首页 > 解决方案 > Python为图像中断PNG添加噪声

问题描述

我正在尝试在 Python 3 中创建一个用于 Web 应用程序的图像系统。这个想法是从磁盘加载图像并为其添加一些随机噪声。当我尝试这个时,我得到一个看起来完全随机的图像,与原始图像不同:

测试1 在此处输入图像描述

import cv2
import numpy as np
from skimage.util import random_noise
from random import randint
from pathlib import Path
from PIL import Image
import io


image_files = [
    {
        'name': 'test1',
        'file': 'test1.png'
    },
    {
        'name': 'test2',
        'file': 'test2.png'
    }
]


def gen_image():
    rand_image = randint(0, len(image_files)-1)
    image_file = image_files[rand_image]['file']
    image_name = image_files[rand_image]['name']
    image_path = str(Path().absolute())+'/img/'+image_file
    img = cv2.imread(image_path)
    noise_img = random_noise(img, mode='s&p', amount=0.1)
    img = Image.fromarray(noise_img, 'RGB')

    fp = io.BytesIO()
    img.save(fp, format="PNG")
    content = fp.getvalue()
    return content


gen_image()

我也尝试过使用 pypng:

import png

# Added the following to gen_image()
content = png.from_array(noise_img, mode='L;1')
content.save('image.png')

如何从磁盘加载 png(具有 alpha 透明度),为其添加一些噪音,然后返回它以便它可以由 Web 服务器代码(flask、aiohttp 等)显示。

正如 makayla 的回答所指出的那样,这使它变得更好:noise_img = (noise_img*255).astype(np.uint8)但是颜色仍然错误并且没有透明度。

这是更新的功能:

def gen_image():
    rand_image = randint(0, len(image_files)-1)
    image_file = image_files[rand_image]['file']
    image_name = image_files[rand_image]['name']
    image_path = str(Path().absolute())+'/img/'+image_file
    img = cv2.imread(image_path)

    cv2.imshow('dst_rt', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # Problem exists somewhere below this line.
    img = random_noise(img, mode='s&p', amount=0.1)
    img = (img*255).astype(np.uint8)
    img =  Image.fromarray(img, 'RGB')

    fp = io.BytesIO()
    img.save(fp, format="png")
    content = fp.getvalue()

    return content

这将弹出一个预噪声图像并返回噪声图像。返回的图像中存在 RGB(和 alpha)问题。

我认为问题是它需要,RGBA但是当我改变它时,我得到ValueError: buffer is not large enough

标签: pythonpython-3.ximage-processingsignal-processing

解决方案


鉴于所有新信息,我正在更新我的答案,并提供更多调试问题的技巧。

我在这里找到了一个创建示例透明图像的站点。我创建了一个透明层为 0.5 的 64x64 青色(R=0,G=255,B=255)图像。我用它来测试你的代码。

青色图像

我在图像中阅读了两种比较方法:im1 = cv2.imread(fileName)im2 = cv2.imread(fileName,cv2.IMREAD_UNCHANGED). np.shape(im1)回来了(64,64,3)np.shape(im2)回来了(64,64,4)。这就是为什么需要该标志的原因——opencv 中的默认 imread 设置将在透明图像中读取为普通 RGB 图像。

然而,opencv 读取为 BGR 而不是 RGB,并且由于您无法使用 opencv 保存,因此您需要将其转换为正确的顺序,否则图像将具有相反的颜色。例如,我的青色图像,当以反转颜色查看时,如下所示:

青色反转(黄色)

您可以像这样使用 openCV 的颜色转换功能im = cv2.cvtColor(im, cv2.COLOR_BGRA2RGBA)来更改它(是所有颜色转换代码的列表)。再次,如果需要,请仔细检查图像的大小,因为您将其转换为 RGBA,它仍然应该有四个通道。

您现在可以将噪点添加到图像中。正如您所知,这也会给您的 Alpha 通道添加噪点,随机使一些像素更透明,而另一些像素则更不透明。skimage中的random_noise 函数将您的图像转换为浮点数并将其作为浮点数返回。这意味着图像值(通常是 0 到 255 的整数)将转换为 0 到 1 的十进制值。您的行img = Image.fromarray(noise_img, 'RGB')不知道如何处理浮点noise_img。这就是为什么当你保存它时图像都搞砸了,当我试图显示它时。

所以我拍摄了我的青色图像,添加了噪点,然后将浮点数转换回 8 位。

noise_img = random_noise(im, mode='s&p', amount=0.1)
noise_img = (noise_img*255).astype(np.uint8)
img = Image.fromarray(noise_img, 'RGBA')

现在看起来像这样(截图)使用img.show()

s&p 青色图像

我使用 PIL 库而不是 openCV 来保存我的图像,因此它尽可能接近您的代码。

fp = 'saved_im.png'
img.save(fp, format="png")

我将图像加载到 powerpoint 中,以仔细检查当我使用这种方法保存它时它是否保留了透明度。这是在PowerPoint中覆盖在红色圆圈上的保存图像的屏幕截图:

透明覆盖


推荐阅读