Coincidencia de la imagen con la colección de imágenes

Tengo un gran grupo de imágenes de cartas y una foto de una carta en particular. ¿Qué herramientas puedo usar para encontrar qué imagen de colección es más similar a la mía?

Aquí está la muestra de la colección:

  • Abundancia
  • Urgencia agresiva
  • Desmitificar

Esto es lo que estoy tratando de encontrar:

  • Foto de la tarjeta

Gracias por publicar algunas fotos.

He codificado un algoritmo llamado Perceptual Hashing que encontré por el Dr. Neal Krawetz. Al comparar sus imágenes con la Tarjeta, obtengo las siguientes medidas de similitud porcentuales:

 Card vs. Abundance 79% Card vs. Aggressive 83% Card vs. Demystify 85% 

por lo tanto, no es un discriminador ideal para su tipo de imagen, pero funciona un poco. Es posible que desee jugar con él para adaptarlo a su caso de uso.

Calcularía un hash para cada una de las imágenes de tu colección, una a la vez y almacenaría el hash para cada imagen solo una vez. Luego, cuando obtenga una nueva tarjeta, calcule su hash y compárelo con los almacenados.

 #!/bin/bash ################################################################################ # Similarity # Mark Setchell # # Calculate percentage similarity of two images using Perceptual Hashing # See article by Dr Neal Krawetz entitled "Looks Like It" - www.hackerfactor.com # # Method: # 1) Resize image to black and white 8x8 pixel square regardless # 2) Calculate mean brightness of those 64 pixels # 3) For each pixel, store "1" if pixel>mean else store "0" if less than mean # 4) Convert resulting 64bit string of 1's and 0's, 16 hex digit "Perceptual Hash" # # If finding difference between Perceptual Hashes, simply total up number of bits # that differ between the two strings - this is the Hamming distance. # # Requires ImageMagick - www.imagemgick.org # # Usage: # # Similarity image|imageHash [image|imageHash] # If you pass one image filename, it will tell you the Perceptual hash as a 16 # character hex string that you may want to store in an alternate stream or as # an attribute or tag in filesystems that support such things. Do this in order # to just calculate the hash once for each image. # # If you pass in two images, or two hashes, or an image and a hash, it will try # to compare them and give a percentage similarity between them. ################################################################################ function PerceptualHash(){ TEMP="tmp$$.png" # Force image to 8x8 pixels and greyscale convert "$1" -colorspace gray -quality 80 -resize 8x8! PNG8:"$TEMP" # Calculate mean brightness and correct to range 0..255 MEAN=$(convert "$TEMP" -format "%[fx:int(mean*255)]" info:) # Now extract all 64 pixels and build string containing "1" where pixel > mean else "0" hash="" for i in {0..7}; do for j in {0..7}; do pixel=$(convert "${TEMP}"[1x1+${i}+${j}] -colorspace gray text: | grep -Eo "\(\d+," | tr -d '(,' ) bit="0" [ $pixel -gt $MEAN ] && bit="1" hash="$hash$bit" done done hex=$(echo "obase=16;ibase=2;$hash" | bc) printf "%016s\n" $hex #rm "$TEMP" > /dev/null 2>&1 } function HammingDistance(){ # Convert input hex strings to upper case like bc requires STR1=$(tr '[az]' '[AZ]' <<< $1) STR2=$(tr '[az]' '[AZ]' <<< $2) # Convert hex to binary and zero left pad to 64 binary digits STR1=$(printf "%064s" $(echo "obase=2;ibase=16;$STR1" | bc)) STR2=$(printf "%064s" $(echo "obase=2;ibase=16;$STR2" | bc)) # Calculate Hamming distance between two strings, each differing bit adds 1 hamming=0 for i in {0..63};do a=${STR1:i:1} b=${STR2:i:1} [ $a != $b ] && ((hamming++)) done # Hamming distance is in range 0..64 and small means more similar # We want percentage similarity, so we do a little maths similarity=$((100-(hamming*100/64))) echo $similarity } function Usage(){ echo "Usage: Similarity image|imageHash [image|imageHash]" >&2 exit 1 } ################################################################################ # Main ################################################################################ if [ $# -eq 1 ]; then # Expecting a single image file for which to generate hash if [ ! -f "$1" ]; then echo "ERROR: File $1 does not exist" >&2 exit 1 fi PerceptualHash "$1" exit 0 fi if [ $# -eq 2 ]; then # Expecting 2 things, ie 2 image files, 2 hashes or one of each if [ -f "$1" ]; then hash1=$(PerceptualHash "$1") else hash1=$1 fi if [ -f "$2" ]; then hash2=$(PerceptualHash "$2") else hash2=$2 fi HammingDistance $hash1 $hash2 exit 0 fi Usage 

