[PYTHON] Ich habe versucht, eine einfache Bilderkennungs-API mit Fast API und Tensorflow zu erstellen

Einführung

Normalerweise benutze ich Flask häufig, aber mein Bekannter sagte: "Schnelle API ist gut!", Also entschied ich mich für eine einfache Bilderkennungs-API. Da ich jedoch nicht viele japanische Artikel über FastAPI und ML gesehen habe, habe ich beschlossen, diesen Artikel anstelle eines Memos zu erstellen!

In diesem Artikel werden wir nach der Vorbereitung der Entwicklungsumgebung eine kurze Erläuterung des API-Servers und des Frontends geben.

Der gesamte diesmal verwendete Code wird auf Github veröffentlicht. ** (Die Ordnerstruktur der folgenden Implementierung wird unter der Annahme von Github beschrieben. Der Download des Beispielmodells wird auch in README.md beschrieben.) **

Was ist FastAPI?

Es ist eines der Python-Frameworks wie Flask.

Eine einfache Übersicht und eine Zusammenfassung der Verwendung finden Sie im folgenden Artikel. (Vielen Dank für Ihre Hilfe auch in diesem Artikel!)

https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9

Für diejenigen, die mehr wissen möchten, empfehlen wir die offiziellen Fast API-Tutorials!

https://fastapi.tiangolo.com/tutorial/

Über die Bilderkennung

Ich hatte diesmal keine Zeit, also werde ich es mit dem Modell von tensorflow.keras bauen!

Insbesondere werden wir ResNet50 verwenden, das von imagenet gelernt wurde, und daraus schließen, zu welcher der 1000 Klassen das Eingabebild gehört.

(Das Modell, das ich wirklich verwenden wollte, war nicht rechtzeitig, weil ich gerade Anerkennung lernte ...)

https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/keras?hl=ja

Entwicklungsumgebung

Mac OS X Mojave Python3.7.1(Anaconda)

Umgebung

Installieren Sie die erforderliche Python-Bibliothek.

$pip install tensorflow==1.15
$pip install fastapi
$pip install uvicorn

Da die folgenden Bedingungen erfüllt sind, installieren Sie auch die erforderlichen Bibliotheken. --Render index.html

$pip install Jinja
$pip install aiofiles
$pip install python-multipart
$pip install opencv-python

API-Server

Die Implementierung des API-Servers ist wie folgt.

# -*- coding: utf-8 -*-
import io
from typing import List

import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras.applications.resnet50 import decode_predictions
from fastapi import FastAPI, Request, File, UploadFile
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

#Vorbereitung des Bilderkennungsmodells
global model, graph
graph = tf.get_default_graph()
model = tf.keras.models.load_model("./static/model/resnet_imagenet.h5")

#Vorbereitung der schnellen API
app = FastAPI()

# static/js/post.Index js.Erforderlich, um von HTML aufzurufen
app.mount("/static", StaticFiles(directory="static"), name="static")

#Index unter Vorlagen gespeichert.Erforderlich, um HTML zu rendern
templates = Jinja2Templates(directory="templates")


def read_image(bin_data, size=(224, 224)):
    """Bild laden

    Arguments:
        bin_data {bytes} --Bildbinärdaten

    Keyword Arguments:
        size {tuple} --Bildgröße, deren Größe Sie ändern möchten(default: {(224, 224)})

    Returns:
        numpy.array --Bild
    """
    file_bytes = np.asarray(bytearray(bin_data.read()), dtype=np.uint8)
    img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, size)
    return img


@app.post("/api/image_recognition")
async def image_recognition(files: List[UploadFile] = File(...)):
    """Bilderkennungs-API

    Keyword Arguments:
        files {List[UploadFile]} --Hochgeladene Dateiinformationen(default: {File(...)})

    Returns:
        dict --Inferenzergebnis
    """
    bin_data = io.BytesIO(files[0].file.read())
    img = read_image(bin_data)
    with graph.as_default():
        pred = model.predict(np.expand_dims(img, axis=0))
        result_label = decode_predictions(pred, top=1)[0][0][1]
        return {"response": result_label}


@app.get("/")
async def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Erhalten Sie Daten von der Rezeption

