こんちわ。seiです。えー、タイトルから言ってネタですが。
最近、子供たちと一緒にパトレイバーにはまってます。
パトレイバー生誕30周年を超え、まだまだ人気が衰えることのないコンテンツです。
TVアニメ版「特車二課壊滅す」とかもう抱腹絶倒です。自分が子供の時に見たときは面白いって感覚よりも「イングラムかっこいい」って気持ちが先行してましたが、今になって見直すとイングラムが登場しなくたっておもしろいし、パトレイバーが登場した当時のまだ社会にITなんて概念が広く浸透していない時代にHOSとかコンピューターウィルスといった設定や、バビロンプロジェクトのように未来を予言でもしているかというような先進的な内容も多く、今になってパトレイバーというコンテンツの素晴らしさを再認識させられました。アーリーデイズも新OVA版も最高ですよ。
U-NEXTで上記パトレイバーシリーズが配信中ですので是非ご覧ください。
————————————————————————
本ページの情報は2024年8月時点のものです。
最新の配信状況はU-NEXTサイトにてご確認ください。
————————————————————————
ブルーレイボックスも絶賛発売中です。
そんなパトレイバーの主役機、AV-98イングラムのプラモデルでも作ろうというのが今回のブログの内容になります。
イングラムのプラモデルを探してみた
ってことでイングラムのプラモデル探してみました。自分が子供のころに作ったのは1/60のモデル。その後、なんとMGとして1/35のイングラムが販売されていました。ちょいと高い・・・。気が付いたら再販されてるっぽいし。
現在はMGも入手困難でしてグッドスマイルカンパニーのモデロイドがパトレイバーつくるには入手しやすいでしょうか・・・。
ただ、イングラム買ってそのまま作っても面白みがないので、今回は「HGのモビルスーツの中で一番イングラムに似ていると思われる機体をイングラムっぽく塗装して我慢するw」って趣旨で行ってみたいと思います。
CustomVisonを使って一番イングラムに似ているMSを探し出す
では、一番イングラムっぽいMSって何かなぁってなった時に、普通であれば直感に頼るんでしょうか?いろいろな人の意見を聞いて決めるのでしょうか?
決め方はいろいろあるのですが、今回はMicrosoftが提供しているAIや機械学習の技術を用いたサービスであるCustomVisionを使用して決定したいと思います。
CustomVisionとは
CustomVisionとは、Microsoftが提供しているCognitive Servicesのサービスの中の1つで、タグ(=正解)を付けた画像をアップロードし、学習させることで独自の画像分類器を作成することができるサービスです。
すでに登場から3、4年たっていますが今も進化を続けています。要Azureサブスクリプションかな・・・。無料で取得できると思いますが。
今回のシチュエーションでいうと、
①HGのMSの画像にそのMS名をタグとしてつけてCustomVisionプロジェクト上にアップロードする。
②登録したプロジェクトを学習させ、それぞれの画像の違いを理解させることで画像分類器を作成する。
③AV-98イングラムの画像を画像分類器に渡し、どのHGのMS名が一番高い確率で返ってくるかでイングラムに一番似ているMSとする。
というような流れになります。
CustomVisionを使用した画像分類器の作成方法
画像分類モデルの作成なんですが、CustomVisionのサイトにアクセスして画像をアップロードしてタグをつけてトレーニングすればあっという間に出来上がってしまいます。あとはイングラムの画像を食わせて、どのMS名が返ってくるかを見るだけです。
ただ、今回はVisualBasicを使用して、1からプログラミングしてみました。といっても、CognitiveServicesに関してはRESTfulAPIでほとんどのサービスが提供されているため、エンドポイントとリクエストパラメーターなど少し変えればほぼ大体のことがこなせてしまうためあまり苦労はないと思います。一番苦労するのはリファレンスが英語であることかな・・・。
ってことで、CustomVisionを使用した画像分類器を作るまでは、
【CustomVisionTrainingAPI】
①プロジェクトを登録する。
②対象プロジェクトに画像をアップロードする。
③画像に対してタグを設定する。
④プロジェクトをトレーニングする。
【CustomVisionPredictionAPI】
予測用画像を引き渡し、結果を得る。
上記の流れになります。
CustomVisionの実装内容
実装コードは以下のようになります。VisualStudio2019です。VS2015以降なら問題ないかな。CustomVisionAPIへのリクエスト部分のみ抜粋。削除などは省略してあります。Jsonの解析にはNewtonsoft.json.dllを使用しています。コントロールのコードなんかも入っちゃってるのでコピペしただけでは動かないと思います。悪しからず。
Azure側で取得したサブスクリプションキーはテストプログラムなのでapp.config内にべた書きしてあります。
登録したプロジェクト、タグなどの情報はXMLでシリアル化してローカルに保存してWebAPIとのやり取りを節約しております。まぁそんなにがつがつやらなければ無料枠内で可能かと思いますが。
ちなみに、CustomVisionの無料枠は
- プロジェクトは2つまで
- 1時間/月のトレーニング
- 登録画像は5000枚まで
- 10000回/月までの予測実行
となっております。登録画像数によってはトレーニング時間が一番引っ掛かりやすいかな。
CustomVisionAPIは現在v3が最新かと思いますが、記述した当時のv1.1のままになっています。
トレーニング状況の取得はもうちょいちゃんとやった方がよかったかな・・・。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
Private Async Sub Request(ByVal action As RequestType) Dim client = New HttpClient() Dim queryString = HttpUtility.ParseQueryString(String.Empty) ' Request headers Dim uri = String.Empty Dim response As HttpResponseMessage ' RESTful APIの呼び出し Select Case action Case RequestType.CreateProject ' Projectの登録 uri = "https://japaneast.api.cognitive.microsoft.com/customvision/v1.1/Training/projects?name=" & Me.ProjectNameTextBox.Text ' uriにクエリーストリングを付加 uri &= queryString.ToString ' Request headers client.DefaultRequestHeaders.Add("Training-key", My.Settings.CustomVisionTrainSubscriptuionKey) Dim bodyString As String = String.Empty Dim byteData() As Byte = Encoding.UTF8.GetBytes(bodyString) Using content = New ByteArrayContent(byteData) content.Headers.ContentType = New MediaTypeHeaderValue("application/json") response = Await client.PostAsync(uri, content) End Using If response.StatusCode = System.Net.HttpStatusCode.OK Then Dim responceBody = Await response.Content.ReadAsStringAsync() ' 要求が成功した場合 Me.ProjectNameTextBox.ReadOnly = True Me.CreateProjectButton.Enabled = False Me.DeleteProjectButton.Enabled = True Me.ProjectIDLabel.Text = JsonConvert.DeserializeObject(responceBody)("Id") MessageBox.Show("プロジェクトの登録に成功しました。", My.Application.Info.AssemblyName, MessageBoxButtons.OK, MessageBoxIcon.Information) Else MessageBox.Show("プロジェクトのの登録に失敗しました。", My.Application.Info.AssemblyName, MessageBoxButtons.OK, MessageBoxIcon.Error) End If Case RequestType.CreateTag Me.CreateTagsButton.Enabled = False Me.DeleteTagsButton.Enabled = False Dim targetTextBox As TextBox = Nothing For i As Integer = 1 To 5 Select Case i Case 1 targetTextBox = Me.Tag1TextBox Case 2 targetTextBox = Me.Tag2TextBox Case 3 targetTextBox = Me.Tag3TextBox Case 4 targetTextBox = Me.Tag4TextBox Case 5 targetTextBox = Me.Tag5TextBox End Select If targetTextBox.Text = String.Empty Then Continue For End If ' タグ名の存在チェック Dim target As CustomVisionAccessor.TagInfo = (From value In accessor.TagList Where value.Name = targetTextBox.Text.Trim Select value).FirstOrDefault If Not target.Equals(CType(Nothing, CustomVisionAccessor.TagInfo)) Then ' すでに登録済みのタグ名は登録スキップ ' Do nothing Else ' タグの登録を行い、タグIDを取得する uri = "https://japaneast.api.cognitive.microsoft.com/customvision/v1.1/Training/projects/" & Me.ProjectIDLabel.Text & "/tags?name=" & targetTextBox.Text ' uriにクエリーストリングを付加 uri &= queryString.ToString ' Request headers client.DefaultRequestHeaders.Add("Training-key", My.Settings.CustomVisionTrainSubscriptuionKey) Dim bodyString As String = String.Empty Dim byteData() As Byte = Encoding.UTF8.GetBytes(bodyString) Using content = New ByteArrayContent(byteData) content.Headers.ContentType = New MediaTypeHeaderValue("application/json") response = Await client.PostAsync(uri, content) If response.StatusCode = Net.HttpStatusCode.OK Then Dim responceBody = Await response.Content.ReadAsStringAsync() Dim id As String = JsonConvert.DeserializeObject(responceBody)("Id") Me.TagListView.Items.Add(New ListViewItem(targetTextBox.Text)) Me.TagListView.Items(Me.TagListView.Items.Count - 1).SubItems.Add(id) Me.TagListView.Items(Me.TagListView.Items.Count - 1).SubItems.Add(0) ' タグListに追加 accessor.TagList.Add(New CustomVisionAccessor.TagInfo With {.Name = targetTextBox.Text, .Id = id, .ImageCount = 0}) targetTextBox.Text = String.Empty End If End Using End If Next Me.CreateTagsButton.Enabled = True Me.DeleteTagsButton.Enabled = True Case RequestType.AddImages Me.AddImagesButton.Enabled = False Me.TrainingButton.Enabled = False Me.AddImageStatusLabel.ForeColor = Color.Red Me.AddImageStatusLabel.Text = "画像登録中..." Dim index As Integer = 0 For Each item As ListViewItem In Me.TagListView.CheckedItems ' 画像に同時に追加するタグはクエリー文字列に配列として追加する必要あり!! queryString.Add("tagIds[]", item.SubItems(1).Text) Next ' Projectにタグと画像の一括登録 uri = "https://japaneast.api.cognitive.microsoft.com/customvision/v1.1/Training/projects/" & Me.ProjectIDLabel.Text & "/images/image?" ' uriにクエリーストリングを付加 uri &= queryString.ToString Dim files() As String = Nothing If Me.FolderSelectRadioButton.Checked Then ' 指定ディレクトリ内の画像ファイルを一括登録する場合 files = Directory.GetFiles(Me.ImageDirectoryTextBox.Text) Else ' 単一画像ファイルを登録する場合 files = {Me.ImageDirectoryTextBox.Text} End If ' Request headers client.DefaultRequestHeaders.Add("Training-key", My.Settings.CustomVisionTrainSubscriptuionKey) Dim imageCounter As Integer = 0 For Each imageFilePath As String In files Dim byteData() As Byte Try ' 画像ファイルをバイト配列にする Using fs As FileStream = New FileStream(imageFilePath, FileMode.Open, FileAccess.Read) byteData = New Byte(fs.Length) {} fs.Read(byteData, 0, byteData.Length) End Using Using content = New ByteArrayContent(byteData) ' 画像ファイルを直接指定の場合 content.Headers.ContentType = New MediaTypeHeaderValue("application/octet-stream") response = Await client.PostAsync(uri, content) End Using If response.StatusCode = Net.HttpStatusCode.OK Then imageCounter += 1 End If Catch ex As Exception ' 失敗した場合は、処理を継続する Using sw As StreamWriter = New StreamWriter(Path.Combine(Application.StartupPath, "Error.log)"), True, Encoding.GetEncoding("Shift_JIS")) sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") & " " & "画像登録失敗:ファイル名 = " & imageFilePath & ",エラー詳細 = " & ex.ToString) End Using End Try If Me.FolderSelectRadioButton.Checked Then ' 指定ディレクトリ内の画像ファイルを一括登録する場合 ' 2トランザクション/秒の制限があるため、500ミリ秒待機する Threading.Thread.Sleep(500) Else ' 単一画像ファイルを登録する場合 ' Do nothing End If Next ' 対象となるタグのイメージ件数をアップデートする For Each item As ListViewItem In Me.TagListView.CheckedItems item.SubItems(2).Text = CType(item.SubItems(2).Text, Integer) + imageCounter ' AccessorのタグListもアップデート For i As Integer = 0 To accessor.TagList.Count - 1 If accessor.TagList(i).Name = item.Text Then Dim value As CustomVisionAccessor.TagInfo = accessor.TagList(i) value.ImageCount += imageCounter accessor.TagList(i) = value Exit For End If Next Next ' 画像登録を行った場合はトレーニング未実施状態にする Me.TrainingStatusLabel.ForeColor = Color.Black Me.TrainingStatusLabel.Text = "トレーニング未実施" accessor.HasTrained = False Me.AddImagesButton.Enabled = True Me.TrainingButton.Enabled = True Me.AddImageStatusLabel.ForeColor = Color.Blue Me.AddImageStatusLabel.Text = "画像登録完了" Case RequestType.Train ' トレーニングの実施 uri = "https://japaneast.api.cognitive.microsoft.com/customvision/v1.1/Training/projects/" & Me.ProjectIDLabel.Text & "/train" ' uriにクエリーストリングを付加 uri &= queryString.ToString ' Request headers client.DefaultRequestHeaders.Add("Training-key", My.Settings.CustomVisionTrainSubscriptuionKey) Dim bodyString As String = String.Empty Dim byteData() As Byte = Encoding.UTF8.GetBytes(bodyString) Using content = New ByteArrayContent(byteData) content.Headers.ContentType = New MediaTypeHeaderValue("application/json") response = Await client.PostAsync(uri, content) End Using Dim responceBody = Await response.Content.ReadAsStringAsync() Dim errorMessage As String = JsonConvert.DeserializeObject(responceBody)("Message") If response.StatusCode = System.Net.HttpStatusCode.OK Then Me.TrainingStatusLabel.ForeColor = Color.Blue Me.TrainingStatusLabel.Text = "トレーニング完了" accessor.HasTrained = True accessor.LastIterationId = JsonConvert.DeserializeObject(responceBody)("Id") Else If errorMessage.Contains("Nothing changed since last training") Then Me.TrainingStatusLabel.ForeColor = Color.Blue Me.TrainingStatusLabel.Text = "トレーニング完了" accessor.HasTrained = True MessageBox.Show("前回のトレーニング後から変更されていません。", My.Application.Info.AssemblyName, MessageBoxButtons.OK, MessageBoxIcon.Information) Else Me.TrainingStatusLabel.ForeColor = Color.Red Me.TrainingStatusLabel.Text = "※トレーニング失敗" accessor.HasTrained = False MessageBox.Show("トレーニングに失敗しました。" & vbCrLf & "エラー詳細 = " & errorMessage, My.Application.Info.AssemblyName, MessageBoxButtons.OK, MessageBoxIcon.Error) End If End If Case RequestType.Predict ' 画像の予測を行う uri = "https://japaneast.api.cognitive.microsoft.com/customvision/v1.1/Prediction/" & Me.ProjectIDLabel.Text & "/image/nostore?" ' uriにイテレーションIDを追加する ' 複数イテレーションがある状態だと「デフォルトのイテレーションが存在しないよ」って怒られてPredictionできないため queryString("iterationId") = accessor.LastIterationId ' uriにクエリーストリングを付加 uri &= queryString.ToString ' Request headers ' CustomVisionPredict用のサブスクリプションキーを使う client.DefaultRequestHeaders.Add("Prediction-Key", My.Settings.CustomVisionPredictSubscriptuionKey) Dim byteData() As Byte ' 画像ファイルをバイト配列にする Using fs As FileStream = New FileStream(Me.FilePathTextBox.Text, FileMode.Open, FileAccess.Read) byteData = New Byte(fs.Length) {} fs.Read(byteData, 0, byteData.Length) End Using Using content = New ByteArrayContent(byteData) ' 画像ファイルを直接指定の場合 content.Headers.ContentType = New MediaTypeHeaderValue("application/octet-stream") response = Await client.PostAsync(uri, content) End Using If response.StatusCode = Net.HttpStatusCode.OK Then Me.PredictionStatusLabel.Text = "予測完了" Me.PredictionStatusLabel.ForeColor = Color.Blue Dim responceBody = Await response.Content.ReadAsStringAsync() Dim predictions = JsonConvert.DeserializeObject(responceBody)("Predictions") Dim dic As Dictionary(Of String, String) = New Dictionary(Of String, String) For Each result In predictions If Not dic.ContainsKey(result("Tag")) Then dic.Add(result("Tag"), Math.Round(CType(result("Probability"), Decimal) * 100) & "%") End If Next Dim list As List(Of KeyValuePair(Of String, String)) = New List(Of KeyValuePair(Of String, String)) list.AddRange(dic) Me.ResultDataGridView.DataSource = list With Me.ResultDataGridView .AutoGenerateColumns = True .AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders) .AutoResizeColumnHeadersHeight() ' 編集不可に設定 .ReadOnly = True ' 新規行の追加を不可に設定 .AllowUserToAddRows = False .AllowUserToDeleteRows = False ' 行ヘッダーを非表示に設定 .RowHeadersVisible = False ' 行の高さ変更不可に設定 .AllowUserToResizeRows = False ' 垂直スクロールバーのみ表示を設定 .ScrollBars = ScrollBars.Vertical .RowsDefaultCellStyle.BackColor = Color.PaleGreen .AlternatingRowsDefaultCellStyle.BackColor = Color.White .Columns(0).Width = CType(.ClientRectangle.Width / 2, Integer) .Columns(1).Width = CType(.ClientRectangle.Width / 2, Integer) .Font = New Font("MS 明朝", 14) End With Else Me.PredictionStatusLabel.Text = "予測失敗" & vbCrLf & "トレーニング中の可能性もありますので、しばらくしてから再実行してください" Me.PredictionStatusLabel.ForeColor = Color.Red End If End Select End Sub |
アプリケーションを使用して画像分類モデルを構築
さっそく出来上がったアプリケーションを使用して、HG画像の登録・タグ付けを行い、イングラム検索モデルを作成してみました。
現在発売されているすべてのHGではトレーニング用画像の用意が大変なため、独断と偏見でジム系のMSのみエントリーさせることにしました。すんません。ちなみに、購入が安易にできるようにバンダイホビーサイト未記載のHG、および、オンラインショップ系のHGは省いております。それではエントリーしたMSはこちらです。
GM/GM | ガンキャノン | ジェガン(エコーズ仕様) |
ジェガンブラストマスター | ジム(サンダーボルト) | ジム・インターセプトカスタム |
ジム・ガードカスタム | ジムⅢ | ジムⅢ・ビームマスター |
ジムカスタム | ジムスナイパーⅡ | ジムスナイパーK9 |
スタークジェガン | ネモ(デザートカラー) | ネモ(ユニコーンVer) |
パワードジム・カーディガン | ブルーディスティニー | リゼル |
リゼル(ゼネラルレビル) | 陸戦型ジム |
CustomVisionの制約にある「各タグごとに5枚の画像が必要」とのことなので、各MS最低5枚の画像を用意しています。
それでは「IngramSearch」というプロジェクトを作り、画像登録・タグ付けを行いました。初めはイングラムは白と黒だけだからと、各トレーニング画像すべてをグレースケール変換したものをアップしてからトレーニングを行った結果です。CustomVisionサイトで確認するもあんまり精度が出ていません。白黒2値のパターンで判断されてしまっているのでしょうか・・・。
ですので、こんどはちゃんとフルカラー画像をアップして、先ほどのグレースケールと合わせてトレーニングを行ってみました。そこそこ精度が上がり、これなら使い物になるかと思います。
構築したモデルで画像の予測を行う
さぁ、それでは作成した「イングラム検索」CustomVisionモデルを使用してNo.1イングラムに似ているMSを決定したいと思います。
どぅるるるるるるるるるるる・・・・じゃん!!
最もイングラムに似ているMSは、「ジムカスタム」でした!!おめでとぅーーすっ!!
ちなみに、予測に使ったイングラムの画像はプラモデルではなく、ロボット魂の画像です。
左前方からの画像。トレーニング画像には必ず入っているポジションです。頭一つジムカスタムかな。
背面からの画像もジムカスタムが若干高い。
他の対象物ある画像ですがジムカスタム。ジムスナイパーⅡが検討しているのもなんとなくうなずけます。
ポージングを決めている画像はトレーニング画像にあまりなかったのですが、こいつは文句なしでジムカスタムですね。すばらしい。
その他の画像も例外ありますがほぼほぼジムカスタムと認識されています。
さいごに
CustomVisionですが、とても簡単にユーザー独自の画像認識技術を利用することができます。 今回行ったのは「画像は何か?」といった分類の内容ですが、 最近の機能アップデートでは、「画像内に対象はいくつ存在しどこにあるか?」といった物体検出の機能も追加され、ますます利用価値が増したように感じます。
それでは次回、今回の「一番イングラムっぽいMS選手権」の結果を踏まえて、ジムカスタムのイングラム化塗装を行っていきたいと思いますw
塗装した後もう一度CustomVisionに予測させてイングラム率はどれだけ上がるかも試してみたいですね。
いつになるかわかりませんがお楽しみにw
続きはこちらで。
コメント