2012-07-24 5 views
15

Ho sviluppato un'applicazione per contare oggetti circolari come colonie batteriche dalle immagini.Rileva cluster di oggetti circolari mediante soglie adattative e analisi di forma iterative

Ciò che rende semplice il fatto che gli oggetti sono generalmente ben distinti dallo sfondo.

Tuttavia, poche difficoltà effettuare l'analisi ingannevole:

  1. Lo sfondo presenterà graduale e rapido cambiamento di intensità.
  2. Nei bordi del contenitore, l'oggetto sarà ellittico anziché circolare.
  3. I bordi degli oggetti a volte sono piuttosto sfocati.
  4. Gli oggetti verranno raggruppati.
  5. L'oggetto può essere molto piccolo (6px di diametro)
  6. In definitiva, gli algoritmi verranno utilizzati (tramite GUI) da persone che non hanno una profonda comprensione dell'analisi delle immagini, quindi i parametri devono essere intuitivi e molto pochi.

Il problema è stato affrontato molte volte nella letteratura scientifica e "risolto", ad esempio utilizzando la trasformazione circolare Hough o gli approcci spartiacque, ma non sono mai stato soddisfatto dai risultati.

Un approccio semplice che è stato descritto è quello di ottenere il primo piano mediante soglie adattive e divisione (come ho descritto in this post) gli oggetti in cluster utilizzando la trasformazione a distanza.

Ho implementato con successo questo metodo, ma non è sempre stato possibile gestire cambiamenti improvvisi di intensità. Inoltre, mi è stato chiesto dai colleghi di uscire con un approccio più "nuovo".

Ho quindi cercato un nuovo metodo per estrarre in primo piano.

Ho quindi studiato altri metodi di rilevamento soglia/blob. Ho provato MSER ma ho scoperto che non erano molto robusti e piuttosto lenti nel mio caso.

