# Squared cropping functions

I created multiple functions to help with cropping square images. These can be handy for creating thumbnails, for example.

Another example might be machine learning. Image classification and training sometimes requires images of a fixed size, usually square.

https://github.com/krakkus/krakkus_python_examples/tree/main/squared_cropping_functions

## Functions

### function: squared_crop_from_box_reduce_one

The first one is a rather simple way to get a square crop out of a bounding box and a larger image.

First, determine the longest side of the bounding box, and then recalculate either ymax or xmax of the box coordinates to equal the shortest side.

And because we are reducing the box in size, it can never go past the edge of the full image.

```import cv2

def squared_crop_from_box_reduce_one(image, box):
# Extract the box coordinates
ymin, xmin, ymax, xmax = box

# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# The coordinates must be in correct order
assert xmax > xmin and ymax > ymin

# The coordinates must be within the image
assert xmax <= image_w and ymax <= image_h
assert xmin >= 0 and ymin >= 0

# Get the width and height of the box
box_h = ymax - ymin
box_w = xmax - xmin

# If for some reason the box is already squared, get the crop
if box_h == box_w:
crop = image[ymin:ymax, xmin:xmax]
# We need to adjust the box
else:
# If the box height is bigger than its width...
if box_h > box_w:
# Then we adjust the height to be equal to the width
ymax = ymin + box_w
# If the box width is bigger than its height
else:
# Then we adjust the width to be equal to the height
xmax = xmin + box_h

# We make the crop using the new coordinates
crop = image[ymin:ymax, xmin:xmax]

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\india_women_hockey.jpg'

# Coordinates in tensorflow format e.g. ymin, xmin, ymax, xmax
box_coordinates = (105, 863, 664, 1130)

# Load image into a numpy array

# Extract the box coordinates
ymin, xmin, ymax, xmax = box_coordinates

# Do a regular crop
regular_crop_image = source_image[ymin:ymax, xmin:xmax]

# And do the custom crop
custom_crop_image = squared_crop_from_box_expand(source_image, box_coordinates)

# Draw rectangle around the object
cv2.rectangle(source_image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 3)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Regular crop', regular_crop_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```

### function: squared_crop_from_box_reduce_both

Just like the function above, this one takes an image and box coordinates, and returns a square crop. The difference is that this shortens the long side at both ends, leaving you with a center crop.

```import cv2

def squared_crop_from_box_reduce_one(image, box):
# Extract the box coordinates
ymin, xmin, ymax, xmax = box

# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# The coordinates must be in correct order
assert xmax > xmin and ymax > ymin

# The coordinates must be within the image
assert xmax <= image_w and ymax <= image_h
assert xmin >= 0 and ymin >= 0

# Get the width and height of the box
box_h = ymax - ymin
box_w = xmax - xmin

# If for some reason the box is already squared, get the crop
if box_h == box_w:
crop = image[ymin:ymax, xmin:xmax]
# We need to adjust the box
else:
# If the box height is bigger than its width...
if box_h > box_w:
# Determine how much the height of the box needs shortened and take half of that
half = int((box_h-box_w) / 2)
# Use this to increase ymin so the crop becomes centered
ymin += half
# Then we adjust the height to be equal to the width
ymax = ymin + box_w
# If the box width is bigger than its height
else:
# Determine how much the width of the box needs shortened and take half of that
half = int((box_w - box_h) / 2)
# Use this to increase xmin so the crop becomes centered
xmin += half
# Then we adjust the width to be equal to the height
xmax = xmin + box_h

# We make the crop using the new coordinates
crop = image[ymin:ymax, xmin:xmax]

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\india_women_hockey.jpg'

# Coordinates in tensorflow format e.g. ymin, xmin, ymax, xmax
box_coordinates = (105, 863, 664, 1130)

# Load image into a numpy array

# Extract the box coordinates
ymin, xmin, ymax, xmax = box_coordinates

# Do a regular crop
regular_crop_image = source_image[ymin:ymax, xmin:xmax]

# And do the custom crop
custom_crop_image = squared_crop_from_box_expand(source_image, box_coordinates)

# Draw rectangle around the object
cv2.rectangle(source_image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 3)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Regular crop', regular_crop_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```

### function: squared_crop_from_box_expand

Instead of cutting off part of the crop to make it square, this function expands the bounding box equally. When the bounding goes beyond the edges of the image, we shift it back within the limits. This might cause the target to not be in the center of the crop. But it is complete and square.

Also, if the squared crop area can’t fit within the bounds of the image, it will raise an exception. This could be solved by falling back to another cropping method, but I will leave that up to you.

