IT评测·应用市场-qidao123.com技术社区

标题: 双线性插值算法:原理、实现、优化及在图像处理和多范畴中的广泛应用与发展 [打印本页]

作者: 惊落一身雪    时间: 2025-1-14 08:23
标题: 双线性插值算法:原理、实现、优化及在图像处理和多范畴中的广泛应用与发展
一、头文件和命名空间


二、像素类型定义

  1. typedef cv::Point3_<uint8_t> Pixel;
复制代码


三、双线性插值函数 bilinearInterpolation

  1. void bilinearInterpolation(Mat& src, Mat& dst, double sx, double sy) {
  2.     int dst_rows = static_cast<int>(src.rows * sy);
  3.     int dst_cols = static_cast<int>(src.cols * sx);
  4.     dst = Mat::zeros(cv::Size(dst_cols, dst_rows), src.type());
复制代码


  1.     dst.forEach<Pixel>([&](Pixel &p, const int * position) -> void {
  2.         int row = position[0];
  3.         int col = position[1];
复制代码


  1.         double before_x = double(col + 0.5) / sx - 0.5f;
  2.         double before_y = double(row + 0.5) / sy - 0.5;
  3.         int top_y = static_cast<int>(before_y);
  4.         int bottom_y = top_y + 1;
  5.         int left_x = static_cast<int>(before_x);
  6.         int right_x = left_x + 1;
复制代码


  1.         double u = before_x - left_x;
  2.         double v = before_y - top_y;
复制代码


  1.         if ((top_y >= src.rows - 1) && (left_x >= src.cols - 1)) {//右下角
  2.             for (size_t k = 0; k < src.channels(); k++) {
  3.                 dst.at<Vec3b>(row, col)[k] = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k];
  4.             }
  5.         } else if (top_y >= src.rows - 1) { //最后一行
  6.             for (size_t k = 0; k < src.channels(); k++) {
  7.                 dst.at<Vec3b>(row, col)[k]
  8.                         = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]
  9.                           + (1. - v) * u * src.at<Vec3b>(top_y, right_x)[k];
  10.             }
  11.         } else if (left_x >= src.cols - 1) {//最后一列
  12.             for (size_t k = 0; k < src.channels(); k++) {
  13.                 dst.at<Vec3b>(row, col)[k]
  14.                         = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]
  15.                           + (v) * (1. - u) * src.at<Vec3b>(bottom_y, left_x)[k];
  16.             }
  17.         } else {
  18.             for (size_t k = 0; k < src.channels(); k++) {
  19.                 dst.at<Vec3b>(row, col)[k]
  20.                         = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]
  21.                           + (1. - v) * (u) * src.at<Vec3b>(top_y, right_x)[k]
  22.                           + (v) * (1. - u) * src.at<Vec3b>(bottom_y, left_x)[k]
  23.                           + (u) * (v) * src.at<Vec3b>(bottom_y, right_x)[k];
  24.             }
  25.         }
  26.     });
  27. }
复制代码


四、主函数 main
  1. int main() {
  2.     Mat src = imread(".../grass.jpg");
  3.     imshow("src", src);
复制代码


  1.     double sx = 1.5;
  2.     double sy = 1.5;
  3.     Mat dst;
  4.     bilinearInterpolation(src,dst, sx, sy);
复制代码


  1.     imshow("dst", dst);
  2.     waitKey(0);
  3.     return 0;
  4. }
复制代码


延申部门

一、双线性插值的数学原理

双线性插值是一种二维插值方法,其核心思想是根据目标图像中的像素位置,在源图像中找到其对应的位置,并根据该位置周围的四个相邻像素进行加权平均盘算。假设我们在源图像中要找到位置 (x, y) 的像素值,而 x 和 y 是浮点数,其相邻的四个像素为 (x1, y1)、(x1, y2)、(x2, y1) 和 (x2, y2),其中 x1 = floor(x)、x2 = ceil(x)、y1 = floor(y)、y2 = ceil(y),并且 u = x - x1、v = y - y1。对于单通道图像,双线性插值公式为:
 
