(追記: どの応募案がどのクラスに分類されたかの結果を貼り忘れていました。CSVファイルをダウンロード)

2014年の建築界隈の主要トピックスの一つであるGuggenheim Helsinkiのデザインコンペティションは1715の応募数を集め、2002年の大エジプト博物館コンペの応募数1557を凌ぐ史上最大規模の建築デザインコンペとなったようです。(参考)

建築デザインの経済的求心力がビルバオ・グッゲンハイムで明らかになったことが、人々の関心を建築に集めたことは間違いないでしょう。

応募案はhttp://designguggenheimhelsinki.org/stageonegallery/view/ にアクセスすれば一覧出来ますが、とにかく多すぎる。審査員もうんざりしたことでしょう。僕は傾向を見たいだけなので、適当に画像処理技術を使ってクラスタリングしてみました。

審査員も適当にザッピングして評価してるよ。きっと。建築の歴史はメディアの歴史でどうこうむにゃむにゃ言うつもりはありません。

あわせて読みたい:

素材集め

http://designguggenheimhelsinki.org/stageonegallery/view/にアクセスしてctrl+sでhtmlと画像ファイルをダウンロードします。なぜかファイナリストの画像だけダウンロードできなかったので、ファイナリスト6人のエントリーIDごとに”http://a5.designguggenheimhelsinki.org/GH-1128435973-partC1.jpg”にアクセスして画像をダウンロードします。

これで150×150ピクセルの1715個の画像が集まりました。

処理系の準備

解析に使うのはもちろんPython。なぜなら彼もまた特別な存在だからです。

Anacondaをインストールすればだいたい必要なもの(numpyとかpandasとか)はインストールされます。

あとは追加で画像処理ライブラリのpillow(PIL)をインストールしましょう。コマンドプロンプトから以下をタイプ。

easy_install pillow

手法

教師ありの画像認識的な分類(食べ物の画像かどうか)の例はいくつかあったのですが、教師なしで応募案の画像をクラスタリングする、という感じの例はありませんでした。

なので、画像を読み込んで特徴ベクトル化するところまでは教師あり分類の例にしたがって、特徴ベクトル群をk-meansでクラスタリングしてみます。

参考にしたのは以下。

まず、1715個の画像を読み込んで、150×150のRGB値のデータをRandomized PCAで2次元まで減らしてプロットしてみます。

pca_feature

 

あんまりうまいこと分かれている感じはしませんが、まあそんなもんでしょう。

では、今度は20次元にしたデータをscikit-learnのk-meansで8つのクラスタに分けてみます。

クラス0: 216案, ファイナリスト1(GH-76091181)

0

青空の遠景にひっそりとたたずむ画像がほとんどです。あっ、コンペ要旨の割と最初らへんに出てくる画像ですねコレ。堂々とした正攻法が好印象です。

ここに属するファイナリストはGH-76091181。色にこだわりを感じます。

76091181

クラス1: 181案, ファイナリスト0

1

光と影のコントラストを強調したパースが多いです。この建築のここがいいっしょ!?という強い意志とフェティシズムを感じます。しかし周辺との関係はわかりづらいかも。

クラス2: 284案, ファイナリスト1(GH-121371443)

2

 

全体的に薄暗く、描線の多い絵 or 雲の存在感のあるマジックアワー、といったところ。雲の画像の方はコンペ要旨の最後らへんの画像です。

ファイナリストはGH-121371443。美しいです。
121371443

クラス3: 186案, ファイナリスト0

3

クラス2よりもさらに時間は進み、夜との境目~夜。個人的にはこれぐらいの時刻の写真が好きです。

クラス4: 250案, ファイナリスト3(GH-04380895, GH-1128435973, GH-5631681770)

4

 

全体的に低めな彩度や霞のかかった画像。エッジの効いた彫刻のような存在感ではなく、曖昧な視界にヌッと現れる不気味な存在感、という感じ。

ファイナリストはGH-04380895, GH-1128435973, GH-5631681770と3つも属しています。

04380895 5631681770 1128435973

 

 

 

 

 

カッチリした形で勝負するのでないところが、ヘルシンキという文脈に合うのかもしれません。

クラス5: 271案, ファイナリスト0

5

 

さらに白い。白いが昼間の画像が多い感じです。ヘルシンキの天気を考慮したもののその文脈での美しさを見つけられなかった、という感じでしょうか。

クラス6: 210案, ファイナリスト1(GH-5059206475)

6

