Rozpoznávání obrazu s buňkami

milo86

Rozpoznávání obrazu s buňkami
« kdy: 31. 01. 2014, 16:42:02 »
Ahoj mám otázku ohledně naprogramování rozpoznání obrazu.
Jedná se o spočtení fosforem označených buňek. Hloupé je, že aktivní látka je i mimo buňky v roztoku, takže obrázek je ve stupních zelenné a ještě jsou dost patrné podsvetlovací ledky.
obrázek je k nahlédnutí zde : https://drive.google.com/file/d/0B2DyiLtSwdCvQ3drRDJWRzVfVFU/edit?usp=sharing
obrázek jsem 8x zmenšil oproti originálu co leze z foťáku.
muj pokus zde : https://drive.google.com/file/d/0B2DyiLtSwdCvWmVSMUx1c1UwdXM/edit?usp=sharing

Problém je že některé buňky jsou blízko sebe a materiál mezi nimi je nasycen fosforem vice než jádra některých buněk. Nedochází mi jakou operaci-e mám na obrázek provést abych jej mohl snadno převést do binárního obrazu a zůstaly mi jen jádra buněk. Protože ať jsem obraz filtroval jak jsem chtěl tak buď zůstaly některý buňky spojené anebo jsem o některé buňky přišel.
Normálně k vyhodnocení používám knihovnu opencv, kde se považuji za zkušeného začátečníka, tato knihovna snadno spočítá uzavřený plochy proto jsem převádím do binárního souboru.

pokud je nějaké jednodušší řešení na spolehlivé sečtení buněk tak budu také rád.
díky Milan
« Poslední změna: 31. 01. 2014, 17:46:11 od Petr Krčmář »


Pavel Tisnovsky

Re:rozpoznávání obrazu
« Odpověď #1 kdy: 31. 01. 2014, 17:43:34 »

Problém je že některé buňky jsou blízko sebe a materiál mezi nimi je nasycen fosforem vice než jádra některých buněk. Nedochází mi jakou operaci-e mám na obrázek provést abych jej mohl snadno převést do binárního obrazu a zůstaly mi jen jádra buněk. Protože ať jsem obraz filtroval jak jsem chtěl tak buď zůstaly některý buňky spojené anebo jsem o některé buňky přišel.


Globalne aplikovany prah tady nebude fungovat, chtelo by to lokalni filtr napriklad ve stylu "pokud je svetlost pixelu o 50% mensi nez prumerna svetlost okolnich pixelu (4-okoli, 8-okoli), nastav cernou, jinak bilou". Bude to urcite chtit otestovat na mnoha snimcich.

JardaP .

  • *****
  • 11 064
    • Zobrazit profil
    • E-mail
Re:Rozpoznávání obrazu s buňkami
« Odpověď #2 kdy: 31. 01. 2014, 18:40:33 »
Otazka je, jestli dokazete ty bunky spocitat podle oka. Napriklad, kolik bunek je tady: http://img249.imagevenue.com/img.php?image=82358_segment1_122_5lo.jpg a kolik tady: http://img45.imagevenue.com/img.php?image=th_82362_segment2_122_389lo.jpg? (BTW, az zase nekam budete davat obrazek, tak ho nedavejte na Google, kde se domahaji zalogovani).

Neni mi zcela jasne, jestli se jedna o par obzvlaste tlustych bunek nebo je jich nekolik tak sikovne slepenych, ze vypadaji jako jedna velka.

Pokud se jedna vzdy o jednotlive bunky a ne o slepence, a pokud se da definovat neco jako minimalni mozna vzdalenost bunek, mohl byste vysroubovat konstrast a stahnout jas tak, az obraz zcerna. Nasledne po krocich zvysovat jas a sledovat, kde se objevi zeleny bod. Pozici vzdy oznacit a o krok zvysit jas. Nasledne znovu zjistit, jestli se nekde objevila barva. Pokud je zhruba ve vzdalenosti definovane minimalni vzdalenosti bunek od nejblizsiho jiz znameho bodu nebo vetsi, tak poznacit, jinak ignorovat. Atd, az dojedete s jasem k maximu nebo spise nejake smyslyplne hodnote, kterou si vyzkousite, protoze od urciteho jasu z toho vychazeji blbosti.

Bylo by to mnohapruchodove a asi ne moc rychle. Vyzkousejte si to v GIMPu.

