R 言語の sketcher パッケージを使って写真をスケッチ風に変換するスクリプト

プログラム,レビュー,画像処理imagemagick,R,sketcher,シェルスクリプト,スケッチ

Twitter でたまたま紹介されているのを見つけた「sketcher」という R 言語(GNU R)用のパッケージを使って、様々なパラメータを使ってスケッチ風の画像に変換するスクリプトを書いたのでまとめておきます。

sketcher による変換例

札幌 JR タワーからの景色(東側)

JR タワー展望台の景色は誕生日だと無料で楽しめます。詳しくは「誕生日は札幌JRタワーの展望室に無料で上れるので1日で8回はしごしてみた話」をご覧ください。

H-II ロケットのエンジン

北大構内にいた猫


sketcher パッケージ

津田裕之さんによって開発・公開された R のパッケージで、CRAN に登録されています。詳細は Tsuda, H. (2020)[1]Tsuda, H. (2020). sketcher: An R package for converting a photo into a sketch style image. PsyArXiv Preprints. https://doi.org/10.31234/osf.io/svmw5を参照してください。

sketcher のページにあるサンプルのように、画像を読み込んで鉛筆スケッチのような絵に変換する画像プロセッサです。以下のようなパラメータが設定できます。

パラメータ説明デフォルト値
styleスケッチスタイル1 or 21
lineweight線の強さ実数 >= 0.31
smooth表面のスムージング整数 >= 01
gain画像のゲイン 0 – 1 の実数 0.02
contrast画像のコントラスト実数 >= 020 (style 1) or 4 (style 2)
shadow影のしきい値0 – 1 の実数0.0
max.size読込画像の解像度整数 > 02048

詳細は sketcher のサンプルを見てほしいのですが、オプションによって結構印象が変わります。

style と lineweight を変更したの組み合わせの例

なお、このサンプル画像は以下の記事「2021年 北大生協「きぼうの虹」フォトコンテストで入賞した話」のときに撮ったものです。

パラメータを色々変えてみるスクリプト

元画像によって適当なパラメータが変わると思うので、いろんなパラメータで画像一覧を出力してみるスクリプトをつくりました。以下のように style 別の画像が 2 枚出力されます。

style 1 で lineweight を増やす(横方向)と smooth を増す(縦方向)のサンプル
style 2 で lineweight を増やす(横方向)と smooth を増す(縦方向)のサンプル

それぞれ横方向に lineweight を 4 パターン増やし、縦方向に smooth を 3 パターン増やしています。これを見て適当なパラメータを検討して、自分好みのスケッチパラメータを探すことが目的です。各画像は 1024 px の幅に縮めることで変換時間を節約しています、列挙すると以下のような処理をしています。

  • convert -resize で小さい画像を作る
  • Rscript に画像のパスとファイル名を引数として与えて、様々なパラメータで画像を変換
  • 変換した画像を convert -append コマンドで縦横に結合

R と Imagemagick がインストールされていて、Rscript コマンドと convert コマンドいれば使えると思います。ファイル操作と Imagemagick を使うシェルスクリプト “mk_test_tileSketcher.sh" と、画像を変換する R スクリプト “sketcher_multi.R" の 2 つのスクリプトを組み合わせて使います。

#!/bin/sh

# 一時ファイルにつける名前
temp_prefix="temporal_00000"

