Algorytm wyszukiwania podobnych obrazów

83

Potrzebuję algorytmu, który określi, czy dwa obrazy są „podobne” i rozpozna podobne wzorce koloru, jasności, kształtu itp. Mogę potrzebować wskazówek, jakich parametrów używa mózg ludzki do „kategoryzowania” obrazów. ..

Patrzyłem na dopasowywanie oparte na hausdorffie, ale wydaje się, że jest to głównie do dopasowywania przekształconych obiektów i wzorów kształtów.

kitsune
źródło
Jest kilka dobrych odpowiedzi na to inne podobne pytanie: stackoverflow.com/questions/25977/ ...
blak
2
wiele „puszek” i „potęg”. Czy ktoś wypróbuje wszystkie te sugestie i wie, co jest najlepsze?
john ktejik

Odpowiedzi:

60

Zrobiłem coś podobnego, rozkładając obrazy na sygnatury za pomocą transformacji falkowej .

Moje podejście polegało na wybraniu najbardziej znaczących współczynników n z każdego przekształconego kanału i zarejestrowaniu ich lokalizacji. Dokonano tego poprzez sortowanie listy krotek (moc, lokalizacja) według abs (moc). Podobne obrazy będą miały podobieństwa, ponieważ będą miały znaczące współczynniki w tych samych miejscach.

Stwierdziłem, że najlepiej jest przekształcić obraz w format YUV, który skutecznie pozwala na podobieństwo wagi w kształcie (kanał Y) i kolorze (kanały UV).

Możesz znaleźć moją implementację powyższego w mactorii , nad którą niestety nie pracowałem tak dużo, jak powinienem :-)

Inną metodą, której niektórzy moi przyjaciele używali z zaskakująco dobrymi wynikami, jest po prostu zmiana rozmiaru obrazu, powiedzmy, piksel 4x4 i zapisanie, które są Twoją sygnaturą. Jak podobne są dwa obrazy, można ocenić, na przykład obliczając odległość Manhattanu między dwoma obrazami przy użyciu odpowiednich pikseli. Nie mam szczegółów, w jaki sposób wykonali zmianę rozmiaru, więc być może będziesz musiał zagrać z różnymi algorytmami dostępnymi dla tego zadania, aby znaleźć odpowiedni.

wolna przestrzeń
źródło
7
Zmiana rozmiaru na 4x4 to świetny pomysł (nie żeby twoja metoda też nie była świetna), ale pierwsza jest prostsza.
Alix Axel
@freespace, czy mógłbyś wyjaśnić to „obliczanie odległości Manhattanu między dwoma obrazami przy użyciu odpowiednich pikseli”
Ambika
1
@Ambika: potraktuj kolor każdego piksela jako wektor o długości 3 i oblicz odległość Manhattanu między odpowiadającymi mu pikselami w porównywanych obrazach. To daje 4 odległości Manhattanu. To, w jaki sposób uzyskasz z tego jedną miarę, zależy od ciebie. Najbardziej oczywiste jest podsumowanie ich razem.
wolna przestrzeń
45

pHash może Cię zainteresować.

percepcyjne hash n. odcisk palca pliku audio, wideo lub obrazu, który jest matematycznie oparty na zawartej w nim treści audio lub wizualnej. W przeciwieństwie do kryptograficznych funkcji skrótu, które opierają się na lawinowym efekcie niewielkich zmian danych wejściowych prowadzących do drastycznych zmian w danych wyjściowych, skróty percepcyjne są „blisko” siebie, jeśli dane wejściowe są wizualnie lub audytucyjnie podobne.

Alvis
źródło
8
Właśnie sprawdziłem stronę pHash. Obecnie mają w swojej witrynie tę funkcję, która pozwala przesłać dwa obrazy i informuje, czy są podobne, czy nie. Wypróbowałem około 10 obrazów, które były podobne i 10, które nie były. Wskaźnik sukcesu nie był niestety imponujący.
rodrigo-silveira
2
pHash jest w rzeczywistości dość surowe, możesz chcieć użyć „ahash” lub średniej wartości hash, która jest mniej rygorystyczna. Możesz znaleźć implementację Pythona tutaj github.com/JohannesBuchner/imagehash/blob/master/…
Rohit
13

