使用opencv python检测角点后裁剪图像

2024-09-27 00:21:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我是openCV和计算机视觉的新手。刚才我正在尝试在检测到角点后裁剪Tiff扫描,然后使用python、openCV、numpy和OCR以及Tesseract根据精确的坐标x:y从中提取信息

我现在实现的是上传一个图像(扫描),对其进行二值化,修复旋转并删除空白。结果已经很好了,但还不够好。我的图像仍然总是有点旋转。下面是图像示例ExampleExample(w/o Arrows)

问题是:如何检测这些角点并裁剪它们之外的所有东西

这是我目前的代码:

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"')

[Result After first try

更新:最终代码

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)

Tags: imageimgiftopnpvalmincv2
2条回答

这对我在Python/OpenCV中使用模板匹配定位一个角非常有用。只需使模板图像大于您的角落,使其周围有一些白色

输入:

enter image description here

模板:

enter image description here

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)


资料:

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


结果:

enter image description here

您还可以优化结果以获得亚像素精度。见https://www.bbsmax.com/A/lk5aBbGod1/

将模板旋转3次,每次旋转90度,以形成其他3个模板,测量或计算从左上角到角线交点的偏移,并进行模板匹配。一旦你有了所有的4个匹配项,你就可以使用numpy切片进行裁剪了

我建议通过“调整过滤器”使用模板匹配:

  • 将图像转换为二进制图像(正如您所做的),并使用关闭而不是腐蚀
  • 将图像转换为“-1”s和“1”s:将0替换为-1,将255替换为1
  • 构建L形内核h(用于查找左下角):
    • -1放在im中的值需要为-1的地方,将1放在值需要为1的地方
    • 确保h中L形的角位于中心(这有点浪费-您可以避免它,稍后再固定位置)
    • 内核示例(小规模):
      0 -1 1 0 0
      0 -1 1 0 0
      0 -1 1 1 1
      0 -1 -1 -1 -1
      0 0 0 0 0
  • 使用内核h筛选im-输出的最大值是最匹配h的位置
  • 查找过滤图像最大值的x、y坐标

下面是找到左下角的代码示例:

import numpy as np
import cv2

img = cv2.imread('img1.tif')
orig_img = img.copy()

gray = 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)
img = cv2.morphologyEx(img, cv2.MORPH_OPEN, np.ones((3, 3), np.uint8))  # Opening - remove white dots around corners.

# cv2.imwrite('img.tif', img)

# Convert from 0 to -1 and 255 to 1 (im is "binary" image with "-1"s and "1"s).
im = img.astype(float) / 127.5 - 1

# Build L shape kernel h that matches the L shape we want to search.
# Place "-1" where value in im needs to be "-1", and "1" when value needs to be "1".
# Make sure the corner of the L shape in h is at the center (it's a bit of a waste).
# Example for kernel (small scale):
#   0 -1  1  0  0
#   0 -1  1  0  0
#   0 -1  1  1  1
#   0 -1 -1 -1 -1
#   0  0  0  0  0

h = np.zeros((75, 75))  # Kernel size is 75x75
h[0:37, 37:39] = 1  # Two columns of "1"s from top to center
h[36:38, 37:] = 1   # Two rows of "1"s from center to right side
h[0:39, 36] = -1    # One column of "-1"s
h[38, 36:] = -1     # One row of "-1"s

# Save h kernel as an image for testing
h2 = h.copy()
h2 = ((h2+1)*127.5).astype(np.uint8)
cv2.imwrite('h2.png', h2)

# Filter im with kernel h - the maximum value of the output is the position that best matches h
imf = cv2.filter2D(im, -1, h)

# Find index of maximum value from 2D numpy array
pos_y, pos_x = np.where(imf == np.amax(imf))

# Draw red circle around coordinate (pos_x, pos_y) for testing.
cv2.circle(orig_img, (int(pos_x), int(pos_y)), 8, (0, 0, 255), thickness=2)
cv2.imwrite('circled_im.png', orig_img)  # Save image for testing

结果(左下角):
bottom left corner

筛选内核(作为映像):
Filter kernel (as an image)


更新:

如果还有其他“L形”对象,您可能需要使用更“激进”的内核

例如:

# More "aggressive" kernel
h = np.zeros((75, 75))  # Kernel size is 75x75
h[0:37, 37:41] = 1  # 4 columns of "1"s from top to center
h[34:38, 37:] = 1   # 4 rows of "1"s from center to right side
h[0:39, 36] = -1    # One column of "-1"s
h[38, 36:] = -1     # One row of "-1"s
h[0:34, 41] = -1  # 1 columns of "-1"s from top to center
h[33, 41:] = -1   # 1 rows of "-1"s from center to right side

enter image description here

相关问题 更多 >

    热门问题