Qt GUI: what is the fastest way to load a binary pixmap? - c++

I have a binary file called terrain.bil which has 54478160 items, and I would like to use information contained in this file to plot a 7988x6820 pixmap. And here is my code:
QFile op("terrain.bil");
op.open(QIODevice::ReadOnly);
QByteArray rawArr = op.readAll();
const qint16 *rawData = (const qint16*) rawArr.data();
op.close();
QPixmap pixmap = QPixmap(7988,6820);
pixmap.fill(Qt::white);
QPainter pixPaint(&pixmap);
QPoint q1;
for(int i=0;i<6820;++i){
for(int j=0;j<7988;++j){
q1.setX(j);//QPixmap(#Col,#Row)
q1.setY(i);
if (rawData[i*7988+j] != -9999){
pixPaint.setPen(QColor(floor(double(rawData[i*7988+j])*0.59),floor(double(rawData[i*7988+j])*0.59),floor(double(rawData[i*7988+j])*0.59),255));
}
else{
pixPaint.setPen(Qt::green);
}
pixPaint.drawPoint(q1);
}
}
scene->addPixmap(pixmap);
I tried to plot the pixmap point by point and it takes 1 minute to finish the double for loop. Is there any way that I can plot faster with this binary file?
Thanks very much!!!

I suggest writing a tool that will draw your pixmaps and serialize them as a binary file.
You can load the file directly into a pixmap and call a function to display the pixmap.
Also research "bit blitting".

You can use QImage to get direct access to the pixel data without using QPainter object.
That way you use QImage::setPixel with qRgb macro. Look here to get more info on this. Then you can convert QImage to QPixmap and add it to your scene.

Related

How to overlay 2 Images in QT

I have 2 images that I load in QImage and my question is :
How can I overlay these 2 images in the simplest way, to then save it in a QPixmap ?
Here are the following images :
And the awaited result :
(The final image will be used in a QTableView to show the user if it has more of the same potion)
I was able to find the answer on my own after some more research.
Here is my code that I came up with (if you can do it better I would like to see it because I would like to implement any better approach if there is any):
QPixmap base = QPixmap::fromImage(QImage("Your 1st img"));
QPixmap overlay = QPixmap::fromImage(QImage("your 2nd img"));
QPixmap result(base.width(), base.height());
result.fill(Qt::transparent); // force alpha channel
{
QPainter painter(&result);
painter.drawPixmap(0, 0, base);
painter.drawPixmap(0, 0, overlay);
}
QStandardItem *pCombinedItem = new QStandardItem(); //this variable should be in the .h file if you want to conserve it further on.
pCombinedItem->setData(QVariant(result), Qt::DecorationRole);//adding the final img into the StandardItem which we can put then into our table after we put it into a StandardItemModel like so :
model->setItem(1,4,pCombinedItem);
pInventory->setModel(model); //and we can put our model into our tableview

How to correctly subscribe the pixel value over the pixmap in the QGraphicsScene (as it is in OpenCV namedWindow)?

