見出し画像

Generative-AI in Grasshopper (2) -Mesh

Grasshopper における生成AIの導入方法と用途を模索してみる
今回は画像生成とMeshについて



1. Image to Mesh


1-1. Grasshopper における画像の取り扱い

Rhinoceros や Grasshopper は画像を扱うことにはあまり適していない。
特にGHはデフォルトの機能だけではかなり厳しい。ImageSampleやImportImageなどがあるが、どれも自由度や応用可能性は低い。

このnoteでは、いくつかの追加プラグインやpythonによる自作プログラムなどを用いながら、GHでの画像の取り扱いの可能性を見ていく。画像生成AI関連で話を進めていくが、一般の画像においても活用できる。

GHで画像を扱うにあたり、大きく分けて以下の3つの使用法が思い浮かぶ。
今回はMeshについて考えてみる。

1. 画像の情報を参考にする
2. 画像をMeshとして扱う
3. 画像をTextureとして扱う


プラグインは主に以下の2つを用いる。
bitmapの取得や編集のための"Bitmap+"と、urlなどオンラインとの通信を行う"ShapeDiver"である。


また、前回はHopsを用いてGH上で画像を生成する手法を模索したが、これ以降の解説では、基本的にGH以外で生成した画像でも問題ない話題が多い。stable diffusion webui などで生成した画像をGHで読み込むほうが分かりやすくはある。インタラクティブに処理する必要がない場合、あまりメリットは感じないかもしれない。

外部の画像のインポートは、例えば以下のように行う。FilePathで設定したパスから、Bitmap+のコンポーネントでbitmapを取得する。

Hopsを用いてGH内で生成すると以下のようになる。
stablediffusionは生成画像のurlが渡されるだけなので、こちらでアクセスする必要がある。この例では出力にurlとfilepathを設定した。url先の画像をoutputフォルダに自動保存するプログラムを組み込んでいる。
Hopsでの処理が終わると、ShapeDiverでurlの、Bitmap+でfilepathの画像を取得できるようになっている。
※Hopsでは入出力に画像を設定できないため、urlやpathを通してやり取りする必要がある

import os
import replicate
from flask import Flask
import ghhops_server as hs
import rhino3dm
import requests
import datetime

os.environ["REPLICATE_API_TOKEN"] = "YOUR_REPLICATE_API_KEY"

app = Flask(__name__)
hops = hs.Hops(app)

def save(u):
    dir = './output'
    now = datetime.datetime.now()
    filename = now.strftime('%Y%m%d-%H%M%S')
    path = os.path.join(dir,filename+".jpg")

    response = requests.get(u)
    im = response.content
    with open(path, "wb") as aaa:
        aaa.write(im)
    return path

@hops.component(
    "/rep-sdxl",
    name="Rep-img",
    description="img from replicate",
    inputs=[
        hs.HopsString("p","p","prompt")
    ],
    outputs=[
        hs.HopsString("o","o","output url"),
        hs.HopsString("f","f","file path")
    ],
)
def rep-sdxl(prompt):
    url = replicate.run(
    "stability-ai/sdxl:d830ba5dabf8090ec0db6c10fc862c6eb1c929e1a194a5411852d25fd954ac82",
    input={"prompt": prompt}
    )
    fn = save(url[0])
    absfn = os.path.abspath(fn)
    return url, absfn


if __name__ == "__main__":
    app.run(debug=True)


1-2. Image to Mesh

GHに取り込んだ画像のMesh変換は簡単にできる。
デフォルトコンポーネントのImportImageを用いてMeshを作る。Meshの各頂点の色が画像のPixelの色に対応している。

1024x1024をそのままMeshにすると、100万以上の頂点数になってしまいかなり重くなる。サクサク動かすためにある程度精度を落として読み込む必要がある。(プログラム作成中は頂点数を少なくしておいて、レンダリング時に最高精度にするなど)

Meshの頂点のみを抽出すると点群が得られる。
DeconstructMesh の C から色情報を取得できる。例えばこれを明度に変換することで、画像の明るさのヒストグラムを作ることができる。


