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