답지에서 해당 답의 영역만을 추출하기 위해서는
다음과 같은 "답"이라는 이미지를 답지 페이지내에서 찾아낸 후 해당 이미지를 기준으로 그 아래를 자르면 된다
그래서 일단 이번 게시글에서는
이런 답지의 페이지 내에서
다음과 같이 "답"이라는 이미지를 전부 찾아내는 것을 목적으로 한다
1. 찾아내길 원하는 이미지 크롭하기
먼저 이 이미지를 따로 만들어내야 한다
주의할 점은 위와 같은 찾아내려는 목적이미지를 절대로 캡쳐 해서는 안되고
원본이미지(답지이미지)에서 그 부분("답")을 잘라내야 한다는 점이다.
잘라내는 방법으론 여러가지 방법이 있지만 그냥 자르는 사이트를 이용해서 잘라냈다
www.iloveimg.com/ko/crop-image
2. 이미지 불러오기
답지 이미지와 찾아내길 원하는 이미지 (지금부터 탬플릿이라고 부른다) 를 불러온다
# 답지 이미지
img_rgb = cv2.imread('/ans_2021_suwan_ga_002.png')
#탬플릿
template = cv2.imread('/answer_temp.png')
3. 템플릿 매칭
matchTemplate
res = cv2.matchTemplate(image : 원본이미지 , template : 찾아낼 이미지 , method)
원본이미지와, 찾아낼 이미지(template)을 input으로 넣고, method를 정하면
원본 픽셀이 템플릿의 이미지와 유사한 정도를 gray이미지로 표현한 것을 array형태로 return한다.
-> 이미지가 array형태로 변환되어 각 픽셀당 계산 값을 담아 출력된다.
(약간 이런형태)
이때 강도는 매칭 방법에 따라서 다르다.
method
- TM_SQDIFF : 픽셀값의 제곱차를 이용 ,가장 어두운 곳을 기준으로 매칭
- TM_SQDIFF_NORMED : 제곱차 매칭 방법에서 정규화, 가장 어두운 곳을 기준으로 매칭
- TM_CCORR : 상관관계계 방법 입력 값의 곱을 모두 제곱하여 더함, 가장 밝은 곳을 기준으로 매칭
- TM_CCORR_NORMED : 위의 방법을 정규화한 것, 가장 밝은 곳을 기준으로 매칭
- TM_CCOEFF : 보통 가장 정확한 결과를 내나 연산량이 많음, 가장 밝은 곳을 기준으로 매칭
- TM_CCOEFF_NORMED : 위의 방법을 정규화한 것, 가장 밝은 곳을 기준으로 매칭
더 자세한 내용
docs.opencv.org/master/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d
# 탬플릿 매칭 해내기
res = cv2.matchTemplate(img_rgb, template, cv2.TM_CCOEFF_NORMED)
# 임계치 정하기
threshold = .65
#임계치 이상만 배열에 저장
loc = np.where(res >= threshold)
템플릿을 매칭 한 후 임계치를 정한다
특정 임계치 값을 넘어서는 값일 때만 "답"이미지 라고 판단한다
*임계치는 실험적인 값으로 정해야한다
너무 작으면 이상한 이미지를 해당 이미지라고 할 수 있고,
너무 크면 "답"이미지를 정확하게 전부 추출할 수 없다.
np.where(조건문)
res라는 array에서 괄호 안에 들어있는 조건을 만족할 경우만 return한다.
4. 이미지에 박스 그리기
#템플릿의 가로(w),세로(h)길이 저장
h, w = template.shape[:-1]
for pt in zip(*loc[::-1]): # Switch collumns and rows
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
#빨간색 굵기2의 박스를 그림
cv2_imshow(img_rgb)
내장함수 zip
동일한 개수로 이루어진 자료형을 묶어 주는 효과를 준다.
loc를 출력해보면 다음과 같다
print(loc)
>>(array([ 279, 279, 625, 626, 754, 754, 1026, 1027, 1235, 1235, 1716]),
array([1544, 1545, 777, 777, 1544, 1545, 777, 777, 1544, 1545, 777]))
array 2개로 이루어져 있는데
첫번째 array는 원본 이미지 내 "답"이라는 템플릿의 y좌표
두번째 array는 원본 이미지 내 "답"이라는 템플릿의 x좌표
이다. *여기서 보통 x가 먼저 오는게 순서에 맞기 때문에 [::-1]을 사용해서 column과 row를 바꾸어준 것
이에 zip을 쓰면 첫번째와 두번째 array의 갯수가 동일 하기 때문에,
이들을 각각 하나로 묶어서 pt라는 배열로 만들어준다
#for를 이용하여
print(pt)
>> (1544, 279)
(1545, 279)
(777, 625)
(777, 626)
(1544, 754)
(1545, 754)
(777, 1026)
(777, 1027)
(1544, 1235)
(1545, 1235)
(777, 1716)
pt[0] : x 좌표
pt[1] : y좌표
*args
해당 파라미터에 몇개를 입력받을지 모를 때 사용한다.
함수 내부에서는 tuple로 받은 것 처럼 인식한다.
-> 여기에서는 zip이라는 내장함수를 이용하는데 몇개의 파라미터를 받을지 모르기 때문에 사용한 것으로 추측
[:-1] : 맨 오른쪽 값을 제외한 전체
h, w = template.shape[:-1]
shape는 height,width, channel을 반환하기 때문에 맨 오른쪽 channel 값을 제외한
h = 템플릿의 height값
w = 템플릿의 width값 을 저장함
[::-1] : 역순으로 배열
arr[a:b:c] , index a부터 index b까지 c의 간격으로 배열을 만들기
arr[::-1] : 전체 배열을 -1간격으로 (역순으로)
rectangle: 사각형 그리는 함수
cv2.rectangle(image : 사각형을 그리려는 원본이미지, point1 : 왼쪽top point, point2 : 오른쪽 bottom point, color : rgb , thickness : 선 굵기)
5. 끝
전체코드)
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
#구글 드라이브와 연동
from google.colab import drive
drive.mount('/gdrive')
img_rgb = cv2.imread('/gdrive/MyDrive/ProjectStudy/answers/raw_data/Suwan_ga/2021/example/ans_2021_suwan_ga_002.png')
template = cv2.imread('/gdrive/MyDrive/cropStudy/answer_temp.png')
h, w = template.shape[:-1]
res = cv2.matchTemplate(img_rgb, template, cv2.TM_CCOEFF_NORMED)
threshold = .65
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # Switch collumns and rows
print(pt)
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
cv2_imshow(img_rgb)
결과)
참고사이트
stackoverflow.com/questions/7853628/how-do-i-find-an-image-contained-within-an-image
m.blog.naver.com/windowsub0406/220540208296
medium.com/@hckcksrl/python-zip-내장함수-95ad2997990
'졸업프로젝트 > OpenCV' 카테고리의 다른 글
[OpenCV] 답지 문제별로 자르기(2) - 오류 (3) | 2021.01.20 |
---|---|
[OpenCV] 답지 문제별로 자르기(1) (0) | 2021.01.19 |
[OpenCV]문제지에서 문제 크롭(2) (0) | 2021.01.12 |
[OpenCV]Morphological Transformation (0) | 2021.01.12 |
[OpenCV] 문제지에서 문제 크롭(1) (0) | 2021.01.12 |