こんにちは。虎の穴ラボのCTO野田です。
この記事は「虎の穴ラボ夏のアドベントカレンダー」18日目の記事です。
17日目は辻村さんによる「本番環境に寄り添った開発用Docker環境の構築手法」が投稿されました。
今回紹介するもの
画像の任意の位置にロゴ透かし(ウォーターマーク)を挿入し合成できるCLIツールをGo言語で作ってみました。
今回作った動機
- Golangの勉強にはいい教材だから。
- 画像を扱う業務では使えるかもしれない。
メイン実装
package main import ( "flag" "fmt" "image" "log" "os" "path/filepath" "strings" "image/draw" "image/jpeg" "image/png" ) func main() { var oImageFilePath string var logoImagePath string var outFolderPath string var logoPosition string var useOriginalFilename bool flag.StringVar(&oImageFilePath, "f", "", "originalImageFilePath") flag.StringVar(&logoImagePath, "l", "", "logoImageFilePath") flag.StringVar(&outFolderPath, "o", "", "outFolderPath") flag.StringVar(&logoPosition, "p", "", "TopLeft | TopRight | BottomLeft | BottomRight | Center") flag.BoolVar(&useOriginalFilename, "u", false, "出力ファイル名にオリジナルファイル名を使う") flag.Parse() originFile, err := os.Open(oImageFilePath) if err != nil { fmt.Println(err) } defer originFile.Close() logoFile, err := os.Open(logoImagePath) if err != nil { fmt.Println(err) } defer logoFile.Close() originImg, format, err := image.Decode(originFile) if err != nil { log.Fatalf("failed to decode image: %s", err.Error()) } logoImg, _, err := image.Decode(logoFile) if err != nil { log.Fatalf("failed to decode image: %s", err.Error()) } oPoint := originImg.Bounds().Size() lPoint := logoImg.Bounds().Size() x, y := positionInt(logoPosition, oPoint.X, oPoint.Y, lPoint.X, lPoint.Y) fmt.Printf("%d, %d, %d ,%d\n", oPoint.X, oPoint.Y, lPoint.X, lPoint.Y) fmt.Printf("%d, %d\n", x, y) startPointLogo := image.Point{x, y} logoRectangle := image.Rectangle{startPointLogo, startPointLogo.Add(logoImg.Bounds().Size())} originRectangle := image.Rectangle{image.Point{0, 0}, originImg.Bounds().Size()} rgba := image.NewRGBA(originRectangle) draw.Draw(rgba, originRectangle, originImg, image.Point{0, 0}, draw.Src) draw.Draw(rgba, logoRectangle, logoImg, image.Point{0, 0}, draw.Over) if format == "jpeg" { out, err := os.Create(outFilePath(oImageFilePath, "jpg", outFolderPath, useOriginalFilename)) if err != nil { fmt.Println(err) } var opt jpeg.Options opt.Quality = 80 err = jpeg.Encode(out, rgba, &opt) if err != nil { log.Fatalf("failed to encode image: %s", err.Error()) } } else { out, err := os.Create(outFilePath(oImageFilePath, "png", outFolderPath, useOriginalFilename)) if err != nil { fmt.Println(err) } err = png.Encode(out, rgba) if err != nil { log.Fatalf("failed to encode image: %s", err.Error()) } } } func outFilePath(oFilePath string, ext string, outFolder string, useOfn bool) string { const defaultFileName = "logo-watermark" const addFileName = "-lw" var filaneme string if useOfn { ext := filepath.Ext(oFilePath) pf := strings.TrimSuffix(filepath.Base(oFilePath), ext) filaneme = pf + addFileName } else { filaneme = defaultFileName } if outFolder == "" { return filaneme + "." + ext } else { return filepath.Join(outFolder, filaneme+"."+ext) } }
ロゴの位置を決める部分の実装
func positionInt(position string, w int, h int, lw int, lh int) (int, int) { const xLeft = 0 const yTop = 0 xRight := w - lw ybottom := h - lh switch position { case "TopLeft": return xLeft, yTop case "TopRight": return xRight, yTop case "BottomLeft": return xLeft, ybottom case "BottomRight": return xRight, ybottom case "Center": cx, cy := w/2, h/2 clx, cly := lw/2, lh/2 return cx - clx, cy - cly default: // TopLeftをデフォルトとする return xLeft, yTop } }
書いて気づいたTips
image.Decode関数は画像ファイルをフォーマット(jpegやpng)によらずいい感じに開いてくれますが、import _"image/jpeg" などで開きたフォーマットライブラリのブランクインポートが必要になります。それに気づかず一回画像ファイルを開いてフォーマットを調べてから jpeg.Decodeやpng.Decodeを個別に呼び出すコードを最初書いてました。
使い方
ビルド
go build -o ./bin/logo-watermark logo-watermark/logo-watermark.go
ファイル名をlogo-watermark.goとした場合
HELP
./bin/logo-watermark -h Usage of ./bin/logo-watermark: -f string originalImageFilePath -l string logoImageFilePath -o string outFolderPath -p string TopLeft | TopRight | BottomLeft | BottomRight | Center -u 出力ファイル名にオリジナルファイル名を使う
右下にロゴを入れる
./bin/logo-watermark -f ./private/gop-500x500.png -l ./image/logo-sample.png -p BottomRight
真ん中にロゴを入れる
./bin/logo-watermark -f ./private/gop-500x500.png -l ./image/logo-sample.png -p Center
サンプルで使ったGopher君画像
とらラボのサイトで配布しています。 画像プログラムの検証などで是非どんどん使ってください。
実用性を考えてプログラムを発展するには
- 画像でなく文字列をフォントで埋め込み。
- 対象画像が巨大でロゴサイズが小さいと透かし画像が小さくダサく見えるのでロゴをいい感じにプログラムで拡大縮小したい。
- むしろロゴ画像をプログラムで自動生成したい。
- ロゴ画像をベクタ形式の画像で用意すれば解決?
- golangでWEBサーバーてててWEBツール化、もしくはAPI Gateway+AWS lambda化すると応用が効きそう。