개요
졸업작품으로 진행했던 광학 음악 인식 기반 자동 편곡 시스템을
파이썬으로 옮기는 과정에 악보인식 모듈만을 먼저 포스팅해보려 합니다.
Python 문법이나 OpenCV 함수 등에 대한 자세한 설명은 스킵하고
악보인식에 필요한 과정들을 위주로 포스팅하려고 합니다.
Python 및 OpenCV 라이브러리에 대해 조금만 아신다면 크게 문제없이 진행하실 수 있습니다.
물론 클래식 및 가요 등 큰 보표 이상이 사용된 복잡한 악보거나
디지털이 아닌 수기 악보에 대해선 적용이 어렵겠으나,
전체적인 악보인식 진행 방법은 거의 다 비슷하다고 보시면 됩니다.
Python은 당연히 설치가 되어있으셔야 하고, 라이브러리로 opencv, numpy 등이 필요합니다.
이미지 불러오기
먼저 악보 이미지를 OpenCV 라이브러리를 통해 불러오고 화면에 띄워보겠습니다.
# Main.py
import cv2
import os
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 이미지 띄우기
cv2.imshow('image', image)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
전처리 과정 - 1. 보표 영역 추출 및 그 외 노이즈 제거
이제 악보 인식 전 악보 이미지에 대해 전처리 과정들이 몇 개 선행되어야 합니다.
추후 인식 알고리즘의 연산 속도 개선과 정확도 향상을 위해
음악 재생에 필요한 보표 영역을 제외하고 곡의 제목이나 가사들은 지워주도록 하겠습니다.
보표 영역을 검출하기 전에 이미지에 대한 이진화 처리가 필요합니다.
functions.py라는 파일을 하나 만들어 아래와 같은 함수를 정의하겠습니다.
해당 함수는 이미지를 그레이스케일 및 이진화 한 뒤 결과 이미지를 리턴해주는 함수입니다.
# functions.py
import cv2
def threshold(image):
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
return image
이진화를 적용해 보겠습니다.
# Main.py
import cv2
import os
import functions as fs
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image = fs.threshold(image) # 이미지 이진화
# 이미지 띄우기
cv2.imshow('image', image)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
이진화를 통해 배경은 검은색, 객체는 모두 흰색으로 변경하였습니다.
OpenCV에서 작동하는 함수들은 검은색을 배경으로, 흰색을 물체로 인식합니다.
다음은 레이블링을 통해 모든 객체를 검출해 보겠습니다.
객체를 검출하는 데에는 레이블링, 윤곽선 검출, 템플릿 매칭 등 여러 알고리즘을 사용할 수 있지만,
단순히 넓이가 큰 객체(보표)만을 검출하면 되기에 레이블링을 사용하겠습니다.
# Main.py
import cv2
import os
import functions as fs
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image = fs.threshold(image) # 이미지 이진화
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(image) # 레이블링
for i in range(1, cnt):
x, y, w, h, area = stats[i]
cv2.rectangle(image, (x, y, w, h), (255, 0, 0), 1) # 사각형 그리기
# 이미지 띄우기
cv2.imshow('image', image)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
레이블링하고 rectangle 함수를 이용해 모든 객체에 사각형을 그려보겠습니다.
레이블링 함수인 connectedComponentsWithStats는 retval, labels, stats, centroids 네 개의 반환값을 가지고 있습니다.
cnt는 객체수, labels는 레이블맵 행렬, stats는 객체의 정보, centroids는 무게 중심 좌표를 담고 있습니다.
stats에는 x좌표, y좌표, 넓이, 높이, 픽셀의 개수 등의 정보가 담겨있습니다.
0번 객체는 배경이기 때문에 제외하였습니다.
이 중 보표 영역은 이미지에서 상당한 넓이를 가지고 있으며 이것을 이용해서 보표만을 그려보겠습니다.
이미지 넓이 50% 이상의 넓이를 가진 객체만을 대상으로 사각형을 그렸습니다.
# Main.py
import cv2
import os
import functions as fs
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image = fs.threshold(image) # 이미지 이진화
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(image) # 레이블링
for i in range(1, cnt):
x, y, w, h, area = stats[i]
if w > image.shape[1] * 0.5: # 보표 영역에만
cv2.rectangle(image, (x, y, w, h), (255, 0, 0), 1) # 사각형 그리기
# 이미지 띄우기
cv2.imshow('image', image)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
마지막으로 사각형을 마스크 이미지에 그려 넣고 원본 이미지에 마스킹해주면 됩니다.
# Main.py
import cv2
import os
import numpy as np
import functions as fs
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image = fs.threshold(image) # 이미지 이진화
mask = np.zeros(image.shape, np.uint8) # 보표 영역만 추출하기 위해 마스크 생성
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(image) # 레이블링
for i in range(1, cnt):
x, y, w, h, area = stats[i]
if w > image.shape[1] * 0.5: # 보표 영역에만
cv2.rectangle(mask, (x, y, w, h), (255, 0, 0), -1) # 사각형 그리기
masked_image = cv2.bitwise_and(image, mask) # 보표 영역 추출
# 이미지 띄우기
cv2.imshow('image', masked_image)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
마스크 이미지에 사각형을 그릴 땐 rectangle 함수의 마지막 파라미터가 -1로 바뀌게 되는데
사각형에서 테두리만을 그리는 게 아니라 사각형 안을 채워 그려 넣음을 의미합니다.
and 연산은 두 이미지에서 흰색 부분만을 합치는 것을 의미하기에 아래와 같은 이미지가 나온다고 보시면 됩니다.
마스크 이미지가 궁금하신 분들은 마스크 이미지를 띄워보시면 됩니다.
# Main.py
import cv2
import os
import numpy as np
import functions as fs
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image = cv2.imread(resource_path + "music.jpg")
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image = fs.threshold(image) # 이미지 이진화
mask = np.zeros(image.shape, np.uint8) # 보표 영역만 추출하기 위해 마스크 생성
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(image) # 레이블링
for i in range(1, cnt):
x, y, w, h, area = stats[i]
if w > image.shape[1] * 0.5: # 보표 영역에만
cv2.rectangle(mask, (x, y, w, h), (255, 0, 0), -1) # 사각형 그리기
masked_image = cv2.bitwise_and(image, mask) # 보표 영역 추출
# 이미지 띄우기
cv2.imshow('image', mask)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
마스크 이미지는 아래와 같습니다.
modules.py라는 파일을 만들어 이때까지의 과정을 함수로 만들어 놓고 불러다 써보겠습니다.
# modules.py
import cv2
import numpy as np
import functions as fs
def remove_noise(image):
image = fs.threshold(image) # 이미지 이진화
mask = np.zeros(image.shape, np.uint8) # 보표 영역만 추출하기 위해 마스크 생성
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(image) # 레이블링
for i in range(1, cnt):
x, y, w, h, area = stats[i]
if w > image.shape[1] * 0.5: # 보표 영역에만
cv2.rectangle(mask, (x, y, w, h), (255, 0, 0), -1) # 사각형 그리기
masked_image = cv2.bitwise_and(image, mask) # 보표 영역 추출
return masked_image
# Main.py
import cv2
import os
import numpy as np
import functions as fs
import modules
# 이미지 불러오기
resource_path = os.getcwd() + "/resource/"
image_0 = cv2.imread(resource_path + "music.jpg") # 15
# 1. 보표 영역 추출 및 그 외 노이즈 제거
image_1 = modules.remove_noise(image_0)
# 이미지 띄우기
cv2.imshow('image', image_1)
k = cv2.waitKey(0)
if k == 27:
cv2.destroyAllWindows()
'인공지능 > 컴퓨터비전' 카테고리의 다른 글
[OpenCV/Python] 악보 인식(디지털 악보 인식) - 3 (0) | 2021.08.04 |
---|---|
[OpenCV/Python] 악보 인식(디지털 악보 인식) - 2 (0) | 2021.08.04 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 5 (2) | 2021.05.27 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 4 (0) | 2021.05.27 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 3 (0) | 2021.05.24 |
댓글