본문 바로가기
인턴활동

opencv, tesseract를 활용해 이미지 스캐너를 만들고 글씨를 추출해내기

by 새싹감자 2022. 9. 12.

서울대 인턴 진행 중에 시도했던!

이미지 스캐너를 만들고 글씨를 추출해내는 코드를 공유해보고자한다. 

원본 이미지는 본 이미지를 사용하였다.

 




아래에 총 코드를 올려놓겠다.

우선 아래와같은 라이브러리를 import해주자

import cv2
import numpy as np
from PIL import Image
from pytesseract import *

이미지를 받아온다. 경로는 이미지 경로에 맞게 바꾸도록.

win_name = 'scan'
img = cv2.imread('C:\imagedata\document13.jpg')
cv2.imshow('original',img)
cv2.waitKey(0)
draw = img.copy()

이미지가 고화질이라 이렇게 작게 보이지만 잘 받아온 것을 볼 수 있다.

 

입력받은 이미지를 그레이 스케일로 바꾸고 노이즈를 없애고 경계를 검출한다.


gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(3,3), 0)
edged = cv2.Canny(gray, 75, 200)
cv2.imshow(win_name,edged)
cv2.waitKey(0)

 

 

 

 

 

 

가장자리를 찾았다.


( cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

print(len(cnts))
cv2.drawContours(draw, cnts, -1,(0,255,0))
cv2.imshow(win_name, draw)
cv2.waitKey(0)

가장자리가 잘 검출되었다. 참고로 손으로 문서를 잡아 모서리가 잘 보이지 않으면 검출이 잘 안되기도 하더랑,,

 

 

 

가장자리를 가지고 4개의 좌표들을 구한다. 가장자리 중에 영역 크기순으로 정렬후 영역이 가장 큰 가장자리부터 단순화한다.



cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
for c in cnts:
    peri = cv2.arcLength(c,True)
    verticles = cv2.approxPolyDP(c,0.02*peri,True)
    if len(verticles)==4:
        break
pts = verticles.reshape(4,2)
for x, y in pts:
    cv2.circle(draw, (x,y), 10,(0,255,0),-1)

cv2.imshow(win_name,draw)
cv2.waitKey(0)
#merged = np.hstack((img,draw))

cv2.destroyAllWindows()

merged = np.hstack((img,draw))

 

 

모서리가 검출되었다.

 

 

 

 

변환할 상하좌우 좌표를 구하고 문서를 크기에 맞게 잘라 보여준다.

 

 

sm = pts.sum(axis=1)
diff = np.diff(pts, axis=1)
topLeft = pts[np.argmin(sm)]
bottomRight = pts[np.argmax(sm)]
topRight = pts[np.argmin(diff)]
bottomLeft = pts[np.argmax(diff)]

pts1 = np.float32([topLeft, topRight, bottomRight, bottomLeft])
w1 = abs(bottomRight[0]-bottomLeft[0])
w2 = abs(topRight[0]-topLeft[0])
h1 = abs(topRight[1]-bottomRight[1])
h2 = abs(topLeft[1]-bottomLeft[1])
width = max([w1, w2])
height = max([h1, h2])

pts2 = np.float32([[0, 0], [width -1, 0],[width -1, height-1],[0, height-1]])

mtrx = cv2.getPerspectiveTransform(pts1,pts2)
result = cv2.warpPerspective(img,mtrx,(width, height))
cv2.imshow(win_name, result)
cv2.waitKey(0) 

 

 

문서가 잘 잘렸다. 화면 해상도를 줄이면 전체 이미지도 확인 가능하다.

 

 

 

 

cvtColor, threshold, bilateralFilter, morphologyEx를 사용해 글씨만 선명하게 나올 수 있게 설정한다. 이렇게 하고 일차적으로 이미지를 저장하면 아래와 같이 나온다.

result2 = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
_, dst = cv2.threshold(result2, 0,255,cv2.THRESH_OTSU)
src_filtering = cv2.bilateralFilter(dst, -1, 10, 10)

kernel = np.ones((3,1),np.uint8)
src_morphology = cv2.morphologyEx(src_filtering, cv2.MORPH_OPEN, kernel)
#merged = np.hstack((result, src_morphology))
cv2.imshow(win_name,src_morphology)
cv2.imwrite('C:\\imagedata\\result.jpg',src_morphology)
cv2.waitKey(0)
cv2.destroyAllWindows()

이미지 전처리가 완료되었다!

 

 

 

 

이를 tensorflow를 사용해서 검출하면

text = image_to_string(src_morphology, lang="kor")
print(text)

아래와같은 결과가 나온다. 엄청 정확한 결과는 아니지만 이미지의 글씨가 크지않은데도 불구하고 이정도면 나름 좋은 결과인 것 같다. tesseract말고 더 정확도가 높은 모델을 사용하면 더 높은 정확도도 얻을 수 있을 것이다. 나는 tesseract의 미세조정을 활용해 인식 안되는 폰트들도 학습시켜 정확도를 향상시켜보려고 노력하고있는데,,,잘되지 않는다,,문제를 해결하면 다시 포스팅하도록 하겠다!

 

 

 

 

 

 

-----------------------------------------------------------------------------------------------------------------------

import cv2
import numpy as np
from PIL import Image
from pytesseract import *

win_name = 'scan'
img = cv2.imread('C:\imagedata\document13.jpg')
cv2.imshow('original',img)
cv2.waitKey(0)
draw = img.copy()

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray,(3,3), 0)
edged = cv2.Canny(gray, 75, 200)
cv2.imshow(win_name,edged)
cv2.waitKey(0)

( cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

print(len(cnts))
cv2.drawContours(draw, cnts, -1,(0,255,0))
cv2.imshow(win_name, draw)
cv2.waitKey(0)

cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
for c in cnts:
    peri = cv2.arcLength(c,True)
    verticles = cv2.approxPolyDP(c,0.02*peri,True)
    if len(verticles)==4:
        break

pts = verticles.reshape(4,2)
for x, y in pts:
    cv2.circle(draw, (x,y), 10,(0,255,0),-1)

cv2.imshow(win_name,draw)
cv2.waitKey(0)
#merged = np.hstack((img,draw))

cv2.destroyAllWindows()

merged = np.hstack((img,draw))
sm = pts.sum(axis=1)
diff = np.diff(pts, axis=1)

topLeft = pts[np.argmin(sm)]
bottomRight = pts[np.argmax(sm)]
topRight = pts[np.argmin(diff)]
bottomLeft = pts[np.argmax(diff)]

pts1 = np.float32([topLeft, topRight, bottomRight, bottomLeft])

w1 = abs(bottomRight[0]-bottomLeft[0])
w2 = abs(topRight[0]-topLeft[0])
h1 = abs(topRight[1]-bottomRight[1])
h2 = abs(topLeft[1]-bottomLeft[1])
width = max([w1, w2])
height = max([h1, h2])

pts2 = np.float32([[0, 0], [width -1, 0],[width -1, height-1],[0, height-1]])

mtrx = cv2.getPerspectiveTransform(pts1,pts2)
result = cv2.warpPerspective(img,mtrx,(width, height))
cv2.imshow(win_name, result)
cv2.waitKey(0)

result2 = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
_, dst = cv2.threshold(result2, 0,255,cv2.THRESH_OTSU)
src_filtering = cv2.bilateralFilter(dst, -1, 10, 10)

kernel = np.ones((3,1),np.uint8)
src_morphology = cv2.morphologyEx(src_filtering, cv2.MORPH_OPEN, kernel)
#merged = np.hstack((result, src_morphology))
cv2.imshow(win_name,src_morphology)
cv2.imwrite('C:\\imagedata\\result.jpg',src_morphology)
cv2.waitKey(0)
cv2.destroyAllWindows()

text = image_to_string(src_morphology, lang="kor")
print(text)

'인턴활동' 카테고리의 다른 글

SVN  (0) 2023.01.26

댓글