沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 必看导言

    • 就业的核心问题

      • 课件
    • 表现力的训练

      • 课件
  • 简历制作

    • 课件
    • 简历准备课堂笔记
    • 个人信息课堂笔记
    • 求职意向课堂笔记
    • 技术栈课堂笔记
    • 教育经历课堂笔记
    • 个人优势与评价课堂笔记
    • 工作经历课堂笔记
    • 项目
    • 项目亮难点问题

    • 项目经历

  • 项目准备

    • 课件
    • 课件
  • 技术重点

    • 划重点

    • 非技术环节
  • 简历投递和面试准备

    • 简历投递
    • 面试准备
  • 笔面试环节知识讲解
  • 项目准备
  • 难点攻关
  • 图像处理
  • 课件资料
luzhichang
2024-09-18
目录

01-技术讲解

# 图像处理技术讲解

# 什么问题

在线处理图片的时候,会涉及到复杂的操作:

  • 添加图片滤镜:高斯模糊、锐化、浮雕等效果
  • 图片变换:需要改变图像本身,而非仅仅改变显示方式。
  • 图片压缩
  • 图片拼接
  • ....

遇到这样的需求的时候,就需要 专业的图像处理库 来进行处理。

问题:使用这些图像处理库来进行处理的时候,经常会遇到卡顿、耗时的问题。

原因:图像处理库里面的算法涉及到大量的数学运算,这是计算密集型任务。JS 本身不擅长处理计算密集型任务,因为 JS 是单线程的,在主线程上面执行计算密集型任务,会阻塞其他操作,特别是用户交互相关的操作一旦被阻塞,给人的感觉就是卡顿。

# 解决思路

  • 服务器端来解决:服务器端可以使用支持多线程的语言来进行处理,但是这里涉及到网络通信,这又是一个不确定因素。
  • 客户端段来解决:
    • 使用 Web Wokrer
    • 使用 WebAssembly
    • 优化算法(不做考虑)

综合思考下来,考虑前面两个 Web Worker 和 WebAssembly 结合起来,提升图像处理性能。

# 技术细节

1. WebAssembly

WebAssembly 是二进制格式,它接近于机器码,运行时性能接近于原生代码。

WebAssembly 可以与 JavaScript 互操作,允许开发者在网页中使用其他语言(如 C/C++、Rust)编写和性能相关的关键的代码,并将其编译成 WebAssembly 模块,以显著提高计算密集型任务(如图像处理)的性能。

2. 将图像算法迁移WebAssembly里面

这里可以借助 Emscripten,这是一个内置了 LLVM 工具链编译器,这个编译器就可以将 C/C++ 代码编译为 WebAssembly

这里选择将 C/C++ 知名的图像处理库 OpenCV,借助 Emscripten 将其编译为 WebAssembly

OpenCV 的一个示例代码:

// 一个简单的 C++ 图像处理函数,比如将图像转换为灰度图。
#include <cstdint>

extern "C" {
  void grayscale(uint8_t* image, int width, int height) {
     for (int y = 0; y < height; ++y) {
         for (int x = 0; x < width; ++x) {
             int index = (y * width + x) * 4;
             uint8_t r = image[index];
             uint8_t g = image[index + 1];
             uint8_t b = image[index + 2];
             uint8_t gray = 0.299 * r + 0.587 * g + 0.114 * b;
             image[index] = gray;
             image[index + 1] = gray;
             image[index + 2] = gray;
         }
     }
  }
}

使用 Emscripten 进行编译:

emcc image_processing.cpp -O3 -s WASM=1 -s MODULARIZE=1 -s 'EXPORT_NAME="createWasmModule"' -o image_processing.js

编译后会生成 WebAssembly 模块,这个模块由两个部分组成:

  • 二进制格式的 .wasm 文件
  • 一个 js 文件,这个 js 文件是一个胶水代码
image-20240611170957893

编译完成后,就可以动态的在 JS 中加载 WebAssembly 模块:

// index.js
async function loadWasmModule() {
  // 动态加载 Wasm 模块的胶水代码
  const module = await import('./image_processing.js');
  return module();
}

