user2351488
user2351488

Reputation:

OpenCV - Cropping non rectangular region from image using C++

How can I crop a non rectangular region from image?

Imagine I have four points and I want to crop it, this shape wouldn't be a triangle somehow!

For example I have the following image :

enter image description here

and I want to crop this from image :

enter image description here

How can I do this? regards..

Upvotes: 6

Views: 8525

Answers (2)

Tim Sweet
Tim Sweet

Reputation: 645

The procedure for cropping an arbitrary quadrilateral (or any polygon for that matter) part of an image is summed us as:

  • Generate a "mask". The mask is black where you want to keep the image, and white where you don't want to keep it
  • Compute the "bitwise_and" between your input image and the mask

So, lets assume you have an image. Throughout this I'll use an image size of 30x30 for simplicity, you can change this to suit your use case.

cv::Mat source_image = cv::imread("filename.txt");

And you have four points you want to use as the corners:

cv::Point corners[1][4];
corners[0][0] = Point( 10, 10 );
corners[0][1] = Point( 20, 20 );
corners[0][2] = Point( 30, 10 );
corners[0][3] = Point( 20, 10 );
const Point* corner_list[1] = { corners[0] };

You can use the function cv::fillPoly to draw this shape on a mask:

int num_points = 4;
int num_polygons = 1;
int line_type = 8;
cv::Mat mask(30,30,CV_8UC3, cv::Scalar(0,0,0));
cv::fillPoly( mask, corner_list, &num_points, num_polygons, cv::Scalar( 255, 255, 255 ),  line_type);

Then simply compute the bitwise_and of the image and mask:

cv::Mat result;
cv::bitwise_and(source_image, mask, result);

result now has the cropped image in it. If you want the edges to end up white instead of black you could instead do:

cv::Mat result_white(30,30,CV_8UC3, cv::Scalar(255,255,255));
cv::bitwise_and(source_image, mask, result_white, mask);

In this case we use bitwise_and's mask parameter to only do the bitwise_and inside the mask. See this tutorial for more information and links to all the functions I mentioned.

Upvotes: 9

ivan_onys
ivan_onys

Reputation: 2372

You may use cv::Mat::copyTo() like this:

cv::Mat img = cv::imread("image.jpeg");
// note mask may be single channel, even if img is multichannel
cv::Mat mask = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
// fill mask with nonzero values, e.g. as Tim suggests
// cv::fillPoly(...)
cv::Mat result(img.size(), img.type(), cv::Scalar(255, 255, 255));
img.copyTo(result, mask);

Upvotes: 1

Related Questions