需求
如果想在远程PC上看到树莓派摄像头拍摄的视频,可以将树莓派配置成一个视频流服务器。但要实现精细控制,比如得到每帧图像的时间戳,或用OpenCV处理每帧图像,这种方法就不行。
本文实现如下功能:一帧帧获取树莓派摄像头的图像,压缩后通过网络发送给远程PC,远程PC解压并显示图像。
分析
使用RaspiCam获取树莓派摄像头的图像数据。这是一个C++库,我使用它的OpenCV接口,将图像数据存储到一个cv::Mat
对象中,就可以使用OpenCV的函数处理图像。
如果采集彩色图像,尺寸为640x480,帧率为25fps,每秒的数据量将是640×480×3×25字节,大约是22MB,这显然太大了,实测效果也非常卡,因此图像必须经压缩后再传输。压缩和解压缩使用OpenCV的imencode
和imdecode
函数。每帧原始图像的大小虽然是固定值,但压缩后的大小则不相同,所以发送压缩图像数据之前要先发送数据大小的信息,否则接收端无法正常解压。
实现
服务端
树莓派是服务端,每收到客户端的请求,就采集一帧图像并压缩,将压缩后图像数据的大小以字符的形式发送出去,接着再发送图像数据。完整代码请参考image_send.cpp,本文省略了头文件和socket接口函数调用等代码,只展示如下重要代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| raspicam::RaspiCam_Cv camera; camera.set(CV_CAP_PROP_FORMAT, CV_8UC3); camera.set(CV_CAP_PROP_FRAME_WIDTH, 640); camera.set(CV_CAP_PROP_FRAME_HEIGHT, 480); std::cout << "Openning camera..."; if (!camera.open()) { std::cerr << "Error openning the camera!" << std::endl; return -1; } std::cout << "Successfully!" << endl;
std::vector<int> param = std::vector<int>(2); param[0] = CV_IMWRITE_JPEG_QUALITY; param[1] = 95;
cv::Mat frame; std::vector<uchar> buff; char data_size_str[20]; for (;;) { connfd = accept(listenfd, NULL, NULL); printf("Receive a request!\n"); if (connfd == -1) { oops("accept"); }
for (;;) { camera.grab(); camera.retrieve(frame); cv::imencode(".jpg", frame, buff, param); sprintf(data_size_str, "%d", buff.size()); send(connfd, data_size_str, 15, 0); send(connfd, &buff[0], buff.size(), 0); } close(connfd); }
|
客户端
远程PC是客户端,不断重复如下操作:接收固定长度的字符串,从中获取压缩后数据的大小,分配相应大小的空间用于接收即将到来的压缩图像数据,接收数据,解压缩并显示。完整代码请参考image_recv.cpp,重要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| cv::namedWindow("Display image", cv::CV_WINDOW_AUTOSIZE);
char data_size_str[20]; for (;;) { recv(sock, data_size_str, 15, 0); int framelen = atoi(data_size_str); std::vector<uchar> buff(framelen); int bytes = 0; for (int i = 0; i < framelen ; i += bytes) { if ((bytes = recv(sock, &buff[0]+i, framelen-i, 0)) == -1) { oops("Receive"); } } cv::Mat frame = cv::imdecode(cv::Mat(buff), cv::CV_LOAD_IMAGE_COLOR); cv::imshow("Display image", frame); cv::waitKey(20); } close(sock);
|