Re:Rozpoznávání obrazu s buňkami
« Odpověď #3 kdy: 01. 02. 2014, 06:39:16 »
Je si s tím potřeba ještě pohrát, zdaleka to není univerzální řešení. Testoval jsem to jen na prvním obrázku a při stažení toho posuvníku na nízký treshold to dává snad i celkem přijatelné výsledky (jestli jsou teda ty buňky tři). Pro jiné obrázky to pravděpodobně už tak dobře fungovat nebude, kritické bude mít správné parametry ve funkci pro odstraňování zelené barvy. Tam by možná stálo za to vyzkoušet chytit se histogramu: http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html#histogram-calculation. Obecně platí, že pro testování něčeho takového to bude chtít mnohem víc obrázků než jen dva.

Teď jsem na to ještě nesebral síly ale dobré by bylo testovat kolize obdélníků a případně je slučovat do větších. Jakési nehotové cosi tam je přítomno  :).
Minimální velikost obdélníku, který bude vykreslen je pevně zafixovaná, to je špatně. Řekl bych, že by bylo vhodné vycházet z úrovně zazoomování foťáku a fyzické velikosti buňek, když se něco ví jistě, není potřeba se k tomu zkoušet nějak doadaptovávat.

No, zlomte vaz a nenamnožte si tam zelené mužíky  ;D

Kód: [Vybrat]
//============================================================================
// Name        : ocv_bunky.cpp
// Author      : Buněčňák
// Version     :
// Copyright   : moje
// Description : Hello World in C++, Ansi-style
//============================================================================
//zalozeno na http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html#bounding-rects-circles


#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <cv.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

Mat imgSeda;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// Function header
void thresh_callback(int, void* );


inline bool zdaKolize(Rect A,Rect B)
{
if((max(A.x,B.x)<min((A.x+A.width),(B.x+B.width)))&&(max(A.y,B.y)<min(A.y+A.height,B.y+B.height)))
return true;else return false;
}

inline Rect sloucitRect(Rect A,Rect B)
{
Rect vratit;
vratit.x=min(A.x,B.x);
vratit.y=min(A.y,B.y);
vratit.width=max(A.x+A.width,B.x+B.width);
vratit.height=max(A.y+A.height,B.y+B.height);
}

inline void zbavitZelene(Mat & img,int posileniModre=10,int posileniCervene=10)
{
  for(int i=0;i<img.rows;i++)
  for(int j=0;j<img.cols;j++)
  {
Vec3b pixel=img.at<Vec3b>(i,j);
pixel[0]*=posileniModre;
pixel[1]=0;//zelenou odstranit
pixel[2]*=posileniCervene;

if(pixel[2]<100||pixel[0]<100)
{
pixel[2]=0;
pixel[0]=0;
}

img.at<Vec3b>(i,j)=pixel;
  }
}

int main ( int argc, char **argv )
{
  Mat img=imread("bunky.jpg",1);

  zbavitZelene(img);
  cvtColor( img, imgSeda, CV_BGR2GRAY );
  blur( imgSeda, imgSeda, Size(3,3) );

      imshow( "Source", img );
      createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
        thresh_callback( 0, 0 );

  cvWaitKey();
  return 0;
}

/** @function thresh_callback */
void thresh_callback(int, void* )
{
  Mat threshold_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// Detect edges using Threshold
  threshold( imgSeda, threshold_output, thresh, 255, THRESH_BINARY );
  /// Find contours
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// Approximate contours to polygons + get bounding rects and circles
  vector<vector<Point> > contours_poly( contours.size() );
  vector<Rect> boundRect( contours.size() );
  vector<Point2f>center( contours.size() );
  vector<float>radius( contours.size() );

  for( int i = 0; i < contours.size(); i++ )
     { approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
       boundRect[i] = boundingRect( Mat(contours_poly[i]) );
       minEnclosingCircle( (Mat)contours_poly[i], center[i], radius[i] );
     }


  /// Draw polygonal contour + bonding rects + circles
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );


  //test pruniku
  /*for(int i=0;i<contours.size();i++)
  for(int j=i;j<contours.size();j++)
  if(zdaKolize(boundRect[i],boundRect[j]))
  {
  printf("slouceno");
  boundRect[i]=sloucitRect(boundRect[i],boundRect[j]);
  //boundRect[j].width=0;
  boundRect.erase(boundRect.begin()+j);
  }*/


  for( int i = 0; i< contours.size(); i++ )
     {
       //Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       //drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
       if(boundRect[i].width * boundRect[i].height>100)
       rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), Scalar(255,0,0), 2, 8, 0 );
       //circle( drawing, center[i], (int)radius[i], color, 2, 8, 0 );
     }

  /// Show in a window
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}
Bible Kralická, přísloví 26

