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);
相关推荐
wabs6661 小时前
关于二叉树【力扣107.二叉树的层序遍历II的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
千里码aicood1 小时前
flask基于opencv绘图机器人的系统设定(opencv)
opencv·机器人·flask
galaxy_strive1 小时前
Qt C++插件化编写项目(1)
开发语言·c++·qt
见叶之秋1 小时前
【C++】C++ 核心进阶(一):泛型编程基石 —— 模板初阶与 STL 体系开篇
开发语言·c++
一木 之林2 小时前
四、STL 容器与数据结构(进阶)(二)
数据结构·c++·哈希算法
会周易的程序员2 小时前
5Draft使用说明书
服务器·c++·分布式·raft·共识
码匠许师傅2 小时前
【设计模式精讲】21.迭代器模式(Iterator)
c++·设计模式·rpc·迭代器模式·软件工程·uml
YYYing.2 小时前
【C++进阶系列 (一)】关于线程堆栈的那些事 (上篇)
c语言·开发语言·c++·线程堆栈
努力努力再努力wz2 小时前
【Redis入门系列】:从 RESP 协议到 redis-plus-plus:Redis 客户端编程与 C++ 接口设计
开发语言·数据库·c++·redis·分布式·缓存·架构