Numpy Resize/Rescale 图片

Numpy Resize/Rescale Image

我想拍摄一张图片并更改图片的比例,虽然它是一个 numpy 数组。

例如,我有一张可口可乐瓶的图片: bottle-1

转换为形状为 (528, 203, 3) 的 numpy 数组,我想调整它的大小以表示第二张图像的大小: bottle-2

其形状为 (140, 54, 3)

如何在保持原始图像不变的情况下将图像的大小更改为特定形状?其他答案建议剥离每隔一行或第三行,但我想做的基本上是通过图像编辑器缩小图像,但在 python 代码中。在 numpy/SciPy 中是否有任何库可以执行此操作?

是的,您可以安装 opencv(这是一个用于图像处理和计算机视觉的库),并使用 cv2.resize 功能。例如使用:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = <b>cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)</b>

因此,img 是一个包含原始图像的 numpy 数组,而 res 是一个包含 resized 图像的 numpy 数组。一个重要的方面是 interpolation 参数:有几种方法可以调整图像的大小。特别是因为您缩小了图像,并且原始图像的大小 而不是 调整后图像大小的倍数。可能的插值模式是:

  • INTER_NEAREST - a nearest-neighbor interpolation
  • INTER_LINEAR - a bilinear interpolation (used by default)
  • INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
  • INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood

与大多数选项一样,没有 "best" 选项,因为对于每个调整大小架构,在某些情况下,一种策略可能优于另一种策略。

虽然可以单独使用 numpy 来执行此操作,但该操作不是内置的。也就是说,您可以使用 scikit-image(基于 numpy)进行这种图像处理。

Scikit-Image 缩放文档是 here

例如,您可以对图片执行以下操作:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

这将为您处理插值、消除锯齿等问题。

SciPy 的 imresize() 方法是另一种调整大小的方法,但它将从 SciPy v 1.3.0 开始被删除。 SciPy参考PIL图片调整方法:Image.resize(size, resample=0)

size – 以像素为单位的请求大小,作为二元组:(宽度,高度)。
resample – 一个可选的重采样过滤器。这可以是 PIL.Image.NEAREST(使用最近邻)、PIL.Image.BILINEAR(线性插值)、PIL.Image.BICUBIC(三次样条插值)或 PIL.Image.LANCZOS(高质量下采样滤波器)之一).如果省略,或者图像具有模式“1”或“P”,则设置为 PIL.Image.NEAREST.

Link 这里: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize

import cv2
import numpy as np

image_read = cv2.imread('filename.jpg',0) 
original_image = np.asarray(image_read)
width , height = 452,452
resize_image = np.zeros(shape=(width,height))

for W in range(width):
    for H in range(height):
        new_width = int( W * original_image.shape[0] / width )
        new_height = int( H * original_image.shape[1] / height )
        resize_image[W][H] = original_image[new_width][new_height]

print("Resized image size : " , resize_image.shape)

cv2.imshow(resize_image)
cv2.waitKey(0)

对于来自 Google 的人们正在寻找一种快速方法来对 numpy 数组中的图像进行下采样以用于机器学习应用程序,这里有一个超快速的方法(改编自 here ).此方法仅在输入维度是输出维度的倍数时有效。

以下示例将采样率从 128x128 缩减为 64x64(这很容易更改)。

上次订购的频道

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

频道优先排序

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

对于灰度图像,只需将 3 更改为 1,如下所示:

频道优先排序

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

此方法使用等效于最大池化。这是我发现的最快的方法。

Are there any libraries to do this in numpy/SciPy

当然可以。您可以在没有 OpenCV、scikit-image 或 PIL 的情况下执行此操作。

图像缩放基本上是将每个像素的坐标从原始图像映射到其调整后的位置。

由于图像的坐标必须是整数(将其视为矩阵),如果映射坐标具有十进制值,您应该对像素值进行插值以使其近似于整数位置(例如获取最近的像素那个位置被称为 Nearest neighbor interpolation).

