PDA

View Full Version : QPixmap blur when QGraphicsView scale



shanshan
15th October 2014, 16:04
Hello

I am developing a tools to visualize for the image processing result. The problem is that I add a pixmap on the QGraphicsScene and other items on the scene. My user found the position of the items are moving during zooming from this pixel to the neighbour and then back.

My user need to zoom the screen to get exact pixel view of the image.
I use the QGraphicsView->scale(factor, factor) to zoom in and out.
When the factor is 2, there is no problem, but we need the factor as 1.2 or 1.5, then the pixmap will blur like the picture. The line or other items are not at the correct position relative to the pixmap. ( the lines position is correct, the pixmap is not)

10667

I tried the settings about
setRenderHint setOptimizationFlag
none of them works
I cannot setTransformationMode( Qt::SmoothTransformation ), because this is not what we want. we need subpixel level image to be shown correctly.

Please tell me how to set the QGraphicsView to get the correct pixmap.

ChrisW67
15th October 2014, 21:07
What "exact pixel" is at position (0.83, 0.83) in the source image, because that is what you would expect at (1,1) at scale 1.2? There isn't one, so the image is scaled and pixels interpolated to get an estimate of what should be there. This seems like normal operation.

d_stranz
16th October 2014, 01:59
Please tell me how to set the QGraphicsView to get the correct pixmap.

As ChrisW67 said, Qt is doing what it should do. What Qt is giving you is a magnified but pixellated image. This is the same thing you get when you use a digital camera to zoom in. The problem in both cases is that your image still has the same number of pixels, while the view that you are projecting onto has many more. Thus, the image pixels are simply scaled to cover bigger rectangles.

What you are asking for is an interpolated image. Google for "bilinear interpolation". There are lots of examples and implementations. You will need to create a new pixmap each time the zoom level changes, by sampling and interpolating the zoom region of the original image to cover the new view size.

wysota
17th October 2014, 07:45
I think the Smooth pixmap transform mode might be doing bilinear interpolation. Fast mode does the nearest neighbour algorithm, I guess. If one wants to preserve colors and at the same time have pixel perfect image scaling, one should only allow zoom levels that allow for such attributes.

shanshan
17th October 2014, 08:54
I think what you said is correct, Smoothtransform get the picture totally blurred when you zoom in in certain level. Fast mode looks like the pixel is divided into several smaller ones, and some get the nearest neighboured color. That is why I cannot get the whole bigger pixel with the same color at the correct position.

How you mean to allow zoom levels that allow for such attributes, that I can only set the scale factor to 2 or set a maximum saled value? But that cannot fullfill our users request!

Added after 6 minutes:

The problem is that "the image pixels are simply scaled to cover bigger rectangles." This is exactly what I need but cannot get.

You can check on my image that the pixmap is with the size( 1024,768) and the line is drawed at the postion (1010, 767) to (1024,767). I also tracked the cursor's position in QGraphicsScene. The line is drawed at the correct position, but the pixmap is not. The line should be exactly between two big pixel lines, but it is in the middle. Then if I zoom in further, the pixmap is correct and then that is how my user find that the line is moving and then back.

If as you said the pixmap has the same number of pixels, but just cover bigger rectangles, why in the middle of my imag, the rectangles get blurred, That is not what I expect as the upper side of the pixels, that are correctly shown.

wysota
17th October 2014, 09:20
How you mean to allow zoom levels that allow for such attributes, that I can only set the scale factor to 2 or set a maximum saled value?
Use scale factors that will result in integer number of pixels forming from the original pixel (thus integer scale factors).


But that cannot fullfill our users request!

If you have an image consisting of one white and one black pixel (1+1) and you zoom it up by 1.5x then you end up with 1.5 white and 1.5 black pixels however that cannot be represented on three pixels as each one needs to have only one color. Therefore you end up with either one white, one black and one gray pixel in the middle (interpolation) or 2 pixels of one color and one pixel of the other color (nearest neightbour). The latter means the proportions of pixel colors in your image changed from 1:1 to 2:1, falsifying the image greatly. You cannot eat the cake and still keep the cake.

