
- ものづくりやアートが好きな人
- python,ラズベリーパイに興味がある人
- モザイクアートに興味がある人
こんにちは.今日は学外活動としてやっているプロジェクトについて記事を書きたいと思います.
回り回って脳波解析の研究と結びつけられればと思い色んなところに手を伸ばそうとしている一年でありますが,皆さんはいかがお過ごしでしょうか.
そんな私情はさて置き,表題にある「モザイクアート」をご存知でしょうか?
モザイクアートを一言うと,複数のある素材を使って一つの大きな作品を作ること,とでも言っておきましょうか.
対象は何でも構わないと思いますが,今回は「複数のTwitter写真素材を使って一枚の大きな絵を作る」というプロジェクトを実装しました.
それを山口ゆめ花博(公式HP)にて2018.10.24 – 29の6日間,アート作品を展示しましたで報告記事としてまとめておきます.
なお,このプロジェクトはfacebookコミュニティの「山口IoTネットワーク」,山口大学で運営している弊サークル「エンジニアリング交流サロン(@engcomsalon)」の協力のもと,成し遂げることができました.皆さんご協力ありがとうございました.

29日展示最終日の夕方,立ち仕事でかなり疲れましたが良き思い出なり
この記事では,モザイクアートのコア技術や現場での活動内容などを中心にまとめていきます.
モザイクアートのコア技術
モザイクアートの概要紹介動画
全体的な流れの説明は以下Youtube動画(無音)に集約してます.エンジニアリング交流サロンのチャンネルより抜粋です.
1分少々で見れるのでぜひご覧ください.
山口大学で主催されているものづくり講座「テクノロジー×アート2018」の10分間用プレゼンテーション資料としてまとめたものを公開してます.
技術系の方ならば気になると思うシステム実装部分をざっくりと紹介していきますが,対して興味がない方はすっ飛ばしてください.
技術的な話のあれこれ
先のYoutube動画の20秒〜,実際にモザイクアート生成が確認できると思いますが,これはPythonで処理をしてます.
このモザイク画ができるまでの道のりはこんな感じです.
- Twitterより素材写真の収集
- 最終的に絵にしたい画像を決めておく
- どのくらいの解像度するかを決めてモザイク画を作る
以上です.簡単です.
全ての処理はPythonで行ってます.
「道のり1.のTwitterより素材の写真の収集」に関しては特定のハッシュタグ#を追いかけます.
今回は,#山口ゆめ花博,#ゆめ花博,のキーワードに関して,博覧会主催運営より使用許可をいただいているので,そちらのハッシュタグに紐づけられた写真ファイルを取得しました.
使用したパソコンは「ラズベリーパイ」です.

ただ,モザイク生成時のアルゴリズムとしては,コスト計算とモザイク素材の数の不足が開発当初懸念されたために,以下のようなカラーフィルターを通してます.
カラーフィルターの掛け方は,目標値のRGBを参照し,Twitterから取って来た画像に対して,全体的なギャップが小さくするように色合いを寄せます.
こうすることにより,開発当初懸念していた材料不足問題とモザイクが生成時間の短縮を図っています.
また,モザイク画像が生成されていく過程を動画として確認することができていると思いますが,あれもこのようなアルゴリズムだからことリアルタイムに成し得ることができる技です.

Twitterから画像を収集するプログラムに関して
この辺は,ググればすぐにそれなりの動くコードは出て来ますが,TwitterAPIを使う時に現在だと凄く苦労します.
かつて,TwitterAPIで遊んでいたので,おいら達は昔のトークンキーやアクセスキーを使用することが出来ました.
しかし実は,心機一転を兼ねてもう一度APIを取得しようとして申請しました.落ちました笑
申請結果は1〜2週間ほどできたような気がします.

TwitterのApplication Managementに「ついっと」がありますが,こちらを使います.
もう一度言いますがAPIキーの取得は今では凄く難しいです.
どなたかの参考になればと思うので,一様申請までのプロセスをざっくり書いておきます.

