圖像預處理之warpaffine與雙線性插值及其高性能實現

圖像預處理之warpaffine與雙線性插值及其高性能實現

視頻講解:https://www.bilibili.com/video/BV1ZU4y1A7EG

代碼Repo:https://github.com/shouxieai/tensorRT_Pro

本文為視頻講解的個人筆記。

warpaffine矩陣變換

對于坐標點的變換,我們通常考慮的是旋轉、縮放、平移這三種變換。例如將點 P(x,y)P(x,y)P(x,y) 旋轉 θ\thetaθ 度,縮放 scalescalescale 倍,平移 ox,oyox,oyox,oy 。warpaffine 將坐標點的旋轉、縮放、平移三種操作集成為一個矩陣乘法運算。

旋轉變換

我們先來看旋轉,如圖所示,我們要將點 P(x,y)P(x,y)P(x,y) 旋轉到點 P′(x′,y′)P'(x',y')P(x,y) ,推導的過程很簡單,我們要求的就是 x′,y′x',y'x,y 兩點的坐標,將其轉換為 m×cos(θ+α)m\times cos(\theta+\alpha)m×cos(θ+α)m×sin(θ+α)m\times sin(\theta+\alpha)m×sin(θ+α) ,再用公式展開,即得結果(詳見圖中公式):

{x′y′}={cos(θ)?sin(θ)sin(θ)cos(θ)}{xy}\left\{ \begin{array}{rc} x' \\ y' \end{array} \right\}= \left\{ \begin{array}{rc} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \end{array} \right\} {xy?}={cos(θ)sin(θ)??sin(θ)cos(θ)?}{xy?}
再考慮到我們在圖像處理時的坐標系(如在 OpenCV 中的坐標系、常見目標檢測的坐標系等)通常是原點在左上角,因此應該為:

{x′y′}={cos(θ)sin(θ)?sin(θ)cos(θ)}{xy}\left\{ \begin{array}{rc} x' \\ y' \end{array} \right\}= \left\{ \begin{array}{rc} cos(\theta) & sin(\theta) \\ -sin(\theta) & cos(\theta) \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \end{array} \right\} {xy?}={cos(θ)?sin(θ)?sin(θ)cos(θ)?}{xy?}
將旋轉變換的矩陣記為 RRR ,則 P′=RPP'=RPP=RP

在這里插入圖片描述

縮放變換

縮放變換比較簡單,兩坐標直接乘以縮放系數 scalescalescale 即可:

x′=x×scaley′=y×scalex'=x\times scale \\ y'=y\times scale x=x×scaley=y×scale
寫成矩陣形式即:

{x′y′}={scale00scale}{xy}\left\{ \begin{array}{rc} x' \\ y' \end{array} \right\}= \left\{ \begin{array}{rc} scale & 0 \\ 0 & scale \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \end{array} \right\} {xy?}={scale0?0scale?}{xy?}
將縮放變換的變換矩陣記為 SSS,則:
P′=SPP'=SP P=SP
則旋轉+縮放可以通過矩陣相乘寫到同一個矩陣中:
{x′y′}={cos(θ)×scalesin(θ)×scale?sin(θ)×scalecos(θ)×scale}{xy}\left\{ \begin{array}{rc} x' \\ y' \end{array} \right\}= \left\{ \begin{array}{rc} cos(\theta) \times scale & sin(\theta) \times scale \\ -sin(\theta) \times scale & cos(\theta) \times scale \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \end{array} \right\} {xy?}={cos(θ)×scale?sin(θ)×scale?sin(θ)×scalecos(θ)×scale?}{xy?}
即:P′=SRPP'=SRPP=SRP

注意旋轉和縮放順序是隨意的,不影響結果,這也可以通過代碼來驗證:

