ros话题机制默认通过TCP进行传输
因此特别容易堵塞。。。。。。
必须要有compressed的图像, img_raw传输速度非常慢
参考:https://blog.csdn.net/qq_30460905/article/details/107301868?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-1-107301868.nonecase&utm_term=ros%E5%8E%8B%E7%BC%A9%E5%9B%BE%E5%83%8F
1 订阅和发布简单的压缩图像(不可调压缩比)
1.1 订阅者压缩图像:
https://answers.ros.org/question/314979/decompress-jpeg-depth-data/
#include <image_transport/image_transport.h>
void saveIMage3(const sensor_msgs::Image::ConstPtr& msg)
{
}
ros::NodeHandle n;
image_transport::ImageTransport it(n);
image_transport::Subscriber itSub = it.subscribe( "usb_cam/image_raw",1,&saveIMage3,image_transport::TransportHints("compressed"));
在qt中要加入LIBS += /opt/ros/melodic/lib/libimage_transport.so
不用qt的忽略这句话。。
这样在ros中就直接产生了此话题:/usb_cam/image_raw/compressed 就是压缩图像
但是这里的回调函数没法使用boost::bind,使用后会报错,只能简单订阅sensor_msgs::Image::ConstPtr这一个参数给回调函数。
1.2 发布者压缩图像:
ros::NodeHandle nh;
image_transport::ImageTransport it(nh);
image_transport::Publisher image_pub;
image_pub = it.advertise("image_compressed",1);
image_pub.publish( ... );
其中…代表一个sensor_msgs::Image或者sensor_msgs::Image::ConstPtr
因此如果需要cv::Mat的发布,需要有优先转换为sensor_msgs::Image
具体转换方法:https://blog.csdn.net/weixin_44684139/article/details/108759860
2 rqt_reconfigure设置压缩比
可以用rqt_reconfigure设置压缩比:
rosrun rqt_reconfigure rqt_reconfigure
根据format调节quality或level即可。
注意,这种方法并不是只能使用rqt_image_view进行观测才能改画质,而是直接在ros系统中更改的参数。
3 利用Opencv提供的imencode和imdecode进行图像视频传输(Linux下)
参考https://blog.csdn.net/qq_37406130/article/details/78820176
3.1 发送端,先订阅图像,然后压缩并发布
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"WS2_32.lib")
#else
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#endif
#include <iostream>
#include <opencv2/opencv.hpp>
// #include <opencv2/imgproc/imgproc.hpp>
#include "ros/ros.h"
#include <sensor_msgs/Image.h>
#include <cv_bridge/cv_bridge.h>
#include "sensor_msgs/CompressedImage.h"
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
// #include <opencv2/imgproc/imgproc.hpp>
// #include "opencv2/opencv.hpp"
#include <image_transport/image_transport.h> //image_transport
using namespace cv;
using namespace std;
enum
{
PORT = 0X4321
};
sockaddr_in m_servaddr;
int m_sockClient;
void saveIMage(const sensor_msgs::CompressedImage::ConstPtr& msg)
{
try
{
cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::TYPE_8UC3);
// std::cout<<"转换成功!!!!!!!!!!!!!!!!!!!!!!!"<<std::endl;
cv::Mat img__ = cv_ptr->image;
cv::cvtColor(img__, img__, CV_BGR2RGB);
// -----------------------------------------------------------------------------------------
std::vector<uchar> data_encode;
std::vector<int> quality;
quality.push_back(CV_IMWRITE_JPEG_QUALITY);
quality.push_back(50);//进行50%的压缩
imencode(".jpg", img__, data_encode,quality);//将图像编码
char encodeImg[65535];
int nSize = data_encode.size();
for (int i = 0; i < nSize; i++)
{
encodeImg[i] = data_encode[i];
}
m_servaddr.sin_addr.s_addr = inet_addr("192.168.43.103");
m_servaddr.sin_port = htons(0x1234);//设置需要发送的IP和端口号
sendto(m_sockClient, encodeImg, nSize, 0, (const sockaddr*)& m_servaddr, sizeof(m_servaddr));
std::cout<<"发送成功"<<std::endl;
memset(&encodeImg, 0, sizeof(encodeImg)); //初始化结构体
// -----------------------------------------------------------------------------------------
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
}
int main(int argc, char** argv)
{
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(0x01, &wsaData); //创建初始化句柄
#endif
if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //创建socket句柄,采用UDP协议
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
memset(&m_servaddr, 0, sizeof(m_servaddr)); //初始化结构体
m_servaddr.sin_family = AF_INET; //设置通信方式
m_servaddr.sin_port = htons(PORT); //设置端口号
bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定端口号
// VideoCapture capture(0);//打开摄像头
// Mat image;
ros::init(argc, argv, "talker_main");
ros::NodeHandle n;
//ros::Subscriber image_sub_ = n.subscribe<sensor_msgs::CompressedImage>("her/usb_cam/image_raw/compressed", 1,saveIMage);
ros::Subscriber image_sub_ = n.subscribe<sensor_msgs::CompressedImage>("/usb_cam/image_raw/compressed", 1,saveIMage);
ros::spin();
return 0;
}
发送端CMakeLists.txt随便写了以下,可能不是特别严格
set( CMAKE_CXX_FLAGS "-std=c++11")
cmake_minimum_required( VERSION 2.8 )
project( send_receive )
set(OpenCV_DIR "usr/local/opencv3/share/OpenCV")
# 寻找OpenCV库
find_package( OpenCV 3 REQUIRED )
# 添加头文件
include_directories( ${OpenCV_INCLUDE_DIRS} )
find_package(catkin REQUIRED COMPONENTS geometry_msgs sensor_msgs std_msgs robot_state_publisher urdf move_base_msgs visualization_msgs cv_bridge image_transport)
catkin_package(
CATKIN_DEPENDS robot_state_publisher urdf geometry_msgs sensor_msgs std_msgs move_base_msgs visualization_msgs sensor_msgs cv_bridge image_transport
)
include_directories(
${catkin_INCLUDE_DIRS}
)
# 可执行程序
add_executable( send send.cpp )
# 链接OpenCV库
target_link_libraries( send ${OpenCV_LIBS} ${catkin_LIBRARIES})
3.2 接收端,接受图像并显示
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
using namespace cv;
using namespace std;
enum
{
PORT = 0x1234
};
int main(int argc, char** argv)
{
//WSADATA wsaData;
//WSAStartup(0x01, &wsaData); //创建初始化句柄
int m_sockClient;
if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //创建socket句柄,采用UDP协议
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
sockaddr_in m_servaddr;
memset(&m_servaddr, 0, sizeof(m_servaddr)); //初始化结构体
m_servaddr.sin_family = AF_INET; //设置通信方式
m_servaddr.sin_port = htons(PORT); //设置端口号
bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定套接字
Mat image;
char buf[65536];
while (true)
{
std::vector<uchar> decode;
int n = recv(m_sockClient, buf, sizeof(buf), 0);//接受缓存
int pos = 0;
while (pos < n)
{
decode.push_back(buf[pos++]);//存入vector
}
buf[n] = 0;
image = imdecode(decode, CV_LOAD_IMAGE_COLOR);//图像解码
imshow("image", image);
waitKey(30);
}
return 0;
}
接收端CMakeLists.txt
set( CMAKE_CXX_FLAGS "-std=c++11")
cmake_minimum_required( VERSION 2.8 )
project( receive )
set(OpenCV_DIR "usr/local/opencv3/share/OpenCV")
# 寻找OpenCV库
find_package( OpenCV REQUIRED )
# 添加头文件
include_directories( ${OpenCV_INCLUDE_DIRS} )
# 可执行程序
add_executable( receive receive.cpp )
# 链接OpenCV库
target_link_libraries( receive ${OpenCV_LIBS} )