[PYTHON] Lassen Sie uns die Super Resolution API mithilfe der Google Cloud Platform veröffentlichen

Einführung

Dieser Artikel ist [hier](https://aiotadiary.wp.xdomain.jp/2020/03/01/google-cloud-platform%E3%82%92%E4%BD%BF%E3%81%A3% E3% 81% A6% E8% B6% 85% E8% A7% A3% E5% 83% 8F% E5% BA% A6% E5% 8C% 96api% E3% 82% 92% E5% 85% AC% E9% 96% 8B% E3% 81% 97% E3% 81% A6% E3% 81% BF% E3% 82% 8B /) Dies ist eine Neufassung des Blog-Artikels für Qiita. Bitte schauen Sie sich auch diesen an.   Dieses Mal möchte ich die API mithilfe der Google Cloud Platform veröffentlichen, die allgemein als GCP bekannt ist. Ich habe GCP vor einiger Zeit verwendet, daher möchte ich diesmal daran denken und die Erfahrung machen, den Dienst selbst bereitzustellen. Die API ist übrigens ein Super-Resolution-Modell, das das zuvor implementierte ESPCN verwendet. Was ich dieses Mal ansprechen möchte, ist eine Funktion namens Cloud Run in GCP.

Registrierung bei GCP

Um sich für GCP zu registrieren, können Sie normalerweise auf den Google-Leitfaden oder andere Artikel verweisen. Es ist kein Problem, wenn Sie der Startanleitung folgen. Der folgende Artikel wird hilfreich sein. GCP (GCE), um von nun an zu beginnen, verwenden Sie den freien Frame sicher Grundsätzlich registrieren Sie sich in der folgenden Reihenfolge.

  1. Melden Sie sich bei GCP an  image.png
  2. Klicken Sie oben auf dem Bildschirm auf die Schaltfläche "Für kostenlose Testversion registrieren"  image.png
  3. Geben Sie die Informationen gemäß den angezeigten Anweisungen ein  image.png Hier geben Sie Informationen wie eine Kreditkarte ein. Sie können jedoch sicher sein, dass diese nicht ohne Erlaubnis zurückgezogen werden, es sei denn, Sie legen ein bezahltes Konto fest.

Abschluss der Registrierung

Die Registrierung ist jetzt abgeschlossen. Mit der kostenlosen GCP-Stufe können Sie kostenlos eine GCE-Instanz von f1-micro erstellen. Verwenden Sie sie daher bitte. Übrigens können Sie in der kostenlosen Testversion den Service im Wert von 300 $ für 12 Monate kostenlos nutzen, sodass Sie möglicherweise verschiedene Dinge ausprobieren möchten.

Verwenden Sie Cloud Run

Cloud Run ist ein Dienst, mit dem Sie Docker-Container bereitstellen können, und der den Dienst im Wesentlichen mithilfe des in die Container-Registrierung hochgeladenen Docker-Images bereitstellt. Derzeit scheint es eine Möglichkeit zu geben, den Container zu erstellen und bereitzustellen, der durch das Ziehen nach Github ausgelöst wird. Diesmal scheint dies jedoch etwas schwierig zu sein. Daher möchte ich diesmal das lokal erstellte Image hochladen.

Installieren Sie den Befehl gcloud

Da ich einen Mac verwende, ist die folgende Erklärung für niemanden außer Mac hilfreich. Laden Sie zunächst das folgende Archiv herunter.

google-cloud-sdk-245.0.0-darwin-x86_64.tar.gz

Führen Sie dann den folgenden Befehl aus.

$ ./google-cloud-sdk/install.sh 

Initialisieren Sie als Nächstes das SDK.

$ gcloud init

Möchten Sie sich danach anmelden? Es wird eine Meldung angezeigt, die besagt, dass Sie Y eingeben müssen. Wählen Sie danach das Projekt aus, zu dem eine Verbindung hergestellt werden soll. Stellen Sie jedoch sicher, dass nur ein Projekt ausgewählt wird, wenn es vorhanden ist. Danach können Sie den Befehl verwenden, wenn Sie ihn entsprechend eingeben. ↓ ist die offizielle Einrichtungsmethode.

Schnellstart für macOS Schnellstart für Windows Schnellstart für Linux

Erstellen Sie ein Docker-Image

Hier werde ich das zuletzt implementierte ESPCN-Modell verwenden und den Code verwenden, der die Auflösung verdoppelt und als API ausgibt. Ich benutze Flask und Gunicorn.

Als Ablauf der gesamten Operation

  1. Dekodieren Sie die base64 des Eingabebildes aus der Anfrage json
  2. Geben Sie das decodierte Bild in die Superauflösungsfunktion ein
  3. Codieren Sie in base64 und antworten Sie

So einfach ist das. Erstellen Sie zunächst ein Git-Repository für die Espcn-API (Klicken Sie hier, um das Repository aufzurufen). Die Verzeichnisstruktur sieht wie folgt aus.

ESPCN-API
 ├model
 │ └pre_trained_mode.pth
 ├api.py
 ├requirements.txt
 ├Dockerfile
 └networks.py
test
 ├test.py
 └test.png

Testcode

Beginnen wir mit dem Testcode. Im Test müssen zwei Dinge überprüft werden.

・ Eine Antwort wird zurückgegeben (status_code ist 200) ・ Die Größe des Bildes wurde verdoppelt   Wir werden die Operation implementieren, um diese zu bestätigen.

import sys
import requests
import json
from io import BytesIO
from PIL import Image
import base64

def image_file_to_base64(file_path):
    with open(file_path, "rb") as image_file:
        data = base64.b64encode(image_file.read())
    return 'data:image/png;base64,' + data.decode('utf-8')

def base64_to_img(img_formdata):
    img_base64 = img_formdata.split(',')[1]
    input_bin = base64.b64decode(img_base64)
    with BytesIO(input_bin) as b:
        img = Image.open(b).copy().convert("RGB")
    return img

if __name__ == "__main__":
    source_image_path = "test.png "
    source_image = Image.open(source_image_path)
    source_width, source_height = source_image.size
    print("source_width :", source_width)
    print("source_height :", source_height)
    host_url = "http://0.0.0.0:8000"
    data = {"srcImage":image_file_to_base64(source_image_path)}
    json_data = json.dumps(data)

    response = requests.post(host_url, json_data, headers={'Content-Type': 'application/json'})

    assert response.status_code == 200, "validation error status code should be 200"

    res_json = response.json()
    
    res_image = base64_to_img(res_json["sresoImage"])
    sreso_width, sreso_height = res_image.size
    print("sreso_width :", sreso_width)
    print("sreso_height :", sreso_height)
    assert sreso_width == source_width * 2 and sreso_height == source_height * 2 , \
        "validation error image size should be 2 times of input image"
    res_image.show()
    print("OK")

Haupt code

Als nächstes werden wir die Haupt-api.py implementieren. Zunächst haben wir das Grundgerüst der gesamten Implementierung erstellt. Der Inhalt der Funktion ist überhaupt nicht implementiert.

from flask import Flask, request, jsonify
from networks import Espcn
import os
from io import BytesIO
import base64
from torchvision import transforms
from torch import load
import torch
import json
from PIL import Image

device = "cpu"
net = Espcn(upscale=2)
net.load_state_dict(torch.load(opt.model_path, map_location="cpu"))
net.to(device)
net.eval()

def b64_to_PILImage(b64_string):
    """
    process convert base64 string to PIL Image
    input: b64_string: base64 string : data:image/png;base64,{base64 string}
    output: pil_img: PIL Image
    """
    pass

def PILImage_to_b64(pil_img):
    """
    process convert PIL Image to base64
    input: pil_img: PIL Image
    output: b64_string: base64 string : data:image/png;base64,{base64 string}
    """
    pass

def expand(src_image, model=net, device=device):
    pass

@app.route("/", methods=["POST"])
def superResolution():
    pass

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=os.environ.get("PORT", 8000))

