2013-12-12 2 views
5

Sto lavorando su alcune immagini foglia utilizzando OpenCV (Java). Le foglie sono catturati su una carta bianca e alcuni ha ombre come questo:Approccio automatico per rimuovere l'ombra dell'oggetto colord su sfondo bianco?

enter image description here

Naturalmente, è in qualche modo il caso estremo (vi sono ombre lievi).

Ora, voglio fissare la foglia e rimuovere anche l'ombra (mentre riservo i dettagli della foglia).


mio flusso di corrente è questo:

1) Conversione in HSV ed estraendo il Saturazione canale:

Imgproc.cvtColor(colorMat, colorMat, Imgproc.COLOR_RGB2HSV); 
ArrayList<Mat> channels = new ArrayList<Mat>(); 
Core.split(colorMat, channels); 
satImg = channels.get(1); 

2) De-rumorose (mediana) e applicando adaptiveThreshold :

Imgproc.medianBlur(satImg , satImg , 11); 
Imgproc.adaptiveThreshold(satImg , satImg , 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 401, -10); 

E il risultato è questo:

enter image description here

sembra ok, ma l'ombra sta causando alcune anomalie lungo il margine sinistro. Inoltre, ho la sensazione che non sto usando lo sfondo bianco a mio vantaggio.

Ora ho 2 domande:
1) Come posso migliorare il risultato e sbarazzarmi dell'ombra?

2) Posso ottenere buoni risultati senza lavorare sul canale di saturazione?. Il motivo per cui lo chiedo è che sulla maggior parte delle mie immagini, lavorando sul canale L (da HLS) si ottengono risultati migliori (a parte l'ombra, ovviamente).


Aggiornamento: Utilizzando il canale Hue rende threshdolding meglio, ma rende la situazione ombra peggio:

enter image description here


Update2: In alcuni casi, l'ipotesi che l'ombra è più scura di quanto la foglia non regga sempre. Quindi, lavorare sulle intensità non aiuterà. Sto guardando più verso un approccio dei canali di colore.

+1

Poiché la foglia è verde e lo sfondo è bianco/ombra, è possibile lavorare sul canale della tonalità. – GilLevi

+0

@GilLevi, vedere l'aggiornamento. Non aiuta a eliminare l'ombra. – Mahm00d

+0

Hmmm, non sono davvero sicuro. Forse usando un diverso spazio colore, come LAB? Mi dispiace, non posso aiutare ulteriormente. – GilLevi

risposta

2

Io non uso opencv, invece stavo cercando di usare il toolbox di elaborazione immagini matlab per estrarre la foglia. Speriamo che opencv abbia tutte le funzioni di elaborazione per te. Si prega di vedere il mio risultato qui sotto. Ho eseguito tutte le operazioni nell'immagine originale canale 3 e canale 1.

Per prima cosa ho usato il canale 3, impostandolo su 100 (in alto a sinistra). Quindi rimuovo le regioni sul bordo e le regioni con le dimensioni dei pixel inferiori a 100, riempiendo il foro nella foglia, il risultato è mostrato nella parte superiore destra.

Successivamente ho usato il canale 1, ho fatto la stessa cosa che ho fatto nel canale 3, il risultato è mostrato in basso a sinistra. Poi ho scoperto le regioni collegate (ce ne sono solo due come si può vedere nella figura in basso a sinistra), rimuovere quella con un'area più piccola (mostrata in basso a destra).

enter image description here

Supponiamo l'immagine in alto a destra è I1, e l'immagine in basso a destra è I, il foglio viene estratto da attuare ~I && I1. La foglia è:

enter image description here

Speranza che aiuta. Grazie

+0

Davvero un buon approccio. Anche se ci sono 2 problemi nel rendere questo automatico: 1) Non conosco la soglia statica dell'ombra, 2) Non c'è garanzia che la parte più grande sia l'ombra e non l'altra parte (parte della foglia). – Mahm00d

+1

Grazie Mahm00d. La soglia nell'elaborazione delle immagini è sempre delicata, per questo potrebbe essere necessario selezionare i canali che separano la foglia e l'ombra con il margine di intensità maggiore. E potresti aver bisogno di più campioni per stimare approssimativamente i canali e la soglia. Per quanto riguarda i componenti di connessione, l'intensità della radice è molto più vicina all'ombra rispetto alla foglia. Ecco perché di solito ci sono due parti connesse rilevate. A volte la radice può contenere alcune minuscole parti nella foglia, tuttavia l'ipotesi che la maggior parte sarebbe l'ombra è vera la maggior parte delle volte. – lennon310

+0

Hai ragione riguardo alla parte difficile. Ho un campione con una foglia verde scuro. In tal caso, è quasi impossibile trovare una soglia statica che non includa anche parti della foglia. Ancora peggio, a volte la foglia può essere più scura dell'ombra! Quindi, penso che lavorare sui canali anziché sulle intensità sarebbe un approccio migliore. – Mahm00d

1