3 Bič na koně, uzda na osla, a kyj na hřbet blázna.
7 Jakož nejednostejní jsou hnátové kulhavého, tak řeč v ústech bláznů.
14 Dvéře se obracejí na stežejích svých, a lenoch na lůži svém.
27 Kdo jámu kopá, do ní upadá, a kdo valí kámen, na něj se obrací.

Krleš!

Michal

Re:Rozpoznávání obrazu s buňkami
« Odpověď #4 kdy: 01. 02. 2014, 11:32:24 »
Vyzkousej labeling (https://mahotas.readthedocs.org/en/latest/labeled.html)
s ruznymi metodami pro thresholding (http://mahotas.readthedocs.org/en/latest/thresholding.html).

Z labelingu dostanes pocet i velikost jednotlivych oblasti, muzes si pak treba spocitat median velikosti a pomoci toho odhalit vice bunek  prilis blizko u sebe a odhadnout jejich pocet.


milo

Re:Rozpoznávání obrazu s buňkami
« Odpověď #5 kdy: 01. 02. 2014, 11:54:10 »
Děkuji všem za nápady a rady, Do zítra to odzkouším a pravděpodobně přidám další doplňující otázky.
Hezký víkend Milo

Karlos

Re:Rozpoznávání obrazu s buňkami
« Odpověď #6 kdy: 01. 02. 2014, 12:52:04 »
A proč ne ImageJ? Jen tak jsem na to vyzkoušel, od jednoduchého najít maximum po prahování a analyze particles a slušný výsledek. Umí toho strašně moc, jen přijít na správnou metodiku. Možná ale potřebuješ zpracovat větší množství obrázků, to pak asi nebude moc efektivní. Sám jsem v tom pracoval - taky s buňkama, dost užitečný je bandpass filtr.
p.s. doufam, že z toho leze aspoň 16bit grey a ne toto  ;D

Re:Rozpoznávání obrazu s buňkami
« Odpověď #7 kdy: 01. 02. 2014, 16:42:38 »
upravená verze, slučování obdélníků už tam frčí. Jestli se ty buňky budou lepit na sebe, tak to slučování bude spíš na obtíž. Pro oba obrázky, co jste sem dal to funguje dobře.

Kód: [Vybrat]
//============================================================================
// Name        : ocv_bunky.cpp
// Author      : Buněčňák
// Version     :
// Copyright   : moje
// Description : Hello World in C++, Ansi-style
//============================================================================
//zalozeno na http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html#bounding-rects-circles

#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <cv.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

Mat imgSeda;
int thresh = 0;
int max_thresh = 255;

/// Function header
void thresh_callback(int, void* );

inline bool isRectMinimalniVelikost(Rect & A)
{
if(A.width<5 || A.height<5)return false;
if(A.width * A.height<100)return false;

return true;
}

inline bool zdaKolize(Rect A,Rect B)
{
if(A.x> (B.x +B.width)||(A.x + A.width) < B.x)return false;
if(A.y > (B.y + B.height) || (A.y + A.height) < B.y)return false;
return true;
}

inline void vypsatRect(Rect & A)
{
printf("x: %d, y:%d w:%d h:%d\n",A.x,A.y,A.width,A.height);
}

inline Rect sloucitRect(Rect A,Rect B)
{
Rect vratit;
vratit.x=min(A.x,B.x);
vratit.y=min(A.y,B.y);
vratit.width=max(A.x+A.width,B.x+B.width)-vratit.x;
vratit.height=max(A.y+A.height,B.y+B.height)-vratit.y;
printf("\nSlucovani:\n");
printf("A: ");vypsatRect(A);
printf("B: ");vypsatRect(B);
printf("Vysledny: ");vypsatRect(vratit);
return vratit;
}

inline void zbavitZelene(Mat & img,int posileniModre=10,int posileniCervene=10)
{
  for(int i=0;i<img.rows;i++)
  for(int j=0;j<img.cols;j++)
  {
Vec3b pixel=img.at<Vec3b>(i,j);
pixel[0]*=posileniModre;
pixel[1]=0;//zelenou odstranit
pixel[2]*=posileniCervene;

if(pixel[2]<70||pixel[0]<70)
{
pixel[2]=0;
pixel[0]=0;
}

img.at<Vec3b>(i,j)=pixel;
  }
}

int main ( int argc, char **argv )
{
  Mat img=imread("bunky.jpg",1);

  zbavitZelene(img);
  cvtColor( img, imgSeda, CV_BGR2GRAY );
  blur( imgSeda, imgSeda, Size(3,3) );

      imshow( "Source", img );
      createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
        thresh_callback( 0, 0 );

  cvWaitKey();
  return 0;
}

/** @function thresh_callback */
void thresh_callback(int, void* )
{
  Mat threshold_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;

  /// Detect edges using Threshold
  threshold( imgSeda, threshold_output, thresh, 255, THRESH_BINARY );
  /// Find contours
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// Approximate contours to polygons + get bounding rects and circles
  vector<vector<Point> > contours_poly( contours.size() );
  vector<Rect> boundRect( contours.size() );
  //vector<Point2f>center( contours.size() );
  //vector<float>radius( contours.size() );

  for( int i = 0; i < contours.size(); i++ )
     { approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
       boundRect[i] = boundingRect( Mat(contours_poly[i]) );
      // minEnclosingCircle( (Mat)contours_poly[i], center[i], radius[i] );
     }


  /// Draw polygonal contour + bonding rects + circles
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );

  //test pruniku
  for(unsigned int i=0;i<contours.size();i++)
  for(unsigned int j=i+1;j<contours.size();j++)
  if(isRectMinimalniVelikost(boundRect[i]) && isRectMinimalniVelikost(boundRect[j]))
  if(zdaKolize(boundRect[i],boundRect[j]))
  {
  boundRect[i]=sloucitRect(boundRect[i],boundRect[j]);
  boundRect[j].width=0;
  //boundRect.erase(boundRect.begin()+j);
  }


  int kolik=0;
  for(unsigned int i = 0; i< contours.size(); i++)
  if(isRectMinimalniVelikost(boundRect[i]))
       {
       rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), Scalar(255,0,0), 2, 8, 0 );
       kolik++;
   printf("vykresluje se %d. obdelnik: ",kolik);
   vypsatRect(boundRect[i]);
       }
  printf("\nVykresleno %d obdelniku",kolik);

  /// Show in a window
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}


