목표) 문제집 답지의 특정 페이지를 넣으면 각 문제별로 크롭하여 저장하기
input : 수능완성 답안지(png)
output)
(10번이전 일부 문제 포함) 10번 ~21번(약간 잘림) 까지의 이미지
아이디어)
1. 전체 페이지에서 윗단과아랫단 제거 -> 문제영역만 남기기 (trim이용)
2. 페이지를 왼쪽 부분과 오른쪽 부분으로 나눔 -> 중앙 라인 제거
* issue 1) 왼쪽과 오른쪽 부분의 가로 크기는 같아야 할 것
issue 2) 홀수 페이지와 짝수 페이지의 중앙 선 부분의 위치가 다름
3. "답" 이미지를 중심으로 일정 여백을 더하여 해당 부분을 자르면, 모든 문제에 대해서 잘라낼 수 있음
* issue) 페이지의 맨 하단에 있는 "답"이미지가 없는 부분(문제의 시작부분 끝부분은 다음페이지에 있는 경우)은 잘라지지 않음 해결 필요 -> 예 ) 2페이지 12,15번 / 3페이지 18번, 21번
4. 답이 다음 페이지에 이어지는 경우 다음 컷과 이어주기
->예시 : 위 2,3페이지 사이에 걸쳐있는 15번과 같은 경우 2페이지에 존재하는 15번 부분과 다음페이지에 있는 "답"이미지 이전까지잘린 두 이미지를 합치기
* issue) 이를 위해서는 읽는 순서가 (흡사 DFS TREE)
- 페이지 순서대로 ( 2p -> 3p )
- 페이지의 왼쪽의 윗쪽부터 아래쪽까지 ( 10번->11번->12번 (1) -> 12번(1) )
- 페이지의 오른쪽 윗쪽에서 아랫쪽까지 ( -> 12번(2) -> 13번 -> 14번 -> .... )
다음과 같아야만 다음컷과 이을 때 정확한 순서가 맞추어진다 12번(1)->12번(2)
Image trim
- 전체 페이지에서 윗단과아랫단 제거 -> 문제영역만 남기기 (trim이용)
- 페이지를 왼쪽 부분과 오른쪽 부분으로 나눔 -> 중앙 라인 제거
def imtrim(page,page_num):
#페이지 번호 string에서 int형으로 변환
page_num = int(page_num)
#짝수 페이지
if (page_num%2 == 0):
left = page[150:-130, 100:855]
right = page[150:-130,880:-137]
#홀수 페이지
else:
left = page[150:-130, 145:900]
right = page[150:-130,905:-112]
# 왼쪽페이지 먼저 리턴(순서대로)
return left,right
짝수 페이지와 홀수 페이지의 경우 가운데 선 부분의 위치가 다르므로 다르게 잘라주어야 한다.
그래서 main에서 페이지 부분만 따로 추출하여 매개변수로 넘겨준 후, int형으로 변경하여 사용하였다.
잘린 페이지 범위는 실험적인 값으로 정한 것 , 가로는 775으로 맞추었음
"답"이미지를 기준으로 밑부분으로 잘라내기
ORDER
- 이미지 흑백화
- 템플릿 매칭
- 매칭 기준으로 자르기
전체 내용
def contour(page_rl):
#이미지 흑백화
imgray = cv2.cvtColor(page_rl, cv2.COLOR_BGR2GRAY)
#추출하려는 이미지
template = cv2.imread('/gdrive/MyDrive/ProjectStudy/answer_temp.png',0)
w, h = template.shape[::-1]
#템플릿 매칭
res = cv2.matchTemplate(imgray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.5
loc = np.where( res >= threshold)
#for문 내에서 사용할 변수 초기화
y_now = 0 #현재 자를 이미지의 y좌표
pt_now = 0 #현재 탐지한 "답"이미지의 y좌표
#탐지한 "답"이미지의 갯수만큼 반복
for pt in zip(*loc[::-1]):
#탐지한 "답"이미지의 y좌표
pt_past = pt_now # 이전값
pt_now = pt[1] #현재값
# 자를 y축 좌표 계산 (y_past : 시작좌표 / y_now : 끝좌표
y_past = y_now # 이전에 자른 부분(이전 단계의 끝좌표)으로 시작을 대치
y_now = pt_now + h + 25 #( "답"이미지의 left,top의 y좌표 + 이미지 height + 여백 )
#같은 "답"이미지가 여러번 탐지되는 부분을 자르는 것을 방지
if (pt_now >= pt_past+3):
img_trim = page_rl[y_past:y_now,:] # 원본 이미지를 계산한 좌표로 자르기
include(img_trim,qnum) # 잘린 이미지를 저장
# 맨 마지막 부분을 위해 1회 더 수행
img_trim = page_rl[y_now:,:]
include(img_trim,qnum)
이미지 흑백화 및 템플릿 매칭
#이미지 흑백화
imgray = cv2.cvtColor(page_rl, cv2.COLOR_BGR2GRAY)
#추출하려는 "답"이미지
template = cv2.imread('/gdrive/MyDrive/ProjectStudy/answer_temp.png',0)
w, h = template.shape[::-1]
#템플릿 매칭
res = cv2.matchTemplate(imgray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.5
#매칭되는 이미지의 좌표(loc = {array[y1, y2,...] and array[x1,x2,...]})
loc = np.where( res >= threshold)
2021/01/18 - [졸업프로젝트/OpenCV] - [OpenCV] 이미지에서 특정 이미지 찾아내기
해당 게시글 참조!
매칭 기준으로 자르기
#for문 내에서 사용할 변수 초기화
y_now = 0 #현재 자를 이미지의 y좌표
pt_now = 0 #현재 탐지한 "답"이미지의 y좌표
#탐지한 "답"이미지의 갯수만큼 반복
for pt in zip(*loc[::-1]):
#
pt_past = pt_now
pt_now = pt[1]
# 자를 y축 좌표 계산 (y_past : 시작좌표 / y_now : 끝좌표
y_past = y_now # 이전에 자른 부분(이전 단계의 끝좌표)으로 시작을 대치
y_now = pt_now + h + 25 #( "답"이미지의 left,top의 y좌표 + 이미지 height + 여백 )
#같은 "답"이미지가 여러번 탐지되는 부분을 자르는 것을 방지
if (pt_now >= pt_past+3):
img_trim = page_rl[y_past:y_now,:] # 원본 이미지를 계산한 좌표로 자르기
include(img_trim,qnum) # 잘린 이미지를 저장
# 맨 마지막 부분을 위해 1회 더 수행
img_trim = page_rl[y_now:,:]
include(img_trim,qnum)
여기는 삽질을 좀 오래했기 때문에 따로 글을 씀
2021/01/20 - [졸업프로젝트/OpenCV] - [OpenCV] 답지 문제별로 자르기(2) - 오류
해당 이미지 (page_rl) 한 페이지의 오른쪽 또는 왼쪽 부분 중 하나 내에서
모든 "답"이미지를 찾아낸다
이는 loc상에 저장되고 zip( *loc[::-1] )을 이용하여 해당 "답"이미지의 (x,y)좌표 로 저장되며
하나하나 pt[0] : x좌표 / pt[1] : y좌표 에 크기 순서대로 저장된다. ( 즉, 윗쪽에 존재하는 것 부터)
y_past에는 이전에 잘렸던 부분을 저장하고, y_now에는 새로운 "답"이미지의 y좌표를 저장하여
y_past부분 ~ y_now부분까지 잘라주면 된다
2페이지의 왼쪽 페이지 부분을 예시로 설명하면 다음과 같다
저장하기
#저장할이미지 , 해당 이미지의 고유번호(+1씩증가)
def include(cropped,qnum):
#전역변수 qnum을 전역변수로서 사용하기 위해
global qnum
#파일명 = 저장경로 + 고유번호 + _pa 페이지수 . png
newfile=save_path+str(qnum)+"_pg"+j+".png"
#해당 경로에 파일을 저장
cv2.imwrite(newfile,cropped)
#저장을 완료했기 때문에 고유번호 1증가
qnum=qnum+1
각각 크롭된 문제마다 고유번호를 부여하기로 했다(문제와 답지의 고유번호가 같음 -> db검색시 용이)
고유번호인 qnum은 저장될때 마다 1씩증가하게 하며,
전역변수를 main에 선언해두고 사용한다 (밑에나옴)
전역변수 (global)
파이썬에서 전역변수를 사용하는 방법은 다음과 같다
다른 부분에서 해당 변수명으로 변수를 작성해둔 뒤
사용을 원하는 함수 내에서 global이라는 타입으로 변수를 정의한 후 사용한다
#main함수나 다른 곳에서
k = 1
#해당 전역변수를 사용하려는 함수내에서
def function():
# 글로벌로 선언
global k
# 해당 변수 조작
k = k + 3
#해당 function이 실행되고나면 k는 3씩 증가 -> 1회 수행시 k=4가되어있음
크롭함수로 모으기
#페이지 url을 입력하면
def problem_crop(image_url,page_num):
#이미지url을 open CV에 읽어들이기
imgfile = image_url
image = cv2.imread(imgfile)
#image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#한 페이지를 반페이지로 나누기
left,right = imtrim(image,page_num)
#각 반페이지에 대해 크롭 진행
contour(left)
contour(right)
지금까지 작성한 imtrim / contour함수를 사용해서 이미지 url과 page 넘버를 입력하면,
원하는 경로에 자동으로 크롭된 번호를 저장해주는 함수를 작성한다
imtrim을 반환하는 순서를 left , right
contour를 진행하는 순서를 left -> right
로 해주어야 왼쪽 페이지부터 결과를 받을 수 있다
메인함수
#함수실행
if __name__ == "__main__":
#고유번호 (전역변수로 사용할 것)
qnum=1
#이미지를 불러올 경로, 저장경로
load_path='/gdrive/MyDrive/ProjectStudy/answers/raw_data/Suwan_ga/2021/example/'
save_path='/gdrive/MyDrive/ProjectStudy/yj/ans_test2/'
os.chdir(load_path)
#해당 경로에서 .png로 끝나는 모든 이미지를 배열로 저장
images=glob.glob('./*.png')
#폴더내 이미지 순서대로 불러오기
images.sort()
#이미지의 수 만큼 반복
for i in range(len(images)):
#하나의 이미지 경로를 저장
filename=images[i]
#파일 이름에서 페이지 이름을 추출
page_num=filename[20:23]
j=page_num
#이미지 경로와 파일경로를 이용해서 크롭
problem_crop(filename,j)
images.sort를 이용하여 페이지를 순서대로 받아오게한다 (2page->3page)
전체코드)
! apt install tesseract-ocr
! apt install libtesseract-dev
! pip install Pillow
! pip install pytesseract
! sudo apt-get install tesseract-ocr-script-hang tesseract-ocr-script-hang-vert
#라이브러리 임포트
import numpy as np
import cv2 #openCV package
from google.colab.patches import cv2_imshow
import re
import pytesseract
import glob
import os
import time
from PIL import ImageEnhance, ImageFilter, Image
from matplotlib import pyplot as plt
#구글 드라이브와 연동
from google.colab import drive
drive.mount('/gdrive')
#수능완성 page trim
def imtrim(page,page_num):
page_num = int(page_num)
#짝수
if (page_num%2 == 0):
left = page[150:-130, 100:855]
right = page[150:-130,880:-137]
#홀수
else:
left = page[150:-130, 145:900]
right = page[150:-130,905:-112]
return left,right
#반페이지를 입력받고 크롭하기
def contour(page_rl):
imgray = cv2.cvtColor(page_rl, cv2.COLOR_BGR2GRAY)
#추출하려는 이미지
template = cv2.imread('/gdrive/MyDrive/ProjectStudy/answer_temp.png',0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(imgray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.5
loc = np.where( res >= threshold)
y_now = 0
pt_now = 0
for pt in zip(*loc[::-1]):
pt_past = pt_now
pt_now = pt[1]
y_past = y_now
y_now = pt_now + h + 25
if (pt_now >= pt_past+3): #a lot of +1 kind of same pts
img_trim = page_rl[y_past:y_now,:]
cv2_imshow(img_trim)
#include(img_trim,qnum)
img_trim = page_rl[y_now:,:]
#include(img_trim,qnum)
cv2_imshow(img_trim)
def include(cropped,num):
global qnum
newfile=save_path+str(num)+"_pg"+j+".png"
#print(newfile)
#print(num)
cv2_imshow(cropped)
#print("===============================================")
cv2.imwrite(newfile,cropped)
qnum=qnum+1
#페이지 url을 입력하면 크롭
def problem_crop(image_url,page_num):
#이미지url을 open CV에 읽어들이기
imgfile = image_url
image = cv2.imread(imgfile)
#image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#한 페이지를 반페이지로 나누기
left,right = imtrim(image,page_num)
#각 반페이지에 대해 크롭 진행
contour(left)
contour(right)
#함수실행
if __name__ == "__main__":
qnum=1
load_path='/gdrive/MyDrive/ProjectStudy/answers/raw_data/Suwan_ga/2021/example/'
save_path='/gdrive/MyDrive/ProjectStudy/yj/ans_test2/'
os.chdir(load_path)
images=glob.glob('./*.png')
#폴더내 이미지 순서대로 불러오기
images.sort()
print(images)
for i in range(len(images)):
filename=images[i]
page_num=filename[20:23]
j=page_num
problem_crop(filename,j)
결과)
input 상단에 2~3페이지
output
(중략)
(중략)
4단계인 페이지 붙이기는 다음게시글에서 ....
-> 위의 output에 있듯
15번(1) : "답"이미지 없음 과 15번(2) : "답"이미지 있음
두 이미지를 이어붙여 하나로 만드는 과정이다
'졸업프로젝트 > OpenCV' 카테고리의 다른 글
[openCV]답지 문제별로 자르기(3) (0) | 2021.01.29 |
---|---|
[OpenCV] 답지 문제별로 자르기(2) - 오류 (3) | 2021.01.20 |
[OpenCV] 이미지에서 특정 이미지 찾아내기 (2) | 2021.01.18 |
[OpenCV]문제지에서 문제 크롭(2) (0) | 2021.01.12 |
[OpenCV]Morphological Transformation (0) | 2021.01.12 |