示例#1
0
def get_contour(rescaled_image):

    MORPH = 9
    CANNY = 84
    HOUGH = 25

    IM_HEIGHT, IM_WIDTH, _ = rescaled_image.shape

    gray = cv2.cvtColor(rescaled_image, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (7, 7), 0)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (MORPH, MORPH))
    dilated = cv2.dilate(gray, kernel)

    edged = cv2.Canny(dilated, 0, CANNY)
    test_corners = get_corners(edged)

    approx_contours = []

    if len(test_corners) >= 4:
        quads = []

        for quad in itertools.combinations(test_corners, 4):
            points = np.array(quad)
            points = transform.order_points(points)
            points = np.array([[p] for p in points], dtype="int32")
            quads.append(points)

        quads = sorted(quads, key=cv2.contourArea, reverse=True)[:5]
        quads = sorted(quads, key=angle_range)

        approx = quads[0]
        if is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
            approx_contours.append(approx)

    (_, cnts, hierarchy) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
                                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]

    for c in cnts:
        approx = cv2.approxPolyDP(c, 80, True)
        if is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
            approx_contours.append(approx)
            break

    if not approx_contours:
        TOP_RIGHT = (IM_WIDTH, 0)
        BOTTOM_RIGHT = (IM_WIDTH, IM_HEIGHT)
        BOTTOM_LEFT = (0, IM_HEIGHT)
        TOP_LEFT = (0, 0)
        screenCnt = np.array([[TOP_RIGHT], [BOTTOM_RIGHT], [BOTTOM_LEFT],
                              [TOP_LEFT]])

    else:
        screenCnt = max(approx_contours, key=cv2.contourArea)

    return screenCnt.reshape(4, 2)
示例#2
0
 def run_on_image(self, image):
     detect_passport_predictions = self.predictor(image)
     mask = detect_passport_predictions["instances"].pred_masks.numpy()[0]
     mask = np.array(mask, dtype=np.uint8) * 255
     _, contours, _ = cv2.findContours(mask, cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)
     rect = cv2.minAreaRect(contours[0])
     box = cv2.boxPoints(rect)
     box = np.int0(box)
     passport = four_point_transform(image, order_points(box))
     return passport
示例#3
0
def get_image(im, id):
    #resize image to fit screen
    r = 1000.0 / im.shape[1]
    dim = (1000, int(im.shape[0] * r))
    resized = cv2.resize(im, dim, interpolation=cv2.INTER_AREA)

    #convert image to grayscale
    grayscaled = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)

    (x, y, dst_norm_scaled) = cornerHarris_demo(125, grayscaled)

    xmin = int(round(x - (resized.shape[0] * .06)))
    xmax = int(round(x + (resized.shape[0] * .06)))
    ymin = int(round(y - (resized.shape[0] * .06)))
    ymax = int(round(y + (resized.shape[0] * .06)))
    cropped = resized[xmin:xmax, ymin:ymax]
    gray_cropped = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY)

    pts = get_corners(gray_cropped, id)
    ordered = transform.order_points(pts)
    warped = transform.four_point_transform(gray_cropped, ordered)
    return warped
示例#4
0
def getChessContour(img):
    blurred = cv2.GaussianBlur(img, (5, 5), 0.0)
    unsharp = cv2.addWeighted(img, 3, blurred, -2, 0, img)
    img = unsharp
    M, ideal_grid, grid_next, grid_good, spts = findChessboard(img)
    # View
    if M is not None:
        M, _ = generateNewBestFit((ideal_grid+8)*32, grid_next, grid_good) # generate mapping for warping image

        img_warp = cv2.warpPerspective(img, M, (17*32, 17*32), flags=cv2.WARP_INVERSE_MAP)
        best_lines_x, best_lines_y = getBestLines(img_warp)
        min_x, max_x, min_y, max_y = best_lines_x[0], best_lines_x[-1], best_lines_y[0], best_lines_y[-1]
        width, height = max_x - min_x, max_y - min_y
        min_x -= width/6
        max_x += width/6
        min_y -= height/6
        max_y += height/6
        x,y = np.meshgrid([min_x, max_x], [min_y, max_y])
        xy = np.vstack([x.flatten(), y.flatten()]).T.astype(np.float32)
        xy = np.expand_dims(xy,0)
        xy_unwarp = cv2.perspectiveTransform(xy, M)
        pts = order_points(xy_unwarp[0,:,:])
        return [np.array(pts, dtype=np.int32)]
            T_marker = np.array([markerLength, markerLength, 0.0])
            A = np.array([0.0, 0.0, 0.0]) + T_marker
            B = np.array([0.4 + markerSeparation, 0.0, 0.0]) + T_marker
            C = np.array([0.4 + markerSeparation, 0.4 + markerSeparation, 0.0
                          ]) + T_marker
            D = np.array([0.0, 0.4 + markerSeparation, 0.0]) + T_marker
            ### Find Transformatio Matrix ###
            rotM = np.zeros(shape=(3, 3))
            cv2.Rodrigues(rvec, rotM, jacobian=0)
            ### Map to image coordinate ###
            pts, jac = cv2.projectPoints(
                np.float32([A, B, C, D]).reshape(-1, 3), rvec, tvec,
                cameraMatrix, dist)
            pts = np.array([tuple(pts[i].ravel()) for i in range(4)],
                           dtype="float32")
            pts = order_points(pts)
            ### Draw axis ###
            for point in [A, B, C, D]:
                cv2.aruco.drawAxis(image=frame,
                                   cameraMatrix=cameraMatrix,
                                   distCoeffs=dist,
                                   rvec=rvec,
                                   tvec=tvec + np.dot(point, rotM.T),
                                   length=0.1)
            ### Draw work space ###
            # drawBox(frame, rvec, tvec + np.dot(A, rotM.T), size=0.4 + markerSeparation)

        ## Fill Marker ##
        for corner, id in zip(markerCorners, markerIds):
            points = [(int(point[0]), int(point[1])) for point in corner[0]]
            ids = id[0]
