2015-04-03 15 views
13

I rappresentano immagini sotto forma di array 2D. Ho questa immagine:Rilevamento dei bordi per l'immagine memorizzata nella matrice

original

Come posso ottenere i pixel che si trovano direttamente sui confini della regione grigia e colorarle?

colorized

voglio ottenere le coordinate degli elementi di matrice in verde e rosso separatamente. Ho solo regioni bianche, nere e grigie sulla matrice.

+0

Le matrici non sono generalmente un buon modo per rappresentare i dati dei pixel 2D ... non è quello a cui è rivolta la loro semantica. – Sneftel

+2

@Le matrici di Sneftel sono le più comuni, se non l'unica e la migliore, per rappresentare le immagini in librerie come OpenCV, SimpleCV e così via. –

+1

Si stanno confondendo le matrici con gli array 2D. I primi sono dotati di un insieme molto specifico di operazioni che sono del tutto prive di significato da applicare ai dati di immagine. – Sneftel

risposta

0

forse c'è un più elegante modo per farlo ... ma nel caso in cui la matrice è una matrice numpy con dimensioni (N,N) (scala di grigi) si può fare

import numpy as np 

# assuming black -> 0 and white -> 1 and grey -> 0.5 
black_reg = np.where(a < 0.1, a, 10) 
white_reg = np.where(a > 0.9, a, 10) 

xx_black,yy_black = np.gradient(black_reg) 
xx_white,yy_white = np.gradient(white_reg) 

# getting the coordinates 
coord_green = np.argwhere(xx_black**2 + yy_black**2>0.2) 
coord_red = np.argwhere(xx_white**2 + yy_white**2>0.2) 

Il numero 0.2 è solo un soglia e deve essere regolata.

+0

hai preso in considerazione che ho i colori nero, bianco e verde solo sulla prima immagine? (sì, guardo solo le coordinate dei confini delle regioni verdi) –

+0

@begueradj: oh ... mi dispiace. Questo ha bisogno di una piccola modifica. Il nero corrisponde al numero '0' o' 1'? – plonser

+0

puoi impostare qualsiasi convenzione per definire gli elementi nero/bianco e grigio –

0

Penso che probabilmente stai cercando il metodo di rilevamento dei bordi per le immagini in scala di grigi. Ci sono molti modi per farlo. Forse questo può aiutare http://en.m.wikipedia.org/wiki/Edge_detection. Per differenziare i bordi tra bianco e grigio e i bordi tra il nero e il grigio, prova a utilizzare l'intensità media locale.

2

Mentre la risposta di Plonser può essere piuttosto semplice da implementare, la vedo fallire quando si tratta di bordi affilati e sottili. Tuttavia, ti suggerisco di utilizzare parte del suo approccio come precondizionamento.
In una seconda fase si desidera utilizzare Marching Squares Algorithm. Secondo la documentazione di scikit-image, è

un caso speciale dell'algoritmo marching cubes (Lorensen, William e Harvey E. Cline Marching Cubes:.. Una superficie ad alta risoluzione 3D Edilizia Algoritmo Computer Graphics (SIGGRAPH 87 Atti) 21 (4) luglio 1987, pag. 163-170

esiste anche un'implementazione Python come parte del pacchetto scikit-image. sto usando questo algoritmo (la mia propria implementazione Fortran, però) con successo per il rilevamento dei bordi dei diagrammi a occhio nelle comunicazioni ingegneria.

annuncio 1: precondizionamento
Creare una copia dell'immagine e renderlo due colori soltanto, per esempio nero bianco. Le coordinate rimangono le stesse, ma ci si assicura che l'algoritmo possa correttamente rendere una decisione sì/no indipendente dai valori che si usano nella rappresentazione della matrice dell'immagine.

Ad 2: rilevamento di fronte
Wikipedia così come vari blog vi fornirà una bella elaborate description dell'algoritmo in varie lingue, quindi non andrà in esso è particolari. Tuttavia, vorrei darti alcuni consigli pratici:

  1. L'immagine ha dei limiti aperti in basso. Invece di modificare l'algoritmo, puoi aggiungere artificialmente un'altra riga di pixel (nero o grigio per rilegare le aree bianche/grigie).
  2. La scelta del punto di partenza è fondamentale.Se non ci sono troppe immagini da elaborare, ti suggerisco di selezionarlo manualmente. Altrimenti dovrai definire le regole. Poiché l'Algoritmo delle Piastre della Marcia può iniziare ovunque all'interno di un'area delimitata, è possibile scegliere qualsiasi pixel di un determinato colore/valore per rilevare il bordo corrispondente (inizialmente inizierà a camminare in una direzione per trovare un bordo).
  3. L'algoritmo restituisce le esatte posizioni 2D, ad es. (X/y) -tuples. È possibile
    • iterate attraverso la lista e colorare i pixel corrispondenti assegnando un valore diverso o
    • creare un mask per selezionare parti della matrice e assegnare il valore che corrisponde ad un colore diverso, ad esempio verde o rosso.

