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

import numpy as np
from PIL import Image

def remove,python,numpy,python-imaging-library"/>
	














首页 > 解决方案 > 收到错误:“TypeError:无法处理此数据类型:(1、1、3),

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

import numpy as np
from PIL import Image

def remove

问题描述

这是一段代码,目的是从图像中删除像素。我是 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)

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

只要保证接缝从每一行中删除一个元素,您就可以使用以下步骤将其删除:

  1. 将您的(r, c, 3)阵列视为一个(r * c, 3)阵列。这不涉及复制任何数据。
  2. 将每个接缝元素的索引转换为一维索引。
  3. 从一维图像中删除所需的像素。这将创建一个新数组。
  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,但您必须非常小心地执行此操作,包括注意输出的字节顺序:

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

标签: pythonnumpypython-imaging-library

解决方案


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

只要保证接缝从每一行中删除一个元素,您就可以使用以下步骤将其删除:

  1. 将您的(r, c, 3)阵列视为一个(r * c, 3)阵列。这不涉及复制任何数据。
  2. 将每个接缝元素的索引转换为一维索引。
  3. 从一维图像中删除所需的像素。这将创建一个新数组。
  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,但您必须非常小心地执行此操作,包括注意输出的字节顺序:

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

推荐阅读