C++ OpenCV image sending through socket
Asked Answered
K

5

16

I'm trying to implement a streaming service using OpenCV and sockets. The server side loads a given image and sends it to the client, which receives it through the socket and displays it. I'm having a lot of trouble, though, sending the data of the image and reconstructing it on the client side. This is the code I've made so far:

Image sender:

#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;
}

Image receiver:

#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; 
}

Al I receive on the client side is a black image, nothing else. Any help?

Keitt answered 1/12, 2013 at 16:41 Comment(0)
E
20

Get Mat.data and directly send to the socket, the data order is BGR BGR BGR.... On the receiving side assumes you know the size of image. After receiving just assign the received buffer(BGR BGR... array) to a Mat.

Client:-

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))

Server:-

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;
    }
}
Extempore answered 2/12, 2013 at 5:7 Comment(4)
THAT saved my day. Thanks A LOT, now works perfectly. One last question, though. In the server side, I had to initialize "bytes" to a certain value. I just added int bytes = 0; right before the receiving data for loop. Is that right? I mean, it works, but I don't really know if it's correct. Thanks again :DKeitt
Yes you can initialize with 0, that's not a matter. Actually bytes used to check whether the recv() call is successful or not. On successful completion, recv() shall return the length of the message read in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned.Extempore
I would strongly recommend to encode the data to JPEG or PNG before sending over sockets. It can reduce the size dramatically (JPEG 10%, PNG 25%)Lumbering
So it is better to encode to PNG isn't it? If encoding, should reshaping being carried out anyway?Isaacson
A
3
bytes = send(clientSock, frame.data, imgSize, 0));

This statement is giving error in Visual Studio 12.It say that.. ERROR: argument of type "uchar *" is incompatible with parameter of type "const char *" at ""frame.data"".

Annulet answered 6/1, 2014 at 11:32 Comment(0)
E
1

Buffer overflow. And I am not sure that format and sizes of image are the same -> you should serialize your Mat honestly, something like here.

Emancipated answered 1/12, 2013 at 17:22 Comment(1)
1024 is not enough? Or do you mean at the sender? What should I put then? 9999999?Keitt
T
1

Haris's answer is right and worked for me. However, instead of that server code (constructing image from uchar data), you can also use the shorter form:

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);
Thetic answered 26/7, 2015 at 9:31 Comment(0)
S
1

In Microsoft Visual Studio 2012, I have a red line under imgSize in sockData[imgSize] saying expression must have a constant value. I then changed int imgSize to const int imgSize. It still shows me the same problem.

 Mat img = Mat::zeros(100,100, CV_8UC3);
 const int imgSize = img.total()*img.elemSize();
 uchar sockData[imgSize];
Southland answered 16/6, 2016 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.