这篇博文在写之前参考了网上的不少文章,感觉有的文章讲解比较到位,有的会误导人。下面的代码运行结果已经和直接调用opencv算子进行对比确保结果相同,就没有深究其开源代码了。不过根据推断,opencv在实现resize图像插值时(1)需要做像素点中心对齐操作;(2)不需要做copyMakeBorder边缘复制操作。
原理参考:【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值
其中双三次插值的实现参考了:C++ OpenCV实现图像双三次插值算法详解这篇文章原理讲的比较细,但代码实现有瑕疵。
/**
* @description: 最近邻插值
* @param src 输入图像
* @param dst 输出图像
* @param sx x方向缩放比例
* @param sy y方向缩放比例
*/
void nearest(cv::Mat& src, cv::Mat& dst, float sx, float sy)
{
dst = cv::Mat(round(src.rows * sy), round(src.cols * sx), src.type());
for (int i = 0; i < dst.rows; i++)
{
float index_i = (i + 0.5) / sy - 0.5;
if (index_i < 0) index_i = 0;
if (index_i > src.rows - 1) index_i = src.rows - 1;
int i_index = round(index_i);
for (int j = 0; j < dst.cols; j++)
{
float index_j = (j + 0.5) / sx - 0.5;
if (index_j < 0) index_j = 0;
if (index_j > src.cols - 1) index_j = src.cols - 1;
int j_index = round(index_j);
dst.at<uchar>(i, j) = src.at<uchar>(i_index, j_index);
}
}
}
/**
* @description: 双线性插值
* @param src 输入图像
* @param dst 输出图像
* @param sx x方向缩放比例
* @param sy y方向缩放比例
*/
void linear(cv::Mat& src, cv::Mat& dst, float sx, float sy)
{
dst = cv::Mat(round(sy*src.rows), round(sx*src.cols), src.type());
for (int i = 0; i < dst.rows; i++)
{
float index_i = (i + 0.5) / sy - 0.5;
if (index_i < 0) index_i = 0;
if (index_i > src.rows - 1) index_i = src.rows - 1;
int i1 = floor(index_i);
int i2 = ceil(index_i);
float u = index_i - i1;
for (int j = 0; j < dst.cols; j++)
{
float index_j = (j + 0.5) / sx - 0.5;
if (index_j < 0) index_j = 0;
if (index_j > src.cols - 1) index_j = src.cols - 1;
int j1 = floor(index_j);
int j2 = ceil(index_j);
float v = index_j - j1;
dst.at<uchar>(i, j) = (1 - u)*(1 - v)*src.at<uchar>(i1, j1) + (1 - u)*v*src.at<uchar>(i1, j2) +
u*(1 - v)*src.at<uchar>(i2, j1) + u*v*src.at<uchar>(i2, j2);
}
}
}
/**
* @description: 计算权重
* @param c c就是u和v,横坐标和纵坐标的输出计算方式一样
* @param a 系数
* @return 权重
*/
std::vector<float> getWeight(float c, float a = -1.0)
{
std::vector<float> temp = { 1 + c, c, 1 - c, 2 - c };
std::vector<float> weight(4);
weight[0] = a * pow(fabs(temp[0]), 3) - 5 * a * pow(fabs(temp[0]), 2) + 8 * a * fabs(temp[0]) - 4 * a;
weight[1] = (a + 2) * pow(fabs(temp[1]), 3) - (a + 3) * pow(fabs(temp[1]), 2) + 1;
weight[2] = (a + 2) * pow(fabs(temp[2]), 3) - (a + 3) * pow(fabs(temp[2]), 2) + 1;
weight[3] = a * pow(fabs(temp[3]), 3) - 5 * a * pow(fabs(temp[3]), 2) + 8 * a * fabs(temp[3]) - 4 * a;
return weight;
}
/**
* @description: 双三次插值
* @param src 输入图像
* @param dst 输出图像
* @param sx x方向缩放系数
* @param sy y方向缩放系数
*/
void bicubic(cv::Mat& src, cv::Mat& dst, float sx, float sy)
{
int dst_cols = sx*src.cols, dst_rows = sy*src.rows;
dst.create(dst_rows, dst_cols, src.type());
for (int i = 0; i < dst_rows; ++i)
{
float index_i = (i + 0.5) / sy - 0.5;
if (index_i < 0) index_i = 0;
if (index_i > src.rows - 1) index_i = src.rows - 1;
int i0 = index_i - 1, i1 = index_i, i2 = index_i + 1, i3 = index_i + 2;
float u = index_i - i1;
std::vector<float> weight_u = getWeight(u);
for (int j = 0; j < dst_cols; ++j)
{
float index_j = (j + 0.5) / sx - 0.5;
if (index_j < 0) index_j = 0;
if (index_j > src.cols - 1) index_j = src.cols - 1;
int j0 = index_j - 1, j1 = index_j, j2 = index_j + 1, j3 = index_j + 2;
float v = index_j - j1;
std::vector<float> weight_v = getWeight(v);
float temp = 0;
for (int s = 0; s <= 3; s++)
{
for (int t = 0; t <= 3; t++)
{
temp += src.at<uchar>(i1 + s - 1, j1 + t - 1)*weight_u[s] * weight_v[t];
}
}
if (temp < 0) temp = 0;
if (temp > 255) temp = 255;
dst.at<uchar>(i, j) = temp;
}
}
}
代码传送门:https://github.com/taifyang/OpenCV-algorithm