// GUI calls this function when a user left-clicks on an image pixel while in "Region" mode // The method computes a 'region' of pixels connected to 'seed'. The region is grown by adding // to each pixel p (starting at 'seed') all neighbors q such that intensity difference is small, // that is, |Ip-Iq|<T where T is some fixed threshold. Use Queue for active front. // HINT: to compute intensity difference Ip-Iq, use function dI(RGB,RGB) defined in "Image2D.h" void regionGrow(Point seed, double T) { if (!image.pointIn(seed)) return; int counter = 0; Queue<Point> active; active.enqueue(seed); // use BREADTH-FIRST_SEARCH (FIFO order) traversal - "Queue" //.. while (!active.isEmpty()) { Point p = active.dequeue(); region[p]=1; counter++; for (int i=0; i<4; i++) { Point q = p + shift[i]; if (image.pointIn(q) && region[q]==0 && dI(image[q], image[seed])>(T*-1) && dI(image[q], image[seed])<T) { active.enqueue(q); region[q]=2; } } if (view && counter%60==0) {draw(); Pause(20);} } cout << "grown region of volume " << counter << " pixels (threshold " << T << ")" << endl; }
void FeaturesGrid::reset(Table2D<RGB> & im, FEATURE_TYPE ft, float w) { cout << "resetting data... " << endl; int height = im.getHeight(), width = im.getWidth(); Point p; m_seeds.reset(width,height,NO_LABEL); m_labeling.reset(width,height,NO_LABEL); // NOTE: width and height of m_labeling table // are used in functions to_index() and to_Point(), m_dim=0; m_means.clear(); m_features.clear(); // deleting old features m_features.resize(width*height); // creating a new array of feature vectors if (ft == color || ft == colorXY) { m_dim+=3; // adding color features for (int y=0; y<height; y++) for (int x=0; x<width; x++) { pix_id n = to_index(x,y); m_features[n].push_back((double) im[x][y].r); m_features[n].push_back((double) im[x][y].g); m_features[n].push_back((double) im[x][y].b); } } if (ft == colorXY) { m_dim+=2; // adding location features (XY) for (int y=0; y<height; y++) for (int x=0; x<width; x++) { pix_id n = to_index(x,y); m_features[n].push_back((double) w*x); m_features[n].push_back((double) w*y); } } cout << "done" << endl; }
void FeaturesGrid::addFeature(Table2D<double> & im) { m_dim++; // adding extra feature (increasing dimensionality of feature vectors int height = im.getHeight(), width = im.getWidth(); for (int y=0; y<height; y++) for (int x=0; x<width; x++) { pix_id n = to_index(x,y); m_features[n].push_back(im[x][y]); } }
// GUI calls this function when button "Clear" is pressed, or when new image is loaded void reset_segm() { cout << "resetting 'contour' and 'region'" << endl; // removing all region markings region.reset(im.getWidth(),im.getHeight(),0); // add your code below to remove all points from the "contour" while(!contour.isEmpty()) contour.popBack(); closedContour = false; }
void TableFactory::ReadFileHonda(const char* fname, unsigned ny,Table2D& table){ // printf("reading file %s (for Y[%d])\n",fname,ny); std::ifstream inf; inf.open(fname); if(!inf.good()) throw MRexception("Error opening file: \""+std::string(fname)+"\""); double p,mplus,mminus; for (auto nx:Axis::Bins(table.GetXaxis())) { inf>>p>>mplus>>mminus; if(inf.fail()){ throw MRexception("Failed reading from file"); } table.SetPoint(nx,ny,mplus+mminus); } inf.close(); }
// GUI calls this function when a user right-clicks on an image pixel while in "Region" mode. // It marks in 'region' a set of same-color pixels adjacent to the 'seed'. The neighboring pixels // are traversed using DEPTH-FIRST-SEARCH starting at the 'seed'. // Unlike previous function "addSquare()", the shape of the added region depends on the image. void floodFill_DFS(Point seed) { int counter = 0; Stack<Point> active_front; active_front.push(seed); // "mark" all adjecent pixels of the same color in 2D table "region" while traversing them // using DEPTH-FIRST_SEARCH (LIFO order) traversal - use "Stack" while (!active_front.isEmpty()) { Point p = active_front.pop(); region[p]=1; // pixel p is extracted from the "active_front" and added to "region", but counter++; // then, all "appropriate" neighbors of p are added to "active_front" (below) for (int i=0; i<4; i++) // goes over 4 neighbors of pixel p { // uses overloaded operator "+" for Points (see Basics2D.h) and array of 'shifts' (see the top of file) Point q = p + shift[i]; // to compute a "neighbor" of pixel p if (im.pointIn(q) && region[q]==0 && im[q]==im[seed]) { // we checked if q is inside image range and that its "color" matches "seed" // Why do we also need condition region[q]==0 ???? active_front.push(q); region[q]=2; // "region" value 2 is used in "draw()" to visualise pixels that are } // inside "active_front"; note that all pixels in "active front" } // are eventually extracted and their "region" value is set to 1. if (view && counter%60==0) {draw(); Pause(20);} // visualization, skipped if checkbox "view" is off } cout << "flood filled region of size " << counter << " using DFS traversal (LIFO order, Stack)" << endl; }
// This function is called at the end of "addToContourLast()". Function // "contourInterior()" returns volume of the closed contour's interior region // (including points on the contor itself), or -1 if contour is not closed yet. // HINT: live-wire() adds to the contour "water-tight" paths of neighboring pixels int contourInterior() { if (!closedContour || image.isEmpty()) return -1; int counter = 0; // write your code for computing correct mask (region) // which should have 3 for pixels OUTSIDE the contour and 0 // for pixels INSIDE of the contour // HINT: "grow" the "exterior region" from a single exterior point (e.g. a corner of the image). // Make sure that the region does not grow across the contour (points in list "contour") // ... Queue<Point> active; Point q(1,1); active.enqueue(q); region.reset(0); for (unsigned i=1; i<=contour.getLength(); i++) region[contour.retrieve(i)]=2; while (!active.isEmpty()) { Point p = active.dequeue(); region[p]=3; counter++; for (int i=0; i<4; i++) { Point j = p + shift[i]; if (image.pointIn(j) && region[j]!=2 && region[j]!=3) { active.enqueue(j); region[j]=3; } } if (view && counter%60==0) {draw(); Pause(20);} } for (unsigned i=1; i<=contour.getLength(); i++) region[contour.retrieve(i)]=0; return region.getWidth() * region.getHeight() - counter; }
// GUI calls this function when a user middle-clicks on an image pixel while in "Region" mode // It marks in 'region' a square-shaped subset of pixels adjecent to 'seed' (side=40) void addSquare(Point seed) { // The code below marks one pixel (seed.x,seed.y) as part of the "region." // (Note that draw() function in main.cpp displays all 'region=1' pixels in blue.) // // GOAL: Change the code below so that, instead of marking just one pixel, // it marks pixels in a square around the seed. // The square should have size 40x40 and be centered at (seed.x, seed.y). // region[seed]=1; // seed is a Point; there are 2 overloaded operators[] in Table2D.h // this line is equivalent to "region[seed.x][seed.y]=1;" for(int i = -20; i<=20; i++) for(int j = -20; j<=20; j++) if(region.pointIn(seed.x +i, seed.y +j)) { region[seed.x+i][seed.y+j]=2; } cout << "added a square region" << endl; }
// GUI calls this function when a user left-clicks on an image pixel while in "Region" mode. // It marks in 'region' a set of same-color pixels adjacent to the 'seed'. The neighboring pixels // are traversed using BREADTH-FIRST-SEARCH starting at the 'seed'. // This function differs from floodFill_DFS() only by the order of traversal of adjecent pixels. void floodFill_BFS(Point seed) { // INSTEAD OF CODE BELOW THAT MARKS ONLY ONE PIXEL IN "REGION"... // "mark" all adjecent pixels of the same color to 2D array "region" while traversing them // using BREADTH-FIRST_SEARCH (FIFO order) - use "Queue" instead of "Stack" // ... int counter = 0; Queue<Point> active_front; active_front.enqueue(seed); // "mark" all adjecent pixels of the same color in 2D table "region" while traversing them // using DEPTH-FIRST_SEARCH (LIFO order) traversal - use "Stack" while (!active_front.isEmpty()) { Point p = active_front.dequeue(); region[p]=1; // pixel p is extracted from the "active_front" and added to "region", but counter++; // then, all "appropriate" neighbors of p are added to "active_front" (below) for (int i=0; i<4; i++) // goes over 4 neighbors of pixel p { // uses overloaded operator "+" for Points (see Basics2D.h) and array of 'shifts' (see the top of file) Point q = p + shift[i]; // to compute a "neighbor" of pixel p if (im.pointIn(q) && region[q]==0 && im[q]==im[seed]) { // we checked if q is inside image range and that its "color" matches "seed" // Why do we also need condition region[q]==0 ???? active_front.enqueue(q); region[q]=2; // "region" value 2 is used in "draw()" to visualise pixels that are } // inside "active_front"; note that all pixels in "active front" } // are eventually extracted and their "region" value is set to 1. if (view && counter%60==0) {draw(); Pause(20);} // visualization, skipped if checkbox "view" is off } region[seed]=1; // equivalent to region[seed.x][seed.y]=1; (see Table2D.h) // replace the "cout" statement below to indicate the number of "marked" pixels added into "region" using BFS. cout << "added one pixel to region" << endl; }
// GUI calls this function when button "Clear" is pressed, or when new image is loaded // THIS FUNCTION IS FULLY IMPLEMENTED, YOU DO NOT NEED TO CHANGE THE CODE IN IT void reset_segm() { cout << "resetting 'contour' and 'region'" << endl; // removing all region markings region.reset(image.getWidth(),image.getHeight(),0); // remove all points from the "contour" while (!contour.isEmpty()) contour.popBack(); closedContour=false; // resetting 2D tables "dist" and "toParent" (erazing paths) dist.reset(image.getWidth(),image.getHeight(),INFTY); toParent.reset(image.getWidth(),image.getHeight(),NONE); // recomputing "penalties" from an estimate of image contrast at each pixel p=(x,y) if (image.isEmpty()) {penalty.resize(0,0); return;} Table2D<double> contrast = grad2(image); //(implicit conversion of RGB "image" to "Table2D<double>") // NOTE: function grad2() (see Math2D.h) computes (at each pixel) expression Ix*Ix+Iy*Iy where Ix and Iy // are "horizontal" and "vertical" derivatives of image intensity I(x,y) - the average of RGB values. // This expression describes the "rate of change" of intensity, or local image "contrast". penalty = convert(contrast,&fn); // "&fn" - address of function "fn" (defined at the top of this file) // "convert" (see Math2D.h) sets penalty at each pixel according to formula "penalty[x][y] = fn (contrast[x][y])" }
/////////////////////////////////////////////////////////////////////////////////// // computePaths() is a function for "precomputing" live-wire (shortest) paths from // any image pixel to the given "seed". This method is called inside "addToContour" // for each new mouse click. Optimal path from any pixel to the "seed" should accumulate // the least amount of "penalties" along the path (each pixel p=(x,y) on a path contributes // penalty[p] precomputed in "reset_segm()"). In this function you should use 2D tables // "toParent" and "dist" that store for each pixel its "path-towards-seed" direction and, // correspondingly, the sum of penalties on that path (a.k.a. "distance" to the seed). // The function iteratively improves paths by traversing pixels from the seed until // all nodes have direction "toParent" giving the shortest (cheapest) path to the "seed". void computePaths(Point seed) { if (!image.pointIn(seed)) return; region.reset(0); // resets 2D table "region" for visualization // Reset 2D arrays "dist" and "toParent" (erazing all current paths) dist.reset(INFTY); toParent.reset(NONE); dist[seed] = 0; int counter = 0; // just for info printed at the bottom of the function // THE CODE BELOW GENERATES 2d TABLE toParent WITH PATHS BASED ON !!!!!!!! // BASIC BREDTH-FIRST_SEARCH TRAVERSAL FROM THE SEED. REPLACE THIS CODE !!!!!!!! // SO THAT toParent CONTAINS PATHS FOLLOWING HIGH CONSTAST IMAGE BOUNDARIES !!!!!!!! // // Create a queue (or priority_queue) for "active" points/pixels and // traverse pixels to improve paths stored in "toParent" (and "dist") // ..... Queue<Point> active; active.enqueue(seed); while(!active.isEmpty()) { Point p = active.dequeue(); region[p]=1; counter++; for (int i=0; i<4; i++) { Point q = p+shift[i]; //using overloaded operator+ for Points (see "Basic2D.h") double t = dist[p] + penalty[p]; if (image.pointIn(q) && t<dist[q]) { dist[q]=t; toParent[q]=Reverse[i]; region[q]=2; // to visualize pixels added to the queue active.enqueue(q); } } if (view && counter%60==0) {draw(); Pause(20);} // visualization, skipped if checkbox "view" is off } // MY PRIORITY QUEUE METHOD WORKS IN THE SAME FASHION AS THE DEMO // IT IS SLOWER THAN MY QUEUE METHOD, BUT I AM UNSURE IF THIS IS // AN ISSUE WITH RELEASE MODE COMPARED TO DEBUG MODE // COMMENT OUT THE ABOVE QUEUE METHOD AND UNCOMMENT THE BELOW METHOD // TO TEST FUNCTIONALITY OF PRIORITY QUEUE METHOD /*priority_queue<MyPoint> active; MyPoint j(seed, dist[seed]); active.push(j); while(!active.empty()) { Point p = active.top(); active.pop(); region[p]=1; counter++; for (int i=0; i<4; i++) { Point q = p+shift[i]; //using overloaded operator+ for Points (see "Basic2D.h") double t = dist[p] + penalty[p]; if (image.pointIn(q) && t<dist[q]) { dist[q]=t; toParent[q]=Reverse[i]; region[q]=2; // to visualize pixels added to the queue MyPoint b(q, dist[q]); active.push(b); } } if (view && counter%60==0) {draw(); Pause(20);} // visualization, skipped if checkbox "view" is off }*/ // you can print out the number of times a point was removed from the queue cout << "paths computed, number of 'pops' = " << counter << ", number of pixels = " << (region.getWidth()*region.getHeight()) << endl; }