前言
在前端开发的世界里,图片处理和优化是至关重要的一环。本文章浅析了图片处理的多个方面,包括灰度处理、抠图去除背景、证件照一键换背景、图像编辑器以及canvas海报等。此外,还探讨了图片压缩的各种手段,并通过sharp、webpack和oss等示例演示了实际应用。对于图片格式和兼容性的了解,以及小图处理与SVG的应用,也在本文章中得到详细的讨论。无论你是新手还是经验丰富的前端开发者,这里都有你需要的技术指南,助你在图像处理的道路上更进一步。
1、图片处理
1、灰度处理
图片灰度处理的意义:
- 简化图像、提高性能:将彩色图像转换为灰度图像可以简化图像的信息表示。灰度图像只有一个通道,每个像素的值表示亮度,因此比彩色图像更加简洁。这可以减少图像处理的复杂度,并降低存储和传输的成本。
- 色彩失真不敏感+提取关键特征: 在某些图像处理任务中,彩色信息可能不是必要的,而只需关注图像的亮度信息。例如,人脸识别算法通常使用灰度图像进行特征提取,因为亮度信息对于人脸的结构和纹理更具有表达能。灰度图像不受彩色信息的影响,因此对色彩变化更不敏感。在某些图像处理任务中,如边缘检测、图像增强等,灰度图像可以更好地保留图像的结构和细节,而不受颜色变化的干扰, 如:医学影像领域。
- 对于视力障碍者来说,灰度处理有利于他们更好的看世界
JS
// 文件输入框变化时的事件监听器
document.getElementById("imageInput").addEventListener("change", handleImage);
function handleImage(e) {
// 获取选择的文件
const fileInput = e.target;
const file = fileInput.files[0];
if (file) {
// 创建FileReader以读取所选图像
const reader = new FileReader();
// 读取器完成读取时的事件处理程序
reader.onload = function (e) {
// 获取对原始图像和灰度画布的引用
const originalImage = document.getElementById("originalImage");
originalImage.src = e.target.result;
const grayscaleCanvas = document.getElementById("grayscaleCanvas");
const ctx = grayscaleCanvas.getContext("2d");
// 原始图像加载完成时的事件处理程序
originalImage.onload = function () {
// 将灰度画布的尺寸设置为原始图像的尺寸
grayscaleCanvas.width = originalImage.width;
grayscaleCanvas.height = originalImage.height;
// 在灰度画布上绘制原始图像
ctx.drawImage(
originalImage,
0,
0,
originalImage.width,
originalImage.height
);
// 获取灰度图像的图像数据
const imageData = ctx.getImageData(
0,
0,
originalImage.width,
originalImage.height
);
const data = imageData.data;
// 将每个像素的颜色值取平均,实现灰度效果
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg;
}
// // 最大值法
// for (let i = 0; i < data.length; i += 4) {
// const maxVal = Math.max(data[i], data[i + 1], data[i + 2]);
// data[i] = data[i + 1] = data[i + 2] = Math.min(255, maxVal);
// }
// // 加权平均值法:
// const Wr = 0.3; // Weight for red
// const Wg = 0.59; // Weight for green
// const Wb = 0.11; // Weight for blue
// for (let i = 0; i < data.length; i += 4) {
// const weightedAvg =
// (data[i] * Wr + data[i + 1] * Wg + data[i + 2] * Wb) /
// (Wr + Wg + Wb);
// data[i] =
// data[i + 1] =
// data[i + 2] =
// Math.min(255, Math.max(0, weightedAvg));
// }
// 将处理后的图像数据放回灰度画布
ctx.putImageData(imageData, 0, 0);
// 显示原始图像和灰度画布
originalImage.style.display = "block";
grayscaleCanvas.style.display = "block";
};
};
// 读取文件数据URL
reader.readAsDataURL(file);
}
}
注:关于图片灰度转换的6种算法,这里使用了平均值算法
2、扩展(抠图去除背景)
这里借助了三方api: removebg,Cloud-Hosted Background Image Remover
js
// 发送POST请求到remove.bg的API
fetch("https://api.remove.bg/v1.0/removebg", {
method: "POST",
headers: {
"X-Api-Key": "XXXXXX", // 替换成你自己的Remove.bg API密钥
},
body: formData,
})
3、扩展(证件照一键换背景)
到这里就水到渠成,整体思路是:详细代码见上面👆demo
4、扩展(图像编辑器)
5、扩展(canvas海报)
2、图片压缩
根据 HTTP ARCHIVE 的统计,网站静态资源传输数据中位数为 2.5MB 左右,而其中图片传输数据的中位数在 1MB 左右。
1、压缩手段
- 在线网站:tinypng 、ImageOptim、微图
- 第三方:sharp
- webpack: imagemin-webpack-plugin
- 借助对象存储oss的压缩能力
2、demo演示
1、sharp:
压缩效果:
⚠️注意:sahrp存在的问题
2、wepack:
3、oss
js
// 伪代码展示
// 定义一个支持图片处理的 React Component
function CDNImage(path, width, height) {
// 如果是 DataURL,则直接返回图片
if (path.startsWith("data")) {
return <img src={path} />;
}
// 这里的xxxx为域名
const avifSrc = `https://xxxx/${path}?x-oss-process=image/format,avif/quality,Q_75/resize,w_${width},h_${height}`;
const webPSrc = `https://xxxx/${path}?x-oss-process=image/format,webp/quality,Q_75/resize,w_${width},h_${height}`;
const src = `https://xxxx/${path}`;
return (
<picture>
<source srcSet={avifSrc} type="image/avif" />
<source srcSet={webpSrc} type="image/webp" />
<img src={src} width={width} height={height} loading="lazy" />
</picture>
);
}
// 使用它
import hello from "./hello.png";
<CDNImage path={hello} width={100} height={100} />;
3、图片格式+兼容性
从存储和表示图像的方式来划分,主要为两类:
- 矢量图: 使用数学公式来描述图像。图像由一系列的点、线、曲线等数学对象组成,因此无论放大还是缩小,图像都能保持较高的质量。
- 位图:由像素阵列组成的图像。每个像素都有自己的颜色信息,因此在缩放时可能会失去一些图像质量。
矢量图典型: SVG(Scalable Vector Graphics)
位图(bitmap):常见的图片格式 GIF、JPEG、PNG、WEBP 和 AVIF 都属于位图
位图格式的图片 :相同质量的情况下,他们的体积大小不同,一般来说: jpeg,png,webp,avif 依次减小
根据 webp and avif comparison 文章,针对相同质量的 jpeg,webp 减少了 30% 的体积(中位数),而 avif 减少了 50% 的体积。
综上,avif 拥有更小的体积,但为什么没大量使用呢?在于它的浏览器的兼容性,目前只有86%的支持率:
处理兼容性 : 在前端项目中,我们一般通过 img
标签来表示图片,但基于兼容性影响,可以考试使用picture
,它可以根据浏览器对图片格式的支持情况,来降级处理。如此一来,它即使不支持 avif
图片格式,也可以优雅地降级到 webp
甚至 jpeg
:
html
<picture>
<source srcset="shanyue-hello.avif" type="image/avif">
<source srcset="shanyue-hello.webp" type="image/webp">
<img src="shanyue-hello.jpeg">
</picture>
4、小图处理与svg
在前端性能优化中,常有一项是将小图片转化为 DataURL,转化为 DataURL 虽然无法使得图片体积减小,但是可以使得图片地址内联,减少了 HTTP 请求次数
从上可知:
- DataURL 并不仅可代表图片,他同样也可代表其它所有文件类型,比如音视频,Markdown,HTML 等,只要你给出了对应的 MIME Type。
- DataURL 并不仅可由 base64 表示,它也可以使用纯文本表示,比如 HTML。只有二进制的资源,如 jpeg/avif 等二进制资源必须使用 base64 表示。
html
data:text/html,lots of text...<p><a name%3D"bottom">bottom</a>?arg=val</p>
// 转换前:
lots of text...
<p><a name="bottom">bottom</a>?arg=val</p>
webpack处理小图:
在 webpack 5 中,可使用
asset
模块对小图片进行处理。
json
{
module: {
rules: [
{
test: /.(jpg|png|avif)/,
type: 'asset',
parser: {
// 对小于 4kb 的图片进行 DataURL 处理
dataUrlCondition: {
maxSize: 4 * 1024
}
}
}
]
},
};
注意,以上 asset
仅对二进制图片进行了处理,而无对 svg
这种文本图片进行处理。因为 SVG 为文本格式,使用 base64 编码反而使其体积增大了 30%,而文本格式并不必使用 base64 编码。
处理SVG:
在官方示例中,对 svg 生成 DataURL 时进行了额外的处理。使用了 mini-svg-data-uri
,而 源码 也极其简单,仅有几十行,核心功能是 URLEncode
以及字符串,引号等的处理
javascript
// mini-svg-data-uri demo
const path = require('path');
const svgToMiniDataURI = require('mini-svg-data-uri');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.svg/,
type: 'asset/inline',
generator: {
dataUrl: content => {
content = content.toString();
return svgToMiniDataURI(content);
}
}
}
]
},
};
end❤️