import numpy as nptheta = 0.8
scale = 2
rot = np.array([[np.cos(theta), np.sin(theta)],[-np.sin(theta), np.cos(theta)]
])sca = np.array([[scale, 0],[0, scale]
])print(np.allclose(rot @ sca, sca @ rot))
# 輸出:True

平移變換

平移變換可以表示為:
x′=x+oxy′=y+oyx'=x+ox\\ y'=y+oy x=x+oxy=y+oy
矩陣形式:
{x′y′}={1001}{xy}+{oxoy}\left\{ \begin{array}{rc} x' \\ y' \end{array} \right\}= \left\{ \begin{array}{rc} 1 & 0 \\ 0 & 1 \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \end{array} \right\} + \left\{ \begin{array}{rc} ox \\ oy \end{array} \right\} {xy?}={10?01?}{xy?}+{oxoy?}
可以發現,平移變換直接寫成矩陣形式,已經不是單純的矩陣相乘了,而是多了一個很麻煩的相加的操作。這就很難與我們之前的縮放+旋轉的操作合并到一起,該怎么辦呢?

我們可以增加一個維度,將二維的非齊次的形式轉換為三維的齊次的形式,即這個知乎回答中所提到的:增加一個維度之后,就可以在高維度通過線性變換來完成低維度的放射變換。(該回答將放射變換講的很形象,推薦閱讀)。

那么我們增加一維 (x,y,w)(x,y,w)(x,y,w),從而將點 PPP 表示為 P(xw,yw,1)P(\frac{x}{w},\frac{y}{w},1)P(wx?,wy?,1) ,這樣平移變換就也可以表示為齊次矩陣乘的形式:

{x′y′w}={10ox01oy001}{xy1}\left\{ \begin{array}{rc} x' \\ y' \\ w \\ \end{array} \right\}= \left\{ \begin{array}{rc} 1 & 0 & ox \\ 0 & 1 & oy \\ 0 & 0 & 1 \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \\ 1 \end{array} \right\} ????xyw?????=????100?010?oxoy1?????????xy1?????
最后我們得到縮放+旋轉+平移變換的矩陣表示(注意平移與縮放、旋轉的順序是不能隨意調換的):

{x′y′w}={cos(θ)×scalesin(θ)×scaleox?sin(θ)×scalecos(θ)×scaleoy001}{xy1}\left\{ \begin{array}{rc} x' \\ y' \\ w \end{array} \right\}= \left\{ \begin{array}{rc} cos(\theta) \times scale & sin(\theta) \times scale & ox \\ -sin(\theta) \times scale & cos(\theta) \times scale & oy \\ 0 & 0 & 1 \end{array} \right\} \left\{ \begin{array}{rc} x \\ y \\ 1 \end{array} \right\} ????xyw?????=????cos(θ)×scale?sin(θ)×scale0?sin(θ)×scalecos(θ)×scale0?oxoy1?????????xy1?????
將平移變換的變換矩陣記為 RRR ,則:P′=TSRPP'=TSRPP=TSRP ,可以將整個 warpaffine 三個變換操作的矩陣記為 MMM ,即:M=TSR,P′=MPM=TSR,\ \ P'=MPM=TSR,??P=MP

warpaffine矩陣變換的反變換

  • 旋轉矩陣的逆矩陣,即是其轉置:R?1=RTR^{-1}=R^TR?1=RT
  • 整個 warp affine 的三個變換求反變換,對整個變換矩陣求逆即可:P′=MP,P=M?1P′P'=MP,\ \ P=M^{-1}P'P=MP,??P=M?1P

目標檢測中的常用預處理

在目標檢測中,我們的預處理通常是先對圖像進行等比縮放,然后居中,多余部分填充,就類似下圖所展示的。

在這里插入圖片描述

我們將這個過程分為三個步驟:

  1. 等比縮放,矩陣 SSS 實現

在這里插入圖片描述

  1. 將圖片中心平移到左上坐標原點,矩陣 OOO 實現

在這里插入圖片描述

  1. 將圖片平移到目標位置的重心,矩陣 TTT 實現

