Numpy 调整大小/重新缩放图像
Posted
技术标签:
【中文标题】Numpy 调整大小/重新缩放图像【英文标题】:Numpy Resize/Rescale Image 【发布时间】:2018-06-15 18:09:39 【问题描述】:我想拍摄一张图像并更改图像的比例,而它是一个 numpy 数组。
例如,我有一张可口可乐瓶的图片: bottle-1
转换为形状为(528, 203, 3)
的numpy 数组,我想调整它的大小以说明第二张图像的大小:
bottle-2
形状为(140, 54, 3)
。
如何在保持原始图像不变的情况下将图像的大小更改为特定形状?其他答案建议剥离每隔一行或第三行,但我想要做的基本上是通过图像编辑器但在 python 代码中缩小图像。在 numpy/SciPy 中是否有任何库可以做到这一点?
【问题讨论】:
你能显示你的 numpy 数组的代码吗? scipy.misc.imresize @sascha 已弃用,根据您链接的页面。 @ShpielMeister 我无法让 IntelliJ 完全打印出 numpy 数组,由于某种原因,当输出很大时,它一直在...,所以我只能看到部分数组输出控制台 【参考方案1】:是的,您可以安装opencv
(这是一个用于图像处理和计算机视觉的库),并使用cv2.resize
函数。例如使用:
import cv2
import numpy as np
img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)
这里img
是一个包含原始图像的numpy 数组,而res
是一个包含调整大小 图像的numpy 数组。一个重要方面是interpolation
参数:有几种方法可以调整图像大小。特别是因为您缩小了图像,并且原始图像的大小不是调整后图像大小的倍数。可能的插值模式是:
INTER_NEAREST
- 最近邻插值INTER_LINEAR
- 双线性插值(默认使用)INTER_AREA
- 使用像素区域关系重采样。它可能是图像抽取的首选方法,因为它不会产生莫尔条纹 结果。但是当图像被缩放时,它类似于INTER_NEAREST
方法。INTER_CUBIC
- 4x4 像素邻域的双三次插值INTER_LANCZOS4
- 8x8 像素邻域上的 Lanczos 插值
与大多数选项一样,没有“最佳”选项,因为对于每个调整大小的架构,都存在一种策略优于另一种策略的情况。
【讨论】:
我刚刚试用了这段代码,它可以工作!只有一个变化是dsize
应该是dsize=(54, 140)
,因为它需要 x 然后 y,其中一个 numpy 数组显示形状为 y 然后 x(y 是行数,x 是列数)
我尽量避免使用 cv2,它以 BGR 通道格式交换尺寸和负载。我更喜欢skimage.io.imread('image.jpg')
和skimage.transform.resize(img)
。 scikit-image.org/docs/dev/install.html
@EduardoPignatelli 我避免使用 skimage.transform.resize 因为您无法控制它使用的插值算法。但是,这可能并不重要,具体取决于人们的用例。
@Decker skimage.transform.resize 通过“order”参数提供一些控制。 order=0 是最近邻,1=双线性,2=双二次,3=双三次等。但是没有面积平均值或 lanczos 插值。
@TapioFriberg 啊,是的,我的立场是正确的;我看到了 skimage.transform.warp 的 'order' 参数的文档下定义的算法。在某些时候,更新文档以包含对类型的引用可能会有所帮助,例如,“Bi-quartic”在文档的其他任何地方都没有定义,(截至 2019 年 12 月 10 日) - 单行可能对未来的用户有益。【参考方案2】:
虽然可以单独使用 numpy 来执行此操作,但该操作不是内置的。也就是说,您可以使用scikit-image
(基于 numpy 构建)来进行这种图像处理。
Scikit-Image 缩放文档是here。
例如,您可以对图像执行以下操作:
from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))
这将为您处理插值、抗锯齿等问题。
【讨论】:
谢谢!这个答案也有效!尽管anti_aliasing
标志出现了一些问题,但它似乎已从0.13.1 的最新版本中删除了
这会将图像返回为 float ndarray,即使您的原始图像是 uint8
这是一个很好的技术,因为它适用于任意数量的通道。我尝试将 rgb 数据与深度点云数据相结合,它保留了我想要的关系。
不应该是(64,64,3)
@sziraqui preserve_range=True 可以保留范围 skimage.transform.resize(..., , preserve_range=True)【参考方案3】:
SciPy 的 imresize()
方法是另一种调整大小的方法,但从 SciPy v 1.3.0 开始它将被删除。 SciPy参考PIL图片大小调整方法:Image.resize(size, resample=0)
size – 请求的大小(以像素为单位),作为 2 元组:(宽度,高度)。 resample – 一个可选的重采样过滤器。这可以是 PIL.Image.NEAREST(使用最近邻)、PIL.Image.BILINEAR(线性插值)、PIL.Image.BICUBIC(三次样条插值)或 PIL.Image.LANCZOS(高质量下采样滤波器)之一)。如果省略,或者图像的模式为“1”或“P”,则设置为 PIL.Image.NEAREST。
链接在这里: https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize
【讨论】:
不幸的是,不推荐使用 imresize(),它将在 SciPy 1.3.0 中删除 该问题明确指出图像是一个numpy数组;你不能在上面使用 Pillow。【参考方案4】: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)
【讨论】:
欢迎来到 ***。太好了,您想通过回答他人的问题来帮助他人。但是,与已经使用cv2
并使用适当的调整大小函数而不是重新实现比最近邻插值更差的“次优”调整大小函数的现有答案相比,我看不出您的答案如何增加价值。【参考方案5】:
对于从 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)
此方法使用等效于最大池。这是我发现的最快的方法。
【讨论】:
large_image[:, ::2, ::2] 返回分辨率减半的图像。 @LasseKärkkäinen 但它不会下采样,它只会选择其他所有像素。不同之处在于最终函数“max”可以更改为以稍微更好的方式选择或计算像素(例如使用“min”或“mean”)。你的方法很有用(而且更快),如果这无关紧要的话。 @L.Kärkkäinen 与双分辨率相反的是什么? @rayzinnznp.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
可以将.max(4).max(2)
替换为.mean(4).mean(2)
作为线性插值下采样的快速方法吗?【参考方案6】:
在 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。
【讨论】:
可能无法根据这个答案工作:***.com/questions/37872171/… 就个人而言,始终不推荐使用 OpenCV...【参考方案7】:如果有人来这里寻找一种简单的方法来在 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))
输出:
这适用于缩小/缩放图像,并且适用于 numpy 数组。
【讨论】:
我认为嵌套列表理解会损害可读性【参考方案8】:下采样的单行 numpy 解决方案(按 2):
smaller_img = bigger_img[::2, ::2]
和上采样(2):
bigger_img = smaller_img.repeat(2, axis=0).repeat(2, axis=1)
(假设图像为 HxWxC 形状。h/t 为上述 cmets 中的 L. Kärkkäinen。注意此方法仅允许调整整数大小(例如,2x 但不是 1.5x))
【讨论】:
在您真正不需要/不希望在 skimage 引擎盖下发生的转换为 float64 的情况下操作分割标签掩码的绝佳解决方案。【参考方案9】:对于想要调整(插值)一批numpy数组的人,pytorch提供了更快的函数名称torch.nn.functional.interpolate,只需记住先使用np.transpose将通道从batchxWxHx3更改为batchx3xWxH。
【讨论】:
【参考方案10】:几年后偶然发现了这一点。到目前为止,答案似乎属于以下几类之一:
-
使用外部库。 (OpenCV、SciPy 等)
用户二次幂缩放
使用最近邻
这些解决方案都很不错,所以我提供这个只是为了完整性。它比上述具有三个优点:(1)它可以接受任意分辨率,甚至是非二次方缩放因素; (2)它使用纯Python+Numpy,没有外部库; (3) 它对所有像素进行插值以获得可以说是“看起来更漂亮”的结果。
它没有很好地利用 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
样本重新缩放:
原文:
缩小一半:
按四分之一放大:
【讨论】:
以上是关于Numpy 调整大小/重新缩放图像的主要内容,如果未能解决你的问题,请参考以下文章
imageresizing.net 插件允许图像调整大小/缩放/裁剪