Infine: Alcuni Post-Processing
ho suggerito di aggiungere un confine artificiale per l'immagine. Questo ha due vantaggi: 1. L'Algoritmo delle Piastre della Marcia funziona fuori dalla scatola. 2. Non è necessario distinguere tra il contorno dell'immagine e l'interfaccia tra due aree all'interno dell'immagine. Basta rimuovere il confine artificiale una volta che hai finito di impostare i bordi colorati - questo rimuoverà le linee colorate al confine dell'immagine.

7

Quanto segue dovrebbe essere adatto alle vostre esigenze (o almeno aiutare). L'idea è quella di suddividersi nelle varie regioni usando controlli logici basati su valori soglia. Il bordo tra queste regioni può essere rilevato utilizzando rotolo NumPy per spostare i pixel x ed y e confronto per vedere se siamo a un bordo,

import matplotlib.pyplot as plt 
import numpy as np 
import scipy as sp 
from skimage.morphology import closing 

thresh1 = 127 
thresh2 = 254 

#Load image 
im = sp.misc.imread('jBD9j.png') 

#Get threashold mask for different regions 
gryim = np.mean(im[:,:,0:2],2) 
region1 = (thresh1<gryim) 
region2 = (thresh2<gryim) 
nregion1 = ~ region1 
nregion2 = ~ region2 

#Plot figure and two regions 
fig, axs = plt.subplots(2,2) 
axs[0,0].imshow(im) 
axs[0,1].imshow(region1) 
axs[1,0].imshow(region2) 

#Clean up any holes, etc (not needed for simple figures here) 
#region1 = sp.ndimage.morphology.binary_closing(region1) 
#region1 = sp.ndimage.morphology.binary_fill_holes(region1) 
#region1.astype('bool') 
#region2 = sp.ndimage.morphology.binary_closing(region2) 
#region2 = sp.ndimage.morphology.binary_fill_holes(region2) 
#region2.astype('bool') 

#Get location of edge by comparing array to it's 
#inverse shifted by a few pixels 
shift = -2 
edgex1 = (region1^np.roll(nregion1,shift=shift,axis=0)) 
edgey1 = (region1^np.roll(nregion1,shift=shift,axis=1)) 
edgex2 = (region2^np.roll(nregion2,shift=shift,axis=0)) 
edgey2 = (region2^np.roll(nregion2,shift=shift,axis=1)) 

#Plot location of edge over image 
axs[1,1].imshow(im) 
axs[1,1].contour(edgex1,2,colors='r',lw=2.) 
axs[1,1].contour(edgey1,2,colors='r',lw=2.) 
axs[1,1].contour(edgex2,2,colors='g',lw=2.) 
axs[1,1].contour(edgey2,2,colors='g',lw=2.) 
plt.show() 

del quale il following. Per semplicità ho usato il rotolo con l'inverso di ogni regione. Puoi spostare ogni regione successiva sulla successiva per rilevare i bordi

Grazie a @Kabyle per aver offerto una ricompensa, questo è un problema che ho impiegato un po 'a cercare una soluzione. Ho provato scipy skeletonize, feature.canny, modulo topology e openCV con scarso successo ... In questo modo è stato il più robusto per il mio caso (tracciamento dell'interfaccia droplet). Spero che sia d'aiuto!

+0

Spiacente, ho appena visto che volevi gli indici, questi possono essere ottenuti usando qualcosa come 'np.ma.nonzero (~ edgex1)' –

+0

grazie mille per questo grande sforzo, proverò la tua soluzione e vedrò se funziona –

+1

Questa è una buona prova, ma vedi cosa succede intorno alla piccola isola di grigio nel mezzo dell'area bianca ... Anche il rotolo ha un po 'di problemi al bordi dell'immagine. –

1

Fondamentalmente per suggestione seguito di pyStarter di utilizzare l'algoritmo quadrato marciando scikit-immagine, i contorni potrebbe desiderati possono essere estratti con il codice da:

