Shawn Zhuang
Shawn Zhuang

Reputation: 35

how crop an image evenly without loop

Suppose I have an np.array(image)

img = [[1,2,3,4],
       [5,6,7,8],
       [9,10,11,12],
       [13,14,15,16]]

How can I divide this in to 4 crops?

[[1,2],    [[3,4],    [[9,10],    [[11,12],
 [5,6]]     [7,8]]     [13,14]]     [15,16]]

The only way I know is to use loop to specify the img[x_start:x_end,y_start:y_end]. But this is very time-consuming when it comes to a large 3D Volume. NumPy library seems to perform better by itself than the loop in some algorithms. Btw, if I use img.reshape(-1,2,2), I get the following matrix, which is not what I want:

[[1,2],    [[5,6],    [[9,10],    [[13,14],
 [3,4]]     [7,8]]     [11,12]]     [15,16]]

Of course, it doesn't have to be Numpy library but can also cv2 or something like that which I can use in python

Upvotes: 3

Views: 233

Answers (3)

Naphat Amundsen
Naphat Amundsen

Reputation: 1623

What you want to have is called a sliding window view. NumPy already has a function to do this: numpy.lib.stride_tricks.sliding_window_view, but the function does not take custom strides (step size of your sliding window). I have implemented my own function which gives such a view (requiring minimal memory overhead):

import numpy as np 
from numpy.lib.stride_tricks import as_strided
from typing import Tuple


def get_sliding_window_2d(x: np.ndarray, width: int, height: int, rowstride: int, colstride: int):
    """
    x: np.array
    width: width of window
    height: height of window
    rowstride: horizontal window step size
    colstride: vertical window step size 
    """
    imgRows, imgCols = x.shape
    u = np.array(x.itemsize)
    return as_strided(x,
        shape=((imgRows-width)//rowstride+1, (imgCols-height)//colstride+1, width, height), 
        strides=u*(imgCols*rowstride, colstride, imgCols, 1)
    )

a = np.arange(4*4).reshape(4,4)+1
for windows in get_sliding_window_2d(a, 2, 2, 2, 2):
    for window in windows:
        print(window, end="\n\n")

#[[1 2]
# [5 6]]
#
#[[3 4]
# [7 8]]
#
#[[ 9 10]
# [13 14]]
#
#[[11 12]
# [15 16]]

On a related note, if you plan to use said sliding windows to blur images using a mean kernel or do edge detection or something, you can use scipy.signal.convolve2d (and many other similar functions) to do it.

Upvotes: 0

lefevrej
lefevrej

Reputation: 191

To solve your problem you can play with axis using np.transpose or np.moveaxis for example.

The following solution is probably not the fastest but it illustrates what you can do with these tools.

img = np.array([[1,2,3,4],
         [5,6,7,8],
         [9,10,11,12],
         [13,14,15,16]])
img = img.reshape(-1,2,2)
img = np.moveaxis(img, 0, 1)
img = img.reshape(-1,2,2)

Output:

>>> print(img)
[[[ 1  2]
  [ 5  6]]

 [[ 9 10]
  [13 14]]

 [[ 3  4]
  [ 7  8]]

 [[11 12]
  [15 16]]]

Upvotes: 0

Andrej Kesely
Andrej Kesely

Reputation: 195448

I hope I've undersdoot your question right:

img = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])

out = [np.vsplit(x, 2) for x in np.hsplit(img, 2)]

for arr1 in out:
    for arr2 in arr1:
        print(arr2)
        print()

Prints:

[[1 2]
 [5 6]]

[[ 9 10]
 [13 14]]

[[3 4]
 [7 8]]

[[11 12]
 [15 16]]

Upvotes: 1

Related Questions