身の回りにある「呪いのアイテム」を検出するサービスを未経験エンジニアが作ってみた【個人開発】

![ogp_jubutsuchecker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/225633ba-b46e-fd9a-f3ba-fffab0085a3a.png)

# はじめに

[たいじゅ](https://qiita.com/taiju_higashimachi)と申します。
私は現在、未経験からWebエンジニアへの転職を目指して活動中です。
ポートフォリオはすでに1つ作成したのですが、画像認識の技術を使ってみたかったので、2つ目のポートフォリオを作成しました。

サービス名は**『呪物確認』**です。
マンガやアニメで大人気の『呪術廻戦』をヒントに作った、おバカ系サービスとなっています。

**※現在、サービスは停止しています**

2つ目のポートフォリオということで、あまり時間をかけず10日ほどで作ったサービスです。
なので改善点はたくさんありますが、十分遊べると思いますので、ぜひ使っていただけると嬉しいです。

**この記事では『呪物確認』のサービス内容や使い方、技術のご紹介をさせていただきます。**
画像認識を使ったサービス開発に興味がある方には、ぜひ最後まで読んでいただきたいです。

また一部技術の詳細については、次の記事にまとめましたので、ご興味のある方はぜひご覧ください。

[Vision APIで物体検出した画像に四角い枠を描画する方法【Canvas】](https://qiita.com/taiju365/items/f9fe5aef1938381b3199)

---

### 目次

| 大見出し | 小見出し |
|:--|:--|
|[1．サービスの紹介](#1サービスの紹介)|[トップページ](#トップページ)|
||[リザルトページ](#リザルトページ)|
||[呪物一覧ページ](#呪物一覧ページ)|
|[2．技術の紹介](#2技術の紹介)|[テーブル設計](#テーブル設計)|
||[主な使用技術](#主な使用技術)|
|[3．力を入れたポイント](#3力を入れたポイント)|[ユーザーがアップした画像を、データベースに保存しない](#ユーザーがアップした画像をデータベースに保存しない)|
||[アクセス数に応じたビューの切り替えを実装](#アクセス数に応じたビューの切り替えを実装)|
||[物体検出で四角い枠を表示](#物体検出で四角い枠を表示)|
|[4．さいごに](#4さいごに)||

# 1．サービスの紹介
『呪物確認』は、**身の回りのものを写真に撮ると、それが呪われたモノ（呪物）かどうか確認できるサービス**です。
マンガ『呪術廻戦』に登場する呪物かどうかも確認できます。

それでは、もう少し詳しい内容を、ページごとにご紹介していきます。

## トップページ

![db5ef52b4571c67da83decba6922491a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/ec92f397-c9ab-9d13-5121-f56394489212.gif)

トップページでは、**写真の撮影・選択**ができます。

呪物が写っているか確認したい写真を撮影、もしくは選択します。
パソコンからアクセスした場合は「写真を選択する」のボタンが、スマートフォンからアクセスした場合は「写真を撮影する」のボタンが表示されます。

## リザルトページ
![426b0e9e114094cd110dae28ccbf58b7 (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/bccbcc43-2676-4c71-4374-24776957c7ea.gif)

リザルトページでは、**写真の中に呪物の可能性が検知されたかを確認できます。**

検知された場合は、検知した箇所に四角い枠が表示され、さらに呪物の名前と呪物を説明しているYouTubeの動画が表示されます。

また「Vision APIのレスポンスをもとに、四角い枠を描画する」という処理に関して、ピンポイントで説明されている記事が無く、かなり時間がかかってしまいました。
そのため、別途記事を作成したので、これからVision APIに挑戦する、という方はぜひご覧ください。

[Vision APIで物体検出した画像に四角い枠を描画する方法【Canvas】](https://qiita.com/taiju365/items/f9fe5aef1938381b3199)

## 呪物一覧ページ
![829ed66616471804453ca150b1f08584 (1)-min.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/77d63769-21da-6bf7-e307-df634fa4e0e9.gif)

呪物一覧ページでは、**写真から検知できる呪物の一覧が確認できます。**

YouTubeの埋め込みタグは重いので、インフィニット・スクロールを導入して、2つずつ読み込むようにしています。


# 2．技術の紹介

### GitHubリポジトリ
[JubutsuChecker](https://github.com/TaijuHigashimachi/JubutsuChecker)

## テーブル設計

### ER図

![0a9ca3a91e62db3c56f5d1ac4a35f852.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/1954a81f-ab0a-30b3-998e-f0fabb7a0f4d.png)

### cursed_itemsテーブル

呪物の名前とYouTubeの動画IDを保存するテーブルです。

### label_namesテーブル、labelingsテーブル

呪物とラベルの対応関係を保存するテーブルです。

本サービスでは物体検出にGoogle Cloud Platformの**Vision API**を使用しています。
Vision API側の物体名をラベル名としてデータベースに登録し呪物と関連付けておくことで、Vision APIからのレスポンスに応じて関連のある呪物を表示しています。

たとえばVision APIのレスポンスに「イスが検出されました」というデータがあれば、「イス」というラベルと対応させている呪物『バズビーズチェア』をデータベースから取得してリザルトページに表示しています。

### accessesテーブル
アクセス数に応じてビューの内容を切り替えたかったため、アクセス数をデータベースに保存しています。

ビューを切り替える理由は「[3．力を入れたポイント]()」で説明しています。

## 主な使用技術

### バックエンド

- Ruby 3.0.2
- Rails 6.1.4

### フロントエンド

- HTML
- CSS（SCSS）
- JavaScript（jQuery）
- Bootstrap5
- Font Awesome

### Gem

- High Voltage
    - 静的ページの作成
- Pagy
    - ページネーション
    - Kaminariよりもパフォーマンスが優れているということで選定
- jpmobile
    - PCとスマホで、一部表示わけをしたかったので導入

### パッケージ

- Infinite Scroll
- Swiper

### インフラ

- Heroku

### API

- Vision API

# 3．力を入れたポイント

## ユーザーがアップした画像を、データベースに保存しない

ユーザーがアップする画像は、自分の部屋などプライベートな画像が予想されます。
そのため、画像は一切データベースに保存しないようにしました。

最初は、トップページでアップした画像をデータベースに保存せずに、遷移先のリザルトページで表示する方法がわかりませんでした。

ですが、JavaScriptで実装しているプレビュー機能では、画像をどこから取得しているんだろう？　と、ふと気になったことがきっかけで、画像の参照先を特定することができました。

```ruby
# ユーザーがアップした画像をBase64エンコード

@encoded_image = Base64.strict_encode64(params[:upload_image].read)
```

```javascript
// Viewファイルに記述したcanvasで画像を出力しているため、JS内にRubyタグが埋め込まれています

var data = "data:image/jpeg;base64,<%= @encoded_image %>";
img.src = data;
```

また「Vision APIのレスポンスをもとに、四角い枠を描画する」という処理に関して、ピンポイントで説明されている記事が無く、かなり時間がかかってしまいました。そのため、別途記事を作成したので、これからVision APIに挑戦する、という方はぜひご覧ください。

[Vision APIで物体検出した画像に四角い枠を描画する方法【Canvas】](https://qiita.com/taiju365/items/f9fe5aef1938381b3199)

## アクセス数に応じたビューの切り替えを実装

そもそもなぜビューを切り替える必要があるかというと、物体検出が月に1,000回使用されたら、その月はサービスを利用できなくなるようにしたかったためです。

Vision APIは、月に1,000回までのリクエスト（正確には1,000ユニットまで）なら料金がかかりません。
しかし、1,000回を超えると1リクエスト単位で料金が発生します。
個人で使用するには料金が高額なため、残念ですが本サービスの使用回数に、月単位で制限をかけています。

※Vision APIの利用におけるトラブルについて、私の方では一切責任が取れません。
仕様の変更などが原因で、高額な請求が来る可能性も否定できません。
ですので、**Vision APIを使用なさる場合は、ご自身で料金や仕様をご確認ください。**

・料金  |  Cloud Vision API  |  Google Cloud
https://cloud.google.com/vision/pricing?hl=ja

## 物体検出で四角い枠を表示

![62b29a7cab7e457750e0793842255346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641012/323ab2f5-a100-2025-9b98-1b52f78558a3.png)

物体検出といえば、検出した箇所に四角い枠が表示されているイメージがありますよね。
しかし、Vision APIを使用しても、リクエスト時の画像に四角い枠を描画して、レスポンスで渡してくれるわけではありません。

レスポンスでは、四角い枠の各頂点の座標データが返ってくるだけです。
そのため、四角い枠を表示するためには、次のような座標データをもとに描画する処理が必要となります。

```json
・・・省略
      "mid": "/m/01mzpv",
      "name": "Chair",
      "score": 0.7149181
    },
    {
      "boundingPoly": {
        "normalizedVertices": [
          {
            "x": 0.25293663,
            "y": 0.60546136
          },
          {
            "x": 0.41774547,
            "y": 0.60546136
          },
          {
            "x": 0.41774547,
            "y": 0.8245771
          },
          {
            "x": 0.25293663,
            "y": 0.8245771
          }
        ]
      }
省略・・・
```

開発にあまり時間をかけられなかったのですが、四角い枠が表示されるのとされないのでは、サービスの面白さが全く変わってしまうため、必要だと判断して処理を追加しました。

また「Vision APIのレスポンスをもとに、四角い枠を描画する」という処理に関して、ピンポイントで説明されている記事が無く、かなり時間がかかってしまったので、別途記事を作成しました。

また「Vision APIのレスポンスをもとに、四角い枠を描画する」という処理に関して、ピンポイントで説明されている記事が無く、かなり時間がかかってしまいました。
そのため別途記事を作成したので、これからVision APIに挑戦する、という方のお役に立てれば幸いです。

[Vision APIで物体検出した画像に四角い枠を描画する方法【Canvas】](https://qiita.com/taiju365/items/f9fe5aef1938381b3199)

# 4．さいごに
今回の記事はこれで終わりです。
最後までご拝読いただき、ありがとうございます。