您所需要的只是一个可以为您进行插值的函数。 SciPy 有 interpolate.interp2d.

您可以使用它来调整 numpy 数组中图像的大小,例如 arr,如下所示:

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)

f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

当然,如果你的图像是RGB,你就得对每个通道进行插值。

如果您想了解更多,我建议观看Resizing Images - Computerphile

如果有人来这里寻找一种简单的方法 scale/resize Python 中的图像,而不使用额外的库,这里有一个非常简单的图像调整功能:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

用法示例:将 (30 x 30) 图像调整为 (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

输出:

这适用于 shrink/scale 个图像,并且适用于 numpy 数组。

下采样(乘2)的单行numpy解决方案:

smaller_img = bigger_img[::2, ::2]

上采样(乘以2):

bigger_img = smaller_img.repeat(2, axis=0).repeat(2, axis=1)

(假设 HxWxC 形状的图像。h/t 到上面评论中的 L.Kärkkäinen。请注意,此方法仅允许调整整个整数的大小(例如,2x 但不是 1.5x))

对于想要调整(内插)一批 numpy 数组大小的人,pytorch 提供了更快的函数名称 torch.nn.functional.interpolate,请记住先使用 np.transpose 将通道从 batchxWxHx3 更改为 batchx3xWxH .

几年后偶然发现了这个。到目前为止,答案似乎属于以下几类之一:

  1. 使用外部库。 (OpenCV,SciPy,等)
  2. 用户二次幂缩放
  3. 使用最近邻

这些解决方案都是值得尊敬的,所以我提供这个只是为了完整性。它比上面的有三个优点:(1)它会接受任意分辨率,甚至是非电源- 两个比例因子; (2) 它使用纯 Python+Numpy,没有外部库; (3) 它对所有像素进行插值以获得可以说是 'nicer-looking' 的结果。

它没有很好地利用 Numpy,因此速度不快,尤其是对于大图像。如果您只是重新缩放较小的图像,应该没问题。我根据 Apache 或 MIT 许可提供此服务,由用户自行决定。

import math
import numpy

def resize_linear(image_matrix, new_height:int, new_width:int):
    """Perform a pure-numpy linear-resampled resize of an image."""
    output_image = numpy.zeros((new_height, new_width), dtype=image_matrix.dtype)
    original_height, original_width = image_matrix.shape
    inv_scale_factor_y = original_height/new_height
    inv_scale_factor_x = original_width/new_width

    # This is an ugly serial operation.
    for new_y in range(new_height):
        for new_x in range(new_width):
            # If you had a color image, you could repeat this with all channels here.
            # Find sub-pixels data:
            old_x = new_x * inv_scale_factor_x
            old_y = new_y * inv_scale_factor_y
            x_fraction = old_x - math.floor(old_x)
            y_fraction = old_y - math.floor(old_y)

            # Sample four neighboring pixels:
            left_upper = image_matrix[math.floor(old_y), math.floor(old_x)]
            right_upper = image_matrix[math.floor(old_y), min(image_matrix.shape[1] - 1, math.ceil(old_x))]
            left_lower = image_matrix[min(image_matrix.shape[0] - 1, math.ceil(old_y)), math.floor(old_x)]
            right_lower = image_matrix[min(image_matrix.shape[0] - 1, math.ceil(old_y)), min(image_matrix.shape[1] - 1, math.ceil(old_x))]

            # Interpolate horizontally:
            blend_top = (right_upper * x_fraction) + (left_upper * (1.0 - x_fraction))
            blend_bottom = (right_lower * x_fraction) + (left_lower * (1.0 - x_fraction))
            # Interpolate vertically:
            final_blend = (blend_top * y_fraction) + (blend_bottom * (1.0 - y_fraction))
            output_image[new_y, new_x] = final_blend

    return output_image

样本重新缩放:

原文:

缩小一半:

放大一又四分之一: