CSV 형식으로 특징점 저장
.npy파일로 저장하면 파이썬에서 불러오기에는 용이하지만 c++에서 불러오기 위해서는cnpy라이브러리를 사용하여야 합니다. 오늘은cnpy라이브러리를 사용하기 위해 먼저 특징점을 일정한 길이로 만든 뒤,.npy로 저장하고, c++에서 불러오는 작업을 시작하겠습니다.
# 템플릿 특징점 추출
import cv2
import numpy as np
from google.colab import drive
drive.mount('/content/drive')
# 작업 경로 설정
workspace = '/content/drive/My Drive/돈과 유명세를 잡자/Zumi/recognize_test/{filename}' # {filename} 앞 경로를 리소스가 존재하는 경로로 변경하세요.
templatePath = workspace.format(filename='Hero {num}.png')
# 특징점 추출 알고리즘
orb = cv2.ORB_create()
# 템플릿 이미지 로드 및 특징점 추출
descriptors = []
for num in range(1, 105):
template = cv2.imread(templatePath.format(num=num), cv2.IMREAD_GRAYSCALE)
kp, des = orb.detectAndCompute(template, mask=None)
descriptors.append(des.tolist())
# 패딩을 위한 변수 설정
minmax = lambda arr: (min(arr), max(arr))
min_len, max_len = minmax([len(row) for row in descriptors])
fixed_len = max_len - min_len
# 기존 자료형이 uint였으므로, 패딩된 값인 걸 명시하기 위해 -1로 패딩
for idx, row in enumerate(descriptors):
padded = np.pad(row, (0, fixed_len), mode='constant', constant_values=-1)[:max_len]
descriptors[idx] = np.asarray(padded, dtype=np.int16)
# 특징점 저장
np.save(workspace.format(filename='template.npy'), descriptors)
np.asarray(descriptors).shape, \
descriptors[0][0], descriptors[0][-1], \
np.max(descriptors), np.min(descriptors)
# 출력
((104, 398, 58),
array([152, 221, 31, 254, 50, 206, 87, 184, 52, 10, 226, 96, 247,
119, 32, 81, 214, 247, 93, 98, 105, 205, 89, 63, 225, 235,
103, 16, 48, 242, 39, 59, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1], dtype=int16),
array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1], dtype=int16),
255,
-1)
C++에서 불러오기
앞서 설명하였듯, c++에서 .npy
파일을 불러오기 위해서는 외부 라이브러리인 cnpy
를 사용하여야 합니다. 아래 레포지토리를 로컬 경로에 저장합니다.
라이브러리 레포지토리: https://github.com/rogersce/cnpy
XCode에서 라이브러리를 불러오기 위해선 먼저 Inspector에서 프로젝트 폴더를 우클릭하고 Add Files to "프로젝트 폴더 이름"...
을 누릅니다. 다음으로 추가할 폴더를 선택하고 Added folders 옵션을 Create groups로 설정합니다.
TARGETS
의 Build Settings
에서 Other Linker Flags
에 다음을 추가해줍니다.
-lcnpy -lz
opencv를 불러오려면 조금더 복잡합니다. brew를 통해 opencv를 설치한 경우, TARGETS
의 Build Settings
에서 Header Search Paths
에 /usr/local/include/opencv4
를 추가하고, Library Search Paths
에 /usr/local/lib
를 추가합니다. 이후 아래 명령어를 통해 출력된 내용을 Other Linker Flags
에 추가합니다.
$ cd /usr/local/Cellar/opencv
$ pkg-config --cflags --libs ./4.4.0_1/lib/pkgconfig/opencv4.pc
-I/usr/local/Cellar/opencv/4.4.0_1/include/opencv4 -L/usr/local/Cellar/opencv/4.4.0_1/lib -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_highgui -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_sfm -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_datasets -lopencv_text -lopencv_dnn -lopencv_plot -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_xfeatures2d -lopencv_shape -lopencv_ml -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core
pkg-config
가 설치되어 있지 않다면 아래 명령어를 통해 설치해주세요.
brew install pkg-config
출처: https://gist.github.com/sigmadream/f1a7778eeaeab79f9888a3292976e438
template.npy
도 Create groups
로 불러온 후, 파란색 프로젝트
를 누른 뒤, TARGETS
의 Build Phases
에서 Copy Files
부분에 template.npy
를 추가해줍니다. Copy Files
의 Destination
은 Products Directory
, Subpath
는 비워주시고, Copy only while installing
체크 해제된 상태입니다.
c++ opencv에서 flann의 knnMatch를 이용하기 위해선 디스크립터가 cv::Mat 형태여야 합니다. 이를 위해 cnpy
로 불러온 1차원 데이터를 cv::Mat vector로 변환하도록 하겠습니다.
#include <iostream>
#include <opencv2/opencv.hpp>
#include "cnpy/cnpy.h"
using namespace std;
int main(int argc, const char * argv[]) {
// npy 로드, 1차원 array로 불러와집니다.
cnpy::NpyArray arr = cnpy::npy_load("template.npy");
// 탐색 및 순회를 위한 포인터
int16_t* loaded = arr.data<int16_t>();
// 불러온 npy로부터 데이터 파싱하기
int16_t val;
int rows, cols;
auto dstDes = vector<cv::Mat>(arr.shape[0]);
for (int i=0; i<arr.shape[0]; i++) {
// get rows
for (rows=0; rows<arr.shape[1]; rows++) {
// (x, y) value
val = *(loaded + rows * arr.shape[2]);
// if it's padded value
if (val == -1) {
break;+
}
}
// get cols
for (cols=0; cols<arr.shape[2]; cols++) {
// (x, y) value
val = *(loaded + cols);
// if it's padded value
if (val == -1) {
break;
}
}
// append cv::Mat
dstDes[i] = cv::Mat(rows, cols, CV_8UC1, loaded);
loaded += arr.shape[1] * arr.shape[2];
}
return 0;
}
비디오 캡처
c++ opencv에서 비디오 프레임을 가져와 이전처럼 ORB를 이용하여 특징점을 추출해보도록 하겠습니다. XCode에서 카메라 권한을 얻기 위해서는 다음과 같은 작업이 필요합니다. 먼저 New File을 누르고 Resource
> Property List
를 선택합니다. 이름은 Info.plist
로 지정합니다.
Info.plist
에 진입하면 Root
가 보입니다. 우클릭해서 Property List Type
을 Info.plist
로 변경합니다. 이후에는 key 항목이 권한 목록으로 보입니다. Privacy - Camera Usage Description
을 선택하고, Value는 권한 요구를 위한 팝업창에 표시되는 문구로, 아무 문구나 상관 없습니다.
이제 코드를 작성하고 실행하면 다음과 같은 창이 뜹니다. 첫 실행 때에는 권한 확인 창을 띄우고, 권한이 없어 프로그램이 종료됩니다. 확인을 누르면 다음 실행 시부터 권한이 적용됩니다. 주의할 점은 코드가 바뀌어 빌드를 새로 하게 될 시, 권한 또한 새로 부여해야 한다는 점입니다.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
int main(int argc, const char * argv[]) {
// 특징점 추출 알고리즘
const auto& orb = cv::ORB::create();
// 비디오 캡쳐 초기화
cv::Mat frame;
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "에러 - 카메라를 열 수 없습니다.\n";
return -1;
}
// 특징점 매칭을 위한 변수 선언
cv::Mat srcDes, out;
vector<cv::KeyPoint> srcKp;
// 비디오 캡쳐 시작
while (true) {
// 카메라로부터 캡쳐한 영상을 frame에 저장합니다.
cap.read(frame);
if (frame.empty()) {
cerr << "빈 영상이 캡쳐되었습니다.\n";
break;
}
// 특징점 매칭을 위한 변수 초기화
srcKp.clear();
// 영상에서 특징점을 추출합니다.
orb->detectAndCompute(frame, cv::noArray(), srcKp, srcDes);
// 프레임에 특징점 그리기
cv::drawKeypoints(frame, srcKp, out, cv::Scalar(255,0,0));
// 프레임 표시
cv::imshow("cap", out);
// ESC 키를 입력하면 루프가 종료됩니다.
if (cv::waitKey(25) >= 0)
break;
}
return 0;
}
'Robotics' 카테고리의 다른 글
61일차 - 차원과 단위, SI 단위계, 위치 에너지, 운동 에너지, 역학적 에너지 [쉽게 배우는 기계공학 개론] (0) | 2020.08.30 |
---|---|
60일차 - C++ openCV 4 실시간 영상에서 ORB 특징점 추출 및 FLANN 매칭하기 (0) | 2020.08.30 |
58일차 - Office Hours Week 3 - QnA (0) | 2020.08.28 |
57일차 - Mathmatics for Circular Motion (0) | 2020.08.27 |
56일차 - 특징점 추출을 통해 카드 이미지를 인식 1 (0) | 2020.08.27 |