Ich denke an eine Struktur, in der die Erweiterungsfunktion die Haupt-Superauflösung ausführt und sie in der SuperResolution ausführt, die für die Anforderung ausgeführt wird. Durch die Definition des Modells, das für die Superauflösung außerhalb der Funktion verwendet wird, ist es meines Erachtens möglich, die Probleme beim Laden des Modells zu vermeiden, wenn der Worker mehrere Anforderungen verarbeitet.

Implementieren Sie also zunächst zwei ~ to ~ -Funktionen, die eingegeben und ausgegeben werden.

def b64_to_PILImage(b64_string):
    """
    process convert base64 string to PIL Image
    input: b64_string: base64 string : data:image/png;base64,{base64 string}
    output: pil_img: PIL Image
    """
    b64_split = b64_string.split(",")[1]
    b64_bin = base64.b64decode(b64_split)
    with BytesIO(b64_bin) as b:
        pil_img = Image.open(b).copy().convert('RGB')
    return pil_img

def PILImage_to_b64(pil_img):
    """
    process convert PIL Image to base64
    input: pil_img: PIL Image
    output: b64_string: base64 string : data:image/png;base64,{base64 string}
    """
    with BytesIO() as b:
        pil_img.save(b, format='png')
        b.seek(0)
        img_base64 = base64.b64encode(b.read()).decode('utf-8')
    img_base64 = 'data:image/png;base64,' + img_base64
    return img_base64

Ich hatte Schwierigkeiten mit BytesIO, weil es unerwartet schwierig war. Als nächstes folgen die Erweiterungsfunktion und die Hauptfunktion superResolution.

