【Marigold-LCM】賢すぎてトリックアートに騙されるAI

Marigold-LCM トリックアート 騙されるAI

WEELメディア事業部LLMリサーチャーの中田です。

3月26日、現時点で最強の深度推定モデル「Marigold」の高速バージョン「Marigold-LCM」を、スイス連邦工科大学チューリッヒ校のPhotogrammetry and Remote Sensing研究室が公開しました。

このモデルを用いることで、画像や動画の深度を推定できるんです!

MarigoldのGitHubでのスター数は、すでに1500を超えており、かなり注目されていることが分かります。

この記事ではMarigold-LCMの使い方や、有効性の検証まで行います。本記事を熟読することで、Marigold-LCMの凄さを理解し、「深度推定モデルと言えばこれ!」となるでしょう。

ぜひ、最後までご覧ください。

目次

Marigold-LCMの概要

Marigold-LCMとは、同研究チームが開発した単眼深度推定モデル「Marigold」の高速バージョンです。

そもそも深度とは「カメラから画像中の物体までの距離」のことを指し、「機械学習によって入力画像の深度を推定すること」を深度推定といいます。深度推定の中でも、モノラル画像を入力として扱う場合、「単眼深度推定」といいます。

Marigoldを使うことで、以下のように入力画像の深度を推定できます。

参考:https://huggingface.co/prs-eth/marigold-lcm-v1-0

他にも、動画の深度も推定できるのだとか。Marigoldの研究論文によると、その他の単眼深度推定モデルに比べても、高精度で深度推定できています(赤枠で囲った名前の列が、Marigoldによる深度推定結果)。

Marigoldのアーキテクチャ

以下の図は、Marigoldの学習過程です。

参考:https://marigoldmonodepth.github.io/

基本はStable Diffusion(Latent Diffusion Models)のアーキテクチャをベースとしています。まず、入力となる画像xと深度dを、Stable DiffusionのVAEでエンコードし、深度dの潜在変数にのみノイズを加えて、画像xと結合します。そうして結合したものを、UNetを用いてデノイズします。

以下の図は、Marigoldの推論過程です。

推論過程では、入力画像のみをStable DiffusionのVAEでエンコードし、そうして得た潜在変数を、完全ノイズの深度画像と結合し、UNetを用いてデノイズします。そうして、最終的にクリーンな深度画像を取得できるのです。

単眼深度推定を行えるAIモデルについては、「【Depth Anything】画像内の距離感を正確に理解できるAIにトリックアートを読ませてみた」を合わせてご確認ください。

Marigold-LCMのライセンス

公式HuggingFaceによると、Apache License 2.0のもと、無料で利用することが可能です。

利用用途可否
商用利用⭕️
改変⭕️
配布⭕️
特許使用⭕️
私的使用⭕️
参考:https://www.apache.org/licenses/LICENSE-2.0

Marigold-LCMの使い方

公式のGitHubColabノートブックを参考に、Google Colab上で実行していきます。

まず、以下のコマンドを実行し、diffusersをインストールしましょう。

!pip install --upgrade "diffusers>=0.25.0" --quiet

次に、以下のコードを実行して、フォルダの作成等、準備をしましょう。

import os


# Directories
repo_dir = "/content/Marigold"
input_dir = os.path.join(repo_dir, "input")
output_dir = os.path.join(repo_dir, "output")
output_dir_color = os.path.join(output_dir, "depth_colored")
output_dir_tif = os.path.join(output_dir, "depth_bw")
output_dir_npy = os.path.join(output_dir, "depth_npy")


os.makedirs(repo_dir, exist_ok=True)
os.makedirs(input_dir, exist_ok=True)
os.makedirs(output_dir, exist_ok=True)

os.chdir(repo_dir)

!export HF_HOME=$(pwd)/checkpoint

from diffusers import DiffusionPipeline

pipe = DiffusionPipeline.from_pretrained(
    "Bingxin/Marigold",
    custom_pipeline="marigold_depth_estimation"
)

pipe = pipe.to("cuda")

次に、以下のコードを実行して、推論準備をしましょう。

from IPython.display import display
import ipywidgets as widgets
import shutil

from google.colab import files

button_download = widgets.Button(description="Use sample images")
button_upload = widgets.Button(description="⬆ Upload images")
button_clear_in = widgets.Button(description="♻ Clear input folder")
button_clear_out = widgets.Button(description="♻ Clear output folder")
out_box = widgets.Output()


def on_button_download_clicked(b):
    out_box.clear_output()
    os.chdir(repo_dir)
    if os.path.exists(input_dir):
        shutil.rmtree(input_dir)

    with out_box:
        print("downloading sample images")

    # download data
    !wget -nv --show-progress https://share.phys.ethz.ch/~pf/bingkedata/marigold/in-the-wild_example.tar
    # untar
    !tar -xf "in-the-wild_example.tar"
    !rm "in-the-wild_example.tar"
    !mv "in-the-wild_example" "input"

    with out_box:
        print("sample images are downloaded")


