QT C++ передача QImage по Tcp изображения с камеры + opencv

Задача: передать изображение с камеры из клиента на сервер с помощью сокета Tcp

На данный момент проблема заключается в том, что не могу передать 1 кадр изображения преобразованный в cv::Mat -> QImage -> QByteArray

Код отправки данных(Клиент):

 VideoCapture vcap(0);
  if(!vcap.isOpened()){
         cout << "Error opening video stream or file" << endl;
         return;
  }

while(true){
    Mat frame;
    vcap >> frame;
    
     QImage imgIn= QImage((uchar*) frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
     QByteArray arr;
     QBuffer buffer(&arr);
     buffer.open(QIODevice::WriteOnly);
     imgIn.save(&buffer, "PNG");

     QDataStream out(&arr,QIODevice::WriteOnly);
     out.setVersion(QDataStream::Qt_5_4);

     quint32 bytesSize = (quint32)arr.size();
     out.device()->seek(0);  
     out << (quint32)(arr.size() - sizeof(quint32));
     qDebug() << "bytesSize: " << bytesSize << Qt::endl;

     _socket->write(arr);
     qDebug() << "Sended" << Qt::endl;
     _socket->flush();

     imshow( "Frame", frame );
     char c = (char)waitKey(33);
     if( c == 27 ) break;
}

Код получения данных(сервер):

QDataStream in(_socket);
 in.setVersion(QDataStream::Qt_5_4);

 _socket->waitForReadyRead();

 if (m_blockSize == 0) {
      if (_socket->bytesAvailable() < sizeof(quint32)){
             return;
       }
       in >> m_blockSize;
  }
  qDebug()<<"bytesAvailable: " << _socket->bytesAvailable() << Qt::endl;
  qDebug() << "m_blockSize: " << m_blockSize << Qt::endl;
  
  if (_socket->bytesAvailable() < m_blockSize)
      return;

  QByteArray line = _socket->readAll();

  m_blockSize = 0;
  emit putByte(line);

Преобразование в QImage:

QImage img;
img.loadFromData(line,"PNG");

if(img.isNull()){ // true
    qDebug() << "Null Img" << Qt::endl;
}else{
    img.save("c:/my_projects/ahah/ServerImg.png");
    img_label->setPixmap(QPixmap::fromImage(img));
}

Проблема в том, что img.isNull() => true

Хотелось бы услышать совет как лучше сделать эту задачу, может быть лучше только посредствами Qt: QCamera и т.д. или с помощью других библиотек GStreamer …

UPD1: Упростил способ отправки:

while(true){
   Mat frame;
   vcap >> frame;

   _socket->write((char*)frame.data, frame.size().area());
   
   imshow( "Frame", frame );
   char c = (char)waitKey(33);
   if( c == 27 ) break;
}

И способ получения:

 int maxSize = 921600;
      if (_socket->bytesAvailable() < maxSize)
          return;
    
      QByteArray line = _socket->readAll();
      emit putByte(line);
    
      cv::Mat frame(480,640,CV_8UC3, line.data());

   

 if( frame.empty() ){
        qDebug() << "Empty frame" << Qt::endl;
    }else{
        cv::imshow("Test",frame);
    }

Но появилась новая проблема:

https://i.stack.imgur.com/aiIwi.jpg

Картинка теперь то ли восьмирится или что-то в это роде


Ответы (2 шт):

Автор решения: CyberAdept

Отправка изображения(клиент):

VideoCapture vcap(0);
  if(!vcap.isOpened()){
         cout << "Error opening video stream or file" << endl;
         return;
  }

 while(true){
   Mat frame;
   vcap >> frame;
   quint32 sizeInBytes = frame.size().area() * frame.channels();
   QByteArray databuf = QByteArray(reinterpret_cast<char*>(frame.data), sizeInBytes);
   QDataStream out(&databuf, QIODevice::WriteOnly);

   out.device()->seek(0);

   out << (quint32)sizeInBytes;
   out << (int)frame.rows;
   out << (int)frame.cols;

   if(_socket->isOpen() && _socket->state() == QAbstractSocket::ConnectedState){
      _socket->write(databuf);
      _socket->waitForBytesWritten();
}
  imshow( "Frame", frame );
  char c = (char)waitKey(33);
  if( c == 27 ) break;
}

Получение изображения(сервер):

 QDataStream in(_socket);
 in.setVersion(QDataStream::Qt_5_4);

 _socket->waitForBytesWritten();

 if (m_blockSize == 0) {
     if (_socket->bytesAvailable() < sizeof(quint32)){
         return;
     }
     in >> m_blockSize; // get quint32 size of img
     in >> rows;        // get int
     in >> cols;        // get int
 }
 int maxSize =  m_blockSize; 
 if (_socket->bytesAvailable() < maxSize)
      return;
 if(_socket->isReadable()){
      QByteArray line = _socket->read(maxSize);
      emit putByte(line, rows, cols);
}

Вывод полученного изображения:

cv::Mat frame(rows, cols, CV_8UC3, line.data());

if( frame.empty() ){
    qDebug() << "Empty frame" << Qt::endl;
}else{
     cv::imshow("Test",frame);
}

Благодарность: @AlexanderChernin

Результат


введите сюда описание изображения

→ Ссылка
Автор решения: mugiseyebrows

Я решал похожую задачу и пришел к выводу: лучше взять готовый http протокол вместо того чтобы изобретать похожий, то есть реализовать (или взять готовый) http сервер понимающийх multipart POST запросы (можно даже синхронный однопоточный).

Это даст следующие преимущества: не надо думать как и чем разделять передаваемые изображения, когда понадобиться передавать дополнительные данные помимо изображения (понадобится) не придётся изменять протокол. Отлаживать (и тестировать) сервер и клиент можно будет с помощью веб-технологии: сервера с помощью обычного curl, а клиента с помощью (php или python или nodejs)-сервера из пяти строчек. То есть отлаживать обе части можно будет независимо.

→ Ссылка