在這里插入圖片描述

三步拆分法,看似麻煩了一點,實際上可以方便我們后續可能會需要到的更復雜的變換(比如在 OOO 平移后加入旋轉變換),并且便于記憶。

三步拆分法的矩陣表達:P′=TOSPP'=TOSPP=TOSP

我們直接寫出具體的矩陣:
scale=min(Dst.widthOrigin.width,Dst.heightOrigin.height)M={scale0?scale×Origin.width2+Dst.width20scale?scale×Origin.height2+Dst.height2}scale = min(\frac{Dst.width}{Origin.width}, \frac{Dst.height}{Origin.height}) \\ \\ M = \left\{ \begin{array}{ll} scale & 0 & -\frac{scale \times Origin.width}{2} + \frac{Dst.width}{2} \\ 0 & scale & -\frac{scale \times Origin.height}{2} + \frac{Dst.height}{2} \\ \end{array} \right\} scale=min(Origin.widthDst.width?,Origin.heightDst.height?)M={scale0?0scale??2scale×Origin.width?+2Dst.width??2scale×Origin.height?+2Dst.height??}

{x′y′}={scale0?scale×Origin.width2+Dst.width20scale?scale×Origin.height2+Dst.height2}{xy1}\left\{ \begin{array}{ll} x' \\ y' \\ \end{array} \right\}= \left\{ \begin{array}{ll} scale & 0 & -\frac{scale \times Origin.width}{2} + \frac{Dst.width}{2} \\ 0 & scale & -\frac{scale \times Origin.height}{2} + \frac{Dst.height}{2} \\ \end{array} \right\} \left\{ \begin{array}{ll} x \\ y \\ 1 \end{array} \right\} {xy?}={scale0?0scale??2scale×Origin.width?+2Dst.width??2scale×Origin.height?+2Dst.height??}????xy1?????

