Home > Net >  Resize 1-channel numpy (image) array with nearest neighbour interpolation
Resize 1-channel numpy (image) array with nearest neighbour interpolation

Time:10-29

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:

enter image description here

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])]
  • Related