进程间通信有几种常用方式:TCP/IP、Unix Socket、SHM(Share Memory)
以下代码示例是通过:ZeroMQ(zmq库)来通过Unix域套接字传送图像,OpenCV的imshow函数展示图像
两个重点:1. ZeroMQ的基本用法;2. 控制imshow()函数实现同步播放的方法;
receiver.cpp:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <zmq.hpp>
#include <chrono>
#include <thread>
#include <sys/time.h>
int main() {
// 创建ZeroMQ上下文
zmq::context_t context(1);
// 创建套接字并绑定到接收方
zmq::socket_t socket(context, zmq::socket_type::pull);
socket.bind("ipc:///tmp/video_socket");
// 创建窗口用于显示视频
cv::namedWindow("Received Video", cv::WINDOW_NORMAL);
// 统计变量
int frameCount = 0;
double totalTime = 0.0;
double avgFps = 0.0;
double avgInterval = 0.0;
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
struct timeval tv1, tv2;
gettimeofday(&tv1, NULL);
int lastFrameCount = 0;
double lastTotalTime = 0.0;
float tspan = 0.0;
// 接收并实时播放视频帧
while (true) {
// 接收图像数据
zmq::message_t message;
socket.recv(&message);
// 解析接收到的图像数据
int width, height, type;
memcpy(&width, message.data(), sizeof(int));
memcpy(&height, static_cast<char*>(message.data()) + sizeof(int), sizeof(int));
memcpy(&type, static_cast<char*>(message.data()) + sizeof(int) * 2, sizeof(int));
// 计算图像数据的大小
size_t imageDataSize = message.size() - sizeof(int) * 3 - sizeof(double);
// 创建图像矩阵
cv::Mat frame(height, width, type, static_cast<char*>(message.data()) + sizeof(int) * 3);
// 获取帧率信息
double fps;
memcpy(&fps, static_cast<char*>(message.data()) + sizeof(int) * 3 + imageDataSize, sizeof(double));
// 为了保证周期的准确,一下代码应该写到一起,关键点在于:算当前时间、等待剩余时间、重置时间三者的关系必须是依次紧挨这,中间不要加入额外操作,重置时间以后可以加入其他操作,不用着急imshow
{
// 获取当前时间点
std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now();
// 计算已经过去的时间
std::chrono::duration<double> elapsedSeconds = currentTime - startTime;
// 计算应该等待的时间间隔
double expectedInterval = 1000 / fps;
// 计算实际需要等待的时间间隔
double actualInterval = expectedInterval - elapsedSeconds.count() * 1000;
// 等待剩余时间,以实现与原视频完全一致的帧率
if (actualInterval > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(std::round(actualInterval))));
} else {
printf("Error! Accept and process the image timeout, if it is the first frame please ignore!\n");
}
// 更新起始时间点和计时器
startTime = std::chrono::steady_clock::now();
// 打印帧率和时间间隔信息
// std::cout << "Received FPS: " << fps << std::endl;
// std::cout << "Expected Interval: " << expectedInterval << " ms" << std::endl;
// std::cout << "Elapsed Seconds: " << elapsedSeconds.count() * 1000 << " ms" << std::endl;
// std::cout << "Actual Interval: " << actualInterval << " ms" << std::endl;
}
// 调试,在imshow前引入time.h的时间来统计帧率,看是否已经实时播放
{
gettimeofday(&tv2, NULL);
tspan = (tv2.tv_sec - tv1.tv_sec) * 1000.0 + (tv2.tv_usec - tv1.tv_usec) / 1000.0;
//printf("****************************the tspan=%f ms****************************\n", tspan);
gettimeofday(&tv1, NULL);
}
// 显示图像
cv::imshow("Received Video", frame);
// 按下 ESC 键退出循环
if (cv::waitKey(1) == 27)
break;
// 更新统计变量
frameCount++;
totalTime += tspan/1000;
// 每秒钟打印一次统计结果
if (totalTime - lastTotalTime >= 1.0) {
int currentFrameCount = frameCount - lastFrameCount;
double currentTotalTime = totalTime - lastTotalTime;
avgFps = currentFrameCount / currentTotalTime;
avgInterval = currentTotalTime * 1000 / currentFrameCount;
std::cout << "Average FPS (last 1 second): " << avgFps << std::endl;
std::cout << "Average Interval (last 1 second): " << avgInterval << " ms" << std::endl;
// 更新上次统计的帧数和时间
lastFrameCount = frameCount;
lastTotalTime = totalTime;
}
}
// 关闭套接字
socket.close();
// 关闭 ZeroMQ 上下文
context.close();
return 0;
}
sender.cpp:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <zmq.hpp>
int main() {
// 创建ZeroMQ上下文
zmq::context_t context(1);
// 创建套接字并连接到接收方
zmq::socket_t socket(context, zmq::socket_type::push);
socket.connect("ipc:///tmp/video_socket");
// 打开视频文件
cv::VideoCapture cap("1.mp4");
if (!cap.isOpened()) {
std::cerr << "Failed to open video file" << std::endl;
return 1;
}
// 获取视频帧率
double fps = cap.get(cv::CAP_PROP_FPS);
std::cout << "Original FPS: " << fps << std::endl;
// 逐帧读取并发送
cv::Mat frame;
while (cap.read(frame)) {
// 获取图像尺寸
int width = frame.cols;
int height = frame.rows;
int type = frame.type();
// 创建消息
zmq::message_t message(sizeof(int) * 3 + frame.total() * frame.elemSize() + sizeof(double));
// 将图像尺寸复制到消息
char* dataPtr = static_cast<char*>(message.data());
memcpy(dataPtr, &width, sizeof(int));
dataPtr += sizeof(int);
memcpy(dataPtr, &height, sizeof(int));
dataPtr += sizeof(int);
memcpy(dataPtr, &type, sizeof(int));
dataPtr += sizeof(int);
// 将图像数据复制到消息
memcpy(dataPtr, frame.data, frame.total() * frame.elemSize());
dataPtr += frame.total() * frame.elemSize();
// 将帧率信息复制到消息
memcpy(dataPtr, &fps, sizeof(double));
// 发送消息
socket.send(message);
// 打印帧率
std::cout << "Sent FPS: " << fps << std::endl;
}
// 关闭套接字
socket.close();
// 关闭 ZeroMQ 上下文
context.close();
return 0;
}
编译指令:
sudo apt-get install libzmq3-dev
g++ receiver.cpp -o receiver `pkg-config --cflags --libs opencv` -lzmq
g++ sender.cpp -o sender `pkg-config --cflags --libs opencv` -lzmq
- THE END -
最后修改:2023年9月6日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://blog.melulu.top/?p=352
共有 0 条评论