Arrow
Arrow17. April 2017 05:36

Оптимизация загрузки изображений

Оптимизация загрузки изображений, QPixmap

Приложение загружает для просмотра изображения с указанной пользователем папки или нескольких папок. Загрузка происходит в отдельном потоке.

Загрузить выходит максимум 20 шт., если больше, то программа на Debian начинает тормозить, а под Windows 7 и вовсе отказывается что-то грузить (грузит 20 штук и дальше ни в какую).

Пробовал использовать частичную загрузку по 15 шт., а дальше по мере прокрутки QScrollArea пользователем догружать остатки - как оказалось не вариант пользователь может перейти к любому изображению (прокрутить скрол). При этом чувствуются тормоза (не успевает все прогрузится) и программа занимает много памяти.

Пробовал также удалить прошлые изображения при до-загрузке новых. В общем возникает масса неудобств и нет возможности навигации по всем изображениям и те же тормоза.

Отсюда назрел вопрос: Есть ли какая-то возможность оптимизировать процесс загрузки или как-то уменьшить вес изображений при их загрузке на отображение?

Пытался сделать так, но не помогло (изображение после компрессии не загружается в pixmap):

        QPixmap pix;

//------------------------------------------------------------------------------

        QByteArray array;
        QBuffer buf(&array, this);
        buf.open(QIODevice::WriteOnly);

        pix.save(&buf, "JPG", 30);
        buf.close();
        array = qCompress(array, 9);

        pix.loadFromData(array, "JPG");   // Здесь pix.size() - 0
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

11
Evgenii Legotckoi
  • 17. April 2017 07:03

qCompress однозначно использовать нельзя, поскольку он предназначен просто для компрессии сырых байтов без учёта какого-либо алгоритма организации данных, в данном случае он игнорирует структуру организации данных изображений. Поэтому от такого способа оптимизации сразу отказываемся. Этот инструмент для этого не предназначен.