#Finding contours from main image
contours, hierarchy = cv2.findContours(binary_image_gray.copy(), cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    area = cv2.contourArea(c)

    #Selecting the upper rectangle
    if ((len(approx) == 4) & (area > 1000000) & (area < 2000000)):
        cv2.drawContours(img, [c], -1, (0, 255, 0), 3)
        box = cv2.minAreaRect(c)
        box = cv2.cv.BoxPoints(box)
        box = np.array(box, dtype="int")
        rect = transform.order_points(box)
        x1, y1 = rect[0]
        x2, y2 = rect[2]
        hig = x2 - x1
        wid = y2 - y1

        #Getting transformation from image (avoid rotations)
        warped = transform.four_point_transform(binary_image_gray, box)
        warped2 = transform.four_point_transform(final, box)
        warped3 = transform.four_point_transform(img, box)
        arr = []
        number = 0

        #Finding countours again from the transformed image
        contours2, hierarchy2 = cv2.findContours(warped2.copy(), cv2.RETR_TREE,
                                                 cv2.CHAIN_APPROX_SIMPLE)
示例#7
0
		if cv.contourArea( cont ) < 20000 : continue

		# print(cv.contourArea( cont ))
		arc_len = cv.arcLength( cont, True )
		approx = cv.approxPolyDP( cont, 0.1 * arc_len, True )

		if ( len( approx ) != 4 ): continue
		IS_FOUND = True

		box = np.array([
			approx[0][0],
			approx[1][0],
			approx[2][0],
			approx[3][0]], dtype = "float32")

		box = order_points(box)

		col2 = col
		row2 = col*RATIO

		des = np.float32([[0,0],[row2,0],[row2,col2],[0,col2]])

		M = cv.getPerspectiveTransform(box, des)

		dst = cv.warpPerspective(img,M,(int(row2),col2))

		# cv.drawContours( img, [approx], -1, ( 255, 0, 0 ), 2 )
		for i in range(4):
			# cv.circle(img, (box[i][0], box[i][1]), 3, (0,255,0), 3)
			cv.putText(img, str(i), (box[i][0], box[i][1]), cv.FONT_HERSHEY_PLAIN, 2.0, (0, 0, 255), 2)
		# print(ymin, ymax, xmin, xmax)
def main():
    # cap = cv2.VideoCapture(cv2.CAP_DSHOW)
    # codec = 0x47504A4D  # MJPG
    # cap.set(cv2.CAP_PROP_FPS, 30.0)
    # cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('m','j','p','g'))
    # cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('M','J','P','G'))
    # cap.set(3, 1920)
    # cap.set(4, 1080)

    cap = cv2.VideoCapture("../K.mp4")
    img_list = []
    valid_list = []
    bg_list = []
    ret, img = cap.read()
    frame_counter = 0
    N = 1000

    parameters = cv2.aruco.DetectorParameters_create()
    # parameters(doCornerRefinement=True)
    # markerLength=0.039 # real
    # markerSeparation=0.0975 # real
    markerLength = 0.04
    markerSeparation = 0.01
    # board = cv2.aruco.GridBoard_create(markersX=10, markersY=10, markerLength=0.039, markerSeparation=0.0975, dictionary=dictionary) # real
    board = cv2.aruco.GridBoard_create(markersX=10,
                                       markersY=10,
                                       markerLength=markerLength,
                                       markerSeparation=markerSeparation,
                                       dictionary=dictionary)
    backSub = cv2.createBackgroundSubtractorMOG2(history=30,
                                                 varThreshold=16,
                                                 detectShadows=False)
    # backSub = cv2.createBackgroundSubtractorKNN(history=30, dist2Threshold=1000.0, detectShadows=True)

    for i in range(N):
        ret, frame = cap.read()
        original = frame.copy()
        markerCorners, markerIds, _ = cv2.aruco.detectMarkers(
            frame, dictionary, parameters=parameters)
        if markerIds is not None:
            ret, _, _ = cv2.aruco.estimatePoseBoard(corners=markerCorners,
                                                    ids=markerIds,
                                                    board=board,
                                                    cameraMatrix=cameraMatrix,
                                                    distCoeffs=dist,
                                                    rvec=rvec,
                                                    tvec=tvec)
            if ret:

                # cv2.aruco.drawAxis(image=frame, cameraMatrix=cameraMatrix, distCoeffs=dist, rvec=rvec, tvec=tvec, length=0.1) # origin
                T_marker = np.array([markerLength, markerLength, 0.0])
                A = np.array([0.0, 0.0, 0.0]) + T_marker
                B = np.array([0.4 + markerSeparation, 0.0, 0.0]) + T_marker
                C = np.array([
                    0.4 + markerSeparation, 0.4 + markerSeparation, 0.0
                ]) + T_marker
                D = np.array([0.0, 0.4 + markerSeparation, 0.0]) + T_marker
                ### Find Transformatio Matrix ###
                rotM = np.zeros(shape=(3, 3))
                cv2.Rodrigues(rvec, rotM, jacobian=0)
                ### Map to image coordinate ###
                pts, jac = cv2.projectPoints(
                    np.float32([A, B, C, D]).reshape(-1, 3), rvec, tvec,
                    cameraMatrix, dist)
                pts = np.array([tuple(pts[i].ravel()) for i in range(4)],
                               dtype="float32")
                pts = order_points(pts)
                ### Draw axis ###
                for point in [A, B, C, D]:
                    cv2.aruco.drawAxis(image=frame,
                                       cameraMatrix=cameraMatrix,
                                       distCoeffs=dist,
                                       rvec=rvec,
                                       tvec=tvec + np.dot(point, rotM.T),
                                       length=0.1)
                ### Draw work space ###
                # drawBox(frame, rvec, tvec + np.dot(A, rotM.T), size=0.4 + markerSeparation)

            ## Fill Marker ##
            for corner, id in zip(markerCorners, markerIds):
                points = [(int(point[0]), int(point[1]))
                          for point in corner[0]]
                ids = id[0]
                pts1 = np.array(points, np.int32)
                cv2.fillPoly(frame, [pts1], 255)
            ## Perspective Crop ##
            warped = four_point_transform(original, pts)
            warped = cv2.resize(warped, (800, 800))
            valid_mask = four_point_transform(
                np.ones(original.shape[:2], dtype="uint8") * 255, pts)
            valid_mask = cv2.resize(valid_mask, (800, 800))
            cv2.imshow("Warped", warped)
            cv2.imshow("Valid", valid_mask)
            cv2.waitKey(1)
            img_list.append(warped)
            valid_list.append(valid_mask)

    frame_counter = 0
    for i in range(N):
        index = random.randint(0, len(img_list) - 1)
        warped = img_list[index]
        valid_mask = valid_list[index]
        frame_counter += 1
        if frame_counter > 500 and random.randint(0, N - frame_counter) < 200:
            warped_final = bg_list[random.randint(0, len(bg_list) - 1)]
        else:
            if frame_counter < 200:
                mean_canvas = np.ones(warped.shape, dtype="uint8") * 200
                mean_canvas = cv2.bitwise_or(mean_canvas,
                                             mean_canvas,
                                             mask=cv2.bitwise_not(valid_mask))
            else:
                mean_canvas = cv2.bitwise_or(bg,
                                             bg,
                                             mask=cv2.bitwise_not(valid_mask))
                if frame_counter % 8 == 0: bg_list.append(bg)
            valid_mask = cv2.bitwise_or(warped, warped, mask=valid_mask)
            warped_final = cv2.bitwise_or(mean_canvas, valid_mask)
        cv2.imshow('Passed', warped_final)
        fgMask = backSub.apply(cv2.GaussianBlur(warped_final, (5, 5), 0))
        cv2.imshow('FG Mask', fgMask)
        bg = backSub.getBackgroundImage()
        cv2.imshow('BG', bg)

        # cv2.imshow("Preview", frame)
        # cv2.imshow("marker33", markerImage)
        key = cv2.waitKey(1)
        if key == 27:
            break
        if key == ord('m'):
            mode = not mode
        if key == ord(' '):
            cv2.imwrite(str(i) + ".jpg", warped)
            i += 1
    # cv2.imwrite("Real5.png", bg)
    cv2.waitKey(0)
    cap.release()
    cv2.destroyAllWindows()
    return bg
示例#9
0
def scan():
    if not request.stream:
        return "", 400

    head = request.stream.read(1024)
    mime_detector = magic.Magic(mime=True)
    mime = mime_detector.from_buffer(head)
    if not mime.startswith("image/"):
        return "", 400

    buf = head + request.stream.read()
    nparr = np.frombuffer(buf, np.uint8)
    image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    ratio = image.shape[0] / 500.0
    orig = image.copy()

    image = imutils.resize(image, height=500)
    IM_HEIGHT, IM_WIDTH, _ = image.shape

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (7, 7), 0)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9))
    dilated = cv2.dilate(gray, kernel)

    edged = cv2.Canny(dilated, 0, 84)
    lsd = cv2.createLineSegmentDetector()
    lines = lsd.detect(edged)[0]

    corners = []
    if lines is not None:
        lines = lines.squeeze().astype(np.int32).tolist()
        horizontal_lines_canvas = np.zeros(edged.shape, dtype=np.uint8)
        vertical_lines_canvas = np.zeros(edged.shape, dtype=np.uint8)
        for line in lines:
            x1, y1, x2, y2 = line
            if abs(x2 - x1) > abs(y2 - y1):
                (x1, y1), (x2, y2) = sorted(((x1, y1), (x2, y2)),
                                            key=lambda pt: pt[0])
                cv2.line(horizontal_lines_canvas, (max(x1 - 5, 0), y1),
                         (min(x2 + 5, edged.shape[1] - 1), y2), 255, 2)

            else:
                (x1, y1), (x2, y2) = sorted(((x1, y1), (x2, y2)),
                                            key=lambda pt: pt[1])
                cv2.line(vertical_lines_canvas, (x1, max(y1 - 5, 0)),
                         (x2, min(y2 + 5, edged.shape[0] - 1)), 255, 2)

        lines = []
        contours = cv2.findContours(horizontal_lines_canvas, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_NONE)[1]
        contours = sorted(contours,
                          key=lambda c: cv2.arcLength(c, True),
                          reverse=True)[:2]
        horizontal_lines_canvas = np.zeros(edged.shape, dtype=np.uint8)
        for contour in contours:
            contour = contour.reshape((contour.shape[0], contour.shape[2]))
            min_x = np.amin(contour[:, 0], axis=0) + 2
            max_x = np.amax(contour[:, 0], axis=0) - 2
            left_y = int(np.average(contour[contour[:, 0] == min_x][:, 1]))
            right_y = int(np.average(contour[contour[:, 0] == max_x][:, 1]))
            lines.append((min_x, left_y, max_x, right_y))
            cv2.line(horizontal_lines_canvas, (min_x, left_y),
                     (max_x, right_y), 1, 1)
            corners.append((min_x, left_y))
            corners.append((max_x, right_y))

        contours = cv2.findContours(vertical_lines_canvas, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_NONE)[1]
        contours = sorted(contours,
                          key=lambda c: cv2.arcLength(c, True),
                          reverse=True)[:2]
        vertical_lines_canvas = np.zeros(edged.shape, dtype=np.uint8)
        for contour in contours:
            contour = contour.reshape((contour.shape[0], contour.shape[2]))
            min_y = np.amin(contour[:, 1], axis=0) + 2
            max_y = np.amax(contour[:, 1], axis=0) - 2
            top_x = int(np.average(contour[contour[:, 1] == min_y][:, 0]))
            bottom_x = int(np.average(contour[contour[:, 1] == max_y][:, 0]))
            lines.append((top_x, min_y, bottom_x, max_y))
            cv2.line(vertical_lines_canvas, (top_x, min_y), (bottom_x, max_y),
                     1, 1)
            corners.append((top_x, min_y))
            corners.append((bottom_x, max_y))

        corners_y, corners_x = np.where(horizontal_lines_canvas +
                                        vertical_lines_canvas == 2)
        corners += zip(corners_x, corners_y)

    test_corners = []
    for c in corners:
        if predicate(test_corners, c):
            test_corners.append(c)

    approx_contours = []
    if len(test_corners) >= 4:
        quads = []
        for quad in itertools.combinations(test_corners, 4):
            points = np.array(quad)
            points = transform.order_points(points)
            points = np.array([[p] for p in points], dtype="int32")
            quads.append(points)

        quads = sorted(quads, key=cv2.contourArea, reverse=True)[:5]
        quads = sorted(quads, key=angle_range)

        approx = quads[0]
        if is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
            approx_contours.append(approx)

    (_, cnts, hierarchy) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
                                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
    for c in cnts:
        approx = cv2.approxPolyDP(c, 80, True)
        if is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
            approx_contours.append(approx)
            break

    screenCnt = None
    if not approx_contours:
        TOP_RIGHT = (IM_WIDTH, 0)
        BOTTOM_RIGHT = (IM_WIDTH, IM_HEIGHT)
        BOTTOM_LEFT = (0, IM_HEIGHT)
        TOP_LEFT = (0, 0)
        screenCnt = np.array([[TOP_RIGHT], [BOTTOM_RIGHT], [BOTTOM_LEFT],
                              [TOP_LEFT]])

    else:
        screenCnt = max(approx_contours, key=cv2.contourArea)

    screenCnt = screenCnt.reshape(4, 2)
    warped = transform.four_point_transform(orig, screenCnt * ratio)

    gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
    sharpen = cv2.GaussianBlur(gray, (0, 0), 3)
    sharpen = cv2.addWeighted(gray, 1.5, sharpen, -0.5, 0)
    thresh = cv2.adaptiveThreshold(sharpen, 255,
                                   cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                   cv2.THRESH_BINARY, 21, 15)

    scan = cv2.imencode(mimetypes.guess_extension(mime), thresh)[1]

    return send_file(io.BytesIO(scan),
                     mimetype=mime,
                     as_attachment=True,
                     attachment_filename="Scan")