Użyłem SIFT do ponownego wykrycia tego samego obiektu na różnych obrazach. Jest naprawdę potężny, ale raczej złożony i może być przesadą. Jeśli obrazy mają być bardzo podobne, niektóre proste parametry oparte na różnicy między dwoma obrazami mogą ci wiele powiedzieć. Kilka wskazówek:

  • Normalizuj obrazy, tj. Spraw, aby średnia jasność obu obrazów była taka sama, obliczając średnią jasność obu i skalując najjaśniejszy w dół zgodnie z racją (aby uniknąć przycinania na najwyższym poziomie)), zwłaszcza jeśli bardziej interesuje Cię kształt niż kolor.
  • Suma różnicy kolorów w stosunku do znormalizowanego obrazu na kanał.
  • znajdź krawędzie na obrazach i zmierz odległość między pikselami krawędzi na obu obrazach. (dla kształtu)
  • Podziel obrazy na zestaw dyskretnych regionów i porównaj średni kolor każdego regionu.
  • Ustaw próg obrazów na jednym (lub zestawie) poziomie (poziomach) i policz liczbę pikseli, w których wynikowe obrazy czarno-białe różnią się.
jilles de wit
źródło
Czy możesz wskazać kod, który używa funkcji typu sitowego do obliczania podobieństwa obrazu?
mrgloom
Przepraszam, jestem pewien, że istnieje publicznie dostępny kod, ale nie znam go. Na tej stronie jest kilka przykładów. Na przykład tutaj: stackoverflow.com/questions/5461148/…
jilles de wit
Accord Framework dla .Net ( accord-framework.net ) ma kilka świetnych klas do wykonywania SURF, BagOfVisualWords, Harris Corner Detection, itp. Z mnóstwem różnych jąder i algorytmów klastrowania.
dynamichael
6

Moje laboratorium również musiało rozwiązać ten problem i użyliśmy Tensorflow. Oto pełna implementacja aplikacji do wizualizacji podobieństwa obrazów.

Aby zapoznać się z samouczkiem na temat wektoryzacji obrazów w celu obliczenia podobieństwa, odwiedź tę stronę . Oto Python (ponownie, zobacz post z pełnym przepływem pracy):

from __future__ import absolute_import, division, print_function

"""

This is a modification of the classify_images.py
script in Tensorflow. The original script produces
string labels for input images (e.g. you input a picture
of a cat and the script returns the string "cat"); this
modification reads in a directory of images and 
generates a vector representation of the image using
the penultimate layer of neural network weights.

Usage: python classify_images.py "../image_dir/*.jpg"

"""

# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

"""Simple image classification with Inception.

Run image classification with Inception trained on ImageNet 2012 Challenge data
set.

This program creates a graph from a saved GraphDef protocol buffer,
and runs inference on an input JPEG image. It outputs human readable
strings of the top 5 predictions along with their probabilities.

Change the --image_file argument to any jpg image to compute a
classification of that image.

Please see the tutorial and website for a detailed description of how
to use this script to perform image recognition.

https://tensorflow.org/tutorials/image_recognition/
"""

import os.path
import re
import sys
import tarfile
import glob
import json
import psutil
from collections import defaultdict
import numpy as np
from six.moves import urllib
import tensorflow as tf

FLAGS = tf.app.flags.FLAGS

# classify_image_graph_def.pb:
#   Binary representation of the GraphDef protocol buffer.
# imagenet_synset_to_human_label_map.txt:
#   Map from synset ID to a human readable string.
# imagenet_2012_challenge_label_map_proto.pbtxt:
#   Text representation of a protocol buffer mapping a label to synset ID.
tf.app.flags.DEFINE_string(
    'model_dir', '/tmp/imagenet',
    """Path to classify_image_graph_def.pb, """
    """imagenet_synset_to_human_label_map.txt, and """
    """imagenet_2012_challenge_label_map_proto.pbtxt.""")
tf.app.flags.DEFINE_string('image_file', '',
                           """Absolute path to image file.""")
tf.app.flags.DEFINE_integer('num_top_predictions', 5,
                            """Display this many predictions.""")

# pylint: disable=line-too-long
DATA_URL = 'http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz'
# pylint: enable=line-too-long


