画像に透かしを自動追加するツール|初心者でも簡単にできるウォーターマークの実装手順

この記事では、Pythonを使って画像に透かしを自動で追加する方法をわかりやすく解説します。

写真や画像をインターネットに公開する際、著作権を守るために「透かし(ウォーターマーク)」を追加することは重要です。

特に、InstagramやTwitterなどのSNSで簡単に写真が共有される現代では、画像の無断使用や盗用を防ぐ手段として透かしを自動的に追加できる方法が求められています。

手作業で画像に透かしを追加する手間を省き、効率的に著作権保護を行うことができるので、ブログ運営者や写真家、デザイナーにとって大きな時間の節約になります。

また、自社ロゴやテキストを画像に一括で適用することで、画像がトリミングされた際にも確実に透かしが機能するようにすることが可能です。

透かしによる画像保護で、ブランドの認知度を高め、画像の盗難や不正使用を防ぐための最適な方法をご紹介します。

1. スクリプトの概要

こんなことができるよ
  • 用意したPNG画像を、右下に自動で配置
  • 配置場所の明るさを計算し、黒or白のロゴを自動選択
  • 画像サイズに合わせてロゴのサイズを自動調整
  • JPEG、PNG、BMPなどの一般的な画像形式に対応

このスクリプトは、画像に透かしを自動で追加できます。

また、環境構築用のinstall.batファイルを用意しており、簡単に必要な環境をセットアップできます。

ソフトウェアのダウンロードは以下に案内しております。

2. 前提条件

このスクリプトを使用するには、以下の環境が必要です。

  • Windows環境(バッチファイルのため)
  • Python 3.x がインストールされていること
  • 必要なPythonライブラリ:Pillow、NumPy

3. 使い方

ステップ1:環境のセットアップ

  • Pythonのインストール:Python 3.x未インストールの場合は公式サイトからダウンロードしてインストールします。
  • 環境構築の実行:「Auto-Watermark-by-kia.zip」を解凍し、「install.bat」をダブルクリックして実行します。
  • スクリプトとロゴ画像の配置:解凍後は以下のようなディレクトリ構成になっています。
    ├── watermark.py
    ├── watermark.bat
    ├── install.bat
    ├── logo
    │   ├── logo-black.png
    │   └── logo-white.png
        

ステップ2: 透かし用透過ロゴ画像の準備

透かしに使用するロゴを以下の要件で準備してください。

  • ロゴ画像の準備:埋め込みたい画像を「logo-black.png」と「logo-white.png」として「logo」フォルダに上書き保存で配置する
  • 黒と白の両方のバージョンを用意する
  • 背景を透過したPNG形式で保存する
  • 画像サイズは横幅200px以上最大幅728px程度を推奨

準備したロゴファイルを「logo-black.png」と「logo-white.png」にリネームし、スクリプト内の「logo」フォルダに上書き保存してください。

ステップ3:ウォーターマークを画像に合成

  • 画像ファイルの準備:ウォーターマークを追加したい画像を用意します。
  • ショートカットを作成:watermark.batのショートカットを作成し、好きな場所に移動させて、実行できます。
  • 実行方法:watermark.bat(ショートカットでもOK)の上に画像ファイルをドラッグ&ドロップして実行します。
  • 複数ファイルの処理:複数の画像を一度にドラッグ&ドロップすることで一括処理が可能です。

ステップ4:結果の確認

  • 出力ファイル:元の画像ファイルと同じ場所に「_watermarked」が付いたファイルが生成されます。
  • 例:「sample.jpg」を処理すると「sample_watermarked.jpg」が生成されます。

4. スクリプトの詳細説明

watermark.py

以下が透かしを追加するメインのPythonスクリプトです。

透かしのサイズ・位置を変更する場合の設定

