Более эффективный способ сравнения изображений в Python

Проблема:
У меня есть около 10 000 изображений для сравнения друг с другом. Моя текущая программа сравнивает около 60 изображений каждую секунду, но при такой скорости для завершения потребуется почти 9 дней. Я пытался использовать С++, но окончательный код занял бы почти в 3 раза больше времени, чем код Python.

Вопрос:
Есть ли более быстрый или эффективный способ сравнения изображений? Я в порядке с использованием других языков и других библиотек.

Код:

from PIL import Image
from PIL import ImageChops
import math, operator
from functools import reduce
import os

def rmsdiff(image_1, image_2):
    h = ImageChops.difference(image_1, image_2).histogram()
    return math.sqrt(reduce(operator.add, map(lambda h, i: i%256*(h**2), h, range(len(h)))) / (float(image_1.size[0]) * image_1.size[1]))

current = 0
try:
    dire = "C:\\Users\\Nikola\\Downloads\\photos"
    photos = os.listdir(dire)
    for idx, val in enumerate(photos):
        if val == "":
            start = idx
            break
    for photo_1 in range(start,len(photos)):
        if "." not in photos[photo_1]:
            continue
        print(f'Image: {photos[photo_1]}')
        with Image.open(dire+"\\"+photos[photo_1]) as image_1:
            image_1 = image_1.resize((16,16))
            for photo_2 in range(photo_1+1, len(photos)):
                current = photos[photo_2]
                try:
                    if photos[photo_2][-4] != "." and photos[photo_2][-5] != ".":
                        continue
                except:
                    continue
                with Image.open(dire+"\\"+photos[photo_2]) as image_2:
                    image_2 = image_2.resize((16,16))
                    try:
                        value = rmsdiff(image_1, image_2)
                        if value < 12:
                            print(f'Similar Image: {photos[photo_1]}')
                            continue
                    except:
                        pass
except KeyboardInterrupt:
    print()
    print(current)

С чем вы их сравниваете? вы просто проверяете, идентичны ли они или какую эвристику вы используете для сравнения

Alexander 01.01.2023 11:20

Чего вы на самом деле пытаетесь достичь? Что вы знаете об изображениях — все ли они одного размера? Тот же формат? Тот же предмет? Что вы знаете об используемой вами машине — многоядерный процессор? Быстрые диски NVMe? Много оперативной памяти?

Mark Setchell 01.01.2023 11:29

@ Александр Я сравниваю каждое изображение с другим изображением, которое может быть изменено, размыто, на изображении могут быть артефакты. Оно должно быть достаточно похожим, чтобы в изображении было небольшое количество изменений.

nikola markoski 01.01.2023 11:33

@MarkSetchell Я пытаюсь понять, все ли изображения в определенной степени уникальны. Все изображения имеют разные размеры и форматы, но большинство из них относятся к одной и той же теме. Я не думаю, что мой ноутбук имеет несколько процессоров, у него нет диска NVMe и 8 ГБ ОЗУ.

nikola markoski 01.01.2023 11:41

Вы, кажется, сравниваете каждое изображение со всеми теми, которые следуют за ним в списке каталогов. Это означает, что последнее изображение будет открыто и изменено до размера 16x16 в общей сложности почти 10 000 раз. Если вы сравниваете только версии 16x16, у вас достаточно оперативной памяти для одновременного хранения всех изображений, измененных до 16x16, поэтому вы открываете их и изменяете размер только один раз.

Mark Setchell 01.01.2023 11:48

Возможно, вы могли бы использовать functools.lru_cache(), чтобы украсить функцию, которая открывается и изменяет размер до 16x16, если вы не хотите сильно менять свой алгоритм... docs.python.org/3/library/functools.html

Mark Setchell 01.01.2023 11:52

Меня не слишком беспокоит изменение моего кода. Если нужно, я поменяю языки.

nikola markoski 01.01.2023 11:55

Если ваши изображения в формате JPEG, вам обязательно следует использовать функцию «сжатие при загрузке», также известную как «черновой» режим в PIL, потому что вы изменяете размер очень маленьким stackoverflow.com/a/57717879/2836621

