manatttta
manatttta

Reputation: 3124

OpenCV: check if pixel is within bounding rectangle, separated by contour line

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
}

Upvotes: 0

Views: 2871

Answers (1)

Miki
Miki

Reputation: 41765

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;
}

Upvotes: 1

Related Questions