OpenCVとソケットを使用してストリーミングサービスを実装しようとしています。サーバー側は指定された画像をロードしてクライアントに送信し、クライアントはソケットを介して画像を受信して表示します。しかし、画像のデータを送信してクライアント側で再構築するのは大変です。これは私がこれまでに作ったコードです:
画像送信者:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such Host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
Mat image;
image = imread(argv[3], CV_LOAD_IMAGE_COLOR);
if(! image.data ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
return -1;
}
n = write(sockfd,image.data,image.total()*image.channels());
if (n < 0)
error("ERROR writing to socket");
close(sockfd);
namedWindow( "Client", CV_WINDOW_AUTOSIZE );// Create a window for display.
imshow( "Client", image );
waitKey(0);
return 0;
}
画像レシーバー:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[1024];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,1024);
n = read(newsockfd,buffer,1023);
if (n < 0) error("ERROR reading from socket");
Mat image(690,690,CV_8UC3,*buffer);
imwrite("/home/securitas/images/prova.jpg",image);
close(newsockfd);
close(sockfd);
namedWindow( "Server", CV_WINDOW_AUTOSIZE );// Create a window for display.
imshow( "Server", image );
waitKey(0);
return 0;
}
クライアント側で受け取るすべてが黒い画像で、それ以外は何もありません。何か助けは?
取得する Mat.data
および直接ソケットに送信する場合、データの順序はBGR BGR BGR ....です。受信側では、イメージのサイズがわかっていると想定しています。受信後、受信したバッファ(BGR BGR ...配列)をMat
に割り当てます。
クライアント:-
Mat frame;
frame = (frame.reshape(0,1)); // to make it continuous
int imgSize = frame.total()*frame.elemSize();
// Send data here
bytes = send(clientSock, frame.data, imgSize, 0))
サーバー:-
Mat img = Mat::zeros( height,width, CV_8UC3);
int imgSize = img.total()*img.elemSize();
uchar sockData[imgSize];
//Receive data here
for (int i = 0; i < imgSize; i += bytes) {
if ((bytes = recv(connectSock, sockData +i, imgSize - i, 0)) == -1) {
quit("recv failed", 1);
}
}
// Assign pixel value to img
int ptr=0;
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
img.at<cv::Vec3b>(i,j) = cv::Vec3b(sockData[ptr+ 0],sockData[ptr+1],sockData[ptr+2]);
ptr=ptr+3;
}
}
bytes = send(clientSock, frame.data, imgSize, 0));
このステートメントは、Visual Studio 12でエラーを発生させます。それは.. ERROR:タイプ "uchar *"の引数は、タイプ "const char *"のパラメーター "" frame.data ""と互換性がありません。
ハリスの答えは正しいと私のために働いた。ただし、そのサーバーコード(ucharデータから画像を構成する)の代わりに、より短い形式を使用することもできます。
int imgSize = img.total()*img.elemSize();
uchar sockData[imgSize];
for (int i = 0; i < imgSize; i += bytes) {
if ((bytes = recv(connectSock, sockData +i, imgSize - i, 0)) == -1)
quit("recv failed", 1);
}
// change the last loop to below statement
Mat img(Size(HEIGHT, WIDTH), CV_8UC3, socketData);
Microsoft Visual Studio 2012では、式は定数値でなければならないというsockData [imgSize]のimgSizeの下に赤い線があります。次に、int imgSizeをconst int imgSizeに変更しました。それでも同じ問題が発生します。
Mat img = Mat::zeros(100,100, CV_8UC3);
const int imgSize = img.total()*img.elemSize();
uchar sockData[imgSize];
バッファオーバーフロー。そして、画像のフォーマットとサイズが同じであるかどうかはわかりません->マットを正直にシリアル化する必要があります ここのように 。