shanshan
17th October 2014, 10:35
That is clear enough, thanks!
The 1.5 pixel example is simple and clear, I have the rough idea but I still thought that qt can automatically solve the problem, because when we zoom in in certain level to saw one pixel size in screen, the pixel size in image is hundreds of times of the screen resolution.
If I still need to fix on 1.5 scalefactor I need to sample the img by myself in some deep level, or just simply set integer zoom factor.
Thanks a lot!

Added after 55 minutes:

Hello,

I explain my user your example, but he is not satisfied and ask more questions.

If it is scale factor not integer problem, that the radom sampling only should be one original image pixel size. But what we got is clearly much bigger than one image pixel.

If the resample of the image pixel( interpolation) is taking one nearest, what is the qt algorithm to choose the color, why there is not staight cut for certain interpolation but sometimes choose left sometimes choose right. Why sometimes we get the straight rectangles and sometimes it is not. And when we drag the scene, the colors changed?

wysota
17th October 2014, 10:54
why there is not staight cut for certain interpolation

Because you cannot cut in the middle of a pixel. If you scale up n times, you still take colours from the original image so with our example you still sample from one black and one white pixel. All interpolated pixels closer to the original black one will be black and all those closer to the white one will be white.

Tell your client you cannot upscale an image without losing quality. That happens only in the movies. If your client wants better results he should provide you with higher-resolution image or settle down for an interpolated result.

