Home > OS >  Cropping in OpenCV return images rotated 90 degrees
Cropping in OpenCV return images rotated 90 degrees

Time:07-21

I want to crop images according to their right frame. I have about 10000 of hand X-ray images to preprocess, and what I have done so far:

  1. Apply Gaussian Blur and Threshold (Binary Otsu) on the image.
  2. Apply dilation to get a single object (in this case a hand).
  3. Used cv2.findContours() to draw outline along the edges around the hand.
  4. Used cv2.boundingRect() to find the right frame, and then cv2.minAreaRect() and cv2.boxPoints to get the right points for the bounding box.
  5. Used cv2.warpPerspective to adjust image according to height and width.

The code below describes the above:

import os
import cv2
import numpy as np
from matplotlib import pyplot as plt

# Load image, create mask, grayscale, Gaussian blur, Otsu's threshold
img_path = "sample_image.png"

image = cv2.imread(image_path)
original = image.copy()
blank = np.zeros(image.shape[:2], dtype = np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (33,33), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY   cv2.THRESH_OTSU)[1]

# Merge text into a single contour
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate = cv2.dilate(thresh, kernel, iterations = 3)

# Find contours
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key = lambda x: cv2.boundingRect(x)[0])

for c in cnts:
    # Filter using contour area and aspect ratio (x1 = width, y1 = height)
    x, y, x1, y1 = cv2.boundingRect(c)
    if (x1 > 500) and (y1 > 700):
        rect = cv2.minAreaRect(c)
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        width = int(rect[1][0])
        height = int(rect[1][1])
        src_pts = box.astype("float32")
        dst_pts = np.array([[0, height-1], [0, 0],
                            [width-1, 0], [width-1, height-1]], dtype="float32")

        M = cv2.getPerspectiveTransform(src_pts, dst_pts)
        warped = cv2.warpPerspective(image, M, (width, height))
        plt.imshow(warped)

If you have a look at enter image description here

  • Output images: enter image description here

  • I doubt it's because of the quality of the images. Maybe it's got to do with OpenCV's minAreaRect()? or boxPoints?

    FINAL UPDATE:

    According to @Prashant Maurya, the code was updated with a function added to detect whether the position of the hand is left or right. And then mapping src_pts to right dst_pts. Full code is shown below.

    CodePudding user response:

    Hi there are two changes which will correct the output:

    • The width and height taken in the code is in the wrong order ie: width: 1470 & height: 1118 just switch the values:
    • Map src_pts to right dst_pts the current code is mapping top left corner to bottom left therefore the image is being rotated.
    • Added function to detect whether image is right tilted or left and rotate and rotate it accordingly

    Full code with changes is:

    import os
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    # Load image, create mask, grayscale, Gaussian blur, Otsu's threshold
    img_path = "xray1.png"
    
    image = cv2.imread(img_path)
    cv2.imshow("image original", image)
    cv2.waitKey(10000)
    original = image.copy()
    blank = np.zeros(image.shape[:2], dtype = np.uint8)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (33,33), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY   cv2.THRESH_OTSU)[1]
    
    # Merge text into a single contour
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
    dilate = cv2.dilate(thresh, kernel, iterations = 3)
    
    # Find contours
    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    cnts = sorted(cnts, key = lambda x: cv2.boundingRect(x)[0])
    
    def get_tilt(box):
        tilt = "Left"
        x_list = [coord[0] for coord in box]
        y_list = [coord[1] for coord in box]
    
        print(x_list)
        print(y_list)
    
        x_list = sorted(x_list)
        y_list = sorted(y_list)
    
        print(x_list)
        print(y_list)
    
        for coord in box:
            if coord[0] == x_list[0]:
                index = y_list.index(coord[1])
                print("Index: ", index)
                if index == 1:
                    tilt = "Left"
                else:
                    tilt = "Right"
    
        return tilt
    
    
    for c in cnts:
        # Filter using contour area and aspect ratio (x1 = width, y1 = height)
        x, y, x1, y1 = cv2.boundingRect(c)
        if (x1 > 500) and (y1 > 700):
            rect = cv2.minAreaRect(c)
            print("rect",rect)
            box = cv2.boxPoints(rect)
            box = np.int0(box)
            # print("rect:", box)
            tilt = get_tilt(box)
    
            src_pts = box.astype("float32")
    
            if tilt == "Left":
                width = int(rect[1][1])
                height = int(rect[1][0])
                dst_pts = np.array([[0, 0],
                                    [width-1, 0], [width-1, height-1], [0, height-1]], dtype="float32")
            else:
                width = int(rect[1][0])
                height = int(rect[1][1])
                dst_pts = np.array([[0, height-1], [0, 0],
                                [width-1, 0], [width-1, height-1]], dtype="float32")
    
            print("Src pts:", src_pts)
            print("Dst pts:", dst_pts)
            M = cv2.getPerspectiveTransform(src_pts, dst_pts)
            warped = cv2.warpPerspective(image, M, (width, height))
            print("Showing image ..")
            # plt.imshow(warped)
            cv2.imshow("image crop", warped)
            cv2.waitKey(10000)
    
    • Related