0

I have an image mask, with some contours I got from Canny. I can calculate a bounding rectangle (with a given angle that is fix).

enter image description here

Now I need to separate the 2 areas to the left and right of that rectangle. How can I do that?

Please note that I want to work with the area within the rectangle, not the pixels that are contours.

Edit

This is how I obtain each bounding rectangle from the mask:

cv::Mat img_edges; // mask with contours

// Apply clustering to the edge mask from here
// http://stackoverflow.com/questions/33825249/opencv-euclidean-clustering-vs-findcontours?noredirect=1#comment55433731_33825249

// Find boundary rectangle
for (auto &contour: contours) { // Iterate over every contour cluster
  cv::Mat Srot = cv::getRotationMatrix2D(cv::Point2f(float(img_edges.cols) / 2., float(img_edges.rows) / 2.), -ILLUMINATION_ANGLE_DEG, 1.0);

  cv::transform(contour, contour, Srot);

  float min_x, min_y, max_x, max_y;

  min_x = min_y = std::numeric_limits<float>::max();
  max_x = max_y = -std::numeric_limits<float>::max();

  // Simply find edges of aligned rectangle, then rotate back by inverse of Srot
}
manatttta
  • 3,054
  • 4
  • 34
  • 72
  • Possible duplicate of [Dealing with pixels in contours (OpenCV)?](http://stackoverflow.com/questions/8145036/dealing-with-pixels-in-contours-opencv) – ljetibo Nov 23 '15 at 16:52
  • @ljetibo no, it's not a duplicate of that one. – Miki Nov 23 '15 at 17:20
  • @manattta Some constraints on this? 1) there are 4 connected components in that rotated rect. Which one is the separator? 2) The contour in the middle (I think you want this to separate left from right) is not connected. How do you plan to separate if the boundary is not connected? 3) is the rotated rect always touching the _boundary_ edge? 4) Assuming a few things, this is trivial for axis aligned rects. Do you need specfifically for _rotated_ rects? – Miki Nov 23 '15 at 17:24
  • @Miki; It's a mask within a roi, except the purpose is stated in that question (summing pixel values) when no purpose was stated in this question. The solution should also be the same, albeit probably not what the OP envisioned. Both questions as well are actually asking about a method of getting "pixels inside contours". – ljetibo Nov 23 '15 at 17:26
  • 1
    @ljetibo that is about working with pixels on (or inside a closed) contour. This is about using the contour as a boundary to separate two areas. If in that question is just a trivial matter of masking and anding, here you can't do that. – Miki Nov 23 '15 at 17:30
  • @ljetibo However, I get why you though so. The title of this question is very misleading. I'll fix that as soon the OP makes clear a few stuff. – Miki Nov 23 '15 at 17:42
  • @Miki 1, 2) it is not a connected component, but I want to treat this as if it were (at least the middle line) 4) This is actually an aligned rect, with a previously given angle! – manatttta Nov 23 '15 at 17:45
  • What about 3)? and about 4) if it's rotated now, then it's not axis-aligned. Can you provide an example image, and the _minimal_ code to to get to the point you draw the rectangle around the edges? – Miki Nov 23 '15 at 17:48
  • @Miki 3) What do you mean by bounding edge? 4) I will post now – manatttta Nov 23 '15 at 17:50
  • Nevermind, clearly the answer is NO. – Miki Nov 23 '15 at 17:53
  • @Miki what do you mean? – manatttta Nov 23 '15 at 17:54
  • The clustering seems familiar :D. The most important part is: do you use `boundingRect` or `minAreaRect`? Don't worry about my previous comment.. – Miki Nov 23 '15 at 18:02
  • @Miki I use none. I iterate over all the points and store the (min_x, min_y) and (max_x, max_y) points, then rotate them back – manatttta Nov 23 '15 at 18:03
  • OK, check [here](http://stackoverflow.com/questions/33853288/retrieve-value-from-stdvectorcvpointconst-iterator/33854595#33854595) why this is equivalent to `boundingRect` – Miki Nov 23 '15 at 18:05
  • Ok, requirements are more clear now... The main problem here is how you define the set of edges that define the boundary among the two regions. Did you thought about this? – Miki Nov 23 '15 at 18:10
  • @Miki I am not even that far yet. I'm having trouble getting the start and end point of a contour. Maybe I should fit this to a polyline? – manatttta Nov 23 '15 at 18:16
  • That could be a solution. Yet you need to remove the small blobs, probably. – Miki Nov 23 '15 at 18:18
  • @Miki does opencv support this? Btw how would I get the start and end point of the contour line? – manatttta Nov 23 '15 at 18:21
  • you can use `approxPolyDP`, but it's not going to help here. What you want to achieve is clear and can be easily accomplished if you have a single connected component as the boundary between the two areas. Since an answer with such premises is useless to you, I won't post one. You first need to find a way to get this single connected boundary, then we can talk about using it do separate two areas :D – Miki Nov 23 '15 at 18:47
  • without reading all the comments: can you draw some images to visualize what you want to achieve? I didnt completely understand. Do you "just" want to split the rectangle according to the "contour"? That wont work for each kind of contour I guess... – Micka Nov 23 '15 at 23:25
  • if your "contour" always separates the bounding rect try this (might be slow): 1. draw rotatedRect filled white on a black image (mask1). 2. draw rotatedRect and contour outlines black on white image (mask2). 3. compute distance transform of mask2. 4. compute distT > 0 and mask it with mask1. Now you should have a black image with 2 white contours + noise (e.g. small inner contours) – Micka Nov 23 '15 at 23:37
  • @Miki Ok let's assume I can get a connected component. How can I proceed then? – manatttta Nov 24 '15 at 09:48
  • @Micka that was a good approach, but won't work here, I guess! – manatttta Nov 24 '15 at 09:50

1 Answers1

1

Ok let's assume I can get a connected component. How can I proceed then?

From the comments to the question we agreed that this procedure should work for axis aligned rectangles. This won't lose generality, since you can rotate a rotated rectangle to be axis aligned, apply this procedure, and then rotate points back.

Starting from a sample image with some edges, like:

enter image description here

You can get something like this, where blue is the left part in the bounding box separated by the edge, red is the right part:

enter image description here

This algorithm is probably not the most clever way of doing it, but works ok in practice.

After you found the bounding box of each edge:

  1. Create a matrix tmp on the given roi, plus 1 column on the left and 1 one the right. This will make the algorithm robust to particular cases.
  2. Shift all boundary point in the new coordinate system, and draw then into tmp.
  3. Apply floodFill algorithm to find left points. The seed is the top left corner of tmp.
  4. Apply floodFill algorithm to find right points. The seed is the top right corner of tmp.
  5. Retrieve the points in the two areas, shifting to original coordinate system.

Here the commented code, please ping me if something is not clear:

#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;


void separateAreas(const Rect& roi, const vector<Point>& points, vector<Point>& left, vector<Point>& right)
{
    left.clear();
    right.clear();

    // Temporary matrix
    // 0 : background pixels
    // 1 : boundary pixels
    // 2 : left pixels
    // 3 : right pixels
    Mat1b tmp(roi.height, roi.width + 2, uchar(0));

    // Shift points to roi origin, i.e tmp(0,1)
    vector<Point> pts(points);
    for (int i = 0; i < points.size(); ++i)
    {
        pts[i] -= roi.tl();

        // Draw boundary on tmp matrix
        tmp(pts[i] + Point(1,0)) = 1;
    }

    // Fill left area, seed top left point
    floodFill(tmp, Point(0, 0), Scalar(2));

    // Fill right area, seed top right point
    floodFill(tmp, Point(tmp.cols-1, 0), Scalar(3));

    // Find left and right points
    findNonZero(tmp.colRange(1, tmp.cols - 1) == 2, left);
    findNonZero(tmp.colRange(1, tmp.cols - 1) == 3, right);

    // Shift back
    for (int i = 0; i < left.size(); ++i)
    {
        left[i] += roi.tl();
    }
    for (int i = 0; i < right.size(); ++i)
    {
        right[i] += roi.tl();
    }
}


int main()
{
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    Mat3b res;
    cvtColor(img, res, COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    findContours(img.clone(), contours, RETR_LIST, CV_CHAIN_APPROX_NONE);

    for (int i = 0; i < contours.size(); ++i)
    {
        Rect roi = boundingRect(contours[i]);
        //rectangle(res, roi, Scalar(0,255,0));

        vector<Point> left, right;
        separateAreas(roi, contours[i], left, right);

        // Draw areas on res
        for (int j = 0; j < left.size(); ++j)
        {
            res(left[j]) = Vec3b(255,0,0); // Blue for left
        }
        for (int j = 0; j < right.size(); ++j)
        {
            res(right[j]) = Vec3b(0, 0, 255); // Red for right
        }
    }

    imshow("Image", img);
    imshow("Result", res);
    waitKey();

    return 0;
}
Miki
  • 40,887
  • 13
  • 123
  • 202