如何有效地计算每个多边形区域内的所有非零像素?

时间:2020-11-03 02:36:01

标签: c++ opencv

我想有效地计算每个多边形区域内所有白色像素的数量。

给出一些过程:

// some codes for reading gray image
// cv::Mat gray = cv::imread("gray.jpg");

// given polygons
// vector< vector<cv::Point> > polygons;

cv::Mat cropped;
cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
cv::fillPoly(mask, polygons, cv::Scalar(255));
cv::bitwise_and(gray, gray, cropped, mask);

cv::Mat binary;
cv::threshold(cropped, binary, 20, 255, CV_THRESH_BINARY);

因此,到目前为止,我们可以获得具有多个多边形区域(例如,我们有3个区域)的图像,这些区域具有白色(值为255)像素。然后,经过一些操作,我们期望得到一个像这样的向量:

// some efficient operations
// ...

vector<int> pixelNums;

pixelNums的大小应与此处为3的多边形相同。如果我们将它们打印出来,我们可能会得到一些输出(值基本上取决于预处理):

index: 0; value: 120
index: 1; value: 1389
index: 2; value: 0

这是我的想法。在cv::countNonZero的帮助下对每个多边形区域中的每个像素进行计数,但是我需要在一个循环中调用它,我认为这不是一种有效的方法,不是吗?

vector<int> pixelNums;
for(auto polygon : polygons)
{
  vector< vector<cv::Point> > temp_polygons;
  temp_polygons.push_back(polygon);

  cv::Mat cropped;
  cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
  cv::fillPoly(mask, temp_polygons, cv::Scalar(255));
  cv::bitwise_and(gray, gray, cropped, mask);

  cv::Mat binary;
  cv::threshold(cropped, binary, 20, 255, CV_THRESH_BINARY);

  pixelNums.push_back(cv::countNonZero(binary));
}

如果您有更好的方法,请回答此帖子。在这里,我说better way仅在CPU环境中消耗的时间最少。

1 个答案:

答案 0 :(得分:1)

可以做一些小的改进,但是所有这些结合起来应该可以提供不错的加速效果。

  1. 仅计算一次阈值
  2. 使用多边形的边界框获取感兴趣的区域,对较小的图像进行大多数操作
  3. 在for循环中避免不必要的副本,请使用const auto&

示例代码:

#include <vector>
#include <opencv2/opencv.hpp>

int main()
{
    // Your image
    cv::Mat1b gray = cv::imread("path/to/image", cv::IMREAD_GRAYSCALE);

    // Your polygons
    std::vector<std::vector<cv::Point>> polygons
    {
        { {15,120}, {45,200}, {160,160}, {140, 60} },
        { {10,10}, {15,30}, {50,25}, {40, 15} },
        // etc...
    };

    // Compute the threshold just once
    cv::Mat1b thresholded = gray > 20;

    std::vector<int> pixelNums;
    for (const auto& polygon : polygons)
    {
        // Get bbox of polygon
        cv::Rect bbox = cv::boundingRect(polygon);

        // Make a new (small) mask 
        cv::Mat1b mask(bbox.height, bbox.width, uchar(0));
        cv::fillPoly(mask, std::vector<std::vector<cv::Point>>{polygon}, cv::Scalar(255), 8, 0, -bbox.tl());

        // Get crop
        cv::Mat1b cropped = thresholded(bbox) & mask;

        // Compute the number of white pixels only on the crop
        pixelNums.push_back(cv::countNonZero(cropped));
    }

    return 0;
}