对于多通道图像(如 RGB),必要对每个通道分别进行上述盘算。这种插值方法的优点是盘算简单,结果相对平滑,能够在一定程度上保持图像的连续性,避免了近来邻插值产生的锯齿状结果。它在图像缩放、旋转、仿射变换等操作中广泛使用,由于这些操作通常会导致像素位置从整数坐标变为浮点数坐标,必要根据周围像素来估计新的像素值。
二、性能优化


  1. #include <opencv2/opencv.hpp>#include <opencv2/core/parallel.hpp>using namespace cv;using namespace std;typedef cv::Point3_<uint8_t> Pixel;
  2. class BilinearInterpolationBody : public cv::ParallelLoopBody {private:    Mat& src;    Mat& dst;    double sx;    double sy;public:    BilinearInterpolationBody(Mat& _src, Mat& _dst, double _sx, double _sy) : src(_src), dst(_dst), sx(_sx), sy(_sy) {}    void operator()(const cv::Range& range) const override {        for (int r = range.start; r < range.end; ++r) {            int row = r / dst.cols;            int col = r % dst.cols;            double before_x = double(col + 0.5) / sx - 0.5f;            double before_y = double(row + 0.5) / sy - 0.5;            int top_y = static_cast<int>(before_y);            int bottom_y = top_y + 1;            int left_x = static_cast<int>(before_x);            int right_x = left_x + 1;            double u = before_x - left_x;            double v = before_y - top_y;            if ((top_y >= src.rows - 1) && (left_x >= src.cols - 1)) {                for (size_t k = 0; k < src.channels(); k++) {                    dst.at<Vec3b>(row, col)[k] = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k];                }            } else if (top_y >= src.rows - 1) {                for (size_t k = 0; k < src.channels(); k++) {                    dst.at<Vec3b>(row, col)[k]                            = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]                              + (1. - v) * u * src.at<Vec3b>(top_y, right_x)[k];                }            } else if (left_x >= src.cols - 1) {                for (size_t k = 0; k < src.channels(); k++) {                    dst.at<Vec3b>(row, col)[k]                            = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]                              + (v) * (1. - u) * src.at<Vec3b>(bottom_y, left_x)[k];                }            } else {                for (size_t k = 0; k < src.channels(); k++) {                    dst.at<Vec3b>(row, col)[k]                            = (1. - u) * (1. - v) * src.at<Vec3b>(top_y, left_x)[k]                              + (1. - v) * (u) * src.at<Vec3b>(top_y, right_x)[k]                              + (v) * (1. - u) * src.at<Vec3b>(bottom_y, left_x)[k]                              + (u) * (v) * src.at<Vec3b>(bottom_y, right_x)[k];                }            }        }    }};// 双线性插值算法void bilinearInterpolation(Mat& src, Mat& dst, double sx, double sy) {
  3.     int dst_rows = static_cast<int>(src.rows * sy);
  4.     int dst_cols = static_cast<int>(src.cols * sx);
  5.     dst = Mat::zeros(cv::Size(dst_cols, dst_rows), src.type());
  6.     BilinearInterpolationBody body(src, dst, sx, sy);    cv::parallel_for_(cv::Range(0, dst.rows * dst.cols), body);}int main() {
  7.     Mat src = imread(".../grass.jpg");
  8.     imshow("src", src);
  9.     double sx = 1.5;
  10.     double sy = 1.5;
  11.     Mat dst;
  12.     bilinearInterpolation(src,dst, sx, sy);
  13.     imshow("dst", dst);
  14.     waitKey(0);
  15.     return 0;
  16. }
复制代码

在上述代码中,我们创建了一个 BilinearInterpolationBody 类,它继承自 cv:arallelLoopBody,并重写了 operator() 方法。然后使用 cv::parallel_for_ 函数并行实行 BilinearInterpolationBody 对象的 operator() 方法,将图像分成多个地区,每个线程处理一个地区,提高了处理速率。

  1. #include <opencv2/opencv.hpp>using namespace cv;using namespace std;int main() {
  2.     Mat src = imread(".../grass.jpg");
  3.     imshow("src", src);
  4.     double sx = 1.5;    double sy = 1.5;    Mat dst;    resize(src, dst, Size(), sx, sy, INTER_LINEAR);    imshow("dst", dst);
  5.     waitKey(0);
  6.     return 0;
  7. }
复制代码

resize 函数中的 INTER_LINEAR 参数表示使用双线性插值。使用内置函数可以使代码更加简洁,同时利用了 OpenCV 的优化,性能可能更好。
三、应用场景和局限性


四、与其他插值方法的比较


  1. #include <opencv2/opencv.hpp>using namespace cv;using namespace std;// 近来邻插值算法void nearestNeighborInterpolation(Mat& src, Mat& dst, double sx, double sy) {    int dst_rows = static_cast<int>(src.rows * sy);    int dst_cols = static_cast<int>(src.cols * sx);    dst = Mat::zeros(cv::Size(dst_cols, dst_rows), src.type());    for (int row = 0; row < dst.rows; ++row) {        for (int col = 0; col < dst.cols; ++col) {            int src_row = static_cast<int>(row / sy);            int src_col = static_cast<int>(col / sx);            if (src_row >= src.rows) src_row = src.rows - 1;            if (src_col >= src.cols) src_col = src.cols - 1;            for (size_t k = 0; k < src.channels(); ++k) {                dst.at<Vec3b>(row, col)[k] = src.at<Vec3b>(src_row, src_col)[k];            }        }    }}int main() {
  2.     Mat src = imread(".../grass.jpg");
  3.     imshow("src", src);
  4.     double sx = 1.5;    double sy = 1.5;    Mat dst;    nearestNeighborInterpolation(src, dst, sx, sy);    imshow("dst", dst);
  5.     waitKey(0);
  6.     return 0;
  7. }
复制代码

近来邻插值的优点是盘算速率快,但会导致图像产生锯齿状结果,由于它没有思量周围像素的信息,只是简单复制近来像素的值。

  1. #include <opencv2/opencv.hpp>using namespace cv;using namespace std;int main() {
  2.     Mat src = imread(".../grass.jpg");
  3.     imshow("src", src);
  4.     double sx = 1.5;    double sy = 1.5;    Mat dst;    resize(src, dst, Size(), sx, sy, INTER_CUBIC);    imshow("dst", dst);
  5.     waitKey(0);
  6.     return 0;
  7. }
复制代码
双三次插值通常比双线性插值产生更好的结果,尤其是对于图像的放大操作。它的原理是使用一个三次多项式函数对源图像中周围 16 个像素进行加权盘算,以得到目标像素的值。该多项式函数的计划思量了像素之间的距离和梯度信息,使得天生的图像更加平滑,细节更加丰富,但相应的盘算本钱也更高。



 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4