OpenCV로 카메라 영상 Publish하기


참고

opencv 관련 패키지 설치

$ sudo apt-get install ros-melodic-opencv*
$ sudo apt-get install ros-melodic-usb-cam
$ sudo apt-get install ros-melodic-cv-bridge
$ sudo apt-get install ros-melodic-cv-camera

test_publisher.cpp

#include "cv_bridge/cv_bridge.h"
#include "opencv2/opencv.hpp"
#include "image_transport/image_transport.h"
#include "iostream"
#include "vector"
#include "opencv2/highgui/highgui.hpp"
#include "ros/ros.h" // 로스 관련 헤더 포함
#include "std_msgs/UInt8MultiArray.h"
using namespace std;

#define pb push_back

int main(int argc, char **argv){ 
//int argc = 전달되는 데이터 개수
//char **argv = 실제 전달되는 데이터, 첫 번째 문자열은 프로그램의 실행 경로
        
	ros::init(argc, argv, "image_publish"); 
	//ros 초기화하면서 노드 이름 정함
        
	ros::NodeHandle n; 
	// 노드를 제어할 핸들 생성
        
	ros::Publisher imPublisher = n.advertise<std_msgs::UInt8MultiArray>("camera_image", 3); 
	 
	cv::VideoCapture cap(0); // 0번 캠 연결
	cv::Mat frame;

	while(ros::ok()){
		cap >> frame; // frame에 현재 이미지 저장

		if(!frame.empty()){
			cv::imshow("frame", frame); // 원본 이미지 띄우기

			/* 원본 영상 압축 
				압축하면 영상을 publish하는데 걸리는 시간이 줄어든다고 함.*/
			vector<uchar> encode;
			vector<int> encode_param;

			/* 압축률 */
			encode_param.pb(CV_IMWRITE_JPEG_QUALITY);
			encode_param.pb(20); 

			// frame: 이미지, encode: 압축한 이미지를 담을 벡터, endcode_param: 압축률
			cv::imencode(".jpg", frame, encode, encode_param);
			cv::Mat decode = cv::imdecode(encode,1); // 압축 이미지 디코딩
			cv::imshow("decode", decode); // 압축된 이미지 띄우기

			/* cv::imencode()로 압축된 이미지는 unsigned char 타입의 array */
			std_msgs::UInt8MultiArray msgArray;
			msgArray.data.clear();
			msgArray.data.resize(encode.size());
			std::copy(encode.begin(), encode.end(), msgArray.data.begin());
			imPublisher.publish(msgArray); // 이미지 publish
			
			cv::waitKey(1);
		}
		ros::spinOnce();	
			
	}
	
	return 0;
}

test_subscriber.cpp

#include "ros/ros.h"
#include "opencv2/opencv.hpp"
#include "image_transport/image_transport.h"
#include "opencv2/highgui/highgui.hpp"
#include "vector"
#include "cv_bridge/cv_bridge.h"
#include "std_msgs/UInt8MultiArray"

void callback(const std_msgs::UInt8MultiArray::ConstPtr& msg){
// 메시지를 수신했을 때 호출될 콜백 함수
  
  try{
    cv::Mat frame = cv::imdecode(msg->data,1);
    cv::imshow("subscribe_view", frame);
    cv::waitKey(1);
  }
  catch(cv_bridge::Exception& e){
    ROS_ERROR("cannot decode image");
  }
}       

int main(int argc, char **argv){
  ros::init(argc, argv, "image_subscribe");
  
  cv::namedWindow("subscribe_view");

  ros::NodeHandle n;
  ros::Subscriber imSubscriber = n.subscribe("camera_image",3,callback);
  
  ros::spin(); // 계속 대기하면서 메시지 받는 즉시 콜백 요청하고 다음 메시지 기다림
  cv::destroyWindow("subscribe_view");
  
  return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)
project(test) #패키지 이름

find_package(catkin REQUIRED COMPONENTS
  **cv_bridge
  image_transport
  sensor_msgs
  std_msgs**
  roscpp #패키지 생성할 때 정해준 의존 패키지
)

**find_package(OpenCV REQUIRED)**

**catkin_package(
  INCLUDE_DIRS include
  LIBRARIES opencv
  CATKIN_DEPENDS roscpp cv_bridge image_transport sensor_msgs std_msgs
#  DEPENDS system_lib
)**

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  **${OpenCV_INCLUDE_DIRS}**
)

add_executable(image_publisher src/test_publisher.cpp)
add_executable(image_listener src/test_subscriber.cpp)

target_link_libraries(image_publisher
	${catkin_LIBRARIES}
  **${OpenCV_LIBRARIES}**
)
target_link_libraries(image_listener
	${catkin_LIBRARIES}
  **${OpenCV_LIBRARIES}**
)

package.xml

<?xml version="1.0"?>
<package format="2">
  <name>test</name>
  <version>0.0.0</version>
  <description>The test package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <!-- Example:  -->
  <!-- <maintainer email="[email protected]">Jane Doe</maintainer> -->
  <maintainer email="[email protected]">minkyeong</maintainer>

  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>TODO</license>

  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <!-- Example: -->
  <!-- <url type="website"><http://wiki.ros.org/test></url> -->

  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <!-- Example: -->
  <!-- <author email="[email protected]">Jane Doe</author> -->

  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <!-- Examples: -->
  <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
  <!--   <depend>roscpp</depend> -->
  <!--   Note that this is equivalent to the following: -->
  <!--   <build_depend>roscpp</build_depend> -->
  <!--   <exec_depend>roscpp</exec_depend> -->
  <!-- Use build_depend for packages you need at compile time: -->
  <!--   <build_depend>message_generation</build_depend> -->
  <!-- Use build_export_depend for packages you need in order to build against this package: -->
  <!--   <build_export_depend>message_generation</build_export_depend> -->
  <!-- Use buildtool_depend for build tool packages: -->
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  <!-- Use exec_depend for packages you need at runtime: -->
  <!--   <exec_depend>message_runtime</exec_depend> -->
  <!-- Use test_depend for packages you need only for testing: -->
  <!--   <test_depend>gtest</test_depend> -->
  <!-- Use doc_depend for packages you need only for building documentation: -->
  <!--   <doc_depend>doxygen</doc_depend> -->
  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>roscpp</build_depend>
  **<build_depend>cv_bridge</build_depend>
  <build_depend>sensor_msgs</build_depend>
  <build_depend>image_transport</build_depend>
  <build_depend>std_msgs</build_depend>**

  <build_export_depend>roscpp</build_export_depend>
  **<build_export_depend>cv_bridge</build_export_depend>
  <build_export_depend>sensor_msgs</build_export_depend>
  <build_export_depend>image_transport</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>**

  <exec_depend>roscpp</exec_depend>
  **<exec_depend>cv_bridge</exec_depend>
  <exec_depend>sensor_msgs</exec_depend>
  <exec_depend>image_transport</exec_depend>
  <exec_depend>std_msgs</exec_depend>**

  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>

실행하기

~/catkin_ws/src/test$ catkin build
~/catkin_ws/src/test$ rosrun test image_publisher
~/catkin_ws/src/test$ rosrun test image_listener