MkItYs

MkItYs > ネットワークとサーバを作る > 

images

コンテナの各種メディアを、遠隔からあつかう(音声・画像・映像):Docker, ssh, rsync, sshfs, nginx, Tk (tkinter) , OpenCV, Jupyter Notebook

images

- 2022.10.19

遠隔のコンテナで生成された各種メディア(音声・画像・映像)を、手元で再生/表示するやり方ですーー大きく2コのパターンがあり、[1]そのままダイレクトに(ストリームで)再生/表示する方法、[2]ファイルに落として再生/表示する方法、を解説します。

関連


高機能のウィンドウシステム:X Window System (XQuartz) , Docker, OpenGL, Tcl/Tk, Xvfb, x11vnc

検証


ローカル
OS:macOS 11
ウィンドウシステム:XQuartz 2.8
プレイヤ/ビューア:Preview, VLC, Google Chrome
同期アプリ:rsync
マウント・アプリ:sshfs 2.10
リモート
OS:Ubuntu 20.04
コンテナ:Dcoker 20.10
ウィンドウシステム:X window
プレイヤ/ビューア:display
インタプリタ:Python 3.8
ライブラリ:OpenCV, PIL/Pillow, tkinter
ウェブサーバ:nginx

そのままダイレクトに → リモート側のリソースを使う:専用のプレイヤ/ビューワなど


リモート側にあるプレイヤ/ビューワ・アプリ(Xクライアント)を使い、ローカル側のXサーバで再生するやり方です:

送信:ストリーム
再生:リモートのリソース:画像:X window, Unix (Linux (Ubuntu)) , display
設置:リモート:コンテナ
$ apt install graphicsmagick-imagemagick-compat
利用:ローカル
$ ssh ${user_remote}@${server_remote} docker -i --rm ${image_other} ${command} | \
ssh -X ${user_remote}@${server_remote} docker -i --rm ${image} display -

※1
display は、PIL/Pillow がshow()関数から呼び出す、標準の画像ビューアのひとつです。

そのままダイレクトに → リモート側のリソースを使う:かんたんなスクリプトを書く


リモート側で再生するスクリプト(Xクライアント)を書き、ローカル側のXウィンドウサーバで再生するやり方です(メディアのストリームを標準入力から受け取り〜再生する、という形になります):

送信:ストリーム
再生:リモートのリソース:画像:X window, Unix, Python, PIL/Pillow, Tk (tkinter)
作成:リモート:コンテナ:imgwin_tkw.py
#!/usr/bin/python

import sys
import tkinter
from PIL import Image,ImageTk

window=tkinter.Tk()

pilimg=Image.open(sys.stdin.buffer)
tkwimg=ImageTk.PhotoImage(image=pilimg)

canvas=tkinter.Canvas(width=pilimg.width,height=pilimg.height)
canvas.place(x=0,y=0)
canvas.create_image(0,0,image=tkwimg,anchor=tkinter.NW)

window.title("")
window.geometry(str(pilimg.width) + "x" + str(pilimg.height))
window.mainloop()
利用:ローカル
$ ssh ${user_remote}@${server_remote} docker -i --rm ${image_other} ${command} | \
ssh -X ${user_remote}@${server_remote} docker -i --rm ${image} imgwin_tkw.py

送信:ストリーム
再生:リモートのリソース:画像:X window, Unix, Python, OpenCV[※2]
作成:リモート:コンテナ:imgwin_ocv.py
#!/usr/bin/python

import sys
import numpy
import cv2

strimg=sys.stdin.buffer.read()
arrimg=numpy.frombuffer(strimg,dtype=numpy.uint8)
matimg=cv2.imdecode(arrimg,cv2.IMREAD_COLOR)
cv2.imshow("",matimg)
cv2.waitKey(0)
cv2.destroyAllWindows()
利用:ローカル
$ ssh ${user_remote}@${server_remote} docker -i --rm ${image_other} ${command} | \
ssh -X ${user_remote}@${server_remote} docker -i --rm ${image} imgwin_ocv.py

※1
拡散モデルの画像生成フレームワークのDiffusers は、生成画像の形式にPIL を採用しています。なので、表示側もそれを使えば対称性があるので、コードが分かりやすくなるかもしれません。
※2
コードは短くなりますが、OpenCVは画像解析・画像処理のライブラリなので、大量の依存ライブラリがあります。なのでたんに画像を表示するためだけに使うなら、オーバースペックかもしれません。

そのままダイレクトに → ローカル側のリソースを使う


リモート側のメディアをそのまま流して、ローカル側のプレイヤ/ビューワで再生するやり方です。[※1]