Mark Setchell 01.01.2023 11:55

Что вы имеете в виду под "сравнить"? Каково фактическое правило, которое необходимо реализовать? Если у меня есть несколько изображений в качестве входных данных для этого кода, как я могу вообще знать, что я должен ожидать в качестве вывода?

Karl Knechtel 01.01.2023 13:32

вы просто спрашиваете о микрооптимизации, что здесь неправильно. вам нужно уменьшить сложность всей операции. en.wikipedia.org/wiki/Content-based_image_retrieval — уменьшите пространство поиска, даже не рассматривая пары, которые явно не похожи. используйте некоторый тип хэширования «контента». ИИ может помочь (отрежьте классификационный слой, вы получите векторный слой). есть еще "перцептивное хеширование"

Christoph Rackwitz 02.01.2023 15:09

@ChristophRackwitz Да, я прошу оптимизировать мой код. Я не сказал, что это исходный вопрос, потому что я не думал, что это имеет значение, но я вручную отсортировал около 40000 изображений по 8 различным категориям. Так что я уже сократил время выполнения с ~ 5 месяцев до 9-10 дней.

nikola markoski 05.01.2023 20:04

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

Christoph Rackwitz 06.01.2023 19:48

@ChristophRackwitz Я изучил, что такое перцептивное хеширование. Я внедрил его в свой код, ожидая, что он будет медленнее. Первые несколько запусков были медленнее, потому что я не откалибровал их должным образом, но после калибровки хэширование стало в 10 раз быстрее, и я нашел больше похожих изображений. Спасибо

nikola markoski 07.01.2023 09:35
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
13
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Однако ваша проблема довольно странная. Тот факт, что вам нужно читать сами данные для сравнения, не должен происходить в большинстве случаев, и было бы разумно, если бы у вас были некоторые метаданные для сравнения.


Тем не менее, вот несколько очень разных подходов к ускорению этого.

  1. Вы можете распараллелить код, чтобы добиться ускорения количества ваших ядер.
  2. Вы можете изменить rmse на простое значение abs(diff) или другую более дешевую функцию расстояния. чтобы сэкономить много времени выполнения вычислений.
  3. Вы можете написать свой собственный метод diff, чтобы прекратить вычисления при прохождении некоторого порога diff. Это требует, чтобы функция была скомпилирована или, по крайней мере, вовремя скомпилирована.
  4. Если бы вы могли предварительно рассчитать некоторое уменьшение размерности для каждого изображения, вы могли бы выполнить сравнение в более низком измерении. Например, просуммируйте строки и получите столбец сумм для каждого изображения. Сравните этот столбец, а не все изображение. Затем рассчитывайте все изображение только для изображений, которые вышли с аналогичными представлениями меньшего размера.
  5. Если многие ваши изображения одинаковы, вы можете сгруппировать их, тогда при сравнении с любым изображением в группе вам не придется делать это снова для всех остальных изображений в группе.
  6. Быстрое ускорение, вероятно, можно получить, правильно используя cdist, вычисляя все-все-все расстояния
Ответ принят как подходящий

Следуя моим комментариям, я предполагаю, что загрузка и изменение размера занимают больше всего времени, поэтому я бы стремился к оптимизации.

В настоящее время у меня нет доступного интерпретатора Python для правильного тестирования, но в следующем порядке:

from functools import lru_cache

@lru_cache(maxsize=None)
def loadImage(filename)
    im = Image.open(filename)
    im = im.resize((16,16))
    return im

Это уже должно иметь огромное значение. Затем настройте режим «черновик», например:

    im = Image.open(filename)
    im.draft('RGB',(32,32))
    im = im.resize((16,16)
    return im

Вы также можете использовать многопоточную загрузку, если ваш ноутбук имеет приличный процессор.

Я хотел бы проголосовать за этот ответ, но мне нужно более 15 повторений

nikola markoski 01.01.2023 21:28

Другие вопросы по теме