async function processImage(imageData) {
  const wasmModule = await loadWasmModule();
  // 调用 Wasm 模块里面的方法,这个其实就是 opencv 图像处理库所提供的图像算法
  const grayscale = wasmModule.cwrap('grayscale', null, ['number', 'number', 'number']);

  const width = imageData.width;
  const height = imageData.height;
  const data = new Uint8Array(imageData.data.buffer);

  // 使用 Wasm 模块处理图像
  grayscale(data.byteOffset, width, height);

  // 更新图像数据
  imageData.data.set(data);
}

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = 'path/to/image.jpg';
img.onload = () => {
  ctx.drawImage(img, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  processImage(imageData).then(() => {
     ctx.putImageData(imageData, 0, 0);
  });
};

3. 使用 Web Wokrer做进一步的优化

Web Worker 允许在主线程之外运行脚本,以避免阻塞用户界面。在图像处理过程中,可以将计算密集型任务放到 Web Worker 中执行。通过 postMessage 方法,可以将图像数据传递给 Web Worker 进行处理,处理完成后再将结果返回主线程,从而避免页面卡顿,提高用户体验。

具体的步骤如下:

  1. 创建 Web Worker 并传递图像数据
  2. 在 Web Worker 中调用 WebAssembly 模块提供的图像处理算法来对图像进行处理
  3. 将处理完后的图像数据传回给主线程并且更新 Canvas

4. 使用 OffscreenCanvas 再一次进行优化

OffscreenCanvas 允许在主线程之外的工作线程绘制图像。

使用了 OffscreenCanvas 之后,就不需要将处理后的图像数据传回主线程,因为 OffscreenCanvas 已经包含了绘制内容,可以自动更新主线程的显示。

示例代码:

// 主线程代码
const canvas = document.getElementById('canvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('worker.js');

// 将 OffscreenCanvas 传递给 Web Worker
worker.postMessage({ canvas: offscreen }, [offscreen]);

// 加载图像并发送给 Worker 进行处理
const img = new Image();
img.src = 'path/to/image.jpg';
img.onload = () => {
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    worker.postMessage({ imageData });
};
// worker.js
self.onmessage = async (event) => {
    const { canvas, action } = event.data;

    if (action === 'processImage' && canvas) {
        const ctx = canvas.getContext('2d');

        // 加载和初始化 Wasm 模块
        const module = await import('./image_processing.js');
        const wasmModule = await module();
        const grayscale = wasmModule.cwrap('grayscale', null, ['number', 'number', 'number']);

        // 获取图像数据进行处理
        const width = canvas.width;
        const height = canvas.height;
        const imageData = ctx.getImageData(0, 0, width, height);
        const data = new Uint8Array(imageData.data.buffer);

        // 调用 Wasm 函数处理图像数据
        grayscale(data.byteOffset, width, height);

        // 将处理后的图像数据放回 Canvas
        imageData.data.set(data);
        ctx.putImageData(imageData, 0, 0);

        // 不需要将处理后的图像数据传回主线程,OffscreenCanvas 已经包含了绘制内容
    }
};

5. 使用requestIdleCallback来优化数据的传递

requestIdleCallback 方法允许浏览器在空闲时间执行低优先级任务。在图像处理过程中,可以利用 requestIdleCallback 方法优化数据传递和其他非紧急操作。例如,当用户在进行滚动、点击等操作(紧急操作)时,这些操作会被优先处理,图像处理任务中涉及到的大量数据传递,会在浏览器空闲时进行,确保界面响应迅速。

示例代码:

// 主线程代码
const canvas = document.getElementById('canvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('worker.js');

// 将 OffscreenCanvas 传递给 Web Worker
worker.postMessage({ canvas: offscreen }, [offscreen]);

// 加载图像并发送给 Worker 进行处理
const img = new Image();
img.src = 'path/to/image.jpg';
img.onload = () => {
    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

    // 使用 requestIdleCallback 优化数据传递
    requestIdleCallback(() => {
        worker.postMessage({ action: 'processImage', imageData });
    });
};

最后来整理一下整体的流程:

  1. 在主线程中使用 requestIdleCallback 调度图像处理任务,确保在浏览器空闲时间执行任务,从而不阻塞用户界面响应。
  2. 将图像处理任务放到 Web Worker 中,利用 OffscreenCanvas 和 WebAssembly 进行高效的工作线程处理。
  3. 处理完成后,直接在 OffscreenCanvas 上绘制处理后的图像,无需将图像数据传回主线程,OffscreenCanvas 会自动更新主线程中的显示。

-EOF-

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式