在客戶端咱們能夠用 PhotoShop
等 GUI
工具處理靜態圖片或者動態 GIF
圖片,不過在服務器端對於 WEB
應用程序要處理圖片格式轉換,縮放裁剪,翻轉扭曲,PDF解析等操做, GUI
軟件就很難下手了,因此此處須要召喚命令行工具來幫咱們完成這些事。javascript
ImageMagick: 是一款建立、編輯、合成,轉換圖像的命令行工具。支持格式超過 200
種,包括常見的 PNG, JPEG, GIF, HEIC, TIFF, DPX, EXR, WebP, Postscript, PDF, SVG
等。功能包括調整,翻轉,鏡像(mirror),旋轉,扭曲,修剪和變換圖像,調整圖像顏色,應用各類特殊效果,或繪製文本,線條,多邊形,橢圓和貝塞爾曲線等。php
官網:https://www.imagemagick.org,下面放個小標識。html
支持Linux, Windows, Mac OS X, iOS, Android OS
等平臺
https://www.imagemagick.org/s...
由於我是 MAC 機器,演示一下 brew
的安裝方式咯java
brew install imagemagick
ImageMagick
包括一組命令行工具來操做圖片,安裝好ImageMagick
後,終端就可使用以下命令了。
magick:
建立、編輯圖像,轉換圖像格式,以及調整圖像大小、模糊、裁切、除去雜點、抖動 ( dither )、繪圖、翻轉、合併、從新採樣等。convert:
等同於 magick
命令。identify:
輸出一個或多個圖像文件的格式和特徵信息,如分辨率、大小、尺寸、色彩空間等。mogrify:
與 magick
功能同樣,不過不須要指定輸出文件,自動覆蓋原始圖像文件。composite:
將一個圖片或多個圖片組合成新圖片。montage:
組合多個獨立的圖像來建立合成圖像。每一個圖像均可以用邊框,透明度等特性進行裝飾。git
compare:
從數學和視覺角度比較源圖像與重建圖像之間的差別。display:
在任何 X server 上顯示一個圖像或圖像序列。animate:
在任何 X server 上顯示圖像序列。import:
保存 X server 上的任何可見窗口並把它做爲圖像文件輸出。能夠捕捉單個窗口,整個屏幕或屏幕的任意矩形部分。conjure:
解釋並執行 MSL ( Magick Scripting Language ) 寫的腳本。stream:
一個輕量級工具,用於將圖像或部分圖像的一個或多個像素組件流式傳輸到存儲設備。在處理大圖像或原始像素組件時頗有用。github
基本命令的使用,遵循 Unix
風格的標準格式:web
command [options] input_image output_image
好比咱們將一張寬高 300x300
的圖片 goods.png
轉換成 200x200
的goods.jpg
,能夠這樣用算法
convert -resize 200x200 goods.png goods.jpg
-resize
定義圖片尺寸,ImageMagick
全部的選項參數都在這個【 命令行選項手冊】。
可是隨着功能的複雜,命令緩慢擴大成了這樣的格式:shell
command [options] image1 [options] image2 [options] output_image
因而上面的命令也能夠寫成這樣canvas
convert goods.png -resize 200x200 goods.jpg
筆記:
我的建議,若是轉換的是一張圖片,那麼用第一種格式,由於像 -density
等一些選項必須放在 command
與 input_image
之間,因此爲了省記都不寫錯,都寫在 command
與 input_image
之間豈不很好。
可是若是是多張圖片轉換,就須要按第二種格式,正確輸出命令選項了。
提示:
若是上面的工具命令在計算機上不可使用,則能夠把它們看成 magick
命令的子命令使用,例如
magick identify goods.png
默認狀況下 ImageMagick
會讀取圖像中惟一標識格式的簽名來肯定文件格式,若是沒有,則根據文件的擴展名來肯定格式,如 image.jpg
被認爲 jpeg
格式文件,若是都獲取不到,則須要手動指定文件的格式。命令格式爲 format:input_or_output_image
。
輸入文件通常狀況應該不須要手動指定文件格式,輸出文件的時候,png
格式分 png8
、png24
等格式,若是 png8
格式的文件可以知足需求,指定合理的格式能夠縮小文件的大小,示例以下。
convert goods.png png8:goods_8.png convert goods.png png24:goods_24.png
文中案例基於
ImageMagick 7.0.7
需求:將一張寬高爲 900x600
的圖片 goods.jpg
生成寬高爲 150x100
的縮略圖 thumbnail.jpg
convert -resize 150x100 -quality 70 -strip goods.jpg thumbnail.jpg
解釋:
-resize 150x100
:定義輸出的縮略圖尺寸爲 150x100
。-quality 70
:下降縮略圖的質量爲 70
,取值範圍 1
( 最低圖像質量和最高壓縮率 ) 到 100
( 最高圖像質量和最低壓縮率 ),默認值根據輸出格式有 75
、92
、100
,選項適用於 JPEG / MIFF / PNG
。-strip
:讓縮略圖移除圖片內嵌的全部配置文件,註釋等信息,以減少文件大小。
-resize
延伸解讀,以下。
上面的例子中,輸入的圖片和輸出的圖片比例是一致的,因此不會有特殊狀況出現,可是遇到比例不一樣的時候,上面的寫法並不會獲得 150x100
的圖像,而是會根據圖像的寬高比例,取最大值,得出來的結果多是 150
寬和更小的高,或者 100
高和更小的寬;因此 IamgeMagick
提供了幾種符號來定義縮放。
convert -resize '150x100!' goods.jpg thumbnail.jpg convert -resize '150x100>' goods.jpg thumbnail.jpg convert -resize '150x100<' goods.jpg thumbnail.jpg
!
:無論圖片寬高如何,都縮放成 150x100
這樣的尺寸。>
:只有寬高均大於 150x100
的圖片才縮放成該尺寸 ( 按比例取最大值 ),小於的圖片不作處理。<
:與 >
功能相反。
提示:
由於有些字符是 Linux shell
或其餘系統的特殊字符,因此須要用引號包裹起來或者用反斜線 \
轉義,注意,不一樣平臺可能引號都是有差別的。
需求 ① :給圖片居中加上透明文本水印。
convert -draw 'text 0,0 "JD.COM"' -fill 'rgba(221, 34, 17, 0.25)' -pointsize 36 \ -font 'cochin.ttc' -gravity center joy.jpg watermark.jpg
解釋:
-draw
:繪圖選項,text
聲明繪製文本, 0,0
聲明文本距離圖片左上角的偏移值, JD.COM
聲明繪製的文本,最好用引號包裹起來,避免輸入特殊字符引發錯誤。繪製文本的格式爲 text x,y string
,固然還能夠繪製其餘類型,諸如圓 ( circle )、折線 ( polyline )。-fill
:對文本填充顏色,貌似 ImageMagick
命令中前面的選項
是用來控制後面的選項
的,因此應該把這樣的修飾選項放到 -draw
前面比較好,很重要
,後面的案例就是這樣的。-pointsize
:指定文本的字體大小。-font
:指定字體。-gravity
:設置文本在圖片裏的排列方式 ( 相似 CSS 裏的 align-items + justify-content ),center
表示水平垂直都居中,其餘值還能夠是:NorthWest, North, NorthEast, West, East, SouthWest, South, SouthEast
,不記大小寫。\
:反斜線也是類 Unix
系統的續行字符,當一個命令很長時,咱們能夠把它寫成多行,以便視覺上的美觀和直觀。需求 ② :給圖片加上傾斜平鋪透明文本水印。
convert -size 100x100 xc:none \ -fill '#d90f02' -pointsize 18 -font 'cochin.ttc' \ -gravity center -draw 'rotate -45 text 0,0 "JD.COM"' \ -resize 60% miff:- | composite -tile -dissolve 25 - joy.jpg watermark.jpg
解釋:文本平鋪水印實際上是將文本畫成一張 png
圖片,而後用這張透明圖片在目標圖片上進行平鋪。
-size
:設置畫布的大小。xc:
:全稱 X Constant Image
,是 canvas:
的別名,定義一張畫布,用來繪圖,經常使用格式爲 xc:color
,none
或者 transparent
設置畫布爲透明底,默認爲白色。-resize
:該選項還能夠指定百分比,意爲縮放至原圖像的百分之幾。貌似 -pointsize
小於 14
後,-draw
裏的 rotate
會不生效,因此用 -resize
來把平鋪圖案變得更小。miff:-
:
miff:
聲明輸出 ImageMagick ( IM ) 本身的圖像文件格式:MIFF,主要用途是以複雜的方式處理圖像時當作中間保存格式,適用於從一個 IM 命令向另外一個 IM 命令傳遞圖像元數據和其餘關聯屬性。-
在管道符前面意爲將 IM 命令執行的結果做爲標準輸出,在管道符後面則表示從標準輸入中讀取這個數據,如在管道符後面的 composite
中使用 -
讀取剛剛生成的透明圖像。|
:Linux shell
管道符,用於將上一個命令的標準輸出傳遞到下一個命令做爲標準輸入。這裏將生成的水印圖案傳遞給 composite
命令。-tile
:顧名思義,讓圖案平鋪。-dissolve
:設置平鋪圖案的透明度。圖釋:
大概邏輯以下:
4
個英文字母或數字。100x40
的畫布。16
,每一個字符的寬高也就是 16
左右了,依次計算出每一個字符的 x, y
座標,再增長一丁點旋轉。若是加上隨機計算,可能代碼會比較多,因此這裏寫成固定值,方便理解。
convert 'xc:[100x40!]' -pointsize 20 -font 'cochin.ttc' \ -gravity NorthWest -strokewidth 1 \ -fill '#b72b36' -stroke '#b72b36' -draw 'translate 13,19 rotate 10 text -5,-8 "5"' \ -fill '#821d70' -stroke '#821d70' -draw 'translate 36,13 rotate -8 text -8,-8 "C"' \ -fill '#c7960a' -stroke '#c7960a' -draw 'translate 60,23 rotate 5 text -5,-8 "2"' \ -fill '#03610a' -stroke '#03610a' -draw 'translate 85,25 rotate 13 text -8,-8 "E"' \ -strokewidth 2 -stroke 'rgba(248, 100, 30, 0.5)' -fill 'rgba(0, 0, 0, 0)' \ -draw 'bezier -20,30 -16,10 20,2 50,20' \ -draw 'bezier 50,20 78,42 138,36 140,16' \ +noise Impulse \ captcha.jpg
結果:
鑑於字體比較細,能夠用 strokewidth
加邊框來加粗,或者使用字體的粗體版本,這裏使用了第一種方式。
解釋:
xc:[100x40!]
:設置畫布大小的一種簡寫方式,方括號裏寫入畫布寬高,注意要加 !
,不然會出乎意料喲。文本定位與旋轉
100px
,平均分紅 4
分,每份 25px
, 文字寬 16px
, 得文字 x
的座標左右擺動範圍爲 +0px, +9px
,y
座標同理,用於設置 translate
值。16x16
的區域,根據字體的不一樣,填滿的區域可能各有不一樣,因此根據cochin
字體的特性,上面稍微將字體大小調整爲 20
,實際渲染出來的字母纔是 16x16
左右大小,數字大概是 10x16
,因此設置數字的 x,y
爲 -5,-8
,結合下面兩個屬性解釋 x,y
的計算方式。translate
: 設置文本的橫縱向偏移值。rotate
:設置文本旋轉,單位 degrees
。根據 gravity
的設置座標系統有一丁點變化,因此請設置爲 西北(NorthWest)
,表示以畫布 0,0
座標旋轉,跟 HTML 5 Canvas
座標系統一致。translate
和 text
的 x,y
一塊兒使用,原理可參考這篇文章[圖像旋轉的實現],注意 translate
與 rotate
的順序。strokewidth
:設置文本的邊框寬度或線條寬度。stroke
:設置文本的邊框顏色或線條顏色。-fill 'rgba(0, 0, 0, 0)'
:上面設置了文本的填充顏色,會影響下面的貝塞爾曲線,因此這裏指定一個透明的填充色以覆蓋上面的設定,使曲線沒有填充。bezier
:繪製貝塞爾曲線,一兩句話我怕解釋不清楚,因此請你們參考一下維基百科的解釋或者這篇中文文章的解釋,最後再參考一下 IM 官方示例的描述。上面兩條三次貝塞爾曲線的座標分別表示 起始點
,起始點的控制點
,結束點的控制點
,結束點
。+noise
:增長噪點,可使用 convert -list noise
查看當前系統支持哪些算法的噪點,大概有 Gaussian, Impulse, Laplacian, Multiplicative, Poisson, Random, Uniform
。
這個案例主要了解幾個基本操做的
API
。
convert \ \( -crop 300x300+10+25 joy.jpg \) \ \( -resize 400x400 -crop 300x300+50+0 logo: \) -swap 0,1 +append \ \( -clone 0 -flop -flip \) -append \ -resize 200x200 combined.jpg
結果以下:
解釋:
圓括號 \( ... \)
:圖像堆棧 ( image stack
),至關於建立了一個獨立做用域處理圖像,這個可使圖像以前的處理互不干擾。圓括號需用反斜槓轉義,才能不被 Shell
當作特殊字符處理,而且每一個圓括號兩邊須要用空格隔開
。沒必要要的圓括號會使 IM
增長少量額外的工做,可是卻讓命令更清晰不容易出錯。-crop
:裁剪出圖像的一個或多個矩形區域,格式爲 {size}{+-}x{+-}y
,若是不指定偏移值 x,y
,則會被解釋爲按指定寬高切割圖像成多少份(多圖像)。logo:
:IM
內置圖像,這個就是上圖中拿着魔法棒的主人公了,自己寬高 640x480
,其餘內置圖像還有:rose:
,granite :
等,看這裏。-swap
:
-swap index,index
。IM
在圖像處理操做時,實際上極可能是在處理一個圖像列表,當新圖像被讀入或者建立時,IM
會將該新圖像添加到當前圖像列表的末尾。2
張圖,第一張是 joy
,可是 -swap 0,1
的意思是交換第一張圖與第二張圖的位置,因此 joy
變成跑到後面了。+append
:水平鏈接當前圖像列表的圖像來建立單個較長的圖像。-append
:垂直鏈接當前圖像列表的圖像來建立單個較長的圖像。-clone
:克隆圖像,格式爲 -clone {index_range_list}
。
-clone 0
:表示克隆圖像列表裏的第一張圖像。-clone 1-2
:表示克隆圖像列表裏的第二張到第三張圖像。-clone 0--1
:0
表示第一張圖像,-1
表示最後一張圖像,因此整句命令則表示克隆整個圖像列表。-clone 2,0,1
:表示克隆第三張,第一張,第二張圖像,順序根據指定的索引決定,用逗號分隔。-flop
:將圖像水平翻轉。-flip
:將圖像垂直翻轉。筆記:
-clone
雷同的選項還有諸如:-delete, -insert, -reverse, -duplicate
,用於操做圖像列表,功能與單詞意思相同。convert -coalesce rain.gif frame.jpg
-coalesce
:根據圖像 -dispose
元數據的設置覆蓋圖像序列中的每一個圖像,以重現動畫序列中每一個點的動畫效果。下面用一張結果對比圖來解釋這句話。
原始圖 ( rain.gif ) :
結果對比:
上面默認輸出的文件名爲:frame-0.jpg, frame-1.jpg, frame-2.jpg ...
,
若是想使用下劃線做爲符號,輸出爲 frame_0.jpg, frame_1.jpg, frame_2.jpg ...
,則能夠以下設置。
convert -coalesce rain.gif frame_%d.jpg
或者
convert -coalesce -set filename:n '%p' rain.gif 'frame_%[filename:n].jpg'
解釋:
%d
是 C
語言 printf()
中表示輸出一個整數,參考 -adjoin 選項。第二種爲常規方式。
-set
:設置圖像屬性,格式爲 -set key value
filename:n '%p'
:以 filename:
開頭的 key
用於設置輸出文件名的相關信息,如這裏使用 filename:n
,在輸出文件名時,則可使用 %[filename:n]
拿到剛剛的設置,而設置的內容則是 '%p'
。'%p'
表示圖像在圖像列表中的索引值,更多百分比選項 ( Percent Escapes ) 參考。若是隻想拿到 GIF 的第一幀,能夠這樣設置。
convert -coalesce 'rain.gif[0]' first_frame.jpg
拿到某些幀,如同 -clone
的寫法。
convert -coalesce 'rain.gif[0-2]' some_frames_%d.jpg
經過 identify
命令咱們能夠簡要獲得文件的信息,以下。
identify rain.gif
經過換行符分割,簡單封裝一個 Node.js
函數獲取頁數。
// parser.js const util = require('util') const exec = util.promisify(require('child_process').exec) exports.numberOfPages = async (filePath) => { try { const { stdout } = await exec(`identify '${filePath}'`) return stdout.trim().split('\n').length } catch (err) { throw new Error(err) } }
// main.js const { numberOfPages } = require('./parser') ;(async function start () { const pages = await numberOfPages('rain.gif') console.log('pages:', pages) }())
convert -loop 0 'frame-*.jpg' rain_animation.gif
將全部與 frame-*.jpg
模式匹配的圖像轉換成一張 GIF
圖像,如 frame-0.jpg
,frame-1.jpg
等。-loop
設置動畫循環次數,0
表示無限循環。
設置每張圖像的播放速度可使用 -delay 選項。
筆記:
在 IM
讀取系列文件時,frame-10.jpg
會排在 frame-2.jpg
前面,爲得到圖像正確的讀取順序,能夠爲文件名設置前導零 ( leading zeros
)。如:frame-000.jpg, frame-001.jpg, frame-002.jpg ... frame-010.jpg
。
因此在生成圖像時,咱們可使用 %03d
得到三位前導零。
convert -coalesce rain.gif frame-%03d.jpg
PDF 與圖片互轉跟 GIF 很類似,稍微有些格式自身須要注意的區別。IM
自己是不具有解析 PDF 的功能的,須要依賴專門解析這種格式的外部程序,如官方指明的 ghostscript
解析程序。
首先安裝 gs
,仍是演示 Mac OS
安裝:brew install ghostscript
。
以 這個PDF 爲例,把它轉換成圖片,有兩種方式達到咱們想要的結果:
① convert -density 150 -flatten 'download.pdf[0]' first_page.jpg
② convert -density 150 -background white -alpha remove download.pdf download.jpg
解釋:
-flatten
選項讓其保持白色背景,但加上這個選項,多頁 PDF 不會分紅多個 JPG 圖像
,第二種方式 -background white -alpha remove
則能夠一次命令轉換多頁 PDF 成多個圖像
並保持白色背景。IM
內部應該是一頁一頁的轉換,因此一個 10
頁的 PDF
耗時會比較久,採用第一種方式讓 Node.js
多進程同時轉換該 PDF
能夠提高速率。-density
:指定輸出圖像的分辨率 ( DPI
),在 Mac OS
上,默認的分辨率 ( 72
) 輸出的圖像字跡不清,須要更高分辨率得到清晰的圖像。
直接經過
child_process
模塊執行相應的命令便可,以下。
只須要結果可使用 exec
,
const util = require('util') const exec = util.promisify(require('child_process').exec) ;(async function start () { try { await exec(`convert -resize '150x100!' -strip goods.jpg thumbnail.jpg`) console.log('convert completed.') } catch (err) { console.log('convert failed.', err) } }())
流式輸入輸出可使用 spawn
,
const cp = require('child_process') const fs = require('fs') const args = [ '-', // 使用標準輸入 '-resize', '150x100!', '-strip', 'jpg:-', // 輸出到標準輸出 ] const streamIn = fs.createReadStream('/path/to/goods.jpg') const proc = cp.spawn('convert', args) streamIn.pipe(proc.stdin) proc.stdout.pipe(HttpResponse)
本文同步發表於【凹凸實驗室】博客及微信公衆號,歡迎關注咱們,麼麼噠。