Image Perspective Corrector

import cv2

import numpy as np


# ----------------------------------

# Order points correctly

# ----------------------------------

def order_points(pts):

    rect = np.zeros((4, 2), dtype="float32")


    s = pts.sum(axis=1)

    rect[0] = pts[np.argmin(s)]   # top-left

    rect[2] = pts[np.argmax(s)]   # bottom-right


    diff = np.diff(pts, axis=1)

    rect[1] = pts[np.argmin(diff)]  # top-right

    rect[3] = pts[np.argmax(diff)]  # bottom-left


    return rect



# ----------------------------------

# Perspective transform

# ----------------------------------

def four_point_transform(image, pts):

    rect = order_points(pts)

    (tl, tr, br, bl) = rect


    widthA = np.linalg.norm(br - bl)

    widthB = np.linalg.norm(tr - tl)

    maxWidth = max(int(widthA), int(widthB))


    heightA = np.linalg.norm(tr - br)

    heightB = np.linalg.norm(tl - bl)

    maxHeight = max(int(heightA), int(heightB))


    dst = np.array([

        [0, 0],

        [maxWidth - 1, 0],

        [maxWidth - 1, maxHeight - 1],

        [0, maxHeight - 1]

    ], dtype="float32")


    M = cv2.getPerspectiveTransform(rect, dst)

    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))


    return warped



# ----------------------------------

# Detect document contour

# ----------------------------------

def detect_document(image):

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    blurred = cv2.GaussianBlur(gray, (5, 5), 0)


    edged = cv2.Canny(blurred, 75, 200)


    contours, _ = cv2.findContours(

        edged.copy(),

        cv2.RETR_LIST,

        cv2.CHAIN_APPROX_SIMPLE

    )


    contours = sorted(contours, key=cv2.contourArea, reverse=True)


    for contour in contours:

        peri = cv2.arcLength(contour, True)

        approx = cv2.approxPolyDP(contour, 0.02 * peri, True)


        if len(approx) == 4:

            return approx.reshape(4, 2)


    return None



# ----------------------------------

# Main

# ----------------------------------

if __name__ == "__main__":

    path = input("Enter image path: ").strip()


    image = cv2.imread(path)


    if image is None:

        print(" Could not load image.")

        exit()


    orig = image.copy()

    doc_cnt = detect_document(image)


    if doc_cnt is None:

        print(" Document edges not detected.")

        exit()


    warped = four_point_transform(orig, doc_cnt)


    # Convert to scanned look

    warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)

    warped_thresh = cv2.adaptiveThreshold(

        warped_gray, 255,

        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

        cv2.THRESH_BINARY,

        11, 2

    )


    cv2.imshow("Original", orig)

    cv2.imshow("Scanned Output", warped_thresh)


    cv2.imwrite("scanned_output.jpg", warped_thresh)

    print(" Saved as scanned_output.jpg")


    cv2.waitKey(0)

    cv2.destroyAllWindows()


No comments: