OpenCV4でGstreamerからの入力を受け取り表示する方法
はじめに
この記事では、Raspberry Piのカメラユニットの映像を別PCで動かしているOpenCVで受信し、表示する方法をGolangとGstreamerを使って実現します。Gstreamerを使って映像を配信し、Golangでその映像を受信して表示する方法を紹介します。Raspberry Pi 4を使用して配信と受信を行い、Golangを使っているため、どのOSでもある程度の使い回しが可能です。
Gstreamerでの配信について
配信する側もゆくゆくはソフトとして作っていきますが、今回はコマンドラインで代用します。
Gstreamerの動作確認
インストール済みだとは思いますが、映像を配信できるかを確かめます。
videotestsrcで起動すると映像が配信されます。pattern=snowを選択すると、 地デジになる前のテレビで電波が届いていないときに表示されていたような映像が流れます。(伝わるかしら)映画ドラえもんブリキのラビリンスで、深夜子どもたちが見ていたあのジャギジャギの映像です。
参考 videotestsrc
gst-launch-1.0 -v videotestsrc pattern=snow ! video/x-raw,width=1280,height=720 ! autovideosink
実行するとこんな感じで映像が取得されます。
ジャギジャギなのはあまり落ち着かないので別の映像で試します。
gst-launch-1.0 -v videotestsrc ! video/x-raw,width=1280,height=720 ! autovideosink
後ろでYoutube見ているのがバレちゃいますが、Terminalと合わせてこのように出力されているのが確認できます。
gocvでGstreamerの映像を取得する
映像の配信で使用していたautovideosinkをappsinkに変更すればOKです。
gst-launch-1.0 -v videotestsrc ! video/x-raw,width=1280,height=720 ! appsink
指定されたサイズの映像が、appsinkに流れています。 Terminalでは時間が経過するばかりで、映像が表示されません。
gocvを使ってappsinkを表示するプログラムを書いていきます。 gocvのTutorialにあるHello, videoをべ-スに作ります。
OpenVideoCaptureにgstreamerのコマンドを引数にとして渡してやればOKです。
実行コード
package main
import (
"gocv.io/x/gocv"
)
func main() {
src := `videotestsrc ! video/x-raw,width=1280,height=720 ! appsink`
webcam, _ := gocv.OpenVideoCapture(src)
window := gocv.NewWindow("Hello")
img := gocv.NewMat()
for {
webcam.Read(&img)
window.IMShow(img)
window.WaitKey(1)
}
}
実行結果
先ほどと表示内容に変化はないですが、Windowの名前がHelloになっていることが確認できます。
機器間配信のテスト
次は機器間をまたいで映像を取得します。 Golangで組む前にコマンドで動作することを確認しましょう。
Raspberry pi4(カメラ配信サーバ)IP:192.168.0.3
GstreamerからVideoカメラのデバイスを指定し、配信を開始します。 RTPパケットを使って映像配信を行っていきます。 udpsinkでは受信先のIPアドレスとポート番号を指定してあげます。
gst-launch-1.0 -v v4l2src device=/dev/video1 ! "image/jpeg,width=640, height=360,framerate=30/1" ! rtpjpegpay ! udpsink host=192.168.0.4 port=9000
Raspberry pi4 (映像受信サーバ) IP:192.168.0.4
gst-launch-1.0 -e -v udpsrc port=9000 ! application/x-rtp, encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink
実行結果
カメラ配信サーバに取り付けたUSBカメラの映像を同一ネットワークにおいている別の機器で出力してみました。 想像以上に遅延なくて少し驚いています。こんなに低遅延で実現できるのが驚いています。
tcpserversinkで配信したときは20秒ぐらい遅延があったのでそれを思うとかなり高速になっています。 あとは、WindowsとRaspberry pi2ってのも影響しているのでしょうか。 今回はRaspberry pi4を2台構成で組んでいるのでかなり安定しているように思います。いいですね。
golangから呼び出してみよう
この記事の本題に入ります。
Raspberry pi4 (映像受信サーバ) IP:192.168.0.4
Golangから読み出してやるときMat型で呼び出すためにappsinkを使います。
jpegdec以下をこのように変換します。
jpegdec ! videoconvert ! appsink
全文コード
package main
import (
"gocv.io/x/gocv"
)
func main() {
src := `udpsrc port=9000 ! application/x-rtp, encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! videoconvert ! appsink`
webcam, _ := gocv.OpenVideoCapture(src)
window := gocv.NewWindow("Hello")
img := gocv.NewMat()
for {
webcam.Read(&img)
window.IMShow(img)
window.WaitKey(1)
}
}
Raspberry pi4(カメラ配信サーバ)IP:192.168.0.3
Golangから呼び出すコードを書いていきます。 Gstreamerで呼び出したコマンドをexecで実行してあげます。 他に良いやり方があれば是非教えて下さい。お待ちしております。
全文コード
package main
import (
"fmt"
"os"
"os/exec"
"strings"
)
func main() {
args := strings.Fields("-v v4l2src device=/dev/video1 ! image/jpeg,width=640, height=360,framerate=30/1 ! rtpjpegpay ! udpsink host=192.168.0.4 port=9000")
out, err := exec.Command("gst-launch-1.0", args...).Output()
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println(string(out))
}
実行結果
良い感じに動いています。 やりましたね!!
まとめ
この記事では、GolangとGstreamerを使ってRaspberry Piのカメラユニットの映像を別PCで動かしているOpenCVで受信し、表示する方法を紹介しました。Raspberry Pi 4で配信と受信を行い、Golangで書いているため、どのOSでもある程度使い回しができる