a function to partition images into patches
PEP-8 Guidelines

Your code violates the PEP-8 standard in several area, including (but not limited to):


Operators should have 1 space before and after the operator (y + h instead of y+h)
One space after commas (x, y, w, h not x,y,w,h)
Slices should not have any spaces around the colon (y:y_end not y: y_end)


Spelling

Columns is spelt with an n.

Meaningful names

I don't know what cal_patches means.  Is cal short for "calculate"?  Your question title says "partition"; perhaps that should be part of the function name.

docstring

Add a doc-string to help() the user understand what the function does, and what the function returns ... without having to consult the source code.

Type Hints

With Python 3.6+, you can add type hints to the function to further improve understanding about what the function requires and what it returns:

def cal_patches(img: Image, num_colums: int) -&gt; List[Image]:


Useful arguments

The function can partition the image into a number of vertical columns.  What if I wanted a number of rows as well?  I can't request that.

Oh wait!  It does do rows too.  But always the same number of rows and columns.  Perhaps add num_rows to the argument list too?

Integer division

int(n / d) is verbose and unnecessary.  Use the integer division operator n // d.

Unused variables

ind is never used anywhere, and could be removed.

i and j are never used anywhere, so could be replaced by the "throwaway" variable _.  Even better, you could use the w and h values as the step size for the range(), and get rid of the x += w and y += h statements:

Avoid calculating unchanging values

In the inner loop, y is not changing, so y_end results in the same value each iteration of the inner loop.  It can be moved out of the inner loop.

Bug?

The inner loop goes over the number rows, but is increase x by w, which is a column increment.  Did you accidentally swap the rows and columns?  It works as long as num_rows == num_columns, but it will cause grief when that is relaxed.



Updated code

def create_image_patches(img, num_rows, num_columns):
    """
    Partition an image into multiple patches of approximately equal size.
    The patch size is based on the desired number of rows and columns.
    Returns a list of image patches, in row-major order.
    """

    patch_list = []
    width, height = img.shape[1], img.shape[0]
    w, h = width // num_columns, height // num_rows

    for y in range(0, height, h): 
        y_end = min(y + h, width)
        for x in range(0, width, w):
            x_end = min(x + w, height)
            patch = img[y:y_end, x:x_end]
            patch_list.append(patch)

    return patch_list


Add type hints using the typing module.



Generator

As suggested by @Graipher, this function could be turned into a generator, which may be useful depending on how the patches are processed downstream:

def generate_image_patches(img, num_rows, num_columns):
    """
    Generate image patches of approximately equal size from a source image.
    The patch size is based on the desired number of rows and columns.
    Patches are generated in row-major order.
    """

    width, height = img.shape[1], img.shape[0]
    w, h = width // num_columns, height // num_rows

    for y in range(0, height, h): 
        y_end = min(y + h, width)
        for x in range(0, width, w):
            x_end = min(x + w, height)
            yield img[y:y_end, x:x_end]


A "helper" function could be used to turn the generator back into a list, if required for existing callers of the function:

def create_image_patches(img, num_rows, num_columns):
    """
    Partition an image into multiple patches of approximately equal size.
    The patch size is based on the desired number of rows and columns.
    Returns a list of image patches, in row-major order.
    """

    return list(generate_image_patches(img, num_rows, num_columns))