How to convert QPixmap into a Matrix - qt

I have been looking for such answers.
I found this method in QPixmap transformed() though I think it does not help me in my pursuit of searching a method to convert QPixmap Images (grayscale) into a Matrix...
Thanks for the help =)

The QMatrix in the QPixmap::transformed() function is specifically for warping images.
I think what you want to do is read the values from a QPixmap into some matrix.
You don't specify what grayscale means but I presume that qGray(QRgb) is sufficient if the image is not already grayscale.
I think basically something like this is what you need:
QImage myimage = mypixmap.toImage(); // convert your QPixmap to QImage
int width = myimage.width();
int height = myimage.height();
int *matrix = new int [width*height]; // store 2-D data in 1-D vector
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
matrix[j*width+i] = qGray(myimage.pixel(i,j));
}
}
// ... do stuff ...
delete [] matrix;
You can easily change the matrix variable into some other layout in memory if you like.

Related

Combine multiple mono QImage into a color QImage

For a list of colors, I have a corresponding list of QImage, format mono. The mono images have been processed in such a way that a single pixel can be black from all images.
I would like to merge them into a color image.
I had 3 ideas.
1. Use image composition modes. I was unable to get it to work. (Editing to remove it, to clean up post...)
2. Use the mono images as masks for each of the colors when added to the destination.
I have no idea how to implement it.
3. Iterating through pixels - slow, the documentation says that pixel manipulation functions are slow...
This works:
// Creating destination image
// m_colors: list of (n+1) QCcolor (last one corresponding to background)
// m_images: list of n QImage, Format_Mono, all of the same size (as the destination)
// using indexed image for destination since I have a limited palette
QImage preview = QImage(rect.size().toSize(), QImage::Format_Indexed8);
int previewWidth = preview.size().width();
int previewHeight = preview.size().height();
int colorsSize = m_colors.size();
for(int k = 0; k < colorsSize; ++k)
{
preview.setColor(k, m_colors.at(k).rgb());
}
--colorsSize;
// combining images
for(int j = 0; j < previewHeight; ++j)
{
for(int i = 0; i < previewWidth; ++i)
{
// set background color
preview.setPixel(i, j, colorsSize);
for(int k = 0; k < colorsSize; ++k)
{
QImage im = m_images.at(k);
if(!im.isNull())
{
if(m_images.at(k).pixelIndex(i, j) == 0)
{
preview.setPixel(i, j, k);
}
}
}
}
}
I should at least improve this using scanLine(), but don't know how... I can only find examples that use scanLine() with 32 bit images, not 8 or 2.
Is it actually possible to use scanLine() with 8 or 2 bit images ?
I don't understand the documentation - does it mean that only 32 bit images can be read/written using scanLine() or that regardless of image type, the function will work the same way, and I only use one of the 4 bytes ?
Would be more effective to use 32 bit images instead of 8 or 2 bit ?
If I use 32 bit image for destination, and try to use scanLine() to write data, still how can I improve my reading of the mono images ?
Please help me improve my algorithm, either to improve the version that I get to work iterating through all pixels of images, or perhaps using some tools like combining images using composition.
Is it actually possible to use scanLine() with 8 or 2 bit images ?
Yes it is.
Would be more effective to use 32 bit images instead of 8 or 2 bit ?
You will have to measure, and it will depend on the specific code. I used 8 bit code here for simplicity and because your code did.
If I use 32 bit image for destination, and try to use scanLine() to write data, still how can I improve my reading of the mono images ?
It is probably not a good idea to copy the image in the inner loop
QImage im = m_images.at(k)
and to then not use that copy for the next access.
if(m_images.at(k).pixelIndex(i, j) == 0)
It should speed up your painting if your inner loop iterates over an image instead of iterating over the destination pixels in the inner loop.
If the image is monochrome, then the scan line will point to packed color information which will need to be unpacked. It is easier (and maybe faster) to let convertToFormat convert the image and then use scanLine to read the unpacked information. In the example below the images are all 8 bit.
#include<vector>
#include <QtGui/QImage>
#include <QtGui/QColor>
static char * img1[] = {
"5 5 2 1", "a c #000000", "b c #ffffff",
"aabba", "aabba", "aabba", "aabba", "aabba"
};
static char * img2[] = {
"5 5 2 1","a c #000000","b c #ffffff",
"aaaaa", "aaaaa", "bbbbb", "bbbbb", "aaaaa"
};
int main( int argc, char* arg[] )
{
auto images = std::vector<QImage>( 2 );
images[0] = QImage( img1 );
images[1] = QImage( img2 );
auto colors = std::vector<QColor>( 2 );
colors[0] = QColor( Qt::red );
colors[1] = QColor( Qt::green );
QImage combined = QImage( images[0].size(), QImage::Format_Indexed8 );
combined.setColor( 0, Qt::black );
combined.fill(0);
for( int k = 1, num = images.size(); k <= num; ++k )
{
combined.setColor( k, colors[k-1].rgb() );
QImage img= images[k-1];
for( int i = 0, height = img.height(); i < height ; ++i )
{
uchar* src = img.scanLine(i);
uchar* dst = combined.scanLine(i);
for( int j = 0, width = mono.width(); j < width; ++j )
{
if( src[j] != 0 )
dst[j] = k;
}
}
}

using glDrawPixels to render bitmap raw data

I receive raw image data from server. The server uses MS Dib() function which returns in BGR format. Now, what i want to do is to read this raw data and use glDrawPixels to draw it in Linux.
I was advised that GetClrTabAddress function in MS and alike shall be used to get me the RGB values for each index of 800 by 600 image sent to me.
I do not know how to get these values using indices. Could anyone give some tips.
void func(QByteArray)
{
window_width = 800;
window_height = 600;
size = window_width * window_height;
pixels = new float[size*3];
memcpy(pixels, bytes, bytes.size());
}
void GlWidget::paintGL()
{
//! [5]
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawPixels(window_width,window_height,GL_RGB,GL_FLOAT,pixels);
}
You can use GL_BGR in glDrawPixels, which will do the conversion for you and will probably be faster since AFAIK the GPU will do the work.
QByteArray sounds like you should be using unsigned bytes/chars instead of floats, which means GL_UNSIGNED_BYTE.
I'd assert(size*3*sizeof(float) == bytes.size());.
In this case make sure to set glPixelStorei(GL_UNPACK_ALIGNMENT, 1) if your width doesn't align to the default 4-byte boundry. With GL_BGR very pixel is 3 bytes and by default each row of your pixels is assumed to be padded to the next 4-byte boundary.
[EDIT]
OK, it looks like the image uses a palette. This means every value inthe QByteArray maps to an rgb value in another array. I'm not 100% sure where the palette is and maybe it can be computed implicitly, but you mentioned GetClrTabAddress which sounds promising.
The code will then look something like this
for(int i = 0; i < size; ++i)
{
unsigned char index = btmp[i];
//and something like..
memcpy(bytes + i * 3, GetClrTabAddress() + index * 3, 3);
//or
bytes[i*3+0] = someOtherPaletteData[index].red;
bytes[i*3+1] = someOtherPaletteData[index].green;
bytes[i*3+2] = someOtherPaletteData[index].blue;
}

How to set QImage pixel colour for an RGB888 image in Qt

I have an RGB888 format qImage defined as follows:
myQrgb = QImage(img_in, width, height, QImage::Format_RGB888);
I wish to alter specific pixel values, so I followed the example here, like so:
QRgb value = qRgb(0, 0, 0);
myQrgb.setPixel(i, j, value);
This, however, always produces a segmentation fault regardless of the values of i and j (e.g. i = j = 2).
I am guessing it is because I am incorrectly using QRgb to manipulate pixels in a QImage::Format_RGB888. What should I do instead?
I think the problem may be more related to the img_in data with which you are initializing the image. Are you sure that data is valid?
The following example successfully paints a white square with a black square in the corner.
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QImage img(100, 100, QImage::Format_RGB888);
img.fill(QColor(Qt::white).rgb());
for (int x = 0; x < 10; ++x) {
for (int y = 0; y < 10; ++y) {
img.setPixel(x, y, qRgb(0, 0, 0));
}
}
QLabel l;
l.setPixmap(QPixmap::fromImage(img));
l.show();
return app.exec();
}
There are few things you need to confirm:
According to QImage constructor you're using, make sure img_in remains valid throughout the life span of QImage object. By the way, QImage destructor will not delete your data (img_in).
If the pixel position you're setting is not valid coordinate, setPixel()'s behavior is undefined.
I suspect the first case, img_in is probably vanishing from QImage. You may want to try to create a QImage using other constructor like QImage(10, 10, QImage::Format_RGB888) and play with setPixel().

