image-processing - imagen - reverse image search wikipedia
Coincidencia de la imagen con la colección de imágenes (5)
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.imagemagick.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 ''[a-z]'' ''[A-Z]'' <<< $1)
STR2=$(tr ''[a-z]'' ''[A-Z]'' <<< $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, i.e. 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
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:
Esto es lo que estoy tratando de encontrar:
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 se pudieran comparar 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í. Probé variando la variable mult de 1 a 8 (puedes probar cualquier valor entero), y para todos esos casos, la imagen Demystify dio el producto interno 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) x n 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 a n x 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 <opencv2/opencv.hpp>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <windows.h>
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<Mat> images;
vector<string> 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<imgnorm2_t> 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
¡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:
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
:-)
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 rectángulos ).
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.