送信:ストリーム
再生:ローカルのリソース:画像:Unix (BSD (macOS)) , Preview
利用:ローカル
$ ssh ${user_remote}@${server_remote} docker -i --rm ${image_other} ${command} | \
open -f -a Preview

※1
ssh とdockerコマンドを使い、リモート側のメディアを、ストリームとして標準出力〜標準入力に流し、ローカル側のプレイヤ/ビューワに送り込みます(なのでローカル側のプレイヤ/ビューワは、標準入力からの入力に対応している必要があります)。

ファイルに落として → ローカル側のリソースを使う:フォルダを同期する


コンテナと共有するリモートのフォルダを、ローカルのフォルダに同期させます:[※1]

送信:ファイル:同期:rsync
再生:ローカルのリソース
利用:ローカル
$ rsync --archive --delete --rsh ssh ${user_remote}@${server_remote}:${diretory_remote}/ ${directory_local}

※1
オプション「--delete」は、送信元と送信先を完全に同期させます(ないものは削除する)。なので同期の方向を誤ると、意図しない形でファイルが消えることがあります[要注意]。

ファイルに落として → ローカル側のリソースを使う:フォルダをマウントする


コンテナと共有するリモートのフォルダを、ローカルのフォルダにマウントします:[※1][※2]

送信:ファイル:マウント:sshfs
再生:ローカルのリソース
利用:ローカル
$ sshfs ${user_remote}@${server_remote}:${diretory_remote} ${directory_local}
$ umount ${d}

※1
これは遠隔のファイルが手元に同期されるタイミングが微妙なので、使い勝手がいいとはいえないかもしれません(とくにサイズが大きくなるファイルほど)。
※2
このアプリを開発していたNikratio氏がプロジェクトから手を引いたようで、後継を募集しています(2022.05):
https://github.com/libfuse/sshfs

ファイルに落として → ローカル側のリソースを使う:ウェブサーバとウェブブラウザを使う


あつかうメディアの種類が増えると、それにともなって(リモート側にせよローカル側にせよ)必要なプレイヤ/ビューア/スクリプトが増えていきます。

ウェブブラウザは、さまざまなメディアをあつえる、おそらくもっとも手近にあるプレイヤ/ビューアですーーコンテナを使えば、ウェブサーバも数ステップで導入できるので、プライベートでテンポラリな使い方をするなら、選択肢に入るかもしれません。[※1][※2]

送信:ファイル:ウェブサーバ:UNIX, nginx
再生:ローカルのリソース
設置:リモート
$ docker pull nginx:latest
利用:リモート
$ docker run -d --rm -p 8080:80 -v ${directory}:/usr/share/nginx/html --name ${container} nginx:latest
利用:ローカル
# 画像
$ open http://${server_remote}:8080/${file}.png
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome http://${server_remote}:8080/${file}.png

# 映像
$ open http://${server_remote}:8080/${file}.mp4
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome http://${server_remote}:8080/${file}.mp4
$ /Applications/VLC.app/Contents/MacOS/VLC http://${server_remote}:8080/${file}.mp4

※1
不特定多数に公開するサーバなら、セキュリティやパフォーマンスを考えなければなりませんーーしかしプライベートでテンポラリな利用に限定するなら、ある程度セキュリティをゆるくしてもかまない、といった考え方もあるかもしれません。
※2
とはいえ完全にオープンにしていると、アドレスやポートの番号が分かれば、不特定多数がすべてのファイルを閲覧できてしまいますーーみられるとマズイものがあるなら、最低限のアクセス制限(ダイジェスト認証)はかけておいた方が無難です。

ファイルに落として → ローカル側のリソースを使う:ウェブサーバとウェブブラウザを使う:Jupyter Notebook


フォルダ機能をもつウェブサーバアプリ(Jupyter Notebook, etc.)を使えば、コンテナと共有するリモートのフォルダに生成した画像などを、ローカルのウェブブラウザで表示できます:[※1]

設置:リモート:コンテナ
$ pip install jupyterlab
利用:リモート
$ docker run -d -p 8888:8888 --rm -v ${directory_host}:{$directory_container} --name ${container} ${image} jupyter lab --ip=0.0.0.0 --allow-root --NotebookApp.token=''
利用:ローカル
$ open http://${server_remote}:8888/lab

※1
この場合も、なんの対策もしなれば、ウェブサーバアプリはどこからでもアクセスできる状態になりますーーホスト側のポート番号を既定のものから変え、コンテナでパスワードやトークンを設定しておく、など、最低限の対策はしておいた方が無難です。