io alla fine è venuto fuori con un algoritmo che, finora, mi dà ottimi risultati:

  1. ho diviso i tre canali della mia immagine e ridurre il loro rumore (sfocatura/sfocatura mediana). Per ogni canale:
  2. Applico un'implementazione manuale del primo passaggio della soglia adattativa calcolando la differenza assoluta tra il canale originale e uno sfocato (con una sfocatura del kernel di grandi dimensioni). Quindi, per tutti i valori rilevanti di soglia:
  3. applico una soglia sul risultato di 2)
  4. trovare contorni
  5. convalidare o contorni sulla concessione della loro forma (dimensioni, settore, convessità ...
  6. solo le regioni continue valide (ovvero delimitate da contorni) vengono quindi ridisegnate in un accumulatore (1 accumulatore per canale).
  7. Dopo aver accumulato regioni continue su valori di soglia, ho terminato con una mappa di "punteggi di regioni".Le regioni con l'intensità più alta sono quelle che soddisfano più spesso i criteri del filtro morfologico.
  8. Le tre mappe (uno per canale) vengono poi convertiti in scala di grigi e thresholded (la soglia è controllato dall'utente)

solo per mostrarvi il tipo di immagine che ho di lavorare con: enter image description here Questa immagine rappresenta parte di 3 immagini campione nella parte superiore e il risultato del mio algoritmo (blu = primo piano) delle rispettive parti in basso.

Ecco la mia applicazione C++ di: 3-7

/* 
* cv::Mat dst[3] is the result of the absolute difference between original and convolved channel. 
* MCF(std::vector<cv::Point>, int, int) is a filter function that returns an positive int only if the input contour is valid. 
*/ 

/* Allocate 3 matrices (1 per channel)*/ 
cv::Mat accu[3]; 

/* We define the maximal threshold to be tried as half of the absolute maximal value in each channel*/ 
int maxBGR[3]; 
for(unsigned int i=0; i<3;i++){ 
    double min, max; 
    cv::minMaxLoc(dst[i],&min,&max); 
    maxBGR[i] = max/2; 
    /* In addition, we fill accumulators by zeros*/ 
    accu[i]=cv::Mat(compos[0].rows,compos[0].cols,CV_8U,cv::Scalar(0)); 
} 
/* This loops are intended to be multithreaded using 
#pragma omp parallel for collapse(2) schedule(dynamic) 
For each channel */ 
for(unsigned int i=0; i<3;i++){ 
    /* For each value of threshold (m_step can be > 1 in order to save time)*/ 
    for(int j=0;j<maxBGR[i] ;j += m_step){ 
      /* Temporary matrix*/ 
      cv::Mat tmp; 
      std::vector<std::vector<cv::Point> > contours; 
      /* Thresholds dst by j*/ 
      cv::threshold(dst[i],tmp, j, 255, cv::THRESH_BINARY); 
      /* Finds continous regions*/ 
      cv::findContours(tmp, contours, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_L1); 
      if(contours.size() > 0){ 
       /* Tests each contours*/ 
       for(unsigned int k=0;k<contours.size();k++){ 
        int valid = MCF(contours[k],m_minRad,m_maxRad); 
        if(valid>0){ 
         /* I found that redrawing was very much faster if the given contour was copied in a smaller container. 
         * I do not really understand why though. For instance, 
         cv::drawContours(miniTmp,contours,k,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y)); 
         is slower especially if contours is very long. 
         */ 
         std::vector<std::vector<cv::Point> > tpv(1); 
         std::copy(contours.begin()+k, contours.begin()+k+1, tpv.begin()); 
         /* We make a Roi here*/ 
         cv::Rect rect = cv::boundingRect(tpv[0]); 
         cv::Mat miniTmp(rect.height,rect.width,CV_8U,cv::Scalar(0)); 
         cv::drawContours(miniTmp,tpv,0,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y)); 
         accu[i](rect) = miniTmp + accu[i](rect); 
        } 
       } 
      } 
     } 
    } 
/* Make the global scoreMap*/ 
cv::merge(accu,3,scoreMap); 
/* Conditional noise removal*/ 
if(m_minRad>2) 
    cv::medianBlur(scoreMap,scoreMap,3); 
cvtColor(scoreMap,scoreMap,CV_BGR2GRAY); 

Ho due domande:

  1. Qual è il nome di tale approccio estrazione di primo piano e vedete alcuna ragione per la quale potrebbe essere improprio usarlo in questo caso?

  2. Poiché la ricerca e il disegno di contorni ricorsivi è piuttosto intensivo, vorrei rendere più veloce il mio algoritmo. Puoi indicarmi un modo per raggiungere questo obiettivo?

La ringrazio molto per l'aiuto,

+2

Hai provato [Metodo Otsu] (http://en.wikipedia.org/wiki/Otsu%27s_method), invece di punti 2 e 3 ? È una soglia adattiva estremamente veloce che può essere facilmente calcolata dall'istogramma. Di solito funziona abbastanza bene per la soppressione dello sfondo. La sintassi è ad es. 'cvThreshold (img_src, img_dest, 128, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); '. – smocking

+0

@smocking La ringrazio molto, l'ho fatto, ma 1) ho trovato che non funziona bene come soglia adattiva per tutti i miei campioni. E 2), i passaggi 2 e 3 sono relativamente veloci (rispetto a 3-7) per produrre l'immagine in scala di grigi necessaria per il secondo ... –

+0

OK suona bene; che dire dell'utilizzo di [analisi dei componenti connessi] (http://en.wikipedia.org/wiki/Connected_component_labeling) per trovare le forme contigue? Sfortunatamente non è nella libreria OpenCV di base, ma c'è una libreria chiamata [cvBlobsLib] (http://opencv.willowgarage.com/wiki/cvBlobsLib/) che lo farà. – smocking

risposta

2

Diversi anni fa ho scritto un'applicazione che rileva le cellule immagine un microscopio in. Il codice è scritto in Matlab, e ora penso che sia più complicato di quello che dovrebbe essere (era il mio primo progetto di CV), quindi traccerò solo trucchi che saranno effettivamente utili per voi. A proposito, era mortalmente lento, ma era davvero bravo a separare grandi gruppi di celle gemelle.

ho definita una metrica cui calcolare la probabilità che un dato punto è il centro di una cella: - Luminosità diminuisce in modo circolare intorno - La varianza della luminosità trama segue un dato schema - un la cella non copre più del% di una cella vicina

Con esso, ho iniziato a cercare iterativamente la cella migliore, contrassegnarla come trovata, quindi cercare quella successiva. Poiché tale ricerca è costosa, ho utilizzato algoritmi genetici per effettuare ricerche più rapidamente nel mio spazio delle funzionalità.

Alcuni risultati sono i seguenti:

Cells 2 Cells

+0

Grazie, mi piace l'approccio che hai usato per il tuo progetto !. Puoi pubblicare gli originali delle foto che hai inserito? Nel mio caso, non penso di poter andare così in profondità dal momento che non voglio fare ipotesi extra sul pattern di luminosità o sulla texture (alcuni oggetti sono più scuri nei bordi, ad esempio). Il mio obiettivo è trovare una soluzione versatile e, come ho detto nel mio post, sono anche preoccupato dell'elaborazione della velocità. –

+0

Sì, penso che fosse eccessivamente complicato, ma i risultati sono stati fantastici. Penso che una soluzione più semplice ti dia buoni risultati, come la morfologia, combinata con la soglia adattiva. Pubblicherò le foto, e forse qualche informazione in più, quando torno a casa. – Sam