golmschenk
golmschenk

Reputation: 12384

OpenCV - Finding contour end points?

I'm looking for a way to get the end points of a thin contour extracted from a Canny edge detection. I was wondering is this is possible with some built-in way. I would plan on walking through the contour to find the two points with the largest distance from each other (moving only along the contour), but it would be much easier if a way already exists. I see that cvarcLength exists to get the perimeter of a contour, so it's possible there would be a built-in way to achieve this. Is it are the points within a contour ordered in such a way that some information can be known about the end points? Any other ideas? Thank you much!

Upvotes: 2

Views: 5091

Answers (3)

Arnaud P
Arnaud P

Reputation: 12607

The solution based on neighbor distances check didn't work for me (Python + opencv 3.0.0-beta), because all contours I get seem to be folded on themselves. What would appear as "open" contours at first glance on an image are actually "closed" contours collapsed on themselves.

So I had to resort to look for "u-turns" in each contour's sequence, an example in Python:

import numpy as np

def draw_closing_lines(img, contours):
    for cont in contours:
        v1 = (np.roll(cont, -2, axis=0) - cont)
        v2 = (np.roll(cont, 2, axis=0) - cont)
        dotprod = np.sum(v1 * v2, axis=2)
        norm1 = np.sqrt(np.sum(v1 ** 2, axis=2))
        norm2 = np.sqrt(np.sum(v2 ** 2, axis=2))
        cosinus = (dotprod / norm1) / norm2
        indexes = np.where(0.95 < cosinus)[0]
        if len(indexes) == 1:
            # only one u-turn found, mark in yellow
            cv2.circle(img, tuple(cont[indexes[0], 0]), 3, (0, 255, 255))
        elif len(indexes) == 2:
            # two u-turns found, draw the closing line
            cv2.line(img, tuple(tuple(cont[indexes[0], 0])), tuple(cont[indexes[1], 0]), (0, 0, 255))
        else:
            # too many u-turns, mark in red
            for i in indexes:
                cv2.circle(img, tuple(cont[i, 0]), 3, (0, 0, 255))

Not completely robust against polluting cusps and quite time-consuming, but that's a start. I'd be interested in other ideas, naturally :)

Upvotes: 0

jensph
jensph

Reputation: 785

As you say, you can always step through the contour points.

The following finds the two points, ptLeft and ptRight, with the greatest separation along x, but could be modified as needed.

    CvPoint ptLeft = cvPoint(image->width, image->height);
    CvPoint ptRight = cvPoint(0, 0);
    CvSlice slice = cvSlice(0, CV_WHOLE_SEQ_END_INDEX);
    CvSeqReader reader;
    cvStartReadSeq(contour, &reader, 0);
    cvSetSeqReaderPos(&reader, slice.start_index);
    int count = cvSliceLength(slice, contour);
    for(int i = 0; i < count; i++)
    {
        reader.prev_elem = reader.ptr;
        CV_NEXT_SEQ_ELEM(contour->elem_size, reader);

        CvPoint* pt = (CvPoint*)reader.ptr;
        if( pt->x < ptLeft.x )
            ptLeft = *pt;
        if( pt->x > ptRight.x )
            ptRight = *pt;
    }

Upvotes: 0

Ryan
Ryan

Reputation: 106

I was looking for the same function, I see HoughLinesP has end points since lines are used not contours. I am using findContours however so I found it was helpful to order the points in the contours like this below and than take the first and last points as the start and end points.

struct contoursCmpY {
    bool operator()(const Point &a,const Point &b) const {
        if (a.y == b.y)
            return a.x < b.x;

        return a.y < b.y;
    }
} contoursCmpY_;

vector<Point> cont;
cont.push_back(Point(194,72));
cont.push_back(Point(253,14));
cont.push_back(Point(293,76));
cont.push_back(Point(245,125));

std::sort(cont.begin(),cont.end(), contoursCmpY_);

int size = cont.size();
printf("start Point x=%d,y=%d end Point x=%d,y=%d", cont[0].x, cont[0].y, cont[size].x, cont[size].y);

Upvotes: 1

Related Questions