HOMEWORKS 2019に出展しました。

会社のブログで書いていましたが、会社のブログで書く必要がないと言われたので、放置状態だったはてなブログに投稿することにしました。

HOMEWORKSって?

f:id:novogrammer:20200111092822g:plain

会場の様子

インスタ部のサイトのHOMEWORKS 2016のページには以下のように書かれている。

 HOMEWORKSは関西を中心に活動する、エンタメとメディアート、電子工作が好きな団体が

 一同に集まり、テクノロジーを使ったインスタレーション作品を発表する展示イベントです。

 

つまり、(デジタルとは限らない)インスタレーション作品を作るのが好きな人たちが、仕事や学業とは別にHOMEWORKとして作品を作り、展示するイベントである。 チームで作る出展者もいれば、河本のように個人で作る出展者もいる。
河本は初回のHOMEWORKS 2016から毎回個人で参加している。

今回のHOMEWORKS2019は、2019年8月31日から9月1日の間、THE BOLY OSAKAというホテルの地下にあるN GALLERYで開催した。
(HOMEWORKS 2016〜2018の会場となったACDC; Galleryが使えなくなった。)

河本の作品

河本は「こねこたくさん」という作品を作った。

親猫から親猫に似た子猫が子猫が次々と生まれます。

子猫は親猫についていきます。

ゴールに着くと世代交代します。

そのままほうっておくと身動きが取れなくなることも?

多頭飼いの崩壊を風刺したものです。

こねこたくさん 0:52から成功例

ソースコード

ソースコードは以下にあり、Python3の実行環境を準備できれば動かせる。 https://github.com/novogrammer/CatGenerator

作品作り

技術の選定

河本が運営しているアンテナ大阪という勉強会のslackで、 「Jetson Nanoがアツい」と話題になった。 モダン AI のパワーを数百万のデバイスへ - NVIDIA Jetson Nano

SNACKS Vol.2で作った作品の消費電力が数ワットであり、同様に省電力でGPGPUをしたくなった。

Jetson Nanoの宣伝文句をそのまま受け取り、「省電力で動くすごいGPUを積んだマシンなのでは?」とHOMEWORKSで使うことにした。 (最終的にスペック不足で使わなかった)

Jetson Nanoきっかけで、ディープラーニングの理論の学習をはじめ、おおまかな仕組みがわかる程度にはなった。 どうせなら定番のプラットフォームを使いたいと思い、keras(内部でtensorflowを使用)を選んだ。

以下のサイトのDCGANプログラムを利用し、猫画像でトレーニングした。 GANについて概念から実装まで ~DCGANによるキルミーベイベー生成~

Generatorが学習していく様子。スロー再生推奨。

レーニング過程はJetson Nanoではメモリ不足で、MacbookProではNVIDIAGPUが載っていないため、tensorflowからGPUを使えない。(CPUで計算することは可能)
そのため、AWSGPUインスタンスを借り、トレーニングすることにした。

p2.xlargeインスタンスは東京リージョンで1.542[USD/時間]、GPUはTesla K80一つ。
Tesla K80とほぼ同スペックのGPUを調べたところGTX 1080だった。
2ヶ月以上計算させるならゲーミングPCを購入した方が安くつく。
p2.xlargeで途中まで計算させてみたところ、Macとの比較で10倍の速度が出たため、24時間もあれば充分であることがわかった。

なるべく正面を向いた猫を選ぶ、写真写りの悪い猫は省くのがポイントだった。
反省点としては子猫が混じって不穏な感じになってるので、思い切って省くべきだった。
200000エポックを実行するのにp2.xlargeで20時間ほどかかった。

これで100次元のパラメータで猫画像を生成できる仕組みができた。
Generatorを走らせるだけであれば、CPUのみ(MacBook Pro)でも50[ms]で実行できる。
この仕組みをキーにして作品を考えていくことにした。

候補に挙がったが使わなかったもの

オブジェクト検出系

レーニング(転移学習)がうまくいくか不安。オブジェクト検出が当たり前になりすぎていていまいち活用例が思い浮かばなかった。

DeepDream系

猫の画像を学習させることで猫だらけの画像を生成できないかと思ったが、転移学習ではimagenetの絵にしかならないし、一枚の画像を加工するのに1分ほどかかることがわかり、インスタレーション作品としては現実的ではないことがわかった。

表示部分

猫画像を利用するにあたって、
表示部分をどうするか検討した。
バリエーションを見せたかったため、猫がたくさん出てくることを先に決めた。

2Dの場合、まじまじと見られてしまうので、単純に顔画像を並べるだけだと味気ないが、フィルターをかけてしまっては良さが死んでしまう。
3Dの場合、顔画像からリアルな体のテクスチャを再現するのは難しいが、マインクラフトのキューブのような造形であれば、での3D表現を受け入れやすい。
3Dでたくさんの猫がいることを表現するために、積み上げる表現を考えた。
あることを表現するには物理エンジンが必須だと思った。
直方体をベースとすることで、物理エンジンでも直方体で近似しやすくなり、物理計算の計算コストも下がる。