# 引数の有無をチェック
if [ $# != 1 ]; then
    echo "ERROR! ファイル名の指定がない"
    exit 1
fi

# パスとファイル名の抜き出し
dirName=`dirname $1`
srcFilename=`basename $1`

# Sketcher に投げる前に画像サイズを適当に縮小する
tempFilename="${temp_prefix}${srcFilename}_resized.png"
convert -resize 1024x ${dirName}/${srcFilename} ${dirName}/${tempFilename}

# 画像変換 R スクリプトの実行
Rscript sketch_multi.R $dirName $tempFilename

filename="${tempFilename}_style#st-lineweight#lw-smooth#sm.png"

for style in 1 2
do
    for smooth in 1 3 5
    do
        smFile=`echo $filename | sed -e "s/#sm/${smooth}/" -e "s/#lw/*/" -e "s/#st/${style}/"`
        # 横方向に結合
        convert +append $smFile ${temp_prefix}${smooth}_sm_append.png
    done

    # 縦方向に結合
    convert -append ${temp_prefix}*_sm_append.png ${srcFilename}_style${style}.png
done

# 一時ファイル削除処理
rm ${temp_prefix}*.png
#!/usr/bin/Rscript
# sketcher を使って引数に与えた画像ファイルを
# 様々なパラメータで描画する R スクリプト
# lineweight と smooth と style を組み合わせ
# 全部で 12 x 2 パターンの画像を出力する

# 引数: ファイルのあるパス, ファイル名

# ライブラリ読み込み
library(sketcher)

# 引数の読み込み
args <- commandArgs(trailingOnly = TRUE)

# 引数から作業ディレクトリとファイル名を取得する
rootDir = args[1]
imgFilename = args[2]
# rootDir = "./"
# imgFilename = "test.jpg"
print(rootDir)
print(imgFilename)

# ファイルパスの作成と画像の読み込み
src_img_path = paste0(rootDir, "/", imgFilename)
src_img = im_load(src_img_path)
# plot(src_img) # 画像表示

# 線幅の範囲
lineweight_param = c(0.5, 1, 3, 5)

# style 種類の範囲
style_param = 1:2

# smooth の範囲
smooth_param = seq(1, 6, 2)

# 線幅パラメータでプロット
for(style in style_param) {
    for (line_weight in lineweight_param) {
        for (smooth in smooth_param) {
            # 画像変換
            new_img = sketch(src_img, style = style, lineweight = line_weight, smooth = smooth)
            # ファイル名の指定 (文字列連結)
            name_img = paste0(imgFilename, "_style", style, "-lineweight", line_weight, "-smooth", smooth)
            print(name_img)
            # 画像保存
            im_save(new_img, name = name_img, path = rootDir, format = "jpg", quality = .90)
            # im_save(new_img, name = name_img, path = rootDir, format = "png")
        }
    }
}

実行方法は “mk_test_tileSketcher.sh" に引数としてファイルのパスを与えるだけです。絶対パスでも相対パスでも動作します。

bash ./mk_test_tileSketcher.sh /path/to/img_file.jpg

好みのパラメータが見つかったら

適当なパラメータを見つけたら、それを使って画像を変換しましょう。そのためのスクリプトが “sketcher_single.R" です。

#!/usr/bin/Rscript
# sketcher を使って引数に与えた画像ファイルを
# 変換するスクリプト

# 引数: ファイルのあるパス, ファイル名

# ライブラリ読み込み
library(sketcher)

# 引数の読み込み
args <- commandArgs(trailingOnly = TRUE)

# 引数から作業ディレクトリとファイル名を取得する
rootDir = args[1]
imgFilename = args[2]
# rootDir = "./"
# imgFilename = "test.jpg"
print(rootDir)
print(imgFilename)

# ファイルパスの作成と画像の読み込み
src_img_path = paste0(rootDir, "/", imgFilename)
src_img = im_load(src_img_path)
# plot(src_img) # 画像表示

# 線幅の範囲 > 0.3 (0.5, 1, 3, 5)
lineweight_param = 0.8

# style 種類の範囲 1 or 2
style_param = 2

# smooth の範囲 >= 0 (1, 3, 5)
smooth_param = 1

# 線幅パラメータでプロット
# 画像変換
new_img = sketch(src_img, style = style_param, lineweight = lineweight_param, smooth = smooth_param, max.size = 8000)
# ファイル名の指定 (文字列連結)
name_img = paste0(imgFilename, "_style", style_param, "-lineweight", lineweight_param, "-smooth", smooth_param)
print(name_img)
# 画像保存
# im_save(new_img, name = name_img, path = rootDir, format = "jpg", quality = .95)
im_save(new_img, name = name_img, path = rootDir, format = "png")

途中にある “lineweight_param", “style_param", “smooth_param" にお好みの値を指定して実行してください。

“sketcher_single.R" は 2 つの引数をとり、「ファイルパス(ディレクトリ)」と「ファイル名」を指定する必要があります[2]真面目につくればファイルパスを引数に与えるだけで「ディレクトリ」と「ファイル名」にわけられると思いますが、サボりました。。例えば以下のように指定します。ディレクトリ名とファイル名が分けて与えられないとエラーがでてうまく動きません。

Rscript ./sketcher_single.R /path/to/file_directory img_file.jpg

なお、この sketcher は大きな画像を読み込むとかなりメモリを食います。5000 x 3500 px 程度の png ファイル(30 MB 程度の容量が大きな画像)を読み込んだらピーク時で 5 GB もメモリを使ったので、かなり気をつけた方がいいです。ちょっとしたノート PC では落ちると思うので、適当なサイズに縮小してから使う方が懸命ででしょう。

R / sketcher のインストール方法

詳しくは公式ドキュメントを参照してください。Linux の Debian 系環境(Ubuntu など)の場合、 R は

# apt install r-base

でインストールできます。また sketcher ライブラリは、R を実行してから以下のコマンドでインストールします。

> install.packages("sketcher")

ビルドに結構時間がかかり、10 分弱かかりました。私の環境ではビルドの最中に次のようなエラーが出て止まりました。

ERROR: dependencies ‘readbitmap’, ‘imager’ are not available for package ‘sketcher’

依存関係解決のため apt を使って libtiff-dev を追加でインストールする必要がありました。

画像を変換するといえば

以前、Twitter で画像投稿するのに、いい感じで 4 分割するスクリプトを作りました。詳しくは「Twitterで投稿するために4分割された画像を作るシェルスクリプト」の記事をご覧ください。

脚注

脚注
1Tsuda, H. (2020). sketcher: An R package for converting a photo into a sketch style image. PsyArXiv Preprints. https://doi.org/10.31234/osf.io/svmw5
2真面目につくればファイルパスを引数に与えるだけで「ディレクトリ」と「ファイル名」にわけられると思いますが、サボりました。