Robotics

ROS에서 OpenCV 사용하기 (C++)

콩지니어 2023. 5. 10. 08:00

로봇공학 분야뿐 아니라 컴퓨터와 관련된 거의 모든 분야에서는 프로그래밍 자체에 대한 이해만큼이나 중요한 것이 필요한 기능을 담고 있는 라이브러리에 대한 이해와 활용능력입니다. 이론적으로는 개발자가 지식이 많으면 처음부터 끝까지 모두 직접 개발할 수 있다고 생각해 볼 수는 있으나, 현실적으로는 많이 사용되는 라이브러리 없이 수준 높은 프로그램을 개발하는 것은 불가능에 가깝고, 가능하다고 해도 너무나도 비효율적이기 때문에 일정 수준까지는 공개된, 가능하면 무료로 공개된 라이브러리를 사용하는 것이 큰 도움이 됩니다. 다른 한편으로는 다른 개발자들과 같은 라이브러리를 사용하면서 호환성에 대한 이점도 크게 가져갈 수 있습니다.

그럼에도 많은 경우에 라이브러리를 개발중인 시스템에 불러오는 과정과 사용법에서 막히면서 시간을 많이 소모하게 됩니다. 그나마 요즘 많이 사용하는 Python에서는 이런 번거로움들이 어느 정도 해소되었지만, Python 자체의 단점도 분명하여 번거로워도 C++을 사용해야 하는 경우도 있습니다.

오늘은 로보틱스 연구분야에서 잘 알려진 오픈소스 프레임워크인 ROS에서 영상처리 분야에서 가장 잘 알려진 OpenCV 라이브라리를 불러와 사용하는 방법에 대해 소개하고자 합니다.

 

Python 대신 C++을 사용하는 이유

Python은 그 편리성에서 C++에 비해 많은 장점이 있지만, 그럼에도 C++로 작성된 프로그램이 같은 알고리즘을 실행하는 Python 프로그램 보다 성능면에서 상당한 차이를 보이기도 합니다. 간단한 프로그램에서는 큰 차이를 보이지 않는 경우가 많지만, 연산량이 많은 프로그램, 특히 메모리 접근이 자주 발생되는 경우에는 그 차이가 상당히 벌어지기도 합니다. 그 이유는 아래 두 가지 정도로 정리해 볼 수 있습니다:

 
 
  • 속도: C++은 컴파일 언어이므로 컴파일 과정에서 최적화가 가능하며, 실행 속도가 빠릅니다. 특히 대용량 데이터 처리, 실시간 시스템, 그래픽 처리 등의 고성능 애플리케이션 개발에 적합합니다.
  • 메모리 관리: C++은 개발자가 직접 메모리를 할당하고 해제할 수 있으며, 이를 통해 효율적인 메모리 관리가 가능합니다. Python은 가비지 컬렉션 기능을 제공하여 자동으로 메모리를 관리하지만, 이로 인해 실행 속도가 느려질 수 있습니다.

OpenCV는 이미지 데이터를 주로 다루는데, 이는 다뤄야 하는 데이터량이 많기도 하고 데이터 처리 과정에서 메모리 접근이 빈번할 뿐 아니라, 애초에 C++로 개발된 라이브러리이기 때문에 아무래도 C++로 이용하는 것이 성능이 더 좋을 수 밖에 없습니다.

최근에는 그래도 핵심기능은 메모리 접근 효율을 많이 개선한 NumPy와 같은 라이브러리가 사용되면서 많이 나아지기는 했지만, 그래도 조금의 성능차이를 다투는 경우라면 C++을 선택할 수 밖에 없기도 합니다. 예를 들면, 카메라가 초당 30 프레임의 영상을 보낸다고 가정하면 한 프레임에 대한 연산을 0.033초 내에 해내야 실시간으로 처리가 가능한데, 조금만 연산이 복잡해져도 Python에서는 힘들어질 수 있습니다.

카카오에서 공개한 포스팅에 성능비교가 잘 되어있어 이를 공유해봅니다.

표준편차 알고리즘의 프로그래밍 언어별 성능 비교 [출처: tech.kakao.com]

 

NumPy와 C++ Extensions의 성능 비교

파이썬은 놀라운 생산성을 발휘하는 언어입니다. 하지만 성능 문제는 늘 발목을 잡게 합니다. 이 문제를 극복하는 방법으로 일반적으로 C Extension을 작성하는 방법이 권장되며, 여기서는 표준 편

tech.kakao.com

 

ROS Package에 OpenCV 불러오기

