ぼっちプログラマのメモ

UE4とかUE5とかについて書いたり書かなかったり。

C#信者がOvrvisionSDK(C#)+OpenCVSharpを触ってみた結果www Oculus Rift 2 Advent Calendar 2014


Oculus Rift 2 Advent Calendar 2014 - Qiitaの6日目です。

↑のカレンダーが途切れそうだったので、急いでネタを作りました。
今回はゲームエンジン使いません。題名の通り、
C# + OvrvisionSDK(C#) + OpenCVSharp + WPFでゴリゴリコード書く感じです。
VisualStudioCommunity2013を使います。
いつも以上に説明雑いです。仕様です。



OvrvisionSDK(C#)をセットアップしてみた結果www

まずはセットアップです。公式サイトのスタートアップガイドに
手順が書いてあるので、それを参考にセットアップしましょう!
startup_manual - Ovrvision Developer Document



f:id:pafuhana1213:20141206145130j:plain
C#版のセットアップ手順書いてねええええええええええええええ!!!
いいもん、自力でセットアップするもん(おこ
(2014/12/06現在)

セットアップしてみた

まずWindows用のSDKをダウンロードします。
downloads - Ovrvision Developer Document

ダウンロードしたファイルの中にある「example_csharp_vs2008」フォルダを
開きます。
そして、中にある以下のファイルを自分のC#プロジェクトに追加して下さい

  • COvrvision.cs
  • COvrvisionProperty.cs

あとは、ovrvision.dllを追加します。dllの追加方法はググって下さい
面倒くさい方は、System32やSysWow64にぶち込めばいいと思います
("ovrvisionsdk_windows\bin\x86_csharp\ovrvision.dll")

OpenCVSharpをセットアップした結果www

Ovrvisionから受け取った画像データをそのまま出すだけでは芸がないので
OpenCVSharpで画像を出してみます。OpenCVSharpでフィルタ処理などを
使うことができるので、色々楽しいことができそうです

セットアップ方法
OpenCvSharpをつかう その17(NuGetで導入) - schima.hatenablog.com

以上。手抜きです、はい。
(本家のOpenCV、Nugetで取れるようになっていたんですね…素晴らしい…)

OvrvisionSDK(C#)リファレンスを見た結果www

作業に取り掛かる前にリファレンスを見ます。大切です。見ます!
reference_cs - Ovrvision Developer Document

↓リファレンスの一部
f:id:pafuhana1213:20141206142722j:plain


…え?



あ、はい

リファレンスが整備されるまでは、「COvrvision.cs」の中身を見ましょう
コードがリファレンスです

WPF上にovrvisionから受け取った画像を出してみた結果www

細かい解説してもアレなので、コード全部貼ります。
WPFプロジェクト作成時に用意されているMainWindowのコードです。

MainWindows.xaml

<Window x:Class="OVRVision_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Width="1280" Height="480
        ">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Viewbox Grid.Column="0" Stretch="Fill" >
            <Image Name="ImageLeft"  HorizontalAlignment="Left" Height="480" Margin="0,0,0,0" VerticalAlignment="Top" Width="640" Loaded="Window_Loaded"/>
        </Viewbox>

        <Viewbox Grid.Column="1" Stretch="Fill">
        <Image Name="ImageRight" HorizontalAlignment="Right" Height="480" Margin="0,0,0,0" VerticalAlignment="Top" Width="640"/>
        </Viewbox>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using OpenCvSharp;
using ovrvision_app;
using OpenCvSharp.Blob;
using OpenCvSharp.Extensions;
using System.Threading;

namespace OVRVision_WPF
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// http://thinkami.hatenablog.com/entry/2014/08/01/074831
    /// http://sourcechord.hatenablog.com/entry/2014/10/05/022144
    /// </summary>
    public partial class MainWindow : Window
    {
        BackgroundWorker worker;
        COvrvision Ovrvision = new COvrvision();
        IplImage imgLeft = new IplImage();
        IplImage imgRight = new IplImage();
        WriteableBitmap wbLeft;
        WriteableBitmap wbRight;

        public MainWindow()
        {
            InitializeComponent();
            bool result = Ovrvision.Open();
            Console.WriteLine("OVR Open:" + result.ToString());

            worker = new BackgroundWorker();

            // ProgressChangedイベントを発生させるようにする
            worker.WorkerReportsProgress = true;
            worker.DoWork += (sender, e) =>
            {
                while (true)
                {
                    Ovrvision.UpdateCamera();
 
                    worker.ReportProgress(0);
                    Thread.Sleep(16);
                }
            };

            // ReportProgressメソッドで呼ばれるProgressChangedのイベントハンドラを追加
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
        }

        private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // 左目
            Ovrvision.UpdateLeft();
            using( imgLeft = (IplImage)BitmapConverter.ToIplImage(Ovrvision.imageDataLeft))
            {
                if (wbLeft == null)
                {
                    wbLeft = new WriteableBitmap(imgLeft.Width, imgLeft.Height, 256, 256, PixelFormats.Bgr24, null);
                }
                WriteableBitmapConverter.ToWriteableBitmap(imgLeft, wbLeft);
                ImageLeft.Source = wbLeft;
            }

            // 右目
            Ovrvision.UpdateRight();
            using( imgRight = (IplImage)BitmapConverter.ToIplImage(Ovrvision.imageDataRight))
            {
                if (wbRight == null)
                {
                    wbRight = new WriteableBitmap(imgRight.Width, imgRight.Height, 256, 256, PixelFormats.Bgr24, null);
                }
                WriteableBitmapConverter.ToWriteableBitmap(imgRight, wbRight);
                ImageRight.Source = wbRight;
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // DoWorkイベントハンドラの実行を開始
            worker.RunWorkerAsync();
        }
    }
}

…コード汚いけど直す気なし

実行すると、こんな感じで左目と右目用画像が並んだウィンドウ表示されます
f:id:pafuhana1213:20141206151119j:plain

これをOculus(Extend)ウィンドウに持って行って、最大化して下さい。
やった!表示された!ばんじゃーい!
f:id:pafuhana1213:20141206154459j:plain
おやすみなさい…(現在午前4時

色々と苦労したったwww

↑を作る上で苦労した・嵌った点を書いていきます

Ovrvision → OpenCVの画像変換

Ovrvision側で持っている画像形式は「System.Drawing.Bitmap」、
OpenCV側は「OpenCVSharp.IplImage」です。で、変換は以下のサイトを参考にしました。
おちラボ:教育システム研究開発BLOG: 【C#】OpenCVSharpでのBitmapとIplImageへの相互変換

imgLeft = (IplImage)BitmapConverter.ToIplImage(Ovrvision.imageDataLeft)

OpenCVWPF Imageの画像変換

WPFで扱う画像形式は「WriteableBitmap」です。
変換処理は以下のサイトを参考にしました。別スレッド処理もこちらのサイトを参考にしています
C# + OpenCvSharp + WPF で、USBカメラ画像の表示や保存をしてみた - メモ的な思考的な

ImageLeft.Source = WriteableBitmapConverter.ToWriteableBitmap(imgLeft);

ハイパーメモリ枯渇タイム

画像変換も終わり、無事表示できたと思ったらメモリ枯渇で落ちました
PCのメモリ容量が不足してるので、メモリ増設しましょう。以上

f:id:pafuhana1213:20141206160004j:plain
( ‘д‘⊂彡☆))Д´) パーン

↓の記事に原因・解決方法が解説されていました
OpenCvSharpで動画再生 - SourceChord

毎フレームごとに、ToWriteableBitmap()メソッドで、
新規WriteableBitmapインスタンスを生成しているためですね。
無駄なメモリ消費をしないように、一度作成したWriteableBitmapを
使いまわすように修正します。

ということで、修正しました。内容は上のコードを見て下さい。

作ったもの公開したったwww

ということで、ここまでの実装内容をアップしました
簡単なフィルタを色々用意してます。

https://dl.dropboxusercontent.com/u/30427841/exe/OVRVision_WPF.ZIP

サンプル

びふぉー

f:id:pafuhana1213:20141206182041j:plain

あふたー

f:id:pafuhana1213:20141206182046j:plain

動かなかったら報告下さい。たぶん直します。

最後に

今作るなら、C++の方が…
OvrvisionのサンプルもC++の方が気合入ってますし…
凹みさんの記事オススメです…
Unity と OpenCV を組み合わせて現実・仮想双方を加工した AR な世界を Oculus Rift 越しに覗いてみた - 凹みTips


カレンダー繋いだはいいけど、12/7(日)はどうしよう…誰か…
Oculus Rift 2 Advent Calendar 2014 - Qiita