3

I have searched everywhere and i cannot find any solution after 2 days of trying.

The Problem:

I'm doing an image Viewer with "Fit Image to View" feature. I load a picture of say 3000+ pixels in my GraphicsView (which is a lot smaller ofcourse), scrollbars appear that's good. When i click my btnFitView and executed:

ui->graphicsView->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);

This is down scaling right? After fitInView() all lines are pixelated. It looks like a saw went over the lines on the image.

For example: image of a car has lines, image of a texbook (letters become in very bad quality).

My code sample:

// select file, load image in view
QString strFilePath = QFileDialog::getOpenFileName(
            this,
            tr("Open File"),
             "/home",
            tr("Images (*.png *.jpg)"));

imageObject = new QImage();
imageObject->load(strFilePath);
image = QPixmap::fromImage(*imageObject);

scene = new QGraphicsScene(this);
scene->addPixmap(image);
scene->setSceneRect(image.rect());
ui->graphicsView->setScene(scene);


// on_btnFitView_Clicked() :
ui->graphicsView->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);

Just before fitInView(), sizes are:

qDebug()<<"sceneRect = "<< scene->sceneRect();
qDebug()<<"viewRect = " << ui->graphicsView->rect();

sceneRect =  QRectF(0,0 1000x750)
viewRect =   QRect(0,0 733x415)  

If it is necessary i can upload screenshots of original loaded image and fitted in view ?

Am i doing this right? It seems all examples on the Web work with fitInView for auto-fitting. Should i use some other operations on the pixmap perhaps?

SOLUTION

// LOAD IMAGE
bool ImgViewer::loadImage(const QString &strImagePath)
{
  m_image = new QImage(strImagePath);
  if(m_image->isNull()){
    return false;
  }
  clearView();

  m_pixmap = QPixmap::fromImage(*m_image);     
  m_pixmapItem = m_scene->addPixmap(m_pixmap); 
  m_scene->setSceneRect(m_pixmap.rect());      
  this->centerOn(m_pixmapItem);

  // preserve fitView if active
  if(m_IsFitInView)
      fitView();

  return true;
}

// TOGGLED FUNCTIONS
void ImgViewer::fitView()
{
    if(m_image->isNull())
      return;

    this->resetTransform();
    QPixmap px = m_pixmap; // use local pixmap (not original) otherwise image is blurred after scaling the same image multiple times
    px = px.scaled(QSize(this->width(),this->height()),Qt::KeepAspectRatio,Qt::SmoothTransformation);
    m_pixmapItem->setPixmap(px);
    m_scene->setSceneRect(px.rect());
}
void ImgViewer::originalSize()
{
    if(m_image->isNull())
       return;

    this->resetTransform();
    m_pixmap = m_pixmap.scaled(QSize(m_image.width(),m_image.height()),Qt::KeepAspectRatio,Qt::SmoothTransformation); 
    m_pixmapItem->setPixmap(m_pixmap);
    m_scene->setSceneRect(m_pixmap.rect());
    this->centerOn(m_pixmapItem); //ensure item is centered in the view.
}

On downshrink this produces good quality. Here are some stats after calling these 2 functions:

//    "originalSize()"  : IMAGE SIZE =   (1152, 2048)
//    "originalSize()"  : PIXMAP SIZE =  (1152, 2048)
//    "originalSize()"  : VIEW SIZE =    (698, 499)
//    "originalSize()"  : SCENE SIZE =   (1152, 2048)

//    "fitView()"   : IMAGE SIZE =   (1152, 2048)
//    "fitView()"   : PIXMAP SIZE =  (1152, 2048)
//    "fitView()"   : VIEW SIZE =    (698, 499) 
//    "fitView()"   : SCENE SIZE =   (280, 499)

There is a problem now, after call to fitView() look the size of scene? Much smaller.

And if fitView() is activated, and I now scale the image on wheelEvent (zoomIn/zoomOut), with the views scale function: scale(factor,factor); ..produces terrible result. This doesn't happen with originalSize() where scene size is equal to image size.

PathOfNeo
  • 1,089
  • 4
  • 21
  • 39
  • Does it getting better, if you set the render hints to `ui->graphicsView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);`? – tomvodi Nov 07 '14 at 12:47
  • renderingHints were already set, unfortunately didn't help – PathOfNeo Nov 07 '14 at 13:01

1 Answers1

2

Think of the view as a window into the scene.

Moving the view large amounts, either zooming in or out, will likely create images that don't look great. Rather than the image being scaled as you would expect, the view is just moving away from the scene and doing its best to render the image, but the image has not been scaled, just transformed in the scene.

Rather than using QGraphicsView::fitInView, keep the main image in memory and create a scaled version of the image with QPixamp::scaled, each time FitInView is selected, or the user zooms in / out. Then set this QPixmap on the QGraphicsPixmapItem with setPixmap.

You may also want to think about dropping the scroll bars and allowing the user to drag the image around the screen, which provides a better user interface, in my opinion; though of-course it depends on your requirements.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • you are right, it's better without scrollbars. Only thing is i now got a 1 or 2 pixel movement when the image is fitted in view. I have edited my question you can see my solution above. Thx – PathOfNeo Nov 08 '14 at 00:14
  • Please don't edit a question to ask a new one, or something else, once it has been answered. You're better off asking a new, separate question, which provides greater visibility and more likely to receive good answers. Once you've created a new question, feel free to post a comment here to direct to it. – TheDarkKnight Nov 10 '14 at 08:58
  • Works, but unfortunately creating a scaled pixmap wastes lots of time. In my case for some test pictures 50 ms. – user136036 Jan 24 '20 at 20:58
  • @user136036 wastes time, or takes time?! – TheDarkKnight Jan 27 '20 at 15:23
  • The problem is creating a huge pixmap in the first place, as this is extremely slow (some 6k x 8k pic takes ~600ms here). I came up with a better solution after writing my post above: Use `QImageReader`, set the `setScaledSize()` to the one of your image viewer (you have to calculate aspect ratio yourself) and then create a pixmap out of it. So we create a much smaller pixmap in the first place, hence saving *a lot* of time. For my image viewer for example, times went from 600 ms to display an image down to 100 ms and you also get proper smoothing. – user136036 Jan 27 '20 at 15:46
  • Though for then zooming I have to create the big pixmap. But given that zooming is a rare occasion (for my usecase), it's ok for me to then load the big picture on demand instead of every time. – user136036 Jan 27 '20 at 15:47
  • @user136036 Are you also using hardware acceleration? If not, that might give you the original performance you'd want. – TheDarkKnight Jan 27 '20 at 17:12
  • @TheDarkKnight I do not know to be honest. I started using Qt for my little project some days ago and tinkered with it until I found the trick I described above. I searched if there was some way to make QPixmap faster, but did not find anything relevant. The 600 ms I stated above are not due to slow HDD, they are for parsing directly from a memory buffer (and only for creating the pixmap). Could you elaborate on what you mean with 'hardware acceleration' please? The goal is to have an image viewer that can show large images in speeds comparable to IrfanView or the like. – user136036 Jan 27 '20 at 19:02
  • *Could you elaborate on what you mean with 'hardware acceleration' please?* - using the hardware of your graphics card, instead of software on the general CPU. Explained [here](https://en.wikipedia.org/wiki/Hardware_acceleration). For Qt, that means using [OpenGL](https://doc.qt.io/qt-5/qtgui-index.html#opengl-and-opengl-es-integration) – TheDarkKnight Jan 28 '20 at 09:47