見出し画像

画像生成AIの指に関する研究 #4 - 物体検出の確信度を用いたアプローチを検討してみる


本研究のシリーズ記事


画像生成AIの指に関する研究 #1
何故AIは指が苦手なのか」

画像生成AIの指に関する研究 #2
CLIPが出したトークンは紐解けるのか

画像生成AIの指に関する研究 #3
特徴点抽出+統計分析で指の描画改善方法を導けるか



前回のおさらい


画像生成AIの指に関する研究4回目、ということで進めていきます。
前回、「特徴点抽出」で得られた類似度を割り出して、統計手法を用いて、実際に手の描写が改善されているかどうかを実践してみました。

以下は、おさらいです。

  • 最大値(Max)
    文字の如く、算出結果内の最大値です。

  • 最小値(Min)
    こちらも文字の如く、算出結果内の最小値です。

  • 平均値(Average)
    算出結果の平均値になります。

  • 中央値(Median)
    算出結果の分布の中央にくる値のことです。
    平均値ではありません。

  • 標準偏差(Standard Deviation)
    標準偏差とは、算出結果内の平均値からの散らばり具合(ばらつき)を表す指標の一つです。
    値が小さければ、その分ばらつきが小さい=高精度であるとなります。
    こちらは√分散になります。

  • 分散(Variance)
    標準偏差と似て非になるものです。こちらも散らばり具合(ばらつき)を表す指標の一つです。
    こちらも、値が小さければ、その分ばらつきが小さいことになります。
    こちらは、二乗分散になります。

特徴点抽出は0に近づけば近づくほど類似している、という指標になります。

また、キーポイント検出とは、特徴点を可視化する処理のことを指します。
出力はこのように表示されます。

マスター画像と対象画像と比較し、画像の特徴量を検出及び一致性を算出し、視覚化します。
通常は特徴量マッチング部分の線描画を減らしてます。
何も指定しなければこうなります。

もう何が何だかわからないので、参考までに…。

ちなみに、画像がグレースケール化しているのは、造形の輪郭を捉えやすくする為です。
色味や色彩は一切考慮されません。
指の造形の比較である為、フルカラーである必要がないからです。


発覚した問題点


以上のような手法を用いて、特徴点抽出の差分比較を実施していました。
しかしここには、大きな問題点があります。

特徴点抽出の特性上、非常に近似しているデータ同士を突合し、その中でも微妙に変化している点の差分を抽出するのには最適な手法です。

しかし、この手法だと今回の統計解析をする場合、偏ったバイアスの統計のみを抽出することになり、データ自体の信頼性に揺らぎが生じてしまうのではないか、と私の中で懸念が生じました。

その、偏ったバイアスとは何でしょうか。

  • 描写角度を考慮していない(=様々な手の角度が通常は存在する)

  • 人物や背景の造形、構図が変化すると、サンプルに不適

  • ランダムシードによる描写の統計が取れない

  • img2img+Variation Seedを使用するのと、txt2imgで生成するのとでは生成過程が異なる為、サンプルとして不適

これらの要素から算出した統計情報は、特定のバイアスのみ働いているデータになると思われる為、統計データの手法を再度検討してみることにしました。


新しいアプローチ方法


物体検出モデルを使用する

物体検知モデルはSD WebUIの拡張機能でも一部使用されているものがあります。
それは、After Detailer(ADetailer)です。
顔検出や手検出などを行って、抽出された座標と範囲に対して、補正をかける拡張機能です。
その拡張機能で使用されているのが、YOLOv8の技術になります。


YOLOv8とは?

YOLOv8とは、非常に有名な物体検出手法であるYOLOのモデルになります。
今回は、2023年11月時点で最新バージョンのYOLOv8を使用することにしました。
YOLOでは、Detection(物体検出)、Segmentation(領域検出)、Classfication(分類検出)の3つのタスクが利用可能です。
推論を実行すると学習内容の物体に対して確信度(Confidence Score)を算出し、確信度閾値(Confidence Threshold)以上の物体を出力します。(Confidence Thresholdのデフォルトは0.25)

以下がYOLOv8の検出アルゴリズムに関するPaperです。


使用するYOLOv8モデルは?

今回の検証にとっても丁度いいモデルがあります。
それは、After Detailerに同梱されている「hand_yolov8n.pt」です。
Hugging Faceにも同モデルはアップロードされていますので、興味のある方は参考までに。


