图像插值处理
问题描述:读取图像,然后对图像进行最近邻插值、双线性插值、双三次插值处理。
本文主要侧重于代码的实现,相关的原理性介绍可参考
最邻近插值
最近邻插值 法的优点是计算量很小,算法也简单,因此运算速度较快。但它仅使用离待测采样点最近的像素的灰度值作为该采样点的灰度值,而没考虑其他相邻像素点的影响,因而重新采样后灰度值有明显的不连续性,图像质量损失较大,会产生明显的马赛克和锯齿现象。
可采用下述公示进行图像的最邻近插值,其中 I ′ I' I′为放大后图像, I I I为放大前图像, a a a为放大率,方括号是四舍五入取整操作:
I ′ ( x , y ) = I ( [ x a ] , [ y a ] ) I'(x,y) = I([\frac{x}{a}], [\frac{y}{a}]) I′(x,y)=I([ax],[ay])
// 最近邻插值
cv::Mat nearest_neighbor(cv::Mat img, double rx, double ry)
{
int row = img.rows;
int col = img.cols;
int channel = img.channels();
// Resize width and height
int resized_col = (int)(col * rx);
int resized_row = (int)(row * ry);
cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);
int pos_before_x, pos_before_y;
// nearest_neighbor
for (int i = 0; i < resized_row; i++)
{
pos_before_y = (int)round(i / ry);
pos_before_y = fmin(pos_before_y, row - 1);
for (int j = 0; j < resized_col; j++)
{
pos_before_x = (int)round(j / rx);
pos_before_x = fmin(pos_before_x, col - 1);
for (int chan = 0; chan < channel; chan++)
{
new_image.at<cv::Vec3b>(i, j)[chan] = img.at<cv::Vec3b>(pos_before_y, pos_before_x)[chan];
}
}
}
return new_image;
}
输入图像 (fate.jpeg) | 输出图像 (fate_nearest_neighbor.jpeg) |
---|---|
双线性插值
双线性插值考察 4 4 4邻域的像素点,并根据距离设置权值。虽然计算量增大使得处理时间变长,但是可以有效抑制画质劣化。
-
放大后图像的座标 ( x ′ , y ′ ) (x',y') (x′,y′)除以放大率 a a a,可以得到对应原图像的座标 ( ⌊ x ′ a ⌋ , ⌊ y ′ a ⌋ ) (\lfloor \frac{x'}{a}\rfloor , \lfloor \frac{y'}{a}\rfloor) (⌊ax′⌋,⌊ay′⌋)。
-
求原图像的座标 ( ⌊ x ′ a ⌋ , ⌊ y ′ a ⌋ ) (\lfloor \frac{x'}{a}\rfloor , \lfloor \frac{y'}{a}\rfloor) (⌊ax′⌋,⌊ay′⌋)周围 4 4 4邻域的座标 I ( x , y ) I(x,y) I(x,y), I ( x + 1 , y ) I(x+1,y) I(x+1,y), I ( x , y + 1 ) I(x,y+1) I(x,y+1), I ( x + 1 , y + 1 ) I(x+1, y+1) I(x+1,y+1):
-
分别求这4个点与 ( x ′ a , y ′ a ) (\frac{x'}{a}, \frac{y'}{a}) (ax′,ay′)的距离,根据距离设置权重: w = d ∑ d w = \frac{d}{\sum\ d} w=∑ dd
-
根据下式求得放大后图像 ( x ′ , y ′ ) (x',y') (x′,y′)处的像素值:
d x = x ′ a − x d y = y ′ a − y I ′ ( x ′ , y ′ ) = ( 1 − d x ) ( 1 − d y ) I ( x , y ) + d x ( 1 − d y ) I ( x + 1 , y ) + ( 1 − d x ) d y I ( x , y + 1 ) + d x d y I ( x + 1 , y + 1 ) d_x = \frac{x'}{a} - x\\ d_y = \frac{y'}{a} - y\\ I'(x',y') = (1-d_x)\ (1-d_y)\ I(x,y) + d_x\ (1-d_y)\ I(x+1,y) + (1-d_x)\ d_y\ I(x,y+1) + d_x\ d_y\ I(x+1,y+1) dx=ax′−xdy=ay′−yI′(x′,y′)=(1−dx) (1−dy) I(x,y)+dx (1−dy) I(x+1,y)+(1−dx) dy I(x,y+1)+dx dy I(x+1,y+1)
// 双线性插值
cv::Mat bilinear(cv::Mat img, double rx, double ry)
{
int row = img.rows;
int col = img.cols;
int channel = img.channels();
// Resize width and height
int resized_col = (int)(col * rx);
int resized_row = (int)(row * ry);
cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);
int pos_before_j, pos_before_i;
double dx, dy;
double val;
// bilinear
for (int i = 0; i < resized_row; i++)
{
pos_before_i = (int)round(i / ry);
pos_before_i = fmin(pos_before_i, row - 1);
dy = round(i / ry) - pos_before_i;
for (int j = 0; j < resized_col; j++)
{
pos_before_j = (int)round(j / rx);
pos_before_j = fmin(pos_before_j, col - 1);
dx = round(j / rx) - pos_before_j;
for (int chan = 0; chan < channel; chan++)
{
val = (1 - dx) * (1 - dy) * img.at<cv::Vec3b>(pos_before_i, pos_before_j)[chan] +
dx * (1 - dy) * img.at<cv::Vec3b>(pos_before_i, pos_before_j + 1)[chan] +
(1 - dx) * dy * img.at<cv::Vec3b>(pos_before_i + 1, pos_before_j)[chan] +
dx * dy * img.at<cv::Vec3b>(pos_before_i + 1, pos_before_j + 1)[chan];
new_image.at<cv::Vec3b>(i, j)[chan] = (uchar)val;
}
}
}
return new_image;
}
输入图像 (fate.jpeg) | 输出图像 (fate_nearest_neighbor.jpeg) |
---|---|
双三次插值
双三次插值是双线性插值的扩展,使用邻域 16 16 16像素进行插值。
各自像素间的距离由下式决定:
d
x
1
=
∣
x
′
a
x
−
(
x
−
1
)
∣
d
x
2
=
∣
x
′
a
x
−
x
∣
d
x
3
=
∣
x
′
a
x
−
(
x
+
1
)
∣
d
x
4
=
∣
x
′
a
x
−
(
x
+
2
)
∣
d
y
1
=
∣
x
′
a
y
−
(
y
−
1
)
∣
d
y
2
=
∣
x
′
a
y
−
y
∣
d
y
3
=
∣
x
′
a
y
−
(
y
+
1
)
∣
d
y
4
=
∣
x
′
a
y
−
(
y
+
2
)
∣
d_{x_1} = |\frac{x'}{a\ x} - (x-1)|\quad d_{x_2} = |\frac{x'}{a\ x}- x| \quad d_{x_3} = |\frac{x'}{a\ x}- (x+1)|\quad d_{x_4} = |\frac{x'}{a\ x} - (x+2)|\\ d_{y_1} = |\frac{x'}{a\ y} - (y-1)|\quad d_{y_2} = |\frac{x'}{a\ y} - y| \quad d_{y_3} = |\frac{x'}{a\ y} - (y+1)| \quad d_{y_4} = |\frac{x'}{a\ y} - (y+2)|
dx1=∣a xx′−(x−1)∣dx2=∣a xx′−x∣dx3=∣a xx′−(x+1)∣dx4=∣a xx′−(x+2)∣dy1=∣a yx′−(y−1)∣dy2=∣a yx′−y∣dy3=∣a yx′−(y+1)∣dy4=∣a yx′−(y+2)∣
权重由基于距离的函数取得。
a
a
a在大部分时候取
−
1
-1
−1。权重公示如下:
h
(
t
)
=
{
(
a
+
2
)
∣
t
∣
3
−
(
a
+
3
)
∣
t
∣
2
+
1
when
∣
t
∣
≤
1
a
∣
t
∣
3
−
5
a
∣
t
∣
2
+
8
a
∣
t
∣
−
4
a
when
1
<
∣
t
∣
≤
2
0
else
h(t)= \begin{cases} (a+2)\ |t|^3 - (a+3)\ |t|^2 + 1 &\text{when}\quad |t|\leq 1 \\ a\ |t|^3 - 5\ a\ |t|^2 + 8\ a\ |t| - 4\ a&\text{when}\quad 1<|t|\leq 2\\ 0&\text{else} \end{cases}
h(t)=⎩⎪⎨⎪⎧(a+2) ∣t∣3−(a+3) ∣t∣2+1a ∣t∣3−5 a ∣t∣2+8 a ∣t∣−4 a0when∣t∣≤1when1<∣t∣≤2else
利用上面得到的权重,通过下面的式子扩大图像。将每个像素与权重的乘积之和除以权重的和。
I
′
(
x
′
,
y
′
)
=
1
∑
j
=
1
4
∑
i
=
1
4
h
(
d
x
i
)
h
(
d
y
j
)
∑
j
=
1
4
∑
i
=
1
4
I
(
x
+
i
−
2
,
y
+
j
−
2
)
h
(
d
x
i
)
h
(
d
y
j
)
I'(x', y')=\frac{1}{\sum\limits_{j=1}^4\ \sum\limits_{i=1}^4\ h(d_{xi})\ h(d_{yj})}\ \sum\limits_{j=1}^4\ \sum\limits_{i=1}^4\ I(x+i-2,y+j-2)\ h(d_{xi})\ h(d_{yj})
I′(x′,y′)=j=1∑4 i=1∑4 h(dxi) h(dyj)1 j=1∑4 i=1∑4 I(x+i−2,y+j−2) h(dxi) h(dyj)
// weight function
double weight(double t)
{
double a = -1;
if (fabs(t) <= 1)
{
return (a + 2) * pow(fabs(t), 3) - (a + 3) * pow(fabs(t), 2) + 1;
} else if (fabs(t) <= 2)
{
return a * pow(fabs(t), 3) - 5 * a * pow(fabs(t), 2) + 8 * a * fabs(t) - 4 * a;
}
return 0;
}
cv::Mat bicubic(cv::Mat img, double rx, double ry)
{
int row = img.rows;
int col = img.cols;
int channel = img.channels();
// Resize width and height
int resized_col = (int)(col * rx);
int resized_row = (int)(row * ry);
int pos_before_j, pos_before_i;
int pos_j, pos_i;
double dx, dy, weight_j, weight_i, weight_sum;
double val;
cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);
// bicubic
for (int i = 0; i < resized_row; i++)
{
dy = i / ry;
pos_before_i = (int)floor(dy);
for (int j = 0; j < resized_col; j++)
{
dx = j / rx;
pos_before_j = (int)floor(dx);
for (int chan = 0; chan < channel; chan++)
{
weight_sum = 0;
val = 0;
for (int di = -1; di < 3; di++)
{
pos_i = fmin(fmax(pos_before_i + di, 0), row - 1);
weight_i = weight(fabs(dy - pos_i));
for (int dj = -1; dj < 3; dj++)
{
pos_j = fmin(fmax(pos_before_j + dj, 0), col - 1);
weight_j = weight(fabs(dx - pos_j));
weight_sum += weight_i * weight_j;
val += (double)img.at<cv::Vec3b>(pos_i, pos_j)[chan] * weight_i * weight_j;
}
}
val /= weight_sum;
val = fmin(fmax(val, 0), 255);
new_image.at<cv::Vec3b>(i, j)[chan] = (uchar)val;
}
}
}
return new_image;
}
int main(){
// read image
cv::Mat img = cv::imread("../fate.jpeg", cv::IMREAD_COLOR);
// cv::Mat new_image = nearest_neighbor(img, 1.5, 1.5); // 最近临插值
// cv::Mat new_image = bilinear(img, 1.3, 1.3); // 双线性插值
cv::Mat new_image = bicubic(img, 1.4, 1.4); // 双三次插值
// cv::imwrite("../1-10/fate_nearest_neighbor.jpeg", new_image);
// cv::imwrite("../1-10/fate_bilinear.jpeg", new_image);
cv::imwrite("../1-10/fate_bicubic.jpeg", new_image);
cv::imshow("vv", new_image);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
输入图像 (fate.jpeg) | 输出图像 (fate_bicubic.jpeg) |
---|---|