def on_button_upload_clicked(b):
    out_box.clear_output()
    os.makedirs(input_dir, exist_ok=True)
    os.chdir(input_dir)
    with out_box:
        uploaded = files.upload()
    os.chdir(repo_dir)

def on_button_clear_in_clicked(b):
    out_box.clear_output()
    shutil.rmtree(input_dir)
    os.makedirs(input_dir)
    with out_box:
        print("Input images are cleared")

def on_button_clear_out_clicked(b):
    out_box.clear_output()
    shutil.rmtree(output_dir)
    os.makedirs(output_dir)
    with out_box:
        print("Output folder is cleared")

button_download.on_click(on_button_download_clicked)
button_upload.on_click(on_button_upload_clicked)
button_clear_in.on_click(on_button_clear_in_clicked)
button_clear_out.on_click(on_button_clear_out_clicked)

widgets.VBox([widgets.HBox([button_upload, button_download]),
              widgets.HBox([button_clear_in, button_clear_out]),
              out_box])

すると、以下のように表示されます。「Upload images」を押せば、手持ちの画像で深度を推定できます。今回は「Use sample images」を選択し、サンプル画像の深度を推定しました。

次に、以下のコードを実行して、深度を推定する生画像を表示して確認しましょう。

%matplotlib inline

from glob import glob
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# Pre-defined function
def display_images(image_paths, n_images_per_row = 4):
    n_rows = math.ceil(len(image_paths) / n_images_per_row)

    plt.figure(figsize=(10, 2 * n_rows))  # Adjust the size as needed
    for i, img_path in enumerate(image_paths):
        img = mpimg.imread(img_path)
        plt.subplot(n_rows, n_images_per_row, i + 1)  # Number of rows, number of images per row, current index
        plt.imshow(img)
        plt.axis('off')  # To not display axis

    plt.tight_layout()
    plt.show()


image_paths = glob(os.path.join(input_dir, "*"))

display_images(image_paths)

最後に、以下のコードを実行して、深度を推定しましょう。

# @title ▶️ 2.2.4 Run inference
denoising_steps = 10 # @param {type:"integer"}
ensemble_size = 10 # @param {type:"integer"}
processing_res = 768 # @param {type:"integer"}
match_input_res = True # @param ["False", "True"]

import torch
from tqdm.auto import tqdm
from glob import glob
from PIL import Image


EXTENSION_LIST = [".jpg", ".jpeg", ".png"]

# Image list
rgb_filename_list = glob(os.path.join(input_dir, "*"))
rgb_filename_list = [
    f for f in rgb_filename_list if os.path.splitext(f)[1].lower() in EXTENSION_LIST
]
rgb_filename_list = sorted(rgb_filename_list)

# Create output folders
os.makedirs(output_dir, exist_ok=True)
os.makedirs(output_dir_color, exist_ok=True)
os.makedirs(output_dir_tif, exist_ok=True)
os.makedirs(output_dir_npy, exist_ok=True)

# Run Inference
with torch.no_grad():
    os.makedirs(output_dir, exist_ok=True)

    for rgb_path in tqdm(rgb_filename_list, desc=f"Estimating depth", leave=True):
        # Read input image
        input_image = Image.open(rgb_path)

        # Predict depth
        pipeline_output = pipe(
            input_image,
            denoising_steps=denoising_steps,     # optional
            ensemble_size=ensemble_size,       # optional
            processing_res=processing_res,     # optional
            match_input_res=match_input_res,   # optional
            batch_size=0,           # optional
            color_map="Spectral",   # optional
            show_progress_bar=True, # optional
        )

        depth_pred: np.ndarray = pipeline_output.depth_np
        depth_colored: Image.Image = pipeline_output.depth_colored

        # Save as npy
        rgb_name_base = os.path.splitext(os.path.basename(rgb_path))[0]
        pred_name_base = rgb_name_base + "_pred"
        npy_save_path = os.path.join(output_dir_npy, f"{pred_name_base}.npy")
        if os.path.exists(npy_save_path):
            logging.warning(f"Existing file: '{npy_save_path}' will be overwritten")
        np.save(npy_save_path, depth_pred)

        # Save as 16-bit uint png
        depth_to_save = (depth_pred * 65535.0).astype(np.uint16)
        png_save_path = os.path.join(output_dir_tif, f"{pred_name_base}.png")
        if os.path.exists(png_save_path):
            logging.warning(f"Existing file: '{png_save_path}' will be overwritten")
        Image.fromarray(depth_to_save).save(png_save_path, mode="I;16")

        # Colorize
        colored_save_path = os.path.join(
            output_dir_color, f"{pred_name_base}_colored.png"
        )
        if os.path.exists(colored_save_path):
            logging.warning(f"Existing file: '{colored_save_path}' will be overwritten")
        depth_colored.save(colored_save_path)