Does Qt have a way to find bounding box of an image?

Given a .png image with a transparent background, I want to find the bounding box of the non-transparent data. Using nested for loops with QImage.pixel() is painfully slow. Is there a built-in method of doing this in Qt?
There is one option that involves using a QGraphicsPixmapItem and querying for the bounding box of the opaque area (QGraphicsPixmapItem::opaqueArea().boundingRect()). Not sure if it is the best way but it works :) It might be worth digging into Qt's source code to see what code is at the heart of it.
The following code will print out the width and height of the image followed by the width and height of the opaque portions of the image:
QPixmap p("image.png");
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(p);
std::cout << item->boundingRect().width() << "," << item->boundingRect().height() << std::endl;
std::cout << item->opaqueArea().boundingRect().width() << "," << item->opaqueArea().boundingRect().height() << std::endl;
If pixel() is too slow for you, consider more efficient row-wise data adressing, given a QImage p:
int l =p.width(), r = 0, t = p.height(), b = 0;
for (int y = 0; y < p.height(); ++y) {
QRgb *row = (QRgb*)p.scanLine(y);
bool rowFilled = false;
for (int x = 0; x < p.width(); ++x) {
if (qAlpha(row[x])) {
rowFilled = true;
r = std::max(r, x);
if (l > x) {
l = x;
x = r; // shortcut to only search for new right bound from here
}
}
}
if (rowFilled) {
t = std::min(t, y);
b = y;
}
}
I doubt it will get any faster than this.
The easiest and also relatively fast solution is to do as follows:
QRegion(QBitmap::fromImage(image.createMaskFromColor(0x00000000))).boundingRect()
If you have a QPixmap rather than QImage, then you can use:
QRegion(pixmap.createMaskFromColor(Qt::transparent)).boundingRect()
QPixmap::createMaskFromColor internally will convert the pixmap to an image and do the same as above. An even shorter solution for QPixmap is:
QRegion(pixmap.mask()).boundingRect()
In this case, a QPixmap without alpha channel will result in an empty region, so you may need to check for that explicitly. Incidentally, this is also what QGraphicsPixmapItem::opaqueArea mentioned by #Arnold Spence is based on.
You may also want to try QImage::createAlphaMask, though the cutoff point will not be at 0 alpha but rather somewhere at half opacity.