左側に書かれてあるSTATUS欄を全て埋めます.Google翻訳使うか,Chromeで日本語表記で進めるか,ご自身の英語能力で突き進むか,頑張ってください笑
その中でも「Use case details」が厄介です,英語で詳細な説明が求められます.今回のTwitter画像をしたモザイク画の生成に関しては,以下のように書きました.
The purpose of this product is to make a mosaic art by utilizing unique pictures from twitter timeline. the unique pictures of this product means identified one by a hashtag which was determined by our community in advance.
This product deliver to our users excitement because a mosaic art is created by user’s pictures. all of users can enjoy through creating a mosaic art.
We do not analyze users tweets, users and their contents. we just collect their identified pictures.
The picture of twitter data display a part of mosaic art. that is to say, many user’s small pictures make one large picture.
Users can see a large mosaic art displayed on other web page and may well be able to see your tweeted picture when you zoom in on the large picture.
が,落ちました.
目的とか,ユーザーに与える価値とか,ツイートをどのように解析するか書けとか言われており,そのように書いたつもりでしたが書けていなかったようです.
ケーススタディとして参考になれば幸いです.
モザイクアート生成プログラムに関して
こちらもpythonの3系にて動くと思います.
プログラムはECSメンバーtomitomi1021のgithubにて公開中ですが,こちらにも参考までに書いておきます.
目標画像(mokuhyou.jpg)に対して,動画ファイル(movie.mp4)とモザイク画像(output.jpg)が作られます.
# coding:utf-8
from PIL import Image,ImageFilter
import subprocess
import os
import random
import math
import time
import cv2 as cv
import numpy as np
#==============設定===================
#素材画像の入っているフォルダ
SourceDir=os.getcwd()+"/sozai/"
#目標となる画像
GoalImage=os.getcwd()+"/mokuhyou.jpg"
#出力の名前
OutputImage="output.jpg"
#動画.mp4.avi
OutputMovie="movie.mp4"
#動画のフレームレート
Framerate=25
#動画フォーマット.mp4v.DIV3.XVID
fourcc="mp4v"
#フレームスキップ
FrameSkip=100
#素材画像の色の平均値を出す時に色を取得する間隔
PickInterval=(1,1)
#素材の画像のサイズ(読み込み時にここで指定したサイズに変換される)
SourceImageSize=(100,100)
#x,yそれぞれ何個の画像を並べるか
TileCount=(300,300)
#ターゲット画像の倍率
TargetZoom=3
#=====================================
def ColorAvg(img,interval=(1,1)): #pythonのループ使用
#画像の色の平均値を求める
s=(0,0,0)
n=0
for y in range(0,img.size[1],interval[1]):
for x in range(0,img.size[0],interval[0]):
c=img.getpixel((x,y))
s=tuple(i+k for i,k in zip(s,c))
n=n+1
s=tuple(math.floor(i/n) for i in s)
return s
def ColorChange(img,col,Avg):
#imgの色の平均値がcolになるようにフィルターに掛ける
#元画像の平均値を必要とする
ds=tuple(i-k for i,k in zip(col,Avg))
r,g,b=img.split()[0:3]
r=r.point(lambda x: x+ds[0] if 0 <= x+ds[0] and x+ds[0] < 256 else 0 if x+ds[0] < 0 else 255)
g=g.point(lambda x: x+ds[1] if 0 <= x+ds[1] and x+ds[1] < 256 else 0 if x+ds[1] < 0 else 255)
b=b.point(lambda x: x+ds[2] if 0 <= x+ds[2] and x+ds[2] < 256 else 0 if x+ds[2] < 0 else 255)
return Image.merge("RGB",(r,g,b))
def Mosaic(img,src,video=None,count=(100,100),interval=(10,10),FrameSkip=0):
#モザイクアートを生成する
#img:元画像
#dst:動画の保存先
#src:モザイクアートに使う画像群
#count:x方向,y方向にそれぞれ何個の画像を使うか
#split:画像の色平均を取る時に色を取得する間隔
Target=Image.new("RGB",img.size)
print("%s:各素材グラフィックの色平均値を導出開始"%(str(time.time()-startTime)))
SourceImg=[(i,ColorAvg(i,interval)) for i in src]
SourceImgTemp=[]
print("%s:各素材グラフィックの色平均値を導出完了"%(str(time.time()-startTime)))
print("%s:タイルリストを生成開始"%(str(time.time()-startTime)))
RectList=[]
for y in range(0,count[1]):
for x in range(0,count[0]):
tileRect=(x*img.size[0]/count[0],y*img.size[1]/count[1],(x+1)*img.size[0]/count[0],(y+1)*img.size[1]/count[1])
tileRect=tuple(math.floor(i) for i in tileRect)
RectList.append(tileRect)
print("%s:タイルリストを生成完了"%(str(time.time()-startTime)))
print("%s:モザイクアートを生成開始"%(time.time()-startTime))
TileCount=0
Frame=np.array(Target)[:,:,::-1]
video.write(Frame)
while(len(RectList)>0):
r=random.randrange(len(RectList))
tileRect=RectList[r]
del RectList[r]
Pivot=img.getpixel((math.floor((tileRect[0]+tileRect[2])/2),math.floor((tileRect[1]+tileRect[3])/2)))
if(len(SourceImgTemp)==0):
SourceImgTemp=SourceImg.copy()
r=random.randrange(len(SourceImgTemp))
tempSrc=ColorChange(SourceImgTemp[r][0],Pivot,SourceImgTemp[r][1])
del SourceImgTemp[r]
tempSrc=tempSrc.resize((tileRect[2]-tileRect[0],tileRect[3]-tileRect[1]))
Target.paste(tempSrc,(tileRect[0],tileRect[1]))
if(TileCount%(FrameSkip+1)==0):
Frame=np.array(Target)[:,:,::-1]
video.write(Frame)
#cv.imshow("",Frame)
#cv.waitKey(1)
TileCount+=1
Frame=np.array(Target)[:,:,::-1]
video.write(Frame)
print("%s:モザイクアートを生成完了"%(str(time.time()-startTime)))
return Target
if __name__=="__main__":
startTime=time.time()
Res=subprocess.run(["ls",SourceDir],stdout=subprocess.PIPE)
Res=Res.stdout.decode().split("\n")
Res=Res[:len(Res)-1]
src=[Image.open(SourceDir+i).resize(SourceImageSize) for i in Res]
target=Image.open(GoalImage)
target=target.resize(tuple(math.floor(i*TargetZoom) for i in target.size))
fourcc2=cv.VideoWriter_fourcc(*fourcc)
#fourcc2=cv.cv.CV_FOURCC('m','p','4','v')
#fourcc2=cv.VideoWriter_fourcc('m','p','4','v')
v=cv.VideoWriter(OutputMovie,int(fourcc2),Framerate,target.size)
Mosaic(target,src,video=v,interval=PickInterval,count=TileCount,FrameSkip=FrameSkip).save(OutputImage)
v.release()
ラズベリーパイでもmacの環境でもOpenCvが入っていれば動きます.
おいらのmac環境では,OpenCVを入れる際に割と手こずったので,その際は以下の記事を参考にしてみてください.
もしかしたら解決するかもしれません.
(参考:MacにOpenCVを入れてPython3を走らせようと思ったけどかなりのErrorと戦った!)
ゆめ花博当日,現場での活動内容は!?
おいら達は,野外テントにてモザイクアートの展示を行いました.
展示する作品は大きく分けて以下の4点.
- 紙ベースのアート作品集
- デジタルベースのアート作品集
- モザイクアートのリアルタイム作成体験コーナ
- 顔面追従するカメラロボットやレゴ作品,アルディーノで制御された80発LEDおもちゃ
などなどなど.
上記の4は完全に子供達が来た時に,モザイクアート以外で楽しんで貰う用で置いてましたが,
お年寄りばっかりだったのであんまり効果を発揮せず,少子高齢化の世の中を肌で感んじました.
紙ベースのアート作品集

