2020年11月17日火曜日

Imagemagickでロゴ画像に平均色を自動着色して合成する


描いた絵に対してサインとして簡易URLのついたロゴを使っていて、それを自動で合成、それも色をなるべく浮かせないように合成するまでのあれこれ。

やり方が結構ズルズルしてるけど強引にワンライナに収められたので良しとします。

Debian 10 + ImageMagick(convert)

画像とか

base.png

JPGでもOK。アニメーションGIFはパッと見1コマ切り出すだけなのでサイン合成にはffmpegを使いましょう。
800x520
sign.png
アルファPNGでロゴと文字以外は透明(CSSの設定で灰色になってるっぽい)
220x36

まず普通に合成

$ convert base.png sign.png -composite result.png

左上にロゴがついた。サインは見えづらいくらいが丁度いいとしても、ちょっと見えにくすぎるような…。


$ convert base.png sign.png -gravity southeast -composite result.png

gravityオプションで比較的明るい右下にロゴを寄せる。
見やすくなったけども、ディテールがあって見どころでもある部分を隠してしまうのはちょっと…。

ちなみに

ロゴ画像を白黒2種準備して引数とかで適宜切り替えるという方法もあるけど省略。

gravityオプションについて
Northwest North Northeast
West   Center   East
Southwest South Southeast

平均色に変えてから合成させる

画像の平均色を取り出す

convertにピクセルの情報を出すオプションがあるそうなので、これを使う。
平均色といってもガシガシに縮小して縮こまったピクセルの色をもらってくるとかいう力技。
$ convert base.png -resize 3x3! -crop 1x1+2+2 -format "%[fx: r*255],%[fx: g * 255],%[fx: b*255]" info: | sed "s@\.@,@g" | awk -F"," '{print $1","$3","$5}' 

$ 66,55,36 
  • -resize 3x3! で画像をアスペクト無視(!をつける)で縦横3pxに縮小
  • -crop 1x1+2+2 で左上から縦横2pxめの1pxに切り落とす(真ん中だけにする)
  • -format "%[fx: r*255],%[fx: g * 255],%[fx: b*255]" info: でrgb値をカンマ区切りで出力
  • 小数点以下を切り捨てるためにsedで「.」をカンマに変え、
    awkでカンマを区切り文字にして1,3,5番目(小数点以上)を出力する

参考ページ探してたらもうちょい短い方法があった。
$ convert base.png -resize 3x3! -crop 1x1+2+2 txt: | tail -n1 | sed -e "s/.*srgb(//" -e "s/)$//g"


$ 67,55,36
txt: 出力を使うと各ピクセルの色についてカラーコードなどと一緒に表示するらしい。ちょっとだけ前述のやり方と誤差がある。
コメントがついてしまうのでtailで切り、sedで丸カッコを外す。

平均色で塗ったレイヤcolor.pngを作成

いちいち変数に入れるのはアレなのでパイプしてxargsする。
$ convert base.png -resize 3x3! -crop 1x1+2+2  txt: | tail -n1 |sed -e "s/.*srgb(//" -e "s/)$//g" | xargs -I COLOR convert -size 300x300 xc:"rgb(COLOR)" color.png

color.png
黒地に金色の絵図だったのでまあ妥当。

参考:【 xargs 】コマンド――コマンドラインを作成して実行する

color.pngにsign.pngのアルファ値を適用

CopyOpacityを使う。
$ convert color.png sign.png -compose CopyOpacity -gravity southwest  -composite signcolor.png
signcolor.png
これまた分かりづらいけど300x300の透明画像の左下に色ロゴがついている。

絵に平均色ロゴを合成

最初に上げた合成と一緒。

$ convert base.png signcolor.png -composite result.png

_人人人人人人人人人人_
>真ん中寄りじゃねーか<
 ̄YYYYYYYYYY ̄

gravityをつけましょう。

$ convert base.png signcolor.png -gravity southwest -composite result.png

最高!

一括操作

これセミコロンで繋げてるだけでは………???

$ convert base.png -resize "3x3!" -crop 1x1+2+2 txt: | tail -n1 | sed -e "s/.*srgb(//" -e "s/)$//g" | xargs -I COLOR convert -size 300x300 xc:"rgb(COLOR)" color.png; convert color.png sign.png -compose CopyOpacity -gravity southwest -composite signcolor.png; convert base.png signcolor.png -gravity southwest -composite result.png; rm color.png signcolor.png

フォルダ内一括合成とか引数でロゴ位置とか考えてたけど眠いので終わります。

追記

シェルスクリプトにしました。引数にファイルパス、2番引数(任意)にImagemagickのGravity値を入れます。ロゴ画像の場所などは変数へ。

フォルダ内一括させたくてfor書いてるけど今のところフォルダ一括は無理です。ごめんね。

#!/bin/bash
# 自動で平均色をとって画像にサインロゴをはめ込むスクリプト
# -aでディレクトリ内全部、パス指定で1画像を、posに投げ込む

if [ "x$1" = "x" ]; then
echo "autosign [picture file path ] [position] "
exit
fi
cd $HOME

WMARK="$HOME/Pictures/wmark-autosign.png"
POSDIR="$HOME/Pictures/pos"
AVGCOLOR="$HOME/Pictures/avgcolor.png"
COLSIGN="$HOME/Pictures/colsign.png"
SOURCE="$1"
POS="southwest"
DEST=""

case "x$1" in
"-a" | "-A" ) SOURCE="ls -Q $PWD --color=never" ;;
* ) SOURCE="$1";;
 esac
 
case "x$2" in 
"x" ) POS="southwest" ;;
* ) POS="$2";;
esac

for i in $SOURCE ;do
DEST="$POSDIR/$(basename $i | sed s/...$/jpg/)"
echo "generating $DEST ..."

#入力から画像情報を取得、Tailで切りSedでRGB値のみに、Xargsで色サンプル画像を作る
convert $i -resize "3x3!" -crop 1x1+2+2 txt: |
tail -n1 |
sed -e "s/.*srgb(//" -e "s/)$//g" |
xargs -I COLOR convert -size 300x300 xc:"rgb(COLOR)" $AVGCOLOR
#色サンプル画像をロゴ画像で切り抜き
convert $AVGCOLOR sign.png -compose CopyOpacity -gravity $POS -composite $COLSIGN
convert $i $COLSIGN -gravity $POS -composite -quality 100 -interlace JPEG -comment "0xconfig.net" $DEST

rm $AVGCOLOR $COLSIGN

done
echo "Done."

0 件のコメント:

コメントを投稿