class NodeLookup(object):
  """Converts integer node ID's to human readable labels."""

  def __init__(self,
               label_lookup_path=None,
               uid_lookup_path=None):
    if not label_lookup_path:
      label_lookup_path = os.path.join(
          FLAGS.model_dir, 'imagenet_2012_challenge_label_map_proto.pbtxt')
    if not uid_lookup_path:
      uid_lookup_path = os.path.join(
          FLAGS.model_dir, 'imagenet_synset_to_human_label_map.txt')
    self.node_lookup = self.load(label_lookup_path, uid_lookup_path)

  def load(self, label_lookup_path, uid_lookup_path):
    """Loads a human readable English name for each softmax node.

    Args:
      label_lookup_path: string UID to integer node ID.
      uid_lookup_path: string UID to human-readable string.

    Returns:
      dict from integer node ID to human-readable string.
    """
    if not tf.gfile.Exists(uid_lookup_path):
      tf.logging.fatal('File does not exist %s', uid_lookup_path)
    if not tf.gfile.Exists(label_lookup_path):
      tf.logging.fatal('File does not exist %s', label_lookup_path)

    # Loads mapping from string UID to human-readable string
    proto_as_ascii_lines = tf.gfile.GFile(uid_lookup_path).readlines()
    uid_to_human = {}
    p = re.compile(r'[n\d]*[ \S,]*')
    for line in proto_as_ascii_lines:
      parsed_items = p.findall(line)
      uid = parsed_items[0]
      human_string = parsed_items[2]
      uid_to_human[uid] = human_string

    # Loads mapping from string UID to integer node ID.
    node_id_to_uid = {}
    proto_as_ascii = tf.gfile.GFile(label_lookup_path).readlines()
    for line in proto_as_ascii:
      if line.startswith('  target_class:'):
        target_class = int(line.split(': ')[1])
      if line.startswith('  target_class_string:'):
        target_class_string = line.split(': ')[1]
        node_id_to_uid[target_class] = target_class_string[1:-2]

    # Loads the final mapping of integer node ID to human-readable string
    node_id_to_name = {}
    for key, val in node_id_to_uid.items():
      if val not in uid_to_human:
        tf.logging.fatal('Failed to locate: %s', val)
      name = uid_to_human[val]
      node_id_to_name[key] = name

    return node_id_to_name

  def id_to_string(self, node_id):
    if node_id not in self.node_lookup:
      return ''
    return self.node_lookup[node_id]


def create_graph():
  """Creates a graph from saved GraphDef file and returns a saver."""
  # Creates graph from saved graph_def.pb.
  with tf.gfile.FastGFile(os.path.join(
      FLAGS.model_dir, 'classify_image_graph_def.pb'), 'rb') as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())
    _ = tf.import_graph_def(graph_def, name='')


def run_inference_on_images(image_list, output_dir):
  """Runs inference on an image list.

  Args:
    image_list: a list of images.
    output_dir: the directory in which image vectors will be saved

  Returns:
    image_to_labels: a dictionary with image file keys and predicted
      text label values
  """
  image_to_labels = defaultdict(list)

  create_graph()

  with tf.Session() as sess:
    # Some useful tensors:
    # 'softmax:0': A tensor containing the normalized prediction across
    #   1000 labels.
    # 'pool_3:0': A tensor containing the next-to-last layer containing 2048
    #   float description of the image.
    # 'DecodeJpeg/contents:0': A tensor containing a string providing JPEG
    #   encoding of the image.
    # Runs the softmax tensor by feeding the image_data as input to the graph.
    softmax_tensor = sess.graph.get_tensor_by_name('softmax:0')

    for image_index, image in enumerate(image_list):
      try:
        print("parsing", image_index, image, "\n")
        if not tf.gfile.Exists(image):
          tf.logging.fatal('File does not exist %s', image)

        with tf.gfile.FastGFile(image, 'rb') as f:
          image_data =  f.read()

          predictions = sess.run(softmax_tensor,
                          {'DecodeJpeg/contents:0': image_data})

          predictions = np.squeeze(predictions)

          ###
          # Get penultimate layer weights
          ###

          feature_tensor = sess.graph.get_tensor_by_name('pool_3:0')
          feature_set = sess.run(feature_tensor,
                          {'DecodeJpeg/contents:0': image_data})
          feature_vector = np.squeeze(feature_set)        
          outfile_name = os.path.basename(image) + ".npz"
          out_path = os.path.join(output_dir, outfile_name)
          np.savetxt(out_path, feature_vector, delimiter=',')

          # Creates node ID --> English string lookup.
          node_lookup = NodeLookup()

          top_k = predictions.argsort()[-FLAGS.num_top_predictions:][::-1]
          for node_id in top_k:
            human_string = node_lookup.id_to_string(node_id)
            score = predictions[node_id]
            print("results for", image)
            print('%s (score = %.5f)' % (human_string, score))
            print("\n")

            image_to_labels[image].append(
              {
                "labels": human_string,
                "score": str(score)
              }
            )

        # close the open file handlers
        proc = psutil.Process()
        open_files = proc.open_files()

        for open_file in open_files:
          file_handler = getattr(open_file, "fd")
          os.close(file_handler)
      except:
        print('could not process image index',image_index,'image', image)

  return image_to_labels


