c++ - 计算两个梯形的 'percentage matched'的最快方法

标签 c++ opencv image-processing

我问了以下问题here并得到了一个很好的解决方案,但发现它的性能太慢(640x480 图像需要 2-300 毫秒)。现在我想考虑如何优化它。

问题:
给定两个多边形(总是平行于 X 轴的梯形),我想通过某种方式计算它们的匹配程度。我的意思是重叠面积是不够的,因为如果一个多边形有多余的面积,则需要以某种方式对其进行计数。最理想的是,我想知道两个多边形创建的面积的共同百分比是多少。例如,请参阅所需的图像。

Example

一个有效(但缓慢)的解决方案:
- 在空图像上绘制多边形一(cv::fillConvexPoly)
- 在空图像上绘制多边形二 (cv::fillConvexPoly)
- 执行按位运算并创建所有重叠像素的图像
- 计算所有非零像素 --> 重叠像素
- 反转第一个图像并使用非反转的第二个图像重复 --> 像素过多
- 反转第二个图像并使用非反转的第一个图像重复 --> 更多过多的像素
- 将“重叠像素”除以“过多像素”的总和

正如您所看到的,当前的解决方案计算量很大,因为它对图像的每个像素进行约 12 次评估/操作。我更喜欢一个计算该区域的解决方案,该解决方案需要经过繁琐的构建和评估多个图像。

现有代码:

#define POLYGONSCALING 0.05
typedef std::array<cv::Point, 4> Polygon;

float PercentMatch( const Polygon& polygon,
                    const cv::Mat optimalmat )
{
    //Create blank mats
    cv::Mat polygonmat{ cv::Mat(optimalmat.rows, optimalmat.cols, CV_8UC1, cv::Scalar(0)) };
    cv::Mat resultmat{ cv::Mat(optimalmat.rows, optimalmat.cols, CV_8UC1, cv::Scalar(0)) };

    //Draw polygon
    cv::Point cvpointarray[4];
    for  (int i =0; i < 4; i++ ) {
        cvpointarray[i] = cv::Point(POLYGONSCALING * polygon[i].x, POLYGONSCALING *
            polygon[i].y);
    }
    cv::fillConvexPoly( polygonmat, cvpointarray, 4,  cv::Scalar(255) );

    //Find overlapped pixels
    cv::bitwise_and(polygonmat, optimalmat, resultmat);
    int overlappedpixels { countNonZero(resultmat) };

    //Find excessive pixels
    cv::bitwise_not(optimalmat, resultmat);
    cv::bitwise_and(polygonmat, resultmat, resultmat);
    int excessivepixels { countNonZero(resultmat) };
    cv::bitwise_not(polygonmat, resultmat);
    cv::bitwise_and(optimalmat, resultmat, resultmat);
    excessivepixels += countNonZero(resultmat);

    return (100.0 * overlappedpixels) / (overlappedpixels + excessivepixels);
}

目前,我设计的唯一性能改进是在函数外部绘制“optimalmat”,这样它就不会重新绘制(它与许多其他多边形进行比较),而且我还添加了 POLYGONSCALING 来缩小多边形的大小,失去一些分辨率,但获得一些性能。还是太慢了。

最佳答案

我可能误解了你想要什么,但我认为你应该能够像这样更快地完成......

  • 在零背景上用 1 填充第一个梯形。
  • 在零背景上用 2 填充第二个梯形。
  • 将两个垫子添加在一起。

现在每个像素必须为 0、1、2 或 3。创建一个包含 4 个元素的数组,并在一次遍历所有元素时,不使用 if 语句,只需增加相应的数组元素即可根据每个像素的值。

然后,数组的第一个索引中的像素总数是梯形都不存在的位置,索引 1 和 2 的元素是存在梯形 1 或 2 的元素,索引 3 的元素重叠。

此外,尝试对两个梯形的洪水填充进行基准测试,如果这是一个很大的比例,如果您有时间,也许有第二个线程填充第二个梯形。

enter image description here

基准

