cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

カメラ画像をOpenCVで取得してJpegエンコードして、ZeroMQを通してネットワーク配信するプログラムサンプル

(メモ)
Windows10, Visual Studio2015環境で、openFrameworks9.3でofxZmq addonとOpenCV3.1を使用して高速に画像を配信するためのサンプルプログラムです。
ofxZmqにstd::vector& dataを受け取れるように、インターフェースの追加をちょっとだけしています。


ofApp.h

#pragma once

#include "ofMain.h"

#include "opencv2/opencv.hpp"

class ofxZmqSubscriber;
class ofxZmqPublisher;

class testApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed  (int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);
		
		ofxZmqSubscriber* subscriber;
		ofxZmqPublisher* publisher;

		cv::VideoCapture cap;

		cv::Mat frame;
		std::vector<unsigned char> send_buf;
		std::vector<int> params;
		string type;

		cv::Mat image;
		std::vector<unsigned char> recv_buf;
};

ofApp.cpp

#include "testApp.h"

#include "ofxZmq.h"

#include "opencv2/opencv.hpp"

#pragma comment(lib, "opencv_calib3d310.lib")
#pragma comment(lib, "opencv_core310.lib")
#pragma comment(lib, "opencv_features2d310.lib")
#pragma comment(lib, "opencv_flann310.lib")
#pragma comment(lib, "opencv_highgui310.lib")
#pragma comment(lib, "opencv_imgcodecs310.lib")
#pragma comment(lib, "opencv_imgproc310.lib")
#pragma comment(lib, "opencv_ml310.lib")
#pragma comment(lib, "opencv_objdetect310.lib")
#pragma comment(lib, "opencv_photo310.lib")
#pragma comment(lib, "opencv_shape310.lib")
#pragma comment(lib, "opencv_stitching310.lib")
#pragma comment(lib, "opencv_superres310.lib")
#pragma comment(lib, "opencv_ts310.lib")
#pragma comment(lib, "opencv_video310.lib")
#pragma comment(lib, "opencv_videoio310.lib")
#pragma comment(lib, "opencv_videostab310.lib")

//--------------------------------------------------------------
void testApp::setup()
{
	ofSetFrameRate(30);

	cap.open(0);

	if (cap.isOpened()) {
		cout << "Camera is ready!" << endl;

        	cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
	        cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
        	cap.set(cv::CAP_PROP_FPS, 30);

		type = ".jpg";
		//type = ".webp";
		if (type == ".jpg") {
			params.push_back(CV_IMWRITE_JPEG_QUALITY);
			params.push_back(100);
			params.push_back(CV_IMWRITE_JPEG_OPTIMIZE);
			params.push_back(0);
			params.push_back(CV_IMWRITE_JPEG_PROGRESSIVE);
			params.push_back(0);
		}
		else if (type == ".webp") {
			params.push_back(CV_IMWRITE_WEBP_QUALITY);
			params.push_back(100);
		}

		// start server
		publisher = new ofxZmqPublisher();
                publisher->setHighWaterMark(1);
		publisher->bind("tcp://*:9999");
	}

	// start client
	subscriber = new ofxZmqSubscriber();
	subscriber->setHighWaterMark(1);
	subscriber->connect("tcp://localhost:9999");
}

//--------------------------------------------------------------
void testApp::update()
{
	while (subscriber->hasWaitingMessage())
	{
		subscriber->getNextMessage(recv_buf);
		image = cv::imdecode(recv_buf, CV_LOAD_IMAGE_COLOR);

		cout << "received data: " << recv_buf.size() << endl;
	}

	if (!cap.isOpened()) return;

	if (cap.grab()) {
		cap >> frame;

		int msec_start = ofGetElapsedTimeMillis();
		bool ret = cv::imencode(type, frame, send_buf, params);
		int msec_stop = ofGetElapsedTimeMillis();

		if (ret) {
			publisher->send(send_buf);

			int lap = msec_stop - msec_start;
			int fps = (lap) ? 1000.0 / lap : ofGetFrameRate();
			cout << "encoded size(" << type << ") : " << send_buf.size()
			     << ", time : " << lap << ", fps : " << fps << endl;
		}
	}
}

//--------------------------------------------------------------
void testApp::draw()
{
	if (!image.empty()) {
		cv::imshow("image", image);
	}
}

//--------------------------------------------------------------
void testApp::keyPressed(int key)
{

}

//--------------------------------------------------------------
void testApp::keyReleased(int key)
{

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y)
{

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button)
{

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button)
{

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button)
{

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h)
{

}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg)
{

}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo)
{

}

Core i7-4770S環境でJpegエンコードがおよそ10msec,出力データサイズが220kBytesでした。
WebPエンコードは、およそ60msecで、150kBytesでした。
WebPでQualityを70賭したときにはおよそ40msecで40kBytesでした。ストリーミングでも十分実用が見込めるぐらいの速さになってきました。

それぞれ、libjpeg-turboとMethod2でthreadを有効化したlibwebp1.5をOpenCVに静的リンクさせて使っています。