「位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。」这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。
「用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。」位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。常用的位图处理软件有Photoshop、Painter和Windows系统自带的画图工具等。
分辨率是位图不可逾越的壁垒,在对位图进行缩放、旋转等操作时,无法生产新的像素,因此会放大原有的像素填补空白,这样会让图片显得不清晰。
图中的小方块被称为像素,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。
可以将像素视为整个图像中不可分割的单位或者是元素。「不可分割的意思是它不能够再切割成更小单位抑或是元素,它是以一个单一颜色的小格存在。」每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。
所谓矢量图,就是使用直线和曲线来描述的图形,构成这些图形的元素是一些点、线、矩形、多边形、圆和弧线等,*「它们都是通过数学公式计算获得的,具有编辑后不失真的特点。」*例如一幅画的矢量图形实际上是由线段形成外框轮廓,由外框的颜色以及外框所封闭的颜色决定画显示出的颜色。
「矢量图以几何图形居多,图形可以无限放大,不变色、不模糊。」常用于图案、标志、VI、文字等设计。常用软件有:CorelDraw、Illustrator、Freehand、XARA、CAD等。
这里我们以Web开发者比较熟悉的SVG(「ScalableVectorGraphics——可缩放矢量图形」)为例,来了解一下SVG的结构:
可缩放矢量图形(英语:ScalableVectorGraphics,SVG)是一种基于可扩展标记语言(XML),用于描述二维矢量图形的图形格式。SVG由W3C制定,是一个开放标准。
SVG主要支持以下几种显示对象:
了解完位图与矢量图的区别,下面我们来介绍一下位图的数学表示。
位图的像素都分配有特定的位置和颜色值。每个像素的颜色信息由RGB组合或者灰度值表示。
根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。
「1.3.1二值图像」
位深度为1的像素位图只有两个可能的值(黑色和白色),所以又称为二值图像。二值图像的像素点只有黑白两种情况,因此每个像素点可以由0和1来表示。
比如一张4*4二值图像:
1101110110001010「1.3.2RGB图像」
RGB图像由三个颜色通道组成,其中RGB代表红、绿、蓝三个通道的颜色。8位/通道的RGB图像中的每个通道有256个可能的值,这意味着该图像有1600万个以上可能的颜色值。
有时将带有8位/通道(bpc)的RGB图像称作24位图像(8位x3通道=24位数据/像素)。通常将使用24位RGB组合数据位表示的的位图称为真彩色位图。
RGB彩色图像可由三种矩阵表示:一种代表像素中红色的强度,一种代表绿色,另一种代表蓝色。
AlloyImage基于HTML5技术的专业图像处理库,来自腾讯AlloyTeam团队。它拥有以下功能特性:
对于该库AlloyTeam团队建议的使用场景如下:
「使用示例」
//$AI或AlloyImage初始化一个AlloyImage对象varps=$AI(img,600).save('jpg',0.6);//save将合成图片保存成base64格式字符串varstring=AlloyImage(img).save('jpg',0.8);//saveFile将合成图片下载到本地img.onclick=function(){AlloyImage(this).saveFile('处理后图像.jpg',0.8);}「在线示例」
blurify.js是一个用于图片模糊,很小的JavaScript库(约2kb),并支持从CSS模式到Canvas模式的优雅降级。该插件支持三种模式:
importblurifyfrom'blurify';newblurify({images:document.querySelectorAll('.blurify'),blur:6,mode:'css',});//orinshorthandblurify(6,document.querySelectorAll('.blurify'));「在线示例」
看到这里是不是有些小伙伴觉得只是模糊处理而已,觉得不过瘾,能不能来点更酷的。嘿嘿,有求必应!阿宝哥立马来个「“酷炫叼”」的库——midori,该库用于为背景图创建动画,使用three.js编写并使用WebGL。本来是想给个演示动图,无奈单个Gif文件太大,只能放个体验地址,感兴趣的小伙伴自行体验一下。
Cropper.js是一款非常强大却又简单的图片裁剪工具,它可以进行非常灵活的配置,支持手机端使用,支持包括IE9以上的现代浏览器。它可以用于满足诸如裁剪头像上传、商品图片编辑之类的需求。
Cropper.js支持以下特性:
Exif信息以0xFFE1作为开头标记,后两个字节表示Exif信息的长度。所以Exif信息最大为64kB,而内部采用TIFF格式。
//import'cropperjs/dist/cropper.css';importCropperfrom'cropperjs';constimage=document.getElementById('image');constcropper=newCropper(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);},});「在线示例」
compressorjs是JavaScript图像压缩器。使用浏览器原生的canvas.toBlobAPI进行压缩工作,这意味着它是有损压缩。通常的使用场景是,在浏览器端图片上传之前对其进行预压缩。
在浏览器端要实现图片压缩,除了使用canvas.toBlobAPI之外,还可以使用Canvas提供的另一个API,即toDataURLAPI,它接收type和encoderOptions两个可选参数。
其中type表示图片格式,默认为image/png。而encoderOptions用于表示图片的质量,在指定图片格式为image/jpeg或image/webp的情况下,可以从0到1的区间内选择图片的质量。如果超出取值范围,将会使用默认值0.92,其他参数会被忽略。
相比canvas.toDataURLAPI来说,canvas.toBlobAPI是异步的,因此多了个callback参数,这个callback回调方法默认的第一个参数就是转换好的blob文件信息。canvas.toBlob的签名如下:
canvas.toBlob(callback,mimeType,qualityArgument)「使用示例」
importaxiosfrom'axios';importCompressorfrom'compressorjs';//
Fabric.js是一个框架,可让你轻松使用HTML5Canvas元素。它是一个位于Canvas元素之上的交互式对象模型,同时也是一个「SVG-to-canvas」的解析器。
使用Fabric.js,你可以在画布上创建和填充对象。所谓的对象,可以是简单的几何形状,比如矩形,圆形,椭圆形,多边形,或更复杂的形状,包含数百或数千个简单路径。然后,你可以使用鼠标缩放,移动和旋转这些对象。并修改它们的属性——颜色,透明度,z-index等。此外你还可以一起操纵这些对象,即通过简单的鼠标选择将它们分组。
Fabric.js支持所有主流的浏览器,具体的兼容情况如下:
Resemble.js使用HTMLCanvas和JavaScript来实现图片的分析和比较。兼容大于8.0的Node.js版本。
//比较两张图片vardiff=resemble(file).compareTo(file2).ignoreColors().onComplete(function(data){console.log(data);/*{misMatchPercentage:100,//%isSameDimensions:true,//orfalsedimensionDifference:{width:0,height:-1},getImageDataUrl:function(){}}*/});「在线示例」
Pica可用于在浏览器中调整图像大小,没有像素化并且相当快。它会自动选择最佳的可用技术:webworkers,webassembly,createImageBitmap,纯JS。
借助Pica,你可以实现以下功能:
constpica=require('pica')();//调整画布/图片的大小pica.resize(from,to,{unsharpAmount:80,unsharpRadius:0.6,unsharpThreshold:2}).then(result=>console.log('resizedone!'));//调整大小并转换为Blobpica.resize(from,to).then(result=>pica.toBlob(result,'image/jpeg',0.90)).then(blob=>console.log('resizedtocanvas&createdblob!'));「在线示例」
tui.image-editor是使用HTML5Canvas的全功能图像编辑器。它易于使用,并提供强大的过滤器。同时它支持对图像进行裁剪、翻转、旋转、绘图、形状、文本、遮罩和图片过滤等操作。
tui.image-editor的浏览器兼容情况如下:
//ImageeditorvarimageEditor=newtui.ImageEditor("#tui-image-editor-container",{includeUI:{loadImage:{path:"img/sampleImage2.png",name:"SampleImage",},theme:blackTheme,//orwhiteThemeinitMenu:"filter",menuBarPosition:"bottom",},cssMaxWidth:700,cssMaxHeight:500,usageStatistics:false,});window.onresize=function(){imageEditor.ui.resizeEditor();};在线示例
gif.js是运行在浏览器端的JavaScriptGIF编码器。它使用类型化数组和WebWorker在后台渲染每一帧,速度真的很快。该库可工作在支持:WebWorkers,FileAPI和TypedArrays的浏览器中。
gif.js的浏览器兼容情况如下:
vargif=newGIF({workers:2,quality:10});//addanimageelementgif.addFrame(imageElement);//oracanvaselementgif.addFrame(canvasElement,{delay:200});//orcopythepixelsfromacanvascontextgif.addFrame(ctx,{copy:true});gif.on('finished',function(blob){window.open(URL.createObjectURL(blob));});gif.render();「在线示例」
Sharp的典型应用场景是将常见格式的大图像转换为尺寸较小,对网络友好的JPEG,PNG和WebP格式的图像。由于其内部使用libvips,使得调整图像大小通常比使用ImageMagick和GraphicsMagick设置快4-5倍。除了支持调整图像大小之外,Sharp还支持旋转、提取、合成和伽马校正等功能。
Sharp支持读取JPEG,PNG,WebP,TIFF,GIF和SVG图像。输出图像可以是JPEG,PNG,WebP和TIFF格式,也可以是未压缩的原始像素数据。
//改变图像尺寸sharp(inputBuffer).resize(320,240).toFile('output.webp',(err,info)=>{...});//旋转输入图像并改变图片尺寸sharp('input.jpg').rotate().resize(200).toBuffer().then(data=>{...}).catch(err=>{...});「在线示例」
常见图片类型对应的魔数如下表所示:
这里我们以阿宝哥的头像(abao.png)为例,验证一下该图片的类型是否正确:
在日常开发过程中,如果遇到检测图片类型的场景,我们可以直接利用一些现成的第三方库。比如,你想要判断一张图片是否为PNG类型,这时你可以使用is-png这个库,它同时支持浏览器和Node.js,使用示例如下:
「Node.js」
//npminstallread-chunkconstreadChunk=require('read-chunk');constisPng=require('is-png');constbuffer=readChunk.sync('unicorn.png',0,8);isPng(buffer);//=>true「Browser」
(async()=>{constresponse=awaitfetch('unicorn.png');constbuffer=awaitresponse.arrayBuffer();isPng(newUint8Array(buffer));//=>true})();3.2如何获取图片的尺寸图片的尺寸、位深度、色彩类型和压缩算法都会存储在文件的二进制数据中,我们继续以阿宝哥的头像(abao.png)为例,来了解一下实际的情况:
560(十进制)=>0x0230
因此如果想要获取图片的尺寸,我们就需要依据不同的图片格式对图片二进制数据进行解析。幸运的是,我们不需要自己做这件事,image-size这个Node.js库已经帮我们实现了获取主流图片类型文件尺寸的功能:
「同步方式」
varsizeOf=require('image-size');vardimensions=sizeOf('images/abao.png');console.log(dimensions.width,dimensions.height);「异步方式」
varsizeOf=require('image-size');sizeOf('images/abao.png',function(err,dimensions){console.log(dimensions.width,dimensions.height);});image-size这个库功能还是蛮强大的,除了支持PNG格式之外,还支持BMP、GIF、ICO、JPEG、SVG和WebP等格式。
利用HTMLFileReaderAPI,我们也可以方便的实现图片本地预览功能,具体代码如下:
constapp=require('express')();app.post('/upload',function(req,res){letimgData=req.body.imgData;//获取POST请求中的base64图片数据letbase64Data=imgData.replace(/^data:image/w+;base64,/,"");letdataBuffer=Buffer.from(base64Data,'base64');fs.writeFile("image.png",dataBuffer,function(err){if(err){res.send(err);}else{res.send("图片上传成功!");}});});3.4如何实现图片压缩在一些场合中,我们希望在上传本地图片时,先对图片进行一定的压缩,然后再提交到服务器,从而减少传输的数据量。在前端要实现图片压缩,我们可以利用Canvas对象提供的toDataURL()方法,该方法接收type和encoderOptions两个可选参数。
下面我们来看一下具体如何实现图片压缩:
functioncompress(base64,quality,mimeType){letcanvas=document.createElement("canvas");letimg=document.createElement("img");img.crossOrigin="anonymous";returnnewPromise((resolve,reject)=>{img.src=base64;img.onload=()=>{lettargetWidth,targetHeight;if(img.width>MAX_WIDTH){targetWidth=MAX_WIDTH;targetHeight=(img.height*MAX_WIDTH)/img.width;}else{targetWidth=img.width;targetHeight=img.height;}canvas.width=targetWidth;canvas.height=targetHeight;letctx=canvas.getContext("2d");ctx.clearRect(0,0,targetWidth,targetHeight);//清除画布ctx.drawImage(img,0,0,canvas.width,canvas.height);letimageData=canvas.toDataURL(mimeType,quality/100);resolve(imageData);};});}对于返回的DataURL格式的图片数据,为了进一步减少传输的数据量,我们可以把它转换为Blob对象:
functiondataUrlToBlob(base64,mimeType){letbytes=window.atob(base64.split(",")[1]);letab=newArrayBuffer(bytes.length);letia=newUint8Array(ab);for(leti=0;i functionuploadFile(url,blob){letformData=newFormData();letrequest=newXMLHttpRequest();formData.append("image",blob);request.open("POST",url,true);request.send(formData);}3.5如何操作位图像素数据如果想要操作图片像素数据,我们可以利用CanvasRenderingContext2D提供的getImageData来获取图片像素数据,其中getImageData()返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx,sy)、宽为sw、高为sh。其中getImageData方法的语法如下: ctx.getImageData(sx,sy,sw,sh);相应的参数说明如下: 在获取到图片的像素数据之后,我们就可以对获取的像素数据进行处理,比如进行灰度化或反色处理。当完成处理后,若要在页面上显示处理效果,则我们需要利用CanvasRenderingContext2D提供的另一个API——putImageData。 该API是Canvas2DAPI将数据从已有的ImageData对象绘制到位图的方法。如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。putImageData方法的语法如下: voidctx.putImageData(imagedata,dx,dy);voidctx.putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight);相应的参数说明如下: 下图是在线的图片隐写工具,将「“全栈修仙之路”」这6个字隐藏到原始的图片中,然后使用对应的解密工具,解密出隐藏信息的结果: 目前有多种方案可以实现图片隐写,以下是几种常见的方案: