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 шт):
Отправка изображения(клиент):
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
Результат
Я решал похожую задачу и пришел к выводу: лучше взять готовый http протокол вместо того чтобы изобретать похожий, то есть реализовать (или взять готовый) http сервер понимающийх multipart POST запросы (можно даже синхронный однопоточный).
Это даст следующие преимущества: не надо думать как и чем разделять передаваемые изображения, когда понадобиться передавать дополнительные данные помимо изображения (понадобится) не придётся изменять протокол. Отлаживать (и тестировать) сервер и клиент можно будет с помощью веб-технологии: сервера с помощью обычного curl, а клиента с помощью (php или python или nodejs)-сервера из пяти строчек. То есть отлаживать обе части можно будет независимо.