def tensor_to_pil(src_tensor):
    src_tensor = src_tensor.mul(255)
    src_tensor = src_tensor.add_(0.5)
    src_tensor = src_tensor.clamp_(0, 255)
    src_tensor = src_tensor.permute(1, 2, 0)
    src_tensor = src_tensor.to("cpu", torch.uint8).numpy()
    return Image.fromarray(src_tensor)


def expand(src_image, model=net, device=device):
    src_tensor = transforms.ToTensor()(src_image).to(device)
    if src_tensor.dim() == 3:
        src_tensor = src_tensor.unsqueeze(0)
    
    srezo_tensor = model(src_tensor).squeeze()
    srezo_img = tensor_to_pil(srezo_tensor)
    return srezo_img

@app.route("/", methods=["POST"])
def superResolution():
    req_json = json.loads(request.data)
    src_b64 = req_json["srcImage"]

    # main process
    src_img = b64_to_PILImage(src_b64)
    srezo_img = expand(src_img)
    srezo_b64 = PILImage_to_b64(srezo_img)

    results = {"sresoImage":srezo_b64}

    return jsonify(results)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=os.environ.get("PORT", 8000))

Zuerst wollte ich es in zwei Funktionen einpassen, aber die Konvertierung von torch.tensor zu PIL Image wurde bei der Verwendung von transforms.ToPILImage merkwürdig, daher habe ich beschlossen, es separat zu definieren. Hat. In Bezug auf das Argument zum Zeitpunkt des letzten app.run scheint es außerdem erforderlich zu sein, die Umgebungsvariable PORT zu lesen, da jedem Docker-Container aufgrund der Spezifikationen des Cloud-Laufs automatisch ein anderer Port zugewiesen wird.

Erstellen Sie nun diese lokal überprüften Vorgänge als Docker-Image.

$ docker build -t gcr.io/[project id]/espcn-api:0 .

Laden Sie dann das Bild mit dem Befehl gcloud in die Containerregistrierung hoch.

$ gcloud docker -- push gcr.io/[project id]/espcn-api:0

Sie können das Bild tatsächlich in der Container-Registrierung auf GCP sehen. image.png Stellen Sie dann das hochgeladene Image bereit.

$ gcloud beta run deploy SR-API --image=gcr.io/[project id]/espcn-api:0 

Der Teil, in dem Sie nach der Bereitstellung die SR-API eingeben, ist der Name des Dienstes, sodass Sie ihn nach Belieben hinzufügen können. Es scheint auch, dass die Beta-Komponente installiert wird, wenn sie zum ersten Mal ausgeführt wird. Wenn Sie zu diesem Zeitpunkt nicht auf der Konsole eingeben (--platform verwaltet)

 [1] Cloud Run (fully managed)
 [2] Cloud Run for Anthos deployed on Google Cloud
 [3] Cloud Run for Anthos deployed on VMware
 [4] cancel
Please enter your numeric choice: _

Sie werden aufgefordert, 1 einzugeben. Geben Sie also 1 ein. Es wird schwierig sein, kostenlos zu arbeiten, wenn es nicht vollständig verwaltet wird. Dann werden Sie nach der Region gefragt.

 [1] asia-east1
 [2] asia-northeast1
 [3] europe-north1
 [4] europe-west1
 [5] europe-west4
 [6] us-central1
 [7] us-east1
 [8] us-east4
 [9] us-west1
 [10] cancel
Please enter your numeric choice: _

Hier wählen wir "us- *". In Cloud Run ist das Downlink-Netzwerk in Nordamerika bis zu 1 GB kostenlos. Wenn Sie dies also über Cloud-Funktionen aufrufen (da Cloud-Funktionen das Downlink-Netzwerk überall bis zu 5 GB kostenlos ist), können Sie es fast kostenlos verwenden.

Danach werden Sie gefragt, ob Sie nicht authentifizierten Zugriff zulassen möchten. Im Moment besteht der Zweck darin, ihn zu verschieben. Setzen Sie ihn also auf "Ja". Wenn Sie wie oben eingeben, wird der Dienst bereitgestellt und die folgende Nachricht ausgegeben.

Deploying container to Cloud Run service [espcn-api] in project [studied-brace-261908] region [us-central1]
✓ Deploying new service... Done.                                                                                                                       
  ✓ Creating Revision...                                                                                                                               
  ✓ Routing traffic...                                                                                                                                 
  ✓ Setting IAM Policy...                                                                                                                              
Done.                                                                                                                                                  
Service [espcn-api] revision [espcn-api-00001-guj] has been deployed and is serving 100 percent of traffic at https://espcn-api-~~~-uc.a.run.app

Schließlich wird eine Meldung angezeigt, in der angegeben ist, wo und wo die URL bereitgestellt wurde. Als ich hier versuchte, mit test.py eine Anfrage zu stellen, konnte ich bestätigen, dass die Antwort ordnungsgemäß zurückgegeben wurde.

