qpixmap.scaled function not working - c++

I'm new in qt and base on my research i can change the size of a pixmap/image using the scaled function. but when i run the program the image size is still the same. here is the code.
QStringList headers;
QString headerValues="header1,header2";
headers=headerValues.split(",");
ui->tableWidget_2->setColumnCount(2);
ui->tableWidget_2->setHorizontalHeaderLabels(headers);
QStringList verticalHeaders;
QString verticalHeaderValues="vertical1,vertical2";
verticalHeaders=verticalHeaderValues.split(",");
ui->tableWidget_2->setRowCount(1);
ui->tableWidget_2->setVerticalHeaderLabels(verticalHeaders );
ui->tableWidget_2->horizontalHeader()->setDefaultSectionSize(150);
ui->tableWidget_2->verticalHeader()->setDefaultSectionSize(150);
ui->tableWidget_2->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
ui->tableWidget_2->verticalHeader()->setResizeMode(QHeaderView::Fixed);
QString filename="/home/marcvincento/Desktop/Private/Projects/fixButtons/Printer.png";
QPixmap pic(filename);
pic.scaled ( 20, 20, Qt::IgnoreAspectRatio, Qt::FastTransformation );
QBrush brush(pic);
QTableWidgetItem* item=new QTableWidgetItem();
item->setBackground(brush);
item->setText("ADD 5,1");
ui->tableWidget_2->setItem(0,0,item);

If you read the Qt documentation of QPixmap, you will see that the scaled() functions are declared const and return a QPixmap. That means the pixmap itself is not modified. What happens is that these functions return a scaled version of the pixmap. You will have to store that somewhere. In your case, you can just pass the scaled pixmap to the QBrush constructor:
QPixmap pic(filename);
QBrush brush(pic.scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));
If you would need to use the same scaled pixmap again later on and have no use for the original, unscaled pixmap, then you should construct the pic pixmap directly from a scaled version of the source, so that you won't end up performing the same scaling operation multiple times:
QPixmap pic(QPixmap(filename).scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));
If you do need both the scaled and unscaled versions, then obviously you'd need two pixmaps; one of the original and one for the scaled version:
QPixmap pic(filename);
QPixmap picScaled(pic.scaled(20, 20, Qt::IgnoreAspectRatio,
Qt::FastTransformation));

Related

How to fill a Rectangle (a Rect object) with an image in Qt?

I've tried this:
QBrush brush(QPixmap(":/new/prefix1/car.jpg"));
painter.setBrush(brush);
QRectF car(positions[i],120, 20, 10 );
painter.drawRect(car);
It shows the image but it repeats itself during a simulation in QPaint.
I want a way to fill a rectangle with an image but i'm not finding any specific methods for Rect. Any tricks for that?
Use QPainters drawPixmap. There is an overloaded function, that takes both a QPixmap and a QRect into which the pixmap will be painted:
http://doc.qt.io/qt-5/qpainter.html#drawPixmap-9

QT, C++: fast way to draw live image stream from camera on QGraphicsview