I am trying to implement the same functionality in my widget as it is in cv:: namedWindow.
The goal is to enable zooming and to make the overlay with the grid and the values of pixel's colors directly over the original pixmap. Here is the example: сv picture zoomed:
I inherited the QGraphicsView widget, added to QGraphicsScene the QGraphicsPixmapItem and reimplemented the QWheelEvent so that zooming in and out works correctly now. The problem starts with creating an overlay.
Instead of creating a pack of QGraphicsLineItems and adding them to the scene in order to make the grid, I inherit the QGraphicsRectItem and draw the whole grid on it.
void QGraphicsOverlayItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen pen(Qt::black);
pen.setWidthF(0.02);
painter->setPen(pen);
painter->setBrush(Qt::BrushStyle::NoBrush);
QVector<QLineF> crossLines = createCrossLines();
painter->drawLines(crossLines);
}
This works very fast. But when I try to drawText with the QPainter and set the QFont:: pointSizeF() as small as possible, it works incorrectly (symbols change their size from small to huge during zooming or even disappear at all). Nevertheless, the best result that I get this way is the following:
the QPainter's drawText() result:
QFont font(painter->font());
font.setPointSizeF(0.1);
font.setLetterSpacing(QFont::SpacingType::AbsoluteSpacing,0.01);
painter->setFont(font);
painter->drawText(432,195,"123");
The easiest way is to add to scene a lot of QGraphicsTextItems and scale them to correct size, but it is too slow.
So the question is how can I subscribe the pixel's color value in the QGraphicsScene directly over the QPixmapItem?
I finally watched through the openCV source code and found what I looked for.
The answer for me was the QTransform matrix. OpenCV developers show the image not by using the scene in the QGraphicsView, but actually painting the image directly on the viewport in the paintEvent.
The QTransform matrix is stored in the class and is passed to QPainter in the beginning of the paintEvent.
void DefaultViewPort::paintEvent(QPaintEvent *event)
{
QPainter painter(viewport());
painter.setWorldTransform(param_matrixWorld);
painter.drawImage(QRect(0,0,viewport()->width(),viewport()->height()),image2Draw,QRect(0,0,image2Draw.width(),image2Draw.height()));
If you know the ratio of the image's size to the widget's size, and you also know the scale of QTransform matrix used to paint the image, it is easy to calculate how much area does the single source pixel take on the screen:
qreal ratioX = width() / float(image2Draw.width());
qreal ratioY = height() / float(image2Draw.height());
double pixel_width = qtransform_matrixWorld.m11()*ratioX;
double pixel_height = qtransform_matrixWorld.m11()*ratioY;
If we know the pixel_height, we can just set the QFont::pixelSize like this:
QFont font = painter->font();
font.setPixelSize(pixel_height/5);
painter->setFont(font);

How do I rotate photos in QPixmap?

Marked a needle on the speedometer.
// 이미지 출력
QPixmap pix("C:\\Users\\WORK002\\Desktop\\speedmeter.png");
QPixmap pix2("C:\\Users\\WORK002\\Desktop\\pointer.png");
QPixmap pointer = pix2.scaled(300, 300);
scene.addPixmap(pix);
scene.addPixmap(pointer);
ui-> graphicsView ->setScene(&scene);
ui-> graphicsView ->show();
I want rotate and reposition.
How can i do this?
You don't have to mess with the QPixmap, you can manipulate the QGraphicsPixmapItem returned by QGraphicsScene::addPixmap:
QGraphicsPixmapItem *pixmap_item = scene.addPixmap(pix);
To set the position, you can use QGraphicsItem::setPos.
To set the rotation, first set the transform origin point with QGraphicsItem::setTransformOriginPoint,(this will set the point around which your item will be rotated) and then set the rotation with QGraphicsItem::setRotation:
pixmap_item->setPos(50, 0);
pixmap_item->setTransformOriginPoint(pixmap_item->boundingRect().center());
pixmap_item->setRotation(90);
You will have to set the correct values yourself, but this should lead you in the right way.
You can look into QPixmap::transformed()
QPixmap QPixmap::transformed(const QTransform &transform, Qt::TransformationMode mode = Qt::FastTransformation) const
The specification can be given through the QTransform object:
rotate() for rotation
translate() for reposition
may this one can help:
//Please note this method takes 2 mseg to finish for a 20x20 pixmap.
QPixmap rotatedPixmap(m_pixOriginal.size());
rotatedPixmap.fill(QColor::fromRgb(0, 0, 0, 0)); //the new pixmap must be transparent.
QPainter* p = new QPainter(&rotatedPixmap);
QSize size = m_pxOriginal.size();
p->translate(size.height()/2,size.height()/2);
p->rotate(m_iAngle);
p->translate(-size.height()/2,-size.height()/2);
p->drawPixmap(0, 0, m_pxOriginal);
p->end();
delete p;
m_pxItem->setPixmap(rotatedPixmap);
copied from:
Read this forum thread

How do I grab a QPixmap of a QtDataVizualisation in a QWidget and save to file?

I am trying to make a 3D scatter plot using QtDataVisualizationQ3DScatter and then to take a QPixmap of that and save to file.
Here is my code.
//set up the 3D scatter chart
QtDataVisualization::Q3DScatter *graph = new QtDataVisualization::Q3DScatter();
QWidget *widget = QWidget::createWindowContainer(graph);
QtDataVisualization::QScatter3DSeries *series = new QtDataVisualization::QScatter3DSeries;
//set up the data variable
QtDataVisualization::QScatterDataArray data;
//add data to the data variable
for (int i; i < nodes.size(); i = i + 1)
{
data << QVector3D(x[i], y[i], z[i]);
}
//add the data to series
series->dataProxy()->addItems(data);
//add series to graph
graph->addSeries(series);
//set the graph properties
graph->activeTheme()->setType(QtDataVisualization::Q3DTheme::ThemeArmyBlue);
graph->scene()->activeCamera()->setCameraPreset(QtDataVisualization::Q3DCamera::CameraPresetIsometricRight);
graph->seriesList().at(0)->setMesh(QtDataVisualization::QAbstract3DSeries::MeshPoint);
graph->setShadowQuality(QtDataVisualization::QAbstract3DGraph::ShadowQuality(0));
//widget->show();
const QRect rectangle(0,0,widget->width(),widget->height());
QPixmap test = widget->grab(rectangle);
test.save(QString(workingDirectory) + "/test.png", "PNG");
When I uncomment the "widget->show()", the test.png is just a white square but the widget comes up and shows the full 3DScatter. When I comment out the "widget->show()", test.png is still just a white square. I can't seem to get this to work.
When commenting widget->show(), add a widget->repaint() call to force the widget to be painted/rendered before saving it in the file.
I realise this question is quite old but for anyone who's interested:
QAbstract3DGraph has a function renderToImage. This is inherited by Q3DScatter.
You should be able to grab a qimage of your scene using:
QImage myImage = graph->renderToImage();
and then convert this to a pixmap:
QPixmap p;
p.convertFromImage(myImage);

Display image in Qt that is coming from an unsigned char**

I am trying to display an image in Qt that is coming in as data from another function.
That function stores the rows of an image as an unsigned char**
I read somewhere I could somehow store it as a QByteArray and then create a QPixMap then set the QPixMap of a label to display it, but I am not having much luck.
This is what I had:
unsigned char* fullCharArray = new unsigned char[imheight * imwidth];
for (int i = 0 ; i < imheight ; i++)
for (int j = 0 ; j < imwidth ; j++)
fullCharArray[i*j+j] = imageData[i ][j];
QPixmap *p = new QPixmap(reinterpret_cast<const char *>(fullCharArray));
ui->viewLabel->setPixMap(p);
But this seems to give me an error, and may be the wrong thing anyway. When i tried to call setPixMap(p[]) the error goes away, but the image does not get displayed in the label.
imageData is the 2D array that is populated by my other function. Best I figured in order to create a new QPixMap I had to convert that to a 1D array, and do the index calculations manually there. That is what the double For loop is doing.
So is there a better way of displaying this image data in Qt?
Use QImage. This constructor should help you: http://doc.qt.io/qt-5/qimage.html#QImage-4
Pixmaps are not 2D arrays of pixels. They are actually defined as an array of character strings describing the image.
For example, a 4x4 pixels image with center 4 pixels black, and 4 corner pixels red is:
/* XPM */
static char * sample_xpm[] = {
"4 4 3 1",
" c #FF0000",
". c #FFFFFF",
"+ c #000000",
" .. ",
".++.",
".++.",
" .. "};
It is a convenient file format to store small images (e.g. for toolbar buttons) that can be integrated as is into a C source file using a standard #include directive, to be compiled along with the rest of the program.
You use QPixmap if the data is already in an image format like PNG or jpeg with a header.
To create an image from pixel values use Qimage, then convert this to a pixmap (Qpixmap.fromImage() - if needed to save it or display it.