1. Introduction
豊田工業高等専門学校 情報工学科 4年の加藤愛斗です。
先日高専プロコンの自由部門において優秀賞を頂きました!!
自分の記録も兼ねて、体験記を執筆します。
2. About
2-1. Project
iMake!という、プロジェクターを用いた、どんな人でもメイクを気軽に楽しめる仮想メイクシステムを作成しました。iMake!では、プロジェクターから顔に映し出された画像を、鏡を通して見ることで、より理想的なメイクを体感できます。
2-2. Members
- 伊藤桃 (2年)
- リーダー
- デザイン
- 伊藤優汰 (2年)
- 開発
- プレゼン
- 佐藤凛 (2年)
- デモ
- 朝倉優衣 (1年)
- デモ
-
加藤愛斗 (4年) [私]
- 開発
3. Techniques
(先に完成したプロジェクトの技術概要を説明したほうが、のちの内容が理解しやすいと考えたので、冒頭で説明します。)
3-1. 画像合成
カメラ画像を取得して、MediaPipeのFaceMeshを用いて顔の特徴点を取得し、その特徴点を元に、ドロネー図を作成します。予め用意してあるメイク画像をドロネー図に合わせて配置し、その画像を合成します。
※実際は、解像度や処理速度などの問題から、もう少し泥臭い努力をしたのですが、簡潔化のために割愛します。
3-2. UI
Vue.jsを用いて記述しており、PythonとのマッピングはEELを使用しています。モダンなフロント技術を用いてUIを作成することにより、きれいなUIを実現できました。
3-3. 色変更
1024x1024のメイク画像を予め用意して、それを重ね合わせて描画しているのですが、各パーツの色を任意に変更したかったです。そのようにすれば、メイク画像を用意する子も一つの形さえ用意するだけで済むし、色も無限に変えられるのでバリエーションも広がります。そこで、下の写真のように青単色でテンプレートを作成し、プログラム側で色を変更する仕様にしました。これは、後輩に担当してもらったのですが、グラジュエーションをきれいに色変更することが難しかったそうです。
4. Beginning
今回僕は、4年生(僕)x1、2年生x3、1年生x1というチーム構成で参加しました。
元々は、同じクラスのメンバーで出場を検討していたのですが、アイデアがなかなか思いつかなかったために断念していました。そのタイミング(5月半ば)で、2年生の後輩から開発担当としてjoinしてくれないかという誘いを受けたため、ありがたく参加させていただきました。
5. Goal
メンバーの中で、ある程度プログラミングの経験がある後輩は1人だけでした。
そこで、賞を取ることを目標にはおかず、後輩育成を目標としました。
cf. 目標設定時のnotion
6. Review
6-1. 5月中旬
joinしました。
まず最初に、私のチームにおける立ち位置を話し合い、明確化しました。後輩育成に目標をおいたので、自分は単なる開発メンター的な立ち位置としました。
次に、発案者からやりたいことの概要をキャッチアップしました。正直なところ、アイデアについて、実現性や有用性については疑問がかなりありましたが、自分は開発メンターという立ち位置なので、その辺は後輩に自由に任せてみることにしました。
6-2. 5月下旬
予選資料の作成が必要でした。
ノンデザイナーズ・デザインブックを貸したり、資料のレビューなどを少ししました。
また、技術構成について記述する必要があったので、どのように実現するか検討しました。
その当時に参考にした記事です。
- 最新のプロジェクションマッピング is ヤベェ。この世の全てがディスプレイ化できちゃう
- FaceForge.pdf
- Facial mapping (landmarks) with Dlib + python
最終的に、gen2-facemeshという今回の実装に似ている部分が大きいリポジトリを見つけたので、それを元に技術概要を作成しました。
基本的には、全てを後輩に丸投げしていました。(頑張ってくれた後輩には感謝しかないです。)
6-3. 7月上旬
予選通過
予選通過のお知らせがありました。
A評価での通過でした。
正直なところ、予選通過する確率は半々くらいだと考えていたので、びっくりしたとともに、かなり重いタスクが増えたなと感じました。(学校祭の副実行委員長を努めており、ちょうど同時期に忙しくなることがわかっていたことと、かつ、開発経験のしっかりある人が自分ひとりで、どのように実装すれば完成するかの見通しが全然立っておらず不安が大きかったことから、少しマイナスに感じてしまいました。)
開発の取り掛かり
技術概要の作成時に使用したリポジトリを少しいじっていました。
そして、mediapipe facemeshというAI Solutionを見つけました。
進捗はほぼ皆無です。
6-4. 7月下旬
テスト期間により、開発はほぼ停止しました。
6-5. 8月上旬
テストが終わり、遊びまくりました。やらなければいけないという気持ち・罪悪感は少しありましたが、遊びは遊びで楽しみました笑。
6-6. 8月中旬
ようやく、開発に取り掛かりました。まずは、gen2-facemeshのコード理解からはじめました。このリポジトリが、私達のやりたいことの大部分を占めていたので、どのようなアルゴリズムとなっているかの把握に努めました。cf. コード読解時のメモ
一週間かけてようやく、コードの流れを理解することができました。
6-7. 8月下旬
夏期インターンのため、開発はほぼ停止しました。
この時点で、1024x1024の画像を顔に貼り付けることで、メイクの投影を実現する方向性が決まっていたので、いろいろなメイク画像の作成を低学年にお願いしました。ここで、各自が作成したメイク画像を自分でGitHubにアップロードしてもらうようにして、1つの目標である、低学年にgitに触れてもらうことを達成しました。
6-8. 9月上旬
1024x1024の画像を顔に合わせて貼り付けることは、gen2-facemeshのおかげでできるようになりました。しかし、今回私達は、透過画像を使用する必要があったのですが、透過画像の処理は一筋縄ではいきませんでした。
6-9. 9月中旬
5つモードが有るのですが、各モードにて使用するclassを作成しました。
cookpadさんのインターンに行っていました。
HackDayに参加してました。
6-10. 9月下旬
UIを作成し始めました。GUIを作成する必要があったのですが、既存のGUIライブラリ(tkinterなど)を使用すると、どうしてもダサいUIになってしまいどうしても嫌だったため、色々調べ、EELというライブラリにたどり着きました。このライブラリは、HTML/JSなどのフロントのコードをPython側から呼び出したり、逆にJSからPythonのコードを呼び出すことが出来るような、Electron-likeなものです。私はフロントが本当に嫌いで、知見も一切なく、どうしてもフロントエンドを書くのが嫌だったのですが、泣きながら書きました。(極限に追い込まれた状態で、フロントをゴリゴリ書いていたら、案外面白さに気づいてしまいました笑)
6-11. 10月上旬
ここからは本当に追い込みました。一日2時間くらいしか寝ずに一生プログラミングしてました。精神もだいぶすり減っていました。冒頭にも書きましたが、学校祭の副実行委員長も務めており、夕方はその仕事があったため、夜の時間を削るしかありませんでした。さらに、UIの部分が本当に難しく、どれだけ時間を割いても完成の兆しが見えなかったため、本当に辛かったです。
そしてようやく何とか、本番数日前に、当初想定していたものを盛り込んだシステムが完成しました。(圧倒的に遅い)
そこから、実機(プロジェクターやJetsonなど)を用いて使用してみたのですが、様々な問題が発生しました。
- Jetsonの処理速度おそすぎ
- そもそもビルドをしないとfacemeshが使用できない
- 4fpsくらいしか出ない
- 照明の問題
急ピッチで、解決策を考え対応しました。
6-12. 行きのバス
まだまだ開発は続きます。微修正に微修正を重ねました。
6-13. 1日目
うまく動くかどうか気が気でないです。うまく動きませんでした。なんで!? カメラの解像度が想定していたものとは違ったことが原因でした。本当に焦った。
6-14. 1日目の夜
顔を動かしたときの追従性に難があったので、徹夜開発です。考え出したコードがなかなかうまくうごかず、ほぼ半ギレ状態で実装していました。ホテルが同室だった後輩にはまじで申し訳ない。最終的にはうまく動きました。
スライドの修正も急ピッチで行いました。
6-15. 2日目
もうコーディング面では出し切ったので、デモの質疑応答で技術面に関する部分を応対して、他の展示などを回ったりしてました。
やはり、オフライン開催は得るものが大きいですね。
6-16. 結果発表
冒頭にも書きましたが、目標は後輩育成で、ある程度この時点で達成できていたので、賞はあまり気にしていませんでした。とはいえ、競技部門で同校のチームが4位で特別賞を受賞し、課題部門の同校チーム(後輩たち)も特別賞を受賞が発表されたときは、やはり賞が欲しくなりました。4年生の私が本気で作成したプロジェクトが何も賞を取れないと恥ずかしいという気持ちが大きかったです。そんな中、光栄にも優秀賞をいただくことが出来、嬉しさ・安堵が舞い降りました。
6-18. まとめ
インターン・学校祭・HackDayなどと並行してやっていて、さらに締め切りドリブンの性格のため、カツカツのプロジェクト開発となりましたが、新たな気づきや後輩との交流ができるとても良い経験となりました。
7. Efforts, Difficulties
7-1. 後輩育成
7-1-1. コードレビュー
開発経験のある2年生が1人いたため、開発はその子と私の2人で行っていました。
コンテストとなると、機能や見た目に注力が置かれ、コードの品質が下がることが一般的だと私は捉えています。しかし、今回私は、企業のプロダクトと同等のコード品質を保持することを目標にしました。後輩は開発経験があるといっても、個人開発のみで、企業などで書かれているクオリティの高いコードは目にしたことがありませんでした。この先、インターンなどで企業のプロダクトに関わる祭に、少しでもプラスになればと思い、質の高いコードの開発を経験してほしかったです。
のように、私のこれまでのインターンでの経験を生かして、ビシバシレビューをしました。文面で伝わりづらいところに関しては、適宜オンラインMTGをしてキャッチアップしました。自分の書いたコードを修正される気持ちは痛いほどわかりますが、技術力向上のために、妥協せずしっかりとしたレビューを行いました。
後輩からのレビューについての感想
実務を通した人からのレビューは具体的でしたし、いろいろな知識が付いて良かったですね、細かいレビューのお陰で個人開発では踏み込めない領域の知識もつきました
コードレビューもなんですけど、commitコメントのfix: とか add: 知れたのもよかったです
少しでも、後輩の技術力を上げることができたのであればとても嬉しいです。そういえば、commitにprefixもつけさせるようにしてました。
7-1-2. Gitの使い方
Gitは難しいと思います。僕もインターンを始めたときはさっぱり分かりませんでした。そのため、当時何がわからなかったのかを思い出しつつ教えることを意識しました。
まず最初に、Gitがある意味・何を目的にしているのか、何が嬉しいのかなどの概念を説明しました。その次に、clone, add, commit, ...と1つずつコマンドを教えていきました。初心者にとって、コマンドはかなり敷居が高いですが、あえてsourcetreeなどのツールを使わずに、コマンドのみで教えました。
Gitは経験あるのみだと考えているので、わかっていない部分も多くあったと思いますが、とりあえず触らせることが出来たので、良かったと感じています。
後輩からのgitについての感想
説明受けての感想は最初リモートとローカルとかの概念がわからなかったり、今のブランチの状態理解するのが大変だったり、コミットとプッシュがわからなかったりとかですかね。そもそも私にとっては初めて触れるものというか概念だったので理解が大変だったり勉強が必要な事が多かったのはありますが、それもまた勉強意欲にもつながったり開発してる感?笑みたいなのがあって楽しかったです。あとGit使ってみてvscode便利だなって思いました。(←git historyや、GUIでadd, commitが出来ることを言っている)
わかったことは…概念とかは理解できたんですけどコンフリクトとかがちわからないのであんまりです…prまでの流れがわかったくらいです。
単純に便利だなって思ったのでほんとちょっとだけGit使うようになりました。
PRまでの流れがつかめてもらえてとても嬉しいです。コンフリクト対応などは難しいので、新しい画像の追加のみをやってもらい、コンフリクトなどが起きる場面を極力少なくしていました。今後コード開発などを通して身につけて欲しいです。
7-1-3. リーダーシップ
自分はこれまで参加したほぼすべてのPJにおいてリーダーを努めてきました。しかし今回は誘われた身だったため、後輩にリーダーを担当してもらいました。そこで、自分が今まで培ったリータースキルの継承も1つの目標としていました。
ディサイダーを決める
議論をしていた際に平行線になりかけていた時があり、どうしても収集がつかなさそうだったため、このような場合は、ディサイダーを決めておいて、その人の独断で決めるのも1つの手段だよということを伝えました。(これは、cookpadのインターンにおいて私が学んだことの1つです。)
チームの方針を統一させる
私はこれまでのリーダー経験を通して、チーム全員が同じ方向性を向いていないと、目標は達成できないし、チームも崩壊することを学びました。そのため、初期の段階で、このチームが目指すところを明確にさせ、それをチームメンバー全員の共通認識として定着させるようにしました。
後輩からのリーダーシップについての感想
技術面以外でも学ぶ部分が沢山ありました。リーダー性とかです。
ディサイダー。実際先輩が決めてくれたりする部分多くて体験して学べた感。とても良かった。
何かを学んでもらえてよかったです。
7-1-4. UI設計
どのようにしたら使いやすく、わかりやすいUIになるかを低学年に考えてもらい、デザインカンプを作成してもらいました。figmaなどを用いたかったのですが、そこまでは踏み出すことが出来ませんでした。
操作を10キーで行うようにしたことも特徴の1つで、10キーとUIを対応させることによりUXを高める試みを図っていました。
7-1-5. スライド
資料作成やデモ・プレゼンは全て後輩に任せていました。二日目が発表だったため、一日目の夜に発表の練習をみんなで集まってしました。しかし、はっきり言って、賞をとることができる発表だとは感じられませんでした。そこで、もう一度チームの目標を再確認したところ、全員の間に「ここまで、これだけ頑張ったから、なにか賞は欲しい」という気持ちが共有されていました。ならば、やることは1つです。かなり厳しいダメ出しをしました。プレゼンターの伊藤優汰くんはかなり心が折れたと思います。でも、あえて厳しく言いました。彼ならば、反骨精神で強くなってくれると信じていたからです。
スライドの修正においては、
-
1スライド1メッセージ
- 余分なスライドがあったため
- 伝えたい主軸
- アイデアを出したときにコンセプトとしていたもの
- 作品制作でこだわったこと
- もし賞を取れなくても後悔の残らない発表となるように
というアドバイスをして、改善してもらいました。最終的にスライドも含めてものすごくよいプレゼンになったと思います。
7-2. コード品質
7-2-1. OOP
ゴリゴリのオブジェクト指向プログラミングをしました。自分的には、ものすごくきれいにディレクトリ・クラスを分けて書くことが出来たと感じています。このコードを後輩に見せることが出来たのもとても大きかったと考えています。
7-2-2. type hints
Pythonの難点である型情報がないことを補うために、type hintsを導入しました。これにより、IDEの補完が効くようになり、開発体験が上がりました。個人開発では中々書かないと思うので、後輩にtype hintsを通して、型情報を付与することの重要性を伝えました。
後輩からの感想
また自分が少し強くなれたのはマンズさん(←僕のあだ名です)のお陰です。PythonのType HintsやGitのPRにおいての作法など、実務を通してしかしれないようなノウハウを2年生という低学年の時に学習することができたのはマンズさんのお陰です。今回のプロコンを通してより、技術に対しての姿勢が積極的になりました。
7-2-3. dataclass
from pydantic import Field
from pydantic.dataclasses import dataclass
@dataclass
class RGB:
r: int = Field(..., ge=0, le=255)
g: int = Field(..., ge=0, le=255)
b: int = Field(..., ge=0, le=255)
def __str__(self) -> str:
return f"RGB({self.r}, {self.g}, {self.b})"
@dataclass
class HSV:
h: float = Field(..., ge=0, le=360)
s: float = Field(..., ge=0, le=100)
v: float = Field(..., ge=0, le=100)
def __str__(self) -> str:
return f"HSV({self.h}, {self.s}%, {self.v}%)"
pydanticのdataclassを用いて、データを正しく保持するようにしました。
7-2-4. pre-commit
pre-commitを導入しました。これにより、コミット前にコードの品質をチェックすることが出来るようになりました。
7-2-5. docstring
def create_effect(self, target_image: np.ndarray, target_landmarks: np.ndarray) -> np.ndarray:
"""Creates effect image that can be rendered into an image the size of
the target image.
Args:
target_image (np.ndarray): target image
target_landmarks (np.ndarray): landmarks on the target image. Must be the same size as the source landmarks.
Returns:
np.ndarray: effect image (BGRA)
"""
if self.effect_image is None:
raise ValueError("Effect image is not set")
関数名では補いきれない情報や、引数の意味をdocstringで補足しました。これにより、DXが上がったと感じています。
7-2-6. マジックナンバー
当たり前のことで、ここに記述するまでもないですが、クラス変数として保持してマジックナンバーを排除しています。
class EyeDiagnosis:
LEFT_EYE_LEFTMOST_IDX: Final = 33
LEFT_EYE_RIGHTMOST_IDX: Final = 133
RIGHT_EYE_LEFTMOST_IDX: Final = 362
RIGHT_EYE_RIGHTMOST_IDX: Final = 263
7-2-7. Readme
しっかり書きました。
7-3. 技術選定
7-3-1. EEL
EELの選択は我ながら天才的だったと思います。UIをモダンなフロント技術を用いて実装できたため、開発効率が高く、開発体験が良かったです。
7-3-2. Python
処理速度的にPythonを選択したことは正解ではなかったかもしれません。しかし、自分たちのスキル的に、Pythonで開発することを決定したのは正解だったと考えています。限りあるassetsの中でPJを回すのがコンテストの醍醐味の1つであると思います。
7-4. 著作権
gen2-facemeshのコードを大きく引用させてもらっているため、著作権については注意が必要でした。そのため、どのように表記すればよいかなどをしっかり調べて適切に表記しました。
7-5. OSS
このプロジェクトはコードを公開しています。上記の著作権的に必然的にそうなっているのですが、OSSにすることによって、いつか誰かの役に立てるかと思います。
コードを公開することの重要さ
私はコードを公開することがものすごく重要だと考えています。おそらく誰も見ないでしょう。その上、自分の汚いコードを公開したくないと感じる人も多いと思います。しかし、公開しましょう。いつかどこかで誰かの役に立つ可能性があるのです。実際、今回のPJ開発の中でEELでVue.jsを使用するときに苦戦したのですが、全く同じことをやっていたリポジトリがあったため、参考にさせていただきました。 また、コードが公開されていることから、少しでも良いコードを書こうという気持ちにもなると思います。私は帰りのバスで後輩に対して、
みんなにこの後やってほしいことが2つある。1つは記事の執筆、もう1つはリポジトリの公開。記事は公開しなくてもいいから、感想などを文書としてどこかに保存しておいて欲しい。きっといつか役に立つから。リポジトリの公開はコードが汚かったりして、したくない人もいると思うけどしましょう。コードが汚いなんて言うのはどうでもいいです。君たちのコードなんて、いつまでたっても、僕からしたら全然汚いです。僕のコードも出来る人からしたら来た2位と思います。公開することに意味があるんです。
と強く伝えました。それくらい私はコードを公開することが大事だと考えています。
7-6. Slack
僕がjoinした当時はTeamsを使用してコミュニケーションをとっていたのですが、自分がSlackが大好きなので、Slackに移行してもらいました。そして、times-
チャンネルを各自作成してもらい、思ったことなどのぼやきをしてもらうようにしました。これにより、チームのコミュニケーションが活発になったり、他人が何をやっているか・詰まっているのかの把握がスムーズになったと感じています。
8. Lesson
8-1. 技術差のあるチーム
技術差に本当に苦しみました。自分は一年間インターンをバリバリやってきたので、ある程度実装力があり、きれいなコードもかけます。しかし、後輩は個人開発レベルの技術力で、はっきり言って、コードはあまりきれいではありませんでした。しかし、後輩育成が目標であったため、しっかりとしたレビューを行いました。その後、修正されたものが上がってくるのですが、それが自分の想定しているものとだいぶ違ったときは、かなり絶望しました。プログラミング力は経験が物を言うので、仕方がないことだと頭の中では理解できていても、かなり辛かったです。このとき、一度自分はどのようにして今あるプログラミング力を身に着けたのかを振り返ってみました。すると、自分もインターン先のCTOに何度もコードレビューをしてもらって、自分のコードをきれいにしていったことを思い出しました。たまたま、インターン先のCEOとご飯をする機会があったため、「CTOはどのようにして技術差に向き合っていたのだろうか」という質問をしてみました。すると、「きっと、加藤くんが、学ぶ意欲・向上心がある子だったから、CTOもしっかりとしたコードレビューを続けられたんじゃないかな。もし、向上心がなかったら、レビューをせずにどんどん巻き取る方針でやっていたと思う。」というような答えをいただきました。これを聞いて、自分はハッとさせられました。後輩はコードを書きたいという向上心があったのにも関わらず、開発期間に追われていたために心に余裕がなくなり、コードレビューをおろそかにしようとしていたからです。向上心がある人に対しては、その気持ちを正しい形で伸ばすのが上に立つ人の役目だと感じました。
似たような悩みを抱えていたのが、鳥羽商船の宝くんです。高専プロコンの会場で、いろいろな話をさせてもらったのですが、このような悩みを抱えているのは自分だけじゃないんだとどこか安心しました。このような会話が生まれるため、オフライン開催は本当に重要であると思います。
8-2. フロントエンド
自分は今までフロントエンドを毛嫌いしてきたのですが、どうしてもやらざるを得ない状況になったため泣きながらやりました。いざやってみると、最初は本当に何もわからなかったため、ただひたすら辛かったのですが、徐々に慣れ始めると楽しさを少し実感しました。エンジニアにとって、好きな分野をとことんやることは最も成長できる道であるため良いことだと考えていますが、知見を広げるために好きでない分野にも、一歩踏み出してみることが重要だと感じました。
8-3. 分業
今まで自分が開発してきたPJはアイデアが自分のものであったり、ドメイン知識をもっていたりと、自分ひとりで開発をすすめることが出来ました。しかし、今回は、メイクが題材ということで、ドメイン知識がなく、アイデアも自分のものでないため、今までのPJよりも遥かに進めづらさがありました。そこで、しっかりとどのような完成像を思い描いているのかや、メイクに関する知識を何度もキャッチアップしながら進めました。実際の業務などにおける開発に近く新たな経験となりました。
8-4. 背中で語る
今回のPJにおいて私は開発をしただけですが、後輩は色々と感じ取ってくれたみたいです。
後輩の日記・感想
あるチャンスは掴むってこういうことか。極めてる先輩は本当にすごいし尊敬する。チャンスはあるものだという前提なところも感じて好き。
継続が苦手だから人を巻き込むようにしてるって言ってた。先輩も継続苦手って言ってることに驚いたし、そういう手があるのかというか、あるのは知ってたけど先輩が言うと説得力違うかも。
ゲームをしないってのが今回の技術班の特徴でしたね。時間の使い方や価値観学びました。
全体の感想としては嬉しかったのもあるんですが、どちらというと刺激されました
背中で語って尊敬される人間になりたいですね。
9. Conclusion
高専5年間の間に一度はやってみたかった高専プロコンに出場することが出来、さらに優秀賞という大変光栄な賞も受賞することが出来たため、本当に嬉しかったです。この機会を与えてくれた、後輩と先生には感謝しかないです。また、このPJを通して、自分のスキルを高めることが出来たので、これからも頑張っていきたいと思います。
後輩の感想
全体の感想としては嬉しかったのもあるんですが、どちらというと刺激されました。高専プロコンのお陰でプログラミングのやる気が出て今では一日一回コード書くことを続けています。
とりあえず今までで一番良い経験でした。様々な高専生だったり企業の方と話せる機会もそう無いと思うので。技術面何もできてないんですけど見たりしているだけでも勉強になりました。まなと先輩と一緒にプロコン出れたのが一番大きいなって本当に思います。技術面以外でも学ぶ部分が沢山ありました。リーダー性とかです。
プロコン通して色々とモチベは上がったなって思います。それと人脈、環境、機材あたりで高専て良いなって思いました。
ものづくりってやっぱ楽しいなってなりました。大会当日とかオーキャンでその場で評価だったりフィードバックしてもらえるのも勉強になるしなにより嬉しくて楽しかったです。
自分の無知さと無力さを実感したのでこれから勉強していきたいと思います。
これから何もしないんじゃなくて、ここから成長してこの経験が人生の中で分岐点になったと言えるようになりたいと思いました。
大会までの道のりで何度か折れそうになりましたが、終えてみるといい思い出・経験になりました。高専プロコン楽しかった!
10. Links