图像双三次插值的原理,就是目标图像的每一个像素都是由原图上相对应点周围的4x4=16个像素经过加权之后再相加得到的。本文主要介绍了通过C++OpenCV实现图像双三次插值算法,需要的可以参考一下
前言
近期在学习一些传统的图像处理算法,比如传统的图像插值算法等。传统的图像插值算法包括邻近插值法、双线性插值法和双三次插值法,其中邻近插值法和双线性插值法在网上都有很详细的介绍以及用c++编写的代码。但是,网上关于双三次插值法的原理介绍虽然很多,也有对应的代码,但是大多都不是很详细。因此基于自己对原理的理解,自己编写了图像双三次插值算法的c++ opencv代码,在这里记录一下。
一、图像双三次插值算法原理
首先是原理部分。图像双三次插值的原理,就是目标图像的每一个像素都是由原图上相对应点周围的4x4=16个像素经过加权之后再相加得到的。这里的加权用到的就是三次函数,这也是图像双三次插值算法名称的由来(个人猜测)。用到的三次函数如下图所示:
二、C++ OpenCV代码
1.计算权重矩阵
前面说了权重矩阵就是横坐标的4个输出和纵坐标的4个输出相乘,因此只需要求出横坐标和纵坐标相对应的8个输出值就行了。
代码如下:
std::vector<double> getWeight(double c, double a = 0.5)
{
//c就是u和v,横坐标和纵坐标的输出计算方式一样
std::vector<double> temp(4);
temp[0] = 1 + c; temp[1] = c;
temp[2] = 1 - c; temp[3] = 2 - c;
//y(x) = (a+2)|x|*|x|*|x| - (a+3)|x|*|x| + 1 |x|<=1
//y(x) = a|x|*|x|*|x| - 5a|x|*|x| + 8a|x| - 4a 1<|x|<2
std::vector<double> weight(4);
weight[0] = (a * pow(abs(temp[0]), 3) - 5 * a * pow(abs(temp[0]), 2) + 8 * a * abs(temp[0]) - 4 * a);
weight[1] = (a + 2) * pow(abs(temp[1]), 3) - (a + 3) * pow(abs(temp[1]), 2) + 1;
weight[2] = (a + 2) * pow(abs(temp[2]), 3) - (a + 3) * pow(abs(temp[2]), 2) + 1;
weight[3] = (a * pow(abs(temp[3]), 3) - 5 * a * pow(abs(temp[3]), 2) + 8 * a * abs(temp[3]) - 4 * a);
return weight;
}
2.遍历插值
代码如下:
void bicubic(cv::Mat& src, cv::Mat& dst, int dst_rows, int dst_cols)
{
dst.create(dst_rows, dst_cols, src.type());
double sy = static_cast<double>(dst_rows) / static_cast<double>(src.rows);
double sx = static_cast<double>(dst_cols) / static_cast<double>(src.cols);
cv::Mat border;
cv::copyMakeBorder(src, border, 1, 1, 1, 1, cv::BORDER_REFLECT_101);
//处理灰度图
if (src.channels() == 1)
{
for (int i = 1; i < dst_rows + 1; ++i)
{
int src_y = (i + 0.5) / sy - 0.5; //做了几何中心对齐
if (src_y < 0) src_y = 0;
if (src_y > src.rows - 1) src_y = src.rows - 1;
src_y += 1;
//目标图像点坐标对应原图点坐标的4个纵坐标
int i1 = std::floor(src_y);
int i2 = std::ceil(src_y);
int i0 = i1 - 1;
int i3 = i2 + 1;
double u = src_y - static_cast<int64>(i1);
std::vector<double> weight_x = getWeight(u);
for (int j = 1; j < dst_cols + 1; ++j)
{
int src_x = (j + 0.5) / sy - 0.5;
if (src_x < 0) src_x = 0;
if (src_x > src.rows - 1) src_x = src.rows - 1;
src_x += 1;
//目标图像点坐标对应原图点坐标的4个横坐标
int j1 = std::floor(src_x);
int j2 = std::ceil(src_x);
int j0 = j1 - 1;
int j3 = j2 + 1;
double v = src_x - static_cast<int64>(j1);
std::vector<double> weight_y = getWeight(v);
//目标点像素对应原图点像素周围4x4区域的加权计算(插值)
double pix = weight_x[0] * weight_y[0] * border.at<uchar>(i0, j0) + weight_x[1] * weight_y[0] * border.at<uchar>(i0, j1)
+ weight_x[2] * weight_y[0] * border.at<uchar>(i0, j2) + weight_x[3] * weight_y[0] * border.at<uchar>(i0, j3)
+ weight_x[0] * weight_y[1] * border.at<uchar>(i1, j0) + weight_x[1] * weight_y[1] * border.at<uchar>(i1, j1)
+ weight_x[2] * weight_y[1] * border.at<uchar>(i1, j2) + weight_x[3] * weight_y[1] * border.at<uchar>(i1, j3)
+ weight_x[0] * weight_y[2] * border.at<uchar>(i2, j0) + weight_x[1] * weight_y[2] * border.at<uchar>(i2, j1)
+ weight_x[2] * weight_y[2] * border.at<uchar>(i2, j2) + weight_x[3] * weight_y[2] * border.at<uchar>(i2, j3)
+ weight_x[0] * weight_y[3] * border.at<uchar>(i3, j0) + weight_x[1] * weight_y[3] * border.at<uchar>(i3, j1)
+ weight_x[2] * weight_y[3] * border.at<uchar>(i3, j2) + weight_x[3] * weight_y[3] * border.at<uchar>(i3, j3);
if (pix < 0) pix = 0;
if (pix > 255)pix = 255;
dst.at<uchar>(i - 1, j - 1) = static_cast<uchar>(pix);
}
}
}
//处理彩色图像
else if (src.channels() == 3)
{
for (int i = 1; i < dst_rows + 1; ++i)
{
int src_y = (i + 0.5) / sy - 0.5;
if (src_y < 0) src_y = 0;
if (src_y > src.rows - 1) src_y = src.rows - 1;
src_y += 1;
int i1 = std::floor(src_y);
int i2 = std::ceil(src_y);
int i0 = i1 - 1;
int i3 = i2 + 1;
double u = src_y - static_cast<int64>(i1);
std::vector<double> weight_y = getWeight(u);
for (int j = 1; j < dst_cols + 1; ++j)
{
int src_x = (j + 0.5) / sy - 0.5;
if (src_x < 0) src_x = 0;
if (src_x > src.rows - 1) src_x = src.rows - 1;
src_x += 1;
int j1 = std::floor(src_x);
int j2 = std::ceil(src_x);
int j0 = j1 - 1;
int j3 = j2 + 1;
double v = src_x - static_cast<int64>(j1);
std::vector<double> weight_x = getWeight(v);
cv::Vec3b pix;
pix[0] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[0] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[0]
+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[0] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[0]
+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[0] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[0]
+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[0] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[0]
+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[0] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[0]
+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[0] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[0]
+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[0] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[0]
+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[0] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[0];
pix[1] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[1] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[1]
+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[1] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[1]
+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[1] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[1]
+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[1] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[1]
+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[1] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[1]
+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[1] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[1]
+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[1] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[1]
+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[1] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[1];
pix[2] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[2] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[2]
+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[2] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[2]
+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[2] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[2]
+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[2] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[2]
+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[2] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[2]
+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[2] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[2]
+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[2] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[2]
+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[2] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[2];
for (int i = 0; i < src.channels(); ++i)
{
if (pix[i] < 0) pix = 0;
if (pix[i] > 255)pix = 255;
}
dst.at<cv::Vec3b>(i - 1, j - 1) = static_cast<cv::Vec3b>(pix);
}
}
}
}
3. 测试及结果
int main()
{
cv::Mat src = cv::imread("C:\\Users\\Echo\\Pictures\\Saved Pictures\\bilateral.png");
cv::Mat dst;
bicubic(src, dst, 309/0.5, 338/0.5);
cv::imshow("gray", dst);
cv::imshow("src", src);
cv::waitKey(0);
}
彩色图像(放大两倍)
以上就是C++ OpenCV实现图像双三次插值算法详解的详细内容,更多关于C++ OpenCV 图像双三次插值算法的资料请关注编程学习网其它相关文章!
本文标题为:C++ OpenCV实现图像双三次插值算法详解


- C语言qsort()函数的使用方法详解 2023-04-26
- Qt计时器使用方法详解 2023-05-30
- 我应该为我的项目使用相对包含路径,还是将包含目录放在包含路径上? 2022-10-30
- C语言手把手带你掌握带头双向循环链表 2023-04-03
- ubuntu下C/C++获取剩余内存 2023-09-18
- Easyx实现扫雷游戏 2023-02-06
- C++ 数据结构超详细讲解顺序表 2023-03-25
- C语言详解float类型在内存中的存储方式 2023-03-27
- c++ const 成员函数,返回一个 const 指针.但是返回的指针是什么类型的 const? 2022-10-11
- 详解C语言中sizeof如何在自定义函数中正常工作 2023-04-09