I have developed a small GUI in C++/Qt, I would like to know a fast way to test if image loaded is grayscale. In practise, when I load a grayscale image of gif format, I want it to be recognized as a grayscale image with depth()=8, and when I load a colored gif image, the depth of QImage would be 32.
Here's my open method :
void ImageViewer::open()
{
int status_off = 0;
fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath());
if (!fileName.isEmpty()) {
current_qimage = QImage(fileName);
if (current_qimage.isNull()) {
QMessageBox::information(this, tr("Image Viewer"),
tr("Cannot load %1.").arg(fileName));
return;
}
updateStatus(status_off);
// Image is colored with depth=8
if (current_qimage.depth()/8 != 4)
{rgblum_status = 0;}
else // Image is grayscale with depth=32
{rgblum_status = 1;}
loadImage();
}
}
From my first test, it seems that current_qimage, in current_qimage = QImage(fileName); inherits firstly from the format (gif here) before the contents of the image. Therefore, QImage has in two cases a depth() equal to 32.
How to make the difference between these two gif images (one grayscale and the other colored) ?
The QImage class has a function that you can call to test if the image is grayscale or not: QImage::isGrayscale(). It works for both 8-bit color table indexed images and 32-bit images.
Related
Is there a way to show a picture/image in a QToolTip?
I want to show small images of keyboard-buttons to explain the user which buttons/shortcuts he can use on that specific widget.
You can easily show images with the following html code:
QToolTip::showText(QCursor::pos(), "<img src=':/icon.png'>Message", this, QRect(), 5000);
This example will show a tooltip with the image from Qt resources and the text for 5 seconds.
For more details, you can see this video: https://youtu.be/X9JD8gKGZ00
Edit1:
If you want to show an image from memory, you can save the QImage/QPixmap to memory (preferably using some lossless compression like PNG) and convert it to base 64 and load it with the html code, like this:
QImage icon = QImage(10, 10, QImage::Format_ARGB32);
icon.fill(QColor(255, 0, 0, 100));
QByteArray data;
QBuffer buffer(&data);
icon.save(&buffer, "PNG", 100);
QString html = QString("<img src='data:image/png;base64, %0'>Message").arg(QString(data.toBase64()));
QToolTip::showText(QCursor::pos(), html, this, QRect(), 5000);
Edit2: Corrected the html string as #Damon Lynch suggests.
With respect to the HTML string, the Python 3 / PyQt equivalent of user2014561's excellent solution to displaying in memory images is like this, assuming a QPixmap (a QImage will work the same):
buffer = QBuffer()
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, "PNG", quality=100)
image = bytes(buffer.data().toBase64()).decode()
html = '<img src="data:image/png;base64,{}">'.format(image)
I have been trying to display a gray-scale image using Qt. The image data is loaded from a .txt file that contains 256x256 float data. There is no header involved for the image. I have tried the solution posted in this link
I used QLabel class to call setPixmap of my uchar* image_data_array. Even though I could get a Qt GUI window to open, but the window shows just blank screen.
imageLabel -> setPixmap(QPixmap::fromImage(*myImage));
QImage img = AImage;
if (!AImage.isNull())
{
int pixels = img.width() * img.height();
if (pixels*(int)sizeof(QRgb) <= img.byteCount())
{
QRgb *data = (QRgb *)img.bits();
for (int i = 0; i < pixels; i++)
{
int val = qGray(data[i]);
data[i] = qRgba(val, val, val, qAlpha(data[i]));
}
}
}
return img;
Use RGBA for good grayscale.
How are you loading the QImage with the grey image data ?
QT doesn't have a greyscale image type, only a bilevel type. You can either create an RGB image with R=G=B=grey. Or more preferably use QImage::Format_Indexed8 and create a colour table where each entry has the same value as the index. i.e.
QImage *qi = new QImage(data_ptr, width, height, QImage::Format_Indexed8);
QVector<QRgb> my_table;
for(int i = 0; i < 256; i++) my_table.push_back(qRgb(i,i,i));
qi->setColorTable(my_table);
In QT 5.9.1 there is an option to create a grayscale image:
QImage *qi = new QImage(data_ptr, width, height, QImage::Format_Grayscale8);
I need to do something similar to QPainter::drawImage, but drawing a triangle part of the given picture (into a triangular region of my widget) instead of working with rectangles.
Any idea how I could do that, besides painfully trying to redraw every pixel?
Thanks for your insights!
If it is feasible for you to use a QPixmap instead of a QImage, you can set a bitmap mask for the QPixmap which defines which of the pixels are shown and which are transparent:
myPixmap->setMask(myTriangleMask);
painter->drawPixmap(myPixmap);
Here is another solution based on QImage:
MaskWidget::MaskWidget(QWidget* parent) : QWidget(parent) {
img = QImage("Sample.jpg"); // The image to paint
mask = QImage("Mask.png"); // An indexed 2-bit colormap image
QPainter imgPainter(&img);
imgPainter.drawImage(0, 0, mask); // Paint the mask onto the image
}
void MaskWidget::paintEvent ( QPaintEvent * event ) {
QPainter painter(this);
painter.drawImage(10, 10, img);
}
Mask.png is an image file with the same size as Sample.jpg. It contains an alpha channel to support transparency. You can create this file easily with The GIMP, for example. I added an alpha channel, changed all areas I want to have painted to transparent and all other areas to white. To reduce the size, I finally converted it to an indexed 2-bit image.
You could even create the mask image programmatically with Qt, if you need your triangle be computed based on various parameters.
Maybe someone can help me with the following problem:
I want to draw the content of a QImage in a QGLWidget, but the widget is painted in black.
class QGLCanvas {
public:
QGLCanvas(QWidget* parent) : QGLWidget(parent) {
}
void setImage(const QImage* image) {
img = image;
}
void paintEvent(QPaintEvent*) {
// From Painter Documentation Qt
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), *img);
p.end();
}
public slots:
void rgb_data(const void *data) {
memcpy((void *)img->bits(), data, img->byteCount()); // data will be copied (sizes are known)
// img.save("text.png"); // saves right image
this->update(); // calls repaint, but does not draw the image.
}
private:
QImage *img;
}
The Bug:
When the slot is called, the memory is copied to the image. If the image is saved, the content is correct. But the repaint method just draws black content to the widget.
The Fix:
If the memcpy line is implemented outside the slot, the image content is drawn to the widget. This fix increased code complexity a lot. Thus, i've got the following question:
The Question:
Why does the memcpy not work within the slot? Is this a general problem with Qt?
There's nothing special about a slot which would stop your code from working.
What's probably the issue is that when you call update(), a repaint is scheduled but happens asynchronously. From the code you've provided the most likely cause is img being modified between the calls to rbg_data and paintEvent
You want to be sure about the format of the QImage. When you call bits and are expecting it to be RGB, you need to check the format.
if( img->format() != QImage::Format_RGB888 )
{
// convert the image format to RGB888
*img = img->convertToFormat(QImage::Format_RGB888);
}
This way, Qt will know the image format when trying to paint it. If you fill it with RGB data but the QImage is "formatted" as ARGB, you will get some painting errors.
i am trying to add a round shape button in my project and i want to set round shape background image on it but the problem is that while setting any background image it is always taking the rectangular image.
You must use bitmap images with transparent background as buttons.
CustomButton::CustomButton(QString file,QString pressedfile,QWidget *parent,int id)
: QPushButton(parent),FileName(file),PressedName(pressedfile),ID(id)
{
connect(this,SIGNAL(clicked()),SLOT(slotClicked()));
setStyleSheet("border: 2px");
QPixmap pixmap(file) ;
setMinimumHeight(pixmap.height());
setMinimumWidth(pixmap.width());
setMaximumHeight(pixmap.height());
setMaximumWidth(pixmap.width());
}
void CustomButton::slotClicked()
{
emit clicked(ID);
}
void CustomButton::setColor(const QColor &c)
{
fontColor=c;
}
//Paint event of button
void CustomButton::paintEvent(QPaintEvent *paint)
{
QPainter p(this);
p.save();
p.setPen(Qt::blue);
QPixmap pixmapdown;
if(PressedName>0)
pixmapdown.load(PressedName);
else
pixmapdown.load(FileName);
QPixmap pixmap;
pixmap.load(FileName);
if(isDown())
p.drawPixmap(1,1,pixmapdown);
else
p.drawPixmap(1,1,pixmap);
if(text().length()>0)
{
p.setPen(fontColor);
p.drawText(rect(), Qt::AlignCenter|Qt::AlignHCenter, text());
}
p.restore();
p.end();}
You could also use a QStyle derived class, although this might be over the top if you really only want these round buttons and not tons of widgets with custom styling.