Merge pull request #25122 from unnonouno:pqueue

Use std::priority_queue in inpaint function for performance improvement #25122

In `cv::inpaint` implementation, it uses a priority queue with O(n) time linear search. For large images it is very slow.
I replaced it with C++'s standard library `std::priority_queue`, that uses O(log(n)) algorithm.
In my use case, it is x10 faster than the original.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Yuya Unno 2024-03-02 19:43:58 +09:00 committed by GitHub
parent deab144e22
commit 500c55a808
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 94 deletions

View File

@ -6,6 +6,7 @@ namespace opencv_test
CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA)
typedef tuple<Size, InpaintingMethod> InpaintArea_InpaintingMethod_t;
typedef perf::TestBaseWithParam<InpaintArea_InpaintingMethod_t> InpaintArea_InpaintingMethod;
typedef perf::TestBaseWithParam<InpaintingMethod> Perf_InpaintingMethod;
PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
@ -34,4 +35,26 @@ PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
SANITY_CHECK(inpaintedArea);
}
PERF_TEST_P(Perf_InpaintingMethod, inpaintDots, InpaintingMethod::all())
{
Mat src = imread(getDataPath("gpu/hog/road.png"));
int inpaintingMethod = GetParam();
Mat mask(src.size(), CV_8UC1, Scalar(0));
Mat result(src.size(), src.type());
for (int i = 0; i < src.size().height; i += 16) {
for (int j = 0; j < src.size().width; j += 16) {
mask.at<unsigned char>(i, j) = 255;
}
}
declare.in(src, mask).out(result).time(120);
TEST_CYCLE() inpaint(src, mask, result, 10.0, inpaintingMethod);
SANITY_CHECK_NOTHING();
}
} // namespace

View File

@ -45,6 +45,8 @@
//
// */
#include <queue>
#include "precomp.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/photo/legacy/constants_c.h"
@ -71,8 +73,16 @@ typedef struct CvHeapElem
{
float T;
int i,j;
struct CvHeapElem* prev;
struct CvHeapElem* next;
int order; // to keep insertion order
bool operator > (const CvHeapElem& rhs) const {
if (T > rhs.T) {
return true;
} else if (T < rhs.T) {
return false;
}
return order > rhs.order;
}
}
CvHeapElem;
@ -84,42 +94,10 @@ private:
CvPriorityQueueFloat& operator=(const CvPriorityQueueFloat &); // assign disabled
protected:
CvHeapElem *mem,*empty,*head,*tail;
int num,in;
std::priority_queue<CvHeapElem, std::vector<CvHeapElem>,std::greater<CvHeapElem> > queue;
int next_order;
public:
bool Init( const CvMat* f )
{
int i,j;
for( i = num = 0; i < f->rows; i++ )
{
for( j = 0; j < f->cols; j++ )
num += CV_MAT_ELEM(*f,uchar,i,j)!=0;
}
if (num<=0) return false;
mem = (CvHeapElem*)cvAlloc((num+2)*sizeof(CvHeapElem));
if (mem==NULL) return false;
head = mem;
head->i = head->j = -1;
head->prev = NULL;
head->next = mem+1;
head->T = -FLT_MAX;
empty = mem+1;
for (i=1; i<=num; i++) {
mem[i].prev = mem+i-1;
mem[i].next = mem+i+1;
mem[i].i = -1;
mem[i].T = FLT_MAX;
}
tail = mem+i;
tail->i = tail->j = -1;
tail->prev = mem+i-1;
tail->next = NULL;
tail->T = FLT_MAX;
return true;
}
bool Add(const CvMat* f) {
int i,j;
for (i=0; i<f->rows; i++) {
@ -133,71 +111,33 @@ public:
}
bool Push(int i, int j, float T) {
CvHeapElem *tmp=empty,*add=empty;
if (empty==tail) return false;
while (tmp->prev->T>T) tmp = tmp->prev;
if (tmp!=empty) {
add->prev->next = add->next;
add->next->prev = add->prev;
empty = add->next;
add->prev = tmp->prev;
add->next = tmp;
add->prev->next = add;
add->next->prev = add;
} else {
empty = empty->next;
}
add->i = i;
add->j = j;
add->T = T;
in++;
// printf("push i %3d j %3d T %12.4e in %4d\n",i,j,T,in);
queue.push({T, i, j, next_order});
++next_order;
return true;
}
bool Pop(int *i, int *j) {
CvHeapElem *tmp=head->next;
if (empty==tmp) return false;
*i = tmp->i;
*j = tmp->j;
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
tmp->prev = empty->prev;
tmp->next = empty;
tmp->prev->next = tmp;
tmp->next->prev = tmp;
empty = tmp;
in--;
// printf("pop i %3d j %3d T %12.4e in %4d\n",tmp->i,tmp->j,tmp->T,in);
if (queue.empty()) {
return false;
}
*i = queue.top().i;
*j = queue.top().j;
queue.pop();
return true;
}
bool Pop(int *i, int *j, float *T) {
CvHeapElem *tmp=head->next;
if (empty==tmp) return false;
*i = tmp->i;
*j = tmp->j;
*T = tmp->T;
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
tmp->prev = empty->prev;
tmp->next = empty;
tmp->prev->next = tmp;
tmp->next->prev = tmp;
empty = tmp;
in--;
// printf("pop i %3d j %3d T %12.4e in %4d\n",tmp->i,tmp->j,tmp->T,in);
if (queue.empty()) {
return false;
}
*i = queue.top().i;
*j = queue.top().j;
*T = queue.top().T;
queue.pop();
return true;
}
CvPriorityQueueFloat(void) {
num=in=0;
mem=empty=head=tail=NULL;
}
~CvPriorityQueueFloat(void)
{
cvFree( &mem );
CvPriorityQueueFloat(void) : queue(), next_order() {
}
};
@ -786,8 +726,6 @@ icvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_
cvSet(t,cvScalar(1.0e6f,0,0,0));
cv::dilate(cv::cvarrToMat(mask), cv::cvarrToMat(band), el_cross, cv::Point(1, 1));
Heap=cv::makePtr<CvPriorityQueueFloat>();
if (!Heap->Init(band))
return;
cvSub(band,mask,band,NULL);
SET_BORDER1_C1(band,uchar,0);
if (!Heap->Add(band))
@ -803,8 +741,6 @@ icvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_
cv::dilate(cv::cvarrToMat(mask), cv::cvarrToMat(out), el_range);
cvSub(out,mask,out,NULL);
Out=cv::makePtr<CvPriorityQueueFloat>();
if (!Out->Init(out))
return;
if (!Out->Add(band))
return;
cvSub(out,band,out,NULL);