def maybe_download_and_extract():
  """Download and extract model tar file."""
  dest_directory = FLAGS.model_dir
  if not os.path.exists(dest_directory):
    os.makedirs(dest_directory)
  filename = DATA_URL.split('/')[-1]
  filepath = os.path.join(dest_directory, filename)
  if not os.path.exists(filepath):
    def _progress(count, block_size, total_size):
      sys.stdout.write('\r>> Downloading %s %.1f%%' % (
          filename, float(count * block_size) / float(total_size) * 100.0))
      sys.stdout.flush()
    filepath, _ = urllib.request.urlretrieve(DATA_URL, filepath, _progress)
    print()
    statinfo = os.stat(filepath)
    print('Succesfully downloaded', filename, statinfo.st_size, 'bytes.')
  tarfile.open(filepath, 'r:gz').extractall(dest_directory)


def main(_):
  maybe_download_and_extract()
  if len(sys.argv) < 2:
    print("please provide a glob path to one or more images, e.g.")
    print("python classify_image_modified.py '../cats/*.jpg'")
    sys.exit()

  else:
    output_dir = "image_vectors"
    if not os.path.exists(output_dir):
      os.makedirs(output_dir)

    images = glob.glob(sys.argv[1])
    image_to_labels = run_inference_on_images(images, output_dir)

    with open("image_to_labels.json", "w") as img_to_labels_out:
      json.dump(image_to_labels, img_to_labels_out)

    print("all done")
if __name__ == '__main__':
  tf.app.run()
duhaime
źródło
5

Możesz użyć Perceptual Image Diff

Jest to narzędzie wiersza poleceń, które porównuje dwa obrazy za pomocą metryki percepcyjnej. Oznacza to, że wykorzystuje model obliczeniowy ludzkiego układu wzrokowego do określenia, czy dwa obrazy różnią się wizualnie, więc pomijane są drobne zmiany w pikselach. Ponadto radykalnie zmniejsza liczbę fałszywych alarmów spowodowanych różnicami w generowaniu liczb losowych, różnicach w systemie operacyjnym lub architekturze maszyny.

Alejandro Bologna
źródło
4

To trudny problem! Zależy to od tego, jak dokładny musisz być, i zależy od rodzaju obrazów, z którymi pracujesz. Możesz używać histogramów do porównywania kolorów, ale to oczywiście nie bierze pod uwagę przestrzennego rozmieszczenia tych kolorów na obrazach (tj. Kształtów). Wykrywanie krawędzi, po którym następuje pewnego rodzaju segmentacja (tj. Wybieranie kształtów), może zapewnić wzór do dopasowania do innego obrazu. Możesz użyć macierzy koocurencji do porównywania tekstur, traktując obrazy jako macierze wartości pikseli i porównując te macierze. Jest kilka dobrych książek na temat dopasowywania obrazów i wizji maszynowej - wyszukiwarka Amazon znajdzie je.

Mam nadzieję że to pomoże!

Ben
źródło
3

Istnieją podobne badania wykorzystujące sieci neuronowe / mapy samoorganizujące się Kohonena

