Каков наилучший способ получить хэш QPixmap?

Я разрабатываю графическое приложение с использованием Qt 4.5 и помещаю изображения в QPixmapCache, я хотел оптимизировать это так, чтобы, если пользователь вставляет изображение, которое уже находится в кеше, он использовал его.

Прямо сейчас каждое изображение имеет уникальный идентификатор, который помогает оптимизировать себя при рисовании. Однако я понимаю, что если бы я мог вычислить хэш изображения, я мог бы найти кеш, чтобы увидеть, существует ли он уже, и использовать его (конечно, это помогло бы больше для повторяющихся объектов).

Моя проблема в том, что если это большой QPixmap, будет ли вычисление хэша замедлять работу или есть более быстрый способ?


person Phil Hannent    schedule 05.03.2009    source источник
comment
опечатка в заголовке (может какая...) :)   -  person claf    schedule 05.03.2009


Ответы (4)


Пара комментариев по этому поводу:

  1. Если вы собираетесь генерировать ключ хеша/кэша растрового изображения, вы можете пропустить QPixmapCache и использовать QCache напрямую. Это устранило бы некоторые накладные расходы на использование QString в качестве ключей (если вы также не хотите использовать путь к файлу для поиска элементов).

  2. Начиная с Qt4.4, QPixmap имеет связанное с ним значение "хэш" (см. QPixmap::cacheKey() ). В документации утверждается, что «Отдельные объекты QPixmap могут иметь один и тот же ключ кэша, только если они ссылаются на одно и то же содержимое». Однако, поскольку Qt использует совместное копирование данных, это может применяться только к скопированным растровым изображениям, а не к двум различным растровым изображениям, загруженным из одного и того же изображения. Небольшое тестирование покажет вам, работает ли это, и если да, то позволит вам легко получить значение хеш-функции.

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

  4. Производительность: не забывайте о бенчмарках, добавленных в Qt в версии 4.5, которые позволят вам сравнить ваши различные идеи хеширования и посмотреть, какой из них работает быстрее всего. Я еще не проверял, но выглядит довольно аккуратно.

person Caleb Huitt - cjhuitt    schedule 05.03.2009

На всякий случай, если кто-то столкнется с этой проблемой (и не слишком сильно разбирается в хэшировании, особенно что-то вроде изображения), вот ОЧЕНЬ простое решение, которое я использовал для хеширования QPixmaps и ввода их в таблицу поиска для последующего сравнения:

qint32 HashClass::hashPixmap(QPixmap pix)
{
    QImage image = pix.toImage();
    qint32 hash = 0;

    for(int y = 0; y < image.height(); y++)
    {
        for(int x = 0; x < image.width(); x++)
        {
            QRgb pixel = image.pixel(x,y);

            hash += pixel;
            hash += (hash << 10);
            hash ^= (hash >> 6);
        }
    }

    return hash;
}

Вот сама хеш-функция (вы можете хэшировать ее в qint64, если хотите меньше коллизий). Как видите, я конвертирую растровое изображение в QImage, просто просматриваю его размеры и выполняю очень простое хеширование по одному для каждого пикселя и возвращаю окончательный результат. Есть много способов улучшить эту реализацию (см. другие ответы на этот вопрос), но это основная суть того, что нужно сделать.

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

void HashClass::initializeImageLookupTable()
{
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path1.png")), "ImageKey1");
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path2.png")), "ImageKey2");
    imageTable.insert(hashPixmap(QPixmap(":/Image_Path3.png")), "ImageKey2");
// Etc...
}

Я использую здесь QMap с именем imageTable, который необходимо объявить в классе как таковой:

QMap<qint32, QString> imageTable;

Затем, наконец, когда вы хотите сравнить изображение с изображениями в вашей таблице поиска (то есть: «каким изображением из известных мне изображений может быть это конкретное изображение?»), вы просто вызываете функцию хеширования на изображение (которое, как я предполагаю, также будет QPixmap), и возвращаемое значение QString позволит вам понять это. Что-то вроде этого будет работать:

void HashClass::compareImage(const QPixmap& pixmap)
{
    QString value = imageTable[hashPixmap(pixmap)];
    // Do whatever needs to be done with the QString value and pixmap after this point.
}

Вот и все. Я надеюсь, что это поможет кому-то - это сэкономило бы мне время, хотя я был счастлив, что разобрался с этим.

person Community    schedule 22.08.2012

Вычисления хэшей должны быть довольно быстрыми (где-то выше 100 МБ/с, если дисковый ввод-вывод не задействован) в зависимости от того, какой алгоритм вы используете. Перед хешированием вы также можете выполнить несколько быстрых тестов, чтобы отсортировать потенциальных кандидатов, например. изображения должны иметь одинаковую ширину и высоту, иначе бесполезно сравнивать их хеш-значения.

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

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

person schnaader    schedule 05.03.2009

Я предполагаю, что вы говорите о фактическом вычислении хэша данных изображения, а не о получении уникального идентификатора, сгенерированного QT.
В зависимости от ваших изображений вам, вероятно, не нужно просматривать все изображение, чтобы вычислить хэш. Может быть, только прочитать первые 10 пикселей? первая строка сканирования?
Может, псевдослучайный выбор пикселей из всего изображения? (с известным начальным числом, чтобы вы могли повторить последовательность) Не забудьте также добавить размер изображения к хэшу.

person shoosh    schedule 05.03.2009