Обрезка изображения после определения углов с помощью opencv python - PullRequest
1 голос
/ 26 февраля 2020

Я новичок в openCV и компьютерном зрении. Просто сейчас я пытаюсь обрезать сканирование Tiff после обнаружения углов, а затем извлечь из него информацию на основе точных координат x: y, используя python, openCV, numpy и OCR с Tesseract.

Что прямо сейчас я достиг того, что я загружаю изображение (сканирую), преобразовываю его в двоичную форму, исправляю вращение и удаляю пустые места. Результат уже хорош, но недостаточно хорош. Мое изображение все еще всегда немного поворачивается. Вот пример изображения Пример Пример (без стрелок)

Вопрос: как обнаружить эти углы и обрезать все, что за их пределами?

Вот мой текущий код:

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)

Ответы [ 2 ]

0 голосов
/ 28 февраля 2020

Это работает для меня в 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 нарезку.

0 голосов
/ 27 февраля 2020

Я предлагаю использовать сопоставление шаблонов с помощью «скорректированного фильтра»:

  • Преобразовать изображение в двоичное изображение (как вы это сделали) и использовать закрытие вместо размытия.
  • Преобразование изображения в «-1» и «1»: замените 0 на -1 и 255 на 1.
  • Сборка L-образного ядра h (для нахождения нижнего левого угла):
    • Поместите -1, где значение в im должно быть -1, и 1, когда значение должно быть 1.
    • Убедитесь, что угол формы L в h находится в центре (это немного напрасно - вы можете избежать этого и исправить положение позже).
    • Пример для ядра (мелкий масштаб):
      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
  • Фильтр im с ядром h - максимальное значение вывода - это позиция, которая лучше всего соответствует 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...