본문 바로가기
인공지능/컴퓨터비전

[OpenCV/Python] 악보 인식(디지털 악보 인식) - 3

by 이민훈 2021. 8. 4.

전처리 과정 - 3. 악보 이미지 정규화

노이즈 및 오선은 모두 제거 되었고 이제 악보 이미지 정규화가 필요합니다.

 

어떤 악보가 입력되던 악보의 이미지가 항상 같은 비율을 가지게끔 하면

 

추후 탐색 및 인식에서 이점으로 작용하기 때문입니다.

 

앞전에 오선의 좌표들을 모두 구했고 이를 이용하면 정규화가 가능합니다.

 

avg_distance = 0
lines = int(len(staves) / 5)  # 보표의 개수
for line in range(lines):
    for staff in range(4):
        staff_above = staves[line * 5 + staff]
        staff_below = staves[line * 5 + staff + 1]
        avg_distance += abs(staff_above - staff_below)  # 오선의 간격을 누적해서 더해줌
avg_distance /= len(staves) - lines  # 오선 간의 평균 간격

 

오선 간의 평균 간격을 구하는 코드입니다.

 

오선의 칸은 보표마다 4개씩 존재하며 모든 칸의 간격을 누적해둔후 칸의 개수만큼 나눠주면 됩니다.

 

칸의 개수는 오선보다 1개씩 작으니 총 오선의 개수 - 보표의 개수로 구할 수 있습니다.

 

height, width = image.shape  # 이미지의 높이와 넓이
weight = standard / avg_distance  # 기준으로 정한 오선 간격을 이용해 가중치를 구함
new_width = int(width * weight)  # 이미지의 넓이에 가중치를 곱해줌
new_height = int(height * weight)  # 이미지의 높이에 가중치를 곱해줌

 

가중치를 구하는 코드입니다.

 

만약 어떤 악보가 입력되어도 오선 한칸의 간격을 10pixel로 정하고자 한다면

 

10 / 오선 평균 간격을 통해 가중치를 구할 수 있습니다.

 

오선의 평균 간격이 8이고 가중치는 1.25가 되겠죠.

 

즉 이미지의 넓이와 높이에 1.25를 곱해야 오선의 간격이 10이 된다고 생각하시면 됩니다.

 

standard라는 변수가 갖게될 오선 간격을 의미합니다.

 

image = cv2.resize(image, (new_width, new_height))  # 이미지 리사이징
ret, image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)  # 이미지 이진화
staves = [x * weight for x in staves]  # 오선 좌표에도 가중치를 곱해줌

 

가중치가 곱해진 넓이와 높이로 이미지를 리사이징해줍니다.

 

리사이징 과정에 이진화된 이미지가 그레이스케일 이미지로 변하기때문에

 

다시금 이진화를 진행해주면 되고, 오선 좌표에도 가중치를 곱해줍니다.

 

# modules.py
import cv2
import numpy as np
import functions as fs

def normalization(image, staves, standard):
    avg_distance = 0
    lines = int(len(staves) / 5)  # 보표의 개수
    for line in range(lines):
        for staff in range(4):
            staff_above = staves[line * 5 + staff]
            staff_below = staves[line * 5 + staff + 1]
            avg_distance += abs(staff_above - staff_below)  # 오선의 간격을 누적해서 더해줌
    avg_distance /= len(staves) - lines  # 오선 간의 평균 간격

    height, width = image.shape  # 이미지의 높이와 넓이
    weight = standard / avg_distance  # 기준으로 정한 오선 간격을 이용해 가중치를 구함
    new_width = int(width * weight)  # 이미지의 넓이에 가중치를 곱해줌
    new_height = int(height * weight)  # 이미지의 높이에 가중치를 곱해줌

    image = cv2.resize(image, (new_width, new_height))  # 이미지 리사이징
    ret, image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)  # 이미지 이진화
    staves = [x * weight for x in staves]  # 오선 좌표에도 가중치를 곱해줌

    return image, staves

 

# 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")

# 1. 보표 영역 추출 및 그 외 노이즈 제거
image_1 = modules.remove_noise(image_0)

# 2. 오선 제거
image_2, staves = modules.remove_staves(image_1)

# 3. 악보 이미지 정규화
image_3, staves = modules.normalization(image_2, staves, 10)

# 이미지 띄우기
cv2.imshow('image', image_3)
k = cv2.waitKey(0)
if k == 27:
    cv2.destroyAllWindows()

 

댓글