Haris
Haris

Reputation: 14043

OpenCV C++ Convert Byte array to Mat

How can I convert byte array to Mat which is received from socket ?.

My client application will send color image data like this

Mat frame; //colour image
int imgSize = frame.total()*frame.elemSize();
int bytes = send(clientSock, frame.data, imgSize, 0));//write to the socket

And the server will receives the data like

    char sockData[imgSize];
    Mat img;
    for (int i = 0; i < imgSize; i += bytes) {
        bytes = recv(connectSock, sockData +i, imgSize  - i, 0));
    }

   // Write to mat
    for (int i = 0;  i < img.rows; i++) {
      for (int j = 0; j < img.cols; j++) {
         (img.row(i)).col(j) = (uchar)sockData[((img.cols)*i)+j];
       }
     }

I am getting distorted image at the receiver. Is there any problem in my code ? Thanks in advance.......

Upvotes: 1

Views: 12355

Answers (4)

fivef
fivef

Reputation: 2557

Doesn't this work?

cv::Mat frame(img.rows, img.cols, CV_8UC3, sockData);

Just replace CV_8UC3 with the correct image format:

CV_<bit-depth>{U|S|F}C(<number_of_channels>)

see https://docs.opencv.org/2.4/modules/core/doc/basic_structures.html


Edit: There is a 5th additional field which can be useful. The number of bytes per row (in case there are a few padding bytes). In working with V4L2 today, I successfully used this cv::Mat constructor:

v4l2_format camera_format = ...; // see https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-g-fmt.html#description
cv::Mat mat(camera_format.fmt.pix.height,
            camera_format.fmt.pix.width,
            CV_8UC3,
            raw_data_ptr,
            camera_format.fmt.pix.bytesperline);

Upvotes: 3

infoclogged
infoclogged

Reputation: 3997

Adding to Michele answer, one can also use the MatIterator to solve this.

cv::Mat m;
m.create(10, 10, CV_32FC3);

// This is the socket data.
float *array = (float *)malloc( 3*sizeof(float)*10*10 );

cv::MatIterator_<cv::Vec3f> it = m.begin<cv::Vec3f>();
for (unsigned i = 0; it != m.end<cv::Vec3f>(); it++ ) {
    for ( unsigned j = 0; j < 3; j++ ) {
        (*it)[j] = *(array + i );
        i++;
    }
}

Now you have a float cv::Mat. In case of 8 bit, simply change float to uchar and Vec3f to Vec3b and CV_32FC3 to CV_8UC3

Upvotes: 1

Michele mpp Marostica
Michele mpp Marostica

Reputation: 2472

If you have colour image you may read it in a math with 3 channels of uchar so change this piece of code:

for (int i = 0;  i < img.rows; i++) {
  for (int j = 0; j < img.cols; j++) {
     (img.row(i)).col(j) = (uchar)sockData[((img.cols)*i)+j];
   }
 }

with this:

int baseIndex = 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[baseIndex + 0],
                                        sockData[baseIndex + 1],
                                        sockData[baseIndex + 2]);
     baseIndex = baseIndex + 3;
   }
 }

Maybe this should work.

Upvotes: 4

Haris
Haris

Reputation: 14043

I solved the problem using below code.

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

Upvotes: 1

Related Questions