WebAssembly探索篇(四)emcc和cmake编译opencv复杂案例

文章目录

最近因为项目原因,研究了一下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++传值

2.4 JavaScript与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通道!

经验&&教训

  1. 官网上都是解决方案,耐心看
  2. 刚学习某方面的知识,可以去github上找demo
  3. 跟踪问题时,先从大的方面定位,然后逐步缩小范围
  4. 尝试在源码github的issue中寻找解决方案
  5. stackoverflow多看跟帖,必然有很多大佬回复

参考

一些有用的学习文档
WebAssembly
Emscripten-FAQ
emscripten-core github issues
C/C++面向WebAssembly编程

最后在吐槽一句,emscripten的知识真少啊~~~~ WebAssembly高级用法待后续探索,TBC~

相关推荐
**K6 分钟前
业务需求方面
c++·objective-c
初晴~35 分钟前
【数据结构】栈与队列
java·c语言·数据结构·算法·链表
Wise cas42941 分钟前
C语言笔记34 •单链表经典算法OJ题-6.环形链表的约瑟夫问题•
c语言·笔记·算法
黎相思1 小时前
单链表算法 - 链表分割
c语言·数据结构·算法·链表
jllws11 小时前
C++基础语法:STL之容器(1)--容器概述和序列概述
开发语言·c++
Java资深爱好者2 小时前
extern “C“的定义以及它在C++代码中有什么作用
java·c语言·c++
@Tianwx2 小时前
Linux C++ 056-设计模式之迭代器模式
linux·c++·设计模式
jjb-coder2 小时前
iterator(迭代器模式)
c++·迭代器模式
埋头编程~2 小时前
【C语言】详解结构体(下)(位段)
c语言·开发语言·算法·visual studio
FS_tar2 小时前
SMU Summer 2024 Contest Round 4
c++