このスクリプトでは、冒頭の設定項目を変更することで、ウォーターマークのサイズ、位置、透過率、明るさの閾値を自由に調整できます。

  • ウォーターマークのサイズ(%指定):画像サイズに対するウォーターマークの大きさを割合で指定します。ロゴの「高さ」と「幅」の短い方を基準にします。
  • 明るさの閾値:ウォーターマークを配置する領域の平均輝度がこの値以上の場合は「黒いロゴ」を、それ未満の場合は「白いロゴ」を使用します。輝度の範囲は0(最も暗い)から255(最も明るい)です。
  • 透過率:黒いロゴと白いロゴの透過率をそれぞれ指定できます。値は0から1の間で設定し、0が完全に不透明、1が完全に透明を意味します。
  • ウォーターマークの位置(%指定):ウォーターマークの右下の位置を、画像の幅と高さに対する割合で指定します。値を調整することで、ウォーターマークの表示位置を自由に変更できます。例えば、0.95と設定すると、画像の右端から5%内側、下端から5%上の位置に配置されます。
  • ウォーターマーク画像のパス:使用する黒いロゴと白いロゴの画像ファイルのパスを指定します。
import os
import sys
import logging
import argparse
from PIL import Image, ImageOps
import numpy as np

########################################################################
# ユーザー設定可能な項目

# ウォーターマークのサイズ(画像の短辺のパーセンテージ)
LOGO_SCALE = 0.08  # 例:0.08は短辺の8%を意味します

# 明るさの閾値(0-255)
BRIGHTNESS_THRESHOLD = 110  # 黒と白のロゴを切り替えるための明るさの閾値(例:110)

# 不透明度(0から1の間)
OPACITY_DARK_LOGO = 0.5  # 黒いロゴの不透明度(0 = 完全に不透明、1 = 完全に透明)
OPACITY_LIGHT_LOGO = 0.4  # 白いロゴの不透明度(0 = 完全に不透明、1 = 完全に透明)

# ウォーターマークの位置(パーセンテージ)
ANCHOR_X_RATIO = 0.95  # ウォーターマークの右下位置(幅のパーセンテージ、例:0.95は右端から内側に5%)
ANCHOR_Y_RATIO = 0.95  # ウォーターマークの下位置(高さのパーセンテージ、例:0.95は下端から上に5%)

# ウォーターマーク画像へのパス
BLACK_WATERMARK_PATH = r"logo\logo-black.png"
WHITE_WATERMARK_PATH = r"logo\logo-white.png"

# ログファイルへのパス
LOG_FILE_PATH = "watermark_log.txt"

# ユーザー設定可能な項目の終了
########################################################################

def setup_logging():
    """ロギングの設定"""
    logging.basicConfig(
        filename=LOG_FILE_PATH,
        filemode='w',
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
    )

def load_logo_images():
    """ウォーターマーク画像を読み込む"""
    try:
        black_logo = Image.open(BLACK_WATERMARK_PATH).convert('RGBA')
    except FileNotFoundError:
        logging.error(f"黒いロゴ画像が見つかりません: {BLACK_WATERMARK_PATH}")
        sys.exit(1)
    except Exception as e:
        logging.error(f"黒いロゴ画像の読み込みエラー: {e}")
        sys.exit(1)

    try:
        white_logo = Image.open(WHITE_WATERMARK_PATH).convert('RGBA')
    except FileNotFoundError:
        logging.error(f"白いロゴ画像が見つかりません: {WHITE_WATERMARK_PATH}")
        sys.exit(1)
    except Exception as e:
        logging.error(f"白いロゴ画像の読み込みエラー: {e}")
        sys.exit(1)

    return black_logo, white_logo

def is_image_file(file_path):
    """Pillowを使用してファイルが画像かどうかを確認する"""
    try:
        with Image.open(file_path) as img:
            img.verify()  # 画像ファイルが破損していないことを確認
        return True
    except (IOError, SyntaxError) as e:
        logging.error(f"ファイル形式の確認エラー: {file_path}")
        logging.error(f"エラー詳細: {e}")
        return False

def calculate_average_brightness(region):
    """指定された領域の平均明るさを計算する"""
    region_rgb = region.convert('RGB')
    pixels = np.array(region_rgb)
    luminance = 0.2126 * pixels[:, :, 0] + 0.7152 * pixels[:, :, 1] + 0.0722 * pixels[:, :, 2]
    average_brightness = luminance.mean()
    return average_brightness

