opencv的普遍特性中矢量化操作

前情

使用opencv过程中,有需要提高运算速度的需求,毕竟opencv操作其实就是对矩阵的计算。为了有效提高其运算的速率,opencv提供了SIMD指令,即Single Instruction, Multiple Data,允许单条指令对多个数据进行处理。

数据类型

有一些类似于cv::v_uint8等的数据类型,其中v代表其与vector(矢量操作)有关,通常是为了利用SIMD指令集来加速计算。

以下是一些基础的矢量寄存器:

  • cv::v_uint8和cv::v_int8 – 8bit整数型 – char
  • cv::v_uint16和cv::v_int16 – 16bit整数型 – short
  • cv::v_uint32和cv::v_int32 – 32bit整数型 – int
  • cv::v_uint64和cv::v_int64 – 64bit整数型 – int64
  • cv::v_float32: 32-bit浮点型 (signed) - float
  • cv::v_float64: 64-bit 浮点型(signed) - double

实例

举一个使用simd的例子,如以下代码中的v_float32x4 value = v_setzero_f32()所示。

void conv_simd(const Mat& src, Mat& dst, const Mat& kernel) {
    int rows = src.rows, cols = src.cols;
    dst = Mat(rows, cols, CV_8UC1);

    int sz = kernel.rows / 2;
    Mat src_padded;
    // 边缘处理,和卷积核的大小相关,src_padded相当于是src多加了一圈,这样边缘的像素也可以和卷积核进行矩阵运算。
    copyMakeBorder(src, src_padded, sz, sz, sz, sz, BORDER_REPLICATE);
	
	// 遍历
    for (int i = 0; i < rows; i++) {
    	// 目标矩阵
        uchar* dptr = dst.ptr<uchar>(i);
        for (int j = 0; j < cols; j++) {
            v_float32x4 value = v_setzero_f32();
            // 遍历源图像的每一个像素,其中k代表行,l代表列
            for (int k = -sz; k <= sz; k++) {
                const uchar* sptr = src_padded.ptr<uchar>(i + sz + k);
                for (int l = -sz; l <= sz; l++) {
                    float kernel_val = kernel.ptr<float>(k + sz)[l + sz];
                    float src_val = static_cast<float>(sptr[j + sz + l]);
                    value = v_fma(v_setall_f32(kernel_val), v_setall_f32(src_val), value); // FMA
                }
            }
            float sum = v_reduce_sum(value);
            dptr[j] = saturate_cast<uchar>(sum);
        }
    }
}

关键点解释

  • v_float32x4 : 这是 OpenCV 的一个向量类型,用于存储 4 个 32 位浮点数。
  • v_setzero_f32() : 返回一个所有元素均为零的 v_float32x4 向量。
  • v_setall_f32() : 将标量值复制到所有向量元素。
  • v_fma() : 执行乘法和加法操作(Fused Multiply-Add),即 a * b + c。这种操作在许多处理器架构上是硬件加速的,非常适合高效的卷积计算。
  • v_reduce_sum() : 将向量中的所有元素求和,得到一个标量值。
  • saturate_cast() : 将浮点结果转换为 8 位无符号整数,同时确保结果值在 0 到 255 的范围内。
注意事项
  1. 数据对齐: 在使用 SIMD 指令时,数据对齐非常重要。一般来说,数据对齐到向量宽度的倍数(例如 16 字节或 32 字节)可以提高性能。

  2. SIMD 扩展的可用性: 不同的硬件平台支持不同的 SIMD 扩展(如 SSE, AVX, NEON 等)。OpenCV 提供的通用向量类型和函数使得代码在不同平台上具有较好的可移植性。

  3. 边界处理: 在真实应用中,还需要仔细处理图像边界,特别是卷积核大小与图像尺寸不匹配时的情况。这里使用 copyMakeBorder 函数将图像进行了扩展,以便在卷积操作时能够访问完整的邻域。

  4. 指针访问 : 在 OpenCV 中,图像数据以行优先(row-major)的顺序存储在连续的内存块中。ptr(i) 返回的是第 i 行的首地址指针,这样就可以通过该指针来直接访问和操作第 i 行的像素数据。通常情况下,这种直接访问的方式比通过二维数组索引访问要快,因此在处理大图像数据时尤为常用。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值