获取错误:“TypeError:无法处理此数据类型:(1,1,3),<i4”

2024-09-30 20:32:50 发布

您现在位置:Python中文网/ 问答频道 /正文

下面是一段代码,目标是从图像中删除像素。我是python新手,我不确定这是否是正确的解决方案。我提出的解决方案是将阵列转换为二维列表,并删除每个像素(我尝试了np.delete,但它一直抱怨阵列必须是矩形的),然后返回到阵列,然后返回到图像,如下所示

import numpy as np
from PIL import Image

def removeSeam(image, seam):
    """
    image: numpy array representing a PIL image
    seam: list of pixel coords represented as tuples in the form (x, y)
    returns a PIL image
    """
    grid = image.tolist()
    for i in range(len(seam)):
        grid[i].pop(seam[i][1])
    return Image.fromarray(np.array(grid))

当我运行类似这样的操作时会引发错误(可追溯到函数中的return语句):

example = np.array([
    [[255, 255, 255], [0,    0,    0], [255, 255, 255]],
    [[0,    0,    0], [255, 255, 255], [0,    0,    0]],
    [[255, 255, 255], [0,    0,    0], [255, 255, 255]]])

# this seam should remove the top left, center, and bottom left pixels
seam = [(0, 0), (1, 1), (0, 2)]

removeSeam(example, seam)

Tags: in图像imageimportnumpypilasnp
1条回答
网友
1楼 · 发布于 2024-09-30 20:32:50

从数组到列表的转换很少有必要,而且总是很昂贵。数组将数据打包到一个连续的缓冲区中,但列表将每个元素打包到一个单独的对象中,并保留指向这些对象的指针。这也意味着数组可以很容易地作为整个单元处理,而列表中的每个对象都需要独立处理。因此,您几乎不想使用列表来处理图像

只要保证seam从每行中删除一个元素,就可以使用以下步骤删除它:

  1. (r, c, 3)数组视为(r * c, 3)数组。这不涉及复制任何数据
  2. 将每个接缝元素的索引转换为一维索引
  3. 从1D图像中删除所需的像素。这将创建一个新数组
  4. 将结果重塑为(r, c - 1, 3)。这也不会复制任何数据

我建议您更新代码,以支持行或列的索引接缝,并支持axis参数remove_seam。这样,您只需要传入一个数组(包括一个列表),该数组与正在移动的轴的大小相匹配:

def remove_seam(image, seam, axis=0):
    image = np.asanyarray(image)
    seam = np.asanyarray(seam)
    axis = np.core.multiarray.normalize_axis_index(axis, image.ndim)

    assert image.ndim == 3
    assert image.shape[-1] == 3
    assert axis in {0, 1}
    assert seam.size == image.shape[axis]

    shape = list(image.shape)
    seam = [seam, seam]
    seam[axis] = np.arange(image.shape[axis])
    seam = np.ravel_multi_index(seam, image.shape[:-1])
    image = image.reshape(-1, 3)
    shape[axis] -= 1
    result = np.delete(image, seam, axis=0).reshape(shape)
    return result

这看起来可能比您现有的函数长得多,但它只执行一个稍微昂贵的操作:np.delete(image, seam),并且尽可能便宜地执行。如果你仔细看一下其他的操作,它们只是将非常小的数字列表混在一起,以获得正确的形状和步幅

以下是为新接口重写的示例:

example = np.array([
    [[255, 255, 255], [0,    0,    0], [255, 255, 255]],
    [[0,    0,    0], [255, 255, 255], [0,    0,    0]],
    [[255, 255, 255], [0,    0,    0], [255, 255, 255]]])

# this seam should remove the top left, center, and bottom left pixels
seam = [0, 1, 0]

remove_seam(example, seam)

这将删除左上、中和左下像素。如果要设置axis=1,则需要删除左上、中和右上像素:

remove_seam(example, seam, axis=1)

要将数组转换为映像,需要将其转换为np.uint8数据类型。有几种方法可以做到这一点。一种方法是输入正确的大小:

example = np.array([[[...]...]...], dtype=np.uint8)

另一种方法是转换输出:

remove_seam(...).astype(np.uint8)

最后一种方法是将输出视为np.uint8,但您必须非常小心地执行此操作,包括注意输出的endianness:

remove_seam(...).view(np.uint8)[..., ::4]

相关问题 更多 >