ppl.cv库的github链接:https://github.com/OpenPPL/ppl.cv
下面几段介绍翻译自该项目的readme文件:
ppl.cv 源于商汤科技不同团队对图像处理的需求,提供了一套常用图像算法的高性能实现,用于不同的深度学习应用的流水线中。
它是一个轻量级的、可定制的图像处理框架。由于 OpenCV 等框架的实现体积大、依赖性复杂,我们希望提供一个灵活的框架,在开发和/或部署深度学习套件时,只需添加所需的算法即可满足各种图像处理应用。使用 ppl.cv,开发人员可以轻松添加新平台以支持新硬件和/或新图像处理算法的新实现以支持新应用程序,用户可以为指定的平台和算法选择图像处理实现,从而生成小巧紧凑的图像处理库以供部署。
它是一个用于深度学习的高性能图像处理库。我们已经在这个框架中填充了多个支持平台和大量精心挑选的函数,以实现深度学习流水线中的图像处理。由于视觉深度学习涉及海量图像,因此每个函数都针对硬件进行了优化,以获得良好的加速比。为了方便使用 ppl.cv,每个函数在函数文档中都提供了一个简短的调用示例、一个用于检查与 OpenCV 中对应函数一致性的单元测试和一个用于与 OpenCV 中对应函数性能进行比较的基准测试,此外还提供了一个实现。
X86
先用opencv来读入图像:
#include <ppl/cv/x86/boxfilter.h>
#include <opencv2/opencv.hpp>
int main(int argc, char* argv)
{
cv::Mat image = cv::imread("bus.jpg");
const int32_t W = image.cols;
const int32_t H = image.rows;
const int32_t C = image.channels();
const int32_t kernelx_len = 3;
const int32_t kernely_len = 3;
bool normalize = 1;
uint8_t* dev_iImage = image.data;
uint8_t* dev_oImage = (uint8_t*)malloc(W * H * C * sizeof(uint8_t));
ppl::cv::x86::BoxFilter<uint8_t, 3>(H, W, W * C, dev_iImage, kernelx_len, kernely_len, normalize, W * C, dev_oImage, ppl::cv::BORDER_DEFAULT);
cv::Mat image_out = image.clone();
std::copy(dev_oImage, dev_oImage + W * H * C, image_out.data);
cv::imwrite("result.jpg", image_out);
free(dev_oImage);
return 0;
}
改用更轻量的stb_image读取图像:
#include <ppl/cv/x86/boxfilter.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include <iostream>
#include <ctime>
int main(int argc, char*argv)
{
int iw, ih, ic;
uint8_t* dev_iImage = stbi_load("bus.jpg", &iw, &ih, &ic, 0);
const int32_t W = iw;
const int32_t H = ih;
const int32_t C = ic;
const int32_t kernelx_len = 3;
const int32_t kernely_len = 3;
bool normalize = 1;
uint8_t* dev_oImage = (uint8_t*)malloc(W * H * C * sizeof(uint8_t));
clock_t start = clock();
for (size_t i = 0; i < 10000; i++)
{
ppl::cv::x86::BoxFilter<uint8_t, 3>(H, W, W * C, dev_iImage, kernelx_len, kernely_len, normalize, W * C, dev_oImage, ppl::cv::BORDER_DEFAULT);
}
clock_t end = clock();
std::cout << end - start << std::endl;
stbi_write_png("out.png", W, H, C, dev_oImage, 0);
free(dev_iImage);
free(dev_oImage);
return 0;
}
在i7-13700F CPU上测试单线程运行需要33s多。
CUDA
#include <ppl/cv/cuda/boxfilter.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include <iostream>
#include <ctime>
#include <cuda_runtime.h>
int main(int argc, char* argv)
{
int iw, ih, ic;
uint8_t* dev_iImage = stbi_load("bus.jpg", &iw, &ih, &ic, 0);
const int32_t W = iw;
const int32_t H = ih;
const int32_t C = ic;
const int32_t kernelx_len = 3;
const int32_t kernely_len = 3;
bool normalize = 1;
uint8_t* dev_oImage = (uint8_t*)malloc(W * H * C * sizeof(uint8_t));
uint8_t* gpu_input;
uint8_t* gpu_output;
cudaMalloc((void**)&gpu_input, W * H * C * sizeof(uint8_t));
cudaMalloc((void**)&gpu_output, W * H * C * sizeof(uint8_t));
clock_t start = clock();
for (size_t i = 0; i < 10000; i++)
{
cudaMemcpy(gpu_input, dev_iImage, W * H * C * sizeof(uint8_t), cudaMemcpyHostToDevice);
cudaStream_t stream;
cudaStreamCreate(&stream);
ppl::cv::cuda::BoxFilter<uint8_t, 3>(stream, H, W, W * C, gpu_input, kernelx_len, kernelx_len, normalize, W * C, gpu_output, ppl::cv::BORDER_DEFAULT);
cudaStreamSynchronize(stream);
cudaStreamDestroy(stream);
cudaMemcpy(dev_oImage, gpu_output, W * H * C * sizeof(uint8_t), cudaMemcpyDeviceToHost);
}
clock_t end = clock();
std::cout << end - start << std::endl;
stbi_write_png("out.png", W, H, C, dev_oImage, 0);
free(dev_iImage);
free(dev_oImage);
return 0;
}
在RTX4070 GPU上测试运行需要5s多。
原图:
结果: