OpenCV性能优化实战
学习目标
本课程将深入探讨如何通过多线程和NPU加速等技术优化OpenCV程序的性能,提高图像处理的速度和效率。通过本课程的学习,学员将能够掌握提升OpenCV应用性能的关键技术,使图像处理程序更加高效。
相关知识点
- OpenCV性能优化
学习内容
1 OpenCV性能优化
1.1 多线程在OpenCV中的应用
在图像处理领域,多线程技术可以显著提高程序的执行效率,尤其是在处理大量图像数据时。OpenCV提供了多种方法来实现多线程处理,包括使用C++标准库中的std::thread
和OpenCV自身的并行处理框架。
多线程技术的基本原理是将一个任务分解成多个子任务,每个子任务由一个独立的线程执行。这样可以充分利用多核处理器的计算能力,提高程序的执行效率。在OpenCV中,可以使用std::thread
来创建和管理线程,也可以使用OpenCV的并行处理框架cv::parallel_for_
来简化多线程编程。
下面是一个使用std::thread
处理图像的简单示例:
#include <opencv2/opencv.hpp>
#include <thread>
#include <vector>
void processImage(const cv::Mat& img, cv::Mat& result, int startRow, int endRow) {
for (int i = startRow; i < endRow; ++i) {
for (int j = 0; j < img.cols; ++j) {
result.at<uchar>(i, j) = 255 - img.at<uchar>(i, j); // 简单的图像反转
}
}
}
int main() {
cv::Mat img = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);
if (img.empty()) {
std::cerr << "Could not open or find the image" << std::endl;
return -1;
}
cv::Mat result = cv::Mat::zeros(img.size(), img.type());
int numThreads = 4;
std::vector<std::thread> threads;
int rowsPerThread = img.rows / numThreads;
for (int i = 0; i < numThreads; ++i) {
int startRow = i * rowsPerThread;
int endRow = (i == numThreads - 1) ? img.rows : (i + 1) * rowsPerThread;
threads.emplace_back(processImage, std::ref(img), std::ref(result), startRow, endRow);
}
for (auto& t : threads) {
t.join();
}
cv::imwrite("output.jpg", result);
return 0;
}
input.jpg:
将以上代码复制粘贴到main2.cpp中,然后运行(需要提前安装好g++/opencv等)
g++ main2.cpp -o mycpp2 `pkg-config --cflags --libs opencv4`
output.jpg:
1.2 NPU加速技术
昇腾NPU(神经网络处理单元)在图像处理和计算机视觉任务中具有显著优势,其架构专为深度学习和大规模并行计算设计,能够高效处理大量数据。OpenCV可通过与昇腾NPU相关的接口和框架来利用NPU加速图像处理。
与GPU类似,NPU加速的原理是将计算任务从CPU转移到NPU,借助NPU的并行计算能力加速处理。但NPU在架构和优化方向上更贴合深度学习等特定计算任务,能更高效地执行与图像处理相关的深度学习模型推理等操作。
昇腾NPU(神经网络处理单元)是专为深度学习和大规模并行计算设计的硬件加速器。其架构特点包括:
- 高并行性:NPU拥有大量的计算核心,能够同时处理多个数据流,显著提高计算效率。
- 专用指令集:NPU针对深度学习任务优化了指令集,能够更高效地执行卷积、矩阵运算等操作。
- 内存优化:NPU配备了高带宽内存(HBM),减少了数据传输的延迟和能耗。
OpenCV 当前支持 20+ 昇腾算子,此处根据图像处理应用场景,选取 add, rotate 和 flip 算子的应用作示例代码。
#include <iostream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/cann.hpp>
#include <opencv2/cann_interface.hpp>
int main(int argc, char* argv[])
{
cv::CommandLineParser parser(argc, argv,
"{@input|puppy.png|path to input image}"
"{@output|output.png|path to output image}"
"{help||show help}");
parser.about("This is a sample for image processing with Ascend NPU. \n");
if (argc != 3 || parser.has("help"))
{
parser.printMessage();
return 0;
}
std::string imagePath = parser.get<std::string>(0);
std::string outputPath = parser.get<std::string>(1);
// 读取输入图像
cv::Mat img = cv::imread(imagePath);
// 生成高斯噪声
cv::Mat gaussNoise(img.rows, img.cols, img.type());
cv::RNG rng;
rng.fill(gaussNoise, cv::RNG::NORMAL, 0, 25);
// cann 初始化及指定设备
cv::cann::initAcl();
cv::cann::setDevice(0);
cv::Mat output;
// 添加高斯噪声到输入图像
cv::cann::add(img, gaussNoise, output);
// 旋转图像 (0, 1, 2, 分别代表旋转 90°, 180°, 270°)
cv::cann::rotate(output, output, 0);
// 翻转图像 (0, 正数, 负数, 分别代表沿 x, y, x 和 y 轴进行翻转)
cv::cann::flip(output, output, 0);
// 写入输出图像
cv::imwrite(outputPath, output);
// cann 去初始化
cv::cann::resetDevice();
cv::cann::finalizeAcl();
return 0;
}
这个需要NPU硬件支持,有条件的去试试。
1.3 OpenCV性能优化技巧
除了多线程和NPU加速,还有一些其他的技术和技巧可以用来优化OpenCV程序的性能,例如使用更高效的算法、减少不必要的内存拷贝、使用预分配内存等。
- 选择高效的算法:不同的算法在性能上可能有显著差异。例如,使用快速傅里叶变换(FFT)进行图像卷积通常比直接卷积更快。
- 减少内存拷贝:频繁的内存拷贝会显著降低程序的性能。尽量使用引用或指针来传递数据,避免不必要的拷贝。
- 预分配内存:在处理大量数据时,动态分配内存可能会导致性能瓶颈。预分配足够的内存可以避免频繁的内存分配和释放。
下面是一个减少内存拷贝的示例:
#include <opencv2/opencv.hpp>
void processImage(cv::Mat& img) {
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::Mat blurred;
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
cv::Mat edges;
cv::Canny(blurred, edges, 50, 150);
if (img.channels() == 3) {
cv::cvtColor(edges, img, cv::COLOR_GRAY2BGR);
} else {
img = edges.clone();
}
}
int main() {
cv::Mat img = cv::imread("input.jpg");
if (img.empty()) {
std::cerr << "Could not open or find the image" << std::endl;
return -1;
}
processImage(img);
cv::imwrite("output.jpg", img);
return 0;
}
瞬间执行完: