首页 > 解决方案 > 计算RotatedRect 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);
            }
        }

    }

标签: c++opencv

解决方案


你可以:

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

推荐阅读