opencv - How to crop away convexity defects? -
i'm trying detect , fine-locate objects in images contours. contours include noise (maybe form background, don't know). objects should similar rectangles or squares like:
i results shape matching (cv::matchshapes
) detect contours objects in them, , without noise, have problems fine-location in case of noise.
noise looks like:
my idea find convexity defects , if become strong, somehow crop away part leads concavity. detecting defects ok, typically 2 defects per "unwanted structure", i'm stuck on how decide , should remove points contours.
here contours, masks (so can extract contours easily) , convex hull including thresholded convexity defects:
could walk through contour , locally decide whether "left turn" performed contour (if walking clockwise) , if so, remove contour points until next left turn taken? maybe starting @ convexity defect?
i'm looking algorithms or code, programming language should not important, algorithm more important.
this approach works on points. don't need create masks this.
the main idea is:
- find defects on contour
- if find @ least 2 defects, find 2 closest defects
- remove contour points between 2 closest defects
- restart 1 on new contour
i following results. can see, has drawbacks smooth defects (e.g. 7th image), works pretty visible defects. don't know if solve problem, can starting point. in practice should quite fast (you can surely optimize code below, specially removefromcontour
function). also, parameter of approach amount of convexity defect, works both small , big defecting blobs.
#include <opencv2/opencv.hpp> using namespace cv; using namespace std; int ed2(const point& lhs, const point& rhs) { return (lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y); } vector<point> removefromcontour(const vector<point>& contour, const vector<int>& defectsidx) { int mindist = int_max; int startidx; int endidx; // find nearest defects (int = 0; < defectsidx.size(); ++i) { (int j = + 1; j < defectsidx.size(); ++j) { float dist = ed2(contour[defectsidx[i]], contour[defectsidx[j]]); if (mindist > dist) { mindist = dist; startidx = defectsidx[i]; endidx = defectsidx[j]; } } } // check if intervals swapped if (startidx <= endidx) { int len1 = endidx - startidx; int len2 = contour.size() - endidx + startidx; if (len2 < len1) { swap(startidx, endidx); } } else { int len1 = startidx - endidx; int len2 = contour.size() - startidx + endidx; if (len1 < len2) { swap(startidx, endidx); } } // remove unwanted points vector<point> out; if (startidx <= endidx) { out.insert(out.end(), contour.begin(), contour.begin() + startidx); out.insert(out.end(), contour.begin() + endidx, contour.end()); } else { out.insert(out.end(), contour.begin() + endidx, contour.begin() + startidx); } return out; } int main() { mat1b img = imread("path_to_mask", imread_grayscale); mat3b out; cvtcolor(img, out, color_gray2bgr); vector<vector<point>> contours; findcontours(img.clone(), contours, retr_external, chain_approx_none); vector<point> pts = contours[0]; vector<int> hullidx; convexhull(pts, hullidx, false); vector<vec4i> defects; convexitydefects(pts, hullidx, defects); while (true) { // debug mat3b dbg; cvtcolor(img, dbg, color_gray2bgr); vector<vector<point>> tmp = {pts}; drawcontours(dbg, tmp, 0, scalar(255, 127, 0)); vector<int> defectsidx; (const vec4i& v : defects) { float depth = float(v[3]) / 256.f; if (depth > 2) // filter defects depth { // defect found defectsidx.push_back(v[2]); int startidx = v[0]; point ptstart(pts[startidx]); int endidx = v[1]; point ptend(pts[endidx]); int faridx = v[2]; point ptfar(pts[faridx]); line(dbg, ptstart, ptend, scalar(255, 0, 0), 1); line(dbg, ptstart, ptfar, scalar(0, 255, 0), 1); line(dbg, ptend, ptfar, scalar(0, 0, 255), 1); circle(dbg, ptfar, 4, scalar(127, 127, 255), 2); } } if (defectsidx.size() < 2) { break; } // if have more 2 defects, remove points between 2 nearest defects pts = removefromcontour(pts, defectsidx); convexhull(pts, hullidx, false); convexitydefects(pts, hullidx, defects); } // draw result contour vector<vector<point>> tmp = { pts }; drawcontours(out, tmp, 0, scalar(0, 0, 255), 1); imshow("result", out); waitkey(); return 0; }
update
working on approximated contour (e.g. using chain_approx_simple
in findcontours
) may faster, length of contours must computed using arclength()
.
this snippet replace in swapping part of removefromcontour
:
// check if intervals swapped if (startidx <= endidx) { //int len11 = endidx - startidx; vector<point> inside(contour.begin() + startidx, contour.begin() + endidx); int len1 = (inside.empty()) ? 0 : arclength(inside, false); //int len22 = contour.size() - endidx + startidx; vector<point> outside1(contour.begin(), contour.begin() + startidx); vector<point> outside2(contour.begin() + endidx, contour.end()); int len2 = (outside1.empty() ? 0 : arclength(outside1, false)) + (outside2.empty() ? 0 : arclength(outside2, false)); if (len2 < len1) { swap(startidx, endidx); } } else { //int len1 = startidx - endidx; vector<point> inside(contour.begin() + endidx, contour.begin() + startidx); int len1 = (inside.empty()) ? 0 : arclength(inside, false); //int len2 = contour.size() - startidx + endidx; vector<point> outside1(contour.begin(), contour.begin() + endidx); vector<point> outside2(contour.begin() + startidx, contour.end()); int len2 = (outside1.empty() ? 0 : arclength(outside1, false)) + (outside2.empty() ? 0 : arclength(outside2, false)); if (len1 < len2) { swap(startidx, endidx); } }
Comments
Post a Comment