¡Nuevo método!

Parece que el siguiente comando ImageMagick, o tal vez una variación de él, dependiendo de mirar una mayor selección de sus imágenes, extraerá la fraseología en la parte superior de sus tarjetas

 convert aggressiveurge.jpg -crop 80%x10%+10%+10% crop.png 

que toma el 10% superior de su imagen y el 80% del ancho (comenzando al 10% desde la esquina superior izquierda y lo almacena en crop.png siguiente manera:

enter image description here

Y si ejecuta eso a través de tessseract OCR de la siguiente manera:

 tesseract crop.png agg 

obtienes un archivo llamado agg.txt contiene:

 E' Aggressive Urge \L® E 

que puedes ejecutar grep para limpiar, buscando solo letras mayúsculas y minúsculas adyacentes entre sí:

 grep -Eo "\<[A-Za-z]+\>" agg.txt 

Llegar

 Aggressive Urge 

🙂

También probé una correlación cruzada normalizada de cada una de tus imágenes con la tarjeta, como esta:

 #!/bin/bash size="300x400!" convert card.png -colorspace RGB -normalize -resize $size card.jpg for i in *.jpg do cc=$(convert $i -colorspace RGB -normalize -resize $size JPG:- | \ compare - card.jpg -metric NCC null: 2>&1) echo "$cc:$i" done | sort -n 

y obtuve esta salida (ordenada por calidad de coincidencia):

 0.453999:abundance.jpg 0.550696:aggressive.jpg 0.629794:demystify.jpg 

que muestra que la tarjeta se correlaciona mejor con demystify.jpg .

Tenga en cuenta que cambié el tamaño de todas las imágenes al mismo tamaño y normalicé su contraste para que pudieran compararse fácilmente y se minimizaran los efectos resultantes de las diferencias en el contraste. Hacerlos más pequeños también reduce el tiempo necesario para la correlación.

Intenté esto organizando los datos de imagen como un vector y tomando el producto interno entre los vectores de imagen de colección y el vector de imagen buscado. Los vectores más similares darán el producto interno más alto. Cambio el tamaño de todas las imágenes al mismo tamaño para obtener vectores de igual longitud para que pueda tomar el producto interno. Este cambio de tamaño reducirá adicionalmente el costo computacional del producto interno y dará una aproximación aproximada de la imagen real.

Puede verificarlo rápidamente con Matlab u Octave. A continuación se muestra el guión de Matlab / Octave. He agregado comentarios allí. Traté de variar la variable mult de 1 a 8 (puedes probar cualquier valor entero), y para todos esos casos, la imagen Demystify dio el producto interior más alto con la imagen de la tarjeta. Para mult = 8, obtengo el siguiente vector ip en Matlab:

ip =

683007892

558305537

604013365

Como puede ver, da el producto interno más alto de 683007892 para la imagen Demystify.

 % load images imCardPhoto = imread('0.png'); imDemystify = imread('1.jpg'); imAggressiveUrge = imread('2.jpg'); imAbundance = imread('3.jpg'); % you can experiment with the size by varying mult mult = 8; size = [17 12]*mult; % resize with nearest neighbor interpolation smallCardPhoto = imresize(imCardPhoto, size); smallDemystify = imresize(imDemystify, size); smallAggressiveUrge = imresize(imAggressiveUrge, size); smallAbundance = imresize(imAbundance, size); % image collection: each image is vectorized. if we have n images, this % will be a (size_rows*size_columns*channels) xn matrix collection = [double(smallDemystify(:)) ... double(smallAggressiveUrge(:)) ... double(smallAbundance(:))]; % vectorize searched image. this will be a (size_rows*size_columns*channels) x 1 % vector x = double(smallCardPhoto(:)); % take the inner product of x and each image vector in collection. this % will result in anx 1 vector. the higher the inner product is, more similar the % image and searched image(that is x) ip = collection' * x; 

EDITAR

Intenté otro enfoque, básicamente tomando la distancia euclidiana (norma l2) entre las imágenes de referencia y la imagen de la tarjeta, y me dio muy buenos resultados con una gran colección de imágenes de referencia (383 imágenes) que encontré en este enlace para la imagen de la tarjeta de prueba.

Aquí, en lugar de tomar toda la imagen, extraje la parte superior que contiene la imagen y la uso para comparar.

En los siguientes pasos, todas las imágenes de entrenamiento y la imagen de prueba se redimensionan a un tamaño predefinido antes de realizar cualquier procesamiento.

  • extraer las regiones de la imagen de las imágenes de entrenamiento
  • realice el cierre morfológico de estas imágenes para obtener una aproximación gruesa (este paso puede no ser necesario)
  • vectorize estas imágenes y almacénelas en un conjunto de entrenamiento (lo llamo entrenamiento aun cuando no haya entrenamiento en este enfoque)
  • cargue la imagen de la tarjeta de prueba, extraiga la región de interés de la imagen (ROI), aplique el cierre, luego vectorice
  • calcular la distancia euclidiana entre cada vector de imagen de referencia y el vector de imagen de prueba
  • elige el elemento de distancia mínima (o los primeros k elementos)

Hice esto en C ++ usando OpenCV. También estoy incluyendo algunos resultados de prueba usando diferentes escalas.

 #include  #include  #include  #include  #include  using namespace cv; using namespace std; #define INPUT_FOLDER_PATH string("Your test image folder path") #define TRAIN_IMG_FOLDER_PATH string("Your training image folder path") void search() { WIN32_FIND_DATA ffd; HANDLE hFind = INVALID_HANDLE_VALUE; vector images; vector labelNames; int label = 0; double scale = .2; // you can experiment with scale Size imgSize(200*scale, 285*scale); // training sample images are all 200 x 285 (width x height) Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); // get all training samples in the directory hFind = FindFirstFile((TRAIN_IMG_FOLDER_PATH + string("*")).c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) { cout << "INVALID_HANDLE_VALUE: " << GetLastError() << endl; return; } do { if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { Mat im = imread(TRAIN_IMG_FOLDER_PATH+string(ffd.cFileName)); Mat re; resize(im, re, imgSize, 0, 0); // resize the image // extract only the upper part that contains the image Mat roi = re(Rect(re.cols*.1, re.rows*35/285.0, re.cols*.8, re.rows*125/285.0)); // get a coarse approximation morphologyEx(roi, roi, MORPH_CLOSE, kernel); images.push_back(roi.reshape(1)); // vectorize the roi labelNames.push_back(string(ffd.cFileName)); } } while (FindNextFile(hFind, &ffd) != 0); // load the test image, apply the same preprocessing done for training images Mat test = imread(INPUT_FOLDER_PATH+string("0.png")); Mat re; resize(test, re, imgSize, 0, 0); Mat roi = re(Rect(re.cols*.1, re.rows*35/285.0, re.cols*.8, re.rows*125/285.0)); morphologyEx(roi, roi, MORPH_CLOSE, kernel); Mat testre = roi.reshape(1); struct imgnorm2_t { string name; double norm2; }; vector imgnorm; for (size_t i = 0; i < images.size(); i++) { imgnorm2_t data = {labelNames[i], norm(images[i], testre) /* take the l2-norm (euclidean distance) */}; imgnorm.push_back(data); // store data } // sort stored data based on euclidean-distance in the ascending order sort(imgnorm.begin(), imgnorm.end(), [] (imgnorm2_t& first, imgnorm2_t& second) { return (first.norm2 < second.norm2); }); for (size_t i = 0; i < imgnorm.size(); i++) { cout << imgnorm[i].name << " : " << imgnorm[i].norm2 << endl; } } 

Resultados:

escala = 1.0;

demystify.jpg: 10989.6, sylvan_basilisk.jpg: 11990.7, scathe_zombies.jpg: 12307.6

escala = .8;

demystify.jpg: 8572.84, sylvan_basilisk.jpg: 9440.18, steel_golem.jpg: 9445.36

escala = .6;

demystify.jpg: 6226.6, steel_golem.jpg: 6887.96, sylvan_basilisk.jpg: 7013.05

escala = .4;

demystify.jpg: 4185.68, steel_golem.jpg: 4544.64, sylvan_basilisk.jpg: 4699.67

escala = .2;

demystify.jpg: 1903.05, steel_golem.jpg: 2154.64, sylvan_basilisk.jpg: 2277.42

Si te entiendo correctamente, debes compararlos como imágenes. Aquí hay una solución muy simple pero efectiva: se llama Sikuli .

¿Qué herramientas puedo usar para encontrar qué imagen de colección es más similar a la mía?

Esta herramienta funciona muy bien con el procesamiento de imágenes y no solo es capaz de encontrar si su tarjeta (imagen) es similar a lo que ya ha definido como patrón, sino que también busca contenido de imágenes parciales (llamados rectangularjs ).

Por defecto, puede ampliar su funcionalidad a través de Python. Any ImageObject se puede configurar para que acepte similitud_patrón en porcentajes y, al hacerlo, podrá encontrar con precisión lo que está buscando.

Otra gran ventaja de esta herramienta es que puedes aprender lo básico en un día.

Espero que esto ayude.

Intereting Posts