PaPeRo iのカメラでリアルタイムモニタ Python編と同じ事をGolangを使ってやってみました。構成や使用するAPI、カメラ等については同じなのでそちらをご参照ください。
Golangで共有メモリを操作する方法
Golangではgolang.org/x/sys/unixパッケージを使用して共有メモリを操作する事ができます。
import (
"log"
"unsafe"
"golang.org/x/sys/unix"
)
const IPC_CREAT = 00001000
const IPC_EXCL = 00002000
const IPC_RMID = 0
func shmget(siz int, shmflag int) (int, error) {
shmid, _, err := unix.Syscall(unix.SYS_SHMGET, uintptr(0), uintptr(siz), uintptr(shmflag))
if err != 0 {
return 0, err
}
return int(shmid), nil
}
func shmdt(shmaddr unsafe.Pointer) error {
_, _, err := unix.Syscall(unix.SYS_SHMDT, uintptr(shmaddr), uintptr(0), uintptr(0))
if err != 0 {
return err
}
return nil
}
func shmat(shmid int, addr unsafe.Pointer, flags int) (unsafe.Pointer, error) {
res, _, err := unix.Syscall(unix.SYS_SHMAT, uintptr(shmid), uintptr(addr), uintptr(flags))
if err != 0 {
return nil, err
}
return unsafe.Pointer(res), nil
}
// 共有メモリの割り当て
func ShmCreate(siz int) (int, error) {
res, err := shmget(siz, IPC_CREAT|IPC_EXCL|0600)
return res, err
}
// 共有メモリのアタッチ
func ShmAttatch(shmid int) (unsafe.Pointer, error) {
res, err := shmat(shmid, unsafe.Pointer(nil), 0)
return res, err
}
// 共有メモリのデタッチ
func ShmDetatch(shmaddr unsafe.Pointer) error {
err := shmdt(shmaddr)
return err
}
// 共有メモリの破棄
func ShmDelete(shmid int) error {
opt := 0
_, _, err := unix.Syscall(unix.SYS_SHMCTL, uintptr(shmid), uintptr(IPC_RMID), uintptr(opt))
if err != 0 {
return err
}
return nil
}
Python版同様起動時に共有メモリの割り当てを行い、取得したIDをSendStartCapturing()に渡すことでカメラ画像が取得できるようになります。データは共有メモリのアタッチを行って取得したunsafe.Pointerから読み出します。unsafe.Pointerは固定長のbyte配列にキャストすることで通常の[]byteとして読み出せるようになります。
GolangでVGAカメラのデータをJPEGに変換する
Pythonでは試しませんでしたがコンパイラ言語であるGolangは高速処理が期待できるので、VGAカメラデータのPaPeRo i上でのJPEG変換も試してみました。まずJavaScript版と以下のロジックでyuyv422からRGBAへ変換し、標準パッケージimage/jpegでJPEGに変換します。
func clipval(minv, maxv, val int) int {
res := val
if val < minv {
res = minv
}
if val > maxv {
res = maxv
}
return res
}
func yuyv2rgba(yuyv []byte, width, height int) *image.RGBA {
od := image.NewRGBA(image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{width, height}})
opos := 0
for h := 0; h < height; h++ {
for w := 0; w < width; w += 2 {
ipos := 2 * (width*h + w)
mc := 298 * (int(yuyv[ipos+0]) - 16)
d := int(yuyv[ipos+1]) - 128
e := int(yuyv[ipos+3]) - 128
v0 := (mc + 409*e + 128) >> 8
v1 := (mc - 100 + d - 208*e + 128) >> 8
v2 := (mc + 516*d + 128) >> 8
if 0 > v0 || v0 > 255 {
v0 = clipval(0, 255, v0)
}
if 0 > v1 || v1 > 255 {
v1 = clipval(0, 255, v1)
}
if 0 > v2 || v2 > 255 {
v2 = clipval(0, 255, v2)
}
od.Pix[opos+0] = byte(v0)
od.Pix[opos+1] = byte(v1)
od.Pix[opos+2] = byte(v2)
od.Pix[opos+3] = 255
opos += 4
mc = 298*int(yuyv[ipos+2]) - 16
v0 = (mc + 409*e + 128) >> 8
v1 = (mc - 100 + d - 208*e + 128) >> 8
v2 = (mc + 516*d + 128) >> 8
if 0 > v0 || v0 > 255 {
v0 = clipval(0, 255, v0)
}
if 0 > v1 || v1 > 255 {
v1 = clipval(0, 255, v1)
}
if 0 > v2 || v2 > 255 {
v2 = clipval(0, 255, v2)
}
od.Pix[opos+0] = byte(v0)
od.Pix[opos+1] = byte(v1)
od.Pix[opos+2] = byte(v2)
od.Pix[opos+3] = 255
opos += 4
}
}
return od
}
他にPython版同様ffmpegとブラウザでの変換も試してみました。
ソースとPaPeRo i用ビルド済みバイナリはここにあります。local/paperocamera/paperocameraをパペロに転送して任意の場所で実行し、パペロのIP:8865/cameraをブラウザで開き、パペロの座布団向かって右ボタンを押すとカメラ画像が表示されます。
なおカメラと変換の種類はmain.goの定数paperoCameraSize、convOnPapero、useFfmpegと、servers.goの290目付近のJavaScriptソースの変数orgSizeとCONV_ON_PAPEROを同時に変更する必要があります。また、カメラの種類を変更する場合にはパペロを一度再起動する必要がある様です。
実行結果
実行結果は以下の様になりました。PCはCore i7の比較的高性能なWindows10のPC、スマホは古いNexus4を使用しました。PC欄とスマホ欄は表示レート/表示遅延です。
Golang版の結果
No | カメラ | データ変換 | 1画像の転送データサイズ | 挿入スリープ時間 | パペロCPU使用率 | PC | スマホ | 備考 |
---|---|---|---|---|---|---|---|---|
1 | VGA | ffmpeg | 約40kB? | 1msec | 約40% | 約4fps/0.6sec | 約4fps/0.7sec | |
2 | VGA | ブラウザ | 614.4kB | 100msec | 約12% | 約7fps/0.4sec | ×(遅延大) | これ以上スリープ減らすと遅延による廃棄発生 |
3 | VGA | ブラウザ | 614.4kB | 250msec | 約10% | 約3fps/0.4sec | 約3fps/0.7sec | スマホは数秒停止することあり |
4 | SXGA | なし | 200kB弱~300kB強? | 50msec | 約15% | 約15fps/0.3sec | ×(数秒でエラー停止) | |
5 | VGA | Golang | 約40kB? | 1msec | 約50% | 約4fps/0.6sec | 約4fps/0.7sec |
傾向はPython版と似ており結論も変わりませんが、表示性能はPythonより少し良い程度、CPU使用率はffmpegを除き半減となりました。No5のGolangによる変換はCPU負荷が高く、ffmpegの方が良い結果となっています。
比較のためPython版の結果も再掲します。
Python版の結果(再掲)
No | カメラ | データ変換 | 1画像の転送データサイズ | 挿入スリープ時間 | パペロCPU使用率 | PC | スマホ | 備考 |
---|---|---|---|---|---|---|---|---|
1 | VGA | ffmpeg | 約40kB? | 1msec | 約40% | 約3fps/0.6sec | 約3fps/0.8sec | |
2 | VGA | ブラウザ | 614.4kB | 100msec | 約24% | 約5fps/0.5sec | ×(遅延大) | これ以上スリープ減らすと遅延による廃棄発生 |
3 | VGA | ブラウザ | 614.4kB | 250msec | 約20% | 約3fps/0.5sec | 約3fps/0.8sec | スマホは数秒停止することあり |
4 | SXGA | なし | 200kB弱~300kB強? | 30msec | 約35% | 約12fps/0.3sec | ×(数秒でエラー停止) | スリープ0.5secでもスマホNG |