I'm writing a QT GUI application in wich a live stream of a connected camera is shown on a QGraphicsview. Therefore an openCV image is first converted to a QImage and than to a QPixmap. This is added to the QGraphicsScene of the QGraphicsView.
The bandwidth is not a problem, the cameras are connected via ethernet or USB.
I am testing the performance with the Analyze Toole build in Visual Studio 2012 and it shows that the conversion to the QPixmap is very slow and takes 60% of the computation time (of displaying the image), so that I end up with 1 FPS or so. The images are 2560 by 1920 or even bigger. Scaling the cv::Ptr stream_image befor converting it to a QImage improves the performance significantly but I need all the image detail in the image.
EDIT
Here is some code how I do the conversion:
cv::Ptr<IplImage> color_image;
// stream_image is a cv::Ptr<IplImage> and holds the current image from the camera
if (stream_image->nChannels != 3) {
color_image = cvCreateImage(cvGetSize(stream_image), IPL_DEPTH_8U, 3);
cv::Mat gr(stream_image);
cv::Mat col(color_image);
cv::cvtColor(gr, col, CV_GRAY2BGR);
}
else {
color_image = stream_image;
}
QImage *tmp = new QImage(color_image->width, color_image->height, QImage::Format_RGB888);
memcpy(tmp->bits(), color_image->imageData, color_image->width * color_image->height * 3);
// update Scene
m_pixmap = QPixmap::fromImage(*tmp); // this line takes the most time!!!
m_scene->clear();
QGraphicsPixmapItem *item = m_scene->addPixmap(m_pixmap);
m_scene->setSceneRect(0,0, m_pixmap.width(), m_pixmap.height());
delete tmp;
m_ui->graphicsView->fitInView(m_scene.sceneRect(),Qt::KeepAspectRatio);
m_ui->graphicsView->update();
EDIT 2
I tested the method from from Thomas answer, but it is as slow as my method.
QPixmap m_pixmap = QPixmap::fromImage(QImage(reinterpret_cast<uchar const*>(color_image->imageData),
color_image->width,
color_image->height,
QImage::Format_RGB888));
EDIT 3
I tried to incorporate Thomas second suggestion:
color_image = cvCreateImage(cvGetSize(resized_image), IPL_DEPTH_32F, 3);
//[...]
QPixmap m_pixmap = QPixmap::fromImage(QImage(
reinterpret_cast<uchar const*>( color_image->imageData),
color_image->width,
color_image->height,
QImage::Format_RGB32));
But that crashes when the drawEvent of the Widget is called.
Q: Is there a way to display the image stream in a QGraphicsView without converting it to a QPixmap first or any other fast/performant way? The QGraphicsView is importent since I want to add overlays to the image.
I have figured out a solution that works for me but also tested a little with different methods and how they perform:
Method one is performant even in debug mode and takes only 23.7 % of the execution time of the drawing procedure (using ANALYZE in VS2012):
color_image = cvCreateImage(cvGetSize(stream_image), IPL_DEPTH_8U, 4);
cv::Mat gr(stream_image);
cv::Mat col(color_image);
cv::cvtColor(gr, col, CV_GRAY2RGBA,4);
QPixmap m_pixmap = QPixmap::fromImage(QImage(reinterpret_cast<uchar const*>( color_image->imageData),
color_image->width,
color_image->height,
QImage::Format_ARGB32));
Method two is still performant in debug mode taking 42,1% of the execution time. when the following enum is used in QPixmap::fromeImage instead
QImage::Format_RGBA8888
Method three is the one I showed in my question and it is very slow in debug builds being responsible for 68,3% of the drawing workload.
However, when I compile in release all three methods are seamingly equally performant.
This is what I usually do. Use one of the constructors for QImage that uses an existing buffer and then use QPixmap::fromImage for the rest. The format of the buffer should be compatible with the display, such as QImage::Format_RGB32. In this example a vector serves as the storage for the image.
std::vector<QRgb> image( 2560 * 1920 );
QPixmap pixmap = QPixmap::fromImage( QImage(
reinterpret_cast<uchar const*>( image.data() ),
2560,
1920,
QImage::Format_RGB32 ) );
Note the alignment constraint. If the alignemnt is not 32-bit aligned, you can use one of the constructors that takes a bytesPerLine argument.
Edit:
If your image is 32bit, then you can write.
QPixmap pixmap = QPixmap::fromImage( QImage(
reinterpret_cast<uchar const*>( color_image->imageData ),
color_image->width,
color_image->height,
QImage::Format_RGB32 ) );

QPainter composition not working as expected with background

