2023/11/08

【画像解析】コラージュを判別したい 【えんとろぴー?編】

きっかけ

最近お仕事関係で本人確認書類の偽造(加工)判定が出来ないか、という話題が上がりました。

そこでMicrosoftが提供している「Bing Chat」を活用して、画像解析を始めてみることにしました。

※自分は文系人間なので学術的な研究はまったくしたことないです。素人の遊び程度なので話半分で聞いてくだされば。

他の会社はどうしてんだろ…

既存技術

サクッと調べた感じ既存の画像真贋判定サービス/ソフトとして以下のようなものがあります。

①fotoforensics.com

②Ghiro(OSS)

どちらもいいサービスなのですが、そもそも個人情報をアップロードできるわけがないので①は却下。

②はメンテナンスがされておらず、python2環境の準備が現実的でないので実用に耐えませんでした。

AIの意見

Bing君にコラージュの検知方法について聞いたところ

・画像のエントロピーを解析し、画像中の情報量が多ければ加工済みと判断する

という謎の回答を得ました。

以下、コード

import cv2
import numpy as np
import sys

# コマンド実行時の引数で画像ファイル名を取得する
img_file = sys.argv[1]

# 画像を読み込む
img = cv2.imread(img_file)

# 画像のヒストグラムを計算する
hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# ヒストグラムの確率分布を計算する
prob = hist / np.sum(hist)

# ヒストグラムのエントロピーを計算する
entropy = -np.sum(prob * np.log2(prob + 1e-9))

# エントロピーを表示する
print("Entropy:", entropy)


いくつか画像を読み込んでみましたが、6~7くらいの数字が出力されました。

下の画像はBingのAI生成の画像です。サンプルとして利用します。


エントロピー:7.7723093

エントロピーとはその画像の持つ情報量を表す値だそうで、そもそも画像ごとに彩度や画質によって大きく変動するものなので真贋判定にはあまり役に立つとは思えませんでした。

エントロピーを有効的な真贋判定に使うのであれば、画像をマス目上に細かく分割し、部分ごとにエントロピーを計算して異常な値になるマスがないかを確認するのがよさそうです。

例えばフォトショ等で運転免許証の本人写真を差し替えた場合に、別の写真を組み合わせているわけなので加工された部分とされていない部分でエントロピーの値が大きく変わるのではないかと考えました。

上記の内容で同様にAIくんに生成してもらいました。

import cv2

import numpy as np

from scipy.stats import entropy


def calculate_entropy(image):

    histogram = cv2.calcHist([image], [0], None, [256], [0,256])

    histogram /= histogram.sum()

    return entropy(histogram)


def entropy_image(image_path, grid_size):

    image = cv2.imread(image_path, 0)

    height, width = image.shape

    entropy_image = np.zeros_like(image, dtype=np.float64)


    for i in range(0, height, grid_size):

        for j in range(0, width, grid_size):

            grid = image[i:i+grid_size, j:j+grid_size]

            entropy_image[i:i+grid_size, j:j+grid_size] = calculate_entropy(grid)


    cv2.normalize(entropy_image, entropy_image, 0, 255, cv2.NORM_MINMAX)

    return entropy_image.astype(np.uint8)


# 画像のパスとグリッドサイズを指定します

image_path = 'your_image_path.jpg'

grid_size = 10


# エントロピー画像を計算し、jpgファイルとして保存します

result = entropy_image(image_path, grid_size)

cv2.imwrite('entropy_image.jpg', result)

さっそく実行してみました。

出てきた画像

???

どうやらエントロピーの値の高さを色で表しているようです。

単純にネガポジ反転させた画像のようになってしまいあまり役には立たなそうです。

そこで次はエントロピー値をそのまま数字で並べて表示するようなスクリプトを作ってもらいました。

import cv2

import numpy as np

from scipy.stats import entropy

from PIL import Image, ImageDraw, ImageFont


def calculate_entropy(image):

    histogram = cv2.calcHist([image], [0], None, [256], [0,256])

    histogram /= histogram.sum()

    return entropy(histogram)


def entropy_image(image_path, grid_size):

    image = cv2.imread(image_path, 0)

    height, width = image.shape

    entropy_values = []


    for i in range(0, height, grid_size):

        for j in range(0, width, grid_size):

            grid = image[i:i+grid_size, j:j+grid_size]

            entropy_values.append(calculate_entropy(grid))


    return entropy_values


def create_entropy_image(image_path, entropy_values, grid_size, output_path):

    image = Image.open(image_path)

    draw = ImageDraw.Draw(image)

    font = ImageFont.truetype('nicomoji-plus_v2.ttf', size=22)


    # エントロピーの平均値を計算

    average_entropy = np.mean(entropy_values)


    index = 0

    for i in range(0, image.width, grid_size):

        for j in range(0, image.height, grid_size):

            if index < len(entropy_values):

                # エントロピーの値を小数点以下第1位までに丸める

                entropy_value = round(float(entropy_values[index][0]), 1)

                # エントロピーの値が平均より大きい場合は赤色、それ以外は青色で表示

                color = 'red' if entropy_value > average_entropy else 'blue'

                draw.text((i, j), str(entropy_value), fill=color, font=font)

                index += 1


    image.save(output_path)


# 画像のパスとグリッドサイズを指定します

image_path = 'input.jpg'

grid_size = 50


# エントロピーの値を計算し、それを画像として保存します

entropy_values = entropy_image(image_path, grid_size)

create_entropy_image(image_path, entropy_values, grid_size, 'entropy_image_num.jpg')

エントロピーの値が平均より高い場合は赤い文字、平均以下は青で表示するように指定しました。

画像を分割し、各所のエントロピーを求めた
※小さくてすみません

まあサンプル画像は別にコラ加工した写真というわけではないので意味はないのですが、コラなしの画像でもこんなにバラバラになってしまうと加工された画像でもあまり意味はなさない気がしますね。

エントロピーって使える?

ぶっちゃけ真贋判定に有効に使えるとは思いません。

ただ最後に(AIが)作った各所のエントロピーを元画像にオーバレイするコードは何か活用できそうな気もしなくもない…

画像解析について調べていくうちにELAなるものを見つけたので次回はそちらの実装を書ければと思います。

他にもトーンカーブをガチャガチャにすることで画像の違和感を見つける手法もありましたのでそちも紹介できれば。

0 件のコメント:

コメントを投稿