当初は描画部分もPythonで統一しようと色々なライブラリを調べたが、three.jsと比べても見劣りするものしかなかった。
Jetson NanoはSD CardイメージがUbuntuで提供されているため、ChromiumChromeオープンソース版)が動く、WebGLでの表現は案件でも実績があり、比較的得意なため、ハマりどころの少なそうなthree.jsを使うことにした。

three.jsのexampleでもammo.jsが使われていたため、同じバージョンのwasm版を使うことにした。
ammo.jsはBullet physics engineをemscriptenによりJavaScriptに変換したもの。
ソースの可読性は皆無で、デバッグもしづらく、C++の元ソースを読んだり、ammo.jsの利用例から学ぶしかなかった。
元のソースがC++なため、明示的なリソース管理が必要になるのはつらかった、今作品もメモリリークのバグを取りきれず5時間放置するとemscripten管理のヒープ領域が枯渇する。

造形はBoxGeometryを並べることで作った。前面以外のUV値は端の値を引き延ばしている。(実際の猫にはない縞模様だが、妥協した)

 

f:id:novogrammer:20191028193554j:plain

正面以外は縞模様

そのままでは1体あたりのドローコールがMeshの数と同じ9回(足4、胴体1、尻尾1、頭1、耳2)になってしまう。 処理時間はドローコール数に比例するため、1体あたりドローコールが1回で済むようにGeometryをマージし、各BoxGeometryに相当する箇所でWeightを分け、SkinnedMeshで同じ動きを再現している。

それでもJetson Nanoで子猫を100体出すと処理落ちするためMacBook Proで動かすことにした。

プログラムの連携

Three.jsを使うことを決めたため、Webサーバを立てることも決まり、
PythonのflaskでWebAPIを作った。
flaskはマルチスレッドモデルなのでtensorflowのGraphをスレッド間で共有する必要がある。

以下、API定義、1パラメータあたり8bitの十六進文字列で定義した。

 

def cat(noise_string):
    response=make_response()
    #中略
    return response

 

アートとして

HOMEWORKS 2019は(インスタ部として)大阪市から平成31年度の上半期の一般助成を交付されることになった。 平成31年度上期審査結果

そのため、hoehoeさんから河本の課題として前回よりもアートに寄せることを要求された。

河本の理解としてアートとは「伝えたいメッセージを表現するもの」だった。

河本が猫関連の伝えたいメッセージに「多頭飼いの崩壊を防ぐために、事情がない限り、飼い猫は去勢すべき」がある。
去勢しない場合、望まれない猫が生まれ、殺処分に繋がる。
崩壊した多頭飼い環境から猫を保護する活動をしているNPOもあり、河本家の猫も保護猫を譲り受けたもの。
多頭飼育崩壊 - Wikipedia

ちなみに野良猫の場合は、地域の猫ボランティアにより去勢され、地域猫(耳カットが目印)としてその世代のみ野良猫生活を送る。適切に餌やトイレの管理を行うことで、地域住民との折り合いをつけ、殺処分を避けている。
地域猫 - Wikipedia

f:id:novogrammer:20190413165739j:plain

中津の地域猫

この時点では「猫がどんどん生まれ、ユーザーが飼う猫を選び、選ばれない猫は死ぬ」ということを直接的に表現しようと考えていた。
ただ、猫好きとしてはフィクションだとしてもその光景を見るのが辛いのでどうにかならないかとも考えていた。

そんな時、愛知で行われたアートの展示の賛否両論あるニュースがあり、河本は不快と感じたため、反面教師にすることにした。
伝えたいことでも、不快な感情を与える、「猫を殺す」ような表現は避けようと思った。
(やばいやつと認識されたら話は聞いてもらえない、そんな作品は子連れにも見せたくないし作っていても滅入りそう)

多頭飼いの崩壊のバッドエンドを殺処分ではなく、その手前の子猫が増えすぎて困っている状態でとどめておくことにした。

成功条件は目的地へ向かう、わかりやすい障害物としての迷路、定期的に子猫が生まれるためタイムオーバーの目安にもなる。
子猫が増えすぎ、生まれる子猫が親猫と干渉し、時間が経つにつれて目的地へと進みづらくなることが、多頭飼いの崩壊の比喩表現にもなっていて、仕様とメッセージが繋がった。

タイムオーバーを入れることで、だれもプレイしていない時でも動きがあるものにもなった。
子猫がたくさん生まれるので作品名は「こねこたくさん」とした。