I'm trying to draw two rectangles with same color and transparency on a QFrame with a white background. These rectangles should overlap and the their transparency should not change (also in the overlapping region). So like this:
Here is the code I have so far:
class Canvas : public QFrame
{
public:
void paintEvent(QPaintEvent * event) override;
};
void Canvas::paintEvent(QPaintEvent *event)
{
QPainter painter( this );
painter.setPen(QPen(Qt::NoPen));
painter.setBrush(QBrush(QColor(0,0,255,125)));
painter.drawRect(QRect(10,10,100,100));
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setBrush(QBrush(QColor(0, 0, 255, 125)));
painter.drawRect(QRect(80, 80, 100, 100));
}
int main( int argc, char **argv )
{
QApplication a( argc, argv );
Canvas canvas;
canvas.setAutoFillBackground(true);
QPalette pal;
pal.setColor(QPalette::Window, QColor(Qt::red));
canvas.setBackgroundRole(QPalette::Window);
canvas.setPalette(pal);
canvas.show();
return a.exec();
}
However this produces the following image:
I have tried every possible composition mode for the painter, but none seem to give me the desired effect. I guess CompositionMode_Source is the correct one since if I use the following code:
QPixmap pixmap(200, 200);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setPen(QPen(Qt::NoPen));
painter.setBrush(QBrush(QColor(0, 0, 255, 125)));
painter.drawRect(QRect(10, 10, 100, 100));
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setBrush(QBrush(QColor(0, 0, 255, 125)));
painter.drawRect(QRect(80, 80, 100, 100));
QLabel label;
label.setPixmap(pixmap);
label.show();
I do get the desired effect (but without the red background):
However if I change the fill to Qt::red I get again:
What am I missing here? How can I get my desired effect? The actual application for this is that I want to draw rectangles on a QFrame derived class which is implemented in a third party lib over which I have limited control.
I spot three problems with the code:
the first rectangle is drawn with alpha blending (Source Over mode) because you're setting the composition mode after the first draw call. The second one instead uses Source mode (i.e. copy the source pixels as-is, do not perform alpha blending).
Indeed Source does not perform alpha blending, which you seem to want. So don't use that! The default composition mode does what you want.
Drawing two different shapes will perform composition between them. That's obviously expected, since you're doing two draw calls; the second draw call will find the destination already changed by the first. If you don't want that, you must find a way to draw both shapes in one draw call (for instance: add both of them to one QPainterPath, then draw the path in one draw call), or perform composition at a later stage (for instance: draw them onto an opaque QImage, then blend the image over the destination in one draw call).

Drawing line to QImage

I am trying to draw line to QImage and show it in Qlabel. However I have some issues that I cannot solve.
QPixmap px;
px.fromImage (imgRaw); // define in header file QImage imgRaw;
QPainter p (&px);
p.setPen (Qt::red);
p.drawLine (mouseStart_X, mouseStart_Y, mouseReleased_X, mouseReleased_Y);
p.end ();
ui->lblRightImg->setPixmap (px);
ui->lblRightImg->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
ui->lblRightImg->setScaledContents(true);
When I used this code above it gives such error :
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setPen: Painter not active
QPainter::end: Painter not active, aborted
Then I change my code because it tries to draw in null pixmap so after changing code like this:
QPixmap px(100, 100);
px.fromImage (imgRaw); // define in header file QImage imgRaw;
Then it gives noisy image(black and gray broken image)
Could you please help me to solve this issue ?
EDIT :
Also tried:
QPixmap px = QPixmap::fromImage (imgRaw);
Then it gives same image without any drawing..
fromImage is a static function of QPixmap and does not affect your 'object', it returns the pixmap you want. Try using following code to initialize your pixmap:
QPixmap px = QPixmap::fromImage(imgRaw);

How to create image file from QGraphicsScene/QGraphicsView?

Given a QGraphicsScene, or QGraphicsView, is it possible to create an image file (preferably PNG or JPG)? If yes, how?
After just dealing with this problem, there's enough improvement here to warrant a new answer:
scene->clearSelection(); // Selections would also render to the file
scene->setSceneRect(scene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
QImage image(scene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
image.fill(Qt::transparent); // Start all pixels transparent
QPainter painter(&image);
scene->render(&painter);
image.save("file_name.png");
I have not tried this, but this is the idea of how to do it.
You can do this in several ways
One form is as follows:
QGraphicsView* view = new QGraphicsView(scene,this);
QString fileName = "file_name.png";
QPixmap pixMap = view->grab(view->sceneRect().toRect());
pixMap.save(fileName);
//Uses QWidget::grab function to create a pixmap and paints the QGraphicsView inside it.
The other is to use the render function QGraphicsScene::render():
QImage image(fn);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
image.save("file_name.png")
grabWidget is deprecated, use grab. And you can use a QFileDialog
QString fileName= QFileDialog::getSaveFileName(this, "Save image", QCoreApplication::applicationDirPath(), "BMP Files (*.bmp);;JPEG (*.JPEG);;PNG (*.png)" );
if (!fileName.isNull())
{
QPixmap pixMap = this->ui->graphicsView->grab();
pixMap.save(fileName);
}