【问题标题】:Proper inter process communication method between Opencv C++ webcam program and C serverOpencv C++网络摄像头程序和C服务器之间正确的进程间通信方法
【发布时间】:2019-05-30 12:27:19
【问题描述】:

考虑一个捕获网络摄像头帧的简单 OpenCV 程序:

#ifndef __OPENCV__
#define __OPENCV__
#include "opencv2/opencv.hpp"
#endif
#include <iostream>
#include "utils.hpp"
#include "constants.hpp"
#include <unistd.h>
#include <vector>
#include "InputStateContext.hpp"
#include <SDL.h>
#include <SDL_events.h>
#include "MiddlewareConnector.hpp"

using namespace cv;
using namespace std;

void onTrackbar_changed(int, void* data);
//void onThreshold_changed(int, void* data);
void onMouse(int evt, int x, int y, int flags, void* param);
int keyboardCallback(SDL_KeyboardEvent ev);
int mouseCallback(SDL_MouseButtonEvent ev, float scaleX, float scaleY, Mat frame);

InputStateContext context;

int main(int argc, char* argv[])
{
  printVersion();
    /* Initialise SDL */
    if( SDL_Init( SDL_INIT_VIDEO ) < 0)
    {
        fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() );
        exit( -1 );
    }

    string host;
    unsigned int port;
    const String sourceReference = argv[1];
    int camNum;
    string sensorName;
    try
    {
        camNum = stoi(sourceReference); // throws std::length_error
    }
    catch (const std::exception& e)// reference to the base of a polymorphic object
    {
        std::cout<<"Exception: " << e.what()<<endl; // information from length_error printed
        return -1;
    }
    if (argc>4)
    {
        try
        {
            host = argv[2];
            port = atoi(argv[3]);
            sensorName = argv[4];
        }
        catch (const std::exception& e)
        {
            cout<<"impossible to convert host or port"<<endl;
            return -1;
        }
    }
    else if(argc>2)
    {
        cout<<"argumetns less than 4"<<endl;
        host = "http://localhost";
        port = 3000;

        sensorName = argv[2];
        cout<<argc<<endl;
        cout<<"sensor name set from arguments: "<< sensorName<<endl;

    }
    else
    {
        cout<<"stopping execution: too few arguments."<<endl;
        return -1;
    }
    MiddlewareConnector middleware(host, port, sensorName, &context);
    context.Attach(&middleware);
    context.Notify(); //register on middleware
    cout<<"camera initializing\n";
    VideoSettings cam(camNum + CAP_V4L);
    cout<<"camera initialized\n";
    /* or
    VideoCapture captUndTst;
    captUndTst.open(sourceCompareWith);*/
    cout<<"Ch3ck c4m3ra is 0p3n3d\n";
    if ( !cam.isOpened())
    {
        cout << "Could not open reference " << sourceReference << endl;
        return -1;
    }

    cout<<"===================================="<<endl<<endl;
    cout<<"Default Brightness %-------> "<<cam.getBrightness()<<endl;
    cout<<"Default Contrast %---------> "<<cam.getContrast()<<endl;
    cout<<"Default Saturation %-------> "<<cam.getSaturation()<<endl;
    cout<<"Default Gain %-------------> "<<cam.getGain()<<endl;
    cout<<"Default hue %--------------> "<<cam.getHue()<<endl<<endl;
    cout<<"====================================\n\n"<<endl;
    Mat frame;
    cam>>frame;
    //resize(frame, frame, cv::Size(frame.cols/WINDOW_SCALE, frame.rows/WINDOW_SCALE));
    //resizeWindow("Camera", cv::Size(frame.cols/WINDOW_SCALE, frame.rows/WINDOW_SCALE));
    SDL_Window* win = SDL_CreateWindow("Camera", 100, 100, frame.cols, frame.rows,
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    int oldWidth = frame.cols, oldHeight= frame.rows;
    int width, height;
    float scaleX=1, scaleY=1;
    SDL_SetWindowTitle(win, "Camera");
    SDL_Renderer * renderer = SDL_CreateRenderer(win, -1, 0);
    /*
    createTrackbar( "Brightness","Camera", &cam.brightness, 100, onTrackbar_changed, &cam );
    createTrackbar( "Contrast","Camera", &cam.contrast, 100,onTrackbar_changed, &cam );
    createTrackbar( "Saturation","Camera", &cam.saturation, 100,onTrackbar_changed, &cam);
    createTrackbar( "Gain","Camera", &cam.gain, 100,onTrackbar_changed, &cam);
    createTrackbar( "Hue","Camera", &cam.hue, 100,onTrackbar_changed, &cam);
    */
    SDL_Event genericEvent;

    //setMouseCallback("Camera", onMouse, &frame);
    SDL_Surface* frameSurface;
    SDL_Texture* frameTexture;
    cv::Size blur_kernel = cv::Size(5, 5);
    while(cam.isOpened())
    {
        while( SDL_PollEvent(&genericEvent) )
        {
            //cout<<genericEvent.type<<endl;
            switch( genericEvent.type )
            {
                /* Keyboard event */
                /* Pass the event data onto PrintKeyInfo() */

                case SDL_KEYDOWN:
                    break;
                case SDL_KEYUP:
                    keyboardCallback(genericEvent.key);
                    break;
                case SDL_MOUSEBUTTONDOWN:
                {
                    mouseCallback(genericEvent.button, scaleX, scaleY, frame);
                    break;
                }
                case SDL_WINDOWEVENT:
                {
                    if (genericEvent.window.event==SDL_WINDOWEVENT_RESIZED)
                    {
                        //oldWidth = width;
                        //oldHeight = height;
                        SDL_GetWindowSize(win, &width, &height);
                        scaleX =  (float)(width)/ float(oldWidth);
                        scaleY = (float)(height) / (float)(oldHeight);
                    }
                    break;
                }
                /* SDL_QUIT event (window close) */
                case SDL_QUIT:
                    return 0;
                    break;
                default:
                    break;
            }
        }
        Mat blurred_frame, frame_out;
        frame_out = frame.clone();
        cv::cvtColor(frame, blurred_frame, cv::COLOR_BGR2GRAY);
        cv::GaussianBlur(blurred_frame, blurred_frame, blur_kernel, 3, 3);
        Mat roi, laplacian;
        Scalar delta;
        for (int ii=0; ii< context.getPolysNumber(); ii++)
        {
            roi = blurred_frame(context.getParks().at(ii).getBoundingRect());
            cv::Laplacian(roi, laplacian, CV_64F);
            delta = cv::mean(cv::abs(laplacian), context.getParks().at(ii).getMask());
            context.setParkingStatus(ii,  abs(delta[0] - context.getParks().at(ii).getThreshold())<0.35 );
        }
        cam>>frame;
        // /resize(frame_out, frame_out, cv::Size(frame.cols/WINDOW_SCALE, frame.rows/WINDOW_SCALE));
        context.draw(frame_out);

        //Convert to SDL_Surface
        frameSurface = SDL_CreateRGBSurfaceFrom((void*)frame_out.data,
            frame_out.size().width, frame_out.size().height,
            24, frame_out.cols *3,
            0xff0000, 0x00ff00, 0x0000ff, 0);

        if(frameSurface == NULL)
        {
            SDL_Log("Couldn't convert Mat to Surface.");
            return -2;
        }

        //Convert to SDL_Texture
        frameTexture = SDL_CreateTextureFromSurface(renderer, frameSurface);
        if(frameTexture == NULL)
        {
            SDL_Log("Couldn't convert Mat(converted to surface) to Texture."); //<- ERROR!!
            return -1;
        }
        //imshow("Camera", frame_out);
        SDL_RenderCopy(renderer, frameTexture, NULL, NULL);
        SDL_RenderPresent(renderer);
        /* A delay is needed to show (it actually wait for an input)*/
        if(waitKey(delay)>delay){;}

    }
    SDL_DestroyTexture(frameTexture);
    SDL_FreeSurface(frameSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    return 0;
}

void onTrackbar_changed(int, void* data)
{
 VideoSettings cam = *((VideoSettings*)data);
 cam.update();
}

void onMouse(int evt, int x, int y, int flags, void* param)
{

    if(evt == EVENT_LBUTTONDOWN)
    {
      context.mouseLeft( x, y);

    }
    else if(evt == EVENT_RBUTTONDOWN)
    {
        try
        {
            Mat* fr = (Mat* ) param;
            context.setFrame(*fr);
        }
        catch (const std::exception& e)
        {
            cout<<"onMouse frame not converted"<<endl;
        }
        context.mouseRight();
    }
}

int keyboardCallback(SDL_KeyboardEvent ev)
{
    switch(ev.keysym.sym)
    {
        case(SDLK_a):
        {
            cout<<"calling context keyboardA"<<endl;
            context.keyboardA();
            break;
        }
        case(SDLK_e):
        {
            cout<<"calling context keyboardE"<<endl;
            context.keyboardE();
            break;
        }
        case(SDLK_m):
        {
            cout<<"calling context keyboardM"<<endl;
            context.keyboardM();
            break;
        }
        case SDLK_UP:
        case SDLK_RIGHT:
        {
            cout<<"calling context RIGHT ARROW"<<endl;
            context.keyboardArrows(1);
            break;
        }
        case SDLK_DOWN:
        case SDLK_LEFT:
        {
            cout<<"calling context LEFT ARROW"<<endl;
            context.keyboardArrows(-1);
            break;
        }
        case (SDLK_RETURN):
        {
            cout<<"calling context RETURN ARROW"<<endl;
            context.keyboardReturn();
            break;
        }
        default:
        break;

    }
    return 0;
}


int mouseCallback(SDL_MouseButtonEvent ev, float scaleX, float scaleY, Mat frame)
{
    if(ev.button == SDL_BUTTON_LEFT)
    {
        cout<<scaleX<<" "<<scaleY<<endl;
        int scaled_x =  static_cast<int> ((float)(ev.x)/scaleX);
        int scaled_y = static_cast<int> ((float)(ev.y)/ scaleY);
        std::cout<<"scaled x: "<<scaled_x<<", scaled y: "<<scaled_y<<endl;
        context.mouseLeft( scaled_x,scaled_y);
    }
    else if(ev.button == SDL_BUTTON_RIGHT)
    {
        try
        {
            //Mat* fr = (Mat* ) param;
            context.setFrame(frame);
        }
        catch (const std::exception& e)
        {
            cout<<"onMouse frame not converted"<<endl;
        }
        context.mouseRight();
    }
}

我有一个单独的服务器/客户端应用程序(在 C 中)(当前使用 TCP,但我将其更改为 UDP),服务器将在从网络摄像头流式传输的程序的同一主机中运行。我希望,当客户端连接时,服务器获取网络摄像头帧(例如每 n 秒)并通过 websockets 将其发送给客户端。

为了方便起见,我考虑将这两个部分分开,因为一个可能没有另一个而存在。

但是我不知道如何使网络摄像头程序和服务器程序使用标准 POSIX 方法进行通信(可能是双向的)。

起初我想从 Opencv 程序中调用fork(),然后运行exec 来启动服务器程序。然而,服务器程序被编程为一个守护进程,所以我不能使用典型的父子进程通信(pipe)。

我可能会从服务器程序调用fork,然后使用openCV 程序运行exec,这样它们就具有父子关系,这将允许使用管道。但我不认为这是正确的。

其他解决方案可以是:

  • FIFO(命名管道)
  • 消息队列
  • 共享内存(这样内存可能会出问题吗?)

【问题讨论】:

  • 你可以把帧扔到 Redis 中,比如说放到一个有序集合中。然后,本地机器上的客户端以及网络上的任何其他地方都可以自己获取最新的(或任何其他)帧。可以将帧设置为在 N 秒后自动过期以控制内存使用。使用 bash、C/C++、PHP、Python、Java 读取或写入帧。

标签: c opencv server ipc


【解决方案1】:

根据我在评论中所说的话,我建议您考虑 Redis,它是一种非常快速的内存中数据结构服务器。它可以提供字符串、哈希、列表、集合、队列等。它安装简单,占地面积小。它可以从 C/C++、bash、Perl、Python、PHP、Java、Ruby 等访问。

此外,它是联网的,因此您可以将数据从一台或多台主机转储到其中,并使用任何其他语言从任何其他主机收集数据。

所以,这是一个示例生成器,它生成 1000 张图像并尽快将它们填充到 Redis 中,将帧命名为 f-0f-1f-2。每帧都有一个“生存时间”,因此它会在 10 秒后自动被删除,并且不会在内存中停留太久。

#!/bin/bash
################################################################################
# generate - generate "video" frames and dump into Redis
################################################################################

# Redis host
host=127.0.0.1

# Clear out Redis of data from any previous run
redis-cli -h $host flushall > /dev/null

# Generate 1000 frames, with Redis key "f-0", "f-1", "f-2" also setting expiration
ttl=10

for ((i=0;i<1000;i++)) ; do
   frameName=f-$i
   echo Generating frame: $frameName
   convert -size 640x480 -background magenta -fill white -gravity center label:"Frame: $i" JPG: | redis-cli -h $host -x setex "$frameName" $ttl > /dev/null
   redis-cli -h $host set currFrame $i > /dev/null
done

中间的convert 命令是ImageMagick,它只是生成一帧“视频”,如下所示:


然后有一个monitor 脚本检查Redis 正在使用多少内存,如下所示:

#!/bin/bash
################################################################################
# monitor
################################################################################
host=127.0.0.1

while :; do
   redis-cli -h $host -r 100 -i 1 info | grep used_memory:
done

然后有一个latest脚本从Redis获取最新的帧并显示出来:

#!/bin/bash
################################################################################
# latest - display latest video frame from Redis cache
################################################################################

# Redis host
host=127.0.0.1

# Get currFrame from Redis
currFrame=$(redis-cli --raw -h $host get currFrame)

redis-cli -h $host get f-$currFrame > current.jpg
open current.jpg

这是一个实际操作的视频:

【讨论】:

  • 感谢您的回复。我去看看。
  • 我使用的唯一 Redis 命令是 setsetexget。这些在 C、Python、PHP 等中看起来几乎相同,因此移植起来应该不难。
猜你喜欢
  • 1970-01-01
  • 2011-01-05
  • 2011-04-17
  • 2013-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-10
相关资源
最近更新 更多