C++ Opencv Image Sending Through Socket

C++ OpenCV image sending through socket

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

How to send OpenCV Mat throught socket

I wouldn't recommend sending a cv::Mat bewtween computers - especially computers with different CPU architecture, byte ordering, word length and memory padding.

convert the image to a non-compressed (or losslessly compressed) format such as png and send that.

You can use cv::encode / cv:: decode to create the image format in memory without having to read/write a temp file.

Note: imdecode determines the image type from data inside the image format (png, jpeg etc) header. The receiver doesn't need to know the size or type.

How to transfer cv::VideoCapture frames through socket in client-server model (OpenCV C++)?

Env: OpenCV 3.3, G++5.4, Ubuntu 16.04


If you want to save the video, you should set CV_FOURCE to MJPG

VideoWriter outputVideo;
Size S = Size((int) 640,(int) 480);
outputVideo.open("receive.avi", CV_FOURCC('M','J','P','G'), 30, S, true);

This is my result:

Sample Image

Code as follow:

客户端(Client.cpp):

//!2017.12.19 19:19:01 CST
//!2017.12.19 22:19:38 CST
//!2017.12.19 22:39:37 CST
// 客户端
// 使用 OpenCV 读取视频(并处理),然后使用 SOCKET 上传到服务器。

#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
int sock;
struct sockaddr_in addr;

sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
exit(1);
}

addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0){
perror("connect");
exit(2);
}

int bbytee;
cout << "before open the cam" << endl;

VideoCapture cap(0);

if(!cap.isOpened()) {
cout<< "Could not open the camera" << endl;
close(sock);
return -1;
}

Mat frame;
frame = Mat::zeros(480, 640, CV_8UC3);
int imgSize = frame.cols*frame.rows*3;

int cnt=0;
//Mat frame;
while(1) {
cap >> frame;
if(frame.empty()) {
cerr<<"[client] VideoCapture(0) error!"<<endl;
}

cout<< ++cnt << ":"<< frame.isContinuous()<<"," <<frame.size()<<endl;;

if( (bbytee = send(sock, frame.data, imgSize, 0)) < 0 ) {
cerr<< "bytes = " << bbytee << endl;
break;
}

cv::imshow("client", frame);
if(cv::waitKey(100) == 'q') {
break;
}
}
close(sock);
return 0;
}

服务端(Server.cpp):

//!2017.12.19 19:19:01 CST
//!2017.12.19 22:19:38 CST
//!2017.12.19 22:39:37 CST
// 服务器
// 监听客户端请求,读取视频流(并处理),保存。
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
int sock, listener;
struct sockaddr_in addr;

if( (listener = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("[server] socket() failed");
exit(1);
}

addr.sin_family = AF_INET;
addr.sin_port = htons(3425);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("[server] binding faild!");
exit(2);
}

listen(listener, 1);

int num_of_recv_bytes;
VideoWriter outputVideo;
Size S = Size((int) 640,(int) 480);
outputVideo.open("receive.avi", CV_FOURCC('M','J','P','G'), 30, S, true);

int imgSize = 480*640*3;
Mat frame = Mat::zeros(480, 640, CV_8UC3);
uchar *iptr = frame.data;
int key;

int cnt=0;
while(1){
cout << ++cnt<<endl;
sock = accept(listener, NULL, NULL);
if(sock < 0){
perror("[server] accept() faild!");
exit(3);
}

while(key != 'q') {
if( num_of_recv_bytes = recv(sock, iptr, imgSize, MSG_WAITALL) == -1 ) {
cerr << "recv failed, received bytes = " << num_of_recv_bytes << endl;
}

outputVideo<< frame;
imshow("server", frame);
if (key = waitKey(100) >= 0) break;
}
outputVideo.release();
close(sock);
break;
}
return 0;
}

Sending Opencv Mat through a TCP connection in C++

Since you already use Boost, you might as well look into the module serialization. Below is an example that converts cv::Mat to std::string:

#include <opencv2/core.hpp>

#include <boost/serialization/split_free.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

#include <cassert>
#include <string>
#include <vector>

BOOST_SERIALIZATION_SPLIT_FREE( cv::Mat )

namespace boost {
namespace serialization {

template <class Archive>
void save( Archive & ar, const cv::Mat & m, const unsigned int version )
{
size_t elemSize = m.elemSize();
size_t elemType = m.type();

ar & m.cols;
ar & m.rows;
ar & elemSize;
ar & elemType;

const size_t dataSize = m.cols * m.rows * m.elemSize();
for ( size_t i = 0; i < dataSize; ++i )
ar & m.data[ i ];
}

template <class Archive>
void load( Archive & ar, cv::Mat& m, const unsigned int version )
{
int cols, rows;
size_t elemSize, elemType;

ar & cols;
ar & rows;
ar & elemSize;
ar & elemType;

m.create( rows, cols, static_cast< int >( elemType ) );
const size_t dataSize = m.cols * m.rows * elemSize;
for (size_t i = 0; i < dataSize; ++i)
ar & m.data[ i ];
}

} // namespace serialization
} // namespace boost

std::string save( const cv::Mat & mat )
{
std::ostringstream oss;
boost::archive::text_oarchive toa( oss );
toa << mat;

return oss.str();
}

void load( cv::Mat & mat, const char * data_str )
{
std::stringstream ss;
ss << data_str;

boost::archive::text_iarchive tia( ss );
tia >> mat;
}

int main( int argc, char ** argv )
{
cv::Mat eye = cv::Mat::eye( 3, 3, CV_32FC1 );

std::string serialized = save( eye );
std::cout << "serialized = " << serialized << std::endl;

cv::Mat deserialized;
load( deserialized, serialized.c_str() );
std::cout << "deserialized = \n\n" << deserialized << std::endl;

return 0;
}

C++ Sending vectoruchar with socket loosing data

You have two major flaws, one which could lead to an infinite loop, and one which leads to the problem you experience:

  1. The infinite loop problem will happen if read fails in the client and it returns -1. -1 != 0, and then read will continue to read -1 forever.

  2. The second and main problem is that you treat the data you send between the programs a strings which it is not. A "string" is a null-terminated (i.e. zero-terminated) sequence of characters, your image is not that. In fact, it might even contain embedded zeros inside the data which will give you invalid data in the middle as well.

To solve both problem I suggest you change the reading loop (and the variables used) to something like this:

uchar buffer[1024];
ssize_t read_result;
std::vector<uchar> data;

// While there's no error (read returns -1) or the connection isn't
// closed (read returns 0), continue to append the received data
// into the vector
while ((read_result = read(sock, buffer, sizeof buffer)) > 0)
{
// No errors, no closed connection
// Append the new data (and only the new data) at the end of the vector
data.insert(end(data), buffer, buffer + read_result);
}

After this loop, and if read_result == 0, then data should contain only the data that was sent. And all of it.



Related Topics



Leave a reply



Submit