我编写了一些代码来尝试上述理论,对于 640x480 的图像,它需要:

  • 绘制第一个多边形需要 181 微秒
  • 绘制第二个多边形需要 84 微秒
  • 计算重叠需要 481 微秒

因此,我的 iMac 上的总时间为 740 微秒。

您可以与第一个多边形并行绘制第二个多边形,但线程创建和连接时间约为 20 微秒,因此您只能节省 60 微秒,即 8% 左右 - 可能不值得。

大部分代码都是计时和调试:

#include "opencv2/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <chrono>

using namespace cv;
using namespace std;

const int COLS=640;
const int ROWS=480;

typedef std::chrono::high_resolution_clock hrclock;
hrclock::time_point t1,t2;
std::chrono::nanoseconds elapsed;
int e;

int
main(int argc,char*argv[]){

   Mat canvas1(ROWS,COLS,CV_8UC1,Scalar(0));
   Mat canvas2(ROWS,COLS,CV_8UC1,Scalar(0));
   Mat sum(ROWS,COLS,CV_8UC1,Scalar(0));

   //Draw polygons on canvases
   Point vertices1[4]={Point(10,10),Point(400,10),Point(400,460),Point(10,460)};
   Point vertices2[4]={Point(300,50),Point(600,50),Point(600,400),Point(300,400)};

   t1 = hrclock::now();
   // FilConvexPoly takes around 150 microseconds here
   fillConvexPoly(canvas1,vertices1,4,cv::Scalar(1));
   t2 = hrclock::now();
   elapsed = t2-t1;
   e=elapsed.count();
   cout << "fillConvexPoly: " << e << "ns" << std::endl;

   imwrite("canvas1.png",canvas1);

   t1 = hrclock::now();
   // FilConvexPoly takes around 80 microseconds here
   fillConvexPoly(canvas2,vertices2,4,cv::Scalar(2));
   t2 = hrclock::now();
   elapsed = t2-t1;
   e=elapsed.count();
   cout << "fillConvexPoly: " << e << "ns" << std::endl;
   imwrite("canvas2.png",canvas2);
   sum=canvas1+canvas2;
   imwrite("sum.png",sum);

   long totals[4]={0,0,0,0};
   uchar* p1=sum.data;
   t1 = hrclock::now();
   for(int j=0;j<ROWS;j++){
      uchar* data= sum.ptr<uchar>(j);
      for(int i=0;i<COLS;i++) {
         totals[data[i]]++;
      }
   }
   t2 = hrclock::now();
   elapsed = t2-t1;
   e=elapsed.count();
   cout << "Count overlap: " << e << "ns" << std::endl;
   for(int i=0;i<4;i++){
      cout << "totals[" << i << "]=" << totals[i] << std::endl;
   }
}

示例运行

fillConvexPoly: 181338ns
fillConvexPoly: 84759ns
Count overlap: 481830ns
totals[0]=60659
totals[1]=140890
totals[2]=70200
totals[3]=35451

使用 ImageMagick 进行验证,如下所示:

identify -verbose sum.png | grep -A4 Histogram:

Histogram:
 60659: (  0,  0,  0) #000000 gray(0)
140890: (  1,  1,  1) #010101 gray(1)
 70200: (  2,  2,  2) #020202 gray(2)
 35451: (  3,  3,  3) #030303 gray(3)

关于c++ - 计算两个梯形的 'percentage matched'的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40333364/

相关文章:

c# - GC.AddMemoryPressure() 不足以按时触发 Finalizer 队列执行

c++ - 在我的 switch 语句完全执行之前,我的 while 循环进入第二次迭代,我正在用 c++ 编码

ruby-on-rails - CarrierWave + RMagick Square Crop?

image-processing - 是否有任何开放技术来识别面部特征?

c++ - 重复符号问题

python - 使用 C++/Python 对 wav 文件进行 FFT

opencv - OpenCV 上的 resize 函数有什么问题?

c++ - OpenCV: vector < vector <Point>>到 vector <Mat>的转换,通过引用调用的问题

python - Python 中的图像注视点

c++ - 将图像裁剪成碎片然后加入,这可以使用 OpenCV 吗?