Rotating a QImage breaks it



  • Hello,

    I'm facing a strange bug while tying to rotate a QPixmap. The resulting image is completely broken, with a lot of missing pixel.

    Here is the image I obtain:
    https://www.dropbox.com/s/6asnb65gv1gxzs7/rotatedIm.png

    However, if I first save the QPixmap to disk, load it, and then rotate it, it gives me a good result.... (https://www.dropbox.com/s/zuqili6bzrm3dhj/rotatedLoIm.png )

    Here is the code:

    @QImage imageFrame = pixelFrame.toImage();

    pixelFrame.save("pixelFrame.png", "PNG");
    imageFrame.save("imageFrame.png", "PNG");

    QTransform transform;
    transform.rotate(45);

    QImage loadedImage("pixelFrame.png");

    QImage rotatedIm = imageFrame.transformed(transform);
    QImage rotatedLoIm = loadedImage.transformed(transform);

    rotatedIm.save("rotatedIm.png", "PNG");
    rotatedLoIm.save("rotatedLoIm.png", "PNG");@

    And the four images I obtain:

    https://www.dropbox.com/s/7vhnho09b4r0k7r/pixelFrame.png

    https://www.dropbox.com/s/qlr4jng0s7gnmxh/imageFrame.png

    https://www.dropbox.com/s/6asnb65gv1gxzs7/rotatedIm.png

    https://www.dropbox.com/s/zuqili6bzrm3dhj/rotatedLoIm.png

    I checked the format of imageFrame and loadedImage, and they are same, as well as their width and height. Pixels in this two QImage are also identicals....

    I don't know at all how to fix this problem....

    I also have to add that pixelFrame is a QPixmap converted from a cv::Mat. Moreover, a rotation of 90° or 180° leads to good images....

    Do you have any suggestion?

    Thank you

    EDIT: I'm using Qt 5.3 with mingw-32 on windows 7 64 bits.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You should also add which version of Qt you are using as well as the OS you are currently running on



  • Hi and thank you,
    I edited my first post.


  • Lifetime Qt Champion

    Can you share the code that you are really using and that exhibit this behavior ?



  • Not really, it's part of a bigger project.
    The image is obtained from a video or a webcam via opencv, processed, and finally displayed.
    But I will try to replicate the bug and post some code in here later today


  • Lifetime Qt Champion

    If you can create a minimal project to replicate the problem it would be even better :)



  • Okay, so here it is:
    http://pastebin.com/v3skFnU2
    If you want to try, you can use : https://www.dropbox.com/s/gs6jr184x1d63ux/test.png as a source image

    I convert the image to CV_32FC1 in order to have the same format as in my original program (and so that I can use the function to go from mat to pixmap).
    Here it breaks a bit the image (I don't know why), but it's not important, the important thing is that the rotatedIm.png is broken while rotatedLoIm.png is not.

    Also, if you try the code, change the angle, cause it's giving good image with 90° (or 180°)

    I believe that something is wrong with the function that converts the cv::mat to the pixmap, but can't find what.... (I'm not the one who wrote it)

    Thank you!



  • Ok so I think I found the bug (the way to fix it at least)
    I replaced the line

    @((cv::Vec3b) dst) = cv::Vec3b(f, f, f);@
    by
    @((cv::Vec4b) dst) = cv::Vec4b(f, f, f, 255);@

    If anyone understand why this is working, I would appreciate to know...

    I first thought it had something to do with the alpha channel, however, when comparing pixels one by one, they were no differences between the two QImage, and every pixels where starting by 0xff (which is where alpha is normally stored in ARGB32.....

    Moreover, to change the alpha channel, shouldn't I put the 255 on the left of the vector ? If I do that the image becomes blue....



  • It seems that your open cv image BGRA format: http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=imread#imread

    The note on the site says that:

    Note

    In the case of color images, the decoded images will have the channels stored in B G R order.



  • It seems right, however, I'm creating a QImage with format QImage::Format_RGB32.

    So why the image that I see on the screen is still the image in the opencv format ?

    I mean, the QImage is in the RGB32 format, not CV_BGRA, so (f, f, f, 255) should be interpreted as a RGB32 image not BGRA which is the opencv format....



  • I am not sure about that but probably QImage gets the format information from the image itself and that is the reason for the original OpenCV format?

    Can you check with an image where you have three parts with only red, only green and only blue. But do not use only columns with that color but also some patches in between because otherwise you would not be able to see something like reverse order of colors with additional reverse order read.



  • Well I didn't check for an image with colors, as the original image is supposed to not have colors (it's a CV_32FC1 image, so a grayscale image)

    However I think that filling a QImage using cv::vec4b is not a good idea, and may be a source of errors....

    I've done this:
    @QPixmap pixmapFromCvMat(const cv::Mat &img) {
    QImage temp(img.size().width, img.size().height, QImage::Format_RGB32);

      QRgb *dst = (QRgb*) temp.bits(), *end = &dst[temp.width()*temp.height()-1]+1;
      const float *src = img.ptr<float>();
    
      for (; dst != end ; ++dst, ++src) {
          float f = clamp(255**src, 0.0f, 255.0f);
          *((QRgb*) dst) = qRgb(f, f, f);
      }
      return QPixmap::fromImage(temp);
    

    }@

    And it works :)
    Does this look okay to you?
    There is a cast from uchar* to unsigned int*, can this be a problem?
    What would be the best way to fill a QImage given float values?

    And actually, I think that it would be a better idea to convert CV_32FC1 to QImage::Format_Indexed8, as even in RGB32, channel are stored in 8bits, and therefore I lose precision anyway......



  • The color thing was just for making sure that you read the data correctly. It was just an idea to see it visually.

    Casting a pointer is only a good idea if you really know what you do (char e. g. needs less space in memory then int).

    I would openCV let do the mapping for you. For example a CV_8UC3 should fit the needs.



  • The point is exactly that I don't really know what I do while casting pointer :D I'm relatively new to C++... And for me, casting from uchar* to unsigned int* seems okay, but I'm not sure...

    What do you mean let openCV do the mapping ?
    I have a CV_32FC1, and I want a QPixmap, what do you think is the best format for that QPixmap? For me it's QImage::Format_Indexed8, but not sure...



  • As far as I can remember OpenCV can convert between the formats. So before hand it over to Qt convert it into the format you use in Qt and then just go through the pixel array and pass the values to the QPixmap instead of doing the conversion in Qt.
    But that would be the way I would do it. Any way which works should be okay.

    @char arr[] = {'0','1','2','3','4','5','6','7','8','9'};
    char *ptrc = arr;
    std::cout << "sizeof(char): " << sizeof(char) << " sizeof(int): " << sizeof(int) << std::endl;
    std::cout << "using char *" << std::endl;
    std::cout << *ptrc << std::endl;
    ptrc++;
    std::cout << *ptrc << std::endl;
    int ptri = (int)arr;
    std::cout << "using int *" << std::endl;
    std::cout << (char)*ptri << std::endl;
    ptri++;
    std::cout << (char)*ptri << std::endl;@

    This will produce the following output (on my system):

    sizeof(char): 1 sizeof(int): 4
    using char *
    0
    1
    using int *
    0
    4

    And that is what I meant with saying you have to be careful with pointer casting. Casting the value on the other hand is perfectly okay.



  • Okay then I will keep my solution :)
    Thanks a lot, I think I start understanding a lot more.

    I went back to my original bug, and the main image and the loaded one have the exact same values pixels by pixels, as well as the same format (QImage::Format_RGB32)
    Byt printing pixels std::cerr << std::hex << imageFrame.pixel(j, i), i have pixels of the form ff373737

    After the rotation, they both have the format (QImage::Format_ARGB32_Premultiplied), however, pixels values are not the same in both images, the one that doesn't work has pixels like "323232", and the one that works: "ff323232"....


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.