本文阿宝哥会为小伙伴们隆重介绍用于图片处理的十个 “小帮手”,他们各个身怀绝技,拥有模糊、压缩、裁剪、旋转、合成、比对等技能。相信认识他们之后,你将能够轻松应对大多数的图片处理场景。
不过在介绍 “小帮手” 前,阿宝哥会先介绍一些图片相关的基础知识。此外,为了让小伙伴们能够学习更多图片相关的知识,阿宝哥精心准备了 “阿宝哥有话说” 章节。该章节你将会学到以下知识:
- 如何区分图片的类型(非文件后缀名);
- 如何获取图片的尺寸(非右键查看图片信息);
- 如何预览本地图片(非图片阅读器);
- 如何实现图片压缩(非图片压缩工具);
- 如何操作位图像素数据(非 PS 等图片处理软件);
- 如何实现图片隐写(非肉眼可见)。
十个图片处理 “小帮手” 已经已经迫不及待想与你见面,还在犹豫什么?赶紧出发吧!
一、基础知识
1.1 位图
位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。 这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。
用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。 位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。常用的位图处理软件有 Photoshop、Painter 和 Windows 系统自带的画图工具等。
分辨率是位图不可逾越的壁垒,在对位图进行缩放、旋转等操作时,无法生产新的像素,因此会放大原有的像素填补空白,这样会让图片显得不清晰。
(图片来源:https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%9B%BE)
图中的小方块被称为像素,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。
可以将像素视为整个图像中不可分割的单位或者是元素。不可分割的意思是它不能够再切割成更小单位抑或是元素,它是以一个单一颜色的小格存在。 每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。
1.2 矢量图
所谓矢量图,就是使用直线和曲线来描述的图形,构成这些图形的元素是一些点、线、矩形、多边形、圆和弧线等,它们都是通过数学公式计算获得的,具有编辑后不失真的特点。 例如一幅画的矢量图形实际上是由线段形成外框轮廓,由外框的颜色以及外框所封闭的颜色决定画显示出的颜色。
矢量图以几何图形居多,图形可以无限放大,不变色、不模糊。 常用于图案、标志、VI、文字等设计。常用软件有:CorelDraw、Illustrator、Freehand、XARA、CAD 等。
这里我们以 Web 开发者比较熟悉的 SVG(Scalable Vector Graphics —— 可缩放矢量图形)为例,来了解一下 SVG 的结构:
可缩放矢量图形(英语:Scalable Vector Graphics,SVG)是一种基于可扩展标记语言(XML),用于描述二维矢量图形的图形格式。SVG 由 W3C 制定,是一个开放标准。
SVG 主要支持以下几种显示对象:
- 矢量显示对象,基本矢量显示对象包括矩形、圆、椭圆、多边形、直线、任意曲线等;
- 嵌入式外部图像,包括 PNG、JPEG、SVG 等;
- 文字对象。
了解完位图与矢量图的区别,下面我们来介绍一下位图的数学表示。
1.3 位图的数学表示
位图的像素都分配有特定的位置和颜色值。每个像素的颜色信息由 RGB 组合或者灰度值表示。
根据位深度,可将位图分为1、4、8、16、24 及 32 位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。
1.3.1 二值图像
位深度为 1 的像素位图只有两个可能的值(黑色和白色),所以又称为二值图像。二值图像的像素点只有黑白两种情况,因此每个像素点可以由 0 和 1 来表示。
比如一张 4 * 4 二值图像:
1 1 0 1
1 1 0 1
1 0 0 0
1 0 1 0
1.3.2 RGB 图像
RGB 图像由三个颜色通道组成,其中 RGB 代表红、绿、蓝三个通道的颜色。8 位/通道的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道(bpc)的 RGB 图像称作 24 位图像(8 位 x 3 通道 = 24 位数据/像素)。通常将使用 24 位 RGB 组合数据位表示的的位图称为真彩色位图。
RGB 彩色图像可由三种矩阵表示:一种代表像素中红色的强度,一种代表绿色,另一种代表蓝色。
(图片来源:https://freecontent.manning.com/the-computer-vision-pipeline-part-2-input-images/)
图像处理的本质实际上就是对这些像素矩阵进行计算。 其实位图中的图像类型,除了二值图像和 RGB 图像之外,还有灰度图像、索引图像和 YUV 图像。这里我们不做过多介绍,感兴趣的小伙伴,可以自行查阅相关资料。
二、图片处理库
2.1 AlloyImage
基于 HTML 5 的专业级图像处理开源引擎。
https://github.com/AlloyTeam/AlloyImage
AlloyImage 基于 HTML5 技术的专业图像处理库,来自腾讯 AlloyTeam 团队。它拥有以下功能特性:
- 基于多图层操作 —— 一个图层的处理不影响其他图层;
- 与 PS 对应的 17 种图层混合模式 —— 便于 PS 处理教程的无缝迁移;
- 多种基本滤镜处理效果 —— 基本滤镜不断丰富、可扩展;
- 基本的图像调节功能 —— 色相、饱和度、对比度、亮度、曲线等;
- 简单快捷的 API —— 链式处理、API 简洁易用、传参灵活;
- 多种组合效果封装 —— 一句代码轻松实现一种风格;
- 接口一致的单、多线程支持 —— 单、多线程切换无需更改一行代码,多线程保持快捷 API 特性。
对于该库 AlloyTeam 团队建议的使用场景如下:
- 桌面软件客户端内嵌网页运行方式 >>> 打包 Webkit 内核:用户较大头像上传风格处理、用户相册风格处理(处理时间平均 < 1s);
- Win8 Metro 应用 >>> 用户上传头像,比较小的图片风格处理后上传(Win8 下 IE 10 支持多线程);
- Mobile APP >>> Andriod 平台、iOS 平台小图风格 Web 处理的需求,如 PhoneGap 应用,在线头像上传时的风格处理、Mobile Web 端分享图片时风格处理等。
使用示例
// $AI或AlloyImage初始化一个AlloyImage对象
var ps = $AI(img, 600).save('jpg', 0.6);
// save将合成图片保存成base64格式字符串
var string = AlloyImage(img).save('jpg', 0.8);
// saveFile将合成图片下载到本地
img.onclick = function(){
AlloyImage(this).saveFile('处理后图像.jpg', 0.8);
}
http://alloyteam.github.io/AlloyImage/
(图片来源:http://alloyteam.github.io/AlloyImage/)
2.2 blurify
blurify.js is a tiny(~2kb) library to blurred pictures, support graceful downgrade from
css
mode tocanvas
mode.https://github.com/JustClear/blurify
blurify.js 是一个用于图片模糊,很小的 JavaScript 库(约 2 kb),并支持从 CSS 模式到 Canvas 模式的优雅降级。该插件支持三种模式:
- css 模式:使用
filter
属性,默认模式; - canvas 模式:使用
canvas
导出 base64; - auto 模式:优先使用 css 模式,否则自动切换到 canvas 模式。
使用示例
import blurify from 'blurify';
new blurify({
images: document.querySelectorAll('.blurify'),
blur: 6,
mode: 'css',
});
// or in shorthand
blurify(6, document.querySelectorAll('.blurify'));
https://justclear.github.io/blurify/
(图片来源:https://justclear.github.io/blurify/)
看到这里是不是有些小伙伴觉得只是模糊处理而已,觉得不过瘾,能不能来点更酷的。嘿嘿,有求必应!阿宝哥立马来个 “酷炫叼” 的库 —— midori,该库用于为背景图创建动画,使用 three.js 编写并使用 WebGL。本来是想给个演示动图,无奈单个 Gif 太大,只能放个体验地址,感兴趣的小伙伴自行体验一下。
midori 示例地址:https://aeroheim.github.io/midori/
2.3 cropperjs
JavaScript image cropper.
https://github.com/fengyuanchen/cropperjs
Cropper.js 是一款非常强大却又简单的图片裁剪工具,它可以进行非常灵活的配置,支持手机端使用,支持包括 IE9 以上的现代浏览器。它可以用于满足诸如裁剪头像上传、商品图片编辑之类的需求。
Cropper.js 支持以下特性:
- 支持 39 个配置选项;
- 支持 27 个方法;
- 支持 6 种事件;
- 支持 touch(移动端);
- 支持缩放、旋转和翻转;
- 支持在画布上裁剪;
- 支持在浏览器端通过画布裁剪图像;
- 支持处理 Exif 方向信息;
- 跨浏览器支持。
可交换图像文件格式(英语:Exchangeable image file format,官方简称 Exif),是专门为数码相机的照片设定的文件格式,可以记录数码照片的属性信息和拍摄数据。Exif 可以附加于 JPEG、TIFF、RIFF 等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。
Exif 信息以 0xFFE1 作为开头标记,后两个字节表示 Exif 信息的长度。所以 Exif 信息最大为 64 kB,而内部采用 TIFF 格式。
使用示例
// import 'cropperjs/dist/cropper.css';
import Cropper from 'cropperjs';
const image = document.getElementById('image');
const cropper = new Cropper(image, {
aspectRatio: 16 / 9,
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
https://fengyuanchen.github.io/cropperjs/
2.4 compressorjs
JavaScript image compressor.
https://github.com/fengyuanchen/compressorjs
compressorjs 是 JavaScript 图像压缩器。使用浏览器原生的 canvas.toBlob
API 进行压缩工作,这意味着它是有损压缩。通常的使用场景是,在浏览器端图片上传之前对其进行预压缩。
在浏览器端要实现图片压缩,除了使用 canvas.toBlob
API 之外,还可以使用 Canvas 提供的另一个 API,即 toDataURL
API,它接收 type
和 encoderOptions
两个可选参数。
其中 type
表示图片格式,默认为 image/png
。而 encoderOptions
用于表示图片的质量,在指定图片格式为 image/jpeg
或 image/webp
的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92
,其他参数会被忽略。
相比 canvas.toDataURL
API 来说,canvas.toBlob
API 是异步的,因此多了个 callback
参数,这个 callback
回调方法默认的第一个参数就是转换好的 blob
文件信息。canvas.toBlob
的签名如下:
canvas.toBlob(callback, mimeType, qualityArgument)
使用示例
import axios from 'axios';
import Compressor from 'compressorjs';
// <input type="file" id="file" accept="image/*">
document.getElementById('file').addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) {
return;
}
new Compressor(file, {
quality: 0.6,
success(result) {
const formData = new FormData();
// The third parameter is required for server
formData.append('file', result, result.name);
// Send the compressed image file to server with XMLHttpRequest.
axios.post('/path/to/upload', formData).then(() => {
console.log('Upload success');
});
},
error(err) {
console.log(err.message);
},
}