Sám jsem v tom pracoval - taky s buňkama, dost užitečný je bandpass filtr.
p.s. doufam, že z toho leze aspoň 16bit grey a ne toto  ;D
S tímhletím nemám praxi, řekl bych ale že tady se ti díky tomu zelenýmu podsvícení víc hodí RGB, snadno dáš pryč zelený kanál a skoro ti zůstane jenom to důležité. A dofiltrovals.
Bible Kralická, přísloví 26

3 Bič na koně, uzda na osla, a kyj na hřbet blázna.
7 Jakož nejednostejní jsou hnátové kulhavého, tak řeč v ústech bláznů.
14 Dvéře se obracejí na stežejích svých, a lenoch na lůži svém.
27 Kdo jámu kopá, do ní upadá, a kdo valí kámen, na něj se obrací.

Krleš!

Karlos

Re:Rozpoznávání obrazu s buňkami
« Odpověď #8 kdy: 01. 02. 2014, 19:53:11 »
Ale tohle je fluorescence, ne? Nějaký nebo filtr na pozadí nebo unmixing by mohla podporovat přímo ta mašina. Tohle mě velmi zajímá, můžeš mi napsat nějaké info okolo na mail, rád bych se ještě na něco zeptal (karel@nahoranku.eu) - jsem z oboru.
Jinak s tím RGB jsem to nepochopil, odebral jsem zelený kanál a zůstalo mi prd. Já jsem se v ImageJ dopočítal dvěma metodama shodně 143 bb, ale neměl jsem moc času. Říkám, nevím jak vznikl ten obrázek, jen mi to přijde divný.

Re:Rozpoznávání obrazu s buňkami
« Odpověď #9 kdy: 01. 02. 2014, 21:53:02 »
Ale tohle je fluorescence, ne? Nějaký nebo filtr na pozadí nebo unmixing by mohla podporovat přímo ta mašina. Tohle mě velmi zajímá, můžeš mi napsat nějaké info okolo na mail, rád bych se ještě na něco zeptal (karel@nahoranku.eu) - jsem z oboru.
Jinak s tím RGB jsem to nepochopil, odebral jsem zelený kanál a zůstalo mi prd. Já jsem se v ImageJ dopočítal dvěma metodama shodně 143 bb, ale neměl jsem moc času. Říkám, nevím jak vznikl ten obrázek, jen mi to přijde divný.


To jsou spíš otázky na autora, já jsem se tady k tomuhle jenom přichomejtnul  :D. Ve svém původním příspěvku se zmiňoval o tom, že jsou ty buňky osvětlovány fosforem a navíc nějakýma leddiodama. Každopádně se mi zdá, že zdroj světla je v zeleném spektru.