If you want to support zoom factor resolution of 0.1 (e.g 1.2, 1.3, 1.4, ...) without losing quality, the resolution of the original image should be 10 times larger (that is every physical separate point in the destination (displayed) image of scale 1 should be represented by 10 samples of the original physical "point" represented by that pixel. In all other cases the quality will be affected.

shanshan
17th October 2014, 11:05
The example is as picture
in some step it is ok, some is not, and the interpolation pixel ( incorrect color) is more than 1.
The insteresting thing is that when we saw the distortion, if we drag the scene, and the distortion changes. Is this a bug in qt or we need to do some settings?
10674

wysota
17th October 2014, 11:10
What is the size of the original pixmap?

BTW. 1.5^8 = 25.628906

shanshan
17th October 2014, 11:23
Hello,

thanks for your fast reply. But this explains that one step scale, that 1.2, 1.3, 1.4... only one image pixel is interpolated. But our case is that we scaled many steps, the image is zoomed 1.5^9 times to 38.4434, but not only one pixel at 39 is interpolated, but multiple of them.
Thant means that when the picture of (1*2) is zoomed to (1,77), (1-38) should be black, but they are not. about( 20-38) are all random and they change when we drag the image. That is why we thought it is a bug of qt

Added after 7 minutes:

The pixmap is 1024*768, the line is at 767,
the line stays between two pixel when zoom 1.5^8 = 25.6289, get higher at 1.5^9 = 38.4434 and move back between two pixel at 1.5^10=57.665

this is just an sample that to point out the problem, our users need to see the circles or results inside exact each image pixel, cannot accept them move around and back.

10675

wysota
17th October 2014, 12:16
I have no idea what you do with your image but it seems Qt behaves correctly, here is a sample app that demonstrates this:


#include <QtWidgets>

class GraphicsView : public QGraphicsView {
public:
GraphicsView() { m_scale = 1; }
protected:
void wheelEvent(QWheelEvent *ev) {
if(ev->delta() > 0) {
m_scale *= 1.5;
qDebug() << "+1.5" << m_scale;
scale(1.5, 1.5);
} else {
m_scale /= 1.5;
qDebug() << "-1.5" << m_scale;
scale(1/1.5, 1/1.5);
}
}
private:
qreal m_scale;
};

int main(int argc, char **argv) {
QApplication app(argc, argv);

QGraphicsScene scene;
QPixmap px(2,2);
px.fill(Qt::white);
QPainter p(&px);
p.setPen(Qt::black);
p.drawPoint(0,0);
p.drawPoint(1,1);
p.end();
scene.addPixmap(px);
GraphicsView view;
view.setScene(&scene);
view.show();
return app.exec();
}

You can see that with small scales the squares are not even but the situation improves as the scales get larger.

shanshan
17th October 2014, 16:58
What I did is a big project and which handled image is like this




MLGraphicsView( MLGraphicsScene * scene, QWidget * parent = 0, Qt::WindowFlags f = 0 )
:QGraphicsView( scene )
,m_scale(1.0)
{
setDragMode( QGraphicsView::ScrollHandDrag );
}
~MLGraphicsView()
{}
protected:
//Take over the interaction
virtual void wheelEvent(QWheelEvent* event)
{
setTransformationAnchor(QGraphicsView::AnchorUnder Mouse);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
// Scale the view / do the zoom
double scaleFactor = 1.5;

if(event->delta() > 0) {
// Zoom in
scale(scaleFactor, scaleFactor);
m_scale *= scaleFactor;

} else {
scale(1.0 / scaleFactor, 1.0 / scaleFactor);
m_scale /= scaleFactor;
}

// some other part
QGraphicsScene* gScene = this->getView()->scene();

gScene->clear();

QPixmap pixmap = m_visDocument->getImag( camId );
if( !pixmap )
{
ART_LOG_ERROR( logger, "Camera " << camId << " is empty. " );
continue;
}
QGraphicsPixmapItem * qpixmap = gScene->addPixmap( pixmap );
QGraphicsLineItem* line = new QGraphicsLineItem( -size, 0, size, 0 );
gScene->addItem(line);




what you did is just a 2*2 pixmap and with the same color, you can try load a bigger windows picture, and then zoom it in in bigger levels, maybe you will see what I get.

Like the 1.5^9 = 38.4434 times, that if qt is doing right, 1-38 pixel should be same color, but 39 is random. But in my img you saw, it lookes like 1-25 is sampled correct, 25-38is not. It is obviously wrong!

wysota
17th October 2014, 19:44
Can you post a minimal compilable example reproducing the problem?

shanshan
21st October 2014, 15:14
Hello, the code is as followed,

I work with the windows default pixtures and
here is the screenshot
when scale factor 25.6289, no problem, when 38.** or 57.6**, the line is not in the correct postion, and when drag the scene with mouse, the pixels blured and changed.



#include <QtWidgets>
#include <QGraphicsView>
#include <QWheelEvent>
#include <QApplication>


class GraphicsView : public QGraphicsView
{

public:
GraphicsView ( QWidget* parent = 0 )
:QGraphicsView( parent )
,m_scale(1.0)
{
setDragMode( QGraphicsView::ScrollHandDrag );
}
protected:
void wheelEvent(QWheelEvent *ev)
{
setTransformationAnchor(QGraphicsView::AnchorUnder Mouse);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
if(ev->delta() > 0) {
m_scale *= 1.5;
qDebug() << "+1.5" << m_scale;
scale(1.5, 1.5);
} else {
m_scale /= 1.5;
qDebug() << "-1.5" << m_scale;
scale(1/1.5, 1/1.5);
}
}
private:
qreal m_scale;
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsScene scene;
QPixmap px;

QString fileName = QFileDialog::getOpenFileName(0, QString("Open File"), QDir::currentPath(), "Image Files (*.png *.jpg *.bmp)");
if (!fileName.isEmpty())
{
QFileInfo fi( fileName );
if( fi.exists())
{
bool err = px.load( fi.absoluteFilePath());
if (!err)
return 0;
}
}

scene.addPixmap(px);
QGraphicsLineItem* line = new QGraphicsLineItem( px.width()-10,px.height()-1,px.width(),px.height()-1 );
QPen pen(Qt::red, 1.0 );
pen.setCosmetic( true );
line->setPen( pen );
scene.addItem( line );
GraphicsView view;
view.setScene(&scene);
view.show();
return app.exec();
}


10685 106861068710688