Количество ненулевых пикселей в cv :: RotatedRect - PullRequest
3 голосов
/ 11 июля 2011

Как видно из заголовка, я пытаюсь найти число ненулевых пикселей в определенной области cv :: Mat, а именно в RotatedRect.

Для обычного Rect можно просто использоватьcountNonZeroPixels на ROI.Однако области интереса могут быть только правильными (не повернутыми) прямоугольниками.

Другая идея состояла в том, чтобы нарисовать повернутый прямоугольник и использовать его в качестве маски.Однако openCV не поддерживает рисование повернутых прямоугольников и не учитывает маску countNonZeroPixels.

У кого-нибудь есть решение для элегантного решения этой проблемы?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 12 июля 2011

Хорошо, вот мой первый взгляд на это.

Идея состоит в том, чтобы повернуть изображение обратно к вращению прямоугольника и затем применить roi к выпрямленному прямоугольнику.

  • Это сломается, если повернутый прямоугольник не будет полностью в изображении
  • Вы, вероятно, можете ускорить это, применив еще одну roi перед вращением, чтобы избежать необходимости поворачивать все изображение ...

    #include <highgui.h>
    #include <cv.h>
    
    
    // From /2215440/opencv-kak-vraschat-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);
    }
    
1 голос
/ 10 апреля 2013

Совершенно другой подход может заключаться в том, чтобы повернуть изображение (в противоположном направлении) и по-прежнему использовать прямоугольную область интереса в сочетании с countNonZeroPixels.Единственная проблема заключается в том, что вам нужно повернуть изображение вокруг оси центра области интереса ...

Чтобы сделать его более понятным, см. Прилагаемый пример:

enter image description here

0 голосов
/ 28 февраля 2015

Чтобы избежать поворота в аналогичной задаче, я перебираю каждый пиксель в RotatedRect с помощью такой функции:

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

Это похоже на обычную итерацию по прямоугольнику, но на каждом шаге мы добавляем вектор единичного 1px к текущей точке вНаправление к месту назначения.

Этот цикл НЕ повторяется по всем точкам и пропускает несколько пикселей, но это было нормально для моей задачи.

UPD: Его гораздо лучше использоватьLineIterator для итерации:

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 */
    }
}
...