Could someone help me with displaying video from a camera. The values from camera are stored in QVector. Is it possible to display it this way?
-
Could you give a bit more detail about the things you have tried so far, perhaps with some code snippets? – gareththegeek Apr 03 '17 at 14:56
-
My data is stored in QVector
then i want to scale it to QVector – smilč Apr 03 '17 at 15:06and into QPixmap class. Or if it's possible directly from QVector to QPixmap class. I tried to save vector value to pix class object, but that doesn't work. -
2I suggest to edit the question and put the code in it. – Marco Apr 03 '17 at 15:43
2 Answers
RGB source image
Direct conversion is not possible, unless the QVector
is a memory-mapped file. If it contains the raw bytes instead (as it seems to), I suggest constructing a QImage
from the raw data as an intermediate object and then converting it to QPixmap
:
// assuming 'v' is the vector, and 'w' and 'h' the size of the image
const QImage tmpImage((const char*)v.data(), w, h, QImage::Format_RGB16);
QPixmap pixmap(QPixmap::convertFromQImage(tmpImage));
Given the original element size is 16 bits, you may need QImage::Format_RGB16
(for 5-6-5) or QImage::Format_RGB555
(for 5-5-5). Check available formats for more options. Assuming, of course, an RGB image.
Grayscale images
If you image only has one color channel, then you'll have to make a manual conversion, since Qt is not able to manipulate 16-bits grayscale images.
Easy code, but may not produce the results you want:
const QImage tmpImage((const char*)v.data(), w, h, QImage::Format_RGB16);
QPixmap pixmap(QPixmap::convertFromQImage(
image.convertToFormat(QImage::Format::Format_Grayscale8)));
Otherwise, you have to reduce the depth of your image. Check this answer for a more detailed implementation. Nevertheless, this is a slow operation. If your Qt version allows it, use QImage::Format_Grayscale8
for destination image instead of the QImage::Format_RGB32
proposed in the linked answer, you'd write 3x less elements.
// 'image' is a QImage(w, h, QImage::Format_Grayscale)
// if possible, re-use it on each frame to reduce construction overhead
// 'ptr' is the pointer to your 16-bits data (const short* ptr)
auto ptr = v.constData();
#pragma omp parallel for // if can use OpenMP you can gain some speed here
for (int ii = 0; ii < image.height(); ++ii) {
auto scanLine = image.scanLine(ii);
for (int jj = 0; jj < image.width(); ++jj, ++ptr, ++scanLine) {
*scanLine = (unsigned char)(*ptr >> 8); // high byte, most significative bits
}
}
-
Thank you for the answer. I'm sorry I could not tried it earlier, because I wasn't at home. With this program that you write, I managed to display a video from my thermal camera. I did some scaling too, but the picture is not as good as I hoped it would be. – smilč Apr 10 '17 at 13:04
-
@smilč, may you please update your question and add the new code too there, it will be easier to read, as well and it will be complete ;), I'll take a look at it then. – cbuchart Apr 10 '17 at 13:09
-
BTW, if you are scaling using [`QPIxmap::scaled`](http://doc.qt.io/qt-5/qpixmap.html#scaled) or similar, be sure to specify the correct [transformation mode](http://doc.qt.io/qt-5/qt.html#TransformationMode-enum). The default one is for quick scaling, but `Qt::SmoothTransformation` produces much better results. – cbuchart Apr 10 '17 at 13:11
-
I guess that the main problem is that the pictures are being displayed as RGB. But there is no option of grayscale in my version of Qtcreator. – smilč Apr 10 '17 at 13:43
-
As you mention a thermal camera, then I assume you're using a mono-channel image, I've updated my answer to cover that (unfortunately Qt is not able to handle that kind of images - 16-bits grayscale, only 8-bits, so conversion is necessary). – cbuchart Apr 10 '17 at 13:54
-
Unfortunately I'm using QT 5.3.2 and grayscale option was intruduced in QT 5.5, so I assume it would be easier to install a newer version? – smilč Apr 10 '17 at 14:06
-
Probably, but take into consideration that it may not be the option you want, since RGB pixels are treated differently when converting to grayscale respect to simply discarding depth in a manual conversion. You can try saving one of the generated frames using [`QImage::save`](http://doc.qt.io/qt-5/qimage.html#save) and converting it to grayscale in GIMP or any other image processing tool. If the result satisfies you, then install the new version and try there. Otherwise, consider the linked answer for a manual conversion. – cbuchart Apr 10 '17 at 14:13
-
I've worked with that and actually it is quite slow, but maybe enough, depending on your framerate. If you do it in Qt previous to 5.5 then your image will have to be RGB, after 5.5 you can use an 8-bit grayscale for destiny of your manual conversion, which will be 3x faster (1 vs 3 channels). Updating answer to cover it. – cbuchart Apr 10 '17 at 14:15
-
See the `auto ptr = v.constData();` line, [`constData()`](http://doc.qt.io/qt-5/qvector.html#constData) returns a pointer to, well, the vector's data. As it is contiguous in memory (assuming, of course, no padding or additional offsets), the above code should do its job. – cbuchart Apr 18 '17 at 07:20
Here is the code:
void MainWindow::slotDataUpdate(QVector<uint16_t> vec)
{
ui->setupUi(this);
double min = *std::min_element(vec.begin(), vec.end());
double max = *std::max_element(vec.begin(), vec.end());
std::transform(vec.begin(), vec.end(), vec.begin(), std::bind2nd(std::plus<double>(), (-1)*min));
int co = 65536/(max - min);
std::transform(vec.begin(), vec.end(), vec.begin(), std::bind2nd(std::multiplies<double>(), co));
const QImage tmpImage((const uchar*)vec.data(), 160, 120, QImage::Format_RGB16);
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << vec; //conversion from Q vector to QByteArray
// QByteArray data = QByteArray::fromRawData(reinterpret_cast<const char*>(vec.constData()),sizeof(double)* vec.size()); //conversion from Q vector to QByteArray
for (int i = 0; i < data.size(); i++)
{
std::cout << data[i] << " " << std::endl;
}
QImage tmpImage(160,120, QImage::Format_RGB32); //prazna slika pravih dimenzij
QRgb *pixels=reinterpret_cast<QRgb*>(tmpImage.bits());
for (size_t i=0;2*i<data.size();++i)
{
uchar pixel_msb=data[2*i+];
pixels[i]=qRgb(pixel_msb, pixel_msb, pixel_msb);
}
QPixmap pixmap(QPixmap::fromImage(tmpImage));
ui->label_img->setPixmap(pixmap.scaled(480,360,Qt::KeepAspectRatio));
}

- 13
- 3
-
OK, take a look at my previous comment about the transformation mode in `scaled`. – cbuchart Apr 10 '17 at 13:13
-
Also, given the possible size of the vector, I suggest changing the method declaration to `void MainWindow::slotDataUpdate(const QVector
& vec)`. More about it [here](http://stackoverflow.com/a/2627179/1485885). – cbuchart Apr 10 '17 at 13:26 -
Hello, I tried that option you mentioned with reducing the depth of an image. But I don't get any results. When I tried to display the content of QByteArray data, I got weird symbols, not the values that are stored in my QVector vec. Could that be because of the conversion from QVector type? Here are the examples I tried: 1.) QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << vec; 2.) QByteArray data = QByteArray::fromRawData(reinterpret_cast
(vec.constData()),sizeof(double)* vec.size()); – smilč Apr 13 '17 at 13:35 -
I don't understand why you're using a `QByteArray`, you can do it directly with the `QVector` (see my answer, updated) – cbuchart Apr 18 '17 at 07:17