개발환경
Client : Android Studio(Java) + NDK(C++) + OpenCV
Server : Python
기본적으로 모바일 애플리케이션을 만들기로 했기 때문에,
클라이언트와 UI(User Interface)는 안드로이드 스튜디오로 개발하였습니다.
클라이언트는 크게 악보 인식 모듈, 뮤직 플레이어 모듈, 데이터 송수신 모듈로 구성돼 있습니다.
서버는 파이썬으로 개발되었고, 수신한 데이터를 기반으로 편곡 후 음악 파일을 생성하고
클라이언트 측으로 파일을 다시 송신합니다.
전처리 과정 1 - 악보 영상 그레이스케일 및 이진화 진행
악보를 인식하여 사운드로 재생하기 위해선 무슨 데이터들이 필요할까요?
음악에 대해 문외한이지만 기본적으로 무슨 장조/단조인지와 각 음의 높낮이와 박자 등이 필요할 겁니다.
이런 데이터들을 확보하기에 앞서 데이터를 수월하게 확보하기 위해 몇 가지 전처리 과정이 필요합니다.
그래서 악보 인식 과정은 크게 전처리 과정과 본 인식 과정으로 나뉩니다.
먼저 추후 객체 검출, 인식 과정 등과 연산 속도의 개선을 위해 악보 영상에 대해
그레이스케일 → 영상 이진화의 과정을 진행합니다.
// ============================================================================================
// 전처리 과정 1 - 악보 영상 그레이스케일 및 이진화 진행
// ============================================================================================
JNIEXPORT void JNICALL Java_com_example_practiceopencv_SheetFragment_preprocess1(
JNIEnv *env,
jobject thiz,
jlong sheetMusicAddr) {
// 로그 태그 및 악보 영상 레퍼런스 변수 생성
const char* LOGTAG = "preprocess1";
Mat &sheetMusic = *(Mat *) sheetMusicAddr;
// 그레이스케일 (RGB → GRAY)
cvtColor(sheetMusic, sheetMusic, COLOR_RGB2GRAY);
// 이진화 (흑백 거꾸로, OTSU 알고리즘 사용)
threshold(sheetMusic, sheetMusic, 127, 255, THRESH_BINARY_INV | THRESH_OTSU);
// 로그
LOGI("Function End :: %s", LOGTAG);
}
https://hackids.tistory.com/103
그레이스케일 및 영상 이진화에 대한 간략한 설명은 위 포스팅을 참조하시거나
구글에 검색하시면 많은 분들이 쉽고 디테일하게 설명해주신 글들이 많습니다.
이진화를 진행하면서 배경과 객체의 색깔을 거꾸로 한 이유는 크게 따로 이유가 있는 것이 아니고
이후에 있을 오선 영역 검출을 조금이라도 더 손쉽게 하기 위함입니다.
배경을 흰색, 객체를 검은색으로 해도 무방합니다.
전처리 과정 2 - 오선 영역 도출 및 그 외 영역 제거
영상 이진화가 완료되면 악보 영상에서 오선 영역을 추출해 그 외의 영역을 제거하게 됩니다.
음악 재생에 필요한 데이터(박자, 조표, 음이름, 음표)들은 모두 오선 영역 안에 존재하기 때문에,
그 외 불필요한 곡의 제목, 가사 등과 같은 데이터들을 제거해줍니다.
오선 영역을 검출하는 것은 OpenCV에 내장된 함수인 findContours(윤곽선 검출)를 사용하면 되는데,
오선 영역은 기본적으로 악보 영상 내에서 큰 가로길이를 가지고 있습니다.
그러므로 객체의 가로길이가 악보 영상의 70% 이상 된다면, 오선 영역으로 판단하고
그 외 영역의 데이터를 모두 제거하였습니다.
오선 영역을 제거할 땐 이미지의 행에 빨간 픽셀이 하나라도 포함되지 않는 행은 모두 데이터를 제거하였습니다.
// ============================================================================================
// 전처리 과정 2 - 오선 영역 도출 및 그 외 영역 제거
// ============================================================================================
JNIEXPORT void JNICALL Java_com_example_practiceopencv_SheetFragment_preprocess2(
JNIEnv *env,
jobject thiz,
jlong sheetMusicAddr) {
// 로그 태그 및 악보 영상 레퍼런스 변수 생성
const char* LOGTAG = "preprocess2";
Mat &sheetMusic = *(Mat *) sheetMusicAddr;
// 악보 영상 행, 열 길이
int rows = sheetMusic.rows, cols = sheetMusic.cols;
// 윤곽선 검출(findContours) 함수로 모든 객체를 찾음(저장은 contorus 벡터에)
vector<vector<Point>> contours;
findContours(sheetMusic, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
// 악보 영상을 3채널로 변환 후
cvtColor(sheetMusic, sheetMusic, COLOR_GRAY2RGB);
// 악보 영상 행 길이의 70%보다 가로로 긴 contours들에 대해 빨간색 박스를 침
for (int i = 0; i < contours.size(); i++) {
Rect rect = boundingRect(contours[i]);
if(rect.width >= cols * 0.7 && rect.width != cols) {
rectangle(sheetMusic, rect, Scalar(255, 0, 0), 3);
}
}
// 오선 영역 외 데이터 제거
uchar* pixelData = sheetMusic.data;
// 영상의 각 행을 탐색하며..
for (int y = 0; y < rows; y++) {
double histogram = 0;
// 빨간색이 포함되어 있는지 검사함
for (int x = 0; x < cols; x++) {
uchar rPixel = pixelData[y * cols * 3 + x * 3];
uchar gPixel = pixelData[y * cols * 3 + x * 3 + 1];
uchar bPixel = pixelData[y * cols * 3 + x * 3 + 2];
if (rPixel > 200 && gPixel < 50 && bPixel < 50) {
histogram++;
for (int i = 0; i < 3; i++) {
pixelData[y * cols * 3 + x * 3 + i] = 0;
}
}
}
// 빨간색이 포함되어 있지 않은 행은 다 날려버림
if (histogram == 0) {
for (int x = 0; x < cols; x++) {
for (int i = 0; i < 3; i++) {
pixelData[y * cols * 3 + x * 3 + i] = 0;
}
}
}
}
LOGI("Function End :: %s", LOGTAG);
}
'인공지능 > 컴퓨터비전' 카테고리의 다른 글
[OpenCV/Python] 악보 인식(디지털 악보 인식) - 1 (0) | 2021.08.04 |
---|---|
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 5 (2) | 2021.05.27 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 4 (0) | 2021.05.27 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 3 (0) | 2021.05.24 |
[OpenCV/악보인식] 광학 음악 인식 기반 자동 편곡 시스템 - 1 (0) | 2021.05.21 |
댓글