tags:

views:

54

answers:

2

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?

+2  A: 

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;
Arnold Spence
opaqueArea() constructs a whole Path describing the area. That should be slower than simple bounding rectangle computation.
ypnos
Hopefully the OP can post some timing results. I'd be interested to see how much time both options take. But yes, for complicated images I can only imagine it being slower.
Arnold Spence
I did a few rough timings of my over-all task without creating a micro-benchmark. This approach used essentially the same amount of wall-clock time as my nested for-loops, but took less cpu time.
retracile
Thanks for the feedback and congrats on your new voting power :)
Arnold Spence
+2  A: 

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.

ypnos
+1: Nice. I was just about to look at the scanLine() option.
Arnold Spence
It's not like Qt docs are advertising it well. Took me half a year to find out about it.
ypnos
I did a few rough timings of my over-all task without creating a micro-benchmark. This approach used essentially the same amount of cpu-time as Arnold's solution, but reduced wall-clock time.
retracile