Es gab eine Sache, die zu diesem Zeitpunkt stecken blieb, aber als ich die Anfrage zum ersten Mal stellte, wurde keine Antwort zurückgegeben. Wenn Sie also das Cloud Run-Protokoll überprüfen,

Memory limit of 244M exceeded with 272M used. Consider increasing the memory limit, see https://cloud.google.com/run/docs/configuring/memory-limits

Ich habe eine Nachricht erhalten, dass nicht genügend Speicher vorhanden ist. Es scheint, dass der zu verwendende Speicher im Cloud-Lauf festgelegt wird und die Größe des Speichers, der angegeben werden kann, 128 MB ~ 2 GB beträgt und standardmäßig 256 MB zugewiesen sind. Da dieser Fehler bei einem 512 x 512-Bild aufgetreten ist, kann er anscheinend durch Zuweisen von 512 MB usw. behoben werden. Wenn Sie jedoch zu viel Speicher zuweisen, wird der freie Frame anscheinend bald überschritten. Verwenden Sie übrigens den folgenden Befehl, wenn Sie den der Anwendung zugewiesenen Speicher ändern möchten.

$ gcloud beta run services update [service name] --memory 512M

Am Ende

Wir haben eine API erstellt und auf Cloud Run veröffentlicht. Im aktuellen Status kann von überall auf diese API zugegriffen werden, und sie ist nicht sicherheits- oder brieftaschenfreundlich. Nachdem Sie den Zugriff auf Cloud Run aus GCP heraus eingeschränkt haben, verwenden Sie dazu die Cloud-Funktionen. Ich möchte zu einer Struktur wechseln, an die Anfragen gesendet werden. Ich möchte eines Tages auf die kontinuierliche Bereitstellung eingehen.

Recommended Posts

Lassen Sie uns die Super Resolution API mithilfe der Google Cloud Platform veröffentlichen
Ich habe versucht, die Google Cloud Vision-API zu verwenden
[GoogleCloudPlatform] Verwenden Sie die Google Cloud-API mit der API-Clientbibliothek
Verwendung der Google Cloud Translation API
Fordern Sie die Herausforderungen von Cyma weiterhin mit dem OCR-Dienst der Google Cloud Platform heraus
Sprachtranskriptionsverfahren mit Python und Google Cloud Speech API
Anzeigen von Wettervorhersagen auf M5Stack + Google Cloud Platform
Versuchen Sie es mit der Twitter-API
Versuchen Sie es mit der Twitter-API
Versuchen Sie, Lebensmittelfotos mithilfe der Google Cloud Vision-API zu beurteilen
Versuchen Sie es mit der PeeringDB 2.0-API
Ich habe die Google Cloud Vision-API zum ersten Mal ausprobiert
Die Geschichte des Erstellens einer Datenbank mithilfe der Google Analytics-API
Sammeln wir automatisch Unternehmensinformationen (XBRL-Daten) mithilfe der EDINET-API (4/10).
Streamen Sie die Spracherkennung mithilfe der gRPC-API von Google Cloud Speech mit Python3 auf dem Mac!
Bis Sie die Google Cloud Vision-API ausprobieren (Erkennung schädlicher Bilder)
Drucken Sie PDF mit Google Cloud Print. (GoogleAPI)
[Python] Klicken Sie auf die Google Übersetzungs-API
Lassen Sie uns die Karte mit der Grundkarte anzeigen
Ich habe versucht, die checkio-API zu verwenden
Zeigen Sie das Ergebnis der Videoanalyse mit der Cloud Video Intelligence API von Colaboratory an.
Bei der Einführung der Google Cloud Vision-API in Schienen habe ich die Dokumentation befolgt.
Google Cloud Speech API im Vergleich zu Amazon Transcribe
Google Cloud Vision API-Beispiel für Python
Versuchen Sie es mit der Wunderlist-API in Python
Streaming-Spracherkennung mit der Google Cloud Speech API
Versuchen Sie, die Kraken-API mit Python zu verwenden
Versuchen Sie, Python mit Google Cloud-Funktionen zu verwenden
Verwenden Sie die Google Cloud Vision-API von Python
Erstellen Sie eine Anwendung mit der Spotify-API
Holen Sie sich Urlaub mit der Google Kalender-API
Bildersammlung mit der benutzerdefinierten Such-API von Google
Spielen Sie mit Dajare mithilfe der COTOHA-API
Erstellen einer Google-Tabelle mit der Python / Google Data-API
Zeichnen Sie benutzerdefinierte Ereignisse mit der Shotgun-API auf
Ich habe versucht, die BigQuery-Speicher-API zu verwenden
Konvertieren der cURL-API in ein Python-Skript (mithilfe des IBM Cloud-Objektspeichers)