2012-02-06 10 views
5

Sto provando a inventare un sistema di punteggio per alcune ricerche sulla psicologia comportamentale.Confronto forma irregolare

Chiedo alle persone di disegnare una lettera, quindi tracciarla sopra, entrambe su una tavoletta grafica. Voglio valutare l'accuratezza di questa traccia. Quindi, disegni una lettera qualsiasi ('a'), poi la fai di nuovo, poi la segnalo in base a quanto fosse simile alla prima volta che la disegnavi. I disegni sono memorizzati come posizioni dei pixel.

La precisione è valutata come la vicinanza alla lettera originale. Il metodo non ha bisogno di consentire la modifica di scala, rotazione o posizione. Concettualmente è come l'area tra le due linee, solo le linee sono molto irregolari, quindi gli integrali (a mia conoscenza) non funzioneranno.

Sto scrivendo in MATLAB, ma qualsiasi aiuto concettuale sarebbe apprezzato. Ho provato a sommare la distanza minima tra tutti i pixel disegnati, ma questo dà buoni (bassi) punteggi a singoli punti ben posizionati.

Questo deve essere stato fatto prima, ma non ho alcuna fortuna con le mie ricerche. Qualsiasi aiuto molto apprezzato!

--- Soluzione parziale utilizzando il metodo suggerito da @Bill in basso. Non funziona, poiché il gradiente di bwdist è troppo ripido. Piuttosto che la bella seconda immagine che Bill mostra, sembra più l'originale.

%% Letter to image 
im = zeros(1080,1920,3); % The screen (possible pixel locations) 
% A small square a bit like the letter 'a', a couple of pixels wide. 
pixthick = 5; 
im(450:450+pixthick,[900:1100],:) = 1; 
im(550:550+pixthick,[900:1100],:) = 1; 
im([450:550],900:900+pixthick,:) = 1; 
im([450:570],1100:1100+pixthick,:) = 1; 
subplot(2,1,1); imagesc(im); %% atransbw = bwdist(im(:,:,1)<0.5); subplot(2,1,2); 
imagesc(atransbw); 
+0

qual è il problema con il metodo che hai già descritto? (la somma delle distanze più brevi) – Ali

+0

Un singolo punto può ottenere un punteggio molto alto, in quanto vi è un solo valore per trovare la distanza più breve per. Per esempio. immagina un punto al centro di un cerchio - la somma delle distanze più corte sarebbe piccola, il che significa un buon punteggio, quando in realtà è una traccia piuttosto brutta. – dgmp88

risposta

2

Cosa potrebbe aiutare voi è un distance transform, implementato in MATLAB come bwdist. Questo premia le linee essendo vicine, anche se non corrispondono.

a_img_1 = imread('a.jpg'); 
imagesc(a_img_1); 

enter image description here

a_img_1_dist_transform = bwdist(a(:, :, 1) < 250); 
imagesc(a_img_1_dist_transform); 

enter image description here

Si può fare lo stesso con la seconda immagine, e riassumere la differenza nei valori dei pixel nelle immagini a distanza trasformata, qualcosa di simile:

score = sum(abs(a_img_1_dist_transform(:) - a_img_2_dist_transform(:))) 

(Nota che questo darà punteggi più alti per immagini meno simili e v.v.)

Per evitare problemi che si menzionano "buoni (bassi) punteggi in punti singoli ben posizionati", è possibile sperimentare altre misure di distanza, come la distanza quadrata tra i valori dei pixel.

+0

OK, cercherò di implementarlo - grazie! Il mio unico problema con questa soluzione è che è piuttosto costoso dato che le 'immagini' sono memorizzate solo come vettori. Scusa, penso di non essere stato così chiaro: il confronto delle immagini potrebbe non essere il tag migliore. Tuttavia, questo potrebbe funzionare, proverò in un momento dopo un incontro. Saluti! (provato a votare, ma il mio rappresentante non è ancora alto) – dgmp88

+0

Vuoi dire che sono memorizzati come immagini vettoriali, piuttosto che file raster? In tal caso, sì, sono sicuro che saranno modi migliori di questa risposta. –

+0

Neanche immagini: è solo un vettore bidimensionale di posizioni di pixel in cui la penna ha toccato la tavoletta grafica. – dgmp88

1

È possibile trovare una trasformazione affine che corrisponda a un criterio di errore, ad esempio errore quadratico medio. In questo modo sarai invariabile alla traduzione e al ridimensionamento. O se si volesse penalizzare la traduzione, si potrebbe aggiungere anche il costo della traduzione. (Sarebbe utile aiutarci se fornirai maggiori informazioni su quale tipo di funzionalità consideri simile o meno)

Ora, un'implementazione efficiente è un'altra questione. Forse dovresti esaminare la registrazione delle immagini. Sono sicuro che questo è stato fatto numerose volte.