3Dの猫を目的地に向かわせる場合、お尻を向けることになり、顔が全く見えなくなってしまった。
インジケーターとして2D猫の顔を復活させることにした。

まとめというか感想

「アートとは」をひたすら考え、悩み続けた。

使ってみたい技術と表現をうまく結びつけることができてよかった。

Arduinoの「error: variable or field 'foo' declared void 」は#includeをまとめて書くと直るかも

Arduinoで別々に書いたスケッチを合わせる時、

単純に#includeとか変数をコピるとエラーになることがある。

確認したのはArduino 1.0.5と1.0.6

 

一番単純な再現コード(/**/が自動挿入される部分)

Arduino IDE inserts forward declaration.ino

 

最初の変数定義の直前に#include "Arduino.h"や関数の前方宣言を挿入するようですが、

includeの順番によっては関数の引数で使っている型(この場合はTwoWire)がその時点で未定義なので、エラーとなる。

 

#includeをまとめて書くことで、前方宣言に不明な型をなくすことができる。

 

C++のつもりで書いているとハマるので注意。

C++が読める人は「環境設定」から「より詳細な情報を表示する」にチェックを入れておくと、Arduinoが書き出すファイルパスなどが出力されるので、謎のエラーを解決しやすくなります。

 

CodePenも使って見た。

お昼ご飯を食べながら、「カエルのレトロゲーがスマホと相性いいよねー」って話しててCodePenで書いてみた。

See the Pen FROG FROG by Yusuke Kawamoto (@novogrammer) on CodePen.

元ネタは多分このゲーム

シェアしてないのに28もfavられてて(ユーザー数が多いらしい)、他人のふんどしだけど見られてるっていいなと思った。

ついでに画像比較のやつもCodePenに置いてみた。

See the Pen Drop file to create diff image by Yusuke Kawamoto (@novogrammer) on CodePen.

HTML5とJavaScriptで差分画像作成

A側の画像とB側の画像のRGBそれぞれを引き算をしています。

差分画像を作るソフトをImageMagickぐらいしか知らなくて、毎回コマンドを叩くのがめんどくさいので作りました。

 

ブラウザ内で完結するので秘密のファイルでも使って下さい。

 

データの流れ

File API -> img(dataURI) -> canvas -> img(dataURI)

 

FreeNASの壊れたディスクを交換した

基本的に参考サイトの手順通りにいけたんだけど、途中で起動しなくて焦った。

最初はこんな感じだった

# zpool status
〜略〜
        NAME                                            STATE     READ WRITE CKS
UM                                                                              
        zfs-raid                                        DEGRADED     0     0    
 0                                                                              
          raidz2-0                                      DEGRADED     0     0    
 0                                                                              
            gptid/486b3fd1-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0    
 0                                                                              
            gptid/48f23bab-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0    
 0                                                                              
            4170355689092158261                         REMOVED      0     0    
 0  was /dev/gptid/496b3dad-2714-11e2-b0bc-4061860e3c47                         
            gptid/49ea9715-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0    
 0                                                                              
                                                                                
errors: No known data errors      

電源をオフにしてディスクを交換してからFreeNASが立ち上がらなくなった。
起動途中で"Fatal trap 12: page fault while in kernel mode"と表示される。
直前のログはディスクアクセスに失敗した痕跡があった。
FreeNASはUSBブートをしているからハードディスクが壊れただけで起動しなくなるのは変だな と思いつつハードディスクを外すと何事も無く起動した。

多分zfs-raidがマウントされていることを前提にした起動スクリプトが原因なんだろうけど、起動シーケンスをよく知らないので、起動後にSATAケーブルを刺して作業続行

こちらのサイトを参考に作業しました。
FreeNAS 9.1 ディスク交換 | wptest

# zpool import zfs-raid
# gpart list
# gpart create -s gpt ada3
# gpart show ada1
# gpart show ada3
# gpart add -i 1 -t freebsd-swap -b 128 -s 4194304 ada3

# zpool replace zfs-raid 4170355689092158261 gptid/f8baf8ac-2820-11e4-9015-4061860e3c47

replace直後はこんな感じ

# zpool status
〜略〜
	NAME                                              STATE     READ WRITE CKSUM
	zfs-raid                                          DEGRADED     0     0     0
	  raidz2-0                                        DEGRADED     0     0     0
	    gptid/486b3fd1-2714-11e2-b0bc-4061860e3c47    ONLINE       0     0     0
	    gptid/48f23bab-2714-11e2-b0bc-4061860e3c47    ONLINE       0     0     0
	    replacing-2                                   UNAVAIL      0     0     0
	      4170355689092158261                         UNAVAIL      0     0     0  was /dev/gptid/496b3dad-2714-11e2-b0bc-4061860e3c47
	      gptid/f8baf8ac-2820-11e4-9015-4061860e3c47  ONLINE       0     0     0  (resilvering)
	    gptid/49ea9715-2714-11e2-b0bc-4061860e3c47    ONLINE       0     0     0

