问题
as the title says i'm trying to find the number of non-zero pixels in a certain area of a cv::Mat, namely within a RotatedRect.
For a regular Rect one could simply use countNonZeroPixels on a ROI. However ROIs can only be regular (non rotated) rectangles.
Another idea was to draw the rotated rectangle and use that as a mask. However openCV neither supports the drawing of rotated rectangles nor does countNonZeroPixels accept a mask.
Does anyone have a solution for how to elegantly solve this ?
Thank you !
回答1:
Ok, so here's my first take at it.
The idea is to rotate the image reverse to the rectangle's rotation and than apply a roi on the straightened rectangle.
- This will break if the rotated rectangle is not completely within the image
You can probably speed this up by applying another roi before rotation to avoid having to rotate the whole 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); }
回答2:
A totally different approach might be to rotate your image (in opposite direction), and still use the rectangular ROI in combination with countNonZeroPixels. The only problem will be that you have to rotate your image around a pivot of the center of the ROI...
To make it clearer, see attached example:
回答3:
To avoid rotation in similar task I iterate over each pixel in RotatedRect with such 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's looks like usual iteration over rectangle, but on each step we add unit 1px vector to current point in direction to destination.
This loop NOT iterate over all points and skip a few pixels, but it was okay for my task.
UPD: It 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 */
}
}
来源:https://stackoverflow.com/questions/6653272/number-of-non-zero-pixels-in-a-cvrotatedrect