Что касается остальной части вопроса, то нужно подумать. Вечером может какие мысли появятся, ну и или другие пользователи форума может что подскажут.

    Evgenii Legotckoi
    • 18. April 2017 01:19

    А какого вообще размера изображения?

    Вы работаете с изображениями, поэтому программа в любом случае будет потреблять большие объёмы памяти, особенно, если это фотографии в высоком качестве, поэтому здесь только смириться, или уменьшать размер фото, вместе с качеством фото.

    К слову говоря, метод QPixmap::scaledToWidth автоматически уменьшает размер изображения и его качество, думаю, что на потреблении памяти это тоже сказывается, поскольку необходимо держать меньшее количество пикселей в памяти. В принципе вопрос потребления памяти можно несколько нивелировать, если хранить в памяти не оригиналы изображения, а пути к изображению, а в случае изменения размеров каждый погружать изображение из этого пути и масштабировать. Хотя, конечно, тут могут появиться задержки при открытии файла. Что касается подгрузки изображений. Вы замечали, что в файловых менеджерах превью изображений сначала выглядит как обычная иконка, а потом только погружается как положено. Можете применить данный способ отображения. То есть заполнить всё сначала некоторыми иконками, которые подразумевают изображение, а потом уже заполнять картинку, когда пользователь прокрутит. Это несколько сгладит когнитивный диссонанс у пользователя.

    Тем более, если вернуться к просмоторщикам, тот же самый встроенный просмоторщик pdf в Linux тоже не отличается идеальной скоростью при открытии очень больших pdf. Так что некоторые задержки будут всегда, особенно на слабом железе.

    Но если у вас программа падает или виснет всего на 20 изображениях, то это говорит о проблемах в логике кода. То есть это скорее не вопрос оптимизации, как я полагаю. Например, в одном из наших обсуждений по этой тематике, я же набросал пробную программку со скроллингом изображений. Она вполне неплохо открывает 200-300 фотографий и не виснет. Хотя я понимаю, что у вас логика уже явно сложнее, но это лишь говорит о том, что где-то, что-то вы делаете не так. Да, в моём варианте видно, что фотографии грузятся, а не всё сразу отображается, но для этого и служат всякие прогресс бары, чтобы показываться процесс загрузки. Поэтому подумайте, над тем, что я написал. Задача скорее не в том, чтобы оптимизировать, а в том, чтобы найти ошибку, поскольку 20 изображений - это не такая большая нагрузка, чтобы повешать приложение на современно ПК, а открываться эти 20 изображении будут секунды, не более.

      Evgenii Legotckoi
      • 18. April 2017 01:24
      • Die Antwort wurde als Lösung markiert.

      Вот, прикладываю мою пробную программку, просто посмотрите как она работает на большом фото архиве. Где фотографий 300 будет. Там кнопка на открытие диалога, в диалоге переходите в каталог с фотками и выделяете все фотографии.

      Посмотрите, как будет грузиться. Это вполне приемлемый по скорости процесс загрузки.

        Arrow
        • 18. April 2017 08:11

        Спасибо!

        У меня грузит с такой же скоростью.

        Изображение: 2400х3400 (23,5 МБт).

        У меня происходит дополнительная обработка изображений и она как оказалось и дает нагрузку.

        Похоже от этого не уйти никак, придется смириться.

        Если показывать подставную картинку выходит очень даже ничего. :)

        Пара вопросов по вашему коду:

        1. Из-за любопытства (чисто для себя)- Почему вы некоторые заголовочные файлы подключаете в файле реализации (*.cpp), а не в заголовочном (*. h) и где правильнее?

        2. По незнанию всех функций Qt:

        // Почему так
        
        QString str = QFileDialog::getOpenFileName(this,
                                    "Выберите папку с изображениями",
                        QStandardPaths::locate(QStandardPaths::HomeLocation, QString()),
                                    "Изображения (*.jpg | *.bmp | *. png | *.tif)");
        
        // А не так
        
        QString str = QFileDialog::getOpenFileName(this,
                                    "Выберите папку с изображениями",
                        QDir::homePath(),
                                    "Изображения (*.jpg | *.bmp | *. png | *.tif)");
          Evgenii Legotckoi
          • 18. April 2017 08:42

          Если изображение 23 Мб весит, то тут да... тяжко будет открывать любому редактору.

          Думаю, что если разобраться с дополнительной обработкой, то можно будет найти какое-нибудь решение, которое ещё уменьшит нагрузку, например, убрать лишние действия, вполне возможно, если вы вернётесь например через пару недель к тому участку кода, то увидите свежим взглядом более элегантное и менее затратное решение, но работа с графикой в принципе накладный процесс сам по себе.

          Могу только посоветовать ещё порыть в направлении распараллеливания подгрузки изображений по ядрам процессора, то есть не просто в одном отдельном потоке всё делать, а делать в 4-х потоках, если процессор 4-х ядерный. Посмотрите в сторону QThreadPool . К сожалению, у меня статей на эту тему нет, но подумаю, может на теме изображений и скроллинга накидаю статью с QThreadPool

          Что касается прочих вопросов.

          1. Заголовочные файлы иных классов следует подключать в cpp файлах, только если эти классы не объявляются в заголовочном классе разрабатываемого файла. То есть Если QUrl не используется в объявлении класса или нет методов, которые его принимают или возвращают, но он используется в коде реализации, то заголовочный файл QUrl следует поместить в файл cpp. Это несколько увеличивает скорость компиляции, поскольку компилятору не требуется постоянно перепроверять, было ли что-то подключено в заголовочных файлах или нет. Нагрузка несколько снижается.

          2. QStandardPaths даёт более расширенный функционал по определению путей к ключевым директориям. Когда разбирался с этим под Андроид, то постоянно пользовался именно QStandardPaths . Почитайте документацию на этот класс, там много разных путей, а QDir ограничен только домашней директорией. Впрочем для использования домашней директории как раз хватает QDir::homePath() , но я просто изначально пользовался QStandardPaths , и даже внимания не обратил на данный метод при чтении документации, так что это из разряда "так получилось ". Так что спасибо, что обратили внимания на данный момент.

            Arrow
            • 18. April 2017 09:13

            Я тут подумал, а что если сделать загрузку изображения не в QLabel, а через прорисовку квадрата QRect и на нем при помощи QPainter рисовать изображение.

            Возможно даже взять и вырезать некоторые пиксели из него (как именно пока не знаю, но такое используется в редакторах изображений при уменьшении размера изображения).

            Или это гиблое дело и ничего выиграть не выйдет?

              Evgenii Legotckoi
              • 18. April 2017 09:28

              Вообще QRect - это просто геометрическая область, которая не имеет никакого понятия о рисовании. У него даже метода paintEvent() нет. Поэтому я не особо понимаю, что вы имели ввиду под этим. А так отрисовка что в QLabel , что в QWidget будет технически одинаково затратной, поскольку QLabel наследован от QWidget , да и техническая реализация будет ненамного больше отличаться, разве только у QWidget будете переопределять метод paintEvent() Да там реализовывать отрисовку изображения. Кода получится больше - это факт.

              Возможно, получится ускориться, если использовать QOpenGLWidget вместо QLabel. Тогда будет графическая подсистема задействоваться. Но это даст эффект только для отрисовки. А у вас ещё дополнительная обработка, которая ложится на CPU.

              Поэтому основные затраты у вас - это дополнительная обработка, вот она и влияет. А уменьшение количества пикселей и так скорее всего происходит во время выполнения метода scaledToWidth . Качество уж точно падает.

              Так что, наиболее эффективным сейчас будет - это внедрение многопоточности, то есть использование всех возможных процессоров на ПК. Именно дополнительную обработку нужно вынести в отдельные потоки.

                Arrow
                • 18. April 2017 09:54

                Спасибо! Попытаюсь реализовать для начала два потока, посмотрю что даст.

                  Arrow
                  • 19. April 2017 08:34

                  Вы пишите, что можно распараллелить подгрузку изображений по ядрам процессора при помощи QThreadPool. Таипа такого:

                  class ImageLoader : public QObject, public QRunnable
                  
                  //-----------------------------------
                  
                  ImageLoader *imgLoader = new ImageLoader();
                  
                      // Обработка изображений
                      connect(imgLoader, &ImageLoader::sendPixmap, this, &MainWindow::addImage);
                  
                      QThreadPool::globalInstance()->start(imgLoader);

                  Чем этот метод отличается от QThread:

                  class ImageLoader : public QObject
                  
                  //-------------------------------------------------------
                  
                     QThread* thread = new QThread;
                      ImageLoader *imgLoader = new ImageLoader();
                  
                      connect(thread, &QThread::started, imgLoader, &ImageLoader::process);
                  
                      connect(imgLoader, &ImageLoader::finished, thread, &QThread::terminate);
                  
                      connect(thread, &QThread::finished, imgLoader, &ImageLoader::deleteLater);
                  
                      connect(imgLoader, &ImageLoader::sendPixmap, this, &MainWindow::addImage);
                  
                      imgLoader->moveToThread(thread);
                      thread->start();

                  И что в данном случае более правильно использовать?

                    Evgenii Legotckoi
                    • 19. April 2017 12:47

                    Метод с QThreadPool отличается тем, что по умолчанию работает с количеством потоков равным количеству процессоров на ПК. Метода QThreadPool::idealThreadCount() позволяет узнать, сколько процессоров имеется в системе. В случае же с QThread, нужно будет следить за количеством процессоров и создавать необходимое количество этих потоков а потом удалять их. QThreadPool же делает это в автоматическом варианте.

                    Фактически нужно будет в зависимости от количества ядер процессора создать несколько объектов ImgLoader и поместить их в QThreadPool . Важным моментом для Вас будет то, что необходимо будет разделить весь список путей к файлам на количество процессоров. То есть, если берём 120 файлов, а количество процессоров(ядер) равно 4, то нужно будет создать 4 ImgLoader, каждый из которых будет обрабатывать по 30 файлов из этих 120. Ну и добавить немного логики, чтобы эти ImgLoader не передрались друг с другом, а также все изображения загружались в нужной последовательности.

                    В вашей задаче будет правильнее использовать QThreadPool, поскольку здесь не просто убираем в отдельные потоки всю логику по загрузке картинок, а ещё её нужно и распределить по потокам, чтобы ускорить процесс. Ну а вариант с одним QThread можно использовать для задач попроще, например, когда нужно проверять какое-то одно событие, которое имеет смысл обрабатывать в отдельном потоке. Зачастую такое необходимо, чтобы увязать с программой на Qt некие внешние SDK, которые ещё могут быть написаны и на Си.

                      Arrow
                      • 19. April 2017 15:09

                      Понял, спасибо!

                        Kommentare

                        Nur autorisierte Benutzer können Kommentare posten.
                        Bitte Anmelden oder Registrieren
                        Letzte Kommentare
                        A
                        ALO1ZE19. Oktober 2024 08:19
                        Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                        ИМ
                        Игорь Максимов5. Oktober 2024 07:51
                        Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                        d
                        dblas55. Juli 2024 11:02
                        QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        k
                        kmssr8. Februar 2024 18:43
                        Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        Jetzt im Forum diskutieren
                        J
                        JacobFib17. Oktober 2024 03:27
                        добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
                        JW
                        Jhon Wick1. Oktober 2024 15:52
                        Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
                        КГ
                        Кирилл Гусарев27. September 2024 09:09
                        Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
                        F
                        Fynjy22. Juli 2024 04:15
                        при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

                        Folgen Sie uns in sozialen Netzwerken