C++实现客户端与服务器的通信(三):在远程服务器中处理本地摄像头数据

这次我们要走得稍微远一点,我需要直接从本地摄像头中读取视频流、传送到服务器、经过服务器上的dlib人脸检测算法、返回bounding box并在本地显示

不过,有了前面数据传输的基础,只要在合适的位置添加一些程序就可以了。

一、图像的序列化

当我们从摄像头中读取出Mat类型的图像数据(img)后,需要首先对其进行序列化使其能够在http下传输,需要借助opencv的imencode函数,程序如下:

std::vector<unsigned char> buffer;
imencode(".jpg", img, buffer);
string src(buffer.begin(), buffer.end());
string base64_src = base64_encode((BYTE const*)src.c_str(), src.length());

图像的解码需要先解码base64字符串,存放到vector<BYTE>中,然后使用opencv的imdecode函数转换为Mat格式,程序如下:

std::vector<BYTE> str_decoded_byte = base64_decode(str_encoded);
Mat mat = imdecode(str_decoded_byte, CV_LOAD_IMAGE_COLOR);

 

二、bounding box的序列化

这一步本来准备用cJSON的,但是后来想到人脸检测的标注数据格式比较简单,例如程序检测到了一张人脸,那么就可以返回一个这样的字符串:

"127 131 200 204"

上述四个数字分别表示人脸的左、上、右、下位置的像素坐标。如果程序检测到多张人脸,也可以用类似方法:

"127 131 200 204 235 87 309 156"

然后只要通过一个split函数就可以将数据分隔开了。C++的string库并没有提供标准的split函数,所以我们要自己定义一个:

void split(const std::string& s, std::vector<std::string>& v, const std::string& c)
{
  std::string::size_type pos1, pos2;
  pos2 = s.find(c);
  pos1 = 0;
  while(std::string::npos != pos2)
  {
    v.push_back(s.substr(pos1, pos2-pos1));
 
    pos1 = pos2 + c.size();
    pos2 = s.find(c, pos1);
  }
  if(pos1 != s.length())
    v.push_back(s.substr(pos1));
}

 

三、C++ dlib人脸检测算法

以前用conda或pip安装的dlib只有python接口,要使用C++接口还是需要源码安装。

github源码地址:https://github.com/davisking/dlib

在dlib根目录下依次运行以下指令:

mkdir build
cd build
cmake ..
cmake --build . --config Release
sudo make install
sudo ldconfig

不出意外的话,就可以安装成功了。

dlib人脸检测算法需要先将Mat格式图片转换成dlib图片格式(dlib::array_2d<bgr_pixel>),然后使用dlib人脸检测器做检测:

dlib::frontal_face_detector detector = dlib::get_frontal_face_detector();
dlib::array2d<bgr_pixel> img;  
dlib::assign_image(img, dlib::cv_image<bgr_pixel>(mat));
std::vector<dlib::rectangle> dets = detector(img);

 

四、修改程序

然后,只要结合上述方法,对程序进行一些修改就可以了,修改后的程序如下:

client test.cpp:

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/core/core.hpp>
#include <iostream>

#include "http.h"
#include "base64.h"

using namespace std;

void split(const std::string& s, std::vector<std::string>& v, const std::string& c);

int main(int argc, char *argv[])
{
  CurlHttp curl_http;
  string str_url = "http://10.108.233.26:8003";  // 地址、端口号

  cv::VideoCapture cap(0);
  if(!cap.isOpened())
  {
    cout << "Something Wrong with the Camera!!" << endl;
    return -1;
  }

  cv::Mat frame, img;
  timeval start, end;
  while (1) {

    gettimeofday(&start, NULL);

    cap >> frame;
    if (frame.empty()) {
      cout << "No Image!!";
      continue;
    }

    float scale = 0.5;
    int width = frame.size().width * scale;
    int height = frame.size().height * scale;
    cv::Size size(width, height);
    cv::resize(frame, img, size);

    std::vector<unsigned char> buffer;
    cv::imencode(".jpg", img, buffer);
    string src(buffer.begin(), buffer.end());
    string base64_src  = base64_encode((BYTE const*)src.c_str(), src.length());
    
    string result;
    int res = curl_http.http_post(str_url.c_str(), base64_src.c_str(), &result);
    cout << "[Response]: " << result << '\n';

    vector<string> str_bbox;
    split(result, str_bbox, " ");
    if (str_bbox.size() % 4 != 0) {
      cout << "Response Error!!";
      return -1;
    }
    int num_bbox = str_bbox.size() / 4;
    for(int i = 0; i < num_bbox; i++) {
      int left   = atoi(str_bbox[i*4].c_str());
      int top    = atoi(str_bbox[i*4 + 1].c_str());
      int right  = atoi(str_bbox[i*4 + 2].c_str());
      int bottom = atoi(str_bbox[i*4 + 3].c_str());
      cv::rectangle(img, cv::Point(left, top + 10), cv::Point(right, bottom), cv::Scalar(255, 0, 0), 2);
    }

    cv::imshow("new", img);
    if (cv::waitKey(5) == 27) {
        break;
    }

    gettimeofday(&end, NULL);
    printf("[Time]: %f\n", (double)((end.tv_sec - start.tv_sec)*1000.0 + (end.tv_usec - start.tv_usec)/1000.0));
  }
  
  return 0;
}

