<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖片處理不用愁,給你十個小幫手

          共 16776字,需瀏覽 34分鐘

           ·

          2020-07-14 09:14


          本文阿寶哥會為小伙伴們隆重介紹用于圖片處理的十個?「“小幫手”」,他們各個身懷絕技,擁有模糊、壓縮、裁剪、旋轉(zhuǎn)、合成、比對等技能。相信認識他們之后,你將能夠輕松應(yīng)對大多數(shù)的圖片處理場景。

          不過在介紹?「“小幫手”」?前,阿寶哥會先介紹一些圖片相關(guān)的基礎(chǔ)知識。此外,為了讓小伙伴們能夠?qū)W習(xí)更多圖片相關(guān)的知識,阿寶哥精心準(zhǔn)備了?「“阿寶哥有話說”」?章節(jié)。該章節(jié)你將會學(xué)到以下知識:

          • 如何區(qū)分圖片的類型(非文件后綴名);
          • 如何獲取圖片的尺寸(非右鍵查看圖片信息);
          • 如何預(yù)覽本地圖片(非圖片閱讀器);
          • 如何實現(xiàn)圖片壓縮(非圖片壓縮工具);
          • 如何操作位圖像素數(shù)據(jù)(非 PS 等圖片處理軟件);
          • 如何實現(xiàn)圖片隱寫(非肉眼可見)。

          十個圖片處理?「“小幫手”」?已經(jīng)已經(jīng)迫不及待想與你見面,還在猶豫什么?趕緊出發(fā)吧!

          一、基礎(chǔ)知識

          1.1 位圖

          「位圖圖像(bitmap),亦稱為點陣圖像或柵格圖像,是由稱作像素(圖片元素)的單個點組成的?!?/strong>?這些點可以進行不同的排列和染色以構(gòu)成圖樣。當(dāng)放大位圖時,可以看見賴以構(gòu)成整個圖像的無數(shù)單個方塊。擴大位圖尺寸的效果是增大單個像素,從而使線條和形狀顯得參差不齊。

          「用數(shù)碼相機拍攝的照片、掃描儀掃描的圖片以及計算機截屏圖等都屬于位圖?!?/strong>?位圖的特點是可以表現(xiàn)色彩的變化和顏色的細微過渡,產(chǎn)生逼真的效果,缺點是在保存時需要記錄每一個像素的位置和顏色值,占用較大的存儲空間。常用的位圖處理軟件有 Photoshop、Painter 和 Windows 系統(tǒng)自帶的畫圖工具等。

          分辨率是位圖不可逾越的壁壘,在對位圖進行縮放、旋轉(zhuǎn)等操作時,無法生產(chǎn)新的像素,因此會放大原有的像素填補空白,這樣會讓圖片顯得不清晰。

          57351f953459fc39ca6565ee35b03a00.webp

          (圖片來源:https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%9B%BE)

          圖中的小方塊被稱為像素,這些小方塊都有一個明確的位置和被分配的色彩數(shù)值,小方格顏色和位置就決定該圖像所呈現(xiàn)出來的樣子。

          可以將像素視為整個圖像中不可分割的單位或者是元素。「不可分割的意思是它不能夠再切割成更小單位抑或是元素,它是以一個單一顏色的小格存在。」?每一個點陣圖像包含了一定量的像素,這些像素決定圖像在屏幕上所呈現(xiàn)的大小。

          1.2 矢量圖

          所謂矢量圖,就是使用直線和曲線來描述的圖形,構(gòu)成這些圖形的元素是一些點、線、矩形、多邊形、圓和弧線等,它們都是通過數(shù)學(xué)公式計算獲得的,具有編輯后不失真的特點。例如一幅畫的矢量圖形實際上是由線段形成外框輪廓,由外框的顏色以及外框所封閉的顏色決定畫顯示出的顏色。

          「矢量圖以幾何圖形居多,圖形可以無限放大,不變色、不模糊?!?/strong>?常用于圖案、標(biāo)志、VI、文字等設(shè)計。常用軟件有:CorelDraw、Illustrator、Freehand、XARA、CAD 等。

          這里我們以 Web 開發(fā)者比較熟悉的 SVG(「Scalable Vector Graphics —— 可縮放矢量圖形」)為例,來了解一下 SVG 的結(jié)構(gòu):

          29b8503def7a66e38cd656926060a6a4.webp

          可縮放矢量圖形(英語:Scalable Vector Graphics,SVG)是一種基于可擴展標(biāo)記語言(XML),用于描述二維矢量圖形的圖形格式。SVG 由 W3C 制定,是一個開放標(biāo)準(zhǔn)。

          SVG 主要支持以下幾種顯示對象:

          • 矢量顯示對象,基本矢量顯示對象包括矩形、圓、橢圓、多邊形、直線、任意曲線等;
          • 嵌入式外部圖像,包括 PNG、JPEG、SVG 等;
          • 文字對象。

          了解完位圖與矢量圖的區(qū)別,下面我們來介紹一下位圖的數(shù)學(xué)表示。

          1.3 位圖的數(shù)學(xué)表示

          位圖的像素都分配有特定的位置和顏色值。每個像素的顏色信息由 RGB 組合或者灰度值表示。

          根據(jù)位深度,可將位圖分為1、4、8、16、24 及 32 位圖像等。每個像素使用的信息位數(shù)越多,可用的顏色就越多,顏色表現(xiàn)就越逼真,相應(yīng)的數(shù)據(jù)量越大。

          「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 位數(shù)據(jù)/像素)。通常將使用 24 位 RGB 組合數(shù)據(jù)位表示的的位圖稱為真彩色位圖。

          RGB 彩色圖像可由三種矩陣表示:一種代表像素中紅色的強度,一種代表綠色,另一種代表藍色。

          06e0de440024ee81b460ecea51dd72d2.webp

          (圖片來源:https://freecontent.manning.com/the-computer-vision-pipeline-part-2-input-images/)

          「圖像處理的本質(zhì)實際上就是對這些像素矩陣進行計算?!?/strong>?其實位圖中的圖像類型,除了二值圖像和 RGB 圖像之外,還有灰度圖像、索引圖像和 YUV 圖像。這里我們不做過多介紹,感興趣的小伙伴,請自行查閱相關(guān)資料。

          二、圖片處理庫

          2.1 AlloyImage

          ?

          基于 HTML 5 的專業(yè)級圖像處理開源引擎。

          https://github.com/AlloyTeam/AlloyImage

          ?

          AlloyImage 基于 HTML5 技術(shù)的專業(yè)圖像處理庫,來自騰訊 AlloyTeam 團隊。它擁有以下功能特性:

          • 基于多圖層操作 —— 一個圖層的處理不影響其他圖層;
          • 與 PS 對應(yīng)的 17 種圖層混合模式 —— 便于 PS 處理教程的無縫遷移;
          • 多種基本濾鏡處理效果 —— 基本濾鏡不斷豐富、可擴展;
          • 基本的圖像調(diào)節(jié)功能 —— 色相、飽和度、對比度、亮度、曲線等;
          • 簡單快捷的 API —— 鏈?zhǔn)教幚?、API 簡潔易用、傳參靈活;
          • 多種組合效果封裝 —— 一句代碼輕松實現(xiàn)一種風(fēng)格;
          • 接口一致的單、多線程支持 —— 單、多線程切換無需更改一行代碼,多線程保持快捷 API 特性。

          對于該庫 AlloyTeam 團隊建議的使用場景如下:

          • 桌面軟件客戶端內(nèi)嵌網(wǎng)頁運行方式 >>> 打包 Webkit 內(nèi)核:用戶較大頭像上傳風(fēng)格處理、用戶相冊風(fēng)格處理(處理時間平均 < 1s);
          • Win8 Metro 應(yīng)用 >>> 用戶上傳頭像,比較小的圖片風(fēng)格處理后上傳(Win8 下 IE 10 支持多線程);
          • Mobile APP >>> Andriod 平臺、iOS 平臺小圖風(fēng)格 Web 處理的需求,如 PhoneGap 應(yīng)用,在線頭像上傳時的風(fēng)格處理、Mobile Web 端分享圖片時風(fēng)格處理等。

          「使用示例」

          //?$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/

          ?
          d30dc7ccd52d44fc3fb7207fde2e6b13.webp

          (圖片來源:http://alloyteam.github.io/AlloyImage/)

          2.2 blurify

          ?

          blurify.js is a tiny(~2kb) library to blurred pictures, support graceful downgrade from?css?mode to?canvas?mode.

          https://github.com/JustClear/blurify

          ?

          blurify.js 是一個用于圖片模糊,很小的 JavaScript 庫(約 2 kb),并支持從 CSS 模式到 Canvas 模式的優(yōu)雅降級。該插件支持三種模式:

          • css 模式:使用?filter?屬性,默認模式;
          • canvas 模式:使用?canvas?導(dǎo)出 base64;
          • auto 模式:優(yōu)先使用 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/

          ?
          f77c2ca5ac36b6c66f79fb1af47c081e.webp

          (圖片來源:https://justclear.github.io/blurify/)

          看到這里是不是有些小伙伴覺得只是模糊處理而已,覺得不過癮,能不能來點更酷的。嘿嘿,有求必應(yīng)!阿寶哥立馬來個?「“酷炫叼”」?的庫 —— midori,該庫用于為背景圖創(chuàng)建動畫,使用 three.js 編寫并使用 WebGL。本來是想給個演示動圖,無奈單個 Gif 文件太大,只能放個體驗地址,感興趣的小伙伴自行體驗一下。

          ?

          midori 示例地址:https://aeroheim.github.io/midori/

          ?

          2.3 cropperjs

          ?

          JavaScript image cropper.

          https://github.com/fengyuanchen/cropperjs

          ?

          Cropper.js 是一款非常強大卻又簡單的圖片裁剪工具,它可以進行非常靈活的配置,支持手機端使用,支持包括 IE9 以上的現(xiàn)代瀏覽器。它可以用于滿足諸如裁剪頭像上傳、商品圖片編輯之類的需求。

          Cropper.js 支持以下特性:

          • 支持 39 個配置選項;
          • 支持 27 個方法;
          • 支持 6 種事件;
          • 支持 touch(移動端);
          • 支持縮放、旋轉(zhuǎn)和翻轉(zhuǎn);
          • 支持在畫布上裁剪;
          • 支持在瀏覽器端通過畫布裁剪圖像;
          • 支持處理 Exif 方向信息;
          • 跨瀏覽器支持。
          ?

          可交換圖像文件格式(英語:Exchangeable image file format,官方簡稱 Exif),是專門為數(shù)碼相機的照片設(shè)定的文件格式,可以記錄數(shù)碼照片的屬性信息和拍攝數(shù)據(jù)。Exif 可以附加于 JPEG、TIFF、RIFF 等文件之中,為其增加有關(guān)數(shù)碼相機拍攝信息的內(nèi)容和索引圖或圖像處理軟件的版本信息。

          Exif 信息以 0xFFE1 作為開頭標(biāo)記,后兩個字節(jié)表示 Exif 信息的長度。所以 Exif 信息最大為 64 kB,而內(nèi)部采用 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/

          ?
          8abac2e9799919603f350db955a70305.webp

          2.4 compressorjs

          ?

          JavaScript image compressor.

          https://github.com/fengyuanchen/compressorjs

          ?

          compressorjs 是 JavaScript 圖像壓縮器。使用瀏覽器原生的?canvas.toBlob?API 進行壓縮工作,這意味著它是有損壓縮。通常的使用場景是,在瀏覽器端圖片上傳之前對其進行預(yù)壓縮。

          在瀏覽器端要實現(xiàn)圖片壓縮,除了使用?canvas.toBlob?API 之外,還可以使用 Canvas 提供的另一個 API,即?toDataURL?API,它接收?type?和?encoderOptions?兩個可選參數(shù)。

          其中?type?表示圖片格式,默認為?image/png。而?encoderOptions?用于表示圖片的質(zhì)量,在指定圖片格式為?image/jpeg?或?image/webp?的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量。如果超出取值范圍,將會使用默認值?0.92,其他參數(shù)會被忽略。

          相比?canvas.toDataURL?API 來說,canvas.toBlob?API 是異步的,因此多了個?callback?參數(shù),這個?callback?回調(diào)方法默認的第一個參數(shù)就是轉(zhuǎn)換好的?blob?文件信息。canvas.toBlob?的簽名如下:

          canvas.toBlob(callback,?mimeType,?qualityArgument)

          「使用示例」

          import?axios?from?'axios';
          import?Compressor?from?'compressorjs';

          //?
          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);
          ????},
          ??});
          });

          「在線示例」

          ?

          https://fengyuanchen.github.io/compressorjs/

          ?
          eb17b22a29809ba3b39d16db69c40cda.webp

          2.5 fabric.js

          ?

          Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser.

          https://github.com/fabricjs/fabric.js

          ?

          Fabric.js 是一個框架,可讓你輕松使用 HTML5 Canvas 元素。它是一個位于 Canvas 元素之上的交互式對象模型,同時也是一個?「SVG-to-canvas」?的解析器。

          使用 Fabric.js,你可以在畫布上創(chuàng)建和填充對象。所謂的對象,可以是簡單的幾何形狀,比如矩形,圓形,橢圓形,多邊形,或更復(fù)雜的形狀,包含數(shù)百或數(shù)千個簡單路徑。然后,你可以使用鼠標(biāo)縮放,移動和旋轉(zhuǎn)這些對象。并修改它們的屬性 —— 顏色,透明度,z-index 等。此外你還可以一起操縱這些對象,即通過簡單的鼠標(biāo)選擇將它們分組。

          Fabric.js 支持所有主流的瀏覽器,具體的兼容情況如下:

          • Firefox 2+
          • Safari 3+
          • Opera 9.64+
          • Chrome(所有版本)
          • IE10,IE11,Edge

          「使用示例」


          <html>
          <head>head>
          <body>
          ????<canvas?id="canvas"?width="300"?height="300">canvas>
          ????<script?src="lib/fabric.js">script>
          ????<script>
          ????????var?canvas?=?new?fabric.Canvas('canvas');
          ????????var?rect?=?new?fabric.Rect({
          ????????????top?:?100,
          ????????????left?:?100,
          ????????????width?:?60,
          ????????????height?:?70,
          ????????????fill?:?'red'
          ????????});

          ????????canvas.add(rect);
          ????
          script>
          body>
          html>

          「在線示例」

          ?

          http://fabricjs.com/kitchensink

          ?
          b2c8e5765cbdc3b0ee530f12bbea7823.webp

          (圖片來源:https://github.com/fabricjs/fabric.js)

          2.6 Resemble.js

          ?

          Image analysis and comparison

          https://github.com/rsmbl/Resemble.js

          ?

          Resemble.js 使用 HTML Canvas 和 JavaScript 來實現(xiàn)圖片的分析和比較。兼容大于 8.0 的 Node.js 版本。

          「使用示例」

          //?比較兩張圖片
          var?diff?=?resemble(file)
          ????.compareTo(file2)
          ????.ignoreColors()
          ????.onComplete(function(data)?{
          ????????console.log(data);
          ?????/*
          ?????{
          ????????misMatchPercentage?:?100,?//?%
          ????????isSameDimensions:?true,?//?or?false
          ????????dimensionDifference:?{?width:?0,?height:?-1?},?
          ????????getImageDataUrl:?function(){}
          ?????}
          ????*/

          });

          「在線示例」

          ?

          http://rsmbl.github.io/Resemble.js/

          ?
          7e667c8cb8c957d823a1c7b34c32824c.webp

          2.7 Pica

          ?

          Resize image in browser with high quality and high speed

          https://github.com/nodeca/pica

          ?

          Pica 可用于在瀏覽器中調(diào)整圖像大小,沒有像素化并且相當(dāng)快。它會自動選擇最佳的可用技術(shù):webworkers,webassembly,createImageBitmap,純 JS。

          借助 Pica,你可以實現(xiàn)以下功能:

          • 減小大圖像的上傳大小,節(jié)省上傳時間;
          • 在圖像處理上節(jié)省服務(wù)器資源;
          • 在瀏覽器中生成縮略圖。

          「使用示例」

          const?pica?=?require('pica')();

          //?調(diào)整畫布/圖片的大小
          pica.resize(from,?to,?{
          ??unsharpAmount:?80,
          ??unsharpRadius:?0.6,
          ??unsharpThreshold:?2
          })
          .then(result?=>?console.log('resize?done!'));

          //?調(diào)整大小并轉(zhuǎn)換為Blob
          pica.resize(from,?to)
          ??.then(result?=>?pica.toBlob(result,?'image/jpeg',?0.90))
          ??.then(blob?=>?console.log('resized?to?canvas?&?created?blob!'));

          「在線示例」

          ?

          http://nodeca.github.io/pica/demo/

          ?
          04fb8bd766073b29e38257169edf7f00.webp

          2.8 tui.image-editor

          ?

          ?? Full-featured photo image editor using canvas. It is really easy, and it comes with great filters.

          https://github.com/nhn/tui.image-editor

          ?

          tui.image-editor 是使用 HTML5 Canvas 的全功能圖像編輯器。它易于使用,并提供強大的過濾器。同時它支持對圖像進行裁剪、翻轉(zhuǎn)、旋轉(zhuǎn)、繪圖、形狀、文本、遮罩和圖片過濾等操作。

          tui.image-editor 的瀏覽器兼容情況如下:

          • Chrome
          • Edge
          • Safari
          • Firefox
          • IE 10+

          「使用示例」

          //?Image?editor
          var?imageEditor?=?new?tui.ImageEditor("#tui-image-editor-container",?{
          ?????includeUI:?{
          ???????loadImage:?{
          ?????????path:?"img/sampleImage2.png",
          ?????????name:?"SampleImage",
          ???????},
          ???????theme:?blackTheme,?//?or?whiteTheme
          ?????????initMenu:?"filter",
          ?????????menuBarPosition:?"bottom",
          ???????},
          ???????cssMaxWidth:?700,
          ???????cssMaxHeight:?500,
          ???????usageStatistics:?false,
          });

          window.onresize?=?function?()?{
          ??imageEditor.ui.resizeEditor();
          };

          在線示例

          ?

          https://ui.toast.com/tui-image-editor/

          ?
          0b7b5f5fabff2451a3795df29d466e43.webp

          2.9 gif.js

          ?

          JavaScript GIF encoding library

          https://github.com/jnordberg/gif.js

          ?

          gif.js 是運行在瀏覽器端的 JavaScript GIF 編碼器。它使用類型化數(shù)組和 Web Worker 在后臺渲染每一幀,速度真的很快。該庫可工作在支持:Web Workers,F(xiàn)ile API 和 Typed Arrays 的瀏覽器中。

          gif.js 的瀏覽器兼容情況如下:

          • Google Chrome
          • Firefox 17
          • Safari 6
          • Internet Explorer 10
          • Mobile Safari iOS 6

          「使用示例」

          var?gif?=?new?GIF({
          ??workers:?2,
          ??quality:?10
          });

          //?add?an?image?element
          gif.addFrame(imageElement);

          //?or?a?canvas?element
          gif.addFrame(canvasElement,?{delay:?200});

          //?or?copy?the?pixels?from?a?canvas?context
          gif.addFrame(ctx,?{copy:?true});

          gif.on('finished',?function(blob)?{
          ??window.open(URL.createObjectURL(blob));
          });

          gif.render();

          「在線示例」

          ?

          http://jnordberg.github.io/gif.js/

          ?
          09060f82d72833d54b44ea638ce460ad.webp1d206c1216b7c8afb680d557a306e53b.webp

          2.10 Sharp

          ?

          High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library.

          https://github.com/lovell/sharp

          ?

          Sharp 的典型應(yīng)用場景是將常見格式的大圖像轉(zhuǎn)換為尺寸較小,對網(wǎng)絡(luò)友好的 JPEG,PNG 和 WebP 格式的圖像。由于其內(nèi)部使用 libvips ,使得調(diào)整圖像大小通常比使用 ImageMagick 和 GraphicsMagick 設(shè)置快 4-5 倍 。除了支持調(diào)整圖像大小之外,Sharp 還支持旋轉(zhuǎn)、提取、合成和伽馬校正等功能。

          Sharp 支持讀取 JPEG,PNG,WebP,TIFF,GIF 和 SVG 圖像。輸出圖像可以是 JPEG,PNG,WebP 和 TIFF 格式,也可以是未壓縮的原始像素數(shù)據(jù)。

          「使用示例」

          //?改變圖像尺寸
          sharp(inputBuffer)
          ??.resize(320,?240)
          ??.toFile('output.webp',?(err,?info)?=>?{?...?});
          ???????
          //?旋轉(zhuǎn)輸入圖像并改變圖片尺寸?????????????????????????????????????????
          sharp('input.jpg')
          ??.rotate()
          ??.resize(200)
          ??.toBuffer()
          ??.then(?data?=>?{?...?})
          ??.catch(?err?=>?{?...?});?????????????????????????????????????????

          「在線示例」

          ?

          https://segmentfault.com/a/1190000012903787

          ?

          該示例是來自阿寶哥 18 年寫的 “Sharp 牛刀小試之生成專屬分享圖片” 這篇文章,主要是利用 Sharp 提供的圖片合成功能為每個用戶生成專屬的分享海報,感興趣的小伙伴可以閱讀一下原文喲。

          const?sharp?=?require("sharp");
          const?TextToSVG?=?require("text-to-svg");
          const?path?=?require("path");

          //?加載字體文件
          const?textToSVG?=?TextToSVG.loadSync(path.join(__dirname,?"./simhei.ttf"));

          //?創(chuàng)建圓形SVG,用于實現(xiàn)頭像裁剪
          const?roundedCorners?=?new?Buffer(
          ??''
          );

          //?設(shè)置SVG文本元素相關(guān)參數(shù)
          const?attributes?=?{?fill:?"white"?};
          const?svgOptions?=?{
          ??x:?0,
          ??y:?0,
          ??fontSize:?32,
          ??anchor:?"top",
          ??attributes:?attributes
          };

          /**
          ?*?使用文本生成SVG
          ?*?@param?{*}?text?
          ?*?@param?{*}?options?
          ?*/

          function?textToSVGFn(text,?options?=?svgOptions)?{
          ??return?textToSVG.getSVG(text,?options);
          }

          /**
          ?*?圖層疊加生成分享圖片
          ?*?@param?{*}?options?
          ?*?
          ?*/

          async?function?genShareImage(options)?{
          ??const?{?backgroudPath,?avatarPath,?qrcodePath,?
          ????userName,?words,?likes,?outFilePath
          ??}?=?options;

          ??//?背景圖片
          ??const?backgroudBuffer?=?sharp(path.join(__dirname,?backgroudPath)).toBuffer({
          ????resolveWithObject:?true
          ??});

          ??const?backgroundImageInfo?=?await?backgroudBuffer;
          ??//?頭像圖片
          ??const?avatarBuffer?=?await?genCircleAvatar(path.join(__dirname,?avatarPath));

          ??//?二維碼圖片
          ??const?qrCodeBuffer?=?await?sharp(path.join(__dirname,?qrcodePath))
          ????.resize(180)
          ????.toBuffer({
          ??????resolveWithObject:?true
          ????});

          ??//?用戶名
          ??const?userNameSVG?=?textToSVGFn(userName);
          ??//?用戶數(shù)據(jù)
          ??const?userDataSVG?=?textToSVGFn(`寫了${words}個字???收獲${likes}個贊`);
          ??const?userNameBuffer?=?await?sharp(new?Buffer(userNameSVG)).toBuffer({
          ????resolveWithObject:?true
          ??});
          ??const?userDataBuffer?=?await?sharp(new?Buffer(userDataSVG)).toBuffer({
          ????resolveWithObject:?true
          ??});

          ??const?buffers?=?[avatarBuffer,?qrCodeBuffer,?userNameBuffer,?userDataBuffer];
          ??//?圖層疊加參數(shù)列表
          ??const?overlayOptions?=?[
          ????{?top:?150,?left:?230?},
          ????{?top:?861,?left:?227?},
          ????{
          ??????top:?365,
          ??????left:?(backgroundImageInfo.info.width?-?userNameBuffer.info.width)?/?2
          ????},
          ????{
          ??????top:?435,
          ??????left:?(backgroundImageInfo.info.width?-?userDataBuffer.info.width)?/?2
          ????}
          ??];

          ??//?組合多個圖層:圖片+文字圖層
          ??return?buffers
          ????.reduce((input,?overlay,?index)?=>?{
          ??????return?input.then(result?=>?{
          ????????console.dir(overlay.info);
          ????????return?sharp(result.data)
          ??????????.overlayWith(overlay.data,?overlayOptions[index])
          ??????????.toBuffer({?resolveWithObject:?true?});
          ??????});
          ????},?backgroudBuffer)
          ????.then((data)?=>?{
          ??????return?sharp(data.data).toFile(outFilePath);
          ????}).catch(error?=>?{
          ??????throw?new?Error('Generate?Share?Image?Failed.');
          ????});
          }

          /**
          ?*?生成圓形的頭像
          ?*?@param?{*}?avatarPath?頭像路徑
          ?*/

          function?genCircleAvatar(avatarPath)?{
          ??return?sharp(avatarPath)
          ????.resize(180,?180)
          ????.overlayWith(roundedCorners,?{?cutout:?true?})
          ????.png()
          ????.toBuffer({
          ??????resolveWithObject:?true
          ????});
          }

          module.exports?=?{
          ??genShareImage
          };

          三、阿寶哥有話說

          3.1 如何區(qū)分圖片的類型

          「計算機并不是通過圖片的后綴名來區(qū)分不同的圖片類型,而是通過 “魔數(shù)”(Magic Number)來區(qū)分。」?對于某一些類型的文件,起始的幾個字節(jié)內(nèi)容都是固定的,跟據(jù)這幾個字節(jié)的內(nèi)容就可以判斷文件的類型。

          常見圖片類型對應(yīng)的魔數(shù)如下表所示:

          文件類型文件后綴魔數(shù)
          JPEGjpg/jpeg0xFFD8FF
          PNGpng0x89504E47
          GIFgif0x47494638(GIF8)
          BMPbmp0x424D

          這里我們以阿寶哥的頭像(abao.png)為例,驗證一下該圖片的類型是否正確:

          2ae81955db9a66b02f72ca5bf0fd4f9d.webp

          在日常開發(fā)過程中,如果遇到檢測圖片類型的場景,我們可以直接利用一些現(xiàn)成的第三方庫。比如,你想要判斷一張圖片是否為 PNG 類型,這時你可以使用 is-png 這個庫,它同時支持瀏覽器和 Node.js,使用示例如下:

          「Node.js」

          //?npm?install?read-chunk
          const?readChunk?=?require('read-chunk');?
          const?isPng?=?require('is-png');
          const?buffer?=?readChunk.sync('unicorn.png',?0,?8);

          isPng(buffer);
          //=>?true

          「Browser」

          (async?()?=>?{
          ?const?response?=?await?fetch('unicorn.png');
          ?const?buffer?=?await?response.arrayBuffer();

          ?isPng(new?Uint8Array(buffer));
          ?//=>?true
          })();

          3.2 如何獲取圖片的尺寸

          圖片的尺寸、位深度、色彩類型和壓縮算法都會存儲在文件的二進制數(shù)據(jù)中,我們繼續(xù)以阿寶哥的頭像(abao.png)為例,來了解一下實際的情況:

          cfdce57dff8dc9ad2345291ce36ff187.webp
          ?

          528(十進制) => 0x0210

          560(十進制)=> 0x0230

          ?

          因此如果想要獲取圖片的尺寸,我們就需要依據(jù)不同的圖片格式對圖片二進制數(shù)據(jù)進行解析。幸運的是,我們不需要自己做這件事,image-size 這個 Node.js 庫已經(jīng)幫我們實現(xiàn)了獲取主流圖片類型文件尺寸的功能:

          「同步方式」

          var?sizeOf?=?require('image-size');

          var?dimensions?=?sizeOf('images/abao.png');
          console.log(dimensions.width,?dimensions.height);

          「異步方式」

          var?sizeOf?=?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 等格式。

          3.3 如何預(yù)覽本地圖片

          利用 HTML FileReader API,我們也可以方便的實現(xiàn)圖片本地預(yù)覽功能,具體代碼如下:

          <input?type="file"?accept="image/*"?onchange="loadFile(event)">
          <img?id="output"/>
          <script>
          ??const?loadFile?=?function(event)?{
          ????const?reader?=?new?FileReader();
          ????reader.onload?=?function(){
          ??????const?output?=?document.querySelector('output');
          ??????output.src?=?reader.result;
          ????};
          ????reader.readAsDataURL(event.target.files[0]);
          ??};
          script>

          在完成本地圖片預(yù)覽之后,可以直接把圖片對應(yīng)的 Data URLs 數(shù)據(jù)提交到服務(wù)器。針對這種情形,服務(wù)端需要做一些相關(guān)處理,才能正常保存上傳的圖片,這里以 Express 為例,具體處理代碼如下:

          const?app?=?require('express')();

          app.post('/upload',?function(req,?res){
          ????let?imgData?=?req.body.imgData;?//?獲取POST請求中的base64圖片數(shù)據(jù)
          ????let?base64Data?=?imgData.replace(/^data:image\/\w+;base64,/,?"");
          ????let?dataBuffer?=?Buffer.from(base64Data,?'base64');
          ????fs.writeFile("image.png",?dataBuffer,?function(err)?{
          ????????if(err){
          ??????????res.send(err);
          ????????}else{
          ??????????res.send("圖片上傳成功!");
          ????????}
          ????});
          });

          3.4 如何實現(xiàn)圖片壓縮

          在一些場合中,我們希望在上傳本地圖片時,先對圖片進行一定的壓縮,然后再提交到服務(wù)器,從而減少傳輸?shù)臄?shù)據(jù)量。在前端要實現(xiàn)圖片壓縮,我們可以利用 Canvas 對象提供的?toDataURL()?方法,該方法接收?type?和?encoderOptions?兩個可選參數(shù)。

          其中?type?表示圖片格式,默認為?image/png。而?encoderOptions?用于表示圖片的質(zhì)量,在指定圖片格式為?image/jpeg?或?image/webp?的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量。如果超出取值范圍,將會使用默認值?0.92,其他參數(shù)會被忽略。

          下面我們來看一下具體如何實現(xiàn)圖片壓縮:

          function?compress(base64,?quality,?mimeType)?{
          ??let?canvas?=?document.createElement("canvas");
          ??let?img?=?document.createElement("img");
          ??img.crossOrigin?=?"anonymous";
          ??return?new?Promise((resolve,?reject)?=>?{
          ????img.src?=?base64;
          ????img.onload?=?()?=>?{
          ??????let?targetWidth,?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;
          ??????let?ctx?=?canvas.getContext("2d");
          ??????ctx.clearRect(0,?0,?targetWidth,?targetHeight);?//?清除畫布
          ??????ctx.drawImage(img,?0,?0,?canvas.width,?canvas.height);
          ??????let?imageData?=?canvas.toDataURL(mimeType,?quality?/?100);
          ??????resolve(imageData);
          ????};
          ??});
          }

          對于返回的 Data URL 格式的圖片數(shù)據(jù),為了進一步減少傳輸?shù)臄?shù)據(jù)量,我們可以把它轉(zhuǎn)換為 Blob 對象:

          function?dataUrlToBlob(base64,?mimeType)?{
          ??let?bytes?=?window.atob(base64.split(",")[1]);
          ??let?ab?=?new?ArrayBuffer(bytes.length);
          ??let?ia?=?new?Uint8Array(ab);
          ??for?(let?i?=?0;?i?????ia[i]?=?bytes.charCodeAt(i);
          ??}
          ??return?new?Blob([ab],?{?type:?mimeType?});
          }

          在轉(zhuǎn)換完成后,我們就可以壓縮后的圖片對應(yīng)的 Blob 對象封裝在 FormData 對象中,然后再通過 AJAX 提交到服務(wù)器上:

          function?uploadFile(url,?blob)?{
          ??let?formData?=?new?FormData();
          ??let?request?=?new?XMLHttpRequest();
          ??formData.append("image",?blob);
          ??request.open("POST",?url,?true);
          ??request.send(formData);
          }

          3.5 如何操作位圖像素數(shù)據(jù)

          如果想要操作圖片像素數(shù)據(jù),我們可以利用 CanvasRenderingContext2D 提供的?getImageData?來獲取圖片像素數(shù)據(jù),其中 getImageData() 返回一個 ImageData 對象,用來描述 canvas 區(qū)域隱含的像素數(shù)據(jù),這個區(qū)域通過矩形表示,起始點為(sx, sy)、寬為 sw、高為 sh。其中?getImageData?方法的語法如下:

          ctx.getImageData(sx,?sy,?sw,?sh);

          相應(yīng)的參數(shù)說明如下:

          • sx:將要被提取的圖像數(shù)據(jù)矩形區(qū)域的左上角 x 坐標(biāo)。
          • sy:將要被提取的圖像數(shù)據(jù)矩形區(qū)域的左上角 y 坐標(biāo)。
          • sw:將要被提取的圖像數(shù)據(jù)矩形區(qū)域的寬度。
          • sh:將要被提取的圖像數(shù)據(jù)矩形區(qū)域的高度。

          在獲取到圖片的像素數(shù)據(jù)之后,我們就可以對獲取的像素數(shù)據(jù)進行處理,比如進行灰度化或反色處理。當(dāng)完成處理后,若要在頁面上顯示處理效果,則我們需要利用 CanvasRenderingContext2D 提供的另一個 API ——?putImageData。

          該 API 是 Canvas 2D API 將數(shù)據(jù)從已有的 ImageData 對象繪制到位圖的方法。如果提供了一個繪制過的矩形,則只繪制該矩形的像素。此方法不受畫布轉(zhuǎn)換矩陣的影響。putImageData 方法的語法如下:

          void?ctx.putImageData(imagedata,?dx,?dy);
          void?ctx.putImageData(imagedata,?dx,?dy,?dirtyX,?dirtyY,?dirtyWidth,?dirtyHeight);

          相應(yīng)的參數(shù)說明如下:

          • imageData:?ImageData?,包含像素值的數(shù)組對象。
          • dx:源圖像數(shù)據(jù)在目標(biāo)畫布中的位置偏移量(x 軸方向的偏移量)。
          • dy:源圖像數(shù)據(jù)在目標(biāo)畫布中的位置偏移量(y 軸方向的偏移量)。
          • dirtyX(可選):在源圖像數(shù)據(jù)中,矩形區(qū)域左上角的位置。默認是整個圖像數(shù)據(jù)的左上角(x 坐標(biāo))。
          • dirtyY(可選):在源圖像數(shù)據(jù)中,矩形區(qū)域左上角的位置。默認是整個圖像數(shù)據(jù)的左上角(y 坐標(biāo))。
          • dirtyWidth(可選):在源圖像數(shù)據(jù)中,矩形區(qū)域的寬度。默認是圖像數(shù)據(jù)的寬度。
          • dirtyHeight(可選):在源圖像數(shù)據(jù)中,矩形區(qū)域的高度。默認是圖像數(shù)據(jù)的高度。

          介紹完相關(guān)的 API,下面我們來舉一個實際例子:


          <html?lang="zh-CN">
          ??<head>
          ????<meta?charset="UTF-8"?/>
          ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
          ????<title>圖片反色和灰度化處理title>
          ??head>
          ??<body?onload="loadImage()">
          ????<div>
          ??????<button?id="invertbtn">反色button>
          ??????<button?id="grayscalebtn">灰度化button>
          ????div>
          ????<canvas?id="canvas"?width="800"?height="600">canvas>
          ????<script>
          ??????function?loadImage()?{
          ????????var?img?=?new?Image();
          ????????img.crossOrigin?=?"";
          ????????img.onload?=?function?()?{
          ??????????draw(this);
          ????????};
          ????????//?這是阿寶哥的頭像喲
          ????????img.src?=?"https://avatars3.githubusercontent.com/u/4220799";
          ??????}

          ??????function?draw(img)?{
          ????????var?canvas?=?document.getElementById("canvas");
          ????????var?ctx?=?canvas.getContext("2d");
          ????????ctx.drawImage(img,?0,?0);
          ????????img.style.display?=?"none";
          ????????var?imageData?=?ctx.getImageData(0,?0,?canvas.width,?canvas.height);
          ????????var?data?=?imageData.data;

          ????????var?invert?=?function?()?{
          ??????????for?(var?i?=?0;?i?4
          )?{
          ????????????data[i]?=?255?-?data[i];?//?red
          ????????????data[i?+?1]?=?255?-?data[i?+?1];?//?green
          ????????????data[i?+?2]?=?255?-?data[i?+?2];?//?blue
          ??????????}
          ??????????ctx.putImageData(imageData,?0,?0);
          ????????};

          ????????var?grayscale?=?function?()?{
          ??????????for?(var?i?=?0;?i?4)?{
          ????????????var?avg?=?(data[i]?+?data[i?+?1]?+?data[i?+?2])?/?3;
          ????????????data[i]?=?avg;?//?red
          ????????????data[i?+?1]?=?avg;?//?green
          ????????????data[i?+?2]?=?avg;?//?blue
          ??????????}
          ??????????ctx.putImageData(imageData,?0,?0);
          ????????};

          ????????var?invertbtn?=?document.getElementById("invertbtn");
          ????????invertbtn.addEventListener("click",?invert);
          ????????var?grayscalebtn?=?document.getElementById("grayscalebtn");
          ????????grayscalebtn.addEventListener("click",?grayscale);
          ??????}
          ????script>
          ??body>
          html>

          需要注意的在調(diào)用?getImageData?方法獲取圖片像素數(shù)據(jù)時,你可能會遇到跨域問題,比如:

          Uncaught?DOMException:?Failed?to?execute?'getImageData'?on?'CanvasRenderingContext2D':?The?canvas?has?been?tainted?by?cross-origin?data.

          對于這個問題,你可以閱讀?「張鑫旭」?大神 “解決canvas圖片getImageData,toDataURL跨域問題” 這一篇文章。

          3.6 如何實現(xiàn)圖片隱寫

          「隱寫術(shù)是一門關(guān)于信息隱藏的技巧與科學(xué),所謂信息隱藏指的是不讓除預(yù)期的接收者之外的任何人知曉信息的傳遞事件或者信息的內(nèi)容?!?/strong>?隱寫術(shù)的英文叫做 Steganography,來源于特里特米烏斯的一本講述密碼學(xué)與隱寫術(shù)的著作 Steganographia,該書書名源于希臘語,意為 “隱秘書寫”。

          下圖是阿寶哥采用在線的圖片隱寫工具,將?「“全棧修仙之路”」?這 6 個字隱藏到原始的圖片中,然后使用對應(yīng)的解密工具,解密出隱藏信息的結(jié)果:

          22eee05c75297d1e2da2aa336f744c38.webp

          (在線圖片隱寫體驗地址:https://c.p2hp.com/yinxietu/)

          目前有多種方案可以實現(xiàn)圖片隱寫,以下是幾種常見的方案:

          • 附加式的圖片隱寫;
          • 基于文件結(jié)構(gòu)的圖片隱寫;
          • 基于 LSB 原理的圖片隱寫;
          • 基于 DCT 域的 JPG 圖片隱寫;
          • 數(shù)字水印的隱寫;
          • 圖片容差的隱寫。

          篇幅有限,這里我們就不繼續(xù)展開,分別介紹每種方案,感興趣的小伙伴可以閱讀 “隱寫術(shù)之圖片隱寫(一)” 這篇文章。

          四、參考資源

          • Baike - 矢量圖
          • Wiki - 可縮放矢量圖形
          • 隱寫術(shù)之圖片隱寫(一)
          • 不能說的秘密——前端也能玩的圖片隱寫術(shù)
          • 又拍圖片管家億級圖像之搜圖系統(tǒng)的兩代演進及底層原理
          • image-manipulation-libraries-for-javascript

          支持

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:


          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長期關(guān)系

          3. 關(guān)注公眾號「高級前端進階」,公眾號后臺回復(fù)「面試題」 送你高級前端面試題,回復(fù)「加群」加入面試互助交流群


          》》面試官都在用的題庫,快來看看《《

          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    午夜寂寞人妻 | 亚洲三级精品视频 | 成人做爰A片一区二区app | 俺去也在线视频 | 伊人大香蕉在线视频网 |