lényege: valamilyen szempont szerint homogén csoportok … · 2017-04-26 · ha -1-et értéket...
TRANSCRIPT
Lényege: valamilyen szempont szerint homogén csoportok képzése a pixelekből.
Amit már ismerünk:
◦ Küszöbölés, vágás, sávkijelölés
◦ hátránya: az azonos csoportba sorolt pixelek nem feltétlenül alkotnak összefüggő halmazt.
Tekintsük a képet egy domborzati térképként.
Képzeljük el, hogy a (környezettől erősen eltérő) lokális minimumhelyeknél feltört a víz.
Az üregek folyamatosan töltődnek fel vízzel. Ahol a különböző forrásokból származó víz összefolyna, ott gátat képzünk (szegmensek határai).
Töltsd le a sejteket tartalmazó képet, és olvasd be színesben. https://arato.inf.unideb.hu/szeghalmy.szilvia/kepfeld/img/sejtek.bmp
Állíts elő két maszkot, az egyiken a sötétlila, a másikon a sötétbarna sejtek legyenek ◦ sötétbarna HSV tartomány: (0, 10, 0) -tól (30, 255, 150) -ig
◦ sötétlila HSV tartomány : (80, 10, 0) -tól (170, 255, 150) -ig
◦ (javasolt a cvtConvert és az inRange használta)
Hozz létre egy képet a markereknek (a "feltörő források") ◦ A kép típusa: CV_32S, mérete az eredeti képével azonos
◦ Kezdetben minden pixel fekete.
◦ A sötétbarna pixeleket állítsd be 128-as értékre. (javasolt: marker.setTo( 128, barna_maszk) )
◦ A sötétlila pixeleket állítsd be 255-ös értékre. (Más pozitív érték is lehet, de ezek jól láthatóan eltérnek, ha megjeleníted a képet 255-tel szorozva.)
Hajtsd végre a watershed trf-et. ◦ watershed(színes_bemeneti_kep, marker_kep);
A marker képet convertált CV_8UC1-re vagy rajzold át a 128-as, ill. 255-ös értékű pontokat egy új képre és jelenítsd meg. (javasolt az első megoldás: marker_kep.convertTo( uj_kep, CV_8U, 1.0))
Töltsd le a következő képet és olvasd be színesben: https://arato.inf.unideb.hu/szeghalmy.szilvia/kepfeld/img/skate.jpg
Állítsd elő a lokális minimumhelyeket tartalmazó maszkot ◦ Alakíts szürkeskálássá a képet.
◦ Készíts egy s×s méretű, CV_8U típusú mátrixot, melynek minden pontja 1, kivéve a középpontja, mely 0. ( s értéke szabályozza a lokális mininum helyek közti távolságot, kb. 5, 7)
◦ Erodáld a képet a mátrixszal: erode( a_szurkeskalas_kep, a_cel_kep, a_matrix);
◦ lokalis_minimum = a_szurkeskalas_kep < a_cel_kep; //megj: homogén részre nem reagál
Hozz létre egy képet a markereknek (a "feltörő források") ◦ A kép típusa: CV_32S, mérete az eredeti képével azonos
◦ Kezdetben minden pixel fekete.
◦ Az összes lokális minimum értéket állítsd egynél nagyobb, egymástól különböző értékre.
(pl. bejárod a lokalis_minimum képet, és ha találsz egy előtérpontot, akkor növelsz egy nulláról induló számlálót. A növelt számláló értékét állítod be a markerkép adott pontjában értéknek.
Hajtsd végre a watershed trf-et. ◦ watershed(színes_bemeneti_kep, marker_kep);
Hozz létre egy szegmens struktúrát / osztályt struct Segment{
vector<cv::Point> pts;
float feature; //lehet több is
int idx; //egy szegmens indexe };
Készíts függvényt, mely összegyűjti a watershed trf. eredményképe alapján a szegmenseket. ◦ A numLabel a lokális minimumhelyek száma (watershed trf-nél meghatároztad)
void collectSegments(cv::Mat_<int> markers, vector<Segment>& segments, int numLabel){
A segments vektor méretezd át numLabel-re.
Járd be a markerképet
m jelölje az aktuális pont markerkép értékét
Ha m eleme a [0, numLabel-1] tartománynak akkor
a segmens vektor m. eleméhez add hozzá az aktuális pontot
//segments[m].push_back( Point(aktualis_pont_koordinatai) );
}
Készíts függvényt, mely beállítja az előbb begyűjtött szegmensek jellemzőit
A numLabel a lokális minimumhelyek száma (watershed trf-nél meghatároztad, ismered)
Ha rgb képet akarsz majd átadni, akkor az img-t alakítsd szürkeskálásra ◦ cvtConvert( img, gray, COLOR_BGR2GRAY );
void setSegmentsFeatures(const cv::Mat img, vector<Segment>& segments) {
//járd be a szegmens vektort (sima for ciklus)
az aktuális szegmes tulajdonságait állítsd be:
idx értéke legyen a vektorban elfoglalt helye (ciklusváltozó)
feature értéke legyen a szegmens pontjainak átlagos szürkeségi értéke
a pts vektor pontjai és az img alapján meghatározhatod
}
Készíts függvényt, mely meghatározza a hasonló szegmenseket. ◦ (ez a fajta megoldás az egymástól távol álló szegmenseket is hasonlónak tudja tekinteni)
Az eredmény egy hasonlósági mátrix. 1-es áll egy mezőben, ha az adott sornak megfelelő indexű szegmens hasonlít az adott oszlopban álló szegmensre.
A tárolás a felső háromszögben történjen (vagy legyen szimmetrikus)
void segmentsForMerge(vector<Segment>& segments, cv::Mat& mergeMat, int threshold) {
Hozd létre csupa nullával feltöltve a hasonlósági mátrixot (mergeMat)
mérete a segmensek számával azonos
Járd be a szegmenseket ( i = 0; … )
Járd be az i-től nagyobb sorszámú szegmenseket ( j = i+1; … )
Ha a két szegmens feature értékének eltérése a megadott
küszöbérték (threshold) alatt van, akkor
a mátrix (i, j). eleme legyen 1-es értékű.
}
Ha szeretnéd, hogy egymástól távoli szegmensek ne kapcsolódhassanak közvetlenül, akkor a marker képen távolítsd el a határoló vonalakat
void dilateMarkers(cv::Mat_<int>& markers){
Járd be a mátrixot
Ha -1-et értéket találsz, írd felül egy nem -1 értékű szomszédjával.
}
Hozz létre egy szomszédsági mátrixot (szimmetrikus / felső háromszög), ez alapján lehet majd törölni az előző mergeMat-ból a nem kívánt elemeket.
void neigbourSegments(Mat_<int> markers, cv::Mat& neigbourMat, int numSeg) {
neigbourMat = Mat::zeros(cv::Size(numSeg, numSeg), CV_8UC1);
//járd be a marker képet (vigyázz, hogy majd a szomszéd is beférjen a tartományba)
//A marker értékek a szegmensek indexével azonosak a korábbi feltöltés miatt
jelölje s1 az (i, j) és s2 a (i, j+1) pont markerét
Ha egyik sem -1, akkor jelöld a s1. és s2. szegmenst szomszédosnak*
neigbourMat.at<uchar>(s1, s2) = neigbourMat.at<uchar>(s2, s1) = 1;
Ugyanezeket a lépéseket tedd meg a (i, j) és (i+1, j) pontokra is.
}
*(a dilateMarkers-nél választott szomszédoktól függően maradhat-1-es érték) akkor
Szegmens összeolvasztás, ha a pontosorzatokkal nem foglalkozunk void mergeSegments(vector<Segment>& segments, cv::Mat_<uchar> mergeMat) {
Járd be a mergeMat mátrix FELSŐ háromszög részét (
for( i = 0; … )
for (j = i+1 …)
ha a mergeMat aktuális eleme nem nulla, akkor a j. szegmens indexét
állítsd át az i. szegmens indexére.
Szegmens összeolvasztás, ha a szegmensek pontjait is meg akarjuk kapni void mergeSegments(vector<Segment>& segments, cv::Mat_<uchar> mergeMat) {
//ilyenkor visszafelé haladunk (hogy a már összevontat vonjuk később össze)
for (int i = mergeMat.rows-1; i >=0; --i) {
for (int j = mergeMat.cols - 1; j >= i + 1; --j) {
ha a mergeMat aktuális eleme nem nulla, akkor
az i. szegmens pontjaihoz (pts) add hozzá a j. szegmens pontjait.
//segments[i].insert(…))
majd töröld a j. szegmensből a pontokat
//segments[j].cleare();
Az alábbi függvény kirajzolja a szegmenseket void drawSegment(cv::InputOutputArray dest, const vector<Segment>& segments) {
vector<cv::Vec3b> lookUp( segments.size());
for (int i = 0; i < segments.size(); ++i)
lookUp[i] = cv::Vec3b(rand() % 250 + 50, rand() % 255, rand() % 255);
cv::Mat& destMat = dest.getMatRef();
for (auto s : segments) {
if (s.pts.size() > 0) {
cv::Vec3b color = lookUp[s.idx];
for (auto p : s.pts)
destMat.at<cv::Vec3b>(p) = color;
}
}
És végül a függvény, ami az egészet összefogja: void hierSegment(const Mat_<cv::Vec3b> img, Mat_<int> markers, Mat& dest, int numSeg, float
threshold = 20.0, bool mergeOnlyNeighbour = false) {
//körvonalak eltávolítása a marker képről (mergeOnlyNeighbour igaz értékénél fontos)
dilateMarkers(markers);
vector<Segment> segments;
collectSegments( markers, segments, numSeg);
setSegmentsFeatures(img, segments);
cv::Mat mergeMat;
segmentsForMerge(segments, mergeMat, threshold);
if (mergeOnlyNeighbours) { //csak ha megcsináltad
cv::Mat neighbourMat;
neigbourSegments(markers, neighbourMat, numSeg);
mergeMat &= neighbourMat;
}
mergeSegments(segments, mergeMat);
dest.create(markers.size(), CV_8UC3);
drawSegment(dest, segments);
}
Watershed (beépített) (gauss 3x3, sigma=2)
markerek a lokális maximumhelyek (5x5)
Hasonló szegmensek összevonása összevonás küszöbértéke 15 jellemző: átlagos intenzitás
Hasonló, 4-szomszédság szerint
kapcsolódó szegmensek összevonása után
Eredeti