Search code examples
python-3.xgoogle-vision

How to crop a square image from normalized vertices


I'm using this code to identify tops and bottoms of photographs: ( as of now I only have it working for tops. one thing at a time ;) )

def get_file(path):
    client = vision.ImageAnnotatorClient()

    
    for images in os.listdir(path):
        # # Loads the image into memory
        with io.open(images, "rb") as image_file:
            content = image_file.read()

        image = types.Image(content=content)

        objects = client.object_localization(image=image).localized_object_annotations

        im = Image.open(images)
        width, height = im.size

        print("Number of objects found: {}".format(len(objects)))
        for object_ in objects:
            if object_.name == "Top":
                print("Top")
                l1 = object_.bounding_poly.normalized_vertices[0].x
                l2 = object_.bounding_poly.normalized_vertices[0].y
                l3 = object_.bounding_poly.normalized_vertices[2].x
                l4 = object_.bounding_poly.normalized_vertices[3].y
                left = l1 * width
                top = l2 * height
                right = l3 * width
                bottom = l4 * height
        
                im = im.crop((left, top, right, bottom))
                im.save('new_test_cropped.tif', 'tiff')
                
                im.show()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Script to automatically crop images based on google vision predictions of 'tops' and 'bottoms'")
    parser.add_argument('--path', help='Include the path to the images folder')

    args = parser.parse_args()

    get_file(args.path)
    

The images are opened, clothing is identified, and then the images are cropped and saved to a new file. (granted as of now they are being overwritten within the loop, but I'll fix that later) What I cant figure out, is how to make the crop a 1:1 ratio. I need to save them out as square-cropped to be put on our website. I'll be honest, the normalized_vertices make no sense to me. Hence why I'm having trouble.

Starting image:

enter image description here

Output:

enter image description here

Desired Output:

enter image description here


Solution

  • "Normalized" means the coordinates are divided by the width or height of the image, so normalized coordinates [1, 0.5] would indicate all the way (1) across the image and halfway down (0.5).

    For a 1:1 aspect ratio you want right - left to be equal to top - bottom. So you want to find out which dimension (width or height) you need to increase, and by how much.

    height = abs(top - bottom)
    width = abs(right - left)
    extrawidth = max(0, height - width)
    extraheight = max(0, width - height)
    

    If height > width, we want to increase width but not height. Since height - width > 0, the correct value will go into extrawidth. But because width - height < 0, extraheight will be 0.

    Now let's say we want to increase the dimensions of our image symmetrically around the original crop rectangle.

    top -= extraheight // 2
    bottom += extraheight // 2
    left -= extrawidth // 2
    right += extrawidth // 2
    

    And finally, do the crop:

    im = im.crop((left, top, right, bottom))
    

    For your image, let's say you get left = 93, right = 215, top = 49, and bottom = 205

    Before: enter image description here

    After: enter image description here