壁一面にA4の紙で貼ったこの絵達,実は全て4万枚の小さいtwitter写真画像を使ったモザイクアートなのです.
それも,会場内で取られた写真のみを使用して作っています.
今回のプロジェクトテーマかつコンセプトの「みんなで咲かせようモザイクアートで大きな花を!」に沿っているのではないでしょうか.
最終日に向かうにつれて,現場最適化が着々と進んで来ており,最終日には壁一面に画像を並べるという状態までたどり着きました.
凄く見やすくて弊サロンの展示ながら綺麗だったと思います.
デジタルベースのアート作品集
デスクトップ型の通常サイズのPC用モニターの他に,実はこの日のためにラズパイ用のタッチモニターを用意しており,
デジタルベースでのモザイクアート展示も試みました.
中高生などスマホ世代にはウケが良かったと思います.
リアルタイムモザイクアート生成
これは,その場で来場者をカメラ撮影し,その撮影された写真をモザイク画で生成するというもの.
モザイク画の生成過程も視覚的に見ることができて,これはかなりウケが良かったと思います.

画像データ自体は,プライバシーの観点から差し上げることが出来ませんでした.
特定のハッシュタグを使っているとはいえ,拡大すると来場者のお顔が写っていたりする画像もちらほらありましたので,,,
いろんなおもちゃ
小さいお子さんも来るかなと想定して,レゴブロックやロボット,LEDなども用意してありました.