「content/Marigold」の直下のフォルダ「input」内の全画像ファイルの深度を推定し、「output」フォルダに結果が格納されます。

以下の入力画像の場合。

深度推定した結果、以下の通りになりました。

MarigoldのHigging Faceスペースも用意されており、そこでは簡単に同様の手順を実行できます。

Marigold-LCMを実行する方法

ちなみに、Marigold-LCMを実行するには、HuggingFaceスペースを利用する方法があります。

オリジナルのMarigoldのスペースと比べて、「Image」「Video」「Bas-relief(3D)」も選べるようになっています。

一通り試した様子は、以下の通りです。

動画の深度も推定できるようです。また、深度推定した画像から、3Dモデルも作れるようです。

Marigoldを動かすのに必要なPCのスペック

■Pythonのバージョン
Python 3.8以上

■使用ディスク量
約4GB

■RAMの使用量
最大11.4GB

「複数の既存AIを組み合わせて作られた画像言語モデルでも深度推定できるのか」を試してみたい方は、「【EvoVLM-JP】存在しない最強のAIモデルを作れるSakana AIの「進化的アルゴリズム」を徹底解説!」を合わせてご確認ください。

既存の深度推定モデルと比較してみた

ここでは、Marigold-LCMの凄さを検証するために、「Marigold-LCM」と「Marigold」と「Depth Anything」で、以下の点で比較しようと思います。

  • 深度推定の精度
  • スピード

デノイジングステップは、すべてのモデルで同じ4ステップにしました。

ここで、入力画像には「奥行感のある画像」と「奥行きがあるように見せて無いトリックアート」の2種類を使いました。

奥行感のある画像で試した

ここで用いるのは、以下の画像です。

参考:https://www.cyclowired.jp/media/304335

各モデルによる深度推定の結果は、以下の通りです。

Marigold-LCM

Marigold

Depth Anything

生成速度の比較結果は、以下の通りです。

モデルスピード(秒)
Marigold-LCM9.5
Marigold9.8
Depth Anything6.8

トリックアートで試した

ここで用いるのは、以下の画像です。

参考:https://soei-creative.jp/news/trickart/

実はすべて平面の絵ですが、各深度推定モデルはどのような結果を出力するのでしょうか?

各モデルによる深度推定の結果は、以下の通りです。

Marigold-LCM

Marigold

Depth Anything

生成速度の比較結果は、以下の通りです。

モデルスピード(秒)
Marigold-LCM9.8
Marigold13.6
Depth Anything7.4

画像認識にも定評のあるClaude 3でも、深度推定できるのか実験したい方は、「【Claude 3】GPT-4を超えるAnthropicのOpus、Sonnet、Haikuとは?使い方や料金も解」を合わせてご確認ください。

深度推定において最高精度のMarigold-LCMの今後に要注目

本記事では、深度推定分野において、SOTAを達成しているMarigoldの高速バージョン「Marigold-LCM」についてご紹介しました。

検証結果より、精度はやはりMarigold-LCMが一番高いように思えました。とはいえ、それほど「生成速度に優位性がある」という結果にもならなかったです。

おそらく今後の改良で、リアルタイム生成が可能になるくらいまで、高速になっているのかもしれませんね。

ちなみに公式のXの投稿のリプライ欄では、以下のような「いくつかテストしてみたところ、Marigold-LCMよりも、オリジナルのMarigoldの方が良いような気がする。」といった意見も。

また、Marigoldを使って推定した深度画像を、Blenderで立体的に表示している方がいました。

最後に

いかがだったでしょうか?

弊社では

・マーケティングやエンジニアリングなどの専門知識を学習させたAI社員の開発
・要件定義・業務フロー作成を80%自動化できる自律型AIエージェントの開発
・生成AIとRPAを組み合わせた業務自動化ツールの開発
・社内人事業務を99%自動化できるAIツールの開発
ハルシネーション対策AIツールの開発
自社専用のAIチャットボットの開発

などの開発実績がございます。

まずは、「無料相談」にてご相談を承っておりますので、ご興味がある方はぜひご連絡ください。

➡︎生成AIを使った業務効率化、生成AIツールの開発について相談をしてみる。

生成AIを社内で活用していきたい方へ
無料相談

「生成AIを社内で活用したい」「生成AIの事業をやっていきたい」という方に向けて、生成AI社内セミナー・勉強会をさせていただいております。

セミナー内容や料金については、ご相談ください。

また、サービス紹介資料もご用意しておりますので、併せてご確認ください。

投稿者

  • 中田

    データサイエンス専攻の大学院生。大学では、生成系AIの拡散モデルを用いた音楽生成について研究。 趣味は作曲、サッカー、コーヒー。

  • URLをコピーしました!
  • URLをコピーしました!
目次