주뇽's 저장소
Pytesseract와 OpenCV를 이용하여 명함스캐너 만들기 본문
728x90
반응형
2023.10.21 - [ComputerVision/OpenCV] - 이미지 처리를 위한 Python OpenCV사용법_1
2023.10.21 - [ComputerVision/OpenCV] - 이미지 처리를 위한 Python OpenCV사용법_2
MAC 사용
brew install tesseract
pip install pytesseract
Step1. 이미지 읽어와서 가장자리 탐색
1. 일반적인 경우
import cv2
from cv2 import imshow
import numpy as np
from google.colab.patches import cv2_imshow
def Imgcontour(image_path):
img = cv2.imread(image_path, cv2.IMREAD_COLOR)
GRAY = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edge = cv2.Canny(GRAY, 100,200)
contours , hierarchy = cv2.findContours(edge, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
### 이진화된 이미지만 넘겨줘야 함
# cv2.imshow("Edge", edge)
cv2_imshow(edge) #-- colab에서 출력하기 위함
cv2.drawContours(img, contours, -1, (0,255,0), 1)
# cv2.imshow("Contours", img)
cv2_imshow(img)#-- colab에서 출력하기 위함
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
image_path = "/content/business-card-318427_1920 2.jpg"
Imgcontour(image_path)
2. 찍고자하는 명함 또는 문서가 구겨진 경우 도형을 근사화 하는 방법
from google.colab.patches import cv2_imshow
import cv2
from cv2 import imshow
import numpy as np
def Imgcontour(image_path):
img = cv2.imread(image_path, cv2.IMREAD_COLOR)
copy_img = img.copy()
copy_img_1 = img.copy()
GRAY = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edge = cv2.Canny(GRAY, 100,200)
contours , hierarchy = cv2.findContours(edge, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Cnt = contours[0]
cv2.drawContours(img, [Cnt],0 , (0,255,0), 2)
cv2.drawContours(copy_img_1, contours[1],0 ,(0,255,0), 2)
epsilon = 0.1 * cv2.arcLength(Cnt, True)
### contour의 둘레의 길이를 확인하고 , 폐곡선 여부 확인 후 근사정확도(10퍼센트) 작을 수록 원본과 비슷함
approx = cv2.approxPolyDP(Cnt, epsilon, True)
cv2.drawContours(copy_img, [approx],0 , (0,255,0), 2)
# cv2.imshow("edge", edge)
# cv2.imshow("img", img)
# cv2.imshow("copy_img", copy_img)
# cv2.imshow("copy_img_1", copy_img_1)
cv2_imshow(edge)
cv2_imshow(img)
cv2_imshow(copy_img)
cv2_imshow(copy_img_1)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
image_path = "/content/ocr_test2.jpg"
Imgcontour(image_path)
Step2. 탐색한 가장자리를 이용하여 투영변환
자동으로 좌표 구하는 방법을 찾아야 한다..
import cv2
from cv2 import warpPerspective
import numpy as np
def wrapPerspective():
img = cv2.imread('ocr.jpeg')
TL = [100,200]
TR = [200,300]
BL = [300,400]
BR = [200,400]
pts1 = np.float32( [TL, TR, BL, BR])
w1 = abs(BL[0] - BR[0])
w2 = abs(TR[0] - TL[0])
h1 = abs(TR[0] - BR[0])
h2 = abs(TL[0] - BL[0])
min_w = min([w1,w2]) ## 최소 너비
min_h = min([h1,h2]) ## 최소 높이
pts2 = np.float32([[0,0], [min_w-1,0],
[min_w-1, min_h-1], [0,min_h-1]])
M = cv2.getPerspectiveTransform(pts1, pts2)
result = warpPerspective(img, M, (int(min_w), int(min_h)))
cv2.imshow("OG img", img)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
wrapPerspective()
Step3. 이진화를 이용하여 조명값 제거
import cv2
def adaptive_threshold():
img = cv2.imread('img.jpeg', cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(img, (7,7), 0)
result_without_blur = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21,10)
result_with_blur = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21,10)
cv2.imshow('Without Blur', result_without_blur)
cv2.imshow('With Blur', result_with_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
adaptive_threshold()
Step4. 합치기
1. 가장자리 검출
import cv2
img = cv2.imread('img.jpeg')
og_img = img.copy()
GRAY = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
GRAY = cv2.GaussianBlur(GRAY, (3,3), 0)
edged = cv2.Canny(GRAY, 75,200)
cv2.imshow("IMG", img)
cv2.imshow("EDGED", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
2. 검출된 가장자리를 이용하여 외곽 찾기
from pickle import TRUE
import cv2
img = cv2.imread('img.jpeg')
ratio = 800.0/img.shape[0]
dim = (int(img.shape[1] * ratio), 800)
img = cv2.resize(img, dim, interpolation= cv2.INTER_AREA)
og_img = img.copy()
GRAY = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
GRAY = cv2.GaussianBlur(GRAY, (5,5), 0)
edged = cv2.Canny(GRAY, 50,200)
cnts, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key =cv2.contourArea, reverse= True)[:5]
## 반환받은 cnt중 면적인 큰 순서대로 5번까지 반환
for c in cnts:
## 순차적으로 탐색
peri = cv2.arcLength(c, True)
## 컨투어의 길이를 반환
approx = cv2.approxPolyDP(c, 0.03 * peri, True)
## 길이의 오차 2퍼센트로 도형을 근사화
if len(approx) == 4:
## 근사화한 도형의 꼭지점이 4개라면 그것이 문서의 외곽
screenCnt = approx
break
cv2.drawContours(img, [screenCnt], -1, (0,255,0), 2)
cv2.imshow("IMG", img)
cv2.imshow("edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 네 개의 꼭지점을 이용하여 투영변환
import numpy as np
import cv2
def order_points(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
rect = order_points(pts)
(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
img = cv2.imread('ocr.jpeg')
ratio = 800.0/img.shape[0]
dim = (int(img.shape[1] * ratio), 800)
img = cv2.resize(img, dim, interpolation= cv2.INTER_AREA)
og_img = img.copy()
GRAY = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
GRAY = cv2.GaussianBlur(GRAY, (3,3), 0)
edged = cv2.Canny(GRAY, 70,200)
cnts, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key =cv2.contourArea, reverse= True)[:5]
## 반환받은 cnt중 면적인 큰 순서대로 5번까지 반환
check = False
for c in cnts:
## 순차적으로 탐색
peri = cv2.arcLength(c, True)
## 컨투어의 길이를 반환
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
## 길이의 오차 2퍼센트로 도형을 근사화
if len(approx) == 4:
## 근사화한 도형의 꼭지점이 4개라면 그것이 문서의 외곽
screenCnt = approx
check = True
break
if check == False:
cv2.imshow("IMG", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.drawContours(img, [screenCnt], -1, (0,255,0), 2)
warped = four_point_transform(og_img, screenCnt.reshape(4, 2))
cv2.imshow("IMG", img)
cv2.imshow("warped", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()
전체 코드
#-- 필요 라이브러리 import
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
#-- 필요 함수 setting
#-- 1. 조명값 이진화 처리 함수
def adaptive_threshold(oriimg):
img = cv2.cvtColor(oriimg, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(img, (7,7), 0)
result_without_blur = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21,10)
result_with_blur = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21,10)
# cv2_imshow(result_without_blur)
# cv2_imshow(result_with_blur)
return result_without_blur
#-- 2. 투영변환 이미지 반환 함수
def order_points(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
rect = order_points(pts)
(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
#-- 3. 1번 2번 함수를 이용하여 고르지 못한 이미지를 투영변환을 통해 반듯한 이미지로 반환해주는 함수
def get_warp_img(image_path):
img = cv2.imread(image_path)
ratio = 800.0/img.shape[0]
dim = (int(img.shape[1] * ratio), 800)
img = cv2.resize(img, dim, interpolation= cv2.INTER_AREA)
og_img = img.copy()
GRAY = adaptive_threshold(og_img)
GRAY = cv2.GaussianBlur(GRAY, (5,5), 0)
edged = cv2.Canny(GRAY, 50,200)
cnts, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key =cv2.contourArea, reverse= True)[:5]
## 반환받은 cnt중 면적인 큰 순서대로 5번까지 반환
check = False
for c in cnts:
## 순차적으로 탐색
peri = cv2.arcLength(c, True)
## 컨투어의 길이를 반환
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
## 길이의 오차 2퍼센트로 도형을 근사화
if len(approx) == 4:
## 근사화한 도형의 꼭지점이 4개라면 그것이 문서의 외곽
screenCnt = approx
check = True
break
if check == False:
# cv2.imshow("IMG", img) #-- opencv
cv2_imshow(img) #-- colab
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.drawContours(img, [screenCnt], -1, (0,255,0), 2)
warped = four_point_transform(og_img, screenCnt.reshape(4, 2))
# cv2.imshow("IMG", img)
# cv2.imshow("warped", warped)
# cv2_imshow(img)
# cv2_imshow(warped)
return warped
#-- 사용
image_path = "자신의 IMG 경로 입력"
ori_img = cv2.imread(image_path)
warp_img = get_warp_img(image_path)
cv2_imshow(ori_img)
cv2_imshow(warp_img)
위 imshow 코드는 colab 기준이며 만약 opencv환경에서는 cv2.imshow("test", img) 방식으로 사용해야 합니다.
#-- 전처리 과정에서 뒷배경이 너무 밝으면 제대로된 4각형을 인식못하기 때문에 배경은 어둡게 해줘야 한다.