python皮同_Python OpenCV 图像的双线性插值算法,全网最细致的算法说明_橡皮擦,一个逗趣的互联网高级网虫-CSDN博客...
原文作者:夢(mèng)想橡皮擦
原文標(biāo)題:Python OpenCV 圖像的雙線性插值算法,全網(wǎng)最細(xì)致的算法說明
發(fā)布時(shí)間:2021-02-17 20:55:32
Python OpenCV 365 天學(xué)習(xí)計(jì)劃,與橡皮擦一起進(jìn)入圖像領(lǐng)域吧。本篇博客是這個(gè)系列的第 42 篇。
該系列文章導(dǎo)航參考:https://blog.csdn.net/hihell/category_10688961.html
基礎(chǔ)知識(shí)鋪墊
本篇博客實(shí)現(xiàn)雙線性插值算法的編寫,順便修改一下 上篇博客 最近鄰插值算法最后實(shí)現(xiàn)與 OpenCV 提供的內(nèi)置參數(shù)不一致問題。
還有一個(gè)問題,是執(zhí)行速度問題,該問題一并在學(xué)習(xí)雙線性插值算法之后解決。
圖像的雙線性插值算法
雙線性內(nèi)插值算法是一種比較好的圖像縮放算法,它利用了源圖像中虛擬點(diǎn)四周四個(gè)真實(shí)存在的像素值,依據(jù)權(quán)重來決定目標(biāo)圖中的一個(gè)像素值。
先摘抄一些原理性的描述:
對(duì)于一個(gè)目標(biāo)像素,通過反向變換可以得到源圖像的虛擬坐標(biāo),大概率是浮點(diǎn)坐標(biāo),格式為(i+u,j+v),其中 i、j 為整數(shù)部分,u、v 為小數(shù)部分,取值 [0,1),這時(shí)在源圖像中 (i+u,j+v) 可以由周邊的四個(gè)像素坐標(biāo) (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1) 計(jì)算獲得,也就是存在公式:
f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
這一步的變換被省略了很多內(nèi)容,橡皮擦也是查閱了很多資料,接下來為你補(bǔ)充上。
先畫一張輔助理解的圖~
首先在 X 方向上進(jìn)行兩次線性插值計(jì)算,然后在 Y 方向上進(jìn)行一次插值計(jì)算。
在計(jì)算之前,又要補(bǔ)充知識(shí)了,叫做線性插值,已知數(shù)據(jù)
(
x
0
,
y
0
)
(x_0,y_0)
(x0?,y0?) 和
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?),要計(jì)算
[
x
0
,
x
1
]
[x_0,x_1]
[x0?,x1?] 區(qū)間內(nèi)某一位置 x 在直線上的 y 值,公式如下:
y
?
y
0
x
?
x
0
=
y
1
?
y
0
x
1
?
x
0
\cfrac{y-y_0}{x-x_0}=\cfrac{y_1-y_0}{x_1-x_0}
x?x0?y?y0??=x1??x0?y1??y0??
公式進(jìn)行變形得到:
y
=
x
1
?
x
x
1
?
x
0
y
0
+
x
?
x
0
x
1
?
x
0
y
1
y=\cfrac{x_1-x}{x_1-x_0}y_0+\cfrac{x-x_0}{x_1-x_0}y_1
y=x1??x0?x1??x?y0?+x1??x0?x?x0??y1?
變換之后大概等用
x
0
x_0
x0? 和
x
1
x_1
x1? 的距離作為一個(gè)權(quán)重,用于
y
0
y_0
y0? 和
y
1
y_1
y1? 的加權(quán),雙線性插值就是在兩個(gè)方向上做線性插值。
繼續(xù)看上圖,在點(diǎn) 1 與點(diǎn) 2 區(qū)間內(nèi)尋找一點(diǎn),依據(jù)公式可得:
f
(
插
值
點(diǎn)
1
)
≈
x
2
?
x
x
2
?
x
1
f
(
點(diǎn)
1
)
+
x
?
x
1
x
2
?
x
1
f
(
點(diǎn)
2
)
f(插值點(diǎn)1)\approx\cfrac{x_2-x}{x_2-x_1}f(點(diǎn)1)+\cfrac{x-x_1}{x_2-x_1}f(點(diǎn)2)
f(插值點(diǎn)1)≈x2??x1?x2??x?f(點(diǎn)1)+x2??x1?x?x1??f(點(diǎn)2) 其中插值點(diǎn) 1 =
(
x
,
y
1
)
(x,y_1)
(x,y1?)
同樣的算法獲取插值點(diǎn) 2:
f
(
插
值
點(diǎn)
2
)
≈
x
2
?
x
x
2
?
x
1
f
(
點(diǎn)
3
)
+
x
?
x
1
x
2
?
x
1
f
(
點(diǎn)
4
)
f(插值點(diǎn)2)\approx\cfrac{x_2-x}{x_2-x_1}f(點(diǎn)3)+\cfrac{x-x_1}{x_2-x_1}f(點(diǎn)4)
f(插值點(diǎn)2)≈x2??x1?x2??x?f(點(diǎn)3)+x2??x1?x?x1??f(點(diǎn)4) 其中插值點(diǎn) 2 =
(
x
,
y
2
)
(x,y_2)
(x,y2?)
接下來在 Y 方向進(jìn)行線性插值計(jì)算:
f
(
P
)
≈
y
2
?
y
y
2
?
y
1
f
(
插
值
點(diǎn)
1
)
+
y
?
y
1
y
2
?
y
1
f
(
插
值
點(diǎn)
2
)
f(P)\approx\cfrac{y_2-y}{y_2-y_1}f(插值點(diǎn)1)+\cfrac{y-y_1}{y_2-y_1}f(插值點(diǎn)2)
f(P)≈y2??y1?y2??y?f(插值點(diǎn)1)+y2??y1?y?y1??f(插值點(diǎn)2)
將上述式子展開,就可以得到最后的結(jié)果了,這個(gè)沒多少難度,寫的時(shí)候與看的時(shí)候都仔細(xì)點(diǎn)就好:
f
(
x
,
y
)
≈
f
(
點(diǎn)
1
)
(
x
2
?
x
)
(
y
2
?
y
)
(
x
2
?
x
1
)
(
y
2
?
y
1
)
+
f
(
點(diǎn)
2
)
(
x
?
x
1
)
(
y
2
?
y
)
(
x
2
?
x
1
)
(
y
2
?
y
1
)
+
f
(
點(diǎn)
3
)
(
x
2
?
x
)
(
y
?
y
1
)
(
x
2
?
x
1
)
(
y
2
?
y
1
)
+
f
(
點(diǎn)
4
)
(
x
?
x
1
)
(
y
?
y
1
)
(
x
2
?
x
1
)
(
y
2
?
y
1
)
f(x,y)\approx\cfrac{f(點(diǎn)1)(x_2-x)(y_2-y)}{(x_2-x_1)(y_2-y_1)}+\cfrac{f(點(diǎn)2)(x-x_1)(y_2-y)}{(x_2-x_1)(y_2-y_1)}+\cfrac{f(點(diǎn)3)(x_2-x)(y-y_1)}{(x_2-x_1)(y_2-y_1)}+\cfrac{f(點(diǎn)4)(x-x_1)(y-y_1)}{(x_2-x_1)(y_2-y_1)}
f(x,y)≈(x2??x1?)(y2??y1?)f(點(diǎn)1)(x2??x)(y2??y)?+(x2??x1?)(y2??y1?)f(點(diǎn)2)(x?x1?)(y2??y)?+(x2??x1?)(y2??y1?)f(點(diǎn)3)(x2??x)(y?y1?)?+(x2??x1?)(y2??y1?)f(點(diǎn)4)(x?x1?)(y?y1?)?
該式子可以進(jìn)一步的簡化,因?yàn)閮蓚€(gè)相鄰點(diǎn)插值是 1,所以簡化如下:
f
(
x
,
y
)
≈
f
(
點(diǎn)
1
)
(
x
2
?
x
)
(
y
2
?
y
)
+
f
(
點(diǎn)
2
)
(
x
?
x
1
)
(
y
2
?
y
)
+
f
(
點(diǎn)
3
)
(
x
2
?
x
)
(
y
?
y
1
)
+
f
(
點(diǎn)
4
)
(
x
?
x
1
)
(
y
?
y
1
)
f(x,y)\approx f(點(diǎn)1)(x_2-x)(y_2-y)+f(點(diǎn)2)(x-x_1)(y_2-y)+f(點(diǎn)3)(x_2-x)(y-y_1)+f(點(diǎn)4)(x-x_1)(y-y_1)
f(x,y)≈f(點(diǎn)1)(x2??x)(y2??y)+f(點(diǎn)2)(x?x1?)(y2??y)+f(點(diǎn)3)(x2??x)(y?y1?)+f(點(diǎn)4)(x?x1?)(y?y1?)
在將所有點(diǎn)的坐標(biāo)帶入
f
(
x
,
y
)
≈
f
(
x
1
,
y
1
)
(
x
2
?
x
)
(
y
2
?
y
)
+
f
(
x
2
,
y
1
)
(
x
?
x
1
)
(
y
2
?
y
)
+
f
(
x
1
,
y
2
)
(
x
2
?
x
)
(
y
?
y
1
)
+
f
(
x
2
,
y
2
)
(
x
?
x
1
)
(
y
?
y
1
)
f(x,y)\approx f(x_1,y_1)(x_2-x)(y_2-y)+f(x_2,y_1)(x-x_1)(y_2-y)+f(x_1,y_2)(x_2-x)(y-y_1)+f(x_2,y_2)(x-x_1)(y-y_1)
f(x,y)≈f(x1?,y1?)(x2??x)(y2??y)+f(x2?,y1?)(x?x1?)(y2??y)+f(x1?,y2?)(x2??x)(y?y1?)+f(x2?,y2?)(x?x1?)(y?y1?)
將 (x,y) 替換成最開始的寫法 (i+u,j+v) ,其他的坐標(biāo)分別為 點(diǎn) 1~點(diǎn) 4 分別為:(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1) ,帶入上述公式,變化結(jié)果如所示:
f
(
i
+
u
,
j
+
v
)
≈
f
(
i
,
j
)
(
i
+
1
?
(
i
+
u
)
)
(
j
+
1
?
(
j
+
v
)
)
+
f
(
i
+
1
,
j
)
(
i
+
u
?
i
)
(
j
+
1
?
(
j
+
v
)
)
+
f
(
i
,
j
+
1
)
(
i
+
1
?
(
i
+
u
)
)
(
j
+
v
?
j
)
+
f
(
i
+
1
,
j
+
1
)
(
i
+
u
?
i
)
(
j
+
v
?
j
)
f(i+u,j+v)\approx f(i,j)(i+1-(i+u))(j+1-(j+v))+f(i+1,j)(i+u-i)(j+1-(j+v))+f(i,j+1)(i+1-(i+u))(j+v-j)+f(i+1,j+1)(i+u-i)(j+v-j)
f(i+u,j+v)≈f(i,j)(i+1?(i+u))(j+1?(j+v))+f(i+1,j)(i+u?i)(j+1?(j+v))+f(i,j+1)(i+1?(i+u))(j+v?j)+f(i+1,j+1)(i+u?i)(j+v?j)
別暈,估計(jì)這是全網(wǎng)最清晰的轉(zhuǎn)換方式了:
f
(
i
+
u
,
j
+
v
)
≈
f
(
i
,
j
)
(
1
?
u
)
(
1
?
v
)
+
f
(
i
+
1
,
j
)
u
(
1
?
v
)
+
f
(
i
,
j
+
1
)
(
1
?
u
)
v
+
f
(
i
+
1
,
j
+
1
)
u
v
f(i+u,j+v)\approx f(i,j)(1-u)(1-v)+f(i+1,j)u(1-v)+f(i,j+1)(1-u)v+f(i+1,j+1)uv
f(i+u,j+v)≈f(i,j)(1?u)(1?v)+f(i+1,j)u(1?v)+f(i,j+1)(1?u)v+f(i+1,j+1)uv
到這里就與本篇博客最開始的公式呼應(yīng)上了。
所以通過目標(biāo)圖像反推出來的一點(diǎn),可以通過四個(gè)點(diǎn)的坐標(biāo)進(jìn)行計(jì)算,每個(gè)坐標(biāo)前面的叫做權(quán)重,假設(shè)存在這樣一個(gè)像素坐標(biāo)為 (1,1),反推在源圖中得到的坐標(biāo)是 (0.75,0.75),由于圖像中不可能存在浮點(diǎn)坐標(biāo),所以獲取周圍四個(gè)坐標(biāo)分別是 (0,0)(0,1)(1,0)(1,1),由于 (0.75,0.75) 距離 (1,1) 最近,所以 (1,1) 點(diǎn)對(duì)該像素顏色作用最大,相應(yīng)的 (1,1) 點(diǎn)對(duì)應(yīng)的點(diǎn)是 f(i+1,i+1) ,該變量前面的系數(shù)權(quán)重為 0.75*0.75 ,結(jié)果最大,這個(gè)說明是通過真實(shí)的數(shù)據(jù)去說明。
拿到計(jì)算方式之后,就可以通過代碼實(shí)現(xiàn)雙線性插值算法了。
先通過內(nèi)置的縮放函數(shù),測試一下運(yùn)行時(shí)間:
if __name__ == '__main__':
src = cv2.imread('./t.png')
start = time.time()
dst = cv2.resize(src, (600, 600))
print('內(nèi)置函數(shù)運(yùn)行時(shí)間:%f' % (time.time() - start))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
得到的時(shí)間為 內(nèi)置函數(shù)運(yùn)行時(shí)間:0.002000 ,非常快。
接下來就是自寫函數(shù)驗(yàn)證了,代碼的說明我寫在了注釋中,你可以研究一下,注意公式的運(yùn)用
import cv2
import numpy as np
import time
def resize_demo(src, new_size):
# 目標(biāo)圖像寬高
dst_h, dst_w = new_size
# 源圖像寬高
src_h, src_w = src.shape[:2]
# 如果圖像大小一致,直接復(fù)制返回即可
if src_h == dst_h and src_w == dst_w:
return src.copy()
# 計(jì)算縮放比例
scale_x = float(src_w) / dst_w
scale_y = float(src_h) / dst_h
# 遍歷目標(biāo)圖像
dst = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)
# return dst
# 對(duì)通道進(jìn)行循環(huán)
# for n in range(3):
# 對(duì) height 循環(huán)
for dst_y in range(dst_h):
# 對(duì) width 循環(huán)
for dst_x in range(dst_w):
# 目標(biāo)在源上的坐標(biāo)
src_x = dst_x * scale_x
src_y = dst_y * scale_y
# 計(jì)算在源圖上 4 個(gè)近鄰點(diǎn)的位置
# i,j
i = int(np.floor(src_x))
j = int(np.floor(src_y))
u = src_x-i
v = src_y-j
if j == src_h-1:
j = src_h-2
if i == src_w-1:
i = src_h-2
# f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
dst[dst_y, dst_x] = (1-u)*(1-v)*src[j, i]+u*(1-v) * \
src[j+1, i] + (1-u)*v*src[j, i+1]+u*v*src[j+1, i+1]
# dst[dst_y, dst_x] = 0.25*src[j, i]+0.25 * \
# src[j+1, i] + 0.25*src[j, i+1]+0.25*src[j+1, i+1]
# dst[dst_y,dst_x,n] = 255
return dst
if __name__ == '__main__':
src = cv2.imread('./t.png')
start = time.time()
dst = resize_demo(src, (500, 600))
print('自寫函數(shù)運(yùn)行時(shí)間:%f' % (time.time() - start))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
代碼運(yùn)行消耗了 2s 多,確實(shí)比較費(fèi)時(shí)間。
橡皮擦的小節(jié)
希望今天的 1 個(gè)小時(shí)你有所收獲,我們下篇博客見~
相關(guān)閱讀
技術(shù)專欄
今天是持續(xù)寫作的第 84 / 100 天。
如果你想跟博主建立親密關(guān)系,可以關(guān)注同名公眾號(hào) 夢(mèng)想橡皮擦,近距離接觸一個(gè)逗趣的互聯(lián)網(wǎng)高級(jí)網(wǎng)蟲。
博主 ID:夢(mèng)想橡皮擦,希望大家點(diǎn)贊、評(píng)論、收藏。
Post Views:
6
總結(jié)
以上是生活随笔為你收集整理的python皮同_Python OpenCV 图像的双线性插值算法,全网最细致的算法说明_橡皮擦,一个逗趣的互联网高级网虫-CSDN博客...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的模块如何学习_在pyth
- 下一篇: vcenter用到java吗_Vijav