void split(const std::string& s, std::vector<std::string>& v, const std::string& c)
{
  std::string::size_type pos1, pos2;
  pos2 = s.find(c);
  pos1 = 0;
  while(std::string::npos != pos2)
  {
    v.push_back(s.substr(pos1, pos2-pos1));
 
    pos1 = pos2 + c.size();
    pos2 = s.find(c, pos1);
  }
  if(pos1 != s.length())
    v.push_back(s.substr(pos1));
}

server test.cpp:

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/core/core.hpp>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/opencv.h>
#include <iostream>

#include "mongoose.h"
#include "base64.h"

using namespace std;
using namespace cv;
using namespace dlib;

int env_handler(struct mg_connection *conn);

int main(int argc, char *argv[])
{
  struct mg_server* server;
  server = mg_create_server(NULL);                  // 初始化一个mongoose server
  mg_set_option(server, "listening_port", "8003");  // 设置端口号为8003
  mg_add_uri_handler(server, "/", env_handler);     // 设置回调函数
  printf("Starting on port %s ...\n", mg_get_option(server, "listening_port"));
  while (1) {
    mg_poll_server(server, 100);  // 超时时间(ms)
  }
  mg_destroy_server(&server);
  
  return 0;
}


int env_handler(struct mg_connection *conn) 
{
  static dlib::frontal_face_detector detector = dlib::get_frontal_face_detector();
  static int counter = 0;
  counter++;

  const char * encoded_data = conn->content;  // 服务端收到的消息
  int encoded_len = conn->content_len;        // 服务端收到的消息长度
  string str_encoded(encoded_data, encoded_len);

  std::vector<BYTE> str_decoded_byte = base64_decode(str_encoded);
  Mat mat = imdecode(str_decoded_byte, CV_LOAD_IMAGE_COLOR);

  // 开始人脸检测算法
  dlib::array2d<bgr_pixel> img;  
  dlib::assign_image(img, dlib::cv_image<bgr_pixel>(mat));

  timeval start, end;
  gettimeofday(&start, NULL);
  std::vector<dlib::rectangle> dets = detector(img);
  gettimeofday(&end, NULL);

  std::string detect_result = "";
  for (int i = 0; i < dets.size(); i++)
  {
    if (!detect_result.empty()) detect_result += " ";

    char ptr_result[30];
    sprintf(ptr_result, "%d %d %d %d", (int)dets[i].left(), (int)dets[i].top(), (int)dets[i].right(), (int)dets[i].bottom());
    string str_result(ptr_result);
    detect_result += str_result;
  }

  printf("Counter: %3d, BBOX: %s, Time of Detect: %f\n", counter, 
                                                         detect_result.empty() ? "Null" : detect_result.c_str(), 
                                                         (double)((end.tv_sec - start.tv_sec)*1000.0 + (end.tv_usec - start.tv_usec)/1000.0));
  mg_printf(conn, "%s", detect_result.c_str());
  
  return 0;
}

 

五、后记

完成上述修改后,运行程序,发现程序运行速度巨慢。经过简单的测试就能发现,dlib人脸检测算法占用了大量的时间(0.6s左右),看一下gpu发现并没有调用,看起来dlib在cpu下运行速度大概就是这样了,而且我还不知道怎么配置dlib支持gpu,豹怒。。

不过程序还是达成了预定的目标,只要将dlib替换成任何需要进行实时检测的其它算法的接口,就可以利用远程服务器来跑算法了。缺点是只有C++接口,遇到python脚本还是没办法。

以后会考虑解决python接口的问题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值