OpenCV的resize函数优化

背景

在使用OpenCV做图像处理的时候,最常见的问题是c++版本性能不足,以resize函数为例来说明,将size为[864,1323,3]的函数缩小一半:

Mat img0;
    gettimeofday(&t4, NULL);
    cv::resize(source, img0, cv::Size(cols_out,rows_out));
    gettimeofday(&t5, NULL);
    time = 1000*(t5.tv_sec - t4.tv_sec) + (double)(t5.tv_usec - t4.tv_usec)/1000;
    printf("frank cv::resize = %lf\n", time);
    
output:
frank cv::resize = 13.569000(ms) on Macmini(2.6 GHz Intel Core i5)

需要13ms才能完成这个操作,对于一半应用的实时性要求,要再33ms内处理一帧,超过1/3的时间花在resize上,是非常不合理的。

优化1

针对上面这种1/2大小的resize,可以这样来思考,既然我已经知道输出mat的长宽是输入mat的一半,那么输出mat的每个像素值都是从原图的4个像素得到的一个映射,事实上OpenCV也是用类似的技巧,从原图的点映射到输出图像的像素点,这个映射可以是Nearest Neiborhood,也可以是双线性插值、平均、取左上角等等。对于一般自然图像,我们可以直接使用NN映射来获得输出图像的点。

使用上面的方法:

    double time = 0.0;
    gettimeofday(&t1, NULL);
    Mat half_img = resizeByHalf(source, &leftTopInt4);
    int wh = half_img.cols;
    int hh = half_img.rows;
    printf("half_img = %d, %d\n", hh, wh);
    gettimeofday(&t2, NULL);
    time = 1000*(t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000;
    printf("frank resizeByHalf = %lf\n", time);
    

output:frank resizeByHalf = 10.539000 

时间相比OpenCV的版本,缩小了3ms

优化2 进一步借鉴最邻近算法

如果我们不是resize到1/2等特定的长宽,而且任意尺寸呢?上面的方法就不能直接使用,但是这个思想是可以借鉴的,我们可以进一步将最临近算法发挥的更好,对outuput的每个点,先根据长宽比计算其在原图中最邻近的像素点,然后直接根据最邻近的思想,直接拷贝Channel个字节作为输出图像:

Mat new_img = Mat(rows_out, cols_out, CV_8UC3);
    resizeByNN(source.data, new_img.data, source.rows, source.cols, source.channels(), new_img.rows, new_img.cols);
    gettimeofday(&t4, NULL);
    time = 1000*(t4.tv_sec - t3.tv_sec) + (double)(t4.tv_usec - t3.tv_usec)/1000;
    printf("frank resizeByNN = %lf\n", time);
    
    
output: frank resizeByNN = 6.738000

时间已经缩小到原先的一半!

优化3

还有一个杀招,待日后更新。。。。

原图:
在这里插入图片描述

resizeByNN的结果:
在这里插入图片描述

上面两个函数的源码

resizeByHalf

uchar maxInt4(uchar x0, uchar x1, uchar x2, uchar x3){
    uchar t0 = (x0 >= x1) ? x0 : x1;
    uchar t1 = (x2 >= x3) ? x2 : x3;
    return (t0 >= t1) ? t0 : t1;
}

uchar meanInt4(uchar x0, uchar x1, uchar x2, uchar x3){
    float t = (x0 + x1 + x2 + x3)/4.0;
    return uchar(round(t));  //四舍五入
}

uchar leftTopInt4(uchar x0, uchar x1, uchar x2, uchar x3){
    return x0;
}

Mat resizeByHalf(Mat input, uchar (*pfun)(uchar x0, uchar x1, uchar x2, uchar x3)){
    Mat output;
    int h_in = input.rows;
    int w_in = input.cols;
    int c = input.channels();
    
    int h_out = h_in/2;
    int w_out = w_in/2;
    output.create(h_out, w_out, CV_8UC(c));
    
    uchar *data_source = input.data;
    uchar *data_half = output.data;
    
    int bpl_source = w_in*3;
    int bpl_half = w_out*3;
    
    for (int i = 0; i < h_out; i++) {
        for (int j = 0; j < w_out; j++) {
            uchar *sr = data_source + i*2*bpl_source + j*2*c;
            uchar *hr = data_half + i*bpl_half + j*c;
            for (int k = 0; k < c; k++) {
                *(hr+k) = pfun(*(sr+k), *(sr+k+c), *(sr+k+bpl_source), *(sr+k+c+bpl_source));
            }
        }
    }
    
    return output;
}

resizeByNN

void resizeByNN(uchar *input, uchar *output, int height_in, int width_in, int channels, int height_out, int width_out){
    
    uchar *data_source = input;
    uchar *data_half = output;
    
    int bpl_source = width_in*3;
    int bpl_dst = width_out*3;
    
    int pos = 0;
    int sep = 0;
    uchar *sr = nullptr;
    uchar *hr = nullptr;
    float step = 0.0;
    float step_x = float(width_in)/float(width_out);
    float step_y = float(height_in)/float(height_out);
    
    for (int i = 0; i < height_out; i++) {
        for (int j = 0; j < width_out; j++) {
            sep = int(step_y*i);
            step = int(j*step_x);
            sr = data_source + sep*bpl_source;
            hr = data_half + i*bpl_dst +j*channels;
            pos = step*channels;
            memcpy(hr, sr+pos, channels);
        }
    }
    return;
}

多谢阅读!

扫描二维码关注公众号,回复: 10686583 查看本文章
发布了42 篇原创文章 · 获赞 33 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/gaussrieman123/article/details/100153994