Istnieją zarówno bardziej akademickie systemy (Google dla PicSOM), jak i mniej akademickie
( http://www.generation5.org/content/2004/aiSomPic.asp , (prawdopodobnie nieodpowiednie dla wszystkich środowisk pracy)).

EPa
źródło
3

Obliczanie sumy kwadratów różnic wartości kolorów pikseli w drastycznie zmniejszonej wersji (np. 6x6 pikseli) działa dobrze. Identyczne obrazy dają 0, podobne obrazy dają małe liczby, różne obrazy dają duże.

Pomysł innych gości, aby włamać się do YUV, na początku brzmi intrygująco - chociaż mój pomysł działa świetnie, chcę, aby moje obrazy były obliczane jako „inne”, aby przyniosły poprawny wynik - nawet z perspektywy obserwatora ślepego na kolory.

chris
źródło
2

To brzmi jak problem ze wzrokiem. Możesz zajrzeć do Adaptive Boosting, a także do algorytmu Burns Line Extraction. Koncepcje zawarte w tych dwóch powinny pomóc w podejściu do tego problemu. Wykrywanie krawędzi jest jeszcze prostszym miejscem do rozpoczęcia, jeśli jesteś nowy w algorytmach wizyjnych, ponieważ wyjaśnia podstawy.

Jeśli chodzi o parametry kategoryzacji:

  • Paleta kolorów i lokalizacja (obliczanie gradientu, histogram kolorów)
  • Zawarte kształty (Ada. Boosting / Training do wykrywania kształtów)
willasaywhat
źródło
2

W zależności od tego, ile dokładnych wyników potrzebujesz, możesz po prostu podzielić obrazy na bloki nxn pikseli i przeanalizować je. Jeśli uzyskasz różne wyniki w pierwszym bloku, nie możesz zatrzymać przetwarzania, co skutkuje pewną poprawą wydajności.

Aby przeanalizować kwadraty, możesz na przykład uzyskać sumę wartości kolorów.


źródło
1

Można wykonać pewnego rodzaju oszacowanie ruchu z dopasowaniem bloków między dwoma obrazami i zmierzyć całkowitą sumę reszt i kosztów wektora ruchu (podobnie jak w przypadku kodera wideo). To zrekompensowałoby ruch; aby uzyskać dodatkowe punkty, wykonaj oszacowanie ruchu z transformacją afiniczną (kompensuje powiększenia i rozciąganie itp.). Możesz również wykonać nakładające się bloki lub przepływ optyczny.

Dark Shikari
źródło
1

W pierwszej kolejności możesz spróbować użyć kolorowych histogramów. Jednak naprawdę musisz zawęzić problematyczną domenę. Ogólne dopasowanie obrazu to bardzo trudny problem.

Dima
źródło
1

Przepraszamy za późne uczestnictwo w dyskusji.

Możemy nawet użyć metodologii ORB do wykrycia punktów podobnych cech między dwoma obrazami. Poniższy link daje bezpośrednią implementację ORB w Pythonie

http://scikit-image.org/docs/dev/auto_examples/plot_orb.html

Nawet openCV ma bezpośrednią implementację ORB. Jeśli chcesz uzyskać więcej informacji, zapoznaj się z artykułem badawczym podanym poniżej.

https://www.researchgate.net/publication/292157133_Image_Matching_Using_SIFT_SURF_BRIEF_and_ORB_Performance_Comparison_for_Distorted_Images

Vivek Srinivasan
źródło
0

W drugim wątku jest kilka dobrych odpowiedzi na ten temat, ale zastanawiam się, czy coś, co wiąże się z analizą spektralną, zadziała? To znaczy, podziel obraz na informacje o jego fazie i amplitudzie i porównaj je. Pozwoli to uniknąć niektórych problemów związanych z kadrowaniem, transformacją i różnicami intensywności. W każdym razie to tylko ja spekuluję, ponieważ wydaje się to interesującym problemem. Jeśli przeszukałeś http://scholar.google.com , jestem pewien, że mógłbyś wymyślić kilka artykułów na ten temat.

neuroguy123
źródło
Analiza widmowa jest oparta na transformacie Fouriera, nie ma kolorowego histogramu, ponieważ można zrekonstruować obraz z dwóch części - wyobrażonej i rzeczywistej. (nie wiem, czy to zadziała, po prostu daję ci znać, że nie należy do tej kategorii).
nlucaroni
Tak, miałem na myśli transformatę Fouriera.
neuroguy123