53

Sto scrivendo per Android con OpenCV. Sto segmentando un'immagine simile a quella di sotto usando uno spartiacque controllato da un indicatore, senza che l'utente contrassegni manualmente l'immagine. Sto pensando di usare i massimi regionali come indicatori.Come definire i marker per Watershed in OpenCV?

minMaxLoc() mi darebbe il valore, ma come posso restringerlo ai BLOB che è quello che mi interessa? Posso utilizzare i risultati dei BLOB cvBlob da findContours() o cvBlob per limitare il ROI e applicare i massimi a ciascun blob?

input image

+1

La risposta dal @mmgp è la soluzione corretta a questa domanda. Sento che dovresti contrassegnarlo come la risposta accettata. –

risposta

91

Innanzitutto: la funzione minMaxLoc trova solo il minimo globale e massima globale per un dato ingresso, quindi è più inutile per determinare i minimi regionale e/o massimi regionale. Ma la tua idea è giusta, l'estrazione di marcatori in base ai minimi/massimi regionali per eseguire una Trasformazione Spartiacque basata su marcatori è assolutamente soddisfacente. Lasciatemi provare a chiarire qual è la Trasformata di Spartiacque e come dovresti utilizzare correttamente l'implementazione presente in OpenCV.

Una discreta quantità di carte che parlano di spartiacque la descrivono in modo simile a quanto segue (potrei perdere qualche dettaglio, se non sei sicuro: chiedi). Considera la superficie di alcune regioni che conosci, contiene valli e picchi (tra gli altri dettagli che per noi sono irrilevanti). Supponiamo che al di sotto di questa superficie tutto ciò che hai sia acqua, acqua colorata. Ora, fai dei buchi in ogni valle della tua superficie e poi l'acqua inizia a riempire tutta l'area. Ad un certo punto si incontreranno acque diversamente colorate, e quando questo accadrà, costruirai una diga in modo che non si tocchino. Alla fine hai una collezione di dighe, che è lo spartiacque che separa tutte le diverse acque colorate.

Ora, se si fanno troppi fori in quella superficie, si finisce con troppe regioni: sovra-segmentazione.Se ne fai troppo pochi ottieni una sotto-segmentazione. Quindi, praticamente tutti i documenti che suggeriscono l'utilizzo dello spartiacque presentano effettivamente tecniche per evitare questi problemi per l'applicazione con cui si occupa la carta.

Ho scritto tutto questo (che è forse troppo ingenuo per chiunque sappia cos'è la Trasformazione di Spartiacque) perché riflette direttamente su come si dovrebbero usare implementazioni di spartiacque (che la risposta attualmente accettata sta facendo in modo completamente sbagliato). Iniziamo ora sull'esempio OpenCV, usando i binding Python.

L'immagine presentata nella domanda è composta da molti oggetti che sono per lo più troppo vicini e in alcuni casi sovrapposti. L'utilità dello spartiacque qui è di separare correttamente questi oggetti, non di raggrupparli in un singolo componente. Quindi hai bisogno di almeno un marcatore per ogni oggetto e buoni indicatori per lo sfondo. Ad esempio, per prima cosa binarizzare l'immagine di input con Otsu ed eseguire un'apertura morfologica per rimuovere piccoli oggetti. Il risultato di questo passaggio è mostrato sotto nell'immagine a sinistra. Ora con l'immagine binaria, considera di applicare la trasformazione della distanza ad essa, risultato a destra.

enter image description hereenter image description here

Con il risultato di trasformare distanza, possiamo considerare una certa soglia in modo tale che noi consideriamo solo le regioni più lontane allo sfondo (immagine a sinistra in basso). In questo modo, possiamo ottenere un marcatore per ogni oggetto etichettando le diverse regioni dopo la soglia precedente. Ora, possiamo anche considerare il bordo di una versione dilatata dell'immagine di sinistra sopra per comporre il nostro marcatore. Il marcatore completo è mostrato sotto a destra (alcuni marker sono troppo scuri per essere visti, ma ogni regione bianca nell'immagine a sinistra è rappresentata nell'immagine giusta).

enter image description hereenter image description here

Questo marcatore che abbiamo qui fa un sacco di senso. Ogni colored water == one marker inizierà a riempire la regione e la trasformazione spartiacque costruirà dighe per impedire che i diversi "colori" si uniscano. Se facciamo la trasformazione, otteniamo l'immagine a sinistra. Considerando solo le dighe componendole con l'immagine originale, otteniamo il risultato a destra.

enter image description hereenter image description here

import sys 
import cv2 
import numpy 
from scipy.ndimage import label 

def segment_on_dt(a, img): 
    border = cv2.dilate(img, None, iterations=5) 
    border = border - cv2.erode(border, None) 

    dt = cv2.distanceTransform(img, 2, 3) 
    dt = ((dt - dt.min())/(dt.max() - dt.min()) * 255).astype(numpy.uint8) 
    _, dt = cv2.threshold(dt, 180, 255, cv2.THRESH_BINARY) 
    lbl, ncc = label(dt) 
    lbl = lbl * (255/(ncc + 1)) 
    # Completing the markers now. 
    lbl[border == 255] = 255 

    lbl = lbl.astype(numpy.int32) 
    cv2.watershed(a, lbl) 

    lbl[lbl == -1] = 0 
    lbl = lbl.astype(numpy.uint8) 
    return 255 - lbl 


img = cv2.imread(sys.argv[1]) 

# Pre-processing. 
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  
_, img_bin = cv2.threshold(img_gray, 0, 255, 
     cv2.THRESH_OTSU) 
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, 
     numpy.ones((3, 3), dtype=int)) 