逆變換:
k=scaleb1=?scale×Origin.width2+Dst.width2b2=?scale×Origin.height2+Dst.height2x′=kx+b1y′=ky+b2x=x′?b1k=x′×1k+(?b1k)y=y′?b2k=y′×1k+(?b2k)M?1={1k0?b1k01k?b2k}k = scale \\ b1 = -\frac{scale \times Origin.width}{2} + \frac{Dst.width}{2} \\ b2 = -\frac{scale \times Origin.height}{2} + \frac{Dst.height}{2} \\ x' = kx + b1 \\ y' = ky + b2 \\ x = \frac{x' - b1}{k} = x'\times \frac{1}{k} + (-\frac{b1}{k}) \\ y = \frac{y' - b2}{k} = y'\times \frac{1}{k} + (-\frac{b2}{k}) \\ M^{-1} = \left\{ \begin{array}{ll} \frac{1}{k} & 0 & -\frac{b1}{k} \\ 0 & \frac{1}{k} & -\frac{b2}{k} \\ \end{array} \right\} k=scaleb1=?2scale×Origin.width?+2Dst.width?b2=?2scale×Origin.height?+2Dst.height?x=kx+b1y=ky+b2x=kx?b1?=x×k1?+(?kb1?)y=ky?b2?=y×k1?+(?kb2?)M?1={k1?0?0k1???kb1??kb2??}

warpaffine正逆變換代碼實驗

TODO

雙線性插值

線性插值

距離目標點越遠,影響就越小,因此權重是對面的距離占比。

如目標點距離冷水 0.6,距離熱水 0.4,則冷水權重為 0.4 ,熱水權重為 0.6 。

在這里插入圖片描述

p0 = 20    # 冷水
p1 = 100   # 熱水
pos = 0.6  # 應該多少度value = (1 - pos) * p0 + pos * p1
print(value)

雙線性插值

線性插值的二維版本,原理一直,只是權重從計算長度占比改為計算面積占比。

調色板,紅點對目標點(紫點)的影響權重即為對面的面積(紅框面積)占總面積的比例。

在這里插入圖片描述

高性能實現

為什么高性能?

  • 我們在操作每個像素的過程中,可以將模型需要的像素級預處理(如減均值除標準差、除以255、BGR通道轉換等)一并做了,避免多個操作分開來反復對每個像素進行循環訪問這種低效行為。
  • warpaffine 極其適合通過 cuda 核函數進行 GPU 加速。可以參考 repo 中的 preprocess_kernel.cu 。完整代碼比較長這里就不放了。
  • 以下是 warpaffine 雙線性插值的 Python 實現,供參考:
def pyWarpAffine(image, M, dst_size, constant=(0, 0, 0)):# 注意輸入的M矩陣格式,是Origin->Dst# 而這里需要的是Dst->Origin,所以要取逆矩陣M = cv2.invertAffineTransform(M)constant = np.array(constant)ih, iw   = image.shape[:2]dw, dh   = dst_sizedst      = np.full((dh, dw, 3), constant, dtype=np.uint8)irange   = lambda p: p[0] >= 0 and p[0] < iw and p[1] >= 0 and p[1] < ihfor y in range(dh):for x in range(dw):homogeneous = np.array([[x, y, 1]]).Tox, oy = M @ homogeneouslow_ox = int(np.floor(ox))low_oy = int(np.floor(oy))high_ox = low_ox + 1high_oy = low_oy + 1# p0     p1#      o# p2     p3pos = ox - low_ox, oy - low_oyp0_area = (1 - pos[0]) * (1 - pos[1])p1_area = pos[0] * (1 - pos[1])p2_area = (1 - pos[0]) * pos[1]p3_area = pos[0] * pos[1]p0 = low_ox, low_oyp1 = high_ox, low_oyp2 = low_ox, high_oyp3 = high_ox, high_oyp0_value = image[p0[1], p0[0]] if irange(p0) else constantp1_value = image[p1[1], p1[0]] if irange(p1) else constantp2_value = image[p2[1], p2[0]] if irange(p2) else constantp3_value = image[p3[1], p3[0]] if irange(p3) else constantdst[y, x] = p0_area * p0_value + p1_area * p1_value + p2_area * p2_value + p3_area * p3_value# 交換bgr  rgb# normalize ->  -mean /std# 1行代碼實現normalize , /255.0# bgr bgr bgr -> bbb ggg rrr# focus# focus offset, 1行代碼實現focusreturn dstcat1 = cv2.imread("cat1.png")
#acat1_cv, M, inv = align(cat1, (100, 100))
M = cv2.getRotationMatrix2D((0, 0), 30, 0.5)
acat1_cv = cv2.warpAffine(cat1, M, (100, 100))
acat1_py = pyWarpAffine(cat1, M, (100, 100))plt.figure(figsize=(10, 10))
plt.subplot(1, 2, 1)
plt.title("OpenCV")
plt.imshow(acat1_cv[..., ::-1])plt.subplot(1, 2, 2)
plt.title("PyWarpAffine")
plt.imshow(acat1_py[..., ::-1])

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/532477.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532477.shtml
英文地址,請注明出處:http://en.pswp.cn/news/532477.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LeetCode-10 正則表達式匹配

LeetCode-10 正則表達式匹配 動態規劃 10. 正則表達式匹配 dp數組含義&#xff1a;dp[i][j]dp[i][j]dp[i][j] 表示 s[0:i?1]s[0:i-1]s[0:i?1] 能否被 p[0:j?1]p[0:j-1]p[0:j?1] 成功匹配。 狀態轉移方程 &#xff1a; 如果 s[i?1]p[j?1]s[i-1]p[j-1]s[i?1]p[j?1] …

shell if判斷和for循環常見寫法

shell if判斷和for循環常見寫法 轉自&#xff1a; Shell中for循環的幾個常用寫法 Shell中if 條件判斷總結 if常見寫法 一、if的基本語法: if [ command ];then符合該條件執行的語句 elif [ command ];then符合該條件執行的語句 else符合該條件執行的語句 fibash shell會按順序…

關于pytorch使用多個dataloader并使用zip和cycle來進行循環時出現的顯存泄漏的問題

關于pytorch使用多個dataloader并使用zip和cycle來進行循環時出現的顯存泄漏的問題 如果我們想要在 Pytorch 中同時迭代兩個 dataloader 來處理數據&#xff0c;會有兩種情況&#xff1a;一是我們按照較短的 dataloader 來迭代&#xff0c;長的 dataloader 超過的部分就丟棄掉…

neovim及coc.nvim自動補全初探

neovim及coc.nvim自動補全初探 安裝 # mac # 安裝 brew install neovim # 查看neovim安裝路徑 brew list nvim# ubuntu apt install neovim習慣了打開 vi/vim 的方式&#xff0c;可以用個 alias 在 ~/.zshrc 中設置一下&#xff1a; alias vi"nvim"插件 vim-plug…

sed 簡明教程

sed 簡明教程 轉自&#xff1a;https://coolshell.cn/articles/9104.html awk于1977年出生&#xff0c;今年36歲本命年&#xff0c;sed比awk大2-3歲&#xff0c;awk就像林妹妹&#xff0c;sed就是寶玉哥哥了。所以 林妹妹跳了個Topless&#xff0c;他的哥哥sed坐不住了&#xf…

awk 簡明教程

awk 簡明教程 轉自&#xff1a;https://coolshell.cn/articles/9070.html 有一些網友看了前兩天的《Linux下應該知道的技巧》希望我能教教他們用awk和sed&#xff0c;所以&#xff0c;出現了這篇文章。我估計這些80后的年輕朋友可能對awk/sed這類上古神器有點陌生了&#xff0c…

應該知道的LINUX技巧

應該知道的LINUX技巧 轉自&#xff1a;https://coolshell.cn/articles/8883.html 這篇文章來源于Quroa的一個問答《What are some time-saving tips that every Linux user should know?》—— Linux用戶有哪些應該知道的提高效率的技巧。我覺得挺好的&#xff0c;總結得比較好…

[深度][PyTorch] DDP系列第一篇:入門教程

[深度][PyTorch] DDP系列第一篇&#xff1a;入門教程 轉自&#xff1a;[原創][深度][PyTorch] DDP系列第一篇&#xff1a;入門教程 概覽 想要讓你的PyTorch神經網絡在多卡環境上跑得又快又好&#xff1f;那你definitely需要這一篇&#xff01; No one knows DDP better than I…

[深度][PyTorch] DDP系列第二篇:實現原理與源代碼解析

[深度][PyTorch] DDP系列第二篇&#xff1a;實現原理與源代碼解析 轉自&#xff1a;https://zhuanlan.zhihu.com/p/187610959 概覽 想要讓你的PyTorch神經網絡在多卡環境上跑得又快又好&#xff1f;那你definitely需要這一篇&#xff01; No one knows DDP better than I do! …

[深度][PyTorch] DDP系列第三篇:實戰與技巧

[深度][PyTorch] DDP系列第三篇&#xff1a;實戰與技巧 轉自&#xff1a;https://zhuanlan.zhihu.com/p/250471767 零. 概覽 想要讓你的PyTorch神經網絡在多卡環境上跑得又快又好&#xff1f;那你definitely需要這一篇&#xff01; No one knows DDP better than I do! – – …

PIL、OpenCV中resize算子實現不同的問題

PIL、OpenCV中resize算子實現不同的問題 測試圖像&#xff1a;https://raw.githubusercontent.com/TropComplique/ssd-pytorch/master/images/dogs-and-cats.jpg &#xff08;直接 wget 可獲得&#xff09; 測試版本&#xff1a; opencv-python 4.4.0.46Pillow 8.0.1 測試代…

mac X11 XQuartz的安裝與使用

mac X11 XQuartz的安裝與使用 本地系統&#xff1a;MacOS 12.4 遠程主機系統&#xff1a;Ubuntu 18.04 命令說明 ssh命令 ssh 命令大家很熟悉了&#xff0c;這里僅介紹與 X11 forwarding 相關的幾個選項。 本部分譯自 ssh 命令手冊&#xff0c;可見 man ssh -X &#xf…

機器學習:系統設計與實現 分布式訓練

機器學習系統:設計與實現 分布式訓練 轉自&#xff1a;https://openmlsys.github.io/chapter_distributed_training/index.html 隨著機器學習的進一步發展&#xff0c;科學家們設計出更大型&#xff0c;更多功能的機器學習模型&#xff08;例如說&#xff0c;GPT-3&#xff09;…

Linux命令行及各常用工具代理設置

Linux命令行及各常用工具代理設置 命令行代理設置 1 通過命令行指定 直接為當前命令行設置代理 對當前終端的全部工具&#xff08;apt、curl、wget、git 等全都有效&#xff09;以下僅以 http 代理為例&#xff0c;如果是其他協議&#xff08;如 socks 等&#xff09;自行改…

VimScript 五分鐘入門(翻譯)

VimScript 五分鐘入門&#xff08;翻譯&#xff09; 轉自&#xff1a;https://zhuanlan.zhihu.com/p/37352209 譯注&#xff1a;折騰 Vim 當然要能看懂和改寫相關腳本&#xff0c;而中文資料匱乏&#xff0c;缺一個提綱挈領的教程。本文翻譯自 Andrew Scala 的 《Five Minute V…

C++多線程推理、生產者消費者模式封裝

C多線程推理、生產者消費者模式封裝 tensorRT從零起步邁向高性能工業級部署&#xff08;就業導向&#xff09; 課程筆記&#xff0c;講師講的不錯&#xff0c;可以去看原視頻支持下。 深度學習推理中的多線程知識概覽 本章介紹的多線程主要是指算法部署時所涉及的多線程內容&a…

在Python中調用C/C++:cython及pybind11

在Python中調用C/C&#xff1a;cython及pybind11 轉自&#xff1a;https://zhuanlan.zhihu.com/p/442935082 Python寫起來非常方便, 但面對大量for循環的時候, 執行速度有些捉急. 原因在于, python是一種動態類型語言, 在運行期間才去做數據類型檢查, 這樣效率就很低(尤其是大規…

Pytorch導出onnx模型,C++轉化為TensorRT并實現推理過程

Pytorch導出onnx模型&#xff0c;C轉化為TensorRT并實現推理過程 前言 本文為旨在實現整個Python導出PyTorch模型&#xff0c;C轉化為TensorRT并實現推理過程過程&#xff0c;只與模型推理&#xff0c;模型部署相關&#xff0c;不涉及模型訓練。為突出整個部署過程而非具體模…

從零Makefile落地算法大項目,完整案例教程

從零Makefile落地算法大項目&#xff0c;完整案例教程 轉自&#xff1a;從零Makefile落地算法大項目&#xff0c;完整案例教程 作者&#xff1a;手寫AI 前言 在這里&#xff0c;你能學到基于Makefile的正式大項目的使用方式和考慮&#xff0c;相信我&#xff0c;其實可以很簡單…

PyTorch擴展自定義PyThonC++(CUDA)算子的若干方法總結

PyTorch擴展自定義PyThon/C(CUDA)算子的若干方法總結 轉自&#xff1a;https://zhuanlan.zhihu.com/p/158643792 作者&#xff1a;奔騰的黑貓 在做畢設的時候需要實現一個PyTorch原生代碼中沒有的并行算子&#xff0c;所以用到了這部分的知識&#xff0c;再不總結就要忘光了 &a…