@app.post("/api/image_recognition")
async def image_recognition(files: List[UploadFile] = File(...)):
    """Bilderkennungs-API

    Keyword Arguments:
        files {List[UploadFile]} --Hochgeladene Dateiinformationen(default: {File(...)})

    Returns:
        dict --Inferenzergebnis
    """
    bin_data = io.BytesIO(files[0].file.read())
    img = read_image(bin_data)
    with graph.as_default():
        pred = model.predict(np.expand_dims(img, axis=0))
        result_label = decode_predictions(pred, top=1)[0][0][1]
        return {"response": result_label}

Dieses Mal verwenden wir die Fast API Upload File, um das POSTed-Image zu erhalten.

bin_data = io.BytesIO(files[0].file.read())

Da nur eine Datei POSTed ist, wird sie als files [0] festgelegt, und da sie von der Vorderseite im BASE64-Format übergeben wird, wurde sie auf der API-Seite in ein Bytes-Array konvertiert.

Daten in Bild konvertieren

def read_image(bin_data, size=(224, 224)):
    """Bild laden

    Arguments:
        bin_data {bytes} --Bildbinärdaten

    Keyword Arguments:
        size {tuple} --Bildgröße, deren Größe Sie ändern möchten(default: {(224, 224)})

    Returns:
        numpy.array --Bild
    """
    file_bytes = np.asarray(bytearray(bin_data.read()), dtype=np.uint8)
    img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = cv2.resize(img, size)
    return img

Mit Hilfe von opencv konvertiert es ein Byte-Array in ein uint8-Image. Da das Standardformat von opencv BGR ist, habe ich es zu diesem Zeitpunkt in RGB konvertiert und die Größe geändert.

Schließen

global model, graph
graph = tf.get_default_graph()
model = tf.keras.models.load_model("./static/model/resnet_imagenet.h5")

...

with graph.as_default():
        pred = model.predict(np.expand_dims(img, axis=0))
        result_label = decode_predictions(pred, top=1)[0][0][1]

Ich habe resnet_imagenet.h5 im Voraus erstellt und oben in der Datei gelesen. Der Inferenzprozess selbst wird mit der Vorhersagefunktion abgeleitet, indem der Kontext in diesem Thread auf das TensorFlow-Diagramm festgelegt wird, das global mit graph.as_default () festgelegt wurde.

Da wir dieses Mal ResNet50 von tf.keras verwenden, verwenden wir decode_predictions, um das Ergebnis der Vorhersage in ein Label zu konvertieren und das Inferenzergebnis zu erhalten.

Ich denke, dass andere Modelle und selbst erstellte Modelle wie diese Implementierung verwendet werden können, indem die .h5-Datei irgendwo im Projektverzeichnis gespeichert und_model geladen wird.

Frontmontage

Ich habe dies als Referenz verwendet. (Vielen Dank!)

https://qiita.com/katsunory/items/9bf9ee49ee5c08bf2b3d

<html>
<head>
    <meta http-qeuiv="Content-Type" content="text/html; charset=utf-8">
    <title>Fastapi Bilderkennungstest</title>
    <script src="//code.jquery.com/jquery-2.2.3.min.js"></script>
    <script src="/static/js/post.js"></script>
</head>

<body>

<!--Dateiauswahlschaltfläche-->
<div style="width: 500px">
  <form enctype="multipart/form-data" method="post">
    <input type="file" name="userfile" accept="image/*">
  </form>
</div>

<!--Bildanzeigebereich-->
<canvas id="canvas" width="0" height="0"></canvas>

<!--Startschaltfläche hochladen-->
<button class="btn btn-primary" id="post">Post</button>
<br>
<h2 id="result"></h2>
</body>
</html>

