Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Converting images to 64base is too slow.
Forum Updated to NodeBB v4.3 + New Features

Converting images to 64base is too slow.

Scheduled Pinned Locked Moved Unsolved General and Desktop
16 Posts 4 Posters 2.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M MyNameIsQt

    @IgKh
    The number of images contained in one file can be larger. So file size is important. I confirmed that when using webp, the size is reduced to about 2/3 compared to jpeg.

    Thanks.

    I Offline
    I Offline
    IgKh
    wrote on last edited by
    #7

    @MyNameIsQt so if the source file is a media container of some sort, that already contains WebP encoded frames, why round trip through QImage? You are decoding and then encoding for nothing, and paying through the nose for it. Just take the byte buffer you used to construct the QImage and pass it as is to the web frame.

    If the transcoding serves some purpose of doing image manipulation before display, that it was originally WebP doesn't mean it has to stay like that. The size benefit doesn't matter, it is all in RAM, and even a 100% quality PNG won't be significantly bigger than the fully expanded pixmap that is QImage.

    M 1 Reply Last reply
    2
    • Christian EhrlicherC Christian Ehrlicher

      Then cache the encoded images instead re-encoding them every time.
      Also 2/3 sounds much but what's the actual size in bytes?

      M Offline
      M Offline
      MyNameIsQt
      wrote on last edited by
      #8

      @Christian-Ehrlicher Now I have 100 random images in jpg and webp format saved in one file.
      The results are as follows:
      jpg: 30.633.851 bytes
      webp: 25.845.760 bytes

      File compression rate varies depending on what image save.

      1 Reply Last reply
      0
      • Christian EhrlicherC Online
        Christian EhrlicherC Online
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #9

        So we really talk about 5MB more ram in contrast to slow loading speed. No need to discuss this further.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        M 1 Reply Last reply
        0
        • I IgKh

          @MyNameIsQt so if the source file is a media container of some sort, that already contains WebP encoded frames, why round trip through QImage? You are decoding and then encoding for nothing, and paying through the nose for it. Just take the byte buffer you used to construct the QImage and pass it as is to the web frame.

          If the transcoding serves some purpose of doing image manipulation before display, that it was originally WebP doesn't mean it has to stay like that. The size benefit doesn't matter, it is all in RAM, and even a 100% quality PNG won't be significantly bigger than the fully expanded pixmap that is QImage.

          M Offline
          M Offline
          MyNameIsQt
          wrote on last edited by
          #10

          @IgKh
          I know what you mean and I tried it.
          The following function is the original function.

          QString WPubManager::getImgObject()
          {
          if (m_pBkgnd && m_pBkgnd->loadImageData(m_file)){
                  const QImage& image = m_pBkgnd->getBkgndImg();
                  qDebug() << "3 size" << image.width();
          
                  if (image.isNull()) {
                      qDebug() << "error: image is null";
                      return QString(); 
                  } else {
                      qDebug() << "Image dimensions: " << image.width() << image.height();
                  }
          
                  QByteArray byteArray;
                  QBuffer buffer(&byteArray);
                  buffer.open(QIODevice::WriteOnly);
          
                  image.save(&buffer, "WEBP");
                  QString base64ImageData = QString::fromLatin1(byteArray.toBase64());
                  qDebug() << "Base64 image data size: " << base64ImageData.size();
                  buffer.close(); 
          
                  return base64ImageData; 
              } else {
                  qDebug() << "error: m_pBkgnd is null";
                  return QString();
              }
          }
          

          The key here is tobase64: Images without this part processed will not be drawn on the canvas.

                  QString base64ImageData = QString::fromLatin1(byteArray.toBase64());
          

          Only after that part is processed can the next statement be processed in js.

          // js
          var img = new Image();
           img.src = "data:image/webp;base64," + imageData;
          

          As you said, I sent several types of image data to js without using QImage.
          But in any case, the toBase64() function must be processed. To do that, I don't know any other way than to go through QImage.

          I 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            So we really talk about 5MB more ram in contrast to slow loading speed. No need to discuss this further.

            M Offline
            M Offline
            MyNameIsQt
            wrote on last edited by MyNameIsQt
            #11

            @Christian-Ehrlicher
            The saved image now contains many small thumbnails. For small thumbnails, there isn't much difference in compression rates between webp and jpg.

            Additionally, the results shown now show that webp has a 25% more efficient compression rate. 5MB is not a big thing, but if 10,000 users upload files simultaneously to the web server (that I created), the amount of traffic or the physical size that occupies the server will not be negligible.

            ( * I had a pleasant fantasy of 10,000 people using the web I created at the same time. ha ha ha...)

            1 Reply Last reply
            0
            • M MyNameIsQt

              @IgKh
              I know what you mean and I tried it.
              The following function is the original function.

              QString WPubManager::getImgObject()
              {
              if (m_pBkgnd && m_pBkgnd->loadImageData(m_file)){
                      const QImage& image = m_pBkgnd->getBkgndImg();
                      qDebug() << "3 size" << image.width();
              
                      if (image.isNull()) {
                          qDebug() << "error: image is null";
                          return QString(); 
                      } else {
                          qDebug() << "Image dimensions: " << image.width() << image.height();
                      }
              
                      QByteArray byteArray;
                      QBuffer buffer(&byteArray);
                      buffer.open(QIODevice::WriteOnly);
              
                      image.save(&buffer, "WEBP");
                      QString base64ImageData = QString::fromLatin1(byteArray.toBase64());
                      qDebug() << "Base64 image data size: " << base64ImageData.size();
                      buffer.close(); 
              
                      return base64ImageData; 
                  } else {
                      qDebug() << "error: m_pBkgnd is null";
                      return QString();
                  }
              }
              

              The key here is tobase64: Images without this part processed will not be drawn on the canvas.

                      QString base64ImageData = QString::fromLatin1(byteArray.toBase64());
              

              Only after that part is processed can the next statement be processed in js.

              // js
              var img = new Image();
               img.src = "data:image/webp;base64," + imageData;
              

              As you said, I sent several types of image data to js without using QImage.
              But in any case, the toBase64() function must be processed. To do that, I don't know any other way than to go through QImage.

              I Offline
              I Offline
              IgKh
              wrote on last edited by
              #12

              @MyNameIsQt I'm not questioning the need to pass the data to Javascript as Base-64 encoded text. But you can Base-64 encode any QByteArray...

              Would you mind sharing how the QImage got created in your getBkgndImg() method? It should have been read from a byte array / binary buffer of some kind, and if that encodes a valid WebP image, directly Base-64 printing it should be sufficient.

              To keep one step ahead, it is possible that the original image data is not actually WebP, or not valid enough in a way that Qt's image format plugin can handle but Chromium can't. You can double check by looking at the result of calling QImageReader::imageFormat on whatever you created the QImage from.

              M 2 Replies Last reply
              0
              • I IgKh

                @MyNameIsQt I'm not questioning the need to pass the data to Javascript as Base-64 encoded text. But you can Base-64 encode any QByteArray...

                Would you mind sharing how the QImage got created in your getBkgndImg() method? It should have been read from a byte array / binary buffer of some kind, and if that encodes a valid WebP image, directly Base-64 printing it should be sufficient.

                To keep one step ahead, it is possible that the original image data is not actually WebP, or not valid enough in a way that Qt's image format plugin can handle but Chromium can't. You can double check by looking at the result of calling QImageReader::imageFormat on whatever you created the QImage from.

                M Offline
                M Offline
                MyNameIsQt
                wrote on last edited by MyNameIsQt
                #13

                @IgKh
                getBkgndImg is just an inline function.

                inline const QImage &getBkgndImg() const {
                       if (m_pBkgndImage) {
                           return *m_pBkgndImage;
                       } else {            
                           static const QImage emptyImage;
                           return emptyImage;
                       }
                   }
                

                The image data saved in the file is created using google's decodeWebp and encodeWebp functions. These work well. Checked in all browsers.

                Base64 to Webp text
                Webp
                Thanks so much..

                1 Reply Last reply
                0
                • I IgKh

                  @MyNameIsQt I'm not questioning the need to pass the data to Javascript as Base-64 encoded text. But you can Base-64 encode any QByteArray...

                  Would you mind sharing how the QImage got created in your getBkgndImg() method? It should have been read from a byte array / binary buffer of some kind, and if that encodes a valid WebP image, directly Base-64 printing it should be sufficient.

                  To keep one step ahead, it is possible that the original image data is not actually WebP, or not valid enough in a way that Qt's image format plugin can handle but Chromium can't. You can double check by looking at the result of calling QImageReader::imageFormat on whatever you created the QImage from.

                  M Offline
                  M Offline
                  MyNameIsQt
                  wrote on last edited by MyNameIsQt
                  #14

                  @IgKh

                  // CBackground.h
                     ...
                     QByteArray m_decodedArray; 
                      uint8_t* m_decodedData;
                      size_t m_imageSize = 0;    
                      ....
                      inline const uchar* getDecodedData() const {
                          return m_decodedData; // QByteArray 변환 없이 원시 데이터 포인터를 반환
                      }
                  
                  // CBackground.cpp
                        bool CBackground::decodeWebp(QFile *pFile) 
                        {      
                            long imageSize = m_bkgndHeader.ImageSize;     
                            long imageAddress = getImageAddress(); 
                        
                            if (!pFile->seek(imageAddress)) {      
                                throw std::runtime_error("Failed to seek to image data");
                                      return false;      
                            }
                        
                            QByteArray imageData(imageSize, 0);      
                            if (pFile->read(imageData.data(), imageSize) != imageSize) {      
                                throw std::runtime_error("Failed to read image data");
                                      return false;
                                  }      
                            int width = 0, height = 0;      
                            m_decodedData = WebPDecodeRGBA(reinterpret_cast<uchar*>(imageData.data()), imageSize, &width, &height);
                        
                            if (!m_decodedData) {      
                                throw std::runtime_error("Failed to decode WebP image");      
                                return false;      
                            }
                            m_imageSize = width * height * 4;
                  
                            retirm trie;
                  }
                  
                  // wemar.cpp
                  QString Wemar::getImgObject()
                  {
                         if (m_pBkgnd && m_pBkgnd->loadImageData(m_file)){       
                              const uchar* decodedData = m_pBkgnd->getDecodedData();      
                              QByteArray webpData(reinterpret_cast<const char*>(decodedData), m_pBkgnd->getImageSize());
                       
                                 if (webpData.isEmpty()) {    
                                  qDebug() << "error: image data is empty";    
                                  return QString();   
                              } else {    
                                  qDebug() << "Base64 before: " << webpData.size();                    
                              }   
                          QString base64ImageData = QString::fromLatin1(webpData.toBase64());
                          qDebug() << "Base64 After: " << base64ImageData.size();
                  
                          return base64ImageData;            
                          } else {    
                              qDebug() << "error: m_pBkgnd is null";    
                              return QString();     
                          }
                  }
                  
                  /// js
                    new QWebChannel(qt.webChannelTransport, function(channel) {
                                 wpubManager = channel.objects.wpubManager;
                              
                      ....
                   async function handleImage() {
                          var bkgndImageData = await wpubManager.getImgObject(); // Promise 완료를 기다림
                          if (bkgndImageData) {
                              console.error("image good");
                              drawCanvas(bkgndImageData);
                          } else {
                              console.error("Failed to get background image data.");
                          }
                      }
                      ....
                      // in drawCanvas function
                          var img = new Image();
                          img.src = "data:image/webp;base64," + imageData;
                  
                          img.onerror = function(){
                              console.error("Image failed to load");
                          };
                  
                          img.onload = function() {
                              canvas.width = img.width;
                              canvas.height = img.height;
                  
                              context.fillStyle = "#d4d4d4";
                              context.fillRect(0, 0, canvas.width, canvas.height);
                  
                              context.drawImage(img, 0, 0, img.width, img.height);
                          };
                  I 1 Reply Last reply
                  0
                  • M MyNameIsQt

                    @IgKh

                    // CBackground.h
                       ...
                       QByteArray m_decodedArray; 
                        uint8_t* m_decodedData;
                        size_t m_imageSize = 0;    
                        ....
                        inline const uchar* getDecodedData() const {
                            return m_decodedData; // QByteArray 변환 없이 원시 데이터 포인터를 반환
                        }
                    
                    // CBackground.cpp
                          bool CBackground::decodeWebp(QFile *pFile) 
                          {      
                              long imageSize = m_bkgndHeader.ImageSize;     
                              long imageAddress = getImageAddress(); 
                          
                              if (!pFile->seek(imageAddress)) {      
                                  throw std::runtime_error("Failed to seek to image data");
                                        return false;      
                              }
                          
                              QByteArray imageData(imageSize, 0);      
                              if (pFile->read(imageData.data(), imageSize) != imageSize) {      
                                  throw std::runtime_error("Failed to read image data");
                                        return false;
                                    }      
                              int width = 0, height = 0;      
                              m_decodedData = WebPDecodeRGBA(reinterpret_cast<uchar*>(imageData.data()), imageSize, &width, &height);
                          
                              if (!m_decodedData) {      
                                  throw std::runtime_error("Failed to decode WebP image");      
                                  return false;      
                              }
                              m_imageSize = width * height * 4;
                    
                              retirm trie;
                    }
                    
                    // wemar.cpp
                    QString Wemar::getImgObject()
                    {
                           if (m_pBkgnd && m_pBkgnd->loadImageData(m_file)){       
                                const uchar* decodedData = m_pBkgnd->getDecodedData();      
                                QByteArray webpData(reinterpret_cast<const char*>(decodedData), m_pBkgnd->getImageSize());
                         
                                   if (webpData.isEmpty()) {    
                                    qDebug() << "error: image data is empty";    
                                    return QString();   
                                } else {    
                                    qDebug() << "Base64 before: " << webpData.size();                    
                                }   
                            QString base64ImageData = QString::fromLatin1(webpData.toBase64());
                            qDebug() << "Base64 After: " << base64ImageData.size();
                    
                            return base64ImageData;            
                            } else {    
                                qDebug() << "error: m_pBkgnd is null";    
                                return QString();     
                            }
                    }
                    
                    /// js
                      new QWebChannel(qt.webChannelTransport, function(channel) {
                                   wpubManager = channel.objects.wpubManager;
                                
                        ....
                     async function handleImage() {
                            var bkgndImageData = await wpubManager.getImgObject(); // Promise 완료를 기다림
                            if (bkgndImageData) {
                                console.error("image good");
                                drawCanvas(bkgndImageData);
                            } else {
                                console.error("Failed to get background image data.");
                            }
                        }
                        ....
                        // in drawCanvas function
                            var img = new Image();
                            img.src = "data:image/webp;base64," + imageData;
                    
                            img.onerror = function(){
                                console.error("Image failed to load");
                            };
                    
                            img.onload = function() {
                                canvas.width = img.width;
                                canvas.height = img.height;
                    
                                context.fillStyle = "#d4d4d4";
                                context.fillRect(0, 0, canvas.width, canvas.height);
                    
                                context.drawImage(img, 0, 0, img.width, img.height);
                            };
                    I Offline
                    I Offline
                    IgKh
                    wrote on last edited by
                    #15

                    @MyNameIsQt Thanks!

                    In this bit:

                    QByteArray imageData(imageSize, 0);      
                    if (pFile->read(imageData.data(), imageSize) != imageSize) {      
                        throw std::runtime_error("Failed to read image data");
                        return false;
                    }
                    

                    Unless something funny is going on, it should sufficient to call QString::fromLatin1(imageData.toBase64()) after that point and send the result over to the web frame.

                    (P.S no need to return after throwing, the return is unreachable as exceptions propagate over an alternative control flow)

                    M 1 Reply Last reply
                    0
                    • I IgKh

                      @MyNameIsQt Thanks!

                      In this bit:

                      QByteArray imageData(imageSize, 0);      
                      if (pFile->read(imageData.data(), imageSize) != imageSize) {      
                          throw std::runtime_error("Failed to read image data");
                          return false;
                      }
                      

                      Unless something funny is going on, it should sufficient to call QString::fromLatin1(imageData.toBase64()) after that point and send the result over to the web frame.

                      (P.S no need to return after throwing, the return is unreachable as exceptions propagate over an alternative control flow)

                      M Offline
                      M Offline
                      MyNameIsQt
                      wrote on last edited by
                      #16

                      @IgKh You didn't show me a direct solution to the problem, but you showed me the right approach to solving the problem. Using qimage as you mentioned is unnecessary. I need to focus on the issue of base64 encoding binary data. thank you

                      1 Reply Last reply
                      0
                      • Christian EhrlicherC Christian Ehrlicher referenced this topic on

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved