Подтвердить что ты не робот

Сравните подобие изображений с помощью OpenCV с Python

Я пытаюсь сравнить изображение с списком других изображений и возвращать выбранные изображения (например, изображения поиска Google) этого списка с до 70% сходства.

Я получаю этот код в этом сообщении и меняю свой контекст

# Load the images
img =cv2.imread(MEDIA_ROOT + "/uploads/imagerecognize/armchair.jpg")

# Convert them to grayscale
imgg =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# SURF extraction
surf = cv2.FeatureDetector_create("SURF")
surfDescriptorExtractor = cv2.DescriptorExtractor_create("SURF")
kp = surf.detect(imgg)
kp, descritors = surfDescriptorExtractor.compute(imgg,kp)

# Setting up samples and responses for kNN
samples = np.array(descritors)
responses = np.arange(len(kp),dtype = np.float32)

# kNN training
knn = cv2.KNearest()
knn.train(samples,responses)

modelImages = [MEDIA_ROOT + "/uploads/imagerecognize/1.jpg", MEDIA_ROOT + "/uploads/imagerecognize/2.jpg", MEDIA_ROOT + "/uploads/imagerecognize/3.jpg"]

for modelImage in modelImages:

    # Now loading a template image and searching for similar keypoints
    template = cv2.imread(modelImage)
    templateg= cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    keys = surf.detect(templateg)

    keys,desc = surfDescriptorExtractor.compute(templateg, keys)

    for h,des in enumerate(desc):
        des = np.array(des,np.float32).reshape((1,128))

        retval, results, neigh_resp, dists = knn.find_nearest(des,1)
        res,dist =  int(results[0][0]),dists[0][0]


        if dist<0.1: # draw matched keypoints in red color
            color = (0,0,255)

        else:  # draw unmatched in blue color
            #print dist
            color = (255,0,0)

        #Draw matched key points on original image
        x,y = kp[res].pt
        center = (int(x),int(y))
        cv2.circle(img,center,2,color,-1)

        #Draw matched key points on template image
        x,y = keys[h].pt
        center = (int(x),int(y))
        cv2.circle(template,center,2,color,-1)



    cv2.imshow('img',img)
    cv2.imshow('tm',template)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Мой вопрос: как я могу сравнить изображение со списком изображений и получить только похожие изображения? Есть ли способ сделать это?

4b9b3361

Ответ 1

Я предлагаю вам взглянуть на расстояние Земли (EMD) между изображениями. Эта метрика дает представление о том, как трудно преобразовать нормализованное изображение в оттенках серого в другое, но может быть обобщено для цветных изображений. Очень хороший анализ этого метода можно найти в следующей статье:

robotics.stanford.edu/~rubner/papers/rubnerIjcv00.pdf

Это можно сделать как на всем изображении, так и на гистограмме (которая действительно быстрее, чем весь метод изображения). Я не уверен в том, какой метод позволяет сопоставить полное изображение, но для сравнения гистограмм вы можете использовать функцию cv.CalcEMD2.

Единственная проблема заключается в том, что этот метод не определяет процент сходства, а расстояние, на которое вы можете фильтровать.

Я знаю, что это не полный рабочий алгоритм, но он все еще является основой для него, поэтому я надеюсь, что это поможет.

EDIT:

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

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

Эта матрица потока имеет три ограничения: каждый член должен быть положительным, сумма каждой строки должна возвращать одно и то же значение пикселя desitnation, а сумма каждого столбца должна возвращать значение исходного пикселя.

Учитывая это, вам нужно минимизировать стоимость преобразования, заданную суммой продуктов каждого потока от (i, j) до (k, l) для расстояния между (i, j) и (k, л).

Это выглядит немного сложным в словах, так что вот тестовый код. Логика правильная, я не уверен, почему scipy solver жалуется на это (вы должны смотреть, может быть, на openOpt или что-то подобное):

#original data, two 2x2 images, normalized
x = rand(2,2)
x/=sum(x)
y = rand(2,2)
y/=sum(y)

#initial guess of the flux matrix
# just the product of the image x as row for the image y as column
#This is a working flux, but is not an optimal one
F = (y.flatten()*x.flatten().reshape((y.size,-1))).flatten()

