使用 PIL 排列二进制掩码中的像素

Permuting Pixels Within a Binary Mask with PIL

我完全被这个问题困住了 - 我需要随机排列 2D 图像上圆圈内的像素。我需要这个用于我正在设计的实验,其中搜索这个小的乱序圆圈将作为任务的低级感知部分。重要的是像素来自图像本身而不是随机值,以保持修改和未修改图像之间的图像强度均匀。

我不太了解 Python 所以到目前为止我的代码很乱 - 我首先打开图像,并确保它是 RGB。然后我做了一个深拷贝(因为我将用蒙版修改原件,但我仍然需要实际的原始像素值)。然后我画了一个亮红色的圆圈(255,0,0),然后把pix_mask定义为红色为255的地方(我们的刺激是自然场景和物体,所以应该没有其他地方图像与此)。然后我使用 pix_mask 创建一个新的蒙版图像,并将蒙版乘以复制的图像数据以获得圆圈内的数据。听起来很乱,但结果如下:

这是我用来执行此操作的代码:

def create_pixel_mask_images(image_fname: str):
    # Open the image which requires a scrambled dot. Add an alpha channel and make a copy of the data 
    image: JpegImage          = Image.open(image_fname)
    image                     = image.convert("RGB")
    image_copy                = image.copy()

    # Create the drawer and draw a filled red circle on the original image (255, 0, 0)
    image_draw                = ImageDraw.Draw(image)
    image_draw.ellipse((150, 150, 250, 250), fill = "red")
    image.save("TEST_DOT.png")

    # Read the data from the copy and the now-modified original image into arrays
    image_arr: NumpyArray      = numpy.array(image)
    image_copy_arr: NumpyArray = numpy.array(image_copy)

    # Create a binary mask by testing for the red circle we drew on the original image (255 red)
    pix_mask: NumpyArray       = image_arr[:, :, 0] == 255

    # Invert the mask (i.e., everything outside of the dot is now white, and the dot black)
    # Write them out to file to error-check
    mask_image: PILImage         = Image.fromarray((pix_mask * 255).astype(numpy.uint8))
    image.convert("RGB")
    mask_image_inverse: PILImage = ImageOps.invert(mask_image)
    image.convert("RGB")
    pix_mask_inverse: NumpyArray = numpy.array(mask_image_inverse)

    # Read the inverted pixel mask as a numpy array. Multiply the unmodified image by both masks
    # in order to get the data both inside and outside of the mask
    inner_data = image_copy_arr * pix_mask[..., None]
    outer_data = image_copy_arr * pix_mask_inverse[..., None]

    # Create new images from the masked data
    inner_image: PILImage = Image.fromarray(inner_data.astype(numpy.uint8))
    outer_image: PILImage = Image.fromarray(outer_data.astype(numpy.uint8))
    outer_image           = ImageOps.invert(outer_image)

    # AS A CHECK multiply the inner- and outer-masked images together. We should get the original image back
    image_data = image.load()

        for y in range(image.size[1]):
            for x in range(image.size[0]):
                if image_data[x, y] == (255, 0, 0):
                    image_data[x, y] = (255, 255, 255)
    recombined_image = ImageChops.multiply(inner_image, outer_image)
    
    inner_image.save('INNER_DATA.png')
    
    mask_image.save('MASK.jpg')
    mask_image_inverse.save('MASK_INVERSE.jpg')
    recombined_image.save('RECOMBINATION_TEST.jpg')

你可以在底部看到我通过将 inner_image(圆掩码内的数据)与 outer_image(其余部分)相乘来创建 recombined_image圆掩码外的数据一起;然而,我最终得到了与这里第二张图片相同的图像(只是圆圈内的数据)。

我在这里使用嵌套的 for-loop 将原始图像中的任何红色像素设置为白色,就好像我将该区域保留为黑色一样我会丢失那里的数据(检查文档 ImageChops.multiply() - Docs)。我还尝试了 ImageChops.overlay() 函数,结果相同 - 除了之前被屏蔽的部分外全黑。

所以现在,我有了蒙版图像,太棒了;但是,我似乎无法将这个蒙版区域与原始区域重新组合。首先,在执行此操作之前,无论如何我都需要能够置换屏蔽区域内的数据。

到目前为止,我的解决方案看起来一团糟,我确信必须有更多 straightforward/elegant 的方法来实现我的需要。有人可以帮忙吗?

好的,这对我有用。我的测试图像大约是 2k x 1500,所以我选择了一个靠近中心的圆圈。我只是在那个圈子内随机选择 75,000 对并交换它们。当然,结果看起来像静态的。

from PIL import Image
import numpy as np
import math
import random

img = Image.open('Melody1.jpg')
img = np.asarray(img.getdata()).reshape(img.size[1],img.size[0],3)
print(img.shape)

center = (1000, 800)
radius = 150

for i in range(75000):
    r1 = radius * math.sqrt(random.random())
    t1 = random.random() * 2 * math.pi
    r2 = radius * math.sqrt(random.random())
    t2 = random.random() * 2 * math.pi

    x1 = int(center[0] + r1 * math.cos(t1))
    y1 = int(center[1] + r1 * math.sin(t1))
    x2 = int(center[0] + r2 * math.cos(t2))
    y2 = int(center[1] + r2 * math.sin(t2))

    img[x1,y1,:], img[x2,y2,:] = img[x2,y2,:], img[x1,y1,:]

print(img.shape)
outimg = Image.fromarray(img.astype(np.uint8))
outimg.save( 'other.jpg' )

这是生成的图像。你会想要做更多的交换以使其无法识别。