サーバ・クライアントモデルで、シングルクライアントのサンプルです。IplImage?構造体をネットワーク上で送受信することで、簡単なビデオストリーミングを実現しています。
Fig.1 とても簡単なイメージ図
ところが IplImage?構造体の大きさは一定ではなく、また画像データは IplImage?->imageData構造体に入っているため、正確な大きさをまじめに調べて送信するのは面倒 ( ゚Д゚)マンドクセーので、とりあえずデータサイズを決め打ちでやってます。すいません。きちんとすべてのデータを送るときは、cvCopy()関数などの実装を参考に作ると良いと思います。cvCopy()関数の実体は、opencv\cxcore\src\cxcopy.cpp にあります。
またサンプルでは、無圧縮の画像サイズが約230KBあります。これを 30 frame/secで送ると仮定すると、ネットワーク帯域が 6.9MB/sec = 55.2Mbps必要です。簡単に実装するなら、簡単な差分データを生成するか、データの送受信に JPEGを使うと良いと思います。
ということで、もう少しまじめに実装したものが、NetCVです。
便利だと思いますので、ぜひご利用ください。
/**************************************************************************************************
Title : OpenCV サンプルプログラム - サーバ
Programer : Yousuke FURUSAWA
Comitter : Yousuke FURUSAWA.
Copyright : Copyright (C) Yousuke FURUSAWA. 2006
Since : 2006/05/09 16:50:53
Filename : server.c
Last up date : 2006/05/09 17:26:31
Kanji-Code : Shift JIS
TAB Space : 4
**************************************************************************************************/
/*=================================================================================================
ヘッダファイルのインクルード
=================================================================================================*/
#include <windows.h>
#include <winsock.h>
#pragma comment(lib, "WSock32.lib")
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cvaux.lib")
#pragma comment(lib, "cvcam.lib")
#pragma comment(lib, "cvhaartraining.lib")
#pragma comment(lib, "highgui.lib")
/*=================================================================================================
プロトタイプ宣言
=================================================================================================*/
SOCKET getServerSocket(unsigned short portnumber);
/**************************************************************************************************
スタートアップ関数
**************************************************************************************************/
int main(int argc, char **argv){
char buf[32];
IplImage *frame;
CvCapture *capture;
WSADATA wsaData;
SOCKET socket;
/* デバイスを開く */
if( !(capture = cvCaptureFromCAM(0)) ){
printf("ERROR: Could not open Camera Device\n");
return(-1);
}
/* WinSockの初期化 */
if( WSAStartup(MAKEWORD(2, 1), &wsaData) != 0){
printf("ERROR: WSAStartup");
return(-2);
}
/* 接続待ち... */
if( (socket = getServerSocket(5030)) == -1){
printf("ERROR: Cound not connet\n");
return(-3);
}
/* 初期化 */
cvNamedWindow("result", 1);
Sleep(500);
/* データを送り続ける */
while( cvWaitKey(1) != 'q' ){
if( !(frame = cvQueryFrame(capture)) ) return(-4);
send(socket, frame, 112, 0);
while(recv(socket, &buf, 2, 0) != 2) Sleep(0);
send(socket, frame->imageData, 230400, 0);
while(recv(socket, &buf, 2, 0) != 2) Sleep(0);
cvShowImage("result", frame);
Sleep(500);
}
/* 終了処理 */
cvDestroyWindow("result");
shutdown(socket, 2);
closesocket(socket);
WSACleanup();
return(0);
}
/**************************************************************************************************
サーバ機能 : Programmed By Junichi KOSAKA. 2003
**************************************************************************************************/
SOCKET getServerSocket(unsigned short portnumber)
{
WORD wVersionRequested = MAKEWORD(2,0); // Winsock 2.0 requested
WSADATA wsaData; // Winsock details
SOCKET s; // socket descriptor
SOCKET cliantS; // cliant socket descriptor
SOCKADDR_IN addr; // Internet address
SOCKADDR_IN cliantAddr; // Internet address
int cliantAddrLen;
/* Winsock の初期化 */
if(WSAStartup(wVersionRequested, &wsaData) != 0){
//printf("WSAStartup() unsuccessful");
return -1;
}
/* 要求したWinsock のバージョンが使用できるかを確認する */
if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)){
//printf("Version of Winsock does not agree\n\n");
WSACleanup(); // terminate Winsock use
return -1;
}
/* ストリームソケットの作成 */
s = socket(AF_INET, SOCK_STREAM, 0); // AF_INET・SOCK_STREAM の指定によってTCP/IP設定となる
if(s != INVALID_SOCKET){
memset(&addr, 0, sizeof(addr));
/* ソケットのアドレス情報を記入する */
addr.sin_family = AF_INET; // address family
addr.sin_port = htons(portnumber); // service port
addr.sin_addr.s_addr = htonl(INADDR_ANY); // Internet address
}else{
//printf("socket() generated error\n");
return -1;
}
/* ソケットに名称を付与する(ソケットとポートの結合) */
if(bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR){
//printf("bind() generated error\n");
return -1;
}
/* 接続を聴取する */
if(listen(s, 1) == SOCKET_ERROR){// backlog は 接続するクライアントの数を指定(1<=backlog<=5)
//printf("listen() generated error\n");
return -1;
}
printf("Waiting for connection ... port = %d\n",portnumber);
cliantAddrLen = sizeof(cliantAddr);
/* 接続を受容する */
cliantS = accept(s, (LPSOCKADDR)&cliantAddr, &cliantAddrLen);
if(cliantS != INVALID_SOCKET){
/*
cAddr.s_addr = cliantAddr.sin_addr.s_addr;
host = gethostbyaddr((const char *)&cAddr , 4 , AF_INET);
if(host){ // クライアントの名前を獲得するのに成功
printf("Connected from %s [%s] port = %d\n",
inet_ntoa(cliantAddr.sin_addr),host->h_name,ntohs(cliantAddr.sin_port));
}else{
printf("Connected from %s [%s] port = %d\n",
inet_ntoa(cliantAddr.sin_addr),host->h_name,ntohs(cliantAddr.sin_port));
}
*/
return cliantS;
}else{
//printf("accept() generated error");
return -1;
}
}
/**************************************************************************************************
Title : OpenCV サンプルプログラム - クライアント
Programer : Yousuke FURUSAWA
Comitter : Yousuke FURUSAWA.
Copyright : Copyright (C) Yousuke FURUSAWA. 2006
Since : 2006/05/09 16:50:53
Filename : client.c
Last up date : 2006/05/09 17:26:38
Kanji-Code : Shift JIS
TAB Space : 4
**************************************************************************************************/
/*=================================================================================================
ヘッダファイルのインクルード
=================================================================================================*/
#include <windows.h>
#include <winsock.h>
#pragma comment(lib, "WSock32.lib")
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cvaux.lib")
#pragma comment(lib, "cvcam.lib")
#pragma comment(lib, "cvhaartraining.lib")
#pragma comment(lib, "highgui.lib")
/*=================================================================================================
構造体
=================================================================================================*/
/* サーバ情報 */
typedef struct {
SOCKET socket;
struct sockaddr_in addr;
struct hostent *hp;
} Network;
typedef Network* pNetwork;
/*=================================================================================================
プロトタイプ宣言
=================================================================================================*/
static pNetwork NETWORK_open(char *server, int port);
static int NETWORK_close(pNetwork network);
/**************************************************************************************************
スタートアップ関数
**************************************************************************************************/
int main(int argc, char **argv){
IplImage frame;
char data1[230400], data2[230400];
WSADATA wsaData;
pNetwork network;
/* WinSockの初期化 */
if( WSAStartup(MAKEWORD(2, 1), &wsaData) != 0){
perror("WSAStartup");
return(-1);
}
/* サーバに接続 */
if((network = NETWORK_open("192.168.1.208", 5030)) == NULL){
perror("NETWORK_open(television)");
return(-2);
}
/* 初期化 */
cvNamedWindow("result", 1);
/* データを取得し続ける */
while( cvWaitKey(1) != 'q' ){
while(recv(network->socket, &frame, 112, 0) != 112);
send(network->socket, "ok", 2, 0);
while(recv(network->socket, &data1, 230400, 0) != 230400);
frame.imageData = &data1;
cvShowImage("result", &frame);
send(network->socket, "ok", 2, 0);
Sleep(0);
}
/* 終了処理 */
cvDestroyWindow("result");
NETWORK_close(network);
return;
}
/*-------------------------------------------------------------------------------------------------
サーバに接続する
-------------------------------------------------------------------------------------------------*/
static pNetwork NETWORK_open(char *server, int port)
{
pNetwork network;
/* メモリを確保 */
if((network = (pNetwork)malloc(sizeof(Network))) == NULL){
perror("malloc(network)");
return(NULL);
}
/*
* addrの中身を0にしておかないと、bind()でエラーが起こることがある
*/
memset((char *)&network->addr, 0, sizeof(network->addr));
/*
* ソケットを作る。このソケットはUNIXドメインで、ストリーム型ソケット。
*/
if ((network->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
free(network);
return(NULL);
}
/*
* ソケットの名前を入れておく
*/
if ((network->hp = gethostbyname(server)) == NULL) {
perror("No such host");
free(network);
return(NULL);
}
memcpy(&network->addr.sin_addr, network->hp->h_addr, network->hp->h_length);
network->addr.sin_family = AF_INET;
network->addr.sin_port = htons(port);
/*
* サーバーとの接続を試みる。これが成功するためには、サーバーがすでに
* このアドレスをbind()して、listen()を発行していなければならない。
*/
if (connect(network->socket, (struct sockaddr *)&network->addr, sizeof(network->addr)) < 0){
perror("connect");
free(network);
return(NULL);
}
return(network);
}
/*-------------------------------------------------------------------------------------------------
切断する
-------------------------------------------------------------------------------------------------*/
static int NETWORK_close(pNetwork network)
{
closesocket(network->socket);
free(network);
return(TRUE);
}
Fig.2 サーバ側接続待ち
Fig.3 サーバ側接続後
Fig.4 クライアント側