计算RotatedRect OpenCV内部白色像素的百分比

时间:2018-12-02 00:20:11

标签: c++ opencv

如何计算cv :: RotatedRect内部白色像素的百分比?我的意思是,如何在cv :: RotatedRect中访问单个像素。如果我能做到这一点,我以后会知道。谢谢

我已经尝试过此线程的解决方案,但是我有例外。 https://stackoverflow.com/a/28780359

    std::vector<cv::RotatedRect> minRect(count.size());
    for (int i = 0; i < count.size(); i++)
    {
        minRect[i] = cv::minAreaRect(cv::Mat(count[i]));
    }
    for (size_t i = 0; i < count.size(); i++){
        if (cv::contourArea(count[i]) > 200) {
            cv::Point2f rect_points[4];
            minRect[i].points(rect_points);
            // Now I'd like to calculate percentage of white pixels inside of RotatedRect, and if value returned by func would be smaller than 30%,continue;
            for (int j = 0; j < 4; j++) {
                cv::line(mask, rect_points[j], rect_points[(j + 1) % 4], (0, 255, 0), 1, 8);
            }
        }

    }

2 个答案:

答案 0 :(得分:1)

我想得到单个像素的最佳方法是,首先获取旋转矩形的bounding box,然后遍历框内的每个像素以查看它们是否在旋转矩形中与pointPolygonTest。我不确定是否有更有效的方法,但这应该可以为您提供所需的结果。

答案 1 :(得分:1)

您可以:

  1. 处理cv::boundingRect定义的子图像
  2. 使用cv::fillConvexPoly创建一个遮罩,其中旋转的矩形内的所有点都是白色的。
  3. 与原始图像进行逻辑与
  4. cv::countNonZero计数白色像素的数量

John Henkel提出的方法有效,但是在我的测试中(非常快),速度要慢10到40倍。

下面两种方法的代码。您会发现结果之间的微小差异,因为旋转的矩形边框上的白色像素处理方式不同。

#include <opencv2\opencv.hpp>
#include <chrono>

int main()
{
    // Create binary image with random pixels b/W
    cv::Mat1b img(5000, 5000);
    cv::randu(img, cv::Scalar(0), cv::Scalar(256));
    img = img > 127;

    // Define a rotated rect
    cv::Point2f center(2000, 2000);
    cv::Size2f sz(1000, 500);
    float angle = 30.f;
    cv::RotatedRect rr(center, sz, angle);

    // Get points
    std::vector<cv::Point2f> points(4);
    rr.points(points.data());

    // Work on ROI
    cv::Rect roi = rr.boundingRect();

    // Area 
    float area = rr.size.width * rr.size.height;

    //// DEBUG, Show rect
    //cv::Mat3b out;
    //cv::cvtColor(img, out, cv::COLOR_GRAY2BGR);
    //for (int i = 0; i < 4; ++i) {
    //  cv::line(out, points[i], points[(i + 1) % 4], cv::Scalar(0, 0, 255));
    //}

    {
        // --------------------
        // Method @Miki
        // --------------------

        auto tic = std::chrono::high_resolution_clock::now();

        cv::Mat1b sub_img = img(roi);

        // Create rotated rect mask
        cv::Mat1b mask(roi.size(), uchar(0));
        std::vector<cv::Point> points_in_sub_image(4);
        for (int i = 0; i < 4; ++i) {
            points_in_sub_image[i] = cv::Point(points[i]) - roi.tl();
        }
        cv::fillConvexPoly(mask, points_in_sub_image, cv::Scalar(255));

        // AND sub image with mask
        cv::Mat1b inside_roi = sub_img & mask;

        //// DEBUG, Draw green points
        //for (int r = 0; r < sub_img.rows; ++r) {
        //  for (int c = 0; c < sub_img.cols; ++c) {
        //      if (inside_roi(r, c) > 0)
        //      {
        //          out(r + roi.y, c + roi.x) = cv::Vec3b(0, 255, 0);
        //      }
        //  }
        //}


        // Get actual count
        int cnz = cv::countNonZero(inside_roi);

        auto toc = std::chrono::high_resolution_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(toc - tic);

        float percent_white_pixels = cnz / area;
        std::cout << "percent_white_pixels: " << percent_white_pixels << " in " << elapsed.count() << " us" <<  std::endl;
    }

    {
        // --------------------
        // Method @John Henkel
        // --------------------

        auto tic = std::chrono::high_resolution_clock::now();

        int cnz = 0;
        for (int y = roi.y; y < roi.y + roi.height; ++y) {
            for (int x = roi.x; x < roi.x + roi.width; ++x) {
                if (
                    (img(y, x) > 0) &&
                    (cv::pointPolygonTest(points, cv::Point2f(x, y), false) >= 0.0)
                    ) 
                {
                    // DEBUG, Draw blue points
                    //out(y, x) = cv::Vec3b(255, 0, 0);
                    ++cnz;
                }
            }
        }

        auto toc = std::chrono::high_resolution_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(toc - tic);

        float percent_white_pixels = cnz / area;
        std::cout << "percent_white_pixels: " << percent_white_pixels << " in " << elapsed.count() << " us" <<  std::endl;
    }

    getchar();
    return 0;
}