OS X でシェルから一気に画像を変換・リサイズを変換する

  • 13
    Like
  • 0
    Comment
More than 1 year has passed since last update.

/usr/bin/sips を使った画像編集。プレビュー.app のコマンドライン版のような感じ。AppleScript の Image Events を使っているならこっちもおすすめ。

Automator のサービス例。Automator に用意されているアクションより速い。シェルの場合は bash より zsh、ksh の方がループが速いので速度優先ならそちらの方がおすすめ。

# テンプレート:サービス
# 選択項目/検索対象:ファイルまたはフォルダ/Finder.app
# シェル:/bin/sh
# 入力の引き渡し方法:引数として

for f
{
    test -f "$f" || continue
    sips -s format jpeg --out "${f%.*}"_out.jpg "$f"
}

man より画像編集関連の部分を引用。sips 10.4.4 なので古いかも。

NAME
     sips -- scriptable image processing system.

SYNOPSIS
     sips [image-query-functions] imagefile ...
     sips [image-modification-functions] imagefile ... [--out result-file-or-dir]

FUNCTIONS
     Image query functions:

     -g key
     --getProperty key
           <key> に対するプロパティの値を標準出力に出力する。

     Image modification functions:

     -s key value
     --setProperty key value
           <key> に対するプロパティの値を <value> に設定する。

     -d key
     --deleteProperty key
           <key> に対するプロパティの値を削除する。

     -e profile
     --embedProfile profile
           画像に <profile> を埋め込む。

     -E profile
     --embedProfileIfNone profile
           画像がプロファイルを所持していない場合に画像に <profile> を埋め込む。

     --deleteColorManagementProperties
           TIFF、PNG、EXIF 辞書のカラーマネージメントプロパティを削除する。

     -r degreesCW
     --rotate degreesCW
           <degreesCW> 度回転する。*1

     -f horizontal|vertical
     --flip horizontal|vertical
           水平または垂直反転する。

     -c pixelsH pixelsW
     --cropToHeightWidth pixelsH pixelsW
           <pixelsH>、<pixelsW> に合わせて画像をクロップ。

     -p pixelsH pixelsW
     --padToHeightWidth pixelsH pixelsW
           <pixelsH>、<pixelsW> に合わせてピクセルを埋める。

     --padColor hexcolor
           パッド時の色。(16進数。デフォルトは 000000)

     -z pixelsH pixelsW
     --resampleHeightWidth pixelsH pixelsW
           <pixelsH>、<pixelsW> に画像をリサンプルする。画像のアスペクト比は変更されるかもしれない。

     --resampleWidth pixelsW
           <pixelsW> に画像をリサンプルする。*2

     --resampleHeight pixelsH
           <pixelsH> に画像をリサンプルする。*2

     -Z pixelsWH
     --resampleHeightWidthMax pixelsWH
           <pixelsWH> より大きくならないように画像をリサンプルする。

     -i
     --addIcon
           Finder アイコンを画像に追加する。(?)

OPTIONS
     --getProperty、--setProperty、--deleteProperty コマンドはパラメータとして次に述べるキーを1つ使用できます。

     Special property keys:
     all              binary data *3
     allxml           binary data *3

     Image property keys:
     dpiHeight        float
     dpiWidth         float
     pixelHeight      integer (read-only)
     pixelWidth       integer (read-only)
     typeIdentifier   string (read-only)
     format           string jpeg | tiff | png | gif | jp2 | pict | bmp | qtif | psd | sgi | tga *4
     formatOptions    string default | [low|normal|high|best|<percent>] | [lzw|packbits *5]
     space            string (read-only)
     samplesPerPixel  integer (read-only)
     bitsPerSample    integer (read-only)
     creation         string (read-only)
     make             string
     model            string
     software         string (read-only)
     description      string
     copyright        string
     artist           string
     profile          binary data
     hasAlpha         boolean (read-only)

