C++ 实现 替代 OpenCV resize INTER_LINEAR 的一种方式

C++ 实现 替代 OpenCV resize INTER_LINEAR 的一种方式

flyfish

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <cmath>
#include <vector>
#include <algorithm>

// 如果编译器支持 OpenMP,取消下面注释可多核加速
// #include <omp.h>

static inline float pil_triangle(float x)
{
    x = std::abs(x);
    return (x < 1.0f) ? (1.0f - x) : 0.0f;
}

/**
 * 近似匹配 PIL Image.BILINEAR 的可分离实现
 * 支持 CV_8UC1 / CV_8UC3
 * 缩小自动扩大滤波窗口(具备基本 anti-aliasing)
 */
cv::Mat pilBilinearResizeOptimized(const cv::Mat& src, int dst_w, int dst_h)
{
    CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3);
    CV_Assert(dst_w > 0 && dst_h > 0);

    const int src_h = src.rows;
    const int src_w = src.cols;
    const int channels = src.channels();

    if (src_w == dst_w && src_h == dst_h)
        return src.clone();

    // ========== 1. 水平方向滤波 (src -> temp) ==========
    // 中间结果使用浮点,避免精度截断
    cv::Mat temp(src_h, dst_w, CV_32FC(channels));

    float scale_x = static_cast<float>(src_w) / dst_w;
    float support_x = std::max(1.0f, scale_x);

    // #pragma omp parallel for
    for (int y = 0; y < src_h; ++y)
    {
        const uchar* src_row = src.ptr<uchar>(y);
        float* temp_row = temp.ptr<float>(y);

        for (int dx = 0; dx < dst_w; ++dx)
        {
            float center = (dx + 0.5f) * scale_x - 0.5f;

            int x_min = std::max(0, static_cast<int>(std::floor(center - support_x + 1e-5f)));
            int x_max = std::min(src_w - 1, static_cast<int>(std::ceil(center + support_x - 1e-5f)));

            float sum[4] = {0.f, 0.f, 0.f, 0.f};
            float w_sum = 0.f;

            for (int sx = x_min; sx <= x_max; ++sx)
            {
                float w = pil_triangle((sx - center) / support_x);
                if (w <= 0.f) continue;

                const uchar* px = src_row + sx * channels;
                for (int c = 0; c < channels; ++c)
                    sum[c] += px[c] * w;
                w_sum += w;
            }

            float* out = temp_row + dx * channels;
            if (w_sum > 1e-6f)
            {
                for (int c = 0; c < channels; ++c)
                    out[c] = sum[c] / w_sum;
            }
            else
            {
                // 极端情况兜底
                int nearest = std::min(src_w - 1, std::max(0, static_cast<int>(std::round(center))));
                const uchar* px = src_row + nearest * channels;
                for (int c = 0; c < channels; ++c)
                    out[c] = static_cast<float>(px[c]);
            }
        }
    }

    // ========== 2. 垂直方向滤波 (temp -> dst) ==========
    cv::Mat dst(dst_h, dst_w, src.type());

    float scale_y = static_cast<float>(src_h) / dst_h;
    float support_y = std::max(1.0f, scale_y);

    // #pragma omp parallel for
    for (int dy = 0; dy < dst_h; ++dy)
    {
        float center = (dy + 0.5f) * scale_y - 0.5f;

        int y_min = std::max(0, static_cast<int>(std::floor(center - support_y + 1e-5f)));
        int y_max = std::min(src_h - 1, static_cast<int>(std::ceil(center + support_y - 1e-5f)));

        int win_size = y_max - y_min + 1;
        std::vector<float> wy(win_size);
        float wy_sum = 0.f;

        // 预计算垂直权重,并只累加有效权重
        for (int sy = y_min, idx = 0; sy <= y_max; ++sy, ++idx)
        {
            float w = pil_triangle((sy - center) / support_y);
            wy[idx] = w;
            if (w > 0.f)
                wy_sum += w;
        }

        uchar* dst_row = dst.ptr<uchar>(dy);

        for (int dx = 0; dx < dst_w; ++dx)
        {
            float sum[4] = {0.f, 0.f, 0.f, 0.f};

            for (int sy = y_min, idx = 0; sy <= y_max; ++sy, ++idx)
            {
                float w = wy[idx];
                if (w <= 0.f) continue;

                const float* px = temp.ptr<float>(sy) + dx * channels;
                for (int c = 0; c < channels; ++c)
                    sum[c] += px[c] * w;
            }

            uchar* out = dst_row + dx * channels;

            if (wy_sum > 1e-6f)
            {
                for (int c = 0; c < channels; ++c)
                    out[c] = static_cast<uchar>(std::round(sum[c] / wy_sum));
            }
            else
            {
                // 极端情况兜底
                int nearest = std::min(src_h - 1, std::max(0, static_cast<int>(std::round(center))));
                const float* px = temp.ptr<float>(nearest) + dx * channels;
                for (int c = 0; c < channels; ++c)
                    out[c] = static_cast<uchar>(std::round(px[c]));
            }
        }
    }

    return dst;
}

使用方式原来的 resize

在 preprocess 里把:

cpp 复制代码
cv::resize(img, scaled, cv::Size(new_w, new_h), 0, 0, cv::INTER_LINEAR);

改成:

cpp 复制代码
cv::Mat scaled = pilBilinearResizeOptimized(img, new_w, new_h);
相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
weilx12344 天前
C++笔记-文件IO-<fcntl.h>
c++
AI的探索之旅4 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
Smileyqp沛沛4 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·4 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁4 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
词却4 天前
OpenCV学习:CNN 人脸检测
opencv
another heaven4 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法