import matplotlib.pyplot as plt 
import numpy as np 
import scipy as sp 
from skimage import measure 
import scipy.ndimage as ndimage 
from skimage.color import rgb2gray 
from pprint import pprint 
#Load image 
im = rgb2gray(sp.misc.imread('jBD9j.png')) 

n, bins_edges = np.histogram(im.flatten(),bins = 100) 
# Skip the black area, and assume two distinct regions, white and grey 
max_counts = np.sort(n[bins_edges[0:-1] > 0])[-2:] 
thresholds = np.select(
    [max_counts[i] == n for i in range(max_counts.shape[0])], 
    [bins_edges[0:-1]] * max_counts.shape[0] 
) 
# filter our the non zero values 
thresholds = thresholds[thresholds > 0] 


fig, axs = plt.subplots() 
# Display image 
axs.imshow(im, interpolation='nearest', cmap=plt.cm.gray) 
colors = ['r','g'] 
for i, threshold in enumerate(thresholds): 
    contours = measure.find_contours(im, threshold) 

    # Display all contours found for this threshold 
    for n, contour in enumerate(contours): 
     axs.plot(contour[:,1], contour[:,0],colors[i], lw = 4) 

axs.axis('image') 
axs.set_xticks([]) 
axs.set_yticks([])   
plt.show() 

Edges!

Tuttavia, dall'immagine non c'è una chiara regione grigia definita, quindi ho preso i due maggiori conteggi di intensità nell'immagine e li ho fissati a questi. Un po 'inquietante è la regione rossa nel mezzo della regione bianca, tuttavia penso che questo potrebbe essere ottimizzato con il numero di contenitori nella procedura dell'istogramma. Puoi anche impostarli manualmente come ha fatto Ed Smith.

3

C'è una soluzione molto semplice a questa: per definizione qualsiasi pixel che ha vicini sia bianchi che grigi è sul bordo "rosso", e vicini grigi e neri è sul bordo "verde". I vicini più chiari/più scuri vengono restituiti dai filtri massimo/minimo in skimage.filters.rank e una combinazione binaria di maschere di pixel con un vicino più chiaro/più scuro che sia bianco/grigio o grigio/nero produce rispettivamente i bordi.

Risultato:

enter image description here

A lavorato soluzione:

import numpy 
import skimage.filters.rank 
import skimage.morphology 
import skimage.io 

# convert image to a uint8 image which only has 0, 128 and 255 values 
# the source png image provided has other levels in it so it needs to be thresholded - adjust the thresholding method for your data 
img_raw = skimage.io.imread('jBD9j.png', as_grey=True) 
img = numpy.zeros_like(img, dtype=numpy.uint8) 
img[:,:] = 128 
img[ img_raw < 0.25 ] = 0 
img[ img_raw > 0.75 ] = 255 

# define "next to" - this may be a square, diamond, etc 
selem = skimage.morphology.disk(1) 

# create masks for the two kinds of edges 
black_gray_edges = (skimage.filters.rank.minimum(img, selem) == 0) & (skimage.filters.rank.maximum(img, selem) == 128) 
gray_white_edges = (skimage.filters.rank.minimum(img, selem) == 128) & (skimage.filters.rank.maximum(img, selem) == 255) 

# create a color image 
img_result = numpy.dstack([img,img,img]) 

# assign colors to edge masks 
img_result[ black_gray_edges, : ] = numpy.asarray([ 0, 255, 0 ]) 
img_result[ gray_white_edges, : ] = numpy.asarray([ 255, 0, 0 ]) 

imshow(img_result) 

P.S. I pixel che hanno vicini in bianco e nero o tutti e tre i colori vicini, si trovano in una categoria indefinita. Il codice sopra non li colora. È necessario capire come si desidera che l'output venga colorato in questi casi; ma è facile estendere l'approccio sopra per produrre un'altra maschera o due per quello.

P.S. I bordi sono larghi due pixel. Non c'è modo di aggirarlo senza ulteriori informazioni: i bordi sono tra due aree e non hai definito quale delle due aree vuoi sovrapporre in ogni caso, quindi l'unica soluzione simmetrica è sovrapporre entrambe le aree di una pixel.

P.S. Questo conta il pixel stesso come suo vicino. Un pixel bianco o nero isolato su grigio, o viceversa, sarà considerato come un bordo (così come tutti i pixel attorno ad esso).