计算两个梯形的'匹配百分比'的最快方法

时间:2016-10-30 20:35:56

标签: c++ opencv image-processing

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

问题:
给定两个多边形(总是与X轴平行的梯形),我想通过某种方式计算出它们的匹配程度。通过这个,我的意思是重叠区域是不够的,因为如果一个多边形有多余的区域,不知何故需要计算它。最理想的是,我想知道两个多边形所创造的面积百分比是多少。例如,根据需要查看图像。

Example

一种有效(但缓慢)的解决方案:
- 在空图像上绘制多边形1(cv :: fillConvexPoly)
- 在空图像上绘制多边形2(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 个答案:

答案 0 :(得分:2)

我可能误解了你的想法,但我认为你应该能够更快地做到这一点......

  • 用零背景填充你的第一个梯形。
  • 在零背景上用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)