Оптимизация загрузки изображений
Оптимизация загрузки изображений, 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
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.Вам це подобається? Поділіться в соціальних мережах!
- Akiv Doros
- 11 листопада 2024 р. 14:58
C++ - Тест 004. Указатели, Массивы и Циклы
- Результат:50бали,
- Рейтинг балів-4
- molni99
- 26 жовтня 2024 р. 01:37
C++ - Тест 004. Указатели, Массивы и Циклы
- Результат:80бали,
- Рейтинг балів4
- molni99
- 26 жовтня 2024 р. 01:29
C++ - Тест 004. Указатели, Массивы и Циклы
- Результат:20бали,
- Рейтинг балів-10
qCompress однозначно использовать нельзя, поскольку он предназначен просто для компрессии сырых байтов без учёта какого-либо алгоритма организации данных, в данном случае он игнорирует структуру организации данных изображений. Поэтому от такого способа оптимизации сразу отказываемся. Этот инструмент для этого не предназначен.
Что касается остальной части вопроса, то нужно подумать. Вечером может какие мысли появятся, ну и или другие пользователи форума может что подскажут.
А какого вообще размера изображения?
Вы работаете с изображениями, поэтому программа в любом случае будет потреблять большие объёмы памяти, особенно, если это фотографии в высоком качестве, поэтому здесь только смириться, или уменьшать размер фото, вместе с качеством фото.
К слову говоря, метод QPixmap::scaledToWidth автоматически уменьшает размер изображения и его качество, думаю, что на потреблении памяти это тоже сказывается, поскольку необходимо держать меньшее количество пикселей в памяти. В принципе вопрос потребления памяти можно несколько нивелировать, если хранить в памяти не оригиналы изображения, а пути к изображению, а в случае изменения размеров каждый погружать изображение из этого пути и масштабировать. Хотя, конечно, тут могут появиться задержки при открытии файла. Что касается подгрузки изображений. Вы замечали, что в файловых менеджерах превью изображений сначала выглядит как обычная иконка, а потом только погружается как положено. Можете применить данный способ отображения. То есть заполнить всё сначала некоторыми иконками, которые подразумевают изображение, а потом уже заполнять картинку, когда пользователь прокрутит. Это несколько сгладит когнитивный диссонанс у пользователя.
Тем более, если вернуться к просмоторщикам, тот же самый встроенный просмоторщик pdf в Linux тоже не отличается идеальной скоростью при открытии очень больших pdf. Так что некоторые задержки будут всегда, особенно на слабом железе.
Но если у вас программа падает или виснет всего на 20 изображениях, то это говорит о проблемах в логике кода. То есть это скорее не вопрос оптимизации, как я полагаю. Например, в одном из наших обсуждений по этой тематике, я же набросал пробную программку со скроллингом изображений. Она вполне неплохо открывает 200-300 фотографий и не виснет. Хотя я понимаю, что у вас логика уже явно сложнее, но это лишь говорит о том, что где-то, что-то вы делаете не так. Да, в моём варианте видно, что фотографии грузятся, а не всё сразу отображается, но для этого и служат всякие прогресс бары, чтобы показываться процесс загрузки. Поэтому подумайте, над тем, что я написал. Задача скорее не в том, чтобы оптимизировать, а в том, чтобы найти ошибку, поскольку 20 изображений - это не такая большая нагрузка, чтобы повешать приложение на современно ПК, а открываться эти 20 изображении будут секунды, не более.
Вот, прикладываю мою пробную программку, просто посмотрите как она работает на большом фото архиве. Где фотографий 300 будет. Там кнопка на открытие диалога, в диалоге переходите в каталог с фотками и выделяете все фотографии.
Посмотрите, как будет грузиться. Это вполне приемлемый по скорости процесс загрузки.
Спасибо!
У меня грузит с такой же скоростью.
Изображение: 2400х3400 (23,5 МБт).
У меня происходит дополнительная обработка изображений и она как оказалось и дает нагрузку.
Похоже от этого не уйти никак, придется смириться.
Если показывать подставную картинку выходит очень даже ничего. :)
Пара вопросов по вашему коду:
1. Из-за любопытства (чисто для себя)- Почему вы некоторые заголовочные файлы подключаете в файле реализации (*.cpp), а не в заголовочном (*. h) и где правильнее?
2. По незнанию всех функций Qt:
Если изображение 23 Мб весит, то тут да... тяжко будет открывать любому редактору.
Думаю, что если разобраться с дополнительной обработкой, то можно будет найти какое-нибудь решение, которое ещё уменьшит нагрузку, например, убрать лишние действия, вполне возможно, если вы вернётесь например через пару недель к тому участку кода, то увидите свежим взглядом более элегантное и менее затратное решение, но работа с графикой в принципе накладный процесс сам по себе.
Могу только посоветовать ещё порыть в направлении распараллеливания подгрузки изображений по ядрам процессора, то есть не просто в одном отдельном потоке всё делать, а делать в 4-х потоках, если процессор 4-х ядерный. Посмотрите в сторону QThreadPool . К сожалению, у меня статей на эту тему нет, но подумаю, может на теме изображений и скроллинга накидаю статью с QThreadPool
Что касается прочих вопросов.
1. Заголовочные файлы иных классов следует подключать в cpp файлах, только если эти классы не объявляются в заголовочном классе разрабатываемого файла. То есть Если QUrl не используется в объявлении класса или нет методов, которые его принимают или возвращают, но он используется в коде реализации, то заголовочный файл QUrl следует поместить в файл cpp. Это несколько увеличивает скорость компиляции, поскольку компилятору не требуется постоянно перепроверять, было ли что-то подключено в заголовочных файлах или нет. Нагрузка несколько снижается.
2. QStandardPaths даёт более расширенный функционал по определению путей к ключевым директориям. Когда разбирался с этим под Андроид, то постоянно пользовался именно QStandardPaths . Почитайте документацию на этот класс, там много разных путей, а QDir ограничен только домашней директорией. Впрочем для использования домашней директории как раз хватает QDir::homePath() , но я просто изначально пользовался QStandardPaths , и даже внимания не обратил на данный метод при чтении документации, так что это из разряда "так получилось ". Так что спасибо, что обратили внимания на данный момент.
Я тут подумал, а что если сделать загрузку изображения не в QLabel, а через прорисовку квадрата QRect и на нем при помощи QPainter рисовать изображение.
Возможно даже взять и вырезать некоторые пиксели из него (как именно пока не знаю, но такое используется в редакторах изображений при уменьшении размера изображения).
Или это гиблое дело и ничего выиграть не выйдет?
Вообще QRect - это просто геометрическая область, которая не имеет никакого понятия о рисовании. У него даже метода paintEvent() нет. Поэтому я не особо понимаю, что вы имели ввиду под этим. А так отрисовка что в QLabel , что в QWidget будет технически одинаково затратной, поскольку QLabel наследован от QWidget , да и техническая реализация будет ненамного больше отличаться, разве только у QWidget будете переопределять метод paintEvent() Да там реализовывать отрисовку изображения. Кода получится больше - это факт.
Возможно, получится ускориться, если использовать QOpenGLWidget вместо QLabel. Тогда будет графическая подсистема задействоваться. Но это даст эффект только для отрисовки. А у вас ещё дополнительная обработка, которая ложится на CPU.
Поэтому основные затраты у вас - это дополнительная обработка, вот она и влияет. А уменьшение количества пикселей и так скорее всего происходит во время выполнения метода scaledToWidth . Качество уж точно падает.
Так что, наиболее эффективным сейчас будет - это внедрение многопоточности, то есть использование всех возможных процессоров на ПК. Именно дополнительную обработку нужно вынести в отдельные потоки.
Спасибо! Попытаюсь реализовать для начала два потока, посмотрю что даст.
Вы пишите, что можно распараллелить подгрузку изображений по ядрам процессора при помощи QThreadPool. Таипа такого:
Чем этот метод отличается от QThread:
И что в данном случае более правильно использовать?
Метод с QThreadPool отличается тем, что по умолчанию работает с количеством потоков равным количеству процессоров на ПК. Метода QThreadPool::idealThreadCount() позволяет узнать, сколько процессоров имеется в системе. В случае же с QThread, нужно будет следить за количеством процессоров и создавать необходимое количество этих потоков а потом удалять их. QThreadPool же делает это в автоматическом варианте.
Фактически нужно будет в зависимости от количества ядер процессора создать несколько объектов ImgLoader и поместить их в QThreadPool . Важным моментом для Вас будет то, что необходимо будет разделить весь список путей к файлам на количество процессоров. То есть, если берём 120 файлов, а количество процессоров(ядер) равно 4, то нужно будет создать 4 ImgLoader, каждый из которых будет обрабатывать по 30 файлов из этих 120. Ну и добавить немного логики, чтобы эти ImgLoader не передрались друг с другом, а также все изображения загружались в нужной последовательности.
В вашей задаче будет правильнее использовать QThreadPool, поскольку здесь не просто убираем в отдельные потоки всю логику по загрузке картинок, а ещё её нужно и распределить по потокам, чтобы ускорить процесс. Ну а вариант с одним QThread можно использовать для задач попроще, например, когда нужно проверять какое-то одно событие, которое имеет смысл обрабатывать в отдельном потоке. Зачастую такое необходимо, чтобы увязать с программой на Qt некие внешние SDK, которые ещё могут быть написаны и на Си.
Понял, спасибо!