def adjust_logo_opacity(logo_image, opacity_value):
    """ロゴの不透明度を調整する"""
    opacity = int(255 * (1 - opacity_value))
    alpha = logo_image.split()[3]
    alpha = alpha.point(lambda p: p * (opacity / 255))
    logo_image.putalpha(alpha)
    return logo_image

def process_image(image_path, black_logo, white_logo):
    """画像にウォーターマークを追加する"""
    if not os.path.isfile(image_path) or not is_image_file(image_path):
        logging.warning(f"サポートされていないファイル形式またはファイルが見つかりません: {image_path}")
        return

    try:
        with Image.open(image_path) as im:
            # EXIFのOrientation情報に基づいて画像の向きを修正
            im = ImageOps.exif_transpose(im)

            # 画像をRGBAモードに変換(透明度を扱うため)
            im = im.convert('RGBA')
            width, height = im.size

            # 画像の短辺に基づいてウォーターマークのサイズを決定
            shorter_side = min(width, height)
            logo_size = int(shorter_side * LOGO_SCALE)  # 短辺に基づいてウォーターマークのサイズを設定

            # アスペクト比を維持しながらロゴの寸法を計算
            aspect_ratio = black_logo.width / black_logo.height
            logo_width = logo_size
            logo_height = int(logo_width / aspect_ratio)

            # ロゴをリサイズ(PNG形式のまま)
            black_logo_resized = black_logo.resize((logo_width, logo_height), resample=Image.LANCZOS)
            white_logo_resized = white_logo.resize((logo_width, logo_height), resample=Image.LANCZOS)

            # ウォーターマークの配置を計算
            anchor_x = int(width * ANCHOR_X_RATIO)
            anchor_y = int(height * ANCHOR_Y_RATIO)
            x = anchor_x - logo_width
            y = anchor_y - logo_height
            x = max(0, x)
            y = max(0, y)

            # ウォーターマークを配置する領域を切り抜く
            region = im.crop((x, y, x + logo_width, y + logo_height))

            # 領域の明るさを計算
            average_brightness = calculate_average_brightness(region)

            # 明るさに基づいてロゴを選択
            if average_brightness >= BRIGHTNESS_THRESHOLD:
                logo_to_use = black_logo_resized
                opacity_value = OPACITY_DARK_LOGO
                logging.info("選択されたロゴ: 黒")
            else:
                logo_to_use = white_logo_resized
                opacity_value = OPACITY_LIGHT_LOGO
                logging.info("選択されたロゴ: 白")

            # 選択したロゴに不透明度を適用
            logo_to_use = adjust_logo_opacity(logo_to_use, opacity_value)

            # ウォーターマーク用の新しいレイヤーを作成
            watermark_layer = Image.new('RGBA', im.size, (0, 0, 0, 0))
            watermark_layer.paste(logo_to_use, (x, y), logo_to_use)

            # 元の画像とウォーターマークを合成
            combined = Image.alpha_composite(im, watermark_layer)

            # 出力ファイルのパスと拡張子を決定
            dir_name, file_name = os.path.split(image_path)
            base_name, ext = os.path.splitext(file_name)
            output_filename = f"{base_name}_watermarked{ext}"
            output_path = os.path.join(dir_name, output_filename)

            # 画像を保存
            if ext.lower() in ['.png', '.gif', '.tiff', '.tif', '.webp']:
                combined.save(output_path)
            else:
                combined_rgb = combined.convert('RGB')
                combined_rgb.save(output_path, quality=95)

            logging.info(f"処理して保存しました: {output_path}")

    except Exception as e:
        logging.error(f"画像の処理中にエラーが発生しました: {image_path}")
        logging.error(f"エラー詳細: {e}")

def main():
    # ロギングの設定
    setup_logging()

    # コマンドライン引数を処理
    parser = argparse.ArgumentParser(description='画像にウォーターマークを追加するスクリプト')
    parser.add_argument('images', nargs='+', help='処理する画像ファイルへのパス')
    args = parser.parse_args()

    # ウォーターマーク画像を読み込む
    black_logo, white_logo = load_logo_images()

    for image_path in args.images:
        process_image(image_path, black_logo, white_logo)

if __name__ == "__main__":
    main()