Zelený kanál jsem zkoušel odstraňovat pomocí téhle funkce:
Kód: [Vybrat]
inline void zbavitZelene(Mat & img,int posileniModre=10,int posileniCervene=10)
{
  for(int i=0;i<img.rows;i++)
  for(int j=0;j<img.cols;j++)
  {
Vec3b pixel=img.at<Vec3b>(i,j);
pixel[0]*=posileniModre;
pixel[1]=0;//zelenou odstranit
pixel[2]*=posileniCervene;

if(pixel[2]<70||pixel[0]<70)
{
pixel[2]=0;
pixel[0]=0;
}

img.at<Vec3b>(i,j)=pixel;
  }
}

V podstatě to projede obrázek po pixelech, vynuluje zelenou a posílí zbylé dva kanály. Dále, pokud se modrá nebo červená nedostanou za určitou mez, vynuluje je to taky. Proto to černé pozadí. Uznávám tedy, že jenom odstranit zelenou problém neřeší.

Na obrázku jsou ještě patrné různé drobné flíčky, hádám, že to jsou buňky označené fosforem jen částečně.

Obrázek zde:
http://img142.imagevenue.com/img.php?image=78503_bezzelene_122_188lo.jpg

Bible Kralická, přísloví 26

3 Bič na koně, uzda na osla, a kyj na hřbet blázna.
7 Jakož nejednostejní jsou hnátové kulhavého, tak řeč v ústech bláznů.
14 Dvéře se obracejí na stežejích svých, a lenoch na lůži svém.
27 Kdo jámu kopá, do ní upadá, a kdo valí kámen, na něj se obrací.

Krleš!

Re:Rozpoznávání obrazu s buňkami
« Odpověď #10 kdy: 15. 02. 2014, 00:36:14 »
Tohle by nepomohlo? Narazil jsem na to náhodou... :-)

Citace
Domovská stránka: http://opencfu.sourceforge.net

OpenCFU is a completely open source lightweight application designed to
enumerate clustered circular objects such as bacterial colonies. It can
handle digital pictures as well as live stream from a video device/webcam.
OpenCFU is cross-platform, fast, reliable and allows the user to implement
intuitive filters.

OpenCFU is published on PLoS ONE:
http://www.plosone.org/article/info%3Adoi%2F10.1371%2Fjournal.pone.0054072

miro016

Re:Rozpoznávání obrazu s buňkami
« Odpověď #11 kdy: 15. 02. 2014, 01:45:13 »
ide o staticky alebo dynamicky obraz? pokial je k dispozicii video a bunky sa aspon ciastocne pohybuju, dal by sa vyriesit problem prekryvania - teda mozem sledovat, ci nemam velky zlepenec, ktory sa po case rozdeli (alebo dve samostatne bunky, ktore sa spoja, ale viem, ze zlepenec je vysledkom dvoch jedincov)... po case by som pomocou trackingu a inych metod vedel aspon statisticky odhadnut skutocny pocet buniek :)

toto by sa vsak dalo pouzit len za predpokladu, ze hladam zive a pohybujuce sa bunky (zaroven by som vedel odfiltrovat aj kvapocky aktivnej latky v roztoku, pretoze tie by sa pravdepodobne nepohybovali, alebo len obmedzene)...

randolf

Re:Rozpoznávání obrazu s buňkami
« Odpověď #12 kdy: 17. 02. 2014, 13:32:54 »
No tak jako ja bych to asi zjednodusil na nasledujici postup:
1) Gaussian smoothing (parametry dle predpokladane velikosti bunek)
2) Non-maxima suppression

Rychle, jednoduche, robustni.
Bude se to chovat trosku nepredpovidatelne pri prekryvech, ale to kazda metoda, dokud si nezacnete hrat s estimovanim elips, ale to by stejne vyzadovalo trosku vice detailu, rekl bych.

randolf

Re:Rozpoznávání obrazu s buňkami
« Odpověď #13 kdy: 17. 02. 2014, 13:35:19 »
No tak jako ja bych to asi zjednodusil na nasledujici postup:
1) Gaussian smoothing (parametry dle predpokladane velikosti bunek)
2) Non-maxima suppression

Rychle, jednoduche, robustni.
Bude se to chovat trosku nepredpovidatelne pri prekryvech, ale to kazda metoda, dokud si nezacnete hrat s estimovanim elips, ale to by stejne vyzadovalo trosku vice detailu, rekl bych.
BTW: primo v OpenCV mate nekolik Corner Detector funkci, ktere vpodstate budou delat to same, jen misto maxima se budou divat na prvni ci druhou derivaci maxima - dle meho nazoru sul-nul.