2015-04-03 3 views
13

Ich repräsentiere Bilder in Form von 2-D-Arrays. Ich habe dieses Bild:Kantenerkennung für das in der Matrix gespeicherte Bild

original

Wie kann ich die Punkte bekommen, die direkt an den Grenzen der grauen Region sind und sie einfärben?

colorized

Ich möchte separat die Koordinaten der Matrixelemente in Grün und Rot zu bekommen. Ich habe nur weiße, schwarze und graue Bereiche auf der Matrix.

+0

Matrizen sind im Allgemeinen keine gute Möglichkeit, 2D-Pixeldaten darzustellen ... darauf zielt ihre Semantik nicht ab. – Sneftel

+2

@Sneftel tatsächlich Matrizen sind die häufigste, wenn nicht die einzige und beste Möglichkeit, Bilder in Bibliotheken wie OpenCV, SimpleCV und so weiter darzustellen. –

+1

Sie verwechseln Matrizen mit 2D-Arrays. Erstere sind mit einem sehr spezifischen Satz von Operationen ausgestattet, die für Bilddaten völlig bedeutungslos sind. – Sneftel

Antwort

0

Vielleicht eine elegantere Möglichkeit besteht, das zu tun ... aber falls Ihr Array ist ein numpy Array mit Dimensionen (N,N) (Graustufen) Sie

tun können
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) 

Die Zahl 0.2 nur ein Schwelle und muss angepasst werden.

+0

hast du darauf geachtet, dass ich schwarze, weiße und grüne Farben nur auf dem ersten Bild habe? (ja, ich suche nur nach den Koordinaten der Grenzen der grünen Regionen) –

+0

@begeradj: oh ... Entschuldigung. Dies erfordert ein wenig Modifikation. Entspricht Schwarz der Zahl "0" oder "1"? – plonser

+0

Sie können jede Konvention setzen, um die schwarz/weiß und grau Elemente zu definieren. –

0

Ich glaube, Sie suchen wahrscheinlich nach Kantendetektionsverfahren für Graustufenbilder. Es gibt viele Möglichkeiten, dies zu tun. Vielleicht kann das helfen http://en.m.wikipedia.org/wiki/Edge_detection. Um die Kanten zwischen Weiß und Grau und die Kanten zwischen Schwarz und Grau zu unterscheiden, verwenden Sie die lokale Durchschnittsintensität.

2

Während die Antwort von plonser ziemlich einfach zu implementieren ist, sehe ich es scheitern, wenn es um scharfe und dünne Kanten geht. Trotzdem schlage ich vor, dass Sie einen Teil seines Ansatzes als Vorkonditionierung verwenden. In einem zweiten Schritt möchten Sie den Marching Squares Algorithm verwenden. Gemäß der Dokumentation von scikit-image ist es

