一 背景
二 原理篇
1 wasm内存布局
2 Javascript与Python的互访
const arr_pyproxy = pyodide.globals.get('arr') // arr是python里的一个全局对象
const arr_pyproxy = pyodide.globals.get('arr') // arr是python里的一个全局对象
import jsfrom js import foo # foo是Javascript里的一个全局对象
import pyodideimport js.cv as cv2print(dir(cv2))
function loadJS( url, callback ){var script = document.createElement('script'),fn = callback || function(){};script.type = 'text/javascript';script.onload = function(){fn();};script.src = url;document.getElementsByTagName('head')[0].appendChild(script);}// 加载opencvloadJS('https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/opencv/opencv.js', function(){console.log('js load ok');});// 加载推理引擎onnxruntime.js。当然也可以使用其他推理引擎loadJS('https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/onnxruntime/onnx.min.js', function(){console.log('js load ok');});// 初始化python运行环境loadJS('https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/pyodide/0.18.0/pyodide.js', function(){console.log('js load ok');});pyodide = await loadPyodide({ indexURL : "https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/pyodide/0.18.0/"});await pyodide.loadPackage(['micropip']);
location ~ ^/wasm/ {add_header 'Access-Control-Allow-Origin' "*";add_header 'Access-Control-Allow-Credentials' "true";root /path/to/wasm_dir;header_filter_by_lua 'uri = ngx.var.uriif string.match(uri, ".js$") == nil thenngx.header["Content-type"] = "application/wasm"end';}
await pyodide.runPythonAsync(`import micropipmicropip.install(["numpy", "Pillow"])`);await pyodide.runPythonAsync(`import pyodideimport js.cv as cv2import js.onnx as onnxruntimeimport numpy as np`);
await pyodide.runPythonAsync(`# 构造一个1080p图片h,w = 1080,1920img = np.arange(h * w * 3, dtype=np.uint8).reshape(h, w, 3)# 使用cv2.resize将其缩小为1/10# 原python代码:small_img = cv2.resize(img, (h_small, w_small))# 改成调用opencv.js:h_small,w_small = 108, 192mat = cv2.matFromArray(h, w, cv2.CV_8UC3, pyodide.to_js(img.reshape(h * w * 3)))dst = cv2.Mat.new(h_small, w_small, cv2.CV_8UC3)cv2.resize(mat, dst, cv2.Size.new(w_small, h_small), 0, 0, cv2.INTER_NEAREST)small_img = np.asarray(dst.data.to_py()).reshape(h_small, w_small, 3)`);
await pyodide.runPythonAsync(`# 使用cv2.findContours来检测轮廓。假设mask为二维numpy数组,只有0、1两个值# 原python代码:contours = cv2.findContours(mask, cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)# 改成调用opencv.js:contours_jsproxy = cv2.MatVector.new() # cv2.Mat数组,对应opencv.js中的 contours = new cv.MatVector()语句hierarchy_jsproxy = cv2.Mat.new()mat = cv2.matFromArray(mask.shape[0], mask.shape[1], cv2.CV_8UC1, pyodide.to_js(mask.reshape(mask.size)))cv2.findContours(mat, pyodide.to_js(contours_jsproxy), pyodide.to_js(hierarchy_jsproxy), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)# contours js格式转python格式contours = []for i in range(contours_jsproxy.size()):c_jsproxy = contours_jsproxy.get(i)c = np.asarray(c_jsproxy.data32S.to_py()).reshape(c_jsproxy.rows, c_jsproxy.cols, 2)contours.append(c)`);
await pyodide.runPythonAsync(`model_url="onnx模型的地址"session = onnxruntime.InferenceSession.new()session.loadModel(model_url)session.run(......)`);
// 创建挂载点pyodide.FS.mkdir('/mnt');// 挂载文件系统pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, '/mnt');// 写入一个文件pyodide.FS.writeFile('/mnt/test.txt', 'hello world');// 真正的保存文件到持久文件系统pyodide.FS.syncfs(function (err) {console.log(err);});
// 创建挂载点pyodide.FS.mkdir('/mnt');// 挂载文件系统pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, '/mnt');// 写入一个文件pyodide.FS.writeFile('/mnt/test.txt', 'hello world');// 真正的保存文件到持久文件系统pyodide.FS.syncfs(function (err) {console.log(err);});
micropip.install("https://foo.com/bar-1.2.3-xxx.whl")from bar import ...
https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/pyodide/test.html
2、文档:
https://pyodide.org/en/stable/usage/type-conversions.html
https://emscripten.org/docs/api_reference/Filesystem-API.html
“Kubernetes 难点攻破训练营系列”的初心是和开发者们一起应对学习和使用 K8s的挑战。这一次,我们从容器持久化存储开始。
点击阅读原文查看详情~