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:
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:
The infinite loop problem will happen if
read
fails in the client and it returns-1
.-1 != 0
, and thenread
will continue to read-1
forever.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
Restrict Variadic Template Arguments
Error::Make_Unique Is Not a Member of 'Std'
Static and Dynamic/Shared Linking with Mingw
Reading an Application's Manifest File
Is Boost Shared_Ptr <Xxx> Thread Safe
Std::Forward_List and Std::Forward_List::Push_Back
Edges on Polygon Outlines Not Always Correct
Cannot Create Constexpr Std::Vector
Convert a Static Library to a Shared Library (Create Libsome.So from Libsome.A): Where's My Symbols
Std::Array with Aggregate Initialization on G++ Generates Huge Code
Changing Dpi Scaling Size of Display Make Qt Application's Font Size Get Rendered Bigger
"Field Has Incomplete Type" Error
Best Library for Statistics in C++
Why Isn't There an Operator[] for a Std::List
How to Create a C++ Boost Undirected Graph and Traverse It in Depth First Search (Dfs) Order
How to Add Static Libraries to a Visual Studio Project
In C/C++, Is Char* Arrayname[][] a Pointer to a Pointer to a Pointer or a Pointer to a Pointer