找回密码
 立即注册
查看: 375|回复: 6

前端图像处理指南

[复制链接]
发表于 2021-11-23 05:51 | 显示全部楼层 |阅读模式
计算机图像处理是一门很成熟的技术,任何一门可操作系统接口的语言都能很轻易的实现各种处理操作。但是前端限于浏览器环境和接口限制,处理起来会有诸多不便,这里所说的前端图像处理,是真的指不借助任何后端服务纯前端实现的图像处理。本文会介绍canvas位图处理,SVG矢量图和CSS3图像处理,重点是canvas,并且最后会附上一个
TrimPNG图像处理应用。
canvas位图处理

HTML5 canvas为我们提供了一块画布,让前端也有了操作位图的功能:图片旋转、缩放、滤镜、压缩等都可以通过JS来实现。
图像基本处理
通过设置drawImage参数可以实现图片绘制、缩放、拉伸和裁剪等操作:



DEMO源码戳这里 JS Bin.
只需要drawImage一个方法,就可以实现基本图形处理功能,再结合鼠标或滚轮事件,就可以实现更复杂的局部放大预览,缩放等功能了。
需要注意的是,canvas尺寸不能过大,如果你要绘制一块全屏的canvas很可能有部分显示不出来。并且不同设备对canvas尺寸限制也不同,比如在Win8 RTM上最大支持8192x8192px,但是在Windows RT上只支持2048x2048px。所以对于大面积的canvas最好分块绘制。
图像滤镜处理
现在的朋友圈发个图都要用滤镜美一下,复古清纯胶片LOMO各种风格应有尽有。canvas提供了getImageData方法来获取图像上每一个像素点的RGBA信息,这样我们就能对图片进行像素级处理了。通过特定算法来重写imageData中的像素信息,然后用setImageData方法把新的数据重新绘制在canvas上,这样就可以实现图像滤镜打码加特效等一系列功能。

比如我们现在要实现一个复古滤镜:

// 复古滤镜处理算法:获取每个像素的RGB信息,并按特定权重返回其加权平均值
let sepiaFilter = function(imgData) {
  let d = imgData.data
  for (let i = 0; i < d.length; i += 4) {
    let r = d
    let g = d[i + 1]
    let b = d[i + 2]
    d = (r * 0.393) + (g * 0.769) + (b * 0.189) // red
    d[i + 1] = (r * 0.349) + (g * 0.686) + (b * 0.168) // green
    d[i + 2] = (r * 0.272) + (g * 0.534) + (b * 0.131) // blue
  }
  return imgData
}
//图像地址必须和当前页面同域,否则会报cross-origin错误
img.src = '/img/logo@2x.png'
img.onload = () => {
  ctx.drawImage(img, 0, 0) // 绘制原图
  let imgData = ctx.getImageData(0, 0, img.width, img.height) // 获取图片信息
  ctx.putImageData(sepiaFilter(imgData), 100, 0) // 绘制处理后图片
}


DEMO源码戳这里 JSFiddle.
滤镜处理关键在于滤镜算法,要想写出更高级的特效需要有计算机图形学基础,对卷积矩阵、拉普拉斯变换、傅里叶变换等数学知识也要了解,这个坑很大我就不挖了。

图像base64存储
加完特效后如果我们想把图像保存下来,就可以用toDataURL方法来对图片进行格式转换、压缩存储了。

// 支持三种文件类型:image/png(默认) | image/jpeg | image/webp(仅Chrome)
canvas.toDataURL() // 默认存储为png
// 仅jpeg/webp支持质量参数(0~1,默认0.92)
canvas.toDataURL("image/jpeg", 0.1) // 存储为质量为0.1的jpeg
由于存储形式是base64编码,原来图片的每三个字节都会被扩展成4字节,所以整体上编码后数据会比原来多约1/3。以下是通过toDataURL存储后的图片和原图大小相关对比数据(数据仅供参考,不具通用性):


