Cropping an image by discarding boundary pixels such that it matches 3:4 ratio

Viewed 140

I am working on an image enhancement use case where one of the tasks is to rescale an image to a 3:4 ratio. But rather than blindly resizing the image by calculation on the height and width from the original image, I want it to be cropped, or in other words, I want to discard boundary pixels such that it matches the ratio and don't cut the primary object.

I have the segmentation mask using which I am getting the bounding box. I am also removing the background making it transparent for some other things. I am sharing both the binary mask and the original image.

I am using the below code to generate the box.

import cv2
import numpy as np

THRESHOLD = 0.9

mask = cv2.imread("mask.png")
mask = mask/255

mask[mask > THRESHOLD] = 1
mask[mask <= THRESHOLD] = 0

out_layer = mask[:,:,2]
x_starts = [np.where(out_layer[i]==1)[0][0] if len(np.where(out_layer[i]==1)[0])!=0 else out_layer.shape[0]+1 for i in range(out_layer.shape[0])]
x_ends = [np.where(out_layer[i]==1)[0][-1] if len(np.where(out_layer[i]==1)[0])!=0 else 0 for i in range(out_layer.shape[0])]
y_starts = [np.where(out_layer.T[i]==1)[0][0] if len(np.where(out_layer.T[i]==1)[0])!=0 else out_layer.T.shape[0]+1 for i in range(out_layer.T.shape[0])]
y_ends = [np.where(out_layer.T[i]==1)[0][-1] if len(np.where(out_layer.T[i]==1)[0])!=0 else 0 for i in range(out_layer.T.shape[0])]

startx = min(x_starts)
endx = max(x_ends)
starty = min(y_starts)
endy = max(y_ends)
start = (startx,starty)
end = (endx,endy)

Segmentation Mask Original Image

1 Answers

If I understood your problem correctly, you just want to have the masking of the person in an image of size ratio 3:4 without cropping the mask. The approach you are talking about is possible but a bit unnecessary. I am sharing below the approach you can use with explanation and also I have used another approach to find the box. Use any approach you like.

import cv2
import numpy as np

MaskImg = cv2.imread("WomanMask.png", cv2.IMREAD_GRAYSCALE)
cv2.imwrite("RuntimeImages/Input MaskImg.png", MaskImg)
ret, MaskImg = cv2.threshold(MaskImg, 20, 255, cv2.THRESH_BINARY)
cv2.imwrite("RuntimeImages/MaskImg after threshold.png", MaskImg)

# Finding biggest contour in the image 
# (Assuming that the woman mask will cover the biggest area of the mask image)
# Getting all external contours
Contours = cv2.findContours(MaskImg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
# exit if no white pixel in the image (no contour found)
if len(Contours) == 0:
    print("There was no white pixel in the image.")
    exit()
# Sorting contours in decreasing order according to their area
Contours = sorted(Contours, key=lambda x:cv2.contourArea(x), reverse=True)
# Getting the biggest contour
BiggestContour = Contours[0]    # This is the contour of the girl mask

# Finding the bounding rectangle
BB = cv2.boundingRect(BiggestContour)
print(f"Bounding rectangle : {BB}")
# Getting the position, width, and height of the woman mask
x, y = BB[0], BB[1]
Width, Height = BB[2], BB[3]

# Setting the (height / width) ratio required
Ratio = ( 3 / 4 )   # 3 : 4 :: Height : Width

# Getting the new dimentions of the image to fit the mask
if (Height > Width):
    NewHeight = Height
    NewWidth = int(NewHeight / Ratio)
else:
    NewWidth = Width
    NewHeight = int(NewWidth * Ratio)

# Getting the position of the woman mask in this new image
# It will be placed at the center
X = int((NewWidth - Width) / 2)
Y = int((NewHeight - Height) / 2)


# Creating the new image with the woman mask at the center
NewImage = np.zeros((NewHeight, NewWidth), dtype=np.uint8)
NewImage[Y : Y+Height, X : X+Width] = MaskImg[y : y+Height, x : x+Width]

cv2.imwrite("RuntimeImages/Final Image.png", NewImage)

Below is the final output mask image Final mask image

Related