今回使用するスクリプト

今回もPythonスクリプトを作成しました。
毎回のことながら、本職の方から見られるとツッコミどころが多いスクリプトになってます。

[ 2023.11.07 修正 ]
物体検出数が2か所以上存在した場合に、テキストファイル操作部分でエラーが発生することを確認したので、コード修正しました。

[ 2023.11.11 修正 ]
YOLOv8が適用しているライセンスがAGPL v3.0であり、YOLOモジュールを適用しているソースコード全文の公開時はライセンス継承表記が必要らしいので、ライセンス表記しました。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------
# Script : object_analysis.py
# ------------------------------------------------------------------------
# Description : Confidence Score Statistics Analysis Tool
# Author : konapieces <https://twitter.com/konapieces>
# License : AGPL 3.0 License
# ------------------------------------------------------------------------
# Copyright (C) 2023 konapieces
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
# ------------------------------------------------------------------------
# Module : YOLOv8 by Ultralytics
# License : AGPL 3.0 License
# ------------------------------------------------------------------------
# Copyright (C) 2023 Ultralytics
# 
# This script uses the YOLOv8 by Ultralytics module.<https://github.com/ultralytics/ultralytics>
# 
# YOLOv8 by Ultralytics is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# 
# YOLOv8 by Ultralytics is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with YOLOv8 by Ultralytics.  If not, see <http://www.gnu.org/licenses/>.
# 
# ------------------------------------------------------------------------

from ultralytics import YOLO
import argparse
import logging
import sys
import glob
import os
import pandas as pd
import datetime
import datetime as dt

# パーサ作成
parser = argparse.ArgumentParser(
            prog='object_analysis.py',
            usage=('\n'
                    'object_analysis.py <arg> <target_directory> <output_directory>'),
            description=('Arguments are required to be specified.\n'
                         'Specify the path of the image file for which you want to detect objects.\n'
                        'Confidence Score Statistics Analysis Tool.'),
            add_help=True,
            )

parser.add_argument('target', help='specifies the file path of the source image')
parser.add_argument('output', help='specifies the output directory')
parser.add_argument('-d', '--hand',action='store_true', help='Perform detect hands')
parser.add_argument('-o', '--object',action='store_true', help='Perform detect objects')

args = parser.parse_args()

# 変数
target_path = args.target
output_path = args.output
target_file_name = os.path.basename(target_path)
i=1
j=1

# Script実行時間取得
f_format = datetime.datetime.now()
output_dir = "{0:%Y%m%d_%H%M%S}".format(f_format)
os.mkdir(output_path+"/"+output_dir)

output = str(output_path +  "/" + output_dir)

# 物体検知使用モデル
if args.hand is True:
        model = YOLO("./models/hand_yolov8n.pt")
        models = "hand_yolov8n.pt"
elif args.object is True:
        model = YOLO("./models/yolov8n.pt")
        models = "yolov8n.pt"
elif args.hand is False and args.object is False:
        print('[!] You must specify an argument')
        exit(1)