示例#10
0
    def get_contour(self, rescaled_image):
        """
        Returns a numpy array of shape (4, 2) containing the vertices of the four corners
        of the document in the image. It considers the corners returned from get_corners()
        and uses heuristics to choose the four corners that most likely represent
        the corners of the document. If no corners were found, or the four corners represent
        a quadrilateral that is too small or convex, it returns the original four corners.
        """

        # these constants are carefully chosen
        MORPH = 9
        CANNY = 84
        HOUGH = 25

        IM_HEIGHT, IM_WIDTH, _ = rescaled_image.shape

        # convert the image to grayscale and blur it slightly
        gray = cv2.cvtColor(rescaled_image, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (7,7), 0)

        # dilate helps to remove potential holes between edge segments
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
        dilated = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel)

        # find edges and mark them in the output map using the Canny algorithm
        edged = cv2.Canny(dilated, 0, CANNY)
        test_corners = self.get_corners(edged)

        approx_contours = []

        if len(test_corners) >= 4:
            quads = []

            for quad in itertools.combinations(test_corners, 4):
                points = np.array(quad)
                points = transform.order_points(points)
                points = np.array([[p] for p in points], dtype = "int32")
                quads.append(points)

            # get top five quadrilaterals by area
            quads = sorted(quads, key=cv2.contourArea, reverse=True)[:5]
            # sort candidate quadrilaterals by their angle range, which helps remove outliers
            quads = sorted(quads, key=self.angle_range)

            approx = quads[0]
            if self.is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
                approx_contours.append(approx)

            # for debugging: uncomment the code below to draw the corners and countour found 
            # by get_corners() and overlay it on the image

            # cv2.drawContours(rescaled_image, [approx], -1, (20, 20, 255), 2)
            # plt.scatter(*zip(*test_corners))
            # plt.imshow(rescaled_image)
            # plt.show()

        # also attempt to find contours directly from the edged image, which occasionally 
        # produces better results
        (cnts, hierarchy) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]

        # loop over the contours
        for c in cnts:
            # approximate the contour
            approx = cv2.approxPolyDP(c, 80, True)
            if self.is_valid_contour(approx, IM_WIDTH, IM_HEIGHT):
                approx_contours.append(approx)
                break

        # If we did not find any valid contours, just use the whole image
        if not approx_contours:
            TOP_RIGHT = (IM_WIDTH, 0)
            BOTTOM_RIGHT = (IM_WIDTH, IM_HEIGHT)
            BOTTOM_LEFT = (0, IM_HEIGHT)
            TOP_LEFT = (0, 0)
            screenCnt = np.array([[TOP_RIGHT], [BOTTOM_RIGHT], [BOTTOM_LEFT], [TOP_LEFT]])

        else:
            screenCnt = max(approx_contours, key=cv2.contourArea)
            
        return screenCnt.reshape(4, 2)