Gpu.js 在醫(yī)學(xué)檢查影像顯示-Web 版的運用
本文轉(zhuǎn)載自微信公眾號「微醫(yī)大前端技術(shù)」,作者楊廣。轉(zhuǎn)載本文請聯(lián)系微醫(yī)大前端技術(shù)公眾號。
前言
醫(yī)學(xué)檢查影像顯示里邊,調(diào)窗是一個基礎(chǔ)的常用操作:通過滑動鼠標來改變窗寬窗位的大小,窗寬(寬度)、窗位(中心點)其實就是一個像素數(shù)值的區(qū)間,圖像的所有像素點的像素值在這個區(qū)間的就顯示,不在區(qū)間的就不顯示。
在 web 版閱片器(圖像顯示工具)的開發(fā)工程中發(fā)現(xiàn),在對 dr、dx 這種大圖(圖像普遍是 2000px 左右寬高的圖像,每個像素大小是 16 位或者 32 位的)進行調(diào)窗時,瀏覽器直接卡死,圖像顯示變化有很大延遲。后來查閱資料,使用 gpujs 來實現(xiàn)調(diào)窗時圖像的處理,結(jié)果:調(diào)窗時圖像顯示變化變得比較線性,性能估計提升 5/6 倍,能滿足業(yè)務(wù)需求。當(dāng)然,圖像的一些高級處理,都是可以用 gpujs 來實現(xiàn),調(diào)窗只是一個點。
使用前效果:
使用后效果:
什么是 GPU.js?
GPU.js 是用于 Web 和 Node.js 中的 GPGPU(GPU 上的通用計算)的 JavaScript 加速庫。GPU.js 自動將簡單的 JavaScript 函數(shù)轉(zhuǎn)換為著色器語言并編譯它們,以便它們在您的 GPU 上運行。使用 GPU 執(zhí)行大規(guī)模并行 GPGPU 計算,當(dāng) GPU 不可用時,優(yōu)雅的純 JavaScript 回退。
GPU,全稱 Graphics Processing Unit,即圖像處理器,早期主要用于顯示圖像使用。因為圖像處理主要偏簡單的矩陣運算,邏輯判斷等很少,因此 GPU 的設(shè)計跟 CPU 架構(gòu)不一樣,也因此做到一個 GPU 上可以有很多計算單元,可以進行大量并行計算。網(wǎng)上找到一個視頻,應(yīng)該是 Nvidia 某年的產(chǎn)品發(fā)布會,形象地演示了 CPU 跟 GPU 的區(qū)別。http://v.youku.com/vshow/idXNDcyNTc1MjQ4==.html 。知乎上也有對 CPU 和 GPU 的對比 https://www.zhihu.com/question/19903344
例如用 GPU.js 編寫的矩陣乘法(對大小為 512 x 512 的 2 個矩陣執(zhí)行矩陣乘法)。把計算 512 x 512 矩陣(2D 數(shù)組)的單個元素的 javascript 函數(shù)轉(zhuǎn)換而來的 GPU 加速內(nèi)核。內(nèi)核函數(shù)在 GPU 上串聯(lián)運行,通常會導(dǎo)致非??焖俚挠嬎?你可以在這里運行一個基準測試。通常,它的運行速度會快 1-15 倍,具體取決于您的硬件。
由于架構(gòu)設(shè)計不一樣,GPU 很適合做簡單的并發(fā)計算,應(yīng)用于圖像處理、深度學(xué)習(xí)等領(lǐng)域能大大加快速度。當(dāng)然直接用 gpu 去開發(fā)程序很難編寫,一般都是由特殊編譯器將代碼編譯成可以在 gpu 上執(zhí)行的代碼。本文提到的 gpu.js 就是在前端將 js 的一個子集編譯成能在 webgl 上執(zhí)行的一個編譯器。更多使用場景 demo 請參照官網(wǎng) https://gpu.rocks/#/examples。
在使用上遇到的坑
使用 npm 引入 gpu.js 包,發(fā)現(xiàn)項目運行不起來,可能和機器有關(guān)系,后來改成直接用 browser 方式引入 js 文件
我們是使用 canvas 方式來直接輸出像素數(shù)據(jù)的,對不同大小的圖像要使用不同大小的 canvas,使用同一個 canvas 會有些像素清不掉,試用了其他方法清除:沒有起到效果
canvas 方式像素數(shù)據(jù)的原點位置,原點是在左下角
創(chuàng)建內(nèi)核,內(nèi)核里邊的函數(shù)或者自定義函數(shù),在打包過程中被壓縮、簡化處理后會出現(xiàn)問題:被轉(zhuǎn)換后的某些運算符 gpu.js 不支持。所以 js 的壓縮轉(zhuǎn)化規(guī)則要做具體的設(shè)置
因為每個設(shè)備的 gpu 特性不一樣,使用 gpujs 庫可能有未知異常,總之加上 try { } catch () { 普通處理 cpu } 能解決大多數(shù)問題。
- # 在 index.html 中引入 gpu.js
- <script type="text/javascript" src="./static/gpu-browser.min.js"></script>
- # 開始使用
- var gpu = new GPU();
- gpu.addFunction(function1) # 添加自定義函數(shù)
- gpu.addFunction(function12) # 添加自定義函數(shù)
- # 創(chuàng)建 Kernel
- var kernel = gpu.createKernel(function(參數(shù) 1,參數(shù) 2,...) {
- # 調(diào)窗實現(xiàn)算法
- #
- #
- #
- },
- {
- graphical: true,
- output: outputsize
- })
- # 執(zhí)行 Kernel
- kernel(入?yún)?shù) 1,入?yún)?shù) 2,...);
- var canvasRender = kernel.canvas;
- # 直接顯示 canvasRender, ....
官方使用場景 demo
地址:https://gpu.rocks/#/examples
An example with the shiny new v2 of GPU.js, "Cosmic Jellyfish"
A simple example to load an image into GPU.js.
GPU Accelerated Heatmap using GPU.js
A simple example wherein colors slowly fade in and fade out.
Browser
- <script src="dist/gpu-browser.min.js"></script>
- <script>
- // GPU is a constructor and namespace for browser
- const gpu = new GPU();
- const multiplyMatrix = gpu.createKernel(function(a, b) {
- let sum = 0;
- for (let i = 0; i < 512; i++) {
- sum += a[this.thread.y][i] * b[i][this.thread.x];
- }
- return sum;
- }).setOutput([512, 512]);
- const c = multiplyMatrix(a, b);
- </script>
Node
- const { GPU } = require('gpu.js');
- const gpu = new GPU();
- const multiplyMatrix = gpu.createKernel(function(a, b) {
- let sum = 0;
- for (let i = 0; i < 512; i++) {
- sum += a[this.thread.y][i] * b[i][this.thread.x];
- }
- return sum;
- }).setOutput([512, 512]);
- const c = multiplyMatrix(a, b);
示例
在本例中,兩個 512x512 矩陣(二維數(shù)組)相乘。計算是在 GPU 上并行完成的。
1.生成矩陣
- const generateMatrices = () => {
- const matrices = [[], []]
- for (let y = 0; y < 512; y++){
- matrices[0].push([])
- matrices[1].push([])
- for (let x = 0; x < 512; x++){
- matrices[0][y].push(Math.random())
- matrices[1][y].push(Math.random())
- }
- }
- return matrices
- }
2.創(chuàng)建“內(nèi)核”
- const gpu = new GPU();
- const multiplyMatrix = gpu.createKernel(function(a, b) {
- let sum = 0;
- for (let i = 0; i < 512; i++) {
- sum += a[this.thread.y][i] * b[i][this.thread.x];
- }
- return sum;
- }).setOutput([512, 512])
3.以矩陣為參數(shù)調(diào)用內(nèi)核
- const matrices = generateMatrices()
- const out = multiplyMatrix(matrices[0], matrices[1])
4.輸出矩陣
console.log(out[y][x]) // Logs the element at the xth row and the yth column of the matrix
console.log(out[10][12]) // Logs the element at the 10th row and the 12th column of the output matri
小結(jié)
gpu.js 是在前端將 js 的一個子集編譯成能在 webgl 上執(zhí)行的一個編譯器,簡單、實用,能快速出結(jié)果,在對性能要求高、又耗時的運算可以考慮使用 gpu.js 來實現(xiàn)。
參考資料
官網(wǎng) https://gpu.rocks/#/
github https://github.com/gpujs/gpu.js
更多例子 https://gpu.rocks/#/examples
楊廣: 微醫(yī)前端技術(shù)部前端工程師。有志成為一名全棧開發(fā)工程師甚至架構(gòu)師,路漫漫,吾求索。生活中通過健身釋放壓力,思考問題。