ROS의 빌드시스템인 Catkin은 CMake를 기반으로 하며, 각 패키지 및 노드 역시 CMake로 빌드환경이 구성됩니다. 라이브러리를 빌드 환경으로 가져오기 위해서는 당연히 PC에 해당 라이브러리가 설치되어 있어야 하는데, ROS를 설치 시 버전에 맞는 OpenCV가 함께 설치됩니다.

설치된 라이브러리를 불러오려면 CMakeLists.txt에 필요한 정보를 입력해주어야 합니다. 필요한 내용을 아래 서술하였는데, 혹시 그냥 만들어진 패키지를 사용하거나 참고하고 싶으신 경우 아래 저의 github를 참고하시면 될 것 같습니다.

 

GitHub - jeeho-ahn/ros-opencv: This is an example code of using openCV on ROS. This package will display the designated pictur

This is an example code of using openCV on ROS. This package will display the designated picture file once it runs. - GitHub - jeeho-ahn/ros-opencv: This is an example code of using openCV on ROS...

github.com

 

우선, find_package에 아래와 같이 필요한 라이브러리를 적어주어야 합니다.

find_package(catkin REQUIRED COMPONENTS
  cv_bridge
  image_transport
  roscpp
  std_msgs
  OpenCV
)

저의 경우에는 OpenCV 라이브러리를 CMake가 찾아야만 빌드가 진행되도록 아래와 같이 REQUIRED 옵션을 넣어두었는데, OpenCV가 잘 설치면 되어있다면 결과적인 차이는 없습니다.

find_package(OpenCV REQUIRED)

그다음, 아래와 같이 OpenCV 디렉터리를 include 시켜줍니다.

include_directories(${OpenCV_INCLUDE_DIRS})

마지막으로, 아래와 같이 라이브러리를 링크해 주면 됩니다.

 target_link_libraries(${PROJECT_NAME}
   ${OpenCV_LIBRARIES}
)

여기까지 완료되면, 이제 OpenCV를 ROS Node에서도 이용할 수 있습니다. 용도에 따라 필요한 헤더를 불러오면 되는데, 저의 Github 예제와 같이 이미지 파일에서 부르고 화면에 띄우고 마우스 클릭을 확인하는 정도의 기능은 아래 세 가지 헤더를 불러주면 됩니다.

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>

 

실행해보기

 기준으로, 패키지의 example.launch를 실행하면 OpenCV 기능을 활용하게 되는 결과를 확인할 수 있습니다.

아래와 같이 위 내용을 적용하여 만든 예제 패키지의 git을 clone 후 catkin_make를 하면 빌드가 완료됩니다.

roscd && cd ../src
git clone https://github.com/jeeho-ahn/ros-opencv.git
cd .. && catkin_make

그리고 아래와 같이 example.launch를 실행합니다.

roslaunch ros_opencv example.launch

노드가 실행되고 잠시 후 제가 여행 중에 찍어온 사진 한 장이 뜨는 것을 확인할 수 있습니다.

샘플 이미지

이 이미지 위 아무 지점에 마우스 커서를 올리거나 클릭해 보면, 터미널 창에 아래와 같이 메시지가 뜨는 것을 확인할 수 있습니다.

OpenCV의 마우스 이벤트 결과

마지막으로, 키보드로 아무 키나 누르면 이미지가 사라지며 노드가 종료됩니다.

위 기능은 아래와 같은 OpenCV를 이용한 callback 함수로 구현한 내용입니다.

void CallBackFunc(int event, int x, int y, int flags, void* userdata)
{
  if (event == EVENT_LBUTTONDOWN)
  {
    cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
  }
  else if (event == EVENT_RBUTTONDOWN)
  {
    cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
  }
  else if (event == EVENT_MBUTTONDOWN)
  {
    cout << "Middle button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;

  }

  else if (event == EVENT_MOUSEMOVE)
  {
    cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;
  }
}

참고로, OpenCV를 이용하면 아래와 같이 간단히 이미지 파일을 읽고, 화면에 띄우면서 마우스 이벤트에 대한 callback 함수를 등록할 수 있습니다.

 srcImage = imread(ros::package::getPath("ros_opencv") + "/travel_pic.png", IMREAD_COLOR);
 
  namedWindow("picture display", 1);
  setMouseCallback("picture display", CallBackFunc, NULL);
  imshow("picture display", srcImage);
  waitKey();

 

이상으로 ROS에 OpenCV 라이브러리를 불러 유용하고 강력한 영상처리 툴을 사용할 수 있는 세팅을 하는 방법에 대해 적어보았습니다.