kuroの覚え書き

96の個人的覚え書き

Raspberry Pi でUSBカメラを使う(整理編)

まずは何はなくともV4L2

$ v4l2-ctl

General/Common options:
  --all              display all information available
  -C, --get-ctrl=<ctrl>[,<ctrl>...]
                     get the value of the controls [VIDIOC_G_EXT_CTRLS]
  -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]
                     set the value of the controls [VIDIOC_S_EXT_CTRLS]
  -D, --info         show driver info [VIDIOC_QUERYCAP]
  -d, --device=<dev> use device <dev> instead of /dev/video0
                     if <dev> starts with a digit, then /dev/video<dev> is used 
#複数のデバイスを接続していたりするときは明示的に選択する必要がある。1台のときは省略可。
  -e, --out-device=<dev> use device <dev> for output streams instead of the
                     default device as set with --device
                     if <dev> starts with a digit, then /dev/video<dev> is used
  -h, --help         display this help message
  --help-all         all options
  --help-io          input/output options
  --help-misc        miscellaneous options
  --help-overlay     overlay format options
  --help-sdr         SDR format options
  --help-selection   crop/selection options
  --help-stds        standards and other video timings options
  --help-streaming   streaming options
  --help-tuner       tuner/modulator options
  --help-vbi         VBI format options
  --help-vidcap      video capture format options
  --help-vidout      vidout output format options
  --help-edid        edid handling options
  -k, --concise      be more concise if possible.
  -l, --list-ctrls   display all controls and their values [VIDIOC_QUERYCTRL]
  -L, --list-ctrls-menus
		     display all controls and their menus [VIDIOC_QUERYMENU]
  -r, --subset=<ctrl>[,<offset>,<size>]+
                     the subset of the N-dimensional array to get/set for control <ctrl>,
                     for every dimension an (<offset>, <size>) tuple is given.
  -w, --wrapper      use the libv4l2 wrapper library.
  --list-devices     list all v4l devices
  --log-status       log the board status in the kernel log [VIDIOC_LOG_STATUS]
  --get-priority     query the current access priority [VIDIOC_G_PRIORITY]
  --set-priority=<prio>
                     set the new access priority [VIDIOC_S_PRIORITY]
                     <prio> is 1 (background), 2 (interactive) or 3 (record)
  --silent           only set the result code, do not print any messages
  --sleep=<secs>     sleep <secs>, call QUERYCAP and close the file handle
  --verbose          turn on verbose ioctl status reporting

接続しているカメラの情報取得1

$ v4l2-ctl --info
Driver Info (not using libv4l2):
	Driver name   : uvcvideo
	Card type     : Venus USB2.0 Camera: Venus USB2
	Bus info      : usb-3f980000.usb-1.1.2
	Driver version: 4.19.66
	Capabilities  : 0x84A00001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format


接続しているカメラの情報取得2
(--info)にプラス現在の解像度、FPSの設定値、その他のパラメータの設定可能範囲と現在の設定値が表示される。

$ v4l2-ctl --all
Driver Info (not using libv4l2):
	Driver name   : uvcvideo
	Card type     : Venus USB2.0 Camera: Venus USB2
	Bus info      : usb-3f980000.usb-1.1.2
	Driver version: 4.19.66
	Capabilities  : 0x84A00001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
	Width/Height      : 800/600
	Pixel Format      : 'MJPG'
	Field             : None
	Bytes per Line    : 0
	Size Image        : 960000
	Colorspace        : sRGB
	Transfer Function : Default
	YCbCr/HSV Encoding: Default
	Quantization      : Default
	Flags             : 
Crop Capability Video Capture:
	Bounds      : Left 0, Top 0, Width 800, Height 600
	Default     : Left 0, Top 0, Width 800, Height 600
	Pixel Aspect: 1/1
Selection: crop_default, Left 0, Top 0, Width 800, Height 600
Selection: crop_bounds, Left 0, Top 0, Width 800, Height 600
Streaming Parameters Video Capture:
	Capabilities     : timeperframe
	Frames per second: 15.000 (15/1)
	Read buffers     : 0
                     brightness (int)    : min=-10 max=10 step=1 default=0 value=-10
                       contrast (int)    : min=0 max=20 step=1 default=10 value=10
                     saturation (int)    : min=0 max=10 step=1 default=4 value=4
                            hue (int)    : min=-5 max=5 step=1 default=0 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=100 max=200 step=1 default=150 value=120
                           gain (int)    : min=32 max=48 step=1 default=32 value=32
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=6500 value=6500 flags=inactive
                      sharpness (int)    : min=0 max=10 step=1 default=4 value=5
         backlight_compensation (int)    : min=0 max=2 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=8 max=16384 step=1 default=512 value=512 flags=inactive

接続しているカメラの情報取得3
(--all)で見られるパラメータの設定可能範囲と現在の設定値だけが表示される。