コード解説:

  • ライブラリのインポート:画像処理にPillow、数値計算にNumPyを使用しています。
  • ウォーターマーク画像の読み込み:指定したパスから黒と白のロゴ画像をRGBAモードで読み込みます。RGBAモードにすることで、透過情報を扱うことができます。
  • 画像ファイルの取得:コマンドライン引数から処理対象の画像ファイルのリストを取得します。これにより、複数の画像を一度に処理することが可能です。
  • 画像のサイズに応じたロゴのリサイズ:画像の縦横比を判定し、設定した割合に基づいてロゴのサイズを調整します。縦長画像では高さ、横長画像では幅を基準にロゴのサイズを計算し、アスペクト比を維持します。
  • ウォーターマークの位置計算:設定した位置の割合に基づいて、ウォーターマークを配置する位置を計算します。右下の位置を基準に、ロゴのサイズを考慮して左上の位置を求めます。位置が画像の範囲外にならないように調整します。
  • 明るさの計算:ウォーターマークを配置する領域の平均輝度を計算します。その値が明るさの閾値以上かどうかで、黒いロゴと白いロゴのどちらを使用するかを決定します。
  • 透明度の設定:選択したロゴに対して、設定した透過率を適用します。アルファチャンネルを操作することで、ロゴの透明度を調整し、画像に自然に重ね合わせます。
  • ウォーターマークの合成:元の画像とウォーターマークを合成します。透過情報を持つウォーターマークを正しく重ねるために、アルファ合成を使用しています。
  • 画像の保存:処理後の画像を、元のファイル名に「_watermarked」を付加して保存します。画像形式に応じて、適切なモードで保存します。例えば、JPEG形式の場合はRGBモードに変換し、品質を指定して保存します。

install.bat

環境構築用のバッチファイルです。仮想環境(venv)を作成し、必要なライブラリをインストールします。

初回ダウンロード時のみ実行する必要があり、インストール後は削除して構いません。


@echo off
chcp 65001 >nul
setlocal

REM 仮想環境を作成
python -m venv venv

REM 仮想環境をアクティベート
call venv\Scripts\activate.bat

REM 必要なライブラリをインストール
pip install --upgrade pip
pip install Pillow numpy

echo 環境構築が完了しました。
pause

コード解説:

  • 仮想環境の作成:Pythonのvenvモジュールを使って仮想環境を作成します。
  • ライブラリのインストール:PillowとNumPyをpipでインストールします。

watermark.bat

以下がPythonスクリプトを実行するためのバッチファイルです。


@echo off
chcp 65001 >nul
setlocal

REM 仮想環境をアクティベート
call venv\Scripts\activate.bat

REM スクリプトのパスを設定(バッチファイルと同じディレクトリに置く場合)
set SCRIPT_PATH=%~dp0watermark.py

REM 引数を収集
set args=
:loop
if "%~1"=="" goto after_loop
set args=%args% "%~1"
shift
goto loop
:after_loop

REM スクリプトを実行
python "%SCRIPT_PATH%" %args%

REM エラーレベルをチェック
if %ERRORLEVEL% neq 0 (
    echo エラーが発生しました。
    pause
)

コード解説:

  • 仮想環境のアクティベート:install.batで作成した仮想環境をアクティベートします。
  • スクリプトの実行:コマンドライン引数で指定した画像ファイルに対してPythonスクリプトを実行します。
  • エラーハンドリング:エラーが発生した場合はメッセージを表示します。

5. まとめ

このスクリプトを使うことで、画像に対する透かしの追加が簡単に自動化できます。明るさに応じたロゴの選択や、画像サイズに合わせたロゴのリサイズなど、細かい調整も不要です。ぜひ一度試してみてください。

ご質問やフィードバックがございましたら、以下のコメント欄からお気軽にお寄せください。

最後までお読みいただきありがとうございました。このスクリプトが皆様のお役に立てれば幸いです。

コメントする

ABOUTこの記事をかいた人

Web、Python、生成AI、投資、行動経済学の知見を活かし、みんなの生活を豊かにするための情報発信を心がけています。日々の選択を少し変えるだけで、ちょっと笑顔が増えるかも。一緒に学び、成長しながら、より良い人生にしていきましょう。