import os
import re
from google.cloud import vision
from google.cloud.vision_v1 import types
from google.cloud import vision_v1
from PIL import Image, ImageDraw
# 인증 설정 (서비스 계정 키 JSON 파일 경로)
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = '😜'
# Vision API 클라이언트 초기화
client = vision_v1.ImageAnnotatorClient()
# 이미지 파일 경로
image_path = './image/테스트단어.png' #250/20 10,30,400,150
# image_path = './image/테스트단어2.png'
# image_path = './image/테스트4.png'
# 이미지 열기
with open(image_path, 'rb') as image_file:
content = image_file.read()
# 이미지 데이터로 Image 객체 생성
image = types.Image(content=content)
#일본어만 추출
def extract_japanese(texts):
japanese_texts = []
# 일본어 문자를 포함하는 문자열을 찾기 위한 정규 표현식
pattern = re.compile(r'[\u3040-\u30FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]')
for text in texts:
if pattern.search(text.description):
japanese_texts.append(text)
return japanese_texts
#한국어 제외
def remove_korean(texts):
non_korean_texts = []
# 한글 문자를 찾기 위한 정규 표현식
pattern = re.compile(r'[\uAC00-\uD7A3]')
for text in texts:
if not pattern.search(text.description):
non_korean_texts.append(text)
return non_korean_texts
# 이미지에서 텍스트 추출
# image_context = vision_v1.ImageContext(language_hints=["ja"])
response = client.text_detection(image=image)
texts = remove_korean(response.text_annotations)
# texts = response.text_annotations
def get_center(vertex1, vertex2):
"""두 꼭지점 간의 중심 좌표를 반환합니다."""
x_center = (vertex1.x + vertex2.x) // 2
y_center = (vertex1.y + vertex2.y) // 2
return x_center, y_center
def calculate_distance(point1, point2):
"""두 점 간의 거리를 계산합니다."""
return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5
def group_boxes(annotations, threshold_x=250, threshold_y=20): #threshold_x 값(간격)을 조절해서 한단어로 묶기
"""임계값 이내의 경계 상자들을 그룹화합니다."""
centers = [get_center(text.bounding_poly.vertices[0], text.bounding_poly.vertices[2]) for text in annotations]
groups = []
visited = set()
for i, center1 in enumerate(centers):
if i in visited:
continue
group = [i]
for j, center2 in enumerate(centers):
dx = abs(center1[0] - center2[0]) # x 좌표의 차이
dy = abs(center1[1] - center2[1]) # y 좌표의 차이
if i != j and j not in visited and dx < threshold_x and dy < threshold_y:
group.append(j)
visited.add(j)
groups.append(group)
visited.add(i)
return groups
def draw_grouped_boxes(image_path, annotations, groups, min_width=10, min_height=30, max_width=400, max_height=150):
"""그룹화된 경계 상자들을 그립니다."""
im = Image.open(image_path)
draw = ImageDraw.Draw(im)
for group in groups:
min_x = min([annotations[i].bounding_poly.vertices[0].x for i in group])
min_y = min([annotations[i].bounding_poly.vertices[0].y for i in group])
max_x = max([annotations[i].bounding_poly.vertices[2].x for i in group])
max_y = max([annotations[i].bounding_poly.vertices[2].y for i in group])
width = max_x - min_x
height = max_y - min_y
if width >= min_width and height >= min_height and width <= max_width and height <= max_height:
draw.rectangle([min_x, min_y, max_x, max_y], outline='red')
# 해당 경계 상자 내의 텍스트를 추출하고 출력
group_texts = [annotations[i].description for i in group]
group_texts = ''.join(group_texts)
print(group_texts)
# print(extract_japanese(group_texts))
im.show()
groups = group_boxes(texts)
draw_grouped_boxes(image_path, texts, groups)
결과 값
待ち合わせ
気のせい
気ㄷ〈 → 気にくわない
合いかぎ
追っかけ
目当て
かける
働〈
実家
事情
暮らし 、 ,
しかと
廊下
留守
100年
大工
保険金
やりがい
by -> もと
散らかる
ほっとする
親孝行
,
코드를 설명하자면
구글의 OCR API 를 활용해 불러온 이미지의 모든 텍스트를 불러온다.
불러온 텍스트 요소들 간의 적당한 간격을 합쳐서 분리되는 요소들을 합처서 온전한 단어로 만들어 주고
경계화된 단어들을 추출해준다.
너무 작은 글씨(한자위의 요미가나)는 지우도록 최소 길이를 지정했다.
여기서 한글을 같이 인식하려다 보면 칸이 뒤죽박죽되는 경우가 있어서
한글을 아예 제외함으로 인식률을 높일 수 있다. 다만 일본어만 걸러내려 해보았는데 몇몇은 뒤에 오는 히라가나는 날려버리고 한자만 가져오는 참사가 일어나는 바람에 일본어만 추출하는것이 아니라 한국어를 제외하는 방식으로 바꾸었다.
그랬더니 히라가나가 짤리는 경우는 사라지고 일본어의 인식이 잘못되어 숫자나 영어로 인식하는 바람에 짤리는 경우도 사라져서 덕분에
잘못인식한 결과값도 그래도 반환해주니 일본어 인식을 놓치는 경우가 좀 더 줄어들었다(잘못인식하더라도 인식자체는 해줌)
위 사진은 인식이 쉬운 편으로 사진을 직접 찍어 올릴때는 어려움이 많다. 또 아직은 글자 크기나 간격을 임의의 값으로 지정해둔터라 다양한 크기의 이미지에 대응할 수 없다. 이는 전처리나 평균값을 바탕으로 구분을 해줄 필요가 있다.
왼쪽은 한글을 제외한 값이고 오른쪽은 제외하지 않은 쪽이다.
확실히 결과값에 차이가 있다. 물론 놓치게 되는경우도 있는것 같은데
이는 또한 사용자가 점검해 볼 수 있는 기능을 제공해서 잡아낼수 있게 해야한다.