OpenCV-8 disparity map using realsense d415
by
본 포스팅은 librealsense2 doc 문서, 여기를 참고하여 만들었습니다.
Stereo matching
Stereo matching이란, 기준 영상의 한 점에 대해 목표 영상에서 동일한 점을 찾는 과정이다.
즉 기준 영상과 목표 영상의 차이인 disparity(시차)를 계산 하는 과정이라 할 수 있다.
기준 영상을 좌측으로 놓든 우측으로 놓든, 시차가 계산되어 나오는 영역만 다를 뿐 시차 값은 동일하다고 한다.
시차 값이 클수록(밝을 수록) 3차원적 거리가 가깝고, 시차 값이 작을수록(어두울 수록) 3차원적 거리가 멀다.
CODE
본 과제의 목표는 realsense 2 SDK 라이브러리를 통해 webcam으로 좌우 IR camera 에 영상을 입력받아, 이를 stereoBM을 통해 계산하여 disparity map을 추출하는 것이다.
코드의 순서는
STEP 1. stereoBM 선언
STEP 2. pipeline에 인자 값(활성화시킬 스트림 등) 주기
STEP 3. While 문을 돌며 키 입력이 있을 떄 까지 프레임 추출
while 문 안의 내용
3-1 pipe.wait_for_frames() 를 통해 새 프레임 세트를 앞서 선언한 frames 객체에 담음
3-2 각 frame 객체 (Mat객체와 비슷하다 생각)에 frames 객체의 IR 2개, BGR1 개의 frame을 담음
3-3 각 Mat 객체를 선언 후 방금 선언한 frame 객체의 주소값을 인자로 주어, 값 할당
3-4 stereoBM의 compute 함수를 통해 disparity map 추출
3-5 imshow
#include <librealsense2/rs.hpp>
#include <opencv2/opencv.hpp>
//#include <opencv2/calib3d.hpp>
using namespace std;
using namespace cv;
int main()
{
Ptr<StereoBM> left_matcher = StereoBM::create(16, 9); //stereobm 선언
rs2::pipeline pipe; //비전 처리 모듈과 사용자 interaction 효과적으로 해주기 위한 pipeline 선언, 카메라 구성 및 스트리밍, 비전 모듈 트리거링 및 스레딩을 추상화
//Create a configuration for configuring the pipeline with a non default profile
rs2::config cfg; //config를 통해 파이프라인 스트림, 장치 선택, 필터 요청, 기기의 파이프 라인 요구 사항과 충돌이 없는지 테스트 가능
//Add desired streams to configuration
cfg.enable_stream(RS2_STREAM_INFRARED, 1, 640, 480, RS2_FORMAT_Y8, 30); //장치 스트림 활성화. RS2_STREAM_INFRARED : RealSense 장치에서 생성 된 IR, 깊이 데이터의 기본 스트림
cfg.enable_stream(RS2_STREAM_INFRARED, 2, 640, 480, RS2_FORMAT_Y8, 30); //장치 스트림 활성화. RS2_STREAM_INFRARED : RealSense 장치에서 생성 된 IR, 깊이 데이터의 기본 스트림
cfg.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8, 30); //RS2_FORMAT_Y8 : 8-bit per-pixel grayscale image(픽셀당 8비트) , 마지막 30 : 초당 가져오는 스트림 프레임
//cfg.enable_stream(RS2_STREAM_DEPTH, 640, 480, RS2_FORMAT_Z16, 30); //RS2_format : 어떻게 binary data가 프레임 내에서 인코딩 되었나 보여줌.
//RS2_FORMAT_Z16 : 16 - bit linear depth values.The depth is meters depth scale * pixel value
//Instruct pipeline to start streaming with the requested configuration
pipe.start(cfg); //방금 준 config 파일을 통해 파이프라인과 스트리밍 시작
while (1)
{
// Camera warmup - dropping several first frames to let auto-exposure stabilize
rs2::frameset frames; //frameset 클래스로 frames이란 객체 생성
frames = pipe.wait_for_frames(); //새 프레임세트를 사용할 수 있을 떄 까지 기다리기. 읽지않은 최신 프레임세트를 가져옴, 함수가 호출되지 않은 동안 생성된 장치 프레임은 삭제
//Get each frame
rs2::frame ir_frame_left = frames.get_infrared_frame(1); //IR 형식의 첫 번쨰 프레임 검색
rs2::frame ir_frame_right = frames.get_infrared_frame(2);
rs2::frame BGR_frame_1 = frames.get_color_frame();
// Creating OpenCV matrix from IR image
//Mat ir(Size(640, 480), CV_8UC1, (void*)ir_frame.get_data()); //(void*)ir_frame.get_data(): data의 시작점(헤드) 위치를 가+르킴. data가 카피되는게 아니라 포인터 개념
Mat IR_left(Size(640, 480), CV_8UC1, (void*)ir_frame_left.get_data());
Mat IR_right(Size(640, 480), CV_8UC1, (void*)ir_frame_right.get_data());
//Mat BGR_1(Size(640, 480), CV_8UC3, (void*)BGR_frame_1.get_data());
Mat disp;
left_matcher->compute(IR_left, IR_right, disp);
disp.convertTo(disp,CV_8U); //32비트로 뱉기에 이를 16비트로 바꿔줌
//applyColorMap(ir, ir, COLORMAP_JET); //컬러로 visualize 해주기 위해서
//imshow("VIDEO_PLAYER", BGR_1);
imshow("VIDEO_PLAYER_L", IR_left);
imshow("VIDEO_PLAYER_R", IR_right);
imshow("VIDEO_PLAYER_d", disp);
if (waitKey(10) == 27) break; //10ms 내에 키 입력되면 break
}
return 0;
}
결과 이미지 :
힘들었던 점
1) stereoBM 객체 선언부에서 인자 값을 주는 부분에서 많이 해멨는데, 결과적으로 default 값을 사용하면 코드 두 줄로 결과 추출 가능
2) compute의 인자로 들어가는 left, right 둘 다 8 bit(0~255) 싱글 이미지 이고, 이를 통해 만들어준 disp 객체는 32bit floating point disparity map이다.
이를 convertTo()를 이용하여 행렬 원소 타입을 CV_8U 즉 8비트로 바꾸어 imshow() 를 통해 visualize 해주면 된다.
디버깅시 함수에 커서를 올려 output의 원소 타입을 읽어보고 이를 변환해주는 습관을 들여야겠다.
참고로, CV_8UC1 에서 C1은 채널 1개를 뜻하기에, 생략하여 CV_8U 로 해주어도 똑같은 값을 갖는다.
Subscribe via RSS