ho provato due cose diverse: 1. altro della soglia sul canale di saturazione 2. cercare di trovare due contorni: ombra e foglia

io uso C++ in modo che i frammenti di codice avrà un aspetto un po 'diverso.

cercando Otsu-thresholding invece di soglia adattativa:

cv::threshold(hsv_imgs,mask,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU); 

che porta a seguire le immagini (solo OTSU della soglia sul canale saturazione):

enter image description hereenter image description here

l'altra cosa sta calcolando informazioni gradiente (Ho usato sobel, vedi la documentazione di oppenCV), ponendo le soglie che e dopo un operatore di apertura ho usato findContours dando qualcosa di simile, non ancora utilizzabile (gradiente approccio contorno):

enter image description hereenter image description here

0

sto cercando di fare la stessa cosa con le foto di farfalle, ma con sfondi più irregolari e imprevedibili, come this. Una volta identificata una buona parte dello sfondo (ad esempio tramite la soglia, o come facciamo, riempire inondazioni da punti casuali), ciò che funziona bene è usare l'algoritmo GrabCut per ottenere tutti quei bit che potresti perdere sul passaggio iniziale. In Python, supponendo che si vuole ancora identificare un'area iniziale di sfondo dal thresholding sul canale di saturazione, provare qualcosa di simile

import cv2 
import numpy as np 

img = cv2.imread("leaf.jpg") 
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1] 
sat = cv2.medianBlur(sat, 11) 
thresh = cv2.adaptiveThreshold(sat , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10); 
cv2.imwrite("thresh.jpg", thresh) 

h, w = img.shape[:2] 
bgdModel = np.zeros((1,65),np.float64) 
fgdModel = np.zeros((1,65),np.float64) 
grabcut_mask = thresh/255*3 #background should be 0, probable foreground = 3 
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) 
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8') 

cv2.imwrite("GrabCut1.jpg", img*grabcut_mask[...,None]) 

Questo in realtà si libera delle ombre per voi, in questo caso, perché il bordo dell'ombra ha effettivamente alti livelli di saturazione, quindi è incluso nella cancellazione del taglio. (Pubblicheremo le immagini, ma non ho abbastanza reputazione)

Di solito, tuttavia, non è possibile considerare le ombre da includere nel rilevamento dello sfondo. In questo caso probabilmente vorrai confrontare le aree dell'immagine con il colore dello sfondo ora noto usando la distorsione di cromaticità proposta da Horprasert et. al. (1999) in "Un approccio statistico per la sottrazione di fondo e il rilevamento dell'ombra" in tempo reale. Questa misura tiene conto del fatto che per i colori desaturati, la tonalità non è una misura pertinente.

Si noti che il pdf della prestampa che si trovano on-line ha un errore (no segni +) nell'equazione 6. È possibile utilizzare la versione re-citato in Rodriguez-Gomez et al (2012), equazioni 1 & 2.Oppure si può utilizzare il mio codice python di seguito:

def brightness_distortion(I, mu, sigma): 
    return np.sum(I*mu/sigma**2, axis=-1)/np.sum((mu/sigma)**2, axis=-1) 


def chromacity_distortion(I, mu, sigma): 
    alpha = brightness_distortion(I, mu, sigma)[...,None] 
    return np.sqrt(np.sum(((I - alpha * mu)/sigma)**2, axis=-1)) 

è possibile alimentare il fondo noto significare & stdev come gli ultimi due parametri della funzione chromacity_distortion, e l'immagine RGB dei pixel come primo parametro, che dovrebbe mostrare che l'ombra è fondamentalmente la stessa cromaticità dello sfondo e molto diversa dalla foglia. Nel codice qui sotto, ho quindi eseguito il thresholding sulla chromacity e ho fatto un altro passaggio grabcut. Questo funziona per rimuovere l'ombra, anche se il primo passaggio grabcut non (ad esempio, se in origine thresholded sulla tonalità)

mean, stdev = cv2.meanStdDev(img, mask = 255-thresh) 
mean = mean.ravel() #bizarrely, meanStdDev returns an array of size [3,1], not [3], so flatten it 
stdev = stdev.ravel() 
chrom = chromacity_distortion(img, mean, stdev) 
chrom255 = cv2.normalize(chrom, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)[:,:,None] 
cv2.imwrite("ChromacityDistortionFromBackground.jpg", chrom255) 

thresh2 = cv2.adaptiveThreshold(chrom255 , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10); 
cv2.imwrite("thresh2.jpg", thresh2) 

grabcut_mask[...] = 3 
grabcut_mask[thresh==0] = 0 #where thresh == 0, definitely background, set to 0 
grabcut_mask[np.logical_and(thresh == 255, thresh2 == 0)] = 2 #could try setting this to 2 or 0 
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) 
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8') 
cv2.imwrite("final_leaf.jpg", grabcut_mask[...,None]*img) 

ho paura con i parametri che ho provato, questo elimina ancora il gambo, però. Penso che sia perché GrabCut pensa che abbia un colore simile alle ombre. Fammi sapere se trovi un modo per tenerlo.