```import cv2

def squared_crop_from_box_expand(image, box):
# Extract the box coordinates
ymin, xmin, ymax, xmax = box

# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# The coordinates must be in correct order
assert xmax > xmin and ymax > ymin

# The coordinates must be within the image
assert xmax <= image_w and ymax <= image_h
assert xmin >= 0 and ymin >= 0

# Get the width and height of the box
box_h = ymax - ymin
box_w = xmax - xmin

# If square box can't never fit, raise exception
if max(box_h, box_w) > min(image_h, image_w):
raise Exception('Squared box cant fit within image limits')

# If for some reason the box is already squared, get the crop
if box_h == box_w:
crop = image[ymin:ymax, xmin:xmax]
# We need to adjust the box
else:
# If the box height is bigger than its width...
if box_h < box_w:
# Determine how much the height of the box needs expanded and take half of that
half = int((box_h-box_w) / 2)
# Use this to decrease ymin so the crop becomes centered
ymin -= half
# Then we adjust the height to be equal to the width
ymax = ymin + box_w
# If the box width is bigger than its height
else:
# Determine how much the width of the box needs expanded and take half of that
half = int((box_w - box_h) / 2)
# Use this to decrease xmin so the crop becomes centered
xmin -= half
# Then we adjust the width to be equal to the height
xmax = xmin + box_h

# Because we have expanded the box, there is a change that box is partially outside the image ...
# and we need to shift it back withing the images limits
if ymin < 0:
diff = 0 - ymin
ymin += diff
ymax += diff
if xmin < 0:
diff = 0 - xmin
xmin += diff
xmax += diff
if ymax > image_h:
diff = ymax - image_h
ymin -= diff
ymax -= diff
if xmax > image_w:
diff = xmax - image_w
xmin -= diff
xmax -= diff

# We make the crop using the new coordinates
crop = image[ymin:ymax, xmin:xmax]

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\india_women_hockey.jpg'

# Coordinates in tensorflow format e.g. ymin, xmin, ymax, xmax
box_coordinates = (105, 863, 664, 1130)

# Load image into a numpy array

# Extract the box coordinates
ymin, xmin, ymax, xmax = box_coordinates

# Do a regular crop
regular_crop_image = source_image[ymin:ymax, xmin:xmax]

# And do the custom crop
custom_crop_image = squared_crop_from_box_expand(source_image, box_coordinates)

# Draw rectangle around the object
cv2.rectangle(source_image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 3)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Regular crop', regular_crop_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```

### function: squared_crop_from_image_reduce_one

Instead of box coordinates, this function takes an image only and returns it squared by cutting of the excess part at the far end of the image.

```import cv2

def squared_crop_from_image_reduce_one(image):
# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# If for some reason the image is already squared, return the image
if image_h == image_w:
crop = image
# We need to adjust the image
else:
# If the image height is bigger than its width...
if image_h > image_w:
# Then we crop with the height to be equal to the width
crop = image[0:image_w, 0:image_w]
# If the image width is bigger than its height
else:
# Then we crop with the width to be equal to the height
crop = image[0:image_h, 0:image_h]

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\german_men_hockey.jpg'

# Load image into a numpy array

# And do the custom crop
custom_crop_image = squared_crop_from_image_reduce_one(source_image)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()```

### function: squared_crop_from_image_reduce_both

This function variant takes an image and cuts off an equal amount of both ends of the long side of the image to make it square.

```import cv2

def squared_crop_from_image_reduce_one(image):
# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# If for some reason the image is already squared, return the image
if image_h == image_w:
crop = image
# We need to adjust the image
else:
# If the image height is bigger than its width...
if image_h > image_w:
# Determine how much the height of the image needs shortened and take half of that
half = int((image_h-image_w) / 2)
# Then we crop with the height to be equal to the width
crop = image[0+half:image_w+half, 0:image_w]
# If the image width is bigger than its height
else:
# Determine how much the width of the image needs shortened and take half of that
half = int((image_w-image_h) / 2)
# Then we crop with the width to be equal to the height
crop = image[0:image_h, 0+half:image_h+half]

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\german_men_hockey.jpg'

# Load image into a numpy array

# And do the custom crop
custom_crop_image = squared_crop_from_image_reduce_one(source_image)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```

### function: squared_crop_from_image_expand_one

This function takes and image and add black to whatever side of short of making the image square.

```import cv2

import numpy as np

def squared_crop_from_image_expand_one(image):
# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# If for some reason the image is already squared, return the image
if image_h == image_w:
crop = image
# We need to adjust the image
else:
# Create a new empty square image which will fit the image
size = max(image_h, image_w)
crop = np.zeros((size, size, 3), np.uint8)

# Copy the image to the top left in the new square image
crop[0:image_h, 0:image_w] = image

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\german_men_hockey.jpg'

# Load image into a numpy array

# And do the custom crop
custom_crop_image = squared_crop_from_image_expand_one(source_image)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```

### function: squared_crop_from_image_expand_both

This one adds black to both short ends of the none squared image, so it becomes square.

```import cv2

import numpy as np

def squared_crop_from_image_expand_both(image):
# Get the dimensions of the source image
image_h, image_w, _ = image.shape

# If for some reason the image is already squared, return the image
if image_h == image_w:
crop = image
# We need to adjust the image
else:
# Create a new empty square image which will fit the image
size = max(image_h, image_w)
crop = np.zeros((size, size, 3), np.uint8)

# If the image height is bigger than its width...
if image_h > image_w:
# Determine how much the width of the image needs extending and take half of that
half = int((image_h-image_w) / 2)
# Then we copy the original image into the center of the black square image
crop[0:image_h, 0+half:image_w+half] = image
# If the image width is bigger than its height
else:
# Determine how much the height of the image needs extending and take half of that
half = int((image_w-image_h) / 2)
# Then we copy the original image into the center of the black square image
crop[0 + half:image_h + half, 0:image_w] = image

# We should now have a crop
assert crop is not None

# And it should be squared
crop_h, crop_w, _ = crop.shape
assert crop_h == crop_w

# Return the final crop
return crop

def main():
# Filename of the image we want to test with
fn_source_image = '..\\images\\german_men_hockey.jpg'

# Load image into a numpy array

# And do the custom crop
custom_crop_image = squared_crop_from_image_expand_both(source_image)

# Show the image, regular crop and squared crop
cv2.imshow('Source image', source_image)
cv2.imshow('Custom crop', custom_crop_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

if __name__ == '__main__':
main()
```