可见存储后图片体积并不是原来的4/3,实际上处理后的图片都会比原图大好几倍,并且不同的图片增大的体积也是不确定的。如果是要上传图片到服务器,可以把base64转化成Blob二进制数据压缩上传;如果要直接在前端显示或供用户下载,jpg还好可以设置质量参数,要是png就没法压缩了。
只是用toDataURL还不够,用户需要通过手动点击图片-右键图片存储为来保存图片,如果要实现点击下载按钮自动下载图片还需要修改图片类型为octet-stream,然后利用HTML5的download属性强制让浏览器下载。
DEMO源码快戳我 JSFiddle.
程序员的备胎
有了以上基础,再结合成熟的图形处理算法,我们可以完成日常工作中大部分图像处理需求,业界也有许多开源的轮子,比如截屏库 html2canvas、代码转图片工具 code-to-image、图片裁剪 Cropper、图像处理 tracking.js、二维码生成库 qrcode2、还有腾讯出的 AlloyImage,基于HTML5的专业级图像处理开源引擎,非常强大:


以上列举部分,更多备胎见GitHub: 0326/canvas
SVG矢量图处理

讲完位图处理再简单说说矢量图处理。矢量图在绘制图标、商业LOGO、动画元素上应用非常广范。Web最开始支持的矢量图形并不是现在的SVG,而是微软主推的VML,所以在低版本IE下面只支持VML而不支持SVG,直到后来SVG成为W3C标准并被普及,微软才在IE9中支持SVG。
SVG遵循XML规范,可以很好的集成在HTML里面,同时支持JS脚本控制,还有基于SMIL标准的动态内容支持,做起动画来也是非常方便。目前基于SVG的JS图形库轮子也是非常多,如svg.js, Snap.svg, Velocity.js, D3.js等等。SVG本身是一种标记语言,简单易学,在此不做展开。
CSS图像处理

如果你的图像特效只是用来给用户展示,并不需要存储的话,可以直接用CSS处理,基本的调整图片大小、拉伸、旋转、裁切等操作直接几个CSS属性width/height/skew/rotate/clip-path等就能搞定了。如果想加特效,使用CSS滤镜Duang的一下就出来了,不需要任何图形学基础和数学知识:



DEMO 源码见JS Bin.
总结

写了这么多 DEMO,最后附上一个比较完整的小工具 TrimPNG。
大家都知道 TinyPNG 可以压缩PNG, TrimPNG 顾名思义就是去除图片空白和白边的,纯前端实现了自动抠图和切图,顺便保存图片功能。



体验地址:TrimPNG
用到的技术点上面都说到了,裁切实现原理就是横向纵向分别扫描两次像素点阵,找出上下左右最外面的非透明点,然后定位出有效图像区域。去白边则是采用标记清除策略,先扫描一遍标记出可疑白点,然后再扫描一遍,凡是与透明区域相邻的可疑白点就清除。

最后,欢迎大家关注:Front End - 知乎专栏
zhuanlan.zhihu.com/frontend不定期更新前端相关,一般都会有干货。也欢迎大家投稿,只限原创。
鉴于傻乎居然不支持Markdown,代码高亮也是Low到不行,连个预览效果也要邀请,所以以后会少贴代码多贴图,我会尽量挑漂亮的:)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-11-23 05:58 | 显示全部楼层
感觉不错
发表于 2021-11-23 05:59 | 显示全部楼层
看得出来你很用心,虽然不是前端也要给你个赞
发表于 2021-11-23 06:01 | 显示全部楼层
前提是HTML5才支持
发表于 2021-11-23 06:07 | 显示全部楼层
是的,Chrome无压力,但是要用于生产环境,还需要很多适配工作。
发表于 2021-11-23 06:16 | 显示全部楼层
唉,傻乎不支持Markdown。。
发表于 2021-11-23 06:17 | 显示全部楼层
难道我评论过这个?我被盗号了?
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 14:24 , Processed in 0.102304 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表