mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 03:00:14 +08:00
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:
parent
deab144e22
commit
500c55a808
@ -6,6 +6,7 @@ namespace opencv_test
|
|||||||
CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA)
|
CV_ENUM(InpaintingMethod, INPAINT_NS, INPAINT_TELEA)
|
||||||
typedef tuple<Size, InpaintingMethod> InpaintArea_InpaintingMethod_t;
|
typedef tuple<Size, InpaintingMethod> InpaintArea_InpaintingMethod_t;
|
||||||
typedef perf::TestBaseWithParam<InpaintArea_InpaintingMethod_t> InpaintArea_InpaintingMethod;
|
typedef perf::TestBaseWithParam<InpaintArea_InpaintingMethod_t> InpaintArea_InpaintingMethod;
|
||||||
|
typedef perf::TestBaseWithParam<InpaintingMethod> Perf_InpaintingMethod;
|
||||||
|
|
||||||
|
|
||||||
PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
|
PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
|
||||||
@ -34,4 +35,26 @@ PERF_TEST_P(InpaintArea_InpaintingMethod, inpaint,
|
|||||||
SANITY_CHECK(inpaintedArea);
|
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
|
} // namespace
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
//
|
//
|
||||||
// */
|
// */
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
#include "opencv2/imgproc/imgproc_c.h"
|
#include "opencv2/imgproc/imgproc_c.h"
|
||||||
#include "opencv2/photo/legacy/constants_c.h"
|
#include "opencv2/photo/legacy/constants_c.h"
|
||||||
@ -71,8 +73,16 @@ typedef struct CvHeapElem
|
|||||||
{
|
{
|
||||||
float T;
|
float T;
|
||||||
int i,j;
|
int i,j;
|
||||||
struct CvHeapElem* prev;
|
int order; // to keep insertion order
|
||||||
struct CvHeapElem* next;
|
|
||||||
|
bool operator > (const CvHeapElem& rhs) const {
|
||||||
|
if (T > rhs.T) {
|
||||||
|
return true;
|
||||||
|
} else if (T < rhs.T) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return order > rhs.order;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CvHeapElem;
|
CvHeapElem;
|
||||||
|
|
||||||
@ -84,42 +94,10 @@ private:
|
|||||||
CvPriorityQueueFloat& operator=(const CvPriorityQueueFloat &); // assign disabled
|
CvPriorityQueueFloat& operator=(const CvPriorityQueueFloat &); // assign disabled
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CvHeapElem *mem,*empty,*head,*tail;
|
std::priority_queue<CvHeapElem, std::vector<CvHeapElem>,std::greater<CvHeapElem> > queue;
|
||||||
int num,in;
|
int next_order;
|
||||||
|
|
||||||
public:
|
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) {
|
bool Add(const CvMat* f) {
|
||||||
int i,j;
|
int i,j;
|
||||||
for (i=0; i<f->rows; i++) {
|
for (i=0; i<f->rows; i++) {
|
||||||
@ -133,71 +111,33 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Push(int i, int j, float T) {
|
bool Push(int i, int j, float T) {
|
||||||
CvHeapElem *tmp=empty,*add=empty;
|
queue.push({T, i, j, next_order});
|
||||||
if (empty==tail) return false;
|
++next_order;
|
||||||
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);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pop(int *i, int *j) {
|
bool Pop(int *i, int *j) {
|
||||||
CvHeapElem *tmp=head->next;
|
if (queue.empty()) {
|
||||||
if (empty==tmp) return false;
|
return false;
|
||||||
*i = tmp->i;
|
}
|
||||||
*j = tmp->j;
|
*i = queue.top().i;
|
||||||
tmp->prev->next = tmp->next;
|
*j = queue.top().j;
|
||||||
tmp->next->prev = tmp->prev;
|
queue.pop();
|
||||||
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);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pop(int *i, int *j, float *T) {
|
bool Pop(int *i, int *j, float *T) {
|
||||||
CvHeapElem *tmp=head->next;
|
if (queue.empty()) {
|
||||||
if (empty==tmp) return false;
|
return false;
|
||||||
*i = tmp->i;
|
}
|
||||||
*j = tmp->j;
|
*i = queue.top().i;
|
||||||
*T = tmp->T;
|
*j = queue.top().j;
|
||||||
tmp->prev->next = tmp->next;
|
*T = queue.top().T;
|
||||||
tmp->next->prev = tmp->prev;
|
queue.pop();
|
||||||
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);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CvPriorityQueueFloat(void) {
|
CvPriorityQueueFloat(void) : queue(), next_order() {
|
||||||
num=in=0;
|
|
||||||
mem=empty=head=tail=NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
~CvPriorityQueueFloat(void)
|
|
||||||
{
|
|
||||||
cvFree( &mem );
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -786,8 +726,6 @@ icvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_
|
|||||||
cvSet(t,cvScalar(1.0e6f,0,0,0));
|
cvSet(t,cvScalar(1.0e6f,0,0,0));
|
||||||
cv::dilate(cv::cvarrToMat(mask), cv::cvarrToMat(band), el_cross, cv::Point(1, 1));
|
cv::dilate(cv::cvarrToMat(mask), cv::cvarrToMat(band), el_cross, cv::Point(1, 1));
|
||||||
Heap=cv::makePtr<CvPriorityQueueFloat>();
|
Heap=cv::makePtr<CvPriorityQueueFloat>();
|
||||||
if (!Heap->Init(band))
|
|
||||||
return;
|
|
||||||
cvSub(band,mask,band,NULL);
|
cvSub(band,mask,band,NULL);
|
||||||
SET_BORDER1_C1(band,uchar,0);
|
SET_BORDER1_C1(band,uchar,0);
|
||||||
if (!Heap->Add(band))
|
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);
|
cv::dilate(cv::cvarrToMat(mask), cv::cvarrToMat(out), el_range);
|
||||||
cvSub(out,mask,out,NULL);
|
cvSub(out,mask,out,NULL);
|
||||||
Out=cv::makePtr<CvPriorityQueueFloat>();
|
Out=cv::makePtr<CvPriorityQueueFloat>();
|
||||||
if (!Out->Init(out))
|
|
||||||
return;
|
|
||||||
if (!Out->Add(band))
|
if (!Out->Add(band))
|
||||||
return;
|
return;
|
||||||
cvSub(out,band,out,NULL);
|
cvSub(out,band,out,NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user