*1:度数を±整数で指定する。角度によってキャンバスに隙間が出来る場合は --padColor の値が使用される。
*2:アスペクト比を維持したままサイズを変更する。
*3:画像が持っている全てのプロパティ(Exif など)が表示されるわけではない。
*4:下記参照。
*5:試してみたら packbits ではなく pacbits だった。


ヘルプに載ってない pdf や icns、ico なども対応。入力はとりあえず「プレビュー.app」で表示できるようなものなら RAW なんかでも変換できそう。下のリストは strings コマンドで確認できたタイプ。

public.tiff
public.fax
public.jpeg
public.jpeg-2000
public.png
public.radiance
public.xbitmap-image
public.camera-raw-image
com.apple.pict
com.apple.macpaint-image
com.apple.quicktime-image
com.apple.icns
com.adobe.pdf
com.adobe.photoshop-image
com.compuserve.gif
com.ilm.openexr-image
com.microsoft.ico
com.microsoft.bmp
com.sgi.sgi-image
com.truevision.tga-image
com.kodak.flashpix-image
com.adobe.raw-image
com.canon.crw-raw-image
com.canon.cr2-raw-image
com.canon.tif-raw-image
com.nikon.raw-image
com.olympus.raw-image
com.fuji.raw-image
com.sony.raw-image
com.konicaminolta.raw-image
com.kodak.raw-image

DNG から JPEG に変換した画像の品質を ImageMagick の identify で確認したもの。low は検出できなかった。

formatOptions Quality
default 93
low ???
normal 80
high 93
best 100

何かしてみる?

  • 編集時は --out を忘れると入力ファイルを上書きするので要注意。
  • 出力先にファイルではなくディレクトリを指定した場合はファイル名を維持する。ディレクトリのパスに - で始まる名前が含まれるとオプションと勘違いされてしまう($TMPDIR 等)。

カラープロファイルを変更する

カラープロファイルは /System/Library/ColorSync/Profile なんかにあるものを指定する。カラースペースが変更できないので RGB -> Gray とかはできないっぽい。

/bin/sh
sips -s profile "/System/Library/ColorSync/Profiles/sRGB Profile.icc" "$f"

枠の追加

クロップとパッドを同時に使用するとどちらかの値でクロップされてしまうらしい。ソースによってはクロップの時点でファイルサイズが増えることもある。

/bin/sh
border=1
((0
    `sips -g all "$f" | sed -n '/pixelHeight:/s//,ih=/p;/pixelWidth:/s//,iw=/p'`
    ,ch=ih-(border*2)
    ,cw=iw-(border*2)
))
sips -c $ch $cw "$f" --out "${f%.*}"_border."${f##*.}"
sips -p $ih $iw "$_"

正方形切り抜き

短辺で切り抜き。クロップの座標指定がないのが残念。

/bin/sh
((0
    `sips -g all "$f" | sed -n '/pixelHeight:/s//,ih=/p;/pixelWidth:/s//,iw=/p'`
    ,s=iw<ih?iw:ih
))
sips -c $s $s "$f" --out "${f%.*}"_square."${f##*.}"

ICNS ファイルを作成する

クロップとリサイズの同時使用は画像サイズがおかしくなることがある。makeicns の代替になればいいなと思ってやってはみたけど sips だと1つにまとめる方法がなかった。

/bin/sh
((0
    `sips -g all "$f" | sed -n '/pixelHeight:/s//,ih=/p;/pixelWidth:/s//,iw=/p'`
    ,s=iw<ih?iw:ih
))
tempfile=`mktemp XXXXXX`
sips -c $s $s --out "$tempfile" "$f"
for i in 16 32 48 128 256 512 1024
{
     ((s>=i)) || break
     sips -z $i $i -s format icns --out "${f%.*}"_${i}x$i.icns "$tempfile"
}
rm "$tempfile"

