Optuna との連携 -- FIE関数のパラメータ自動調整


Optuna と連携し、 FIE関数(平均化フィルタとシフトアベレージ⼆値化)のパラメータを ⾃動調整するサンプルコードです。

本サンプルの実行には予め Optuna と pycma をインストールしておく必要があります。 pip コマンドを使用する場合、次のようにインストールしてください。

pip install optuna==1.2.0 cma==2.7.0

処理内容

下図のようなノイズとシェーディングのあるチェスボード画像の二値化を考えます。

../../_images/optuna_src.png

下図のような出力結果を得ることが理想です。

../../_images/optuna_ans.png

画像処理手続きは下記の通りとします。

  1. 入力画像(図1)読み込み

  2. MxN平均化フィルタ fnFIE_averageMxN() による平滑化

  3. 局所領域平均値とオフセットによる2値化(シフトアベレージ2値化) fnFIE_shift_avr_threshold_offset()

  4. 結果出力

この手続きで使用する下記5種類のパラメータを自動決定します。

  • MxN平均化 フィルタ横サイズ (ave_x)

  • MxN平均化 フィルタ縦サイズ (ave_y)

  • シフトアベレージ2値化 局所領域平均エリア横サイズ (shift_ave_x)

  • シフトアベレージ2値化 局所領域平均エリア縦サイズ (shift_ave_y)

  • シフトアベレージ2値化 オフセット値 (shift_ave_offset)

自動決定のための評価基準として、結果画像と理想画像の差の絶対値和を使用します。

コード

import pyfie
import optuna


def main():
    """メイン処理"""

    # 利便のため、FIE関数のエラーを例外にする
    pyfie.ctrl.enable_f_err_exception(True)

    # 入力画像読み込み
    hsrc = pyfie.imread("optuna_src.png")
    # 正解画像(理想の出力画像)読み込み
    hans = pyfie.imread("optuna_ans.png")

    # パラメータ探索用オブジェクト作成
    objective = Objective(hsrc, hans)
    sampler = optuna.integration.CmaEsSampler()  # サンプリングアルゴリズムはCMA-ES
    study = optuna.create_study(sampler=sampler)
    # パラメータ探索実行
    study.optimize(objective, n_trials=500)

    # 結果出力
    # 推定されたパラメータを表示
    print("推定された最適なパラメータ: ", study.best_params)

    # 出力画像を得るため最適なパラメータでもう一度処理
    objective(optuna.trial.FixedTrial(study.best_params))

    # 出力画像を保存
    pyfie.imwrite("optuna_dst.png", objective.hdst)
    print("出力画像をoptuna_dst.pngとして保存しました。")


class Objective(object):
    """目的関数クラス"""

    def __init__(self, hsrc, hans):
        self.hsrc = hsrc  # 入力画像
        self.hans = hans  # 正解画像

        # 入力画像はuc8, 1ch. 正解画像はbin, 1ch. 2画像のサイズは等しい
        assert hsrc.f_type == pyfie.F_IMG_UC8
        assert hans.f_type == pyfie.F_IMG_BIN
        assert hsrc.ch == 1 and hans.ch == 1
        assert hsrc.width == hans.width and hsrc.height == hans.height

        # 作業用画像確保
        self.have = hsrc.empty_like()
        self.hdiff = hans.empty_like()

        # 出力画像確保
        self.hdst = hans.empty_like()

    def __call__(self, trial):
        """目的関数。画像処理手続き本体を兼ねる"""
        # -- 今回試すパラメータを取得

        # 平均化フィルタサイズ
        ave_x = int(trial.suggest_discrete_uniform('ave_x', 1, 99, 2.0))
        ave_y = int(trial.suggest_discrete_uniform('ave_y', 1, 99, 2.0))

        # 局所領域平均値とオフセットによる2値化
        # 近傍平均エリアサイズ
        shift_ave_x = int(trial.suggest_discrete_uniform(
            'shift_ave_x', 3, 99, 2.0))
        shift_ave_y = int(trial.suggest_discrete_uniform(
            'shift_ave_y', 3, 99, 2.0))
        # オフセット
        shift_ave_offset = trial.suggest_int('shift_ave_offset', -100, +100)

        # -- 処理実行
        pyfie.fnFIE_averageMxN(
            self.hsrc, self.have, ave_x, ave_y, pyfie.F_BORDER_CONTINUOUS, 0
        )
        pyfie.fnFIE_shift_avr_threshold_offset(
            self.have, self.hdst, shift_ave_x, shift_ave_y, shift_ave_offset,
            pyfie.F_BORDER_CONTINUOUS, 0
        )

        # -- 評価値算出
        # 評価値は正解との差の絶対値和とする
        pyfie.fnFIE_img_diff(self.hdst, self.hans, self.hdiff)
        sad = self.hdiff.clone_to_ndarray().sum()

        return sad