$ v4l2-ctl --list-ctrls
                     brightness (int)    : min=-10 max=10 step=1 default=0 value=-10
                       contrast (int)    : min=0 max=20 step=1 default=10 value=10
                     saturation (int)    : min=0 max=10 step=1 default=4 value=4
                            hue (int)    : min=-5 max=5 step=1 default=0 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=100 max=200 step=1 default=150 value=120
                           gain (int)    : min=32 max=48 step=1 default=32 value=32
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=6500 value=6500 flags=inactive
                      sharpness (int)    : min=0 max=10 step=1 default=4 value=5
         backlight_compensation (int)    : min=0 max=2 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=8 max=16384 step=1 default=512 value=512 flags=inactive

接続しているカメラの情報取得4
(--list-ctrls)で見られるパラメータの設定可能範囲と現在の設定値で(menu)の項目の選択肢も含めて表示される。

$ v4l2-ctl --list-ctrls-menu
                     brightness (int)    : min=-10 max=10 step=1 default=0 value=-10
                       contrast (int)    : min=0 max=20 step=1 default=10 value=10
                     saturation (int)    : min=0 max=10 step=1 default=4 value=4
                            hue (int)    : min=-5 max=5 step=1 default=0 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=100 max=200 step=1 default=150 value=120
                           gain (int)    : min=32 max=48 step=1 default=32 value=32
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
				0: Disabled
				1: 50 Hz
				2: 60 Hz
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=6500 value=6500 flags=inactive
                      sharpness (int)    : min=0 max=10 step=1 default=4 value=5
         backlight_compensation (int)    : min=0 max=2 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
				1: Manual Mode
				3: Aperture Priority Mode
              exposure_absolute (int)    : min=8 max=16384 step=1 default=512 value=512 flags=inactive

接続しているカメラの情報取得5
カメラの対応しているビデオフォーマットが確認できる。

$ v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
	Index       : 0
	Type        : Video Capture
	Pixel Format: 'YUYV'
	Name        : YUYV 4:2:2

	Index       : 1
	Type        : Video Capture
	Pixel Format: 'MJPG' (compressed)
	Name        : Motion-JPEG

接続しているカメラの情報取得6
利用できる解像度をリストアップ

$ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Index       : 0
	Type        : Video Capture
	Pixel Format: 'YUYV'
	Name        : YUYV 4:2:2
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 352x288
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 176x144
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 160x120
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 1280x960
			Interval: Discrete 0.133s (7.500 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1280x1024
			Interval: Discrete 0.133s (7.500 fps)
			Interval: Discrete 0.200s (5.000 fps)
		Size: Discrete 1600x1200
			Interval: Discrete 0.200s (5.000 fps)
			Interval: Discrete 0.400s (2.500 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.050s (20.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.100s (10.000 fps)
			Interval: Discrete 0.200s (5.000 fps)

	Index       : 1
	Type        : Video Capture
	Pixel Format: 'MJPG' (compressed)
	Name        : Motion-JPEG
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 352x288
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 160x120
			Interval: Discrete 0.033s (30.000 fps)
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 1280x960
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
		Size: Discrete 1280x1024
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
		Size: Discrete 1600x1200
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.133s (7.500 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.100s (10.000 fps)

パラメータ設定値を個別に確認

$ v4l2-ctl --get-ctrl brightness
brightness: -10

以下のような書き方も同じ結果を返す

$ v4l2-ctl -C brightness
brightness: -10
$ v4l2-ctl --get-ctrl=brightness
brightness: -10

パラメータを設定する

$ v4l2-ctl --set-ctrl brightness=0

$ v4l2-ctl -C brightness
brightness: 0

以下のような書き方も同じ結果を返す

$ v4l2-ctl -c brightness=0

2つ以上のパラメータを同時に設定したり、確認したりもできる

$ v4l2-ctl -c brightness=10,contrast=5

$ v4l2-ctl -C brightness,contrast
brightness: 10
contrast: 5

以上のように確認、設定した上で

$ fswebcam -r 800x600 --no-banner -F 1 -S 100 ./image.jpg

のようにfswebcamで写真を撮る。
f:id:k-kuro:20210509180048p:plain
デフォルト設定で撮影するとこんな感じで

$ v4l2-ctl -l
                     brightness (int)    : min=-10 max=10 step=1 default=0 value=6
                       contrast (int)    : min=0 max=20 step=1 default=10 value=10
                     saturation (int)    : min=0 max=10 step=1 default=4 value=4
                            hue (int)    : min=-5 max=5 step=1 default=0 value=-3
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=100 max=200 step=1 default=150 value=177
                           gain (int)    : min=32 max=48 step=1 default=32 value=32
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=6500 value=6500 flags=inactive
                      sharpness (int)    : min=0 max=10 step=1 default=4 value=9
         backlight_compensation (int)    : min=0 max=2 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=8 max=16384 step=1 default=512 value=512 flags=inactive

という設定だと
f:id:k-kuro:20210509180129p:plain
こうなる。
明るさなどはカメラ側がある程度自動で合わせようとするので、あんまり思ったとおりにはならないようだ。

解像度とFPSの設定は

$ v4l2-ctl --set-fmt-video=width=640,height=480 --set-parm=15

というふうにできるがfswebcamを使うならfswebcamのほうで設定できるので、v4l2で無理に設定しておく必要はないだろう。

Jetson nano 2GB壊れる

その時は突然やってきた。

書籍も買って、本格的にいじっていこうと思った矢先、

絵に書いたようにもわんと煙が立ち上り、あっけなく沈黙。

特になにか激しい解析をしていたとかでもなく、普通にヘッドレスで起動してUSB経由でRDP接続して、まさにこのページを書こうとした瞬間の出来事。

どういうこっちゃぁ〜〜〜〜。

保証で交換してもらえるだろうか。(鬱)

せっかく周辺パーツ準備したのに。

Jetson nano 2GBを使う(1)

まずはこのページもJetson上で編集できるように日本語環境を入れておく。

Jetsonで日本語が入力できるようにする│FABSHOP.JP -デジタルでものづくり! ファブショップ !
こちらの記事を参考にする。

システム設定>言語サポート
で日本語をインストールし、キーボード入力に使うシステムをfcitxに変更する。
一旦ログアウトして、もう一度ログインすれば設定が反映されているはず。

さあ、AIの世界へ。自分でシコシコと環境構築するのと違い、もうすべてお膳立てされていて、すぐに使い始めることができるようだ。
Getting Started with Jetson Nano 2GB Developer Kit | NVIDIA Developer
このサイトの順にまずは見ていく。
JetsonではDockerコンテナーで環境構築することが前提となっていて、すでにDockerはインストールされている。自前構築で、ある程度の感覚は得ているので、とっつきやすくなっていて良いね。

kkuro@kkuro-jetson:~$ sudo docker pull nvcr.io/nvidia/l4t-base:r32.4.3
r32.4.3: Pulling from nvidia/l4t-base
c796196a5194: Pull complete 
9f1f9f625de3: Pull complete 
692bb77a7fc0: Pull complete 
7c6fea64666e: Pull complete 
2e07510b3b6f: Pull complete 
f3cef679c558: Pull complete 
01067c06de71: Pull complete 
607a871f53a8: Pull complete 
77c272c815a4: Pull complete 
d6e9ae6c556e: Pull complete 
6c31b14f8325: Pull complete 
da342f04dbe2: Pull complete 
bc1f73093866: Pull complete 
801ec5982390: Pull complete 
20f4f4b58bf1: Pull complete 
998db3ceb21f: Pull complete 
8f6eafd35194: Pull complete 
c3cf45f768ae: Pull complete 
7b3abe05cce9: Pull complete 
Digest: sha256:547dc36b81eddb7ca8eadd956c61bd96bf432486830701b3dbb019be7f6c9ce2
Status: Downloaded newer image for nvcr.io/nvidia/l4t-base:r32.4.3
nvcr.io/nvidia/l4t-base:r32.4.3
kkuro@kkuro-jetson:~$ sudo docker image ls nvcr.io/nvidia/l4t-base
REPOSITORY                TAG       IMAGE ID       CREATED         SIZE
nvcr.io/nvidia/l4t-base   r32.4.3   c93fc89026d9   10 months ago   631MB

まずは言われるままに

kkuro@kkuro-jetson:~$ sudo docker run -it --rm --net = host --runtime nvidia -e DISPLAY = $ DISPLAY -v /tmp/.X11-unix/:/tmp/.X11-unix nvcr.io/nvidia/l4t-base :r32.4.3
Unable to find image 'host:latest' locally
docker: Error response from daemon: pull access denied for host, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.

ありゃ?

r32.5.0が最新らしいのでとりあえずそっちも入れてみたが、それではやはり起動せず。

kkuro@kkuro-jetson:~$ sudo xhost +si:localuser:root
localuser:root being added to access control list
kkuro@kkuro-jetson:~$ sudo docker run --runtime nvidia --network host -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix/:/tmp/.X11-unix nvcr.io/nvidia/l4t-base:r32.5.0
root@kkuro-jetson:/# 

キター。

Jetson nano 2GB

とうとう手を出すことに。

f:id:k-kuro:20210504213533j:plain
https://amzn.to/33zSXVO

NVIDIA Jetson Nano 2GB 開発者キット

NVIDIA Jetson Nano 2GB 開発者キット

  • 発売日: 2020/10/06
  • メディア: エレクトロニクス

まずはOSのセットアップから。
developer.nvidia.com
現在の最新版は4.5.1らしい。

Getting Started with Jetson Nano 2GB Developer Kit | NVIDIA Developer
ここの手順に従って、Etcherというアプリケーションを使ってSDに書き込む。
ddとかじゃだめなんか?
いやddでいいらしいわ。まあこだわる必要もないけど。

さて、OSをコピーしたらおもむろにカードスロットにセットして、配線配線っと。
今回USB-CのACアダプタとしてMacBookAirの電源を拝借流用しようと考えていたが、これが失敗。どうもこのアダプタでは電源が入らないらしい。理由はわからない。Appleが何らかの独自配線をしているのかなんなのか。

困ったので、ダメ元でMacBookAir本体にUSB-Cを刺して、そこから電源をとってみた。すると何事もなかったようにNvidiaのロゴが表示され、普通に起動してしまった。まあ素直に専用に電源を買うことにしよう。

とりあえず一通りUbuntuのセットアップをやり、諸々のアップデートなども行って、とりあえずLinuxBoxとしては普通に使えるようになった。うん、すごく普通のPC。

UVCカメラ

タイムラプス映像のクオリティをあげるべくカメラを買ってもらった。


これ。届いた翌日に値段が30%近くディスカウントされててちょっとがっかり・・・

このカメラの特徴としては8メガピクセルフルHD 2448P @ 15fps(最大解像度:3264X2448)デフォルトの解像度は1920X1080Pになっているとのこと。
2.8-12mmのズームレンズがCSマウントで装着されており、レンズ交換も可能っぽい。

13メガピクセルの上位機種も出ているようだけど、まあとりあえず無難に8メガにしとく。
SONYのIMX322イメージセンサーを撮像素子として使っているとのこと。

さて、届いたカメラをラズパイに接続してみる。

$ v4l2-ctl -d /dev/video0 --all
Driver Info (not using libv4l2):
	Driver name   : uvcvideo
	Card type     : HD USB Camera
	Bus info      : usb-3f980000.usb-1.4
	Driver version: 4.9.35
	Capabilities  : 0x84200001
		Video Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps   : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
	Width/Height  : 1600/1200
	Pixel Format  : 'MJPG'
	Field         : None
	Bytes per Line: 0
	Size Image    : 3840589
	Colorspace    : Unknown (00000000)
	Flags         : 
Crop Capability Video Capture:
	Bounds      : Left 0, Top 0, Width 1600, Height 1200
	Default     : Left 0, Top 0, Width 1600, Height 1200
	Pixel Aspect: 1/1
Selection: crop_default, Left 0, Top 0, Width 1600, Height 1200
Selection: crop_bounds, Left 0, Top 0, Width 1600, Height 1200
Streaming Parameters Video Capture:
	Capabilities     : timeperframe
	Frames per second: 15.000 (15/1)
	Read buffers     : 0
                     brightness (int)    : min=-64 max=64 step=1 default=-8193 value=36
                       contrast (int)    : min=0 max=64 step=1 default=57343 value=32
                     saturation (int)    : min=0 max=128 step=1 default=57343 value=64
                            hue (int)    : min=-40 max=40 step=1 default=-8193 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=72 max=500 step=1 default=57343 value=100
                           gain (int)    : min=0 max=100 step=1 default=57343 value=0
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=57343 value=4600 flags=inactive
                      sharpness (int)    : min=0 max=6 step=1 default=57343 value=6
         backlight_compensation (int)    : min=0 max=2 step=1 default=57343 value=1
                  exposure_auto (menu)   : min=0 max=3 default=0 value=3
              exposure_absolute (int)    : min=1 max=5000 step=1 default=157 value=157 flags=inactive
         exposure_auto_priority (bool)   : default=0 value=0
                     brightness (int)    : min=-64 max=64 step=1 default=-8193 value=36
                       contrast (int)    : min=0 max=64 step=1 default=57343 value=32
                     saturation (int)    : min=0 max=128 step=1 default=57343 value=64
                            hue (int)    : min=-40 max=40 step=1 default=-8193 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=72 max=500 step=1 default=57343 value=100
                           gain (int)    : min=0 max=100 step=1 default=57343 value=0
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=57343 value=4600 flags=inactive
                      sharpness (int)    : min=0 max=6 step=1 default=57343 value=6
         backlight_compensation (int)    : min=0 max=2 step=1 default=57343 value=1

これがカメラの全スペック。
デフォルトの解像度は1920X1080Pって書いてたけど
Width/Height : 1600/1200
となってる。
このズームレンズはかなり癖のあるレンズだ。

設定はいじらず撮影してみるとちょっと照明がくらいのだけど
ワイド端での最短撮影距離あたりで
f:id:k-kuro:20210429001355j:plain
こんな感じ。結構樽型収差が大きいわ。

こっちだったら本当に歪みないんだろうか。
おなじ位置でテレ端にしてみるとフォーカスがボケボケになるのでピントリングを回す。これがえらく回さないとピントが合わない。
f:id:k-kuro:20210429001538j:plain
でもってテレ端でグッとよってみると、どこまでもよれて、レンズがぶつかってもまだよれる。ただし光が当たらないのでナニも写らない。
f:id:k-kuro:20210429001755j:plain
あと、レンズの前だまが油っぽいものでよごれているし、おそらく撮像素子もなんかプチプチついてる感じがする。これどうにかなるかなあ。

総評するとスペックは高く、可能性を感じるが、製品のクオリティーはやっぱり中華。

Deep learningをDockerで構築

今どきDockerくらい使えないと、というわけですよ。

Install Docker Engine on Ubuntu | Docker Documentation
apt パッケージを更新し、必要なパッケージをインストール

$ sudo apt-get update
$ sudo apt-get -y install curl \
    apt-transport-https \
    ca-certificates \
    gnupg-agent \
    software-properties-common

Docker 公式の GPG 公開鍵をインストール

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88

repository (stable) を追加

$ sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

apt パッケージを更新し、最新版をインストール

$ sudo apt-get update
$ sudo apt-get -y install docker-ce docker-ce-cli containerd.io

現在のユーザーをdockerグループに追加しておく。

$ sudo gpasswd -a $USER docker

Ubuntu 20.04上のdockerでGPUを使うために、NVIDIA Container Toolkitをインストールする。
GitHub - NVIDIA/nvidia-docker: Build and run Docker containers leveraging NVIDIA GPUs

$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

$ sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
$ sudo systemctl restart docker

ここでdocker上でnvidia-smiが実行できるかテストしてみると

$ sudo docker run --gpus all --rm nvidia/cuda nvidia-smi
Unable to find image 'nvidia/cuda:latest' locally
docker: Error response from daemon: manifest for nvidia/cuda:latest not found: manifest unknown: manifest unknown.
See 'docker run --help'.

と、エラーが出て動かない。うーむ。

追記

$ docker run --gpus all --rm nvidia/cuda:11.0-base nvidia-smi
Unable to find image 'nvidia/cuda:11.0-base' locally
11.0-base: Pulling from nvidia/cuda
54ee1f796a1e: Pull complete 
f7bfea53ad12: Pull complete 
46d371e02073: Pull complete 
b66c17bbf772: Pull complete 
3642f1a6dfb3: Pull complete 
e5ce55b8b4b9: Pull complete 
155bc0332b0a: Pull complete 
Digest: sha256:774ca3d612de15213102c2dbbba55df44dc5cf9870ca2be6c6e9c627fa63d67a
Status: Downloaded newer image for nvidia/cuda:11.0-base
Sun Apr 25 12:51:06 2021       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.119.03   Driver Version: 450.119.03   CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GT 710      Off  | 00000000:01:00.0 N/A |                  N/A |
| 50%   54C    P8    N/A /  N/A |    294MiB /   980MiB |     N/A      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

動いた。

NVIDIA NGCのイメージを使ってみる。

$ docker run --gpus all --rm -it nvcr.io/nvidia/tensorflow:20.09-tf1-py3

nvidiaのドライバが450.119.03なので20.09にしてみた。

Unable to find image 'nvcr.io/nvidia/tensorflow:20.09-tf1-py3' locally
20.09-tf1-py3: Pulling from nvidia/tensorflow
f08d8e2a3ba1: Pulling fs layer 
3baa9cb2483b: Pulling fs layer 
94e5ff4c0b15: Pulling fs layer 
1860925334f9: Pulling fs layer 
c6b364205fad: Pulling fs layer 
ffcd5dc3448d: Pulling fs layer 
13cf13e5ce72: Pulling fs layer 
7202bec79e41: Pulling fs layer 
fdfbe893941b: Pulling fs layer 
f73bfa0e0e17: Pulling fs layer 
36aade146566: Pulling fs layer 
0cf8254e1bfe: Pulling fs layer 
40ff6c34e5e5: Pulling fs layer 
0adeec2cfe74: Pulling fs layer 
895d871af5fd: Pulling fs layer 
71c97f6ac83c: Pulling fs layer 
281aa21cb812: Pulling fs layer 
9c7e46bb4080: Pulling fs layer 
150dfb1677cd: Pulling fs layer 
3ce488e63cd3: Pulling fs layer 
0f6e3807a6dc: Pulling fs layer 
3585c705a7d0: Pulling fs layer 
de81ad699822: Pulling fs layer 
bb1a224031d9: Pulling fs layer 
de7308abc9b3: Pulling fs layer 
07620b1781c2: Pulling fs layer 
dbc3331c85c9: Pulling fs layer 
c863ab4a3ce5: Pulling fs layer 
2cb780dadd08: Pulling fs layer 
72698521ce7a: Pulling fs layer 
1860925334f9: Waiting 
c6b364205fad: Waiting 
13cf13e5ce72: Waiting 
c4f3861fc440: Pulling fs layer 
7202bec79e41: Waiting 
c8dbc3fd23eb: Pulling fs layer 
fdfbe893941b: Waiting 
f73bfa0e0e17: Waiting 
cebab9392074: Pulling fs layer 
098124793456: Pulling fs layer 
be7876894a57: Pulling fs layer 
8f6cac9eb6f5: Pull complete 
8794953727b3: Pull complete 
66611ff5ae22: Pull complete 
052da93182d9: Pull complete 
19ab74a7714f: Pull complete 
10fb2f25565b: Pull complete 
99d96c644f99: Pull complete 
e04d68703197: Pull complete 
54b734d972b3: Pull complete 
8737a875ce8c: Pull complete 
66447294ec52: Pull complete 
bff8468ce910: Pull complete 
102507e7b013: Pull complete 
ad60ed3798eb: Pull complete 
7d1ebbc9228a: Pull complete 
6513260fcbe9: Pull complete 
8aaacd84798e: Pull complete 
7961f1c63d21: Pull complete 
c079890a79ca: Pull complete 
1aef1f3f370b: Pull complete 
1db61ffb4058: Pull complete 
30ab2ccfcdb1: Pull complete 
ee27d709b773: Pull complete 
Digest: sha256:e3db261638dc0283bd87d27b59be5731d2298604b44ec8bc81ab3f8e9128b6af
Status: Downloaded newer image for nvcr.io/nvidia/tensorflow:20.09-tf1-py3
                                                                                                                                                
================
== TensorFlow ==
================

NVIDIA Release 20.09-tf1 (build 16003718)
TensorFlow Version 1.15.3

Container image Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.
Copyright 2017-2020 The TensorFlow Authors.  All rights reserved.

NVIDIA Deep Learning Profiler (dlprof) Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.

Various files include modifications (c) NVIDIA CORPORATION.  All rights reserved.
NVIDIA modifications are covered by the license terms that apply to the underlying project or file.
ERROR: Detected NVIDIA GeForce GT 710 GPU, which is not supported by this container
ERROR: No supported GPU(s) detected to run this container

NOTE: MOFED driver for multi-node communication was not detected.
      Multi-node communication performance may be reduced.

NOTE: The SHMEM allocation limit is set to the default of 64MB.  This may be
   insufficient for TensorFlow.  NVIDIA recommends the use of the following flags:
   nvidia-docker run --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 ...

root@03d78aedf70c:/workspace#  python
Python 3.6.9 (default, Jul 17 2020, 12:50:27) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
2021-04-25 13:14:16.276946: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0
WARNING:tensorflow:Deprecation warnings have been disabled. Set TF_ENABLE_DEPRECATION_WARNINGS=1 to re-enable them.
>>> print(tf.__version__)
1.15.3
>>> 

イケてるのかな。
イケてないね。

ERROR: Detected NVIDIA GeForce GT 710 GPU, which is not supported by this container
ERROR: No supported GPU(s) detected to run this container

とな。GT710は対応してないのか。あかんやん。

Ubuntu環境をしっかりと入れる

自宅鯖で深層学習の学習環境を整えようと四苦八苦していて、私が主に使っているCentOS7に構築したkvm環境上のUbuntu18.04にNVIDIAのドライバやtensorflowをインストールするところまでやったわけだが。
結局kvmではビデオカードを仮想環境が使うとホストで使えなくなるとかいろいろ制約があってうまく行かないことがわかったので、ホストとしてUbuntuを入れることに方針を切り替えた。
自宅鯖は完全に実験機なのでデータとか気にする必要がないので、あっさりと上書きインストールができる。

さて、一旦は18.04をインストールしてみたのだが、すぐにアップデートのお誘いが出るなど、世の中20.04へ移行しているんだろうなと言う感じ。保守的なサーバー用途で使う人が多いCentOSred hat)にくらべUbuntuFedoraと同じような立ち位置のデスクトップ環境メインのLinuxっぽいので、安定環境というよりは常に最新にアップデートさせる方向なんだろうと思う。

確かに18.04は何かと古い気はするので20.04を入れていく。
CentOSでは基本最小構成でインストールするようにしているが、不慣れなUbuntuで環境構築をうまくできる自信がないので、一般インストールで、visualのサードパーティをインストールするオプションまで入れて完全おまかせコースとしておく。

おかげでインストールには結構時間がかかった。

インストールが完了して再起動するとまあ普通にデスクトップ環境が立ち上がった。

visualの追加インストールってNVIDIAのドライバも入れてくれてるのかな?ということでターミナルで確認

$ nvidia-smi
Sun Apr 25 12:09:38 2021       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.119.03   Driver Version: 450.119.03   CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GT 710      Off  | 00000000:01:00.0 N/A |                  N/A |
| 50%   39C    P0    N/A /  N/A |    213MiB /   980MiB |     N/A      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

入ってる。CUDAバージョンも最新の11.0が入っちゃってる。これはもしかしたらあとでバージョンの適合性に問題が出る可能性があるかもな。

基本ヘッドレスサーバとして使いたいので、sshとかvncがどうなっているんかなと思って確認したが、どっちも使えるようにはなっていない模様。これはやはりデスクトップ環境を基本としている思想の違いなんだろうな。

$ vncserver -list
vncserver: コマンドが見つかりません

$ ssh 192.168.1.100 -l kuro
ssh: connect to host 192.168.1.100 port 22: Connection refused

早速インストール

$ sudo apt install openssh-server

$ sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-04-25 12:41:42 JST; 2min 9s ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 4750 (sshd)
      Tasks: 1 (limit: 9416)
     Memory: 1.2M
     CGroup: /system.slice/ssh.service
             └─4750 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
(略)

$ ssh 192.168.1.100 -l kuro
kuro@192.168.1.100's password: 
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.8.0-50-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

0 updates can be installed immediately.
0 of these updates are security updates.

Your Hardware Enablement Stack (HWE) is supported until April 2025.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

これでsshはOK。
さて、vncの方はというと実はvncサーバはUbuntuにも最初から入っている模様。
まず、設定のアプリを開き、共有タグの中のスライドスイッチをオンにして、画面共有をアクティブに切り替える。
その上で、

$ gsettings set org.gnome.Vino require-encryption false

こうすることでリモートアクセスも有効になる。どうやらVinoというリモートアクセスサーバを使っているらしい。
その際ポートは自動で5900となるようだ。ポートをどうやって変更するのかはわからない。
Ubuntuはデフォルトではファイヤウォールも有効となっていないので、この状態でアクセス可能なはずだ。
設定>共有の中にリモートアクセスという項目が追加されている。
f:id:k-kuro:20210425131255p:plain


追記
Ubuntuのインストール状態でCUDAはインストールされてなかった。nvidia-smiでCUDA Version: 11.0となっているのは11.0まで対応しているよ!ってことらしい。

Vinoはユーザがログインした状態でないとリモートからログインできないという仕様らしく、ヘッドレスサーバとしては使い勝手が悪い。また、ネットワークの負荷が高いようで、動作はかなりもっさりとした感じでイマイチだな。やはりUbuntuはローカルデスクトップで使う前提なんだろうか。

React JSはじめました。

さっきやっとこさJSONやらAjaxをつかってフロントエンドとバックエンドのやり取りを成功させたところだが、
気を良くしてReactにも手を出してみる。

まずnpmってなんなん?ってとこからですよ。
Node.jsってのをまず入れるんですね。

$ sudo yum install centos-release-scl-rh
$ sudo yum install rh-nodejs10
$ scl enable rh-nodejs10 bash
$ which node
/opt/rh/rh-nodejs10/root/usr/bin/node
$ node --version
v10.21.0
$ node
> console.log('Node is running');
Node is running
undefined
> .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the repl
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file
> .exit
$ which npm
/opt/rh/rh-nodejs10/root/usr/bin/npm
$ npm --version
6.14.4

これでよかろう。

モジュールをインストールしてみる。

$ npm install --save react-calendar-timeline
npm WARN saveError ENOENT: no such file or directory, open '/home/kkuro/database/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/home/kkuro/database/package.json'
npm WARN react-calendar-timeline@0.27.0 requires a peer of interactjs@^1.3.4 but none is installed. You must install peer dependencies yourself.
npm WARN react-calendar-timeline@0.27.0 requires a peer of moment@* but none is installed. You must install peer dependencies yourself.
npm WARN react-calendar-timeline@0.27.0 requires a peer of prop-types@^15.6.2 but none is installed. You must install peer dependencies yourself.
npm WARN react-calendar-timeline@0.27.0 requires a peer of react@>=16.3 but none is installed. You must install peer dependencies yourself.
npm WARN react-calendar-timeline@0.27.0 requires a peer of react-dom@>=16.3 but none is installed. You must install peer dependencies yourself.
npm WARN create-react-context@0.3.0 requires a peer of prop-types@^15.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN create-react-context@0.3.0 requires a peer of react@^0.14.0 || ^15.0.0 || ^16.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN database No description
npm WARN database No repository field.
npm WARN database No README data
npm WARN database No license field.

react-calendar-timeline
これを使いたいのだよ。
インストールはこれでいいんかいな?WARN出まくっているけど。

とりあえずもうちょっと簡単な例からやってみないとな。

jExcelでスプレッドシートをwebアプリに仕込む(完成)

f:id:k-kuro:20210403133226p:plain
最終的にここに落ち着いた。

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Freezer list{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="https://bossanova.uk/jspreadsheet/v4/jexcel.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v4/jsuites.css" type="text/css" />
{% endblock %}
{% block page_top %}
<center>
<h2>-80 freezer sample list</h2>
</center>
<div class="row">
  <div class="col-xs-2">
  </div>
  <div class="col-xs-8"> 
    <div id="spreadsheet1"></div> 
    <div class="container">
      <div class="page-header">
        <button id="btn">save</button>
      </div>
  </div>
      <textarea id='saveData' style='width:0px;height:0px;'></textarea>     #ここに隠しウインドウがある
</div>
  <div class="col-xs-2">
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="https://bossanova.uk/jspreadsheet/v4/jexcel.js"></script>
<script src="https://jsuites.net/v4/jsuites.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
 <script>
 mySpreadsheet = jexcel(document.getElementById('spreadsheet1'),{
  url: '/static/user/freezer/freezer.json',
    // tableOverflow:true,
    // csvHeaders:false,
    columns:[
      {title:'Place', type:'dropdown', width:100, source:['Rack1', 'Rack2', 'Rack3', 'Drawer', 'Upper_area', 'Lower_area']},
      {title:'Position', type:'dropdown', width:150, source:['A1', 'A2', 'A3', 'A4', 'A5','B1', 'B2', 'B3', 'B4', 'B5','C1', 'C2', 'C3', 'C4', 'C5', 'D1', 'D2', 'D3', 'D4', 'D5', 'Basket1','Basket2','Basket3','Basket4','Free' ]},
      {title:'User', type:'text', width:100},
      {title:'Contents', type:'text', width:300},
      {title:'Date', type:'calendar', options:{format:'YYMMDD'}, width:100}, 
      {title:'Comments', type:'text', width:300}
      ]
 });
  </script>
  <script>
    function getjson(){
      document.getElementById('saveData').value =
        JSON.stringify(document.getElementById('spreadsheet1').jexcel.getJson());
    }
  </script>
    <script>
      $(function() {
  $('#btn').click(function() {
    document.getElementById('saveData').value =
        JSON.stringify(document.getElementById('spreadsheet1').jexcel.getJson());
    var textData = JSON.stringify(document.getElementById('spreadsheet1').jexcel.getJson());
    $.ajax({
      url: '{{ url_for('freezer.freezer_save') }}',
      data: textData,
      contentType: 'application/json;charset=UTF-8',
      type: 'POST'
    }).done(function(data){
      console.log(data);
    }).fail(function(){
      console.log('fail');
    });
  })
});
      </script>
 {% endblock %}

JSONを書き出すところでjExcelの機能を利用するため、一旦テーブルの内容からJSON形式のテキストにしたものをウインドウに書き出し、
それをajaxでサーバー側に送信するというややこしいことをしている。ウインドウは見せる意味がないので隠しウインドウにしてある。

サーバー側の受けはこんな感じ

@app.route('/freezer_save', methods=['GET', 'POST'])
@login_required
def freezer_save():
    if request.method == 'POST':
        data=str(request.json).replace('\'', '\"')
        json_file = "./user/mn-user/freezer/freezer.json"
        f = open(json_file, 'w')       
        f.write(data)
        f.close()
        return render_template('/freezer/index.html')
    return render_template('/freezer/index.html')

jExcelの出力するテキストはシングルクオートなんだけどどうもそのままではJSONファイルとして読み込んでもらえないようなのでダブルクオートに置き換えている。

jExcelでスプレッドシートをwebアプリに仕込む

以前チョロっと眺めていたjExcelを使ってウェブアプリに冷凍庫管理表を作ってみる。
javascriptなのでじつはあまり得意ではない。
しかし使い方は結構簡単そうだ。(実は実用するには結構ハードルが高いことは後でわかる)
設置方法は基本HTMLファイルにjavascriptを仕込むだけだ。
例によってFlaskで作っているサイトなのでtemplateファイルに書き込むことになる。
目標としてはexcelのシートがサイトに張り付いていて、項目を随時書き込んだり消したり、書き換えたり、ができるようになることなので
htmlに表を書き込まず外部のテキスト(csv)を読み込んで表示させる仕様とする。

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Freezer list{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="https://bossanova.uk/jspreadsheet/v4/jexcel.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v4/jsuites.css" type="text/css" />
{% endblock %}

{% block page_top %}
<center>
<h2>-80 freezer sample list</h2>
</center>
<div class="row">
  <div class="col-xs-2">
  </div>
  <div class="col-xs-8"> 
    <div id="freezer_list"></div> 
    <form method="post">
      <button type="button" id="save" class="btn btn-primary">Save sheet</button>
  </form>
 
</div>
  <div class="col-xs-2">
  </div>
</div>

{% endblock %}
{% block scripts %}
{{ super() }}
<script src="https://bossanova.uk/jspreadsheet/v4/jexcel.js"></script>
<script src="https://jsuites.net/v4/jsuites.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script> 
 jspreadsheet(document.getElementById('freezer_list'), {
     csv: '/static/user/freezer/freezer.csv',
     tableOverflow:true,
     csvHeaders:false,
     columns:[
       {title:'Place', type:'dropdown', width:100, source:['Rack1', 'Rack2', 'Rack3', 'Drawer', 'Upper_area', 'Lower_area']},
       {title:'Position', type:'dropdown', width:150, source:['A1', 'A2', 'A3', 'A4', 'A5','B1', 'B2', 'B3', 'B4', 'B5','C1', 'C2', 'C3', 'C4', 'C5','C1', 'C2', 'C3', 'C4', 'C5', 'D1', 'D2', 'D3', 'D4', 'D5', 'Basket1','Basket2','Basket3','Basket4','Free' ]},
       {title:'User', type:'text', width:100},
       {title:'Contents', type:'text', width:300},
       {title:'Date', type:'calendar', options:{format:'YYMMDD'}, width:100}, 
       {title:'Comments', type:'text', width:300}
     ]
 });
 </script>
{% endblock %}

テンプレートはずばりこんな感じ。
そしてこれをviewsのスクリプトから読み込む。
f:id:k-kuro:20210401141432p:plain
データはcsvで/static/user/freezer/freezer.csvに置いた。
するとたしかにこんな感じに表示はできる。

ここからが問題。
これで表示できるし、たしかに編集も機能する。ただ、データをサーバにセーブするのがこのままじゃできない。
こっからはAjaxの出番なんだな。これがまだ使いこなせてなくて、今日はここまで。

追記
表示されている内容をJSON形式で読み取ることができた

      <input type="button" onclick="savedata()"
      value="save data">
      <textarea id='saveData' style='width:800px;height:100px;'></textarea>

  <script>
    function savedata(){
      document.getElementById('saveData').value =
        JSON.stringify(document.getElementById('freezer_list').jexcel.getJson());
    }
  </script>

f:id:k-kuro:20210402162642p:plain

読み取った内容を表示しているだけなので、これをAjaxでサーバに送ってFlaskでファイルに落とすところまでを作ればいいんだろうな。