errors: No known data errors
    

3時間ぐらい待つと同期完了

# zpool status
〜略〜
	NAME                                              STATE     READ WRITE CKSUM
	zfs-raid                                        ONLINE       0     0     0
	  raidz2-0                                      ONLINE       0     0     0
	    gptid/486b3fd1-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0     0
	    gptid/48f23bab-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0     0
	    gptid/f8baf8ac-2820-11e4-9015-4061860e3c47  ONLINE       0     0     0
	    gptid/49ea9715-2714-11e2-b0bc-4061860e3c47  ONLINE       0     0     0

errors: No known data errors

なぜかrebootコマンドもDevice not configuredで帰って来るようになったので、 電源ボタンちょい押しでシャットダウンさせた。(viやscpコマンドも使えなかった!)
起動させると何事も無かったかのようにFreeNASが起動した。 なんか逆に気持ち悪い。

MiddlemanでSass 3.3を使うために頑張ったこと

Sassでこんなことをしたかった。
(変数のスコープを限定しつつrootでkeyframesをinclude)

#Front{
  .anime{
    $image:"sprite-steps.png";
    $count:10;
    $wt:image-width($image);
    $w:$wt/$count;
    $h:image-height($image);
    @at-root{
      @include keyframes(frame-animation){
        0%{
          background-position:0px 0;
        }
        100%{
          background-position:$wt*-1 0;
        }
      }
    }
    background:{
      image:image-url($image);
      position:left top;
      repeat:no-repeat;
    }
    width:$w;
    height:$h;
    &:hover{
      @include animation(frame-animation 1s steps($count) infinite);
    }
  }
}

@at-rootはSass 3.3からの機能だけど、
Middlemanを普通に入れるとCompass 0.12.6が入り、
Compass 0.12.6はSass 3.2.19しか対応していない。

compass | RubyGems.org | your community gem host (0.12.6)

Gemfileで

gem "middleman","~>3.3.3"
gem 'sass','3.3.9'

とsassのバージョンを指定すると怒られた。

Bundler could not find compatible versions for gem "sass":
  In Gemfile:
    middleman (~> 3.3.3) ruby depends on
      compass (>= 0.12.4) ruby depends on
        sass (~> 3.2.17) ruby

    sass (3.3.9)

Compass 1.0.0.alpha.20だと3.3に対応していた。

compass | RubyGems.org | your community gem host (1.0.0.alpha.20)

gem 'compass','~> 1.0.0.alpha.20'

これでうまくいくとおもったら警告が、

DEPRECATION WARNING on line 2 of /Users/???/???/vendor/bundle/ruby/2.1.0/gems/animation-0.1.alpha.3/stylesheets/animation/_shared.scss:
Assigning to global variable "$experimental-support-for-mozilla" by default is deprecated.
In future versions of Sass, this will create a new local variable.
If you want to assign to the global variable, use "$experimental-support-for-mozilla: $moz !global" instead.
Note that this will be incompatible with Sass 3.2.

Compassをアニメーション対応させるanimationが古いみたい。

githubを覗くとpull requestが放置されている

Fix experimental globals for SASS 3.3 and future versions by OlenDavis · Pull Request #18 · ericam/compass-animation · GitHub

Why keep this plugin alive for 3.3, when it has completely moved into Compass?

あれ?マージされてるの?とおもって見たらとっくにマージされてた。

Support for animations · Issue #710 · Compass/compass · GitHub

そこで

gem 'animation'

を消して

@import 'animation';

@import 'compass/css3/animation';

としたら警告が消えた。

pathに半角スペースがあるとbundle install --path vendor/bundleでeventmachineがうまく入らない

↓bundle install --path vendor/bundleしたときのエラー

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

/Users/????/.rbenv/versions/2.1.2/bin/ruby extconf.rb 
/Users/????/.rbenv/versions/2.1.2/bin/ruby: invalid switch in RUBYOPT: -S (RuntimeError)

extconf failed, exit code 1

rvmからrbenvに乗り換えようとしてて、ついでにbundlerもちゃんと使おうとしてたので、何が原因か切り分けるのが大変だった。

結局rvmとかrbenvは関係なくて、プロジェクトのパスに半角スペースが含まれているとエラーになるみたい。

 

 systemrbenvrvm
半角スペースあり × × ×
半角スペースなし

 

やっと解決したと思ったらRubyが2.1.2ではうまくmiddlemanが起動しなかった。

Could not find i18n-0.6.9 in any of the sources
Run `bundle install` to install missing gems.

Rubyを2.1.1に落としたら大丈夫だったっぽい。

欲張らず一つずつ変えて行けば良かったかも。