Python Scipy 旋转和缩放图像而不改变其尺寸
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37119071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scipy rotate and zoom an image without changing its dimensions
提问by chasep255
For my neural network I want to augment my training data by adding small random rotations and zooms to my images. The issue I am having is that scipy is changing the size of my images when it applies the rotations and zooms. I need to to just clip the edges if part of the image goes out of bounds. All of my images must be the same size.
对于我的神经网络,我想通过向我的图像添加小的随机旋转和缩放来增强我的训练数据。我遇到的问题是 scipy 在应用旋转和缩放时会改变我的图像的大小。如果图像的一部分越界,我只需要剪裁边缘。我所有的图像必须是相同的大小。
def loadImageData(img, distort = False):
c, fn = img
img = scipy.ndimage.imread(fn, True)
if distort:
img = scipy.ndimage.zoom(img, 1 + 0.05 * rnd(), mode = 'constant')
img = scipy.ndimage.rotate(img, 10 * rnd(), mode = 'constant')
print(img.shape)
img = img - np.min(img)
img = img / np.max(img)
img = np.reshape(img, (1, *img.shape))
y = np.zeros(ncats)
y[c] = 1
return (img, y)
回答by ali_m
scipy.ndimage.rotate
accepts a reshape=
parameter:
scipy.ndimage.rotate
接受一个reshape=
参数:
reshape : bool, optional
If
reshape
is true, the output shape is adapted so that the input array is contained completely in the output. Default is True.
重塑:bool,可选
如果
reshape
为真,则调整输出形状,以便输入数组完全包含在输出中。默认值为真。
So to "clip" the edges you can simply call scipy.ndimage.rotate(img, ..., reshape=False)
.
因此,要“剪裁”边缘,您只需调用scipy.ndimage.rotate(img, ..., reshape=False)
.
from scipy.ndimage import rotate
from scipy.misc import face
from matplotlib import pyplot as plt
img = face()
rot = rotate(img, 30, reshape=False)
fig, ax = plt.subplots(1, 2)
ax[0].imshow(img)
ax[1].imshow(rot)
Things are more complicated for scipy.ndimage.zoom
.
事情比较复杂scipy.ndimage.zoom
。
A naive method would be to zoom
the entire input array, then use slice indexing and/or zero-padding to make the output the same size as your input. However, in cases where you're increasing the size of the image it's wasteful to interpolate pixels that are only going to get clipped off at the edges anyway.
一种天真的方法是对zoom
整个输入数组,然后使用切片索引和/或零填充使输出与输入的大小相同。但是,在您增加图像大小的情况下,插入只会在边缘被剪掉的像素是一种浪费。
Instead you could index only the part of the input that will fall within the bounds of the output array before you apply zoom
:
相反,您可以在应用之前仅索引将落在输出数组范围内的输入部分zoom
:
import numpy as np
from scipy.ndimage import zoom
def clipped_zoom(img, zoom_factor, **kwargs):
h, w = img.shape[:2]
# For multichannel images we don't want to apply the zoom factor to the RGB
# dimension, so instead we create a tuple of zoom factors, one per array
# dimension, with 1's for any trailing dimensions after the width and height.
zoom_tuple = (zoom_factor,) * 2 + (1,) * (img.ndim - 2)
# Zooming out
if zoom_factor < 1:
# Bounding box of the zoomed-out image within the output array
zh = int(np.round(h * zoom_factor))
zw = int(np.round(w * zoom_factor))
top = (h - zh) // 2
left = (w - zw) // 2
# Zero-padding
out = np.zeros_like(img)
out[top:top+zh, left:left+zw] = zoom(img, zoom_tuple, **kwargs)
# Zooming in
elif zoom_factor > 1:
# Bounding box of the zoomed-in region within the input array
zh = int(np.round(h / zoom_factor))
zw = int(np.round(w / zoom_factor))
top = (h - zh) // 2
left = (w - zw) // 2
out = zoom(img[top:top+zh, left:left+zw], zoom_tuple, **kwargs)
# `out` might still be slightly larger than `img` due to rounding, so
# trim off any extra pixels at the edges
trim_top = ((out.shape[0] - h) // 2)
trim_left = ((out.shape[1] - w) // 2)
out = out[trim_top:trim_top+h, trim_left:trim_left+w]
# If zoom_factor == 1, just return the input array
else:
out = img
return out
For example:
例如:
zm1 = clipped_zoom(img, 0.5)
zm2 = clipped_zoom(img, 1.5)
fig, ax = plt.subplots(1, 3)
ax[0].imshow(img)
ax[1].imshow(zm1)
ax[2].imshow(zm2)
回答by MohamedEzz
I recommend using cv2.resize
because it is way faster than scipy.ndimage.zoom
, probably due to support for simpler interpolation methods.
我推荐使用,cv2.resize
因为它比 快得多scipy.ndimage.zoom
,可能是由于支持更简单的插值方法。
For a 480x640 image :
对于 480x640 图像:
cv2.resize
takes ~2 msscipy.ndimage.zoom
takes ~500 msscipy.ndimage.zoom(...,order=0)
takes ~175ms
cv2.resize
大约需要 2 毫秒scipy.ndimage.zoom
需要约 500 毫秒scipy.ndimage.zoom(...,order=0)
大约需要 175 毫秒
If you are doing the data augmentation on the fly, this amount of speedup is invaluable because it means more experiments in less time.
如果您正在即时进行数据增强,那么这种加速是非常宝贵的,因为这意味着在更短的时间内进行更多的实验。
Here is a version of clipped_zoom
using cv2.resize
这是一个clipped_zoom
使用版本cv2.resize
def cv2_clipped_zoom(img, zoom_factor):
"""
Center zoom in/out of the given image and returning an enlarged/shrinked view of
the image without changing dimensions
Args:
img : Image array
zoom_factor : amount of zoom as a ratio (0 to Inf)
"""
height, width = img.shape[:2] # It's also the final desired shape
new_height, new_width = int(height * zoom_factor), int(width * zoom_factor)
### Crop only the part that will remain in the result (more efficient)
# Centered bbox of the final desired size in resized (larger/smaller) image coordinates
y1, x1 = max(0, new_height - height) // 2, max(0, new_width - width) // 2
y2, x2 = y1 + height, x1 + width
bbox = np.array([y1,x1,y2,x2])
# Map back to original image coordinates
bbox = (bbox / zoom_factor).astype(np.int)
y1, x1, y2, x2 = bbox
cropped_img = img[y1:y2, x1:x2]
# Handle padding when downscaling
resize_height, resize_width = min(new_height, height), min(new_width, width)
pad_height1, pad_width1 = (height - resize_height) // 2, (width - resize_width) //2
pad_height2, pad_width2 = (height - resize_height) - pad_height1, (width - resize_width) - pad_width1
pad_spec = [(pad_height1, pad_height2), (pad_width1, pad_width2)] + [(0,0)] * (img.ndim - 2)
result = cv2.resize(cropped_img, (resize_width, resize_height))
result = np.pad(result, pad_spec, mode='constant')
assert result.shape[0] == height and result.shape[1] == width
return result