2. Mesh の加工

画像そのままのMeshでは使い道があまりない。看板の画像やビルのファサードなど、建築での活用は狭い範囲に限られる。
ここでは、AIの画像認識の手法などを応用して、Meshを加工してみる。

2-1. 深度推定

画像として読み込んだMeshを立体にする方法の一つとして、深度マップを使うことが考えられる。AIに画像の奥行きを推定させることで、2次元のメッシュを3次元に拡張できる。

深度推定のためのモデルは色々あるが、今回は以下のモデルを使ってみる。
Stable Diffusion webui の拡張機能として用いると、入力画像の深度を数秒で推定し、白黒マップとして出力してくれる。

ここでは、Stable Diffusion で生成した建築ファサード画像の深度を推定させた。以下のような点群や Mesh に変換できた。最終的に3Dプリントもしてみた。


以下のようなプログラムを組んでいる。

  1. 元画像と深度画像を同じサイズで Import Image する

  2. 深度の方のMesh を Deconstruct Mesh して、各頂点のColor を抽出する

  3. 頂点色の明度や輝度を Color AHSL で抽出

  4. 0.0-1.0 からなる数値を Graph Mapper で微調整する

  5. 各頂点を、その数値に比例した値だけZに移動させる

  6. Construct Mesh で、移動した頂点をもとに Mesh を再構成する

深度推定モデルをGHに直接導入する場合は、以下のようなプログラムを Hops を介して動かす。

@hops.component(
    "/rep-depth",
    name="Rep-depth",
    description="depth from url",
    inputs=[
        hs.HopsString("u","u","input url")
    ],
    outputs=[
        hs.HopsString("o","o","output url")
    ],
)
def rep_depth(url):
    output = replicate.run(
    "cjwbw/zoedepth:6375723d97400d3ac7b88e3022b738bf6f433ae165c4a2acd1955eaa6b8fcb62",
    input={
        "model_type": "ZoeD_N",
        "image":url
        }
    )
    return output


2-2. 背景除去

AIの画像処理関連で最も普及しているのが背景除去だろう。
以下のモデルを取り入れて、Mesh の背景を取り除いてみる。


Stable DIffusion や外部のサービスで、画像の白黒マスクを作成する。
それを用いて、以下のようなプログラムで背景を除去する。

  1. マスク画像を元画像と同じサイズで Import Image する

  2. Deconstruct Mesh と Color AHSL を用いて、各頂点の明度・輝度を抽出する

  3. 数値が0.5より大きいか否かを判別する(白黒なら基本的に0.0か1.0)

  4. 元画像から構成したMesh に対して、Cull Vertices で白部分だけを残す(ここでは2-1で作成した立体Meshに適用)


背景除去モデルをGHに直接導入する場合は、以下のようなプログラムを Hops を介して動かす。ここでは OpenCV を用いている。

from rembg import remove
import cv2
import numpy as np

def mask(p):
    output_mask_path = "output-mask.png"

    img = cv2.imread(p,-1)

    index = np.where(img[:,:,3]!=0)
    img[index] =[255,255,255,255]
    index_alpha = np.where(img[:,:,3]==0)
    img[index_alpha] = [0,0,0,255]

    cv2.imwrite("output-mask.png", img)
    return output_mask_path

@hops.component(
    "/rembg",
    name="Rembg",
    description="remove background",
    inputs=[
        hs.HopsString("i","i","input file path")
    ],
    outputs=[
        hs.HopsString("o","o","output file path"),
        hs.HopsString("m","m","mask file path")
    ],
)
def rembg(path):
    input_path = path
    output_path = "output.png"

    input = cv2.imread(input_path)
    output = remove(input)
    cv2.imwrite(output_path, output)

    output_mask_path = mask(output_path)
    
    return os.path.abspath(output_path), os.path.abspath(output_mask_path)



3. 応用例


"Beautiful Housing" な街並みを3Dで表現

"beautiful housing"



渋谷の壁面にMonsterを埋め込む

この記事が気に入ったらサポートをしてみませんか?