推しを数式化するアレ

#はじめに
この記事は~~Tokyo Submerged University~~[Tokyo City University Advent Calendar 2019](https://adventar.org/calendars/4282) 24日目の記事です.

Qiitaはどんなクソを垂れ流しても"それっぽく"見えるようになると聞いたので, 今回はこのプラットフォームを利用させてもらいました.
まるでトイレを題材にした現代アートですね.
#推しを数式で表すとは
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">まつりちゃんを数式で表した夏色まつり近似曲線を作ったぞ＼＼\\٩( &#39;ω&#39; )و //／／<a href="https://twitter.com/hashtag/%E7%A5%AD%E7%B5%B5?src=hash&amp;ref_src=twsrc%5Etfw">#祭絵</a> <a href="https://t.co/1zXN8HLdzc">pic.twitter.com/1zXN8HLdzc</a></p>&mdash; Nulldehyde (@mt_1682) <a href="https://twitter.com/mt_1682/status/1078138502078902272?ref_src=twsrc%5Etfw">December 27, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
こう言うことですね.
因みにこの子は元気いっぱい! チア部女子高生の夏色まつりちゃんです.


#方法
抽出した輪郭線をフーリエ級数展開で数式化します.
今回はTCUバードの翼くんを数式の世界に堕としていきます.
<img width=300 alt="TCU_Bird_0 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/4631a289-8640-ded7-0805-ea825fe23ffc.png">
Q. そもそもフーリエ級数展開ってなんぞ?
A. 「スゥ...ジョゼフフーリエです...」
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">フーリエ級数展開 <a href="https://t.co/nz101gwKqQ">pic.twitter.com/nz101gwKqQ</a></p>&mdash; Nulldehyde (@mt_1682) <a href="https://twitter.com/mt_1682/status/1128356841560260608?ref_src=twsrc%5Etfw">May 14, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
これはフーリエ級数展開のイメージで, 感覚としては「時間と大きさ」の世界を「周波数と大きさ」の世界に変換する感じです.
別の言い方をすると周期的な複雑な波を正弦波の組み合わせに分解することです.
詳しくはggってね.

### 手順
大まかな手順は
>1. 画像のエッジ検出
2. 検出したエッジを良い感じに加工
3. 作成した線をフーリエ級数展開

となります.

コードは下の方にまとめてあるので, 記事中では基本的に日本語での説明のみになります.
#####1. 画像のエッジ検出
pythonとOpenCVで輪郭を検出しようとするとこうなります

```
import cv2

input = "TCU_Bird_0.png"
mid = "gray.png"
output = "edge.png"

graying = cv2.imread(input, cv2.IMREAD_GRAYSCALE)
detedge = cv2.Canny(graying, 50, 110)

cv2.imwrite(output, detedge)
```
<img width=300 alt="edge.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/575919d6-6f80-e367-5109-6eb7fa54d232.png">
良い感じに抽出されてます.


因みにWolframに魂を売るとMathematicaの守護霊を憑けてもらうことができ, 画像読み込み, エッジ検出がこれだけで済むようになります. 便利ですね.

```
img = Import["画像ファイルのアドレス"]
EdgeDetect[img]
```
<img width=300 alt="piy.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/6dd52b48-99ba-320d-fa82-6cf2525bd24e.png">
これでめでたく画像中のエッジを抽出できるようになり, 「推しを数式化する」という我々の野望に一歩近づいたわけです.
主はWolfram信者なので以降はMathematicaで話を進めて行きます.
#####2. 検出したエッジを良い感じに加工する

MathematicaのEdgeDetect関数では例によって例の如く輪郭を線ではなく点のリストとして出力しているので, フーリエ級数展開するためにも何とかしてその点郡を線として扱いたいわけです.
そこで適当な点を始点として, 近い点同士を繋いでEdgeDetectで検出した輪郭の点郡をいくつかの線に分割し, 1本の線として組み合わせた点郡をそれぞれ線として扱えるようにグループ化します.
...
...
文字に起こしてもよくわからないので図を用意しました.
<img width=600 alt="図1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/01021399-03e7-8d0a-1ed9-dbff2310de95.png">
なんとなく理解できれば十分です.

これは実際に点郡を線化した上で, 各線を色別に表示した画像です.
<img width=300 alt="g1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/50dd5b89-1d52-259a-889e-053a1a6b43db.png">
輪郭検出しただけの白黒画像と見比べると, ある程度離れている部分は別の線として扱われているのがわかるかと思います.
ただ, このままでは「線として見做せるようにした点郡」に過ぎないので, Bスプライン曲線を作成して関数化してやります.
<img width=300 alt="g2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/b49a568c-22ae-d8ce-80a9-8f870046de21.png">
これでフーリエ級数展開を行う準備は全て整いました.

#####3. 作成した線をフーリエ級数展開
曲線が閉じているか開いているかをちょいちょい気にしながらフーリエ係数を求めます.

Q. なぜ開閉を気にするのか?
A. ギブスの現象を回避するため.

WolframのHPにいい感じのプログラムがあったので借りてきました.
まずこれは閉じた曲線
<img width="300" alt="iii1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/0602cfd1-3ad1-8bd2-0ed7-6c32e5d8fa88.png">
綺麗に星形を再現できてます.
次に閉じてない曲線
<img width="300" alt="iii2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/cf8de4c1-a31b-3a09-f773-7f164d23a9af.png">
第3, 4象限でｳﾆｮｳﾆｮしてるのがわかります.
このｳﾆｮｳﾆｮがいわゆるギブスの現象と言われる物で, 余計な成分を持ってくるので排除したかったのです.

あとは適当に数式を表示させれば終了です.
これが推しを堕とし込んだ数式ですね.

<details><summary>結果</summary><div>

```
{{-((1759 cos(t))/6)+13/19 cos(2 t)+<<31>>+2311/210 sin(12 t)+11437/25,<<1>>},
{(606 cos(t))/19+3388/23 cos(2 t)+<<32>>+17701/29,<<1>>},
{-((892 cos(t))/19)-184/25 cos(2 t)+<<32>>+29/21 sin(12 t)+9881/26,-(<<1>>/18)+<<36>>},
{-((418 cos(t))/25)+<<35>>+11/19 sin(12 t)+6856/21,-((17 <<1>>)/13)+<<34>>},
{-((45 cos(t))/16)+973/19 cos(2 t)+617/88 cos(3 t)+<<30>>+4537/17,<<1>>/19+<<42>>},
{(110 cos(t))/21+<<31>>+17/50 sin(12 t)+15526/31,<<1>>},
{-((883 cos(t))/19)+173/16 cos(2 t)-226/23 cos(3 t)+<<30>>+2391/7,(57<<1>><<1>>)/25+<<37>>},
{-((1751 cos(t))/44)+<<34>>+31/22 sin(12 t)+1818/11,<<1>>},
{-((1288 cos(t))/25)-33/32 cos(2 t)+<<30>>+11/13 sin(12 t)+2638/17,<<1>>},
{-((2204 cos(t))/23)+15/14 cos(2 t)-210/29 cos(3 t)+<<29>>+3839/10,<<1>>/15+<<36>>},
{(886 cos(t))/29+145/17 cos(2 t)-66/17 cos(3 t)+<<32>>+415,-((76 <<1>>)/11)+<<39>>},
{(823 cos(t))/22-25/38 cos(2 t)-129/22 cos(3 t)+<<32>>+15608/29,<<1>>/13+<<38>>},
{-((409 cos(t))/27)-71/19 cos(2 t)+<<35>>+7/29 sin(12 t)+7191/14,<<1>>},
{(64 cos(t))/23+434/19 cos(2 t)+<<30>>+3/25 sin(12 t)+6869/21,-(<<1>>/19)+<<37>>},
{(1395 cos(t))/82+400/39 cos(2 t)+<<32>>+4549/18,<<1>>},
{-((168 cos(t))/169)+1/6 cos(2 t)+<<32>>+5/11 sin(12 t)+10300/23,<<1>>/63+<<36>>},
{-((227 cos(t))/18)+9/13 cos(2 t)+<<37>>+7663/20,<<1>>},
{<<42>>+8807/23,<<1>>},
{<<38>>+2351/19,(466 cos(t))/15+59/22 cos(2 t)+<<33>>},
{-((82 cos(t))/11)-51/23 cos(2 t)+<<31>>+8/39 sin(12 t)+6131/16,<<1>>},
{(608 cos(t))/15-17/27 cos(2 t)+13/3 cos(3 t)+<<23>>+5/24 sin(12 t)+5962/13,<<1>>},
{-((13 cos(t))/40)+44/35 cos(2 t)+<<29>>+14523/28,(1028 <<1>>)/27+<<28>>},
{<<41>>+6613/21,<<1>>},
{-((29 cos(t))/10)-9/22 cos(2 t)+<<30>>+1/20 sin(12 t)+7013/15,<<1>>},
{-((18 cos(t))/7)+<<33>>+1/49 sin(12 t)+5077/17,<<1>>},
{-((297 cos(t))/14)+<<31>>+1/33 sin(12 t)+2141/9,<<1>>},
{(109 cos(t))/12+<<37>>+9157/15,(64 cos(t))/17-8/17 cos(2 t)+<<37>>},
{(238 cos(t))/13+cos(2 t)+48/25 cos(3 t)+<<21>>+1/25 sin(12 t)+8419/21,<<1>>},
{(113 cos(t))/16+5/23 cos(2 t)+7/20 cos(3 t)+<<35>>+4256/25,<<1>>},
{<<36>>+1/23 sin(12 t)+20256/25,(248 cos(t))/21-1/17 cos(2 t)+<<29>>},
{-((69 cos(t))/16)+<<32>>+1/180 sin(12 t)+2703/7,-((39<<1>>cos<<1>><<1>>))/20)+<<32>>}}
```
</div></details>


また, 近似する次数を変化させていくと図形がどのように変化していくのか, を見るためにManipulate関数を利用してこんな感じのアニメーションを作成できます.
<img width=300 alt="ezgif.com-optimize.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/31303c52-6fc5-97aa-1d66-0f952cd47fb6.gif">
こうして見るとどのように近似されていくのかがわかって面白いですよね.

まとめたコードは[こっち](https://gist.github.com/mt1682/d5acfd20c1b091c4a8381418b0b5ed61)に上げときます.

晴れて推しを数学の世界に堕とせるようになった我々に最早敵はいません.
法律の範囲内で楽しみましょう.
#終わりに
最近はラズベリーパイのraspbianに無料のmathematicaが搭載されているのでそれを使うのもアリですね.
ラズベリーパイの性能的に相当時間はかかりそうですが, 並列計算でなんとかなるかな?
と思って, ラズパイ6枚でクラスタ組んで適当に並列計算させたのですが, よくわかりませんでした.
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">完成した <a href="https://t.co/AprBp4CBqP">pic.twitter.com/AprBp4CBqP</a></p>&mdash; Nulldehyde (@mt_1682) <a href="https://twitter.com/mt_1682/status/1078204630792728577?ref_src=twsrc%5Etfw">December 27, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
そもそもラズパイ6枚でクラスタを組むお金があったら, 学生版のMathematica買った方が安いですね.
実機はコストがアレだけど仮想環境にraspbianをインストールすればもしかしたら...?
とやってみました.
><img width=300 alt="EMW1ycfVUAAtPd1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542863/5fdc8965-7f25-4056-bff3-fb780b7ef6b1.jpeg">
「_ちくしょう! お前はいつもそうだ. このエラーはまるでお前の人生そのものだ. 何かやるにしても基本のシステムがぶっ壊れてやがる. 誰もお前を愛さない_」

よくよく考えてみれば今年の5月にWolfram Engineが無料公開されてましたね.
[「Mathematica」や“Wolfram|Alpha”の核「Wolfram Engine」が開発者向けに無償開放](https://forest.watch.impress.co.jp/docs/news/1186454.html)
早速Jupyter notebookで弄ってみました.
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おおー<br>Jupyter notebookでWolfram languageが使えてる <a href="https://t.co/Sxtykrw0ep">pic.twitter.com/Sxtykrw0ep</a></p>&mdash; Nulldehyde (@mt_1682) <a href="https://twitter.com/mt_1682/status/1208620719803756544?ref_src=twsrc%5Etfw">December 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">EdgeDetectもちゃんと使えるね <a href="https://t.co/BaM6nfj3CP">pic.twitter.com/BaM6nfj3CP</a></p>&mdash; Nulldehyde (@mt_1682) <a href="https://twitter.com/mt_1682/status/1208623478267502592?ref_src=twsrc%5Etfw">December 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
たのしい.

####アドカレ情報
明日はTokyo City University Advent Calendar 2019最終日, らぴーと氏です.


######参考文献
[wolfram blog](https://blog.wolfram.com/2013/05/17/making-formulas-for-everything-from-pi-to-the-pink-panther-to-sir-isaac-newton/)