#distance matrix, based on euclidean distance
row_x,col_x = meshgrid(range(x.shape[0]),range(x.shape[1]))
row_y,col_y = meshgrid(range(y.shape[0]),range(y.shape[1]))
rows = ((row_x.flatten().reshape((row_x.size,-1)) - row_y.flatten().reshape((-1,row_x.size)))**2)
cols = ((col_x.flatten().reshape((row_x.size,-1)) - col_y.flatten().reshape((-1,row_x.size)))**2)
D = np.sqrt(rows+cols)

D = D.flatten()
x = x.flatten()
y = y.flatten()
#COST=sum(F*D)

#cost function
fun = lambda F: sum(F*D)
jac = lambda F: D
#array of constraint
#the constraint of sum one is implicit given the later constraints
cons  = []
#each row and columns should sum to the value of the start and destination array
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[i,:])-x[i]}     for i in range(x.size) ]
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[:,i])-y[i]} for i in range(y.size) ]
#the values of F should be positive
bnds = (0, None)*F.size

from scipy.optimize import minimize
res = minimize(fun=fun, x0=F, method='SLSQP', jac=jac, bounds=bnds, constraints=cons)

переменная res содержит результат минимизации... но, как я уже сказал, я не уверен, почему она жалуется на сингулярную матрицу.

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

Ответ 2

Я написал программу, чтобы сделать что-то очень похожее, возможно, 2 года назад, используя Python/Cython. Позже я переписал его, чтобы пойти, чтобы получить лучшую производительность. Основная идея исходит из findimagedupes IIRC.

Он в основном вычисляет "отпечаток пальца" для каждого изображения, а затем сравнивает эти отпечатки пальцев для соответствия аналогичным изображениям.

Отпечаток пальца создается путем изменения размера изображения до 160x160, преобразования его в оттенки серого, добавления некоторого размытия, его нормализации, а затем изменения размера до 16x16 монохромного. В конце у вас есть 256 бит вывода: это ваш отпечаток. Это очень легко сделать, используя convert:

convert path[0] -sample 160x160! -modulate 100,0 -blur 3x99 \
    -normalize -equalize -sample 16x16 -threshold 50% -monochrome mono:-

([0] в path[0] используется только для извлечения первого кадра анимированных GIF файлов, если вас это не интересует, вы можете просто удалить его.)

После применения этого к 2 изображениям у вас будет 2 (256-битных) отпечатка пальца, fp1 и fp2.

Оценка сходства этих двух изображений затем вычисляется посредством XORing этих двух значений и подсчета битов, установленных в 1. Для выполнения этого подсчета бит вы можете использовать функцию bitsoncount() из этот ответ:

# fp1 and fp2 are stored as lists of 8 (32-bit) integers
score = 0
for n in range(8):
    score += bitsoncount(fp1[n] ^ fp2[n])

score будет числом от 0 до 256, указывающим, насколько похожи ваши изображения. В моем приложении я делю его на 2.56 (нормализуется до 0-100), и я обнаружил, что изображения с нормализованным счетом 20 или менее часто идентичны.

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

Мне очень жаль, но я больше не могу найти свой код Python. Сейчас у меня только версия Go, но, боюсь, я не могу опубликовать ее здесь (плотно интегрирован в какой-то другой код и, вероятно, немного уродлив, так как это была моя первая серьезная программа в Go...).

В GQView/Geeqie также очень хорошая функция "найти по подобию"; его источник здесь.

Ответ 3

Вы начинаете серьезную проблему, называемую "поиск изображений на основе контента", или CBIR. Это массивное и активное поле. До сих пор нет готовых алгоритмов или стандартных подходов, хотя есть много методов, которые имеют разные уровни успеха.

Даже поиск изображений Google не делает этого (пока) - они выполняют поиск на основе текстовых изображений - например, поиск текста на странице, подобной тексту, который вы искали. (И я уверен, что они работают над использованием CBIR, это святой грааль для многих исследователей по обработке изображений)

Если у вас ограниченный срок или нужно сделать это и скоро работать... yikes.

Вот тонна статей по теме:

http://scholar.google.com/scholar?q=content+based+image+retrieval

Как правило, вам нужно сделать несколько вещей:

  • Извлечь функции (либо в локальные точки интереса, либо в глобальном масштабе, либо как-то SIFT, SURF, гистограммы и т.д.).
  • Кластер/построение модели распределений изображений

Это может включать в себя дескрипторы функций, gists, обучение нескольких экземпляров. и др.