Kong Cameraを作ってみた

本記事は「Kong Advent Calendar 2024」の14日目のエントリで、API GatewayのKongを体感的に使える Kong Cameraを作ってみます
---
ChatGPT/OpenAI APIに代表されるように、世の中APIの必要性は増すばかり。そんなAPIを統合的に管理するKong API Gatewayの需要が高まってきています。
ここでは、カメラデバイスKong Cameraで写真を撮って、それをAIに画像解析してもらいます。この一連の流れをKong Gatewayを通す事により、API管理の仕組みを学んでいきます。

## この記事で学べる事
A. [Kong Konnect、API Gatewayの使い方が分かる](#i-kong-api-gatewayの立ち上げ)
B. [IoTデバイス(Raspberry Pi)とカメラを連動したAPIの作り方](#iii-kong-konnectへのカメラapiの登録)
C. [AIの効率的な管理（Kong AI Gateway）について学べる](#iv-ai-gatewayの設定)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/eea82c7e-d446-af88-1f0e-852459b6563e.png)


# Kong Cameraの作り方はざっとこんな感じ

[ハードウェアで必要なもの](#ハードウェアで必要なもの)
1. [Kong Konnectの導入](#i-kong-api-gatewayの立ち上げ)
2. [Raspberry PiとウェブカムをつないでカメラAPIの作成](#ii-raspberry-piとウェブカムをつないでカメラapiの作成)
3. [Kong KonnectへのカメラAPIの登録](#iii-kong-konnectへのカメラapiの登録)
4. [AI Gatewayの設定](#iv-ai-gatewayの設定)
5. [Kong Button、Lampと連動させて完成！](#v-kong-buttonと連動させて完成)

![スクリーンショット 2024-08-13 18.01.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/45bcabc6-ee1f-97b9-01df-79e7dfc62ae8.png)
(AI Gatewayの使い方を見ていきます)

## ハードウェアで必要なもの
| 必要なもの | 部品 |
| ---- | ---- |
| [Raspberry Pi](https://amzn.to/3OuF5Up) | [![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/5bda97e5-4a2a-95d7-b637-a79803ade07c.png)](https://amzn.to/3OuF5Up) |
| [ウェブカム](https://amzn.to/3B5ZZX1) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/9b5ddf6f-5b5e-7284-2807-3861ff631171.png) |
|その他ケーブル、両面テープ、など | 適宜 |

カメラから映像を撮って、AIが画像解析してくれます。

![camera.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/d5bac095-146a-06c3-ef7a-88c68d1247f0.gif)

## I. Kong API Gatewayの立ち上げ

まだKong環境を用意していない方は、[前回記事](https://qiita.com/Ktrips/items/130f0bdbf69763a87142) と同様の流れに従って、KongのSAAS環境Kong Konnectで、API Gatewayを立ち上げて下さい。

### 1. [Kong Konnect](https://cloud.konghq.com) にアクセス
https://cloud.konghq.com にアクセスして、ユーザー、パスワードなどを登録し、Kong Konnectに入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/e1ff997b-017e-080c-465d-413cd6fe2217.png)

### 2. Control Planeを作成
Konnectにログオンできたら、Gateway Managerスクリーンから New Control Plane を押して、新しいGatewayを作ります。
![スクリーンショット 2024-07-13 16.37.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/0432aff9-ba3f-01d8-f685-22f3f5761a61.png)

### 3. Data Planeを作成
Data Plane Nodesには、Kongのフルマネージド環境の Dedicated Cloud Instances を選びます。
![スクリーンショット 2024-07-13 16.41.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/79e0b87a-4e69-6059-0e44-be07bc251d4f.png)

そうすると Payment method の設定を求められるので、クレジットカードの支払い方法をセットします。
（無料枠＄500があるので、その範囲内の最小限の使い方なら課金されることはありません）

![スクリーンショット 2024-07-13 16.38.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/3b1e6295-66fd-b97e-3ff2-e1eb63741315.png)

Regionを選ぶ画面が出るので、USなどの適当なRegionを選びます。

![スクリーンショット 2024-07-13 16.58.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/5906abda-7e97-10e8-5ead-9de52db019b7.png)

### 4. (Option) ドメインの設定
（オプションですが）左側メニューのCustom Domainsから、個人のドメイン名を紐づけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/9a0d42d0-4c93-dc1c-c6a2-44bc98ac3851.png)

これでhttps://konnect.example.com のようなURLでKong環境にアクセスできるようになりました。


## II. Raspberry PiとウェブカムをつないでカメラAPIの作成
ハードウェアとの連携として、Raspberry Pi（ラズパイ）とカメラを接続して、画像撮影ができるAPIを作ります。

### 1. ラズパイとカメラの接続
今回使ったのはこちらの[ウェブカム](https://amzn.to/3B5ZZX1)で、ラズパイにUSBで接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/9b5ddf6f-5b5e-7284-2807-3861ff631171.png) 

ラズパイとUSBでつないだら接続を確認します。
[このような記事](https://deviceplus.jp/raspberrypi/basic-of-raspberrypi_01/)に従って、ラズパイにSSHでアクセスできるようにします。
以下のコマンドを流してみて下さい。

```JavaScript: Camera
$ lsusb
Bus 001 Device 003: ID 0411:02da BUFFALO INC. (formerly MelCo., Inc.) USB 2.0 Camera
$ ls /dev/video*
/dev/video1
```

![image.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/2f52958d-5415-7f69-3208-cab285c5922d.jpeg)

接続したカメラが見えれば（この場合BUFFALO）撮影に使えます。

次に画像撮影のためのfswebcamをインストールします。
以下のようなコマンドから写真を撮影します。

```JavaScript: fswebcam
$ sudo apt install fswebcam
$ sudo fswebcam -r 320x240 kong.jpg
```
kong.jpgというファイルが作られ、デスクトップ画面で開いてみると、写真が撮れている事が分かると思います。


### 2. カメラAPIの作成
[前回のパトランプAPIの記事](https://qiita.com/Ktrips/private/95c24f86df4aa66f8980)を参考に、カメラAPIを作成します。
前回作ったExpressのapp.jsに、以下の部分を追加します。

```JavaScript: iot/app.js
var express = require('express');
// ...
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var led = require('./routes/led');
var cam = require('./routes/cam'); // << *** Camera API Added ***
// ...
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/led', led);
app.use('/cam', cam); // << *** Camera API added ***
// ...
```

次にroutesフォルダ下に以下のようなコードで、cam.jsファイルを作成します。

```JavaScript: iot/routes/cam.js
var express = require('express');
var router = express.Router();
const homeDir= "/home/pi/Programs/iot/image/";

router.get('/:action', function(req, res, next) {
  var param = {"Camera API":"shot !"};
  res.header('Content-Type', 'application/json; charset=utf-8');
  imageFile = homeDir+"kong.jpg";
  exec('sudo fswebcam -r 320x240 --no-banner -q '+imageFile);
  param["imageFile"] = imageFile;
  res.send(param);
});

module.exports = router;
```

これを実行して、APIとして機能するか試してみます。

```JavaScript: Camera API
$ sudo mkdir image
$ sudo chmod 755 image

$ npm start
$ curl localhost:3000/cam/shot
```

これを実行すると、ローカルフォルダに写真が保存されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/a5a92eca-5f5c-ac78-f635-aa4f8b6f082c.png)

これで写真撮影するカメラAPIが作成できました。


## III. Kong KonnectへのカメラAPIの登録
作ったカメラAPIをKong Konnect上に登録します。

### 1. ngrokによるトンネル化
まだAPIを公開していない場合は、[前回記事](https://qiita.com/Ktrips/private/95c24f86df4aa66f8980)に従って、ngrokというサービスでAPIのトンネル化を実行して下さい。
https://ngrok.com にアクセスし、Setup IntroductionからRaspberry Piのngrokの導入を行なっておきます。

![スクリーンショット 2024-12-02 11.57.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/fc0024cc-505f-cb11-7e6f-1b2f81a47e04.png)

こうすると、以下のようにインターネット上からngrokのURLで、ラズパイのCamera APIを起動する事ができるようになります。

```JavaScript: ngrok install
$ snap install ngrok
$ ngrok config add-authtoken ngrok_token
$ ngrok http localhost:3000
$ curl https://ngrok-endpoint-free.app/cam/shot
```
これでKong Konnectに登録するAPIが準備できました。


### 2. カメラAPIをKongのサービスとして登録
改めて[Kong Konnect](https://cloud.konghq.com)に入ります。
Konnectの左側のGateway Servicesから、New Gateway Serviceを選び、新しいサービスを作成します。
先ほどのngrok Endpointをhostに指定します。Pathは先ほど作った/cam にします。

![スクリーンショット 2024-12-05 15.47.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/2c3bc01b-308a-0253-768d-b1cd6c9e72e9.png)

サービスのルートを以下のように作成します。

![スクリーンショット 2024-12-05 15.54.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/d5fec84e-dafa-2b63-612d-f8d5d21c810f.png)


### 3. Kongからの接続確認
設定が済んだら、KONGのプロキシまたは自分で設定したドメイン経由で、カメラAPIにアクセスします。
このようにブラウザから実行すると結果が表示されます。

![スクリーンショット 2024-12-13 15.54.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/6855bbb3-9148-7a93-af82-da13096f0a54.png)

Kongのプロキシを使って接続して、このような写真が撮れていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/4caf2ee1-2720-0d08-2df6-cbcdf9ffd23c.png)


## IV. AI Gatewayの設定
次にKongのAI機能、AI Gatewayの設定を行っていきます。

### 1. AIサービスの登録 
KongのAI機能のAI Gatewayの登録を行います。
以下のようにOpenAIのサービスの追加をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/b7cb6f80-456d-3de2-3fd7-cd37bc6b5176.png)

以下のように、ルートの追加も行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/3439e886-8426-0807-ef09-63dd54af10c7.png)

これをCurlから実行してみます。
URLは、https://konnect.example.com/openai/v1/chat/completions のような形になります。
XXXXは、OpenAIのKEYであらかじめ、https://openai.com から取得しておいて下さい。

```JavaScript: OpenAI
$ KONG=https://konnect.example.com
$ IMAGE_URL=https://AAAA.com/image.jpg
$ GPT_API_KEY=sk-XXXX

$ curl $PROXY/openai/v1/chat/completions \
$  -H "Content-Type: application/json" \
$  -H “Authorization: Bearer ${GPT_API_KEY}” \
$  -d ‘{
    "model": "gpt-4o",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "What is in this image?"
          },
          {
            "type": "image_url",
            "image_url": {
              "url": ${IMAGE_URL}
            }
          }
        ]
      }
    ],
    "max_tokens": 300
  }’
```

これを実行して、結果が返ってくれば、Konnect経由でOpenAI APIが実行できています。

```JavaScript: OpenAI Result
...
"model": "gpt-4o-2024-08-06",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "This image shows Mount Fuji, a famous and iconic stratovolcano in Japan. The mountain is snow-capped at the top, and in the foreground, there are lush green fields and trees, along with a view of a town or city with roads and buildings. The sky is clear and blue.",
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1118,
    "completion_tokens": 60,
    "total_tokens": 1178,
    "prompt_tokens_details": {
      "cached_tokens": 1024,
      "audio_tokens": 0
    },
...
```

### 2. AI Gatewayの設定
このサービスにAIのPluginsを設定します。
ここでは、AIのトークンベースの流量制限AI Rate Limiting Advancedを追加します。
先ほどのOpenAIのサービスを選んで、Pluginsに行きます。
Pluginsから、AI Rate Limiting Advancedを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/f1f3d96b-ab22-a8d5-9ed2-8d02ea18b263.png)

このPluginの設定は以下のように行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/183eb9a5-d2e8-9e72-a3f0-dcb751f0e26d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/7a8dd003-8a08-a4f8-94b3-3e0ce41fd8a4.png)

これで60秒間に100トークン以上OpenAIが呼ばれた時に、429 Errorを返すようになります。
これを実行してみると、rate limit exceededのメッセージが出て一時的にAPIがコールできないようになります。
KonnectのAnalytics画面でもそれが確認できます。

![スクリーンショット 2024-12-02 16.49.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/703bfa00-b68c-1c38-fd30-536e6c3d27ab.png)


## V. Kong Buttonと連動させて完成！
[Kong Button記事](https://qiita.com/Ktrips/items/130f0bdbf69763a87142) で作成したものと連動させて、ボタン長押しでカメラから写真を撮り、AIに画像解析させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/20ac6705-cb8d-426c-6c65-2fb563a6130e.png)

### 1. カメラAPIへAI機能の追加
ラズパイ上で先ほど作ったカメラAPIで、cam.jsにAIの機能を追加します。
撮影して画像解析した結果をLINEにも送付します。

APIのキーを環境変数に入れておくため、.envファイルを作成しておきます。
ここでは、XXXXはOpenAIのキー、LLLLはLINE Keyの意味です。

```JavaScript: .env
$ sudo vi .env

GPT_API_KEY=XXXX
LINE_NOTIFY_TOKEN=LLLL
```


```JavaScript: cam.js
var express= require('express');
var router = express.Router();

var fs      = require('fs');
var axios   = require('axios');
var FormData= require('form-data');
var config  = require('dotenv').config();

const KONG   = "https://konnect.example.com";
const homeDir= "/home/pi/Programs/iot/image/";
const GPT_API= KONG + "/openai/v1/chat/completions";
const GPT_API_KEY = process.env.GPT_API_KEY;

const LINE_NOTIFY_API  = KONG + "/line/api/notify";
const LINE_NOTIFY_TOKEN= process.env.LINE_NOTIFY_TOKEN;

router.get('/:action', function(req, res, next) {
  var param = {"Result ":"Camera API"};
  res.header('Content-Type', 'application/json; charset=utf-8')
  imageFile = homeDir+"kong.jpg";
  exec('sudo fswebcam -r 320x240 --no-banner -q '+imageFile);
  param["fswebcam"] = imageFile;

  const imageBuffer = fs.readFileSync(imageFile);
  const base64Image = imageBuffer.toString("base64");
  var SYSTEM_PROMPT = "ここに写っている画像を20字で説明して下さい。";

  const analyzeImageByGPT = async () => {
   const messages = [
    {role: "system", content: SYSTEM_PROMPT},
    {role: "user", content: [
        {type: "image_url", image_url: {url: `data:image/jpeg;base64,${base64Image}`}}
      ]
    }
   ];

   try {
    const response = await axios.post(GPT_API, { 
        model: "gpt-4o", messages: messages
    }, {
        headers: {'Authorization': `Bearer ${GPT_API_KEY}`, 'Content-Type': 'application/json'
        }
     });
     imageResult = response.data.choices[0].message.content;
     param["imageResult"] = imageResult;
     res.send(param);

     const imagefile = fs.createReadStream(imageFile);
     const formData = new FormData();
     formData.append('message', imageResult);
     formData.append('imageFile', imagefile);

     const result = await axios({
      method: 'POST',
      url: LINE_NOTIFY_API,
      headers: { Authorization: `Bearer ${LINE_NOTIFY_TOKEN}`, 'apikey': 'AAAA', 'content-type': 'multipart/form-data', ...formData.getHeaders() },
      data: formData
     })

   } catch (error) {
      console.error(`画像解析のエラー: ${JSON.stringify(error)}`);
      return null;
   }
  }
});
```

これを実行すると、写真、解析結果がLINEに送られてきます。

![image.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/31f35c98-d021-fd60-096a-a79a02bc4564.jpeg)


### 2. Kong Buttonにカメラ機能を追加
最後にKong Buttonに機能を追加します。
ボタンを長押しすると、カメラで写真を撮って、AI Gatewayに送信します。
そのために、[前回作った](https://qiita.com/Ktrips/private/130f0bdbf69763a87142) Buttonのスケッチに、以下のように追記します。

```C: M5Stick_Kong_Button_Cam_AI.ino
#include "M5StickCPlus.h"

// ...

const String cam = "https://konnect.example.com/cam";  // *** Added ***

// ...

void loop(){
  M5.update();

// ...

  if (M5.BtnA.wasPressed()) {
    delay(longPress_ms);
    if(M5.BtnA.read() == true){ 
      Serial.print("Red");
      M5.Lcd.fillScreen(RED);
      M5.Lcd.printf("Help!");
      sendLine("問題あるか見て！ HELP！", 8522, 16581287);
      http_get(led+"/on");
      
      http_get(cam+"/shot");             // *** Added ***
      sendOpenAIRequest(prompt);         // *** Added ***
      delay(500);
      http_get(led+"/off");              // *** Added ***
    }
  }
  delay(500);
}
```

### 3. 全部つなげて使ってみよう！

ユースケースとしては、家の事が心配になったらボタンを長押して、家の中にあるラズパイに写真を撮らせます。そこに何が写っているかAIが画像解析して、LINEで通知。家のパトランプも光るので、もし不審者が居ても警告できますね！

![スクリーンショット 2024-08-13 18.01.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/45bcabc6-ee1f-97b9-01df-79e7dfc62ae8.png)

ボタンを長押しすると、写真を撮って、AIが画像解析します。

![kongCamera.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/363e35aa-7ede-e4d1-2702-7a32b70b9789.gif)

スマホには、その写真と解析した内容が送られてきます。

![IMG_1177.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/420ed83f-3182-529d-6328-263b8f497da8.jpeg)

KongのAnalytics画面でAPIコールが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/6490d75e-9c7a-ae5e-76fa-0a8910159cbb.png)

またKonnectのAnalytics Reportsを使うと、APIの状況を監視、確認する事ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/0c9deed7-0d93-2e13-9bfd-a7aa726d336c.png)


# まとめ

という事で、Kong Konnect上にAPIをセットし、ハードウェアのカメラから写真を撮る事ができるようになりました。
またKongのAI Gatewayを使うと、AI登録や管理が簡単にでき、とても便利な事が分かりました。

Kongを使うとさまざまなAPIをまとめて管理でき、またCloud Gatewayを使うと自分自身で環境を持たなくてもフルマネージドな環境が手に入ります。
今後もさまざまなAPIを追加して、便利な環境にしていこうと思います！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212060/7e432954-5e2a-93fc-c3d6-75c499d1b659.png)

それでは、よいKongを！ Happy Kong!

ヨシケン

# 参考リンク
- [Raspberry Piのセットアップ](https://deviceplus.jp/raspberrypi/basic-of-raspberrypi_01/)
- [Raspi 3](https://qiita.com/rubytomato@github/items/07c16a631cee1e9a68be)
- [Kongのセットアップ](https://qiita.com/rubytomato@github/items/07c16a631cee1e9a68be)
- [ Kongのドキュメント(英語)](https://github.com/alexa-pi/AlexaPi/wiki/Installation)
- [ Kong Konnectのページ(英語)](https://developer.amazon.com)
- [Blog.ktrips.netヨシケンのモノ作りブログ](http://blog.ktrips.net)