# メインルーチン
if __name__ == '__main__':
        # Debuglog
        logging.basicConfig(
                level=logging.DEBUG,
                format='%(asctime)s %(levelname)s: %(message)s',
        )
        logging.info('%s Object Detection Test Start.' % (__file__))

        # プロンプト表示
        print("")
        print("Target Image = "+args.target)
        print("Using model = "+models)
        print("")

        # 画像対象取得
        pattern = '%s/*.png'
        comparing_files = glob.glob(pattern % (target_path))
        if len(comparing_files) == 0:
                logging.error('no files.')
                sys.exit(1)

        # 格納画像処理
        for comparing_file in comparing_files:
                comparing_file_name = os.path.basename(comparing_file)
                if comparing_file_name == target_file_name:
                        continue

                comparing_img_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)) + '/../',
                comparing_file,
                )
                # 物体検出推論実行
                results = model.predict(
                        comparing_img_path,
                        save=True,
                        imgsz=512,
                        conf=0.5,
                        show=True,
                        save_conf=True,
                        save_txt=True,
                        exist_ok=True,
                        project=output)

        # 信頼度取得
        pattern = '%s/*.txt'
        txtOutput=output+"/predict/labels/"
        target_file_name = os.path.basename(txtOutput)
        comparing_files = glob.glob(pattern % (txtOutput))
        if len(comparing_files) == 0:
                logging.error('no files.')
                sys.exit(1)

        # テキストファイル処理
        df = pd.DataFrame(columns=['Data','Confidence Score'])
        for comparing_file in comparing_files:
                comparing_file_name = os.path.basename(comparing_file)
                if comparing_file_name == target_file_name:
                        continue

                comparing_txt_path = os.path.join(
                os.path.abspath(os.path.dirname(__file__)) + '/../',
                comparing_file,
                )

                with open(comparing_txt_path, 'r') as file:
                        txtLength = sum(1 for line in file)
                print(txtLength)

                for j in range(txtLength):
                        ref_ = pd.read_csv(comparing_txt_path,
                                                engine='python',
                                                sep="[,;/ :\t]",
                                                names=['Num','X Axis','Y Axis','Width','Height','Confidence Score'],
                                                usecols=['Confidence Score'])
                        ref=float(ref_.values[j])
                        df.loc[str(i)]=[comparing_file_name,ref]
                        i=i+1
                        logging.info('%s: %f.' % (comparing_file_name,ref))
                j=1

        # 統計値算出
        df.loc['Max'] = ['',df['Confidence Score'].max()]
        df.loc['Min'] = ['',df['Confidence Score'].min()]
        df.loc['Average'] = ['',df['Confidence Score'].mean()]
        df.loc['Median'] = ['',df['Confidence Score'].median()]
        df.loc['Standard Deviation'] = ['',df['Confidence Score'].std()]
        df.loc['Variance'] = ['',df['Confidence Score'].var()]
        print(df)

        # csv出力
        now = dt.datetime.now()
        time = now.strftime('%Y%m%d-%H%M%S')
        df.to_csv(output+'\ObjectDetection_Test_{}.csv'.format(time))

        logging.info('%s Object Detection Test End.' % (__file__))
        sys.exit(0)

スクリプト仕様は以下。

  • Parserを作成して、Arg指定できるようにしてますが、全て指定必須です。
    -dで手検出、-oで物体検出します。必ず指定してください。

  • target_directoryに対して、総当たり検査し、output_directoryに結果を出力します。

  • 手検出モデルである「hand_yolov8n.pt」は自動取得しませんが、物体検出モデルの「yolov8n.pt」は指定ディレクトリにモデルがなければ自動的にダウンロードします。

  • スクリプトを使用する場合は、モデルの配置場所を使用環境に応じて適時書き換えてください。

  • 統計関数を使用し算出したものを、CSV形式で出力します。

  • 座標取得したい場合は、「ref_」変数のusecolsプロパティをX AxisやY Axis等に変えれば幸せになれます。

  • デフォルトだと推論結果をリアルタイムで描写するようにしてるので、目障りな場合はmodel.predict()内のshowプロパティをFalseにしてください。

  • 統計値算出は取捨選択できます。必要ないものは消してもOKです。

今更ですけど、defで関数を定義せずに作ってるのが…本職じゃない感ありますね…


試しに使用してみる


では、試しにスクリプトを動かしてみましょう。
今回は、手検出で実施してみます。

python .\object_detection.py -d "D:\predict_img\" "D:\yolo_outputs\"
(e.g.) 00001-4074019568.png
Terminal出力
CSV出力

このスクリプトで何ができるの?


物体検出の確信度統計を取るためのスクリプトなので、YOLOv8の学習済みモデルさえ準備できれば、イメージデータ内における特定の物体描写が、物体検知モデルからどのように見えているかの指標を統計分析することができると思います。

つまりは、様々なNegative TI別の効果分析を、統計的視点から算出することもできると思います。

これはあくまでも、物体検出の確信度に「描写の正確性について数値的に判別でき、データに有用性がある」という前提に立っているので、本来の信頼性があるかどうかはハッキリしないところがあります。

それをご承知ください。


おわりに


画像生成AIの指に関する研究 #4 - 物体検出の確信度を用いたアプローチを検討してみる
いかがだったでしょうか。

今回は新アプローチの部分なので、一般公開とさせて頂きました。
関数定義してないのが本当に恥ずかしい…
おかげさまで、とても見づらいコードになってます…

でもまあ、動くからヨシ!w

次回からは、この新しい手法で検証のほうを進めてみたいと思います!

次回もお楽しみに!


この記事が参加している募集

AIとやってみた

よろしければサポートお願いします!✨ 頂いたサポート費用は活動費(電気代や設備費用)に使わさせて頂きます!✨