Arrow
ArrowСәуір 17, 2017, 5: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
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

11
Evgenii Legotckoi
  • Сәуір 17, 2017, 7:03 Т.Ж.

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

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

    Evgenii Legotckoi
    • Сәуір 18, 2017, 1:19 Т.Ж.

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

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

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

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

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

      Evgenii Legotckoi
      • Сәуір 18, 2017, 1:24 Т.Ж.
      • Жауап шешім ретінде белгіленді.

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

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

        Arrow
        • Сәуір 18, 2017, 8: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, 2017, 8:42 Т.Ж.

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

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

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

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

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

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

            Arrow
            • Сәуір 18, 2017, 9:13 Т.Ж.

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

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

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

              Evgenii Legotckoi
              • Сәуір 18, 2017, 9:28 Т.Ж.

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

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

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

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

                Arrow
                • Сәуір 18, 2017, 9:54 Т.Ж.

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

                  Arrow
                  • Сәуір 19, 2017, 8: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, 2017, 12:47 Т.Қ.

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

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

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

                      Arrow
                      • Сәуір 19, 2017, 3:09 Т.Қ.

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

                        Пікірлер

                        Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
                        Кіріңіз немесе Тіркеліңіз
                        Г

                        C++ - Тест 001. Первая программа и типы данных

                        • Нәтиже:66ұпай,
                        • Бағалау ұпайлары-1
                        t

                        C++ - Тест 001. Первая программа и типы данных

                        • Нәтиже:33ұпай,
                        • Бағалау ұпайлары-10
                        t

                        Qt - Тест 001. Сигналы и слоты

                        • Нәтиже:52ұпай,
                        • Бағалау ұпайлары-4
                        Соңғы пікірлер
                        G
                        GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
                        Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
                        d
                        dblas5Шілде 5, 2024, 11:02 Т.Ж.
                        QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        k
                        kmssrАқп. 8, 2024, 6:43 Т.Қ.
                        Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        АК
                        Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
                        Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        Енді форумда талқылаңыз
                        Evgenii Legotckoi
                        Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
                        добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                        F
                        FynjyШілде 22, 2024, 4:15 Т.Ж.
                        при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
                        BlinCT
                        BlinCTМаусым 25, 2024, 1 Т.Ж.
                        Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
                        BlinCT
                        BlinCTМамыр 5, 2024, 5:46 Т.Ж.
                        Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
                        Evgenii Legotckoi
                        Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
                        Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

                        Бізді әлеуметтік желілерде бақылаңыз