how to display image from array of colors data in Qt?

I have a char* data, where every char represents red/green/blue/alpha value of a pixel.
So, the first four numbers are red, green, blue and alpha value of the first pixel, the next four are R, G, B, A value of the pixel on the right and so on.
It represents a picture (with previously known width and height).
Now, I want to somehow take this array and display it on Qt window. How to do it?
I know I should somehow use QPixmap and/or QImage, but I cannot find anything helpful in the documentation.
QImage is designed for access to the various pixels (among other things), so you could do something like this:
QImage DataToQImage( int width, int height, int length, char *data )
{
QImage image( width, height, QImage::Format_ARGB32 );
assert( length % 4 == 0 );
for ( int i = 0; i < length / 4; ++i )
{
int index = i * 4;
QRgb argb = qRgba( data[index + 1], //red
data[index + 2], //green
data[index + 3], //blue
data[index] ); //alpha
image.setPixel( i, argb );
}
return image;
}
Based on coming across another constructor, you might also be able to do this:
QImage DataToQImage( int width, int height, int length, const uchar *data )
{
int bytes_per_line = width * 4;
QImage image( data, width, height, bytes_per_line,
QImage::Format_ARGB32 );
// data is required to be valid throughout the lifetime of the image so
// constructed, and QImages use shared data to make copying quick. I
// don't know how those two features interact, so here I chose to force a
// copy of the image. It could be that the shared data would make a copy
// also, but due to the shared data, we don't really lose anything by
// forcing it.
return image.copy();
}

Resources