ein Sonderfall des Algorithmus laufender Würfel (Lorensen, William und Harvey E. Cline Marschieren Cubes:.. Eine hohe Auflösung 3D-Oberflächen Construction-Algorithmus Computergrafik (SIGGRAPH 87 Proceedings) 21 (4) Juli 1987 Seite 163-170

Es existiert sogar eine Python-Implementierung als Teil des scikit-image Pakets. ich habe diesen Algorithmus unter Verwendung von (meine eigenen Fortran Implementierung, obwohl) erfolgreich zur Kantenerkennung von Augendiagrammen in der Kommunikation Ingenieurwesen.

Ad 1: Präkonditionierung
eine Kopie des Bildes erstellen und es mit zwei Farben nur machen, z.B. Schwarz-Weiss. Die Koordinaten bleiben gleich, aber Sie stellen sicher, dass der Algorithmus eine Ja/Nein-Entscheidung unabhängig von den Werten, die Sie in Ihrer Matrixdarstellung des Bildes verwenden, treffen kann.

Ad 2: Kantenerkennung
Wikipedia sowie verschiedene Blogs bieten Ihnen eine ziemlich elaborate description des Algorithmus in verschiedenen Sprachen, so werde ich nicht in seinen Einzelheiten. Lassen Sie mich jedoch einen praktischen Hinweis geben:

  1. Ihr Bild hat offene Grenzen am unteren Rand. Anstatt den Algorithmus zu ändern, können Sie künstlich eine weitere Reihe von Pixeln hinzufügen (schwarz oder grau, um die weißen/grauen Bereiche zu begrenzen).
  2. Die Wahl des Startpunktes ist kritisch.Wenn nicht zu viele Bilder verarbeitet werden müssen, sollten Sie sie manuell auswählen. Andernfalls müssen Sie Regeln definieren. Da der Marching-Squares-Algorithmus irgendwo innerhalb eines begrenzten Bereichs beginnen kann, können Sie ein beliebiges Pixel einer bestimmten Farbe/eines bestimmten Werts auswählen, um die entsprechende Kante zu erkennen (es beginnt zunächst in eine Richtung zu laufen, um eine Kante zu finden).
  3. Der Algorithmus gibt die genauen 2D-Positionen zurück, z. (x/y) -Tupel. Sie können entweder
    • iterieren durch die Liste und die entsprechenden Pixel Kolorieren durch einen anderen Wert oder
    • Zuordnung
    • eine mask erstellen Teile des Matrix auszuwählen und den Wert zuweisen, der zu einer anderen Farbe entspricht, z.B. grün oder rot.

Schließlich: Einige Post-Processing
ich eine künstliche Grenze zum Bild hinzuzufügen vorgeschlagen. Dies hat zwei Vorteile: 1. Der Marching Squares Algorithmus funktioniert sofort. 2. Es besteht keine Notwendigkeit, zwischen der Bildgrenze und der Schnittstelle zwischen zwei Bereichen innerhalb des Bildes zu unterscheiden. Entfernen Sie einfach die künstliche Grenze, sobald Sie die farbigen Ränder eingestellt haben - dies entfernt die farbigen Linien an der Grenze des Bildes.

7

Die folgenden sollten hoffentlich für Ihre Bedürfnisse in Ordnung sein (oder zumindest helfen). Die Idee besteht darin, sich in die verschiedenen Regionen unter Verwendung logischer Prüfungen auf der Basis von Schwellenwerten aufzuteilen. Die Kante zwischen diesen Bereichen kann dann numpy Rolle zu verschieben Pixel in x und y und den Vergleich festgestellt werden, um zu sehen, wenn wir an einem Rand sind,

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() 

Welche der following verleiht. Der Einfachheit halber verwende ich Roll mit der Umkehrung jeder Region. Sie könnten jede folgende Region auf die nächste rollen, um Kanten zu erkennen.

Vielen Dank an @Kabyle für die Belohnung, das ist ein Problem, das ich eine Weile nach einer Lösung gesucht habe. Ich habe scipy skelettiere, feature.canny, topology module und openCV mit begrenztem Erfolg ausprobiert ... Dieser Weg war der robusteste für meinen Fall (Droplets Interface Tracking). Ich hoffe es hilft!

+0

Sorry, Sie haben nur die Indizes gesucht, diese können mit etwas wie 'np.ma.nonzero (~ edgex1)' –

+0

vielen Dank dafür erhalten werden große Anstrengung, ich werde deine Lösung ausprobieren und sehen, ob es funktioniert –

+1

Das ist ein guter Versuch, aber schau, was um die kleine graue Insel in der Mitte der weißen Fläche passiert ... Auch Rolle hat ein bisschen ein Problem bei der Kanten des Bildes. –

1

Grundsätzlich mit folgte pyStarter Vorschlag von dem Marsch-Square-Algorithmus aus scikit-Bild verwenden, das konnte gewünschte Konturen können mit dem folgenden Code extrahiert werden:

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!

Allerdings gibt es von Ihrem Bild keinen klar definierten grauen Bereich, also nahm ich die beiden größten Zählungen von Intensitäten im Bild und schwelrestrierte darauf. Ein bisschen störend ist die rote Region in der Mitte der weißen Region, aber ich denke, dass dies mit der Anzahl der Bins in der Histogramm-Prozedur optimiert werden könnte. Sie können diese auch manuell wie Ed Smith einstellen.

3

Es gibt eine sehr einfache Lösung: per Definition ist jeder Pixel, der sowohl weiße als auch graue Nachbarn hat, auf Ihrer "roten" Kante und graue und schwarze Nachbarn auf der "grünen" Kante. Die hellsten/dunkelsten Nachbarn werden von den maximalen/minimalen Filtern in skimage.filters.rank zurückgegeben, und eine binäre Kombination von Masken von Pixeln, die einen hellsten/dunkelsten Nachbarn haben, der weiß/grau oder grau/schwarz ist, erzeugt jeweils die Kanten.

Ergebnis:

enter image description here

Eine Lösung gearbeitet:

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. Pixel, die schwarze und weiße Nachbarn oder alle drei Farben Nachbarn haben, sind in einer undefinierten Kategorie. Der obige Code färbt diese nicht ein. Sie müssen herausfinden, wie die Ausgabe in diesen Fällen farbig sein soll. aber es ist leicht, den obigen Ansatz zu erweitern, um eine oder zwei weitere Masken zu erzeugen.

P.S. Die Kanten sind zwei Pixel breit. Ohne weitere Informationen kommt man nicht herum: Die Kanten liegen zwischen zwei Bereichen, und Sie haben nicht definiert, welcher der beiden Bereiche sich jeweils überlappen soll. Die einzige symmetrische Lösung besteht darin, beide Bereiche um eins zu überlappen Pixel.

P.S. Dies zählt das Pixel selbst als seinen eigenen Nachbarn. Ein isoliertes weißes oder schwarzes Pixel auf Grau oder umgekehrt wird als Kante betrachtet (sowie alle umgebenden Pixel).

Verwandte Themen