【发布时间】: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 读取或写入帧。