キラキラ光る80発LEDのおもちゃ.C#アルディーノでコントローラブルです.
弊サロンで.12月のときわファンタジアで光とテクノロジーを使ったアート展示を実装予定ですが,それの試しに適当に飾ってました.

LEGOで遊ぶ子供とほったらかされるモザイクアート

レゴは,ラズベリーパイをデコレーションするために用意しましたが,子供達の遊び道具になっている模様でした.

弊サロンのメンバーが空き時間で会場で実装した顔面追従カメラ,フレームアウトしなければ,顔面を追い続けます.
また今度ブログにして詳細を書きます.
来場者の反応などなど
6日間アート展示をしましたが,最終日にはなんと350人越えの人たちがアート作品を見てくれました.
1日目は20人でした.成長率17倍以上!笑
展示の仕方や集客の仕方などなどを考え,トライアンドエラーで人に見てもらう工夫をみんなで考えてここまで来ました.
モザイクアートは来場者のうち,中学生や高校生達も文化祭で何人かは経験しているということでしたが,コンピュータを使ったモザイク画の作成には驚いてました.少しでもテクノロジーの力に心を動かされて,将来エンジニアを志すきっかけにでもなれば,作り手冥利につきます.
また,オーバーすぎるくらい驚いてくれる来場者もいたり,そんなに驚かない人もいたり,,,人それぞれ感性というものは違うのだなあと,人間観察してほのぼのしたりしなかったり,,,笑

最後に
アート展示をしているこの6日間,個人的に学会発表を3件こなして死にそうになりましたが,弊サロンのメンバーに助けられました.
圧倒的感謝です.(2日は行けなかったり,2日は途中参加だったりしました,,,)
たちっぱで足痛かったですが,現場の改善改良は凄く経験になりました笑
いろんな方々にお声がけしてもらいました.
広告業をされている方や,自営業をされている方で店舗に導入希望がある方,ミュージシャンのジャケット作成に使いたいなどなど,,,
また,ラジオ局の人も来られて,よければ取材をしてくれるとのことで,今後も話が広がっていきそうです.
今後ともワクテカwktkです.
もし,この記事をご覧になっている方で,お仕事のご協力ができたらご一報くださいませ.という宣伝で締めくくります
(なお,私は学術研究員として国に雇われ&他の仕事が個人的にできないので弊サロンに繋げます^^)
参考機材等々
こちらは,ラズベリーパイ を使って色々遊んで見たい!
という人に向けて参考機材などの紹介をしています.
今回使用したラズベリーパイ(小型PC)やその他機器はハイテクなのに費用が安いのでかなりオススメです.
2018年10月31日現在,最も最新版のラズベリーパイの機種(3B+),9999円で買えるスタータパックです.
これひとつあれば,問題なくパソコンとして動きます.(ただ,キーボード,マウス,モニターは別途必要です.)
プログラミングPythonやスクラッチ(直感型プログラミング),マインクラフト,ブラウジングなどなどできますよ.
IoTシステム開発にいかがでしょう?
ラズベリーパイ と同時に買っておきたい電子工学キット.
これがあれば,とりあえず動くものが作れます.
ラズベリーパイ に標準搭載されているGPIO(電気的信号を外部に伝えるピン)を使って,サーボモータ やLED,圧電ブザー,赤外線モジュール,カメラなどなどその他多数動かすことができます.
やさしくはじめるラズベリー・パイ ~電子工作で簡易ロボット&ガジェットを作ってみよう~
インターネットにラズベリーパイの使い方はかなり溢れかえってますが,じっくり基礎から習得したい方はこれ.
今までラズベリーパイ に関して色んな本を読みましたが,これがビギナー向けの最強本です.
上記の3点があれば,おそらく誰でもラズベリーパイを動かすことができると思います.
ラズベリーパイは普通のパソコンなので,正直WindowパソコンやMacPCを買うよりも経済的で,かつGPIOが付いているので面白いしオススメです.









