ゴリラ判定機、 ゴリネーター！！作ってみた！

# 自己紹介
私は約20歳の専門学生です。
専門学校に入って一年たちました。
専門学校入る前にはプログラミングを全くやったことがなかったです。
今はpythonの基礎が何となくわかるようになったと思うので色々作りながら勉強していきたいです！
# 参考にさせていただいたもの
discord.pyはこちらを参考にさせていただきました
https://qiita.com/mizunana/items/4afddc71f37df555078e
CustomVision,OpenVINOはこちらを参考にさせていただきました
https://github.com/hiouchiy/IntelAI_and_Cloud/blob/master/Azure/demo1/Lesson1_AzureCognitiveService_and_OpenVINO_Collaboration.ipynb
#使ったもの
<ul>
<li>python3.7</li>
<li> discord.py</li>
<li>CustomVision</li>
<li>OpenVINO</li>
</ul>
#概要
今回私はCustom Visionを使って簡単なAIのモデルを作りました。
そしてそれをdiscordのbotとして使えるようにしました
#コード
```python
# -*- coding: utf-8 -*-
import glob
import random
import numpy as np
import glob
import random
import time
import os
import cv2
from PIL import Image
import PIL
import io
import IPython.display
from IPython.display import clear_output
import discord
import requests
from openvino.inference_engine import IENetwork, IEPlugin 

class Model(object):

    def __init__(self):
        self.labels = []
        labels_filename = "labels.txt"

        # Create a list of labels.
        with open(labels_filename, 'rt') as lf:
            for l in lf:
                self.labels.append(l.strip())

    def predict(self, imageFile):
        raise NotImplementedError
    
    def convert_to_opencv(self, image):
        # RGB -> BGR conversion is performed as well.
        image = image.convert('RGB')
        r,g,b = np.array(image).T
        opencv_image = np.array([b,g,r]).transpose()
        return opencv_image

    def crop_center(self, img,cropx,cropy):
        h, w = img.shape[:2]
        startx = w//2-(cropx//2)
        starty = h//2-(cropy//2)
        return img[starty:starty+cropy, startx:startx+cropx]

    def resize_down_to_1600_max_dim(self, image):
        h, w = image.shape[:2]
        if (h < 1600 and w < 1600):
            return image

        new_size = (1600 * w // h, 1600) if (h > w) else (1600, 1600 * h // w)
        return cv2.resize(image, new_size, interpolation = cv2.INTER_LINEAR)

    def resize_to_256_square(self, image):
        h, w = image.shape[:2]
        return cv2.resize(image, (256, 256), interpolation = cv2.INTER_LINEAR)

    def update_orientation(self, image):
        exif_orientation_tag = 0x0112
        if hasattr(image, '_getexif'):
            exif = image._getexif()
            if (exif != None and exif_orientation_tag in exif):
                orientation = exif.get(exif_orientation_tag, 1)
                # orientation is 1 based, shift to zero based and flip/transpose based on 0-based values
                orientation -= 1
                if orientation >= 4:
                    image = image.transpose(Image.TRANSPOSE)
                if orientation == 2 or orientation == 3 or orientation == 6 or orientation == 7:
                    image = image.transpose(Image.FLIP_TOP_BOTTOM)
                if orientation == 1 or orientation == 2 or orientation == 5 or orientation == 6:
                    image = image.transpose(Image.FLIP_LEFT_RIGHT)
        return image




class OpenVINOModel(Model):

    def __init__(self, target_device):
        super(OpenVINOModel, self).__init__()

        # These are set to the default names from exported models, update as needed.
        model_xml = 'model.xml'
        model_bin = 'model.bin'

        # Plugin initialization for specified device and load extensions library if specified
        # Set the desired device name as 'device' parameter. This sample support these 3 names: CPU, GPU, MYRIAD
        ie = IEPlugin(device=target_device, plugin_dirs='')

        # Read IR
        self.net = IENetwork(model=model_xml, weights=model_bin)

        self.input_blob = next(iter(self.net.inputs))
        self.out_blob = next(iter(self.net.outputs))
        self.net.batch_size = 1

        # Loading model to the plugin
        self.exec_net = ie.load(network=self.net)

    def predict(self, imageFile):
        start1 = time.time() 

        # Load from a file
        image = Image.open(imageFile)

        # Update orientation based on EXIF tags, if the file has orientation info.
        image = super().update_orientation(image)

        # Convert to OpenCV format
        image = super().convert_to_opencv(image)

        # If the image has either w or h greater than 1600 we resize it down respecting
        # aspect ratio such that the largest dimension is 1600
        image = super().resize_down_to_1600_max_dim(image)

        # We next get the largest center square
        h, w = image.shape[:2]
        min_dim = min(w,h)
        max_square_image = super().crop_center(image, min_dim, min_dim)

        # Resize that square down to 256x256
        augmented_image = super().resize_to_256_square(max_square_image)

        # Get the input size of the model
        n, c, h, w = self.net.inputs[self.input_blob].shape

        # Crop the center for the specified network_input_Size
        augmented_image = super().crop_center(augmented_image, w, h)
        frame = augmented_image

        #
        augmented_image = augmented_image.transpose((2, 0, 1))

        images = np.ndarray(shape=(n, c, h, w))
        images[0] = augmented_image

        start2 = time.time() 
        predictions = self.exec_net.infer(inputs={self.input_blob: images})
        infer_time = time.time() - start2

        # Print the highest probability label
        predictions = predictions[self.out_blob]
        highest_probability_index = predictions[0].argsort()[-1:][::-1]
        
        if highest_probability_index[0] == 1 and predictions[0][0] > 1e-12:
            print("人間class1")
            score = predictions[0]
            send_message = "あなたはゴリラ係数低いです。\nもし、上げたい場合は毎日３本はバナナを食べるといいでしょう。"
        
        elif highest_probability_index[0] == 1 and predictions[0][0] > 1e-6:
            print("人間class2")
            score = predictions[0]
            send_message = "あなた、もしかして先祖はゴリラなのかもしれません(嘘)"
        
        elif highest_probability_index[0] == 1:
            print("人間class3")
            score = predictions[0]
            send_message = "もしかしてあなたの前世はゴリラなのでは？"
        
        elif highest_probability_index[0] == 0 and predictions[0][1] > 1e-12:
            print("ゴリラclass1")
            score = predictions[0]
            send_message = "あなたは生粋のゴリラですね\n伝説のゴリラになれるでしょう！"
        elif highest_probability_index[0] == 0 and predictions[0][1] > 1e-6:
            print("ゴリラclass2")
            score = predictions[0]
            send_message = "ゴリラ係数が少し低めです\nもっと極めたいのならばジャングルを駆けまわり周りのゴリラを助けてあげましょう！"
        
        elif highest_probability_index[0] == 0:
            print("ゴリラclass3")
            score = predictions[0]
            send_message = "あなたは、人よりのゴリラです！"
        

        return score,send_message 

def download_img(url, file_name):
    r = requests.get(url, stream=True)
    if r.status_code == 200:
        with open(file_name, 'wb') as f:
            f.write(r.content)

def run_inference(target_device='CPU'):
    model = OpenVINOModel('CPU')
    file_list = glob.glob("images/*.png")
    img_path = random.choice(file_list)
    
    return model.predict(img_path)

TOKEN = 'ここはbotのトークンを入れる'
client = discord.Client()

# 起動時に動作する処理
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('ログインしました')

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return

    # 話しかけられたかの判定
    if message.content.startswith('/pic'):
        #ここでdiscordからとってきたとき様々な情報がある
        juge_img = str(message.attachments[0])

        #その情報を一つ一つ分割してそれをリストの一つの要素にする
        juge_img_cre = juge_img.split(' ')
        
        #今はurlの内容が欲しいのでurlのいらない所を削りそのurlをget_imgと定義する
        get_img = juge_img_cre[3].lstrip("url='").rstrip("'>")
        download_img(get_img, "images/image.png")
        
        deta = run_inference(target_device='CPU')

        await message.channel.send("あなたのゴリラ係数は"+str(deta[0][0])+"\n"+deta[1])

client.run(TOKEN)
```
だいたいは参考にさせて頂いたサイトを使わせて頂きました。
##苦戦したところ
discord bot を作るのが初めてて何も分からなかったが、特に苦戦したのは画像が送られてきた時にどう判定するかと、その画像を保存して、どうモデルに渡すかという所です。

まず、送られてきた判定は画像にコメントを付けて送る事で解決しました。

そして、画像を保存する所が少し大変でした。
まず、参考にしたサイトをみて**download_img**を作りました。
参考にさせていたサイトと開発環境の違いかサイト道理動かなかったため、少し変えて作りました。

#OpenVINO
OpenVINOはIntel様が、出してるCPUで機械学習などの処理をするものです。
今回授業で、使ったのですがめちゃくちゃ速さが変わりました、最初はそんなに早くなくてもいいかなと思いましたが、静止画出なく動画とかした時にこれにすると、ラグが少なくいいと言われて使ってみたいとおもったので、今回使わせて頂きました
#感想
私はAIに関する事が興味があり、専門学校に入りました。最初はプログラミングの基礎をやっていたのでAIなど、あまり出来なかったので簡単なのを作ってみようと初めました

custom visionを使うと簡単にモデルが出来てしまうし、discord botを作るのもそんなに難しくなく、出来ました！

これからは、自分でモデルを作って色々改善しながら面白いものをつくっていきたい！

余談ですが、税金の申告が家族で必要で時間ないから聞いてきてくれと言われて税務署に来たのですが、なんと、**100分**待ちですよ、ディズニーランドの人気アトラクション並ですよ。がんばります。