文章目录
- 开发环境
- 工程目录
- web端
- 遇到的问题
-
- JS与C++传值
- [Uncaught TypeError: Module._malloc is not a function](#Uncaught TypeError: Module._malloc is not a function)
- canvas像素RGBA四通道
- 经验&&教训
- 参考
最近因为项目原因,研究了一下WebAssembly。2015年上线与JS、HTML、CSS并称web界四语言,额,虽然已经上线快10年,但是研究的人好少,注定这个探索之路是崎岖的。(事实也是这样,已经耗进去快2周了,人都麻了-_-||)
这篇文章主要介绍在引用图像增强算法时遇到的一些问题,因涉及算法内容,以下代码将做一些处理。。。javaer表示知识的海洋真广阔呀~
开发环境
为啥要把开发环境放在第一位呢,这里面也是采了无数的坑。
开发工具 | 版本 |
---|---|
Ubuntu | 18.04 |
emscripten | 3.1.55 |
cmake | 3.28.3 |
opencv | 3.2.0 |
工程目录
┌─imageAlgorithm 项目名称
│─build 编译文件(emcmake和emmake后的产物)
│ └─CMakeFile
│ │ └─...
│ └─cmake_install.cmake
│ └─CMakeCache.txt
│ └─Makefile
│ └─imageAlgorithm.js
│ └─imageAlgorithm.wasm
├─main.cpp 主入口
├─algorithm1.cpp 算法1
├─...
├─algorithmN.cpp 算法N
├─head1.h 算法1头文件
├─headN.h
├─CMakeLists.txt
CMakeLists.txt
cmake_minimum_required( VERSION 3.8 )
set( CMAKE_CXX_STANDARD 17 )
project( imageAlgorithm )
# Needed for opencv2/opencv.hpp
include_directories( /root/wasm/opencv-demo/opencv/include )
# Needed by opencv.hpp for opencv2/opencv_modules.hpp
include_directories( /root/wasm/opencv-demo/opencv/platforms/js/build_wasm )
# Needed by opencv_modules.hpp for every module
file( GLOB opencv_include_modules "/root/wasm/opencv-demo/opencv/modules/*/include" )
include_directories( ${opencv_include_modules} )
# 此处将所有.cpp文件加入,此处省略下,可以把参与编译的cpp文件全部放在一个文件夹下面
add_executable( imageAlgorithm main.cpp algorithm1.cpp algorithm2.cpp ... algorithmN.cpp)
# Link to opencv.js precompiled libraries
file( GLOB opencv_js "/root/wasm/opencv-demo/opencv/platforms/js/build_wasm/lib/*.a" )
target_link_libraries( imageAlgorithm ${opencv_js} )
set_target_properties(imageAlgorithm PROPERTIES LINK_FLAGS "-s EXIT_RUNTIME=1 -O3 -sNO_DISABLE_EXCEPTION_CATCHING -sALLOW_MEMORY_GROWTH -s EXPORTED_FUNCTIONS=\"['_postProcess', '_malloc', '_free', '_postProcess1']\"")
main.cpp
在编写过程中发现emscripten对C++支持度不够,不能直接暴露类的方法,所以不得不在最外面在包裹一层main.cpp
cpp
#include "algorithm.h"
#include <emscripten.h>
extern "C" void postProcess1(unsigned char* image, int width, int height) {
// 创建一个 Mat 对象来存储传入的图像数据
cv::Mat mat(height, width, CV_8UC4, image);
// 将红色和绿色通道全部置零
cv::MatIterator_<cv::Vec4b> it, end;
for (it = mat.begin<cv::Vec4b>(), end = mat.end<cv::Vec4b>(); it != end; ++it) {
(*it)[2] = 0; // 红色通道置零
(*it)[1] = 0; // 绿色通道置零
}
}
web端
使用emcc+cmake执行编译,将生成后的js和wasm文件拷贝至web项目中,web项目结构如下:
┌─web 项目名称
│─imageAlgorithm.js
│ imageAlgorithm.wasm
│ index.html
index.html
主要变量的说明:
- canvas1为原图,图片数据imageData
- canvas2为经过算法处理后的图,图片数据postImageData
javascript
// 从canvas1中获取图像数据
const imageData = ctx.getImageData(0, 0, width, height);
// 获取图像的像素数组
const pixelData = imageData.data;
var pixels = new Uint8Array(pixelData);
// 分配内存
var dataptr = Module._malloc(pixelData.length);
Module.HEAPU8.set(pixels, dataptr);
// 调用C/C++算法处理
Module._postProcess1(dataptr, width, height);
// 设置canvas2的image对象
var postImageData = ctxNew.createImageData(width, height);
// 将像素数组数据从内存复制到 ImageData 对象
var imageDataArray = Module.HEAPU8.subarray(dataptr, dataptr + width * height * 4); // 假设每个像素有 RGBA 四个通道
postImageData.data.set(imageDataArray);
// 将 ImageData 对象绘制到 Canvas 上
ctxNew.putImageData(postImageData, 0, 0);
// 释放内存
Module._free(dataptr)
效果图
遇到的问题
JS与C++传值
真的没想到啊,两者之间只能传递Number!!!
需要在JavaScript与C/C++之间交换大块的数据时,直接使用参数传递数据显然不可行,此时可以通过内存来交换数据
Uncaught TypeError: Module._malloc is not a function
在JS中调用Module._malloc
报错,理论上,Emscripten应该会把C/C++所有的默认方法都exported出来,为啥会报这个错误呢?
BUG: Uncaught TypeError: Module._malloc is not a function
在emscripten-core项目issue中找到相同问题,有个大佬回复了:版本升级到3.1.31后为了减小emscripten导出体积,把这些默认的导出配置cut掉了,需要开发者自行配置,看官方的changelog
于是乎改一下CMakeLists中的导出命令行,在EXPORTED_FUNCTIONS
中加入_malloc
和_free
canvas像素RGBA四通道
在将前面所有坑都淌过一遍后,觉得自己离最终胜利只有0.1m的距离了,然而现实很残酷,调用算法后的效果图明显不是那么回事!于是乎,不得不从数据对比上下手,将web端和C++算法里的数据取间隔打印,然后终于发现问题点了!
从canvas获取的像素数据是RGBA,而算法中接收时使用CV_8UC3创建cv::Mat对象(这不是RGB么)
最终方案:在算法中进行处理,保证算法处理完图像指针的内容是4通道!
经验&&教训
- 官网上都是解决方案,耐心看
- 刚学习某方面的知识,可以去github上找demo
- 跟踪问题时,先从大的方面定位,然后逐步缩小范围
- 尝试在源码github的issue中寻找解决方案
- stackoverflow多看跟帖,必然有很多大佬回复
参考
一些有用的学习文档
WebAssembly
Emscripten-FAQ
emscripten-core github issues
C/C++面向WebAssembly编程
最后在吐槽一句,emscripten的知识真少啊~~~~ WebAssembly高级用法待后续探索,TBC~