2012-02-13 25 views
5

Ho una domanda.Intersezione di 2D numpy ndarrays

Ho due array numpy che sono scafi convessi OpenCV e voglio controllare l'intersezione senza creare loop o creare immagini ed eseguire numpy.bitwise_and su di essi, entrambi sono piuttosto lenti in Python. Gli array aspetto:

[[[x1 y1]] 
[[x2 y2]] 
[[x3 y3]] 
... 
[[xn yn]]] 

Considerando [[x1 y1]] come un unico elemento, voglio eseguire intersezione tra due ndarrays numpy. Come lo posso fare? Ho trovato alcune domande di natura simile, ma non sono riuscito a capire la soluzione a questo da lì.

Grazie in anticipo!

risposta

0

Quindi questo è quello che ho fatto per ottenere il lavoro fatto:

import Polygon, numpy 

# Here I extracted and combined some contours and created a convex hull from it. 
# Now I wanna check whether a contour acquired differently intersects with this hull or not. 

for contour in contours: # The result of cv2.findContours is a list of contours 
    contour1 = contour.flatten() 
    contour1 = numpy.reshape(contour1, (int(contour1.shape[0]/2),-1)) 
    poly1 = Polygon.Polygon(contour1) 

    hull = hull.flatten() # This is the hull is previously constructued 
    hull = numpy.reshape(hull, (int(hull.shape[0]/2),-1)) 
    poly2 = Polygon.Polygon(hull) 

    if (poly1 & poly2).area()<= some_max_val: 
     some_operations 

ho dovuto usare per il ciclo, e tutto questo sembra un po 'noioso, anche se mi dà risultati attesi. Qualunque metodo migliore sarebbe molto apprezzato!

1

è possibile utilizzare http://pypi.python.org/pypi/Polygon/2.0.4, ecco un esempio:

>>> import Polygon 
>>> a = Polygon.Polygon([(0,0),(1,0),(0,1)]) 
>>> b = Polygon.Polygon([(0.3,0.3), (0.3, 0.6), (0.6, 0.3)]) 
>>> a & b 
Polygon: 
    <0:Contour: [0:0.60, 0.30] [1:0.30, 0.30] [2:0.30, 0.60]> 

per convertire il risultato di cv2.findContours in formato punto Poligono, è possibile:

points1 = contours[0].reshape(-1,2) 

Questo permette di convertire la forma da (N, 1, 2) a (N, 2)

Segue un esempio completo:

import Polygon 
import cv2 
import numpy as np 
from scipy.misc import bytescale 

y, x = np.ogrid[-2:2:100j, -2:2:100j] 

f1 = bytescale(np.exp(-x**2 - y**2), low=0, high=255) 
f2 = bytescale(np.exp(-(x+1)**2 - y**2), low=0, high=255) 


c1, hierarchy = cv2.findContours((f1>120).astype(np.uint8), 
             cv2.cv.CV_RETR_EXTERNAL, 
             cv2.CHAIN_APPROX_SIMPLE) 

c2, hierarchy = cv2.findContours((f2>120).astype(np.uint8), 
             cv2.cv.CV_RETR_EXTERNAL, 
             cv2.CHAIN_APPROX_SIMPLE) 


points1 = c1[0].reshape(-1,2) # convert shape (n, 1, 2) to (n, 2) 
points2 = c2[0].reshape(-1,2) 

import pylab as pl 
poly1 = pl.Polygon(points1, color="blue", alpha=0.5) 
poly2 = pl.Polygon(points2, color="red", alpha=0.5) 
pl.figure(figsize=(8,3)) 
ax = pl.subplot(121) 
ax.add_artist(poly1) 
ax.add_artist(poly2) 
pl.xlim(0, 100) 
pl.ylim(0, 100) 

a = Polygon.Polygon(points1) 
b = Polygon.Polygon(points2) 
intersect = a&b # calculate the intersect polygon 

poly3 = pl.Polygon(intersect[0], color="green") # intersect[0] are the points of the polygon 
ax = pl.subplot(122) 
ax.add_artist(poly3) 
pl.xlim(0, 100) 
pl.ylim(0, 100) 
pl.show() 

uscita:

enter image description here

+0

Questo metodo è veramente veloce? Devo continuare a controllare l'intersezione ogni fotogramma mentre lo acquisisco e la risorsa di sistema non è molto alta. –

+0

Quando provo a creare poligono da un contorno opencv o da uno scafo convesso, questo è l'errore che ottengo: 'cPolygon.Error: poligono o contorno non valido per l'operazione' Il formato che hai specificato non è il formato che ho (mostrato nel mio post originale). Presumo che alcune modifiche potrebbero essere necessarie, ma non riesco a immaginare come potrebbe essere fatto. –

+0

si prega di inviare alcuni dati di esempio. – HYRY

9

È possibile utilizzare una vista del array come una singola dimensione alla funzione intersect1d in questo modo:

def multidim_intersect(arr1, arr2): 
    arr1_view = arr1.view([('',arr1.dtype)]*arr1.shape[1]) 
    arr2_view = arr2.view([('',arr2.dtype)]*arr2.shape[1]) 
    intersected = numpy.intersect1d(arr1_view, arr2_view) 
    return intersected.view(arr1.dtype).reshape(-1, arr1.shape[1]) 

Questo crea una visione di ogni matrice, cambiando ogni riga per una tupla di valori. Quindi esegue l'intersezione e modifica il risultato nel formato originale. Ecco un esempio di utilizzo di esso:

test_arr1 = numpy.array([[0, 2], 
         [1, 3], 
         [4, 5], 
         [0, 2]]) 

test_arr2 = numpy.array([[1, 2], 
         [0, 2], 
         [3, 1], 
         [1, 3]]) 

print multidim_intersect(test_arr1, test_arr2) 

Questo stampa:

[[0 2] 
[1 3]] 
+0

Grazie mille per la risposta! Questo sarebbe perfetto se tutti i punti del perimetro fossero negli array numpy. Tuttavia, negli scafi convessi, penso che solo pochi punti siano passati come guide. Ma l'intersezione, come in questo caso significa, è valori comuni in entrambe le aree, che potrebbero non essere comuni all'interno degli stessi array numpy. Ho appena letto il mio post qui sopra e mi sono reso conto che non l'ho menzionato affatto. Mi dispiace per quello. –

+0

Quando applico la vista su mio numpy, assomiglia a questo: [[[x1,) (y1,)]] [[x2,) (y2,)]] ... [[xn,) (YN,)]]] mentre ciò che abbiamo veramente bisogno è questo: [(x1, y1), (x2, y2), (x3, y3), ..., (xn, yn)] Qualche idea? –

+0

Hai solo un asse in più per qualche motivo? Riuscirai a rimodellarlo prima con '' test_arr1.reshape (len (test_arr1), 2) ''? Ciò eviterà una copia. – jterrace

0

ispirato dalla risposta di jiterrace

mi sono imbattuto in questo post mentre si lavora con Udacity deep learning class( cercando di trovare la sovrapposizione tra i dati di allenamento e di test).

Non ho familiarità con "view" e ho trovato la sintassi un po 'difficile da capire, probabilmente lo stesso quando provo a comunicare ai miei amici che pensano in "table". Il mio approccio è fondamentalmente quello di appiattire/rimodellare il narray di forma (N, X, Y) in forma (N, X * Y, 1).

print(train_dataset.shape) 
print(test_dataset.shape) 
#(200000L, 28L, 28L) 
#(10000L, 28L, 28L) 

1).INNER JOIN (più facile da capire, lento)

%%timeit -n 1 -r 1 
def multidim_intersect_df(arr1, arr2): 
    p1 = pd.DataFrame([r.flatten() for r in arr1]).drop_duplicates() 
    p2 = pd.DataFrame([r.flatten() for r in arr2]).drop_duplicates() 
    res = p1.merge(p2) 
    return res 
inters_df = multidim_intersect_df(train_dataset, test_dataset) 
print(inters_df.shape) 
#(1153, 784) 
#1 loop, best of 1: 2min 56s per loop 

2). SET INTERSEZIONE (veloce)

%%timeit -n 1 -r 1 
def multidim_intersect(arr1, arr2): 
    arr1_new = arr1.reshape((-1, arr1.shape[1]*arr1.shape[2])) # -1 means row counts are inferred from other dimensions 
    arr2_new = arr2.reshape((-1, arr2.shape[1]*arr2.shape[2])) 
    intersected = set(map(tuple, arr1_new)).intersection(set(map(tuple, arr2_new))) # list is not hashable, go tuple 
    return list(intersected) # in shape of (N, 28*28) 

inters = multidim_intersect(train_dataset, test_dataset) 
print(len(inters)) 
# 1153 
#1 loop, best of 1: 34.6 s per loop 
Problemi correlati