I got a 1-channel numpy array with shape 388x388x1 with values in range 1-150 as output of ML inference. I need to resize the array to shape 5000x4000x1 with nearest neighbour interpolation.
Currently I am using PILs resize. It works but it feels overcomplicated having to import PIL just for this.
output = np.random.randint(150, size=(388, 388))
width, height = 4000, 5000
pilImage = Image.fromarray(pred.astype(np.uint8))
pilImageResized = pilImage.resize((width, height), Image.NEAREST)
resizedOutput = np.array(pilImageResized).astype(np.uint8)
Are there easier methods of achieving what I want within numpy?
(not using cv2.resize
, scipy.interpolate
or PIL
)
CodePudding user response:
You can do NN interpolation pretty trivially by constructing the index array that maps each output location to its source in the input. You have to define/assume a couple of things to do it meaningfully. For example, I will assume that you want to match the left edge of each output row to the left edge of the input row, treating the pixels as a surface element, rather than a point source. In the latter case, I would match the centers up instead, causing the edge regions to appear slightly truncated.
One simple method is to introduce a coordinate system in which integer locations point to the centers of the input pixels. That means that images actually go from -0.5px to (N - 0.5)px in each axis. It also means that rounding the centers of output pixels automatically maps them to the nearest input pixel:
This will give each input pixel an approximately equal representation in the output, up to roundoff:
in_img = np.random.randint(150, size=(388, 388, 1), dtype=np.uint8) 1
in_height, in_width, *_ = in_img.shape
out_width, out_height = 4000, 5000
ratio_width = in_width / out_width
ratio_height = in_height / out_height
rows = np.round(np.linspace(0.5 * (ratio_height - 1), in_height - 0.5 * (ratio_height 1), num=out_height)).astype(int)[:, None]
cols = np.round(np.linspace(0.5 * (ratio_width - 1), in_width - 0.5 * (ratio_width 1), num=out_width)).astype(int)
out_img = in_img[rows, cols]
That's it. No complicated function necessary, and the output is guaranteed to preserve the type of the input since it's just a fancy indexing operation.
You can simplify the code and wrap it up for future reuse:
def nn_resample(img, shape):
def per_axis(in_sz, out_sz):
ratio = 0.5 * in_sz / out_sz
return np.round(np.linspace(ratio - 0.5, in_sz - ratio - 0.5, num=out_sz)).astype(int)
return img[per_axis(img.shape[0], shape[0])[:, None],
per_axis(img.shape[1], shape[1])]