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

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

by 이민훈 2021. 8. 6.

8. 인식 과정 - 음표(꼬리)

기둥은 객체 분석 과정에서 검출해낸 것이 있으니 따로 하지 않아도 됩니다.

 

애초에 직선 성분이 있는 객체만 음표로 가정하여 인식하는 것이기도 하구요.

 

# recognition_modules.py
import functions as fs
import cv2

def recognize_note(image, staff, stats, stems, direction):
    (x, y, w, h, area) = stats
    notes = []
    pitches = []
    note_condition = (
        len(stems) and
        w >= fs.weighted(10) and  # 넓이 조건
        h >= fs.weighted(35) and  # 높이 조건
        area >= fs.weighted(95)  # 픽셀 갯수 조건
    )
    if note_condition:
        for i in range(len(stems)):
            stem = stems[i]
            head_exist, head_fill, head_center = recognize_note_head(image, stem, direction)
            if head_exist:
                recognize_note_tail(image, i, stem, direction)

    pass

 

이전 챕터에서 머리 인식 알고리즘 함수를 구현하였습니다.

 

반환 값은 각각 머리 존재 여부, 머리가 채워져 있는지를 담은 Boolean 값, 머리의 중심 y 좌표입니다.

 

머리가 존재하지 않으면 꼬리도 탐색할 필요가 없습니다.

 

머리가 존재하는 객체들에 한하여 꼬리를 탐색하는 함수를 호출하도록 하겠습니다.

 

index 값인 i를 파라미터로 넘겨주는 이유는

 

둘 이상의 음표가 빔으로 연결되어있는 경우 때문인데, 빔으로 연결된 음표는

 

두 번째 음표부터 꼬리를 찾기 위해선 왼쪽을 탐색해야 한다는 특징이 있습니다.

 

recognition_modules.py에 recognize_note_tail 함수를 정의하겠습니다.

 

음표 꼬리 부분을 탐색하는 함수입니다.

 

# recognition_modules.py
import functions as fs
import cv2

def recognize_note_tail(image, index, stem, direction):
    (x, y, w, h) = stem
    if direction:  # 정 방향 음표
        area_top = y  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h - fs.weighted(15)  # 음표 꼬리를 탐색할 위치 (하단)
    else:  # 역 방향 음표
        area_top = y + fs.weighted(15)  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h  # 음표 꼬리를 탐색할 위치 (하단)
    if index:
        area_col = x - fs.weighted(4)  # 음표 꼬리를 탐색할 위치 (열)
    else:
        area_col = x + w + fs.weighted(4)  # 음표 꼬리를 탐색할 위치 (열)
        
    pass

 

특징점으로는 특정 열을 따라가며 픽셀이 존재하는 부분이 몇 번 등장하는지 카운트하여

 

꼬리 개수를 세볼 예정입니다.

 

# recognition_modules.py
import functions as fs
import cv2

def recognize_note_tail(image, index, stem, direction):
    (x, y, w, h) = stem
    if direction:  # 정 방향 음표
        area_top = y  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h - fs.weighted(10)  # 음표 꼬리를 탐색할 위치 (하단)
        area_left = x + w  # 음표 꼬리를 탐색할 위치 (좌측)
        area_right = x + w + fs.weighted(10)  # 음표 꼬리를 탐색할 위치 (우측)
    else:  # 역 방향 음표
        area_top = y + fs.weighted(10)  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h  # 음표 꼬리를 탐색할 위치 (하단)
        area_left = x + w  # 음표 꼬리를 탐색할 위치 (좌측)
        area_right = x + w + fs.weighted(10)  # 음표 꼬리를 탐색할 위치 (우측)
    if index:
        area_col = x - fs.weighted(3)  # 음표 꼬리를 탐색할 위치 (열)
    else:
        area_col = x + w + fs.weighted(3)  # 음표 꼬리를 탐색할 위치 (열)

    cnt = 0

    flag = False
    for row in range(area_top, area_bot):
        if not flag and image[row][area_col] == 255:
            flag = True
            cnt += 1
        elif flag and image[row][area_col] == 0:
            flag = False

    fs.put_text(image, cnt, (x - fs.weighted(10), y + h + fs.weighted(20)))

    pass

 

픽셀이 존재하면 flag를 True로 바꾸고 cnt를 1 증가 시킨 뒤

 

픽셀이 존재하지 않는 부분이 나올 때까지 탐색을 지속합니다.

 

픽셀이 나오지 않고 flag가 True인 상태라면 flag를 False로 바꿉니다.

 

다시 픽셀이 탐색 되고 flag가 False인 상태라면 flag를 True로 바꾸고 cnt를 1 증가 시킵니다.

 

 

잘 작동하는 것을 확인할 수 있습니다.

 

픽셀이 존재하는 부분이 몇번 등장하는지 탐색하는 부분은 functions.py에 함수로 만들어 놓겠습니다.

 

# functions.py
import cv2
import numpy as np

def count_pixels_part(image, area_top, area_bot, area_col):
    cnt = 0
    flag = False
    for row in range(area_top, area_bot):
        if not flag and image[row][area_col] == 255:
            flag = True
            cnt += 1
        elif flag and image[row][area_col] == 0:
            flag = False
    return cnt

 

# recognition_modules.py
import functions as fs
import cv2

def recognize_note_tail(image, index, stem, direction):
    (x, y, w, h) = stem
    if direction:  # 정 방향 음표
        area_top = y  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h - fs.weighted(15)  # 음표 꼬리를 탐색할 위치 (하단)
    else:  # 역 방향 음표
        area_top = y + fs.weighted(15)  # 음표 꼬리를 탐색할 위치 (상단)
        area_bot = y + h  # 음표 꼬리를 탐색할 위치 (하단)
    if index:
        area_col = x - fs.weighted(4)  # 음표 꼬리를 탐색할 위치 (열)
    else:
        area_col = x + w + fs.weighted(4)  # 음표 꼬리를 탐색할 위치 (열)

    cnt = fs.count_pixels_part(image, area_top, area_bot, area_col)

    return cnt

 

# recognition_modules.py
import functions as fs
import cv2

def recognize_note(image, staff, stats, stems, direction):
    (x, y, w, h, area) = stats
    notes = []
    pitches = []
    note_condition = (
        len(stems) and
        w >= fs.weighted(10) and  # 넓이 조건
        h >= fs.weighted(35) and  # 높이 조건
        area >= fs.weighted(95)  # 픽셀 갯수 조건
    )
    if note_condition:
        for i in range(len(stems)):
            stem = stems[i]
            head_exist, head_fill, head_center = recognize_note_head(image, stem, direction)
            if head_exist:
                tail_cnt = recognize_note_tail(image, i, stem, direction)

    pass

 

다음 챕터에서는 점음표인지 탐색하는 알고리즘을 짜도록 하겠습니다.

댓글