//Ändern Sie die Größe des Bildes und zeigen Sie es in HTML an
$(function () {
  var file = null;
  var blob = null;
  const RESIZED_WIDTH = 300;
  const RESIZED_HEIGHT = 300;

  $("input[type=file]").change(function () {
    file = $(this).prop("files")[0];

    //Dateiprüfung
    if (file.type != "image/jpeg" && file.type != "image/png") {
      file = null;
      blob = null;
      return;
    }

    var result = document.getElementById("result");
    result.innerHTML = "";

    //Bildgröße anpassen
    var image = new Image();
    var reader = new FileReader();
    reader.onload = function (e) {
      image.onload = function () {
        var width, height;

        //Passen Sie die Größe an die längere an
        if (image.width > image.height) {
          var ratio = image.height / image.width;
          width = RESIZED_WIDTH;
          height = RESIZED_WIDTH * ratio;
        } else {
          var ratio = image.width / image.height;
          width = RESIZED_HEIGHT * ratio;
          height = RESIZED_HEIGHT;
        }

        var canvas = $("#canvas").attr("width", width).attr("height", height);
        var ctx = canvas[0].getContext("2d");
        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(
          image,
          0,
          0,
          image.width,
          image.height,
          0,
          0,
          width,
          height
        );

        //Holen Sie sich base64-Bilddaten vom Canvas und erstellen Sie Blob für POST
        var base64 = canvas.get(0).toDataURL("image/jpeg");
        var barr, bin, i, len;
        bin = atob(base64.split("base64,")[1]);
        len = bin.length;
        barr = new Uint8Array(len);
        i = 0;
        while (i < len) {
          barr[i] = bin.charCodeAt(i);
          i++;
        }
        blob = new Blob([barr], { type: "image/jpeg" });
        console.log(blob);
      };
      image.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });

  //Wenn Sie auf die Schaltfläche zum Starten des Uploads klicken
  $("#post").click(function () {
    if (!file || !blob) {
      return;
    }

    var name,
      fd = new FormData();
    fd.append("files", blob);

    //POST an API
    $.ajax({
      url: "/api/image_recognition",
      type: "POST",
      dataType: "json",
      data: fd,
      processData: false,
      contentType: false,
    })
      .done(function (data, textStatus, jqXHR) {
          //Wenn die Kommunikation erfolgreich ist, geben Sie das Ergebnis aus
        var response = JSON.stringify(data);
        var response = JSON.parse(response);
        console.log(response);
        var result = document.getElementById("result");
        result.innerHTML = "Dieses Bild...「" + response["response"] + "Yanke";
      })
      .fail(function (jqXHR, textStatus, errorThrown) {
          //Gibt eine Fehlermeldung aus, wenn die Kommunikation fehlschlägt
        var result = document.getElementById("result");
        result.innerHTML = "Die Kommunikation mit dem Server ist fehlgeschlagen...";
      });
  });
});

POST wird mit Ajax an der Bilderkennungs-API ausgeführt und das Ergebnis wird angezeigt.

Funktionsprüfung

Infolgedessen funktioniert es so! demo.png

(Ich wollte die Rezeption etwas modischer machen ...)

abschließend

Ich habe eine Bilderkennungs-API erstellt, um die Fast-API zu studieren. Ich denke nicht, dass die Implementierung, die ich dieses Mal gemacht habe, die beste Vorgehensweise ist, aber ich bin froh, dass ich etwas machen konnte, das funktioniert.

Ich weiß nicht, welches Framework ich in Zukunft für die Arbeit verwenden soll, aber ich dachte, dass FastAPI relativ einfach zu verwenden ist und ich von Flask wechseln sollte.

** Last but not least danke an alle, die uns geholfen haben! ** ** **

Recommended Posts

Ich habe versucht, eine einfache Bilderkennungs-API mit Fast API und Tensorflow zu erstellen
Ich habe eine Web-API erstellt
Ich habe mit Jupyter eine einfache Bilderkennung versucht
Rubyist hat versucht, eine einfache API mit Python + Flasche + MySQL zu erstellen
Ich habe versucht, mit Selenium und Python einen regelmäßigen Ausführungsprozess durchzuführen
Ich habe ein ○ ✕ Spiel mit TensorFlow gemacht
Ich habe versucht, "Sakurai-san" LINE BOT mit API Gateway + Lambda zu erstellen
Ich habe versucht, Grad-CAM mit Keras und Tensorflow zu implementieren
Ich habe versucht, einen periodischen Prozess mit CentOS7, Selenium, Python und Chrome durchzuführen
Ich habe eine Burgsuch-API mit Elasticsearch + Sudachi + Go + Echo erstellt
Ich habe versucht, die Benutzeroberfläche neben Python und Tkinter dreiäugig zu gestalten
Ich habe mit PyQt einen einfachen Texteditor erstellt
Ich habe versucht, mit dem Seq2Seq-Modell von TensorFlow so etwas wie einen Chatbot zu erstellen
[5.] Ich habe versucht, mit Python ein bestimmtes Authenticator-ähnliches Tool zu erstellen
Ich habe versucht, Autoencoder mit TensorFlow zu implementieren
[2nd] Ich habe versucht, mit Python ein bestimmtes Authenticator-ähnliches Tool zu erstellen
Ich habe versucht, mit Python eine 2-Kanal-Post-Benachrichtigungsanwendung zu erstellen
Ich habe versucht, Bulls and Cows mit einem Shell-Programm zu erstellen
Ich habe versucht, eine ToDo-App mit einer Flasche mit Python zu erstellen
[4.] Ich habe versucht, mit Python ein bestimmtes Authenticator-ähnliches Tool zu erstellen
[1.] Ich habe versucht, mit Python ein bestimmtes Authenticator-ähnliches Tool zu erstellen
Ich habe versucht, Jojo mit LSTM ein seltsames Zitat zu machen
Ich habe versucht, mit Python + OpenCV eine Bildähnlichkeitsfunktion zu erstellen
Ich habe versucht, mit Go einen exklusiven Kontrollmechanismus zu erstellen
Ich habe versucht, mit Raspeye 4 (Python Edition) ein signalähnliches Signal zu erzeugen.
Ich habe versucht, Kanas handschriftliche Zeichenerkennung durchzuführen. Teil 2/3 Datenerstellung und Lernen
Ich habe versucht, einen URL-Verkürzungsdienst mit AWS CDK serverlos zu machen
Ich habe versucht, Bilder mit CIFAR-10 mit Keras-Learning- zu erkennen.
Ich habe versucht, Bilder von CIFAR-10 mit Keras-Bilderkennung zu erkennen.
Ich möchte ein Spiel mit Python machen
Als ich versuchte, eine VPC mit AWS CDK zu erstellen, konnte ich es aber nicht schaffen
Ich habe versucht, die Strichzeichnung mit Deep Learning aus dem Bild zu extrahieren
Ich habe versucht, DCGAN mit PyTorch zu implementieren und zu lernen
Versuchen Sie, ein einfaches Spiel mit Python 3 und iPhone zu erstellen
Ich habe versucht, mit VOICEROID2 automatisch zu lesen und zu speichern
Ich habe versucht, unsere Dunkelheit mit der Chatwork-API aufzudecken
Ich habe einen einfachen RPA für die Anmeldung mit Selen ausprobiert
Ich habe versucht, eine OCR-App mit PySimpleGUI zu erstellen
Ich habe versucht, die alternative Klasse mit Tensorflow zu finden
Ich habe mit Pytorch versucht, Bilder von "Moon and Suppon" zu erkennen (mit torchvision.datasets.ImageFolder, das from_from_directry of keras entspricht).
Ich habe versucht, mit Python Machine Learning ein Echtzeit-Modell zur Trennung von Tonquellen zu erstellen
[Mac] Ich möchte einen einfachen HTTP-Server erstellen, auf dem CGI mit Python ausgeführt wird
Bildverarbeitung mit Python (ich habe versucht, es in 0 und 1 Mosaikkunst zu binarisieren)
Ich habe versucht, mit AI kreative Kunst zu machen! Ich habe eine Neuheit programmiert! (Artikel: Creative Adversarial Network)
Ich habe versucht, mit Quantx eine Linie mit gleitendem Durchschnitt des Volumens zu implementieren
Ich habe versucht, die Entropie des Bildes mit Python zu finden
Ich habe versucht, mit Python faker verschiedene "Dummy-Daten" zu erstellen
Ich habe versucht, automatisch einen Bericht mit der Markov-Kette zu erstellen
Ich möchte einen Blog-Editor mit dem Administrator von Django erstellen
Ich möchte ein Klickmakro mit pyautogui (Wunsch) erstellen.
Ich habe versucht, Follow Management mit Twitter API und Python (einfach)