+0

L'invarianza di conversione e ridimensionamento non è importante. Concettualmente, un modo per calcolare l'area tra l'originale e la traccia sarebbe perfetto. Qualcosa di simile a quello che vedi se usi lo strumento di riempimento a secchi in vernice, per ogni posto in cui l'originale e la traccia si discostano l'uno dall'altro, sarebbe perfetto. Guarderò le trasformazioni affini e la registrazione delle immagini, evviva! – dgmp88

0

In realtà suggerirei una soluzione molto più di alto livello. Trova un algoritmo di apprendimento automatico OCR che restituisca una certa confidenza. Oppure, se non si ha fiducia, testare la distanza tra il testo di output e il reale.
Questo è come un umano che osserva la calligrafia e cerca di capirlo. Maggiore è la sicurezza, migliore è il risultato.

+0

La soluzione OCR sembra davvero di alto livello. Sono sicuro che funzionerebbe, ma spero in una soluzione più semplice, poiché sto solo utilizzando raccolte di posizioni di pixel per definire le lettere. Ma sì, potrei doverlo fare alla fine :). – dgmp88

3

I contesti di forma sono un potente descrittore di funzione basato su "istogrammi polari" delle forme. Il numero Wikipedia page è approfondito, ma here è un'altra pagina con informazioni aggiuntive (e una buona spiegazione visiva della tecnica), nonché MATLAB demo code. Le lettere di corrispondenza erano una delle applicazioni originali del metodo e il codice della demo a cui mi collego non richiede la conversione dei vettori di tracciamento in immagini.

Un metodo più semplicistico potrebbe essere una "differenza di immagine" definita come esclusiva o di due lettere. Ciò richiederebbe la conversione dei vettori di tracciamento in immagini binarie. Qualcosa di simile:

x = xor(im1,im2); 
d = sum(x(:))/sum(im1(:)); %# normalize to the first image 

Infine, se i vettori tracce hanno lo stesso numero di punti, o può essere fatto per per campionamento, analisi Procuste potrebbe essere utile. L'idea dell'analisi di Procrustes consiste nel trovare una trasformazione lineare ottimale di minimi quadrati (rotazione, traslazione e ridimensionamento) tra due serie di punti. La bontà di adattamento tra i due set di punti è data dalla "statistica di Procrustes" o da altre misure come la deviazione quadratica media dei punti.

%# Whatever makes sense; 
%# procrustes needs N x 2 matrices with (x,y) coords for N points. 
coords1 = [x1 y1]; 
coords2 = [x2 y2]; 

%# This sampling may be too naive. 
n = max(size(coords1,1), size(coords2,1)); 
coords1 = coords1(1:n,:); 
coords2 = coords2(1:n,:); 

%# d is sum-of-squares error 
%# z is transformed coords2 
%# tr is the linear transformation 
[ d, z, tr ] = procrustes(coords1, coords2); 

%# RMS deviation of points may be better than SSE. 
n = size(coords1,1); 
rmsd = sqrt((sum((coords1(:) - z(:)).^2)/n)); 
+0

Belle idee, grazie! Un problema però è che non voglio l'invarianza a varie trasformazioni che procrustano e modellano i contesti - troppo piccoli/grandi/ruotati contano come errori. – dgmp88

+0

È possibile utilizzare 'procrustes (X, Y, 'ridimensionamento', falso)' per disattivare il componente di ridimensionamento. Potresti anche usare direttamente RMSD o SSE dei punti ... –

+0

grazie, lo proverò e lo confronterò con una versione diversa della soluzione proposta da Bill Cheatham su cui sto lavorando ora. – dgmp88

1

Questa è la mia soluzione finale complicata, che utilizza fondamentalmente il metodo di Bill Cheatham. Grazie per tutto l'aiuto!

% pixLet is the 2D vector contain locations where drawing occurred. First convert it to an image. 

im = zeros(1000,1000); % This is the image 
for pix = 2:size(pixLet,1) 
    y1 = pixLet(pix-1,2); x1 = pixLet(pix-1,1); 
    y2 = pixLet(pix,2); x2 = pixLet(pix,1); 
    xyd = round(pdist([x1 y1; x2 y2])*2); 
    xs = round(linspace(x1,x2,xyd)); 
    ys = round(linspace(y1,y2,xyd)); 
    for linepix = 1:length(xs) 
     im(ys(linepix),xs(linepix)) = 1; 
    end 
end 

% Blur the image 
blur = fspecial('gaussian',[sz sz],reach); 
gausIm = conv2(im,blur,'same'); 

% I made a function of the above to do this for both the template and the trace. 
score = sum(sum(abs(gausIm1-gausIm2))); 
Problemi correlati