メモ:sips は 16/32/48/128/256/512/1024、makeicns は 16/32/128/256/512。

Automator 用ワークフローサンプル

実行時に GUI から出力形式と品質の選択ができます。出力パターンは 入力.png入力_20140123235959.jpg のような感じ。use_dialog=0 に設定すればダイアログなしで default_*** の値を使って変換。sips のログが /var/log/system.log に出力されるようになっています。その都合で sips がエラーになってもアクションエラーが表示されません。

bcc00cec0bc9b8e251e47a0f61550f0c.png 7e6d40e5efea95967f4ed586e3466a0a.png

入力ファイルを上書きしないようにしてはいますが保証はできませんのでお試しは自己責任で。最近あまり AppleScript 書いてないので色々おかしいかも。

sips.workflow
# Name: sips.workflow
# Template: Services
# Item/Target: File or Directory/Finder.app
# Shell: /bin/sh
# Input transfer: Argument

#-------------------------------------------------------------------------------
# Debug options
#-------------------------------------------------------------------------------
set -e
#set -x

#-------------------------------------------------------------------------------
# Useful options
#-------------------------------------------------------------------------------
logname="Automator (sips)"
use_dialog=1 ## boolean
default_format=jpeg
default_formatOptions=default
default_formatExtension=jpg
outname_suffix=`date +%Y%m%d%H%M%S`

#-------------------------------------------------------------------------------
# Functions
#-------------------------------------------------------------------------------
main()
{
    (($# > 0)) || exit 0

    set_format

    for f
    {
        test -f "$f" || continue

        of=$f
        case $f in ## has extension?
        *.???|*.????)
            of=${f%.*}
            ;;
        esac
        of+=_ of+=$outname_suffix ## keep input file
        of+=.$formatExtension

        sips \
            -s format        $format \
            -s formatOptions $formatOptions \
            --out "$of" "$f"
    } 2>&1 | logger -t "$logname"
}

dialog()
{
    ## return: format formatOptions formatExtension

    osascript - <<\!
        set L to {false, false, false}

        set xformat to {"bmp", "exr", "gif", "icns", "ico", "jp2", "jpeg", ¬
                        "pdf", "pict", "png", "psd", "qtif", "sgi", "tga", "tiff"}
        set xformatOptions to {jpeg: {"default", "low", "normal", "high", "best"}, ¬
                               tiff: {"default", "lzw", "pacbits"}}

        tell app "Finder"
            activate

            -- dialog 1: format
            set item 1 of L to (choose from list (xformat) ¬
                with prompt "-s format ???" default items "jpeg")
            if item 1 of L is false then return L

            -- dialog 2: formatOptions
            if item 1 of L is {"jpeg"} or ¬
               item 1 of L is {"jp2"} then
                set xformatOptions to (jpeg of xformatOptions)
            else if item 1 of L is {"tiff"} then
                set xformatOptions to (tiff of xformatOptions)
            else
                set xformatOptions to {"default"}
            end if

            set item 2 of L to (choose from list (xformatOptions) ¬
                with prompt "-s formatOptions ???" default items "default")
            if item 2 of L is false then return L
        end

        -- formatExtension
        set item 3 of L to item 1 of L
        if item 3 of L is {"jpeg"} then set item 3 of L to "jpg"

        set text item delimiters of AppleScript to space
        return L as text
!
}

set_format()
{
    case ${use_dialog:-0} in
    0|no|off|false)
        format=$default_format
        formatOptions=$default_formatOptions
        formatExtension=$default_formatExtension
        return 0
        ;;
    esac

    set -- `dialog`
    case $* in
    *false*)
        exit 0
        ;;
    esac
    format=$1
    formatOptions=$2
    formatExtension=$3
}

#-------------------------------------------------------------------------------
main "$@"

追記:GitHub にワークフローのサンプルをアップしました。

https://github.com/mattintosh4/AMServices/tree/master/sips.workflow