if __name__ == "__main__":
    main()

処理結果例

$ python sample_optuna.py
[I 2020-05-27 16:58:56,489] Finished trial#0 resulted in value: 125000.0. Current best value is 125000.0 with parameters: {'ave_x': 97.0, 'ave_y': 23.0, 'shift_ave_x': 41.0, 'shift_ave_y': 11.0, 'shift_ave_offset': -89}.
[I 2020-05-27 16:58:56,573] Finished trial#1 resulted in value: 58870.0. Current best value is 58870.0 with parameters: {'ave_x': 35.0, 'ave_y': 41.0, 'shift_ave_x': 23.0, 'shift_ave_y': 75.0, 'shift_ave_offset': -2}.
[I 2020-05-27 16:58:56,656] Finished trial#2 resulted in value: 125000.0. Current best value is 58870.0 with parameters: {'ave_x': 35.0, 'ave_y': 41.0, 'shift_ave_x': 23.0, 'shift_ave_y': 75.0, 'shift_ave_offset': -2}.
[I 2020-05-27 16:58:56,736] Finished trial#3 resulted in value: 125000.0. Current best value is 58870.0 with parameters: {'ave_x': 35.0, 'ave_y': 41.0, 'shift_ave_x': 23.0, 'shift_ave_y': 75.0, 'shift_ave_offset': -2}.
[I 2020-05-27 16:58:56,819] Finished trial#4 resulted in value: 123599.0. Current best value is 58870.0 with parameters: {'ave_x': 35.0, 'ave_y': 41.0, 'shift_ave_x': 23.0, 'shift_ave_y': 75.0, 'shift_ave_offset': -2}.

... (中略) ...

[I 2020-05-27 17:01:39,949] Finished trial#497 resulted in value: 81.0. Current best value is 80.0 with parameters: {'ave_x': 3.0, 'ave_y': 3.0, 'shift_ave_x': 65.0, 'shift_ave_y': 55.0, 'shift_ave_offset': -1}.
[I 2020-05-27 17:01:40,487] Finished trial#498 resulted in value: 80.0. Current best value is 80.0 with parameters: {'ave_x': 3.0, 'ave_y': 3.0, 'shift_ave_x': 65.0, 'shift_ave_y': 55.0, 'shift_ave_offset': -1}.
[I 2020-05-27 17:01:40,994] Finished trial#499 resulted in value: 81.0. Current best value is 80.0 with parameters: {'ave_x': 3.0, 'ave_y': 3.0, 'shift_ave_x': 65.0, 'shift_ave_y': 55.0, 'shift_ave_offset': -1}.
推定された最適なパラメータ:  {'ave_x': 3.0, 'ave_y': 3.0, 'shift_ave_x': 65.0, 'shift_ave_y': 55.0, 'shift_ave_offset': -1}
出力画像をoptuna_dst.pngとして保存しました。

結果画像

../../_images/optuna_dst_doc.png

注釈

本サンプルコードは単純な評価基準と探索アルゴリズムを用いているため、 処理結果は不安定で低精度であることにご注意ください。

ダウンロード