The number of non-zero pixels in cv :: RotatedRect

as the title says I'm trying to find the number of nonzero pixels in a specific area of ​​cv :: Mat, namely in RotatedRect.

For a regular Rect, you can simply use countNonZeroPixels in the ROI. However, ROIs can only be regular (no rotation) rectangles.

Another idea was to draw a rotating rectangle and use it as a mask. However, openCV does not support the drawing of rotating rectangles and does not believe that NonZeroPixels accepts the mask.

Does anyone have a solution on how to elegantly solve this?

Thanks!

+3
source share
3 answers

Ok, so here is my first example.

The idea is to rotate the image back to rotate the rectangle, and then apply roi on the rectified rectangle.

  • This will break if the rotation rectangle is not completely inside the image.
  • You can probably speed it up by applying another swarm before turning to avoid rotating the entire image ...

    #include <highgui.h> #include <cv.h> // From http://stackoverflow.com/questions/2289690/opencv-how-to-rotate-iplimage cv::Mat rotateImage(const cv::Mat& source, cv::Point2f center, double angle) { cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0); cv::Mat dst; cv::warpAffine(source, dst, rot_mat, source.size()); return dst; } int main() { cv::namedWindow("test1"); // Our rotated rect int x = 300; int y = 350; int w = 200; int h = 50; float angle = 47; cv::RotatedRect rect = cv::RotatedRect(cv::Point2f(x,y), cv::Size2f(w,h), angle); // An empty image cv::Mat img = cv::Mat(cv::Size(640, 480), CV_8UC3); // Draw rotated rect as an ellipse to get some visual feedback cv::ellipse(img, rect, cv::Scalar(255,0,0), -1); // Rotate the image by rect.angle * -1 cv::Mat rotimg = rotateImage(img, rect.center, -1 * rect.angle); // Set roi to the now unrotated rectangle cv::Rect roi; roi.x = rect.center.x - (rect.size.width / 2); roi.y = rect.center.y - (rect.size.height / 2); roi.width = rect.size.width; roi.height = rect.size.height; cv::imshow("test1", rotimg(roi)); cv::waitKey(0); } 
+3
source

A completely different approach could be to rotate the image (in the opposite direction) and still use a rectangular ROI in combination with countNonZeroPixels. The only problem will be that you need to rotate the image around the axis of the ROI ...

To make this clearer, see the attached example:

enter image description here

+1
source

To avoid rotation in a similar task, I repeat for each pixel in a RotatedRect with this function:

 double filling(Mat& img, RotatedRect& rect){ double non_zero = 0; double total = 0; Point2f rect_points[4]; rect.points( rect_points ); for(Point2f i=rect_points[0];norm(i-rect_points[1])>1;i+=(rect_points[1]-i)/norm((rect_points[1]-i))){ Point2f destination = i+rect_points[2]-rect_points[1]; for(Point2f j=i;norm(j-destination)>1;j+=(destination-j)/norm((destination-j))){ if(img.at<uchar>(j) != 0){ non_zero+=1; } total+=1; } } return non_zero/total; } 

It looks like regular iteration over a rectangle, but at each step we add the 1px vector to the current point in the direction of the destination.

This loop does NOT iterate over all the points and skips a few pixels, but that was normal for my task.

UPD: It is much better to use LineIterator to iterate:

 Point2f rect_points[4]; rect.points(rect_points); Point2f x_start = rect_points[0]; Point2f x_end = rect_points[1]; Point2f y_direction = rect_points[3] - rect_points[0]; LineIterator x = LineIterator(frame, x_start, x_end, 4); for(int i = 0; i < x.count; ++i, ++x){ LineIterator y = LineIterator(frame, x.pos(), x.pos() + y_direction, 4); for(int j=0; j < y_count; j++, ++y){ Vec4b pixel = frame.at<Vec4b>(y.pos); /* YOUR CODE HERE */ } } 
0
source

All Articles