PythonのFlaskで画像データをreturnし、HTMLのcanvas要素へ描写する


PythonのFlaskで取得した画像データをHTMLのcanvas要素へ描写します。

やりたいこと

Google Cloud Storageに保存した画像データをCloud functionsで読み出して、手元のHTMLのcanvas要素に描写します。Cloud functionsでは画像イメージがまずはblob形式で読み出されます。これをpngやjpgに変換してflaskでreturnして、canvas要素で描写します。

Cloud functionsでStorageのファイルを読み込んでダウンロードする

ここは公式ドキュメントが充実しているので迷いませんでした。

main.py
from google.cloud import storage

def download_blob(bucket_name, source_blob_name, destination_file_name):
    """Downloads a blob from the bucket."""
    # bucket_name = "your-bucket-name"
    # source_blob_name = "storage-object-name"
    # destination_file_name = "local/path/to/file"

    storage_client = storage.Client()

    bucket = storage_client.bucket(bucket_name)
    #bucket_nameとは、Storageに作った格納先の名前。手動で設定したもの

    blob = bucket.blob(source_blob_name)#要するにファイル名
    blob.download_to_filename(destination_file_name)

    print(
        "Blob {} downloaded to {}.".format(
            source_blob_name, destination_file_name
        )
    )

実行するとローカルにファイルがダウンロードされます。

単体ファイルとしてダウンロードさせるのではなく、flaskでjpgとしてreturnしてダウンロードする

この辺で詰まり始めた。要するに、blobをbytesで読んで、それにheaderなどをつけて画像としてreturnする。その際、Flaskのsend_fileというモジュールを使う。いや、これわからんでしょ。

なお、今回はホスティングはGCPのCloud functionsでやっているので、app.run()などはありません。ただこれをホスティングすると、あるURLを叩いたらdownload_blob()の関数が動きます。

main.py
from google.cloud import storage
import io
from flask import send_file

def download_blob(request):
    """Downloads a blob from the bucket."""
    bucket_name = "your-bucket-name"
    source_blob_name = "storage-object-name"

    storage_client = storage.Client()

    bucket = storage_client.bucket(bucket_name)
    #bucket_nameとは、Storageに作った格納先の名前。手動で設定したもの

    blob = bucket.blob(source_blob_name)#要するにファイル名
    image_binary= blob.download_as_bytes()
    return send_file(
            io.BytesIO(image_binary),
            mimetype='image/jpeg',
            as_attachment=True,
            attachment_filename='%s.jpg' % source_blob_name)

Cloud functionsでのデプロイは下記を参考に。
https://qiita.com/NP_Systems/items/f0f6de899a84431685ff

ライブラリなどインストールしていたら下記を打つだけで、ローカルでエミュレートします。

teriminal.
functions-framework --target=hello  --port=8081

ホスティングしたURLを叩いたら、画像がダウンロードされます。でも単体ファイルとは違って、returnしています。これを使って、あとは受け取ったものをcanvasへ描写します

canvas要素への描写

ここもかなり詰まりました。こんなシンプルなことなのに。
このサイト本当に助かりました。
https://stackoverflow.com/questions/57014217/putting-an-image-from-flask-to-an-html5-canvas

phpですがhtmlでも同じようなものです。

index.php
<?php get_header(); ?>
<main>
<article>
  <section>
       <input type="button" value="Start" onclick="main();"/>
        <canvas></canvas>
  </section>
</article>
</main>

<script language="javascript" type="text/javascript">

    function main(){
      var canvas = document.getElementsByTagName('canvas');
      var ctx = canvas[0].getContext('2d');

      var img = new Image();
      img.src = 'http://0.0.0.0:8081';//FlaskでホスティングしたURL

      img.onload = function() {
        img.style.display = 'none'; // ようわからん
        console.log('WxH: ' + img.width + 'x' + img.height)

        ctx.drawImage(img, 0, 0);
        var imageData = ctx.getImageData(0, 0, img.width*2, img.height*2)

        for(x = 0 ; x < 100 ; x += 10) {
          for(y = 0 ; y < 100 ; y += 10) {
             ctx.putImageData(imageData, x, y);
          }
        }
      };    }
</script>
<?php get_footer(); ?>

感想

いや、こんなシンプルなことなのに詰まりまくった。blobやpngへの理解が浅い気がする。