作者选择了 科技多样性基金 作为 为捐赠写作 计划的受益方。
介绍
数字图像处理是利用计算机来分析和处理图像的方法。该过程涉及读取图像、应用方法来改变或增强图像,然后保存处理过的图像。处理处理用户上传内容的应用程序经常处理图像。例如,如果您正在编写一个允许用户上传图像的网络应用程序,用户可能会上传不必要的大图像。这可能会对应用程序的加载速度产生负面影响,也会浪费服务器空间。通过图像处理,您的应用程序可以调整大小并压缩所有用户上传的图像,这可以显着提高应用程序的性能并节省服务器磁盘空间。
Node.js拥有一系列可用于处理图像的库,例如sharp、jimp和gm模块。本文将重点介绍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 中的事件循环、回调、Promises 和 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
的新图像文件被创建。
打开您本地机器上的图像。您应该会看到一张 Sammy 的图片,宽度为 150px
,高度为 97px
:
现在您可以调整图像的大小,接下来您将把调整大小后的图像格式从 png
转换为 jpeg
,压缩图像,并将其保存在工作目录中。为此,您将在 resize()
方法后链接使用 toFormat()
方法。
添加以下突出显示的代码以更改图像格式为 jpeg
并压缩它:
在 resizeImage()
函数中,您使用 sharp 模块的 toFormat()
方法来更改图像格式并压缩它。 toFormat()
方法的第一个参数是包含要将图像转换为的图像格式的字符串。第二个参数是一个可选对象,其中包含增强和压缩图像的输出选项。
要压缩图像,您将其传递一个包含布尔值的 mozjpeg
属性。当您将其设置为 true
时,sharp 使用 mozjpeg 默认压缩图像而不会牺牲质量。该对象还可以接受更多选项;有关更多详情,请参阅 sharp 文档。
注意:关于 toFormat()
方法的第二个参数,每种图像格式都需要一个带有不同属性的对象。例如,mozjpeg
属性仅在 JPEG
图像上被接受。
然而,其他图像格式也有类似的选项,如质量
、压缩
和无损
。确保参考文档,了解对于正在压缩的图像格式可接受的选项类型。
接下来,将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
实例将读取图片。然后,该实例链式调用 sharp 模块的 extract()
方法,该方法接受一个带有以下属性的对象:
width
:要裁剪的区域的宽度。height
:要裁剪的区域的高度。top
:要裁剪的区域的垂直位置。left
:要裁剪的区域的水平位置。
当你将 width
设置为 500
,将 height
设置为 330
时,想象一下 sharp 会在你想要裁剪的图片顶部创建一个透明框。适合框内的图像部分将保留,其余部分将被裁剪:
top
和 left
属性控制框的位置。当你将 left
设置为 120
时,框将位于距离图片左边缘 120 像素的位置,将 top
设置为 70
时,框将位于距离图片顶部边缘 70 像素的位置。
图像中适合框内的区域将被提取出来,并保存为单独的图像 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
的高斯模糊。在图像模糊后,您定义一个路径来保存模糊的图像。
现在,您的脚本将使用 sigma 值为 4
模糊旋转后的图像。保存并退出文件,然后在终端中运行脚本:
运行脚本后,在本地机器上打开 sammy-rotated-blurred.png
文件。现在您应该可以看到旋转后的图像已模糊:
旋转并模糊图像后,您将对另一个图像进行合成。
第6步 — 使用 composite()
合成图像
图像合成是将两个或多个独立图片结合在一起创建单个图像的过程。这是为了创建从不同照片中借用最佳元素的效果。另一个常见用例是用标志水印图片。
在这一部分,您将在underwater.png
上叠加sammy-transparent.png
。这将产生山米在海洋深处游泳的错觉。要合成图像,您将将composite()
方法链接到sharp
实例。
在文本编辑器中创建并打开compositeImage.js
文件:
现在,在compositeImages.js
文件中添加以下代码以创建一个合成两个图像的函数:
compositeImages()
函数首先读取underwater.png
图像。接下来,您链接sharp模块的composite()
方法,该方法以数组作为参数。数组包含一个对象,该对象读取sammy-transparent.png
图像。对象具有以下属性:
input
: 获取要叠加到处理后图像上的图像的路径。它还接受Buffer、Uint8Array或Uint8ClampedArray作为输入。top
: 控制要叠加的图像的垂直位置。将top
设置为50
会使sammy-transparent.png
图像从underwater.png
图像的顶部边缘偏移50像素。left
:控制要合成到另一张图片上的图像的水平位置。将left
设置为50
,将sammy-transparent.png
偏移50像素,使其与underwater.png
图像的左边缘对齐。
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的标记语言。您可以绘制文本,或者形状,如圆形、三角形,以及绘制复杂的形状,如插图、标志等。复杂的形状是使用类似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
更改字体粗细。
到目前为止,您已经编写了绘制 SVG 上文本 Sammy the Shark
所需的代码。接下来,您将使用 sharp 将 SVG 图像保存为 png
,以便您可以看到 SVG 是如何绘制文本的。完成后,您将 SVG 图像与 sammy.png
合成。
添加下面的高亮代码来使用 sharp 将 SVG 图像保存为 png
:
Buffer.from()
创建了一个 Buffer 对象,从 SVG 图像中获取。缓冲区是一个临时存储二进制数据的内存空间。
创建缓冲区对象后,使用缓冲区对象作为输入创建一个 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
上。
将以下突出显示的代码添加到sammy.png
图像中以合成SVG文本图形图像。
composite()
方法从svgBuffer
变量中读取SVG图像,并将其位置设置为距离sammy.png
的顶部和左边缘各0
像素。接下来,您将合成的图像保存为sammy-text-overlay.png
。
保存并关闭您的文件,然后使用以下命令运行您的程序:
在您的本地计算机上打开sammy-text-overlay.png
。您应该看到图像上添加了文本:
您现已使用composite()
方法将使用SVG创建的文本添加到另一幅图像上。
结论
在这篇文章中,您学习了如何使用 Node.js 中的 sharp 方法处理图像。首先,您创建了一个实例来读取图像,并使用 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