result = segment_on_dt(img, img_bin) 
cv2.imwrite(sys.argv[2], result) 

result[result != 255] = 0 
result = cv2.dilate(result, None) 
img[result == 255] = (0, 0, 255) 
cv2.imwrite(sys.argv[3], img) 
+1

Grazie per il tutorial. Molto ben spiegato per noi non ha familiarità con l'algoritmo spartiacque. Come hai detto, il numero di oggetti segmentati dipenderà in gran parte da quanti marcatori sono stati trovati nei passaggi principali e qui, sembra che la trasformazione della distanza seguita dalla soglia abbia lasciato alcune pillole segmentate come una sola. Potremmo aver migliorato i risultati cambiando i parametri della soglia? – Denis

+1

dovrebbe essere 'lbl * (255/(ncc + 1))' altrimenti si perde un contorno – Nikita

45

vorrei spiegare un semplice codice su come utilizzare spartiacque qui. Sto usando OpenCV-Python, ma spero tu non abbia alcuna difficoltà a capire.

In questo codice, userò lo spartiacque come strumento per l'estrazione in primo piano di . (Questo esempio è la controparte python del codice C++ nel ricettario OpenCV). Questo è un caso semplice per capire lo spartiacque. Oltre a questo, puoi usare lo spartiacque per contare il numero di oggetti in questa immagine. Questa sarà una versione leggermente avanzata di questo codice.

- Prima carichiamo la nostra immagine, la convertiamo in scala di grigi e la soglia con un valore adeguato. Ho preso binarizzazione di Otsu, quindi avrebbe trovato il miglior valore di soglia.

import cv2 
import numpy as np 

img = cv2.imread('sofwatershed.jpg') 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 

seguenti è il risultato che ho ottenuto:

enter image description here

(anche questo risultato è buono, perché grande contrasto tra sfondo e primo piano le immagini)

2 - Ora dobbiamo creare il marcatore Marker è l'immagine con le stesse dimensioni dell'immagine originale che è 32SC1 (canale singolo con segno a 32 bit).

Ora ci saranno alcune regioni nell'immagine originale in cui sei semplicemente sicuro, quella parte appartiene al primo piano. Contrassegna tale regione con 255 nell'immagine del marcatore. Ora la regione in cui sei sicuro di essere sullo sfondo è contrassegnata da 128. La regione in cui non sei sicuro è contrassegnata con 0. Questo è quanto faremo dopo.

A - Regione primo piano: - Abbiamo già un'immagine di soglia in cui le pillole sono di colore bianco. Li erodiamo un po ', così siamo sicuri che la restante regione sia in primo piano.

fg = cv2.erode(thresh,None,iterations = 2) 

fg:

enter image description here

B - Regione Background: - Qui abbiamo dilatare l'immagine in modo che thresholded regione di fondo viene ridotto. Ma siamo sicuri che la restante regione nera sia al 100% di background. Lo impostiamo a 128.

bgt = cv2.dilate(thresh,None,iterations = 3) 
ret,bg = cv2.threshold(bgt,1,128,1) 

ora otteniamo bg come segue:

enter image description here

C - Ora possiamo aggiungere sia fg e bg:

marker = cv2.add(fg,bg) 

Qui di seguito è quello che si ottiene:

enter image description here

Ora possiamo capire chiaramente dall'immagine sopra, che la regione bianca è al 100% in primo piano, la regione grigia è al 100% di sfondo e la regione nera non è sicura.

Poi abbiamo convertirlo in 32SC1:

marker32 = np.int32(marker) 

3 - Finalmente siamo applichiamo spartiacque e risultato riconvertire in uint8 immagine:

cv2.watershed(img,marker32) 
m = cv2.convertScaleAbs(marker32) 

m:

enter image description here

- Abbiamo soglia in modo corretto per ottenere la maschera ed eseguire bitwise_and l'immagine in ingresso con:

ret,thresh = cv2.threshold(m,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 
res = cv2.bitwise_and(img,img,mask = thresh) 

res:

enter image description here

Speranza che aiuta! !!

ARK

+4

Considera di ricontrollare questo, perché sta mostrando un uso non corretto dello spartiacque. – mmgp

+2

@mmgp: potrebbe essere che hai ragione. Questa è solo una versione python del codice C++ di base fornita nel ricettario, che mostra di impostare i marcatori, ecc. Ho pensato che sarebbe stato un buon esempio per i fresher, incluso me. Ad ogni modo, la tua risposta è grandiosa. Riempie ciò che manca nella mia risposta. –

+0

-1 come da mmgp commento –

Problemi correlati