user1631306
user1631306

Reputation: 4470

python split image in overlapping and rotating tiles

I am doing a image classification. I have very imbalanced data. I am trying couple of approaches to overcome the imbalanced data issue. one of them is oversampling the minority class. The images that i have are already in high resolution(1392x1038), so i am splitting them into 348x256 size 16 tiles. As in oversampling, you simply replicate the minority classes. I was thinking of splitting the image into overlapping tiles with stride 1 or 2, so i would have slighlty different images and it would also help me in oversampling. Following code splits the images into specified number of defined size overlapping tiles

for i in range(0, count):
        start_row_idx = random.randint(0, img_height-target_height-1)
        start_col_idx = random.randint(0, img_width-target_width-1)

        if mode == 'rgb':
            patch = img_array[start_row_idx:(start_row_idx+target_height), start_col_idx:(start_col_idx+target_width), :]
        else:
            patch = img_array[start_row_idx:(start_row_idx+target_height), start_col_idx:(start_col_idx+target_width)]
        patches.append(patch)
        idxs.append((start_row_idx, start_col_idx))

how can I make it work for rotating overlapping tiles with defined number of tiles and size.

Edited Question: In following image, the black squares shows the horizontal stride and tile which is I am able to get. I want to get the red color squares in that shape. I think, with red color type cropping i would be able to get more images for oversampling.

enter image description here

Upvotes: 0

Views: 2637

Answers (1)

rayryeng
rayryeng

Reputation: 104484

As we discussed above, you have tiles that have the potential of being overlapped so this is already being addressed. What is missing are rotating the tiles too. We will need to specify a random angle of rotation so that we can generate a random angle first.

After, this is simply a matter of applying an affine transform that is purely a rotation to the tiles then appending to the list. The problem with rotating images in OpenCV is that when you do rotate the image, it is subject to cropping so you don't get the entire tile contained in the image once you rotate.

I used the following post as inspiration to address this issue so that when you do rotate, the image is fully contained. Take note that the image will expand in dimensions in order to accommodate for the rotation and to keep the entire image contained in the rotated result.

import cv2
import numpy as np

def rotate_about_center(src, angle):
    h, w = src.shape[:2]
    rangle = np.deg2rad(angle)  # angle in radians
    # now calculate new image width and height
    nw = (abs(np.sin(rangle)*h) + abs(np.cos(rangle)*w))
    nh = (abs(np.cos(rangle)*h) + abs(np.sin(rangle)*w))
    # ask OpenCV for the rotation matrix
    rot_mat = cv2.getRotationMatrix2D((nw*0.5, nh*0.5), angle, 1)
    # calculate the move from the old centre to the new centre combined
    # with the rotation
    rot_move = np.dot(rot_mat, np.array([(nw-w)*0.5, (nh-h)*0.5,0]))
    # the move only affects the translation, so update the translation
    # part of the transform
    rot_mat[0,2] += rot_move[0]
    rot_mat[1,2] += rot_move[1]
    return cv2.warpAffine(src, rot_mat, (int(math.ceil(nw)), int(math.ceil(nh))), flags=cv2.INTER_LANCZOS4)

You use this function and call this with a random angle then save the patch when you're done. You'll also need to specify a maximum angle of rotation of course.

import random
max_angle = 20 # +/- 20 degrees maximum rotation
patches = []
idxs = []
for i in range(0, count):
    start_row_idx = random.randint(0, img_height-target_height-1)
    start_col_idx = random.randint(0, img_width-target_width-1)

    # Generate an angle between +/- max_angle
    angle = (2*max_angle)*random.random() - max_angle

    if mode == 'rgb':
        patch = img_array[start_row_idx:(start_row_idx+target_height), start_col_idx:(start_col_idx+target_width), :]
    else:
        patch = img_array[start_row_idx:(start_row_idx+target_height), start_col_idx:(start_col_idx+target_width)]

    # Randomly rotate the image
    patch_r = rotate_about_center(patch, angle)

    # Save it now
    patches.append(patch_r)
    idxs.append((start_row_idx, start_col_idx))

Upvotes: 1

Related Questions