Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

Go言語 GUIアプリ Fyneへの動画表示

先日Fyneを使用して簡単なGUIアプリを作成しました。
elsammit-beginnerblg.hatenablog.com

今回はFyneを利用して動画を表示・再生させるアプリを作成していこうと思います!!



■環境

・OS:Ubuntu20.04(virtualBox上)
 ※すでにFyneがインストールされていることを前提にします。

■Fyneへの画像表示

動画表示・再生を行う前にまずは画像の表示を行っていきます。
コードはこんな感じ。

package main

import (
	"fmt"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/widget"
	"gocv.io/x/gocv"
	"image"
)

func main() {
	Flg = false
	fileName := "ファイルパス"
	img := gocv.IMRead(fileName, -1)
	gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)

	imgD, _ := img.ToImage()

	myApp := app.New()
	w := myApp.NewWindow("Image")

	ShowImg := canvas.NewImageFromImage(imgD)
	ShowImg.FillMode = canvas.ImageFillOriginal

	label := widget.NewLabel("Show image")

	w.SetContent(widget.NewVBox(
		ShowImg,
		label,
	))

	w.ShowAndRun()
}

やっていることは、
opencvで画像データを読み出す。

	img := gocv.IMRead(fileName, -1)
	gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)

画像表示用画像イメージに変換。

        imgD, _ := img.ToImage()

用意したcanvasの上に読み出した画像データをセット。

	ShowImg := canvas.NewImageFromImage(imgD)
	ShowImg.FillMode = canvas.ImageFillOriginal

最後にGUIとしてレイアウトを決めて、

	w.SetContent(widget.NewVBox(
		ShowImg,
		label,
	))

画面表示、

	w.ShowAndRun()

になります。

今回画像をこちらとした場合、
f:id:Elsammit:20201212194817j:plain

先ほどのコードを実行するとこちらのアプリが表示されます。
f:id:Elsammit:20201212195007p:plain

■動画表示

では本題の動画を表示させます。
全体のコードはこちらになります。

package main

import (
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/widget"
	"gocv.io/x/gocv"
	"image"
	"time"
)

func MakeMovie(ShowImg *canvas.Image) {
	movie, _ := gocv.OpenVideoCapture("再生させる動画パス")
	img := gocv.NewMat()
	imgDD, _ := img.ToImage()
	for {
		movie.Read(&img)
		gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)
		imgDD, _ = img.ToImage()
		ShowImg.Image = imgDD
		canvas.Refresh(ShowImg)
		time.Sleep(time.Millisecond * 33)
	}
}

func main() {
	fileName := "lena.jpg"
	img := gocv.IMRead(fileName, -1)
	gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)

	imgD, _ := img.ToImage()

	myApp := app.New()
	w := myApp.NewWindow("Image")

	ShowImg := canvas.NewImageFromImage(imgD)
	ShowImg.FillMode = canvas.ImageFillOriginal

	label := widget.NewLabel("Show Movie")
	go MakeMovie(ShowImg)

	w.SetContent(widget.NewVBox(
		ShowImg,
		label,
	))
	w.ShowAndRun()
}

先ほどの画像表示と異なる点は、

func MakeMovie(ShowImg *canvas.Image) {
	movie, _ := gocv.OpenVideoCapture("再生させる動画パス")
	img := gocv.NewMat()
	imgDD, _ := img.ToImage()
	for {
		movie.Read(&img)
		gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)
		imgDD, _ = img.ToImage()
		ShowImg.Image = imgDD
		canvas.Refresh(ShowImg)
		time.Sleep(time.Millisecond * 33)
	}
}

を追加して、

go MakeMovie(ShowImg)

により並列処理を行っている点です。

MakeMovie関数ですが、
引数に用意した*canvas.Imageを渡し、
動画の1フレーム毎に取得、canvas.Imageにセットする
処理を行っております。

先ほどの画像と同様、canvasへセットするために読みとったフレーム画像は

imgDD, _ = img.ToImage()

より変換を行っております。

最後に!!
毎回フレーム画像をリフレッシュしないと反映されないので必ずこちらの関数を用いてください。

canvas.Refresh(ShowImg)

では、実際にこちらのコードを用いて、動画を再生してみます!!
実行するとこんな感じで再生されます。
f:id:Elsammit:20201212200800g:plain

■再生・停止ボタン追加(おまけ)

おまけとして、再生・停止ボタンを用意して動画の停止・再開の機能を追加したいと思います。
コードはこちらになります。

package main

import (
	"fmt"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/widget"
	"gocv.io/x/gocv"
	"image"
	"time"
)

var Flg bool

func MakeMovie(ShowImg *canvas.Image) {
	Movie, _ := gocv.OpenVideoCapture("./Underwater - 37712.mp4")
	img := gocv.NewMat()
	imgDD, _ := img.ToImage()
	for {
		if Flg == true {
			Movie.Read(&img)
			gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)
			imgDD, _ = img.ToImage()
			ShowImg.Image = imgDD
			canvas.Refresh(ShowImg)
			time.Sleep(time.Millisecond * 33)
		} else {
			time.Sleep(time.Second)
		}
	}
}

func main() {
	Flg = false
	fileName := "lena.jpg"
	img := gocv.IMRead(fileName, -1)
	gocv.Resize(img, &img, image.Point{250, 250}, 0, 0, gocv.InterpolationDefault)

	imgD, _ := img.ToImage()

	myApp := app.New()
	w := myApp.NewWindow("Image")

	ShowImg := canvas.NewImageFromImage(imgD)
	ShowImg.FillMode = canvas.ImageFillOriginal

	label := widget.NewLabel("Show Movie")
	go MakeMovie(ShowImg)
	var button *widget.Button
	button = widget.NewButton("Start", func() {
		label.SetText("Welcome")
		fmt.Println("push Button")

		if Flg == true {
			button.SetText("Start")
			Flg = false
		} else {
			button.SetText("Stop")
			Flg = true
		}
	})

	w.SetContent(widget.NewVBox(
		ShowImg,
		label,
		button,
	))

	w.ShowAndRun()
}

追加しているのはStart/Stopフラグのみ(のはず)。

こちらを実行するとこちらのようになります!!
f:id:Elsammit:20201212201539g:plain

■最後に

今回はfyneを用いてGUIアプリ上に動画を表示させ、最終的に動画再生・停止までが行えるようにしました。
先ほどの動画パスをデバイス番号(0,1,..)に置き換えればカメラキャプチャ動画の再生も行えるようになります。