Q:

Cropping an image after detecting the corners using opencv python

I'm new one to openCV and computer-vision. Just now i'm trying to crop the Tiff scan after detecting the corners and then extract information from it based on exact coordinates x:y using python, openCV, numpy and OCR with Tesseract.
What i achieved right now is that i upload an image (scan), binarize it, fix rotation and remove empty spaces. Result is already good, but not good enough. My image is still always rotated little bit. Here is image example Example
Example(w/o Arrows)
The question is: How to detect these corners and crop everything outside them?
Here is my current code:
for filenumber in range(2,7):
    img = cv2.imread('img' + str(filenumber) + '.tif')

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.bitwise_not(gray)

    img = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 25, 11)
    kernel = np.ones((2, 2), np.uint8)
    img = cv2.erode(img, kernel, iterations=3)
    thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    coords = np.column_stack(np.where(thresh > 0))
    angle = cv2.minAreaRect(coords)[-1]

    if angle < -45:
        angle = -(90 + angle)
    else:
        angle = -angle
    # rotate the image to deskew it
    (h, w) = img.shape[:500]
    center = (w // 400, h // 400)
    M = cv2.getRotationMatrix2D(center, angle, 1)
    rotated = cv2.warpAffine(img, M, (w, h),
        flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
    # draw the correction angle on the image so we can validate it
    cv2.putText(rotated, "Angle: {:.2f} degrees".format(angle),
        (100, 400), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 2)

    img = rotated

    th, threshed = cv2.threshold(img, 240, 255, cv2.THRESH_BINARY_INV)
    ## (2) Morph-op to remove noise
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
    morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)

    ## (3) Find the max-area contour
    cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
    cnt = sorted(cnts, key=cv2.contourArea)[-1]
    ## (4) Crop and save it
    x,y,w,h = cv2.boundingRect(cnt)
    dst = img2[y:y+h, x:x+w]
    img = dst
    size_multiplier = szm = 1
    cv2.imwrite('img_' + str(filenumber) + '_Cropped' + '.jpg', img)

#Configs for OCR segments
for nnumb in range(2, 7):
    print('[INFO2]:   File=' + str(filenumber) + ';  nnumb=' + str(nnumb))
    if nnumb == 1:
        sub_image = img[130:130 + 90, 1220:1220 + 600]
        config = ('-l rus --oem 0 --psm 3 -c tessedit_char_whitelist="0123456789"')
    if nnumb == 2:
        sub_image = img[150:150 + 60, 1980:1980 + 460]
        config = ('-l rus --oem 1 --psm 3 -c tessedit_char_whitelist="0123456789"')
    if nnumb == 3:
        sub_image = img[230:230 + 70, 620:620 + 3000]
        config = ('-l rus --oem 0 --psm 3')
    if nnumb == 4:
        sub_image = img[410:410 + 70, 835:835 + 470]
        config = ('-l rus --oem 0 --psm 1 -c tessedit_char_whitelist="0123456789"')
    if nnumb == 5:
        sub_image = img[480:480 + 220, 610:610 + 1300]
        config = ('-l rus --oem 0 --psm 3')
    if nnumb == 6:
        sub_image = img[720:720 + 70, 110:110 + 500]
        config = ('-l rus --oem 0 --psm 3 -c tessedit_char_whitelist="0123456789"')

[
UPDATE: Final Code
def cornersandcrop(img):
    main_image = img
    main_imageF = main_image.copy()
    gray_image = main_image.copy()
    #Remove parts of image except corners
    gray_image[70:70 + 500, 70:70 + 500] = [255, 255, 255]
    gray_image[44:44 + 100, 1900:1900 + 550] = [255, 255, 255]
    gray_image[2270:2270 + 700, 45:45 + 200] = [255, 255, 255]
    gray_image[140:2880, 0:2500] = [255, 255, 255]
    gray_image[0:3000, 150:2350] = [255, 255, 255]

    gray_image = cv2.cvtColor(gray_image, cv2.COLOR_BGR2GRAY)
    gray_image = cv2.medianBlur(gray_image, 5)
    gray_image = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,11,20)
    kernel = np.ones((2, 2), np.uint8)
    gray_image = cv2.erode(gray_image, kernel, iterations=5)
    gray_image = cv2.dilate(gray_image, kernel, iterations=2)
    gray_image = cv2.morphologyEx(gray_image, cv2.MORPH_OPEN, np.ones((1, 1), np.uint8))

    template = cv2.imread('Templates\\Template_Corner_Top_Left.png', 0)
    template2 = cv2.imread('Templates\\Template_Corner_Top_Right.png', 0)
    template3 = cv2.imread('Templates\\Template_Corner_Bot_Right.png', 0)
    template4 = cv2.imread('Templates\\Template_Corner_Bot_Left.png', 0)

    width, height = template.shape[::-1] #get the width and height
    width2, height2 = template2.shape[::-1]
    width3, height3 = template3.shape[::-1]
    width4, height4 = template4.shape[::-1]

    match = cv2.matchTemplate(gray_image, template, cv2.TM_CCOEFF_NORMED)
    match2 = cv2.matchTemplate(gray_image, template2, cv2.TM_CCOEFF_NORMED)
    match3 = cv2.matchTemplate(gray_image, template3, cv2.TM_CCOEFF_NORMED)
    match4 = cv2.matchTemplate(gray_image, template4, cv2.TM_CCOEFF_NORMED)

    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(match)
    top_Pos1 = max_loc
    Pos1 = (top_Pos1[0] + width-115, top_Pos1[1] + height-115)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(match2)
    top_Pos2 = max_loc
    Pos2 = (top_Pos2[0] + width2-5, top_Pos2[1] + height2-115)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(match3)
    top_Pos3 = max_loc
    Pos3 = (top_Pos3[0] + width3-5, top_Pos3[1] + height3-5)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(match4)
    top_Pos4 = max_loc
    Pos4 = (top_Pos4[0] + width4-115, top_Pos4[1] + height4-5)

    src_pts = np.array([Pos1, Pos2, Pos3, Pos4], dtype=np.float32)
    dst_pts = np.array([[0, 0],   [3000, 0],  [3000, 2500], [0, 2500]], dtype=np.float32)
    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
    warp = cv2.warpPerspective(main_imageF, M, (3000, 2500))
    warp = cv2.resize(warp, (int(2500), int(3000)),fx=1, fy=1, interpolation = cv2.INTER_CUBIC)
return (warp)

A:

This works for me in Python/OpenCV for locating one corner using template matching. Just make the template image larger than your corner so that there is some white around it.
Input:

Template:

import cv2
import numpy as np

# read image
img = cv2.imread('drawing.jpg')

# convert img to grayscale
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# read template as grayscale
tmplt = cv2.imread('corner_ul.png', cv2.IMREAD_GRAYSCALE)
hh, ww = tmplt.shape

# define corner intersection in template
offset_x = 23
offset_y = 28

# do template matching
corrimg = cv2.matchTemplate(img_gray,tmplt,cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(corrimg)
max_val_ncc = '{:.3f}'.format(max_val)
print("normalize_cross_correlation: " + max_val_ncc)
xx = max_loc[0]
yy = max_loc[1]
corner_x = xx + offset_x
corner_y = yy + offset_y
print('xmatchloc =',xx,'ymatch =',yy)
print('cornerlocx =',corner_x,'cornerlocy =',corner_y)

# draw template bounds and corner intersection in red onto img
result = img.copy()
cv2.rectangle(result, (xx, yy), (xx+ww, yy+hh), (0, 0, 255), 2)
cv2.circle(result, (corner_x,corner_y), 1, (0, 0, 255), 2)

cv2.imshow('image', img)
cv2.imshow('template', tmplt)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save results
cv2.imwrite('drawing_template_match_location.jpg', result)

Information:
normalize_cross_correlation: 1.000
xmatchloc = 0 ymatch = 318
cornerlocx = 23 cornerlocy = 346

Result:

You can also refine the result to get sub-pixel accuracy. See https://www.bbsmax.com/A/lk5aBbGod1/
Rotate the template 3 times each by 90 degrees to form the other 3 templates, measure or compute the offsets from the top left corner to the intersection of the corner lines and do the template matching. Then once you have all 4 matches, you can do your cropping using numpy slicing.

