2015-04-16 5 views
5

Ho bisogno di un programma Python su cui sto lavorando per poter scattare una piccola immagine, determinare se esiste all'interno di un'immagine più grande, e se è così, segnala la sua posizione. In caso contrario, segnalalo. (Nel mio caso, l'immagine grande sarà uno screenshot e l'immagine piccola un'immagine che potrebbe essere o meno sullo schermo, in una tela HTML5.) Guardando in linea, ho scoperto la corrispondenza dei modelli in OpenCV, che fa hanno ottimi collegamenti Python. Ho provato quanto segue, sulla base di codice molto simile che ho trovato on line, utilizzando NumPy così:Determinare se un'immagine esiste all'interno di un'immagine più grande, e in tal caso, trovarla, usando Python

import cv2 
import numpy as np 
image = cv2.imread("screenshot.png") 
template = cv2.imread("button.png") 
result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED) 
StartButtonLocation = np.unravel_index(result.argmax(),result.shape) 

Questo non fa quello che ho bisogno di fare, perché restituisce sempre un punto nel ingrandisci; il punto in cui la partita è più vicina, non importa quanto sia terribile una partita. Voglio qualcosa che trovi un pixel esatto per la corrispondenza dei pixel dell'immagine più piccola nell'immagine più grande e, se nessuno esiste, solleva un'eccezione o restituisce False o qualcosa del genere. E, deve essere abbastanza veloce. Qualcuno ha una buona idea su come farlo?

+1

Una domanda veloce: si può presumere che l'immagine "piccola" apparirà nell'immagine "grande" sempre nella sua dimensione originale e esattamente con i suoi valori originali? O hai bisogno di gestire immagini 'piccole 'di dimensioni variabili che potrebbero essere' interpolate' e gestire le variazioni di 'illuminazione'? Voglio dire, tu parli di "corrispondenza esatta", è davvero esatto? –

+2

Stai garantendo SEMPRE l'uso del formato 'PNG'? Chiedo a beause 'JPEG' subire la quantizzazione e la compressione con perdita e le cose che sono apparentemente identiche possono differire nella loro rappresentazione interna. –

risposta

14

Proporrò una risposta che funzioni rapidamente e perfettamente se si sta cercando exact match sia in termini di dimensioni che di valori di immagine.

L'idea è quella di calcolare una ricerca di forza bruta del voluto h x wmodello un'immagine più grande H x W in. L'approccio bruteforce consisterebbe nel guardare tutte le possibili finestre h x w sull'immagine e verificare la corrispondenza pixel per pixel all'interno del modello. Questo tuttavia è molto costoso dal punto di vista computazionale, ma può essere accelerato.

im = np.atleast_3d(im) 
H, W, D = im.shape[:3] 
h, w = tpl.shape[:2] 

Utilizzando la smart integral images uno in grado di calcolare molto velocemente la somma all'interno di una finestra h x w a partire da ogni pixel. Un'immagine integrale è una tabella riassume zona (array sommati cumulativo), che può essere calcolato con NumPy molto velocemente:

sat = im.cumsum(1).cumsum(0) 

e ha davvero belle proprietà, come il calcolo della somma di tutti i valori all'interno una finestra con solo 4 operazioni aritmetiche:

From wikipedia

Così, calcolando la somma del modello e la congruenza con la somma di h x w finestre oltre l'immagine integrale, è facile trovare un elenco di "possibili windows "dove la somma dei valori interni è uguale alla somma dei valori in t lui modello (una rapida approssimazione).

iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:] 
lookup = iD - iB - iC + iA 

Quanto sopra è una vettorizzazione NumPy del funzionamento mostrato nelle immagini per tutte le possibili h x w rettangoli sopra l'immagine (quindi, molto veloce).

Ciò ridurrà molto il numero di finestre possibili (a 2 in uno dei miei test). L'ultimo passo, consiste nel controllare le corrispondenze esatte con il modello:

posible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)])) 
for y, x in zip(*posible_match): 
    if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl): 
     return (y+1, x+1) 

noti che qui y e x coordinate corrispondono al punto A nell'immagine, che è la fila precedente e colonna al modello.

Mettendo tutto insieme:

def find_image(im, tpl): 
    im = np.atleast_3d(im) 
    tpl = np.atleast_3d(tpl) 
    H, W, D = im.shape[:3] 
    h, w = tpl.shape[:2] 

    # Integral image and template sum per channel 
    sat = im.cumsum(1).cumsum(0) 
    tplsum = np.array([tpl[:, :, i].sum() for i in range(D)]) 

    # Calculate lookup table for all the possible windows 
    iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:] 
    lookup = iD - iB - iC + iA 
    # Possible matches 
    possible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)])) 

    # Find exact match 
    for y, x in zip(*possible_match): 
     if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl): 
      return (y+1, x+1) 

    raise Exception("Image not found") 

Funziona sia con scala di grigi e immagini a colori e viene eseguito in 7ms per un'immagine 303x384 a colori con un modello 50x50.

Un esempio pratico:

>>> from skimage import data 
>>> im = gray2rgb(data.coins()) 
>>> tpl = im[170:220, 75:130].copy() 

>>> y, x = find_image(im, tpl) 
>>> y, x 
(170, 75) 

E per ilustrate il risultato:

enter image description here

immagine originale a sinistra, a destra il modello. E qui la corrispondenza esatta:

>>> fig, ax = plt.subplots() 
>>> imshow(im) 
>>> rect = Rectangle((x, y), tpl.shape[1], tpl.shape[0], edgecolor='r', facecolor='none') 
>>> ax.add_patch(rect) 

E per ultimo, solo un esempio del possible_matches per il test:

enter image description here

La somma sopra le due finestre nell'immagine è lo stesso, ma l'ultimo passaggio della funzione filtra quello che non corrisponde esattamente al modello.

+0

Questo era ESATTAMENTE quello di cui avevo bisogno. Funziona perfettamente, ed è molto veloce. Grazie mille! – clj

+0

@clj Benvenuto! Lieto che lo trovi utile –

+0

Ma in questo caso, le due immagini hanno la stessa fonte. Quindi i valori dei pixel saranno uguali. Cosa succede se ottengo l'immagine in "tpl" da una fonte diversa? Funzionerà anche in questo caso? – Neelesh

1

Dato che sei soddisfatto di OpenCV, ti suggerisco di iniziare con quello che hai già fatto e ottenere la migliore corrispondenza. Una volta individuata la posizione migliore, è possibile verificare che sia effettivamente una buona corrispondenza.

Controllare che sia una buona corrispondenza dovrebbe essere facile come estrarre l'immagine corrispondente e confrontarla con il modello. Per estrarre l'immagine potrebbe essere necessario utilizzare cv2.minMaxLoc(result) ed elaborare l'output. Il metodo di estrazione sembra dipendere dal metodo utilizzato per confrontare le immagini e viene eseguito con gli esempi here.

Dopo aver estratto l'immagine, dovresti essere in grado di confrontarli utilizzando numpy.allclose o qualche altro metodo.