白すぎだろ!空が無いものだったり背景が白の図面などが属します。

ファイナリストはGH-5059206475

5059206475

 

うーん白い。白いっていうかなんだこれは。

クラス7: 117案, ファイナリスト0

7

上下余白のある画像です。そういうの分類しにくいからマジ勘弁です。

ここまででクラス4が強いことが分かりました。彩度は低めで、かと言って低すぎず、明度も高すぎず、そしてなにより光っていること。超大事なようです。

ではクラス4をさらに3つぐらいに分割してみましょう。

クラス4-0: 76案, ファイナリスト1(GH-5631681770)

4-0

クラス4-1: 59案, ファイナリスト0

4-1

クラス4-2: 115案, ファイナリスト2(GH-04380895, GH-1128435973)

4-2

やはり霧か…

僕の持論として、美しい画像 = 一つに統一された傾向を持っている画像という法則があるのですが、霧の画像というのは、全体に一つのフィルタをかけるようなものなので統一感が出しやすいのですね。他に統一感のある画像としては、夕焼け(色相)、落ち葉(ベクトル)、木漏れ日(色相、コントラスト)などがありますね。

とにかくここで結論として言えることは、ヘルシンキのコンペでは

  • 霧を使うこと
  • 建築を光らせること
  • 暗すぎてもだめ

という部分を守ると当選確率が上がるかも、という結果論です。これで誰か僕を建築批評シンポジウムか何かに呼んでくれるでしょう(ワクワク)。

一応最後にコードを載せておきます。


#-*- encoding: utf-8 -*-

from PIL import Image
import numpy as np
import os
import pandas as pd
import pylab as plt
import shutil
from sklearn.decomposition import RandomizedPCA
from sklearn.cluster import KMeans


STANDARD_SIZE = (150, 150)


def img_to_matrix(filename, verbose=False):
    """
    load img file as a np.ndarray
    """

    img = Image.open(filename)
    if verbose:
        print('changing size from %s to %s' % (str(img.size), str(STANDARD_SIZE)))
    img = img.resize(STANDARD_SIZE)
    imgArray = np.asarray(img)

    return imgArray

def flatten_image(img):
    """
    takes in an (m, n) numpy array and flattens it
    into an array of shape (1, m * n)
    """

    s = img.shape[0] * img.shape[1] * img.shape[2]
    img_wide = img.reshape(1, s)
    return img_wide[0]

def plot_in_2_dimensions(data):
    """
    plot in 2 dimensions. (Reshape by RandomizedPCA)
    """

    pca = RandomizedPCA(n_components=2)
    X = pca.fit_transform(data)

    df = pd.DataFrame({"x": X[:, 0], "y": X[:, 1], "name": images})
    colors = ['red', 'yellow']
    plt.scatter(df['x'], df['y'], c=colors[0], label=images)

    # labels
    # for image, x, y in zip(images, X[:,0], X[:,1]):
    #     plt.annotate(image, xy = (x, y), xytext = (-20, 20),
    #     textcoords = 'offset points', ha = 'right', va = 'bottom',
    #     bbox = dict(boxstyle = 'round,pad=0.5', fc = 'black', alpha = 0.5))

    plt.savefig('pca_feature.png')
    plt.show()

def main():
    img_dir = 'images/'
    images = [img_dir + f for f in os.listdir(img_dir)]

    data = []
    for image in images:
        img = img_to_matrix(image)
        img = flatten_image(img)
        data.append(img)

    data = np.array(data)

    # plot_in_2_dimensions(data)

    # k-means clustering
    pca = RandomizedPCA(n_components=20)
    X = pca.fit_transform(data)
    km = KMeans(n_clusters=3)
    labels = km.fit_predict(X)


    for i, image in enumerate(images):
        print(image[7:] + "," + str(labels[i]))
        shutil.copyfile(image, "clustered/" + str(labels[i]) + image[6:])



if __name__ == '__main__':
    main()

 

関連記事

自作PCにubuntuでdeep learning用の環境とかを整える

1755 MHz OCのRTX 2080 TiタワーPC(32GBメモリ)を28万円で作った話

Razer Bladeのバッテリーをaliexpressで買って交換した

blenderでオブジェクトのキーフレームを途中からごっそり入れ替える

MiSTEL BAROCCO MD650Lの持ち運びにはあのケースがぴったり

高層を拒否し、「ニュー オールドタウン」を選択したフランクフルトの市民参加型都市計画

コメント

コメントを返信する

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です