介紹
數字影像處理是利用計算機分析和操縱影像的方法。該過程包括讀取影像、應用方法來改變或增強影像,然後保存處理過的影像。處理處理用戶上傳內容的應用程式處理影像是很常見的。例如,如果你正在編寫一個允許用戶上傳圖像的 Web 應用程序,用戶可能會上傳不必要的大圖像。這可能會對應用程序的加載速度造成負面影響,同時也會浪費你的服務器空間。通過影像處理,你的應用程序可以調整大小並壓縮所有用戶上傳的圖像,這可以顯著改善應用程序的性能並節省你的服務器磁盤空間。
Node.js擁有一個可用於處理圖像的庫生態系統,例如sharp、jimp和gm module。本文將聚焦於sharp模塊。sharp是一個流行的Node.js圖像處理庫,支援各種圖像文件格式,如JPEG、PNG、GIF、WebP、AVIF、SVG和TIFF。
在本教程中,您將使用sharp來讀取圖像並提取其元數據,調整大小,更改圖像格式和壓縮圖像。然後,您將裁剪、灰度化、旋轉和模糊化圖像。最後,您將合成圖像,並在圖像上添加文本。通過本教程的結尾,您將對如何在Node.js中處理圖像有一個良好的理解。
先決條件
要完成本教程,您需要:
-
在您的本地開發環境中設置Node.js。您可以參考如何安裝Node.js並建立本地開發環境了解如何在系統上安裝Node.js和npm。
-
有關如何編寫和運行Node.js程序的基本知識。您可以參考如何編寫和運行您的第一個Node.js程序來了解基礎知識。
-
Javascript中異步編程的基本理解。參考理解JavaScript中的事件循環、回調函數、Promise和Async/Await來複習異步編程。
步驟 1 — 設置專案目錄並下載圖片
在開始編寫代碼之前,您需要創建包含代碼和本文將使用的圖片的目錄。
打開您的終端並使用 mkdir
命令創建項目的目錄:
使用 cd
命令進入新創建的目錄:
使用 npm init
命令創建一個 package.json
文件來跟踪專案的依賴項:
使用 -y
選項告訴 npm
創建默認的 package.json
文件。
接下來,安裝 sharp
作為一個依賴項:
您將在本教程中使用以下三個圖片:
接下來,使用curl
命令下載您項目目錄中的圖像。
使用以下命令下載第一張圖像。這將把圖像下載為sammy.png
:
接著,使用以下命令下載第二張圖像。這將把圖像下載為underwater.png
:
最後,使用以下命令下載第三張圖像。這將把圖像下載為sammy-transparent.png
:
項目目錄和相依性已設置完成,您現在可以開始處理圖像了。
步驟 2 — 讀取圖像並輸出元數據
在本節中,您將編寫代碼來讀取圖像並提取其元數據。圖像元數據是嵌入到圖像中的文本,包括有關圖像的信息,例如其類型、寬度和高度。
提取元数据,首先导入 sharp 模块,创建一个 sharp
实例,并将图像路径作为参数传递。之后,您将在实例上链接 metadata()
方法以提取元数据并将其记录到控制台中。
为此,请在您首选的文本编辑器中创建并打开 readImage.js
文件。本教程使用一个名为 nano
的终端文本编辑器:
接下来,在文件顶部引入 sharp
:
sharp 是一个基于 promise 的图像处理模块。当您创建一个 sharp
实例时,它返回一个 promise。您可以使用 then
方法解决 promise,或者使用 async/await
,它具有更清晰的语法。
要使用 async/await
语法,您需要通过在函数开头放置 async
关键字来创建一个异步函数。这将允许您在函数内部使用 await
关键字来解决读取图像时返回的 promise。
在您的 readImage.js
文件中,定义一个异步函数 getMetadata()
,用于读取图像、提取其元数据并将其记录到控制台中:
getMetadata()
是一個異步函數,因為你在 function
標籤之前定義了 async
關鍵字。這允許你在函數內部使用 await
語法。 getMetadata()
函數將讀取一個圖像並返回包含其元數據的對象。
在函數體內,通過調用 sharp()
來讀取圖像,它將圖像路徑作為參數,這裡使用的是 sammy.png
。
除了接受圖像路徑外,sharp()
還可以讀取存儲在 Buffer、Uint8Array 或 Uint8ClampedArray 中的圖像數據,前提是圖像是 JPEG、PNG、GIF、WebP、AVIF、SVG 或 TIFF 格式。
當你使用 sharp()
讀取圖像時,它將創建一個 sharp
實例。然後,你將鏈接到 sharp 模塊的 metadata()
方法到該實例上。該方法返回一個包含圖像元數據的對象,你將其存儲在 metadata
變量中並使用 console.log()
輸出其內容。
現在,你的程序可以讀取圖像並返回其元數據。但是,如果程序在執行過程中出現錯誤,它將崩潰。為了解決這個問題,你需要在錯誤發生時捕獲它們。
要做到這一點,請將getMetadata()
函數中的代碼包裹在try...catch
塊中:
在try
塊內,您讀取一張圖像,提取並記錄其元數據。當此過程中發生錯誤時,執行會跳轉到catch
部分並記錄錯誤,以防止程序崩潰。
最後,通過添加突出顯示的行調用getMetadata()
函數:
現在,保存並退出文件。輸入y
以保存您對文件所做的更改,然後按ENTER
或RETURN
鍵確認文件名。
使用node
命令運行文件:
您應該會看到類似於此的輸出:
Output{
format: 'png',
width: 750,
height: 483,
space: 'srgb',
channels: 3,
depth: 'uchar',
density: 72,
isProgressive: false,
hasProfile: false,
hasAlpha: false
}
現在您已經讀取了一張圖像並提取了其元數據,現在您將調整圖像的大小,更改其格式並對其進行壓縮。
第 3 步 — 調整大小、更改圖像格式和壓縮圖像
調整大小是改變圖像尺寸而不刪除任何內容的過程,這會影響圖像文件大小。在此部分中,您將調整圖像大小,更改圖像類型並壓縮圖像。圖像壓縮是在不損失質量的情況下減小圖像文件大小的過程。
首先,您將從 sharp
實例鏈接 resize()
方法來調整圖像的大小,並將其保存在項目目錄中。其次,您將鏈接 format()
方法到調整大小的圖像上,將其格式從 png
改為 jpeg
。此外,您將向 format()
方法傳遞一個選項來壓縮圖像並將其保存到目錄中。
在文本編輯器中創建並打開 resizeImage.js
文件:
將以下代碼添加到調整圖像大小為 150px
寬和 97px
高:
resizeImage()
函數將 sharp 模塊的 resize()
方法鏈接到 sharp
實例上。該方法以對象作為參數。在該對象中,您使用 width
和 height
屬性設置所需的圖像尺寸。將 width
設置為 150
,將 height
設置為 97
將使圖像寬度為 150px
,高度為 97px
。
調整圖像大小後,鏈接 sharp 模塊的 toFile()
方法,該方法以圖像路徑作為參數。將 sammy-resized.png
作為參數傳遞將在程序的工作目錄中保存該名稱的圖像文件。
現在,保存並退出文件。在終端中運行您的程序:
您將不會得到任何輸出,但您應該在項目目錄中看到一個名為 sammy-resized.png
的新圖像文件。
打開您本地計算機上的圖像。您應該會看到一個寬度為 150px
,高度為 97px
的 Sammy 圖像:
現在,您可以調整圖像大小,接下來您將把調整大小的圖像格式從 png
轉換為 jpeg
,壓縮圖像,並將其保存在工作目錄中。要做到這一點,您將在 resize()
方法之後鏈接 toFormat()
方法。
添加以下突出顯示的代碼以更改圖像格式為 jpeg
並壓縮它:
在 resizeImage()
函數中,您使用 sharp 模塊的 toFormat()
方法來更改圖像格式並壓縮它。 toFormat()
方法的第一個參數是包含您想要將圖像轉換為的圖像格式的字符串。第二個參數是一個可選的對象,其中包含增強並壓縮圖像的輸出選項。
要壓縮圖像,您傳遞一個包含布爾值的 mozjpeg
屬性。當您將其設置為 true
時,sharp 使用 mozjpeg 默認壓縮圖像而不損失質量。該對象還可以接受更多選項;有關更多詳細信息,請參閱 sharp 的 文檔。
注意: 對於 toFormat()
方法的第二個參數,每個圖像格式都需要一個帶有不同屬性的對象。例如,mozjpeg
屬性僅接受在 JPEG
圖像上。
然而,其他圖像格式也有類似的選項,如quality
、compression
和lossless
。請務必參考文檔,以了解對於您正在壓縮的圖像格式可接受的選項類型。
接下來,將不同的文件名傳遞給toFile()
方法,以將壓縮後的圖像保存為sammy-resized-compressed.jpeg
。
現在,保存並退出文件,然後運行以下命令執行您的代碼:
您將不會收到任何輸出,但一個名為sammy-resized-compressed.jpeg
的圖像文件將保存在您的項目目錄中。
在本地計算機上打開圖像,您將看到以下圖像:
現在,圖像已經被壓縮,檢查文件大小以確認壓縮是否成功。在終端中,運行du
命令檢查sammy.png
的文件大小:
-h
選項生成可讀的輸出,顯示文件大小以千字節、兆字節等方式。
運行該命令後,您應該會看到類似於以下的輸出:
Output120K sammy.png
輸出顯示原始圖像為120千字節。
接下來,檢查sammy-resized.png
的文件大小:
運行該命令後,您將看到以下輸出:
Output8.0K sammy-resized.png
sammy-resized.png
現在比原本的 120 千字節小了 8 千字節。這顯示調整大小操作影響了文件大小。
現在,檢查 sammy-resized-compressed.jpeg
的文件大小:
執行命令後,您將看到以下輸出:
Output4.0K sammy-resized-compressed.jpeg
sammy-resized-compressed.jpeg
現在比 8 千字節小了 4 千字節,節省了 4 千字節,表明壓縮起作用了。
現在您已經調整了圖像大小,更改了格式並對其進行了壓縮,接下來將對圖像進行裁剪並轉換為灰度。
步驟 4 — 裁剪並將圖像轉換為灰度
在此步驟中,您將裁剪圖像並將其轉換為灰度。裁剪是從圖像中刪除不需要的區域的過程。您將使用 extend()
方法來裁剪 sammy.png
圖像。之後,您將將 grayscale()
方法鏈接到裁剪的圖像實例上並將其轉換為灰度。
在您的文本編輯器中創建並打開 cropImage.js
:
在您的 cropImage.js
文件中,添加以下代碼來裁剪圖像:
cropImage()
函數是一個異步函數,用於讀取圖片並返回裁剪後的圖片。在 try
塊中,一個 sharp
實例將讀取圖片。然後,鏍鋒模塊的 extract()
方法鏈接到實例,接受一個具有以下屬性的對象:
width
: 您想要裁剪的區域的寬度。height
: 您想要裁剪的區域的高度。top
: 您想要裁剪的區域的垂直位置。left
: 您想要裁剪的區域的水平位置。
當您將 width
設置為 500
,並將 height
設置為 330
時,想像一下鏍鋒會在您想要裁剪的圖像上創建一個透明框。任何符合框的圖像部分將保留,其餘部分將被切割:
top
和 left
屬性控制框的位置。 當您將 left
設置為 120
時,框將位於圖像左邊緣的 120px 處,將 top
設置為 70
將框定位到圖像頂部邊緣的 70px 處。
框內符合的圖像區域將被提取並保存為單獨的圖像 sammy-cropped.png
。
保存並退出文件。在終端中運行該程序:
輸出將不會顯示,但圖像 sammy-cropped.png
將保存在您的項目目錄中。
在本機機器上打開圖像。 您應該會看到已經裁剪的圖像:
現在您已經裁剪了圖像,將圖像轉換為灰度。為此,您將將sharp
實例鏈接到grayscale
方法。添加以下突出顯示的代碼以將圖像轉換為灰度:
cropImage()
函數通過將 sharp
模塊的 grayscale()
方法鏈接到 sharp
實例來將裁剪後的圖像轉換為灰度。然後將圖像保存在項目目錄中,文件名為 sammy-cropped-grayscale.png
。
按 CTRL+X
保存並退出文件。
在終端中運行您的代碼:
在您的本地計算機上打開 sammy-cropped-grayscale.png
。您現在應該看到灰度圖像:
現在您已經裁剪並提取了圖像,您將對其進行旋轉和模糊處理。
步驟 5 — 旋轉和模糊圖像
在此步驟中,您將以 33 度的角度旋轉 sammy.png
圖像。您還將對旋轉後的圖像應用高斯模糊。高斯模糊是使用高斯函數對圖像進行模糊處理的技術,它降低了圖像的噪音水平和細節。
在您的文本編輯器中創建一個名為 rotateImage.js
的文件:
在你的 rotateImage.js
檔案中,請寫下以下程式碼區塊,建立一個函式來將 sammy.png
旋轉到 33
度的角度:
這個 rotateImage()
函式是一個異步函式,它會讀取一張圖片並將其旋轉到 33
度的角度。在函式內部,sharp 模組的 rotate()
方法接受兩個參數。第一個參數是旋轉角度 33
度。預設情況下,sharp 會將旋轉後的圖片背景設為黑色。為了移除黑色背景,你需要將一個物件作為第二個參數傳遞,讓背景變為透明。
這個物件有一個 background
屬性,其中包含一個定義 RGBA 顏色模型的物件。RGBA 代表紅色、綠色、藍色和 alpha。
-
r
:控制紅色的強度。它接受一個介於0
到255
之間的整數值。0
表示該顏色未被使用,而255
則表示紅色達到最高強度。 -
g
:控制綠色的強度。它接受一個介於0
到255
之間的整數值。0
表示未使用綠色,而255
則表示綠色達到最高強度。 -
b
:控制藍色的強度。它同樣接受一個介於0
到255
之間的整數值。0
表示未使用藍色,而255
則表示藍色達到最高強度。 -
alpha
:控制由r
、g
和b
属性定义的颜色的不透明度。0
或0.0
使颜色变为透明,1
或1.1
使颜色变为不透明。
要使alpha
属性起作用,您必须确保定义并设置r
、g
和b
的值。将r
、g
和b
值设置为0
会创建黑色颜色。要创建透明背景,您必须首先定义一个颜色,然后可以将alpha
设置为0
以使其透明。
现在,保存并退出文件。在终端中运行您的脚本:
检查项目目录中是否存在sammy-rotated.png
。在本地机器上打开它。
您应该看到图像旋转了33
度:
接下来,您将对旋转后的图像进行模糊处理。您将通过将blur()
方法链接到sharp
实例来实现。
输入下面突出显示的代码以模糊图像:
rotateImage()
函數現在讀取圖像,將其旋轉,並對圖像應用高斯模糊。它使用 sharp 模塊的 blur()
方法對圖像應用高斯模糊。該方法接受一個包含介於 0.3
和 1000
之間的 sigma 值的單個參數。將其設置為 4
將應用一個 sigma 值為 4
的高斯模糊。圖像模糊後,您定義一個保存模糊圖像的路徑。
現在,您的腳本將使用 4
的 sigma 值對旋轉後的圖像進行模糊處理。保存並退出文件,然後在終端中運行腳本:
運行腳本後,打開本地機器上的 sammy-rotated-blurred.png
文件。現在,您應該看到旋轉後的圖像已模糊:
現在您已經旋轉並模糊了一幅圖像,接下來您將對另一幅圖像進行合成。
步驟 6 — 使用 composite()
合成圖像
圖像合成是將兩幅或更多分開的圖片結合起來創建單一圖像的過程。這是為了創建借用不同照片中最好元素的效果。另一個常見的用例是使用標誌為圖像添加水印。
在這一部分,你將把 sammy-transparent.png
覆蓋在 underwater.png
上。這將創造出 Sammy 在海洋深處游泳的幻象。要合成這兩張圖片,你將鏈接 composite()
方法到 sharp
實例。
在你的文本編輯器中創建並打開文件 compositeImage.js
:
現在,在 compositeImages.js
文件中添加以下代碼以創建一個合成兩張圖片的函數:
compositeImages()
函數首先讀取 underwater.png
圖片。接下來,你將鏈接 sharp 模組的 composite()
方法,該方法以一個數組作為參數。該數組包含一個對象,該對象讀取 sammy-transparent.png
圖片。該對象具有以下屬性:
input
: 接受要在處理後的圖片上合成的圖像的路徑。它還可以接受 Buffer、Uint8Array 或 Uint8ClampedArray 作為輸入。top
: 控制要在其上合成的圖像的垂直位置。將top
設置為50
會將sammy-transparent.png
圖像偏移 50 像素,從underwater.png
圖像的頂部邊緣。left
:控制您想要在另一张图片上合成的图像的水平位置。将left
设置为50
,将sammy-transparent.png
偏移underwater.png
图像的左边缘 50 像素。
composite()
方法需要一张大小相似或更小的图像来处理。
为了可视化 composite()
方法的作用,可以将其想象成创建了一堆图像。将 sammy-transparent.png
图像放置在 underwater.png
图像的顶部:
top
和 left
值将 sammy-transparent.png
图像相对于 underwater.png
图像进行定位。
保存您的脚本并退出文件。运行您的脚本以创建图像组合:
node compositeImages.js
在本地机器上打开 sammy-underwater.png
。现在您应该可以看到 sammy-transparent.png
已合成在 underwater.png
图像上:
您现在已经使用了 composite()
方法合成了图像。在下一步中,您将使用 composite()
方法向图像添加文本。
步骤 7 — 向图像添加文本
在這個步驟中,您將在圖像上寫文本。在撰寫本文時,Sharp還沒有一種原生的方法來向圖像添加文本。要添加文本,首先,您將編寫代碼來使用可縮放向量圖形(SVG)繪製文本。一旦您創建了SVG圖像,您將編寫代碼來使用composite
方法將圖像與sammy.png
圖像合成。
SVG是一種基於XML的用於在Web上創建向量圖形的標記語言。您可以繪製文本,或形狀,如圓圈,三角形,以及繪製複雜的形狀,如插畫,標誌等等。複雜的形狀是使用圖形工具如Inkscape生成的SVG代碼創建的。SVG形狀可以呈現和縮放到任何大小而不失去質量。
在您的文本編輯器中創建並打開addTextOnImage.js
文件。
在您的addTextOnImage.js
文件中,添加以下代碼來創建SVG容器:
addTextOnImage()
函數定義了四個變量:width
、height
、text
和svgImage
。width
保存整數750
,height
保存整數483
。text
保存字符串Sammy the Shark
。這是您將使用SVG繪製的文本。
變數 svgImage
持有 svg
元素。 svg
元素有兩個屬性:width
和 height
,這兩個屬性會內插你之前定義的 width
和 height
變數。 svg
元素根據給定的寬度和高度創建一個透明容器。
你給了 svg
元素一個 width
為 750
和 height
為 483
的值,這樣 SVG 圖像的大小將與 sammy.png
相同。這將有助於使文字看起來居中在 sammy.png
圖像上。
接下來,你將繪製文字圖形。添加下面突出顯示的代碼以在 SVG 容器上繪製 Sammy the Shark
:
SVG text
元素有四個屬性:x
、y
、text-anchor
和 class
。 x
和 y
定義了你在 SVG 容器上繪製文字的位置。 x
屬性將文字水平定位,而 y
屬性將文字垂直定位。
將 x
設置為 50%
會將文字繪製在 x 軸的中心,將 y
設置為 50%
會將文字定位在 SVG 圖像的 y 軸中心。
text-anchor
用於水平對齊文字。將 text-anchor
設置為 middle
將使文字在你指定的 x
坐標的中心對齊。
class
定義了 text
元素上的類名稱。您將使用該類名稱來應用 CSS 樣式到 text
元素。
${text}
插值了存儲在 text
變量中的字符串 Sammy the Shark
。這是將在 SVG 圖像上繪製的文本。
接下來,添加突出顯示的代碼以使用 CSS 為文本添加樣式:
在這段代碼中,fill
將文本顏色更改為黑色,font-size
更改字體大小,font-weight
更改字體粗細。
到目前為止,您已經編寫了繪製文本 Sammy the Shark
的 SVG 所需的代碼。接下來,您將將 SVG 圖像保存為 png
文件,以便您可以查看 SVG 如何繪製文本。完成後,您將將 SVG 圖像與 sammy.png
合併。
添加突出顯示的代碼以使用 sharp 將 SVG 圖像保存為 png
:
Buffer.from()
從 SVG 圖像創建了一個 Buffer 對象。緩衝區是存儲二進制數據的臨時內存空間。
創建緩衝區對象後,使用該緩衝區對象作為輸入創建一個sharp
實例。除了圖像路徑外,sharp還接受緩衝區、Uint9Array或Uint8ClampedArray作為輸入。
最後,將SVG圖像保存在項目目錄中,文件名為svg-image.png
。
以下是完整代碼:
保存並退出文件,然後使用以下命令運行您的腳本:
node addTextOnImage.js
注意:如果您是使用選項2 — 使用 NodeSource PPA 安裝 Node.js或選項3 — 使用 Node 版本管理器安裝 Node.js並且出現錯誤fontconfig error: cannot load default config file: no such file: (null)
,請安裝fontconfig
以生成字體配置文件。
更新您的服務器包索引,然後使用apt install
安裝fontconfig
。
打开您本地机器上的 svg-image.png
。您现在应该看到以透明背景呈现的文本 Sammy the Shark
:
现在您已经确认SVG代码绘制了文本,您将把文本图形叠加到 sammy.png
上。
将以下突出显示的代码添加到将SVG文本图形叠加到 sammy.png
图像的过程中。
composite()
方法从 svgBuffer
变量中读取SVG图像,并将其定位在离 sammy.png
的顶部 0
像素、左边缘 0
像素的位置。接下来,将叠加后的图像保存为 sammy-text-overlay.png
。
保存并关闭您的文件,然后使用以下命令运行您的程序:
在您本地机器上打开 sammy-text-overlay.png
。您应该看到图像上添加了文本:
您现在已经使用了 composite()
方法将SVG创建的文本添加到另一张图像上。
结论
在這篇文章中,您學習了如何在Node.js中使用銳利的方法處理圖像。首先,您創建了一個實例來讀取圖像,並使用metadata()
方法提取圖像元數據。然後,您使用resize()
方法調整圖像大小。之後,您使用format()
方法更改圖像類型並壓縮圖像。接下來,您繼續使用各種sharp方法來裁剪、灰度化、旋轉和模糊圖像。最後,您使用composite()
方法來合成圖像並在圖像上添加文本。
要深入了解更多有關其他sharp方法的信息,請訪問sharp文檔。如果您想繼續學習Node.js,請參閱如何在Node.js系列中編碼。
Source:
https://www.digitalocean.com/community/tutorials/how-to-process-images-in-node-js-with-sharp