ラベル Livet の投稿を表示しています。 すべての投稿を表示
ラベル Livet の投稿を表示しています。 すべての投稿を表示

2016/11/05

WPF(Livet)とNyARToolKitでマーカー検出

マーカーの位置検出を行うために、マーカーの検出をNyARToolKitで行うサンプルプログラムを作成しました。
次のようなことをやります。
  1. Livetを使用したWPFアプリケーション
  2. NyARToolKitでカメラキャプチャとマーカー検出
  3. OpenCVSharpでマーカー位置に矩形を描画
初めにXAML。Grid内だけ変更しています。ボタンを押すとマーカー検出がスタートします。
    
        


次にVeiwModel。
NyARToolKitはその実装の関係上「onBuffer」という関数が呼ばれるので、その中で処理をしています。
NyARToolKitのサンプル等ではBitmapで画像を処理しているので、
それをMatに変換してOpenCVSharpで矩形を描画しています。

そのあとに、MatをWriteableBitmapに直して、XAMLでBindingできるようにします。
ここで、onBufferという別タスクでUIで利用するWriteableBitmapを変更できないので、
Dispatchar.BeginInvokeを使用して、UI関係にアクセスできるようにしています。

using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using jp.nyatla.nyartoolkit.cs;
using jp.nyatla.nyartoolkit.cs.core;
using jp.nyatla.nyartoolkit.cs.detector;
using NyARToolkitCSUtils.Capture;
using NyARToolkitCSUtils.Direct3d;

using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.Extensions;

namespace ar_SimpleLite.ViewModels
{
    public class MainWindowViewModel : ViewModel, CaptureListener
    {
        private NyARToolkitCSUtils.Capture.CaptureDevice m_cap;
        private const String AR_CODE_FILE = "patt.hiro";
        private const String AR_CAMERA_FILE = "camera_para.dat";
        private NyARSingleDetectMarker m_ar;
        private DsRgbRaster m_raster;

        private Dispatcher dispatcher;

        public void Initialize()
        {
            //ARの設定
            //AR用カメラパラメタファイルをロード
            NyARParam ap = NyARParam.createFromARParamFile(new StreamReader(AR_CAMERA_FILE));
            ap.changeScreenSize(320, 240);

            //AR用のパターンコードを読み出し 
            NyARCode code = NyARCode.createFromARPattFile(new StreamReader(AR_CODE_FILE), 16, 16);

            NyARDoubleMatrix44 result_mat = new NyARDoubleMatrix44();

            //計算モードの設定
            //キャプチャを作る
            /**************************************************
            このコードは、0番目(一番初めに見つかったキャプチャデバイス)
            を使用するようにされています。
            複数のキャプチャデバイスを持つシステムの場合、うまく動作しないかもしれません。
            n番目のデバイスを使いたいときには、CaptureDevice cap=cl[0];←ここの0を変えてください。
            手動で選択させる方法は、SimpleLiteDirect3Dを参考にしてください。
            **************************************************/
            CaptureDeviceList cl = new CaptureDeviceList();
            NyARToolkitCSUtils.Capture.CaptureDevice cap = cl[0];

            cap.SetCaptureListener(this);

            cap.PrepareCapture(320, 240, 30);
            this.m_cap = cap;
            //ラスタを作る。
            this.m_raster = new DsRgbRaster(cap.video_width, cap.video_height, NyARBufferType.OBJECT_CS_Bitmap);
            //1パターンのみを追跡するクラスを作成
            this.m_ar = NyARSingleDetectMarker.createInstance(ap, code, 80.0);
            this.m_ar.setContinueMode(false);

            dispatcher = Dispatcher.CurrentDispatcher;
        }


        #region WBmp変更通知プロパティ
        private WriteableBitmap _WBmp;

        public WriteableBitmap WBmp
        {
            get
            { return _WBmp; }
            set
            { 
                if (_WBmp == value)
                    return;
                _WBmp = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region ButtonCommand
        private ViewModelCommand _ButtonCommand;

        public ViewModelCommand ButtonCommand
        {
            get
            {
                if (_ButtonCommand == null)
                {
                    _ButtonCommand = new ViewModelCommand(Button);
                }
                return _ButtonCommand;
            }
        }

        public void Button()
        {
            this.m_cap.StartCapture();
        }
        #endregion


        public void OnBuffer(NyARToolkitCSUtils.Capture.CaptureDevice i_sender, double i_sample_time, IntPtr i_buffer, int i_buffer_len)
        {            
            int w = i_sender.video_width;
            int h = i_sender.video_height;
            int s = w * (i_sender.video_bit_count / 8);

            Bitmap b = new Bitmap(w, h, s, System.Drawing.Imaging.PixelFormat.Format32bppRgb, i_buffer);

            // If the image is upsidedown
            b.RotateFlip(RotateFlipType.RotateNoneFlipY);
            Mat img = b.ToMat();

            //ARの計算
            this.m_raster.setBuffer(i_buffer, i_buffer_len, i_sender.video_vertical_flip);
            if (this.m_ar.detectMarkerLite(this.m_raster, 100))
            {
                NyARDoubleMatrix44 result_mat = new NyARDoubleMatrix44();
                this.m_ar.getTransmationMatrix(result_mat);
                Console.WriteLine("Maker is found.");

                NyARSquare square = m_ar.refSquare();

                var point = new OpenCvSharp.CPlusPlus.Point[4];
                point[0] = new OpenCvSharp.CPlusPlus.Point(square.sqvertex[0].x, square.sqvertex[0].y);
                point[1] = new OpenCvSharp.CPlusPlus.Point(square.sqvertex[1].x, square.sqvertex[1].y);
                point[2] = new OpenCvSharp.CPlusPlus.Point(square.sqvertex[2].x, square.sqvertex[2].y);
                point[3] = new OpenCvSharp.CPlusPlus.Point(square.sqvertex[3].x, square.sqvertex[3].y);

                img.FillConvexPoly(point, new Scalar(0, 0, 255));
            }
            else
            {
                Console.WriteLine("Maker is NOT found.");
            }

            dispatcher.BeginInvoke(new Action(delegate()
            {

                WBmp = img.ToWriteableBitmap();
            }));
        }

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
    }
}


2016/10/30

WPF(Livet)でasync/awaitを使って非同期処理

async/awaitを使って非同期処理を行うサンプルを載せます。
ただ非同期処理をするだけではなく、進捗状況をProgressBarで表示し、CancelボタンでCancelできるようにしています。

 
 まずはXAMLです。Grid内だけ記載しています。
    
        

ViewModelです。
非同期処理を行う関数はTaskFuncなのですが、CancellationTokenを引数に取っているので、Cancelすると例外を出します。
進捗状況はProgressPercentに直接値を代入してXAMLのほうでProgressBarにBindingしています。
Windows FormではIProgressクラスを使用して値をUIスレッドに渡さないといけないのですが、
そもそもWPFではBindingによってUIスレッドとは切り離されているので、ただBindingするだけでいいと思います。
using System.Windows;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaiteSample.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        CancellationTokenSource cancelSrc;

        public void Initialize()
        {
        }

        #region ProgressPercent変更通知プロパティ
        private int _ProgressPercent;

        public int ProgressPercent
        {
            get
            { return _ProgressPercent; }
            set
            { 
                if (_ProgressPercent == value)
                    return;
                _ProgressPercent = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region StartCommand
        private ViewModelCommand _StartCommand;

        public ViewModelCommand StartCommand
        {
            get
            {
                if (_StartCommand == null)
                {
                    _StartCommand = new ViewModelCommand(Start);
                }
                return _StartCommand;
            }
        }

        public async void Start()
        {
            cancelSrc = new CancellationTokenSource();

            await Task.Run(() => TaskFunc(cancelSrc.Token));

        }
        #endregion


        #region StopCommand
        private ViewModelCommand _StopCommand;

        public ViewModelCommand StopCommand
        {
            get
            {
                if (_StopCommand == null)
                {
                    _StopCommand = new ViewModelCommand(Stop);
                }
                return _StopCommand;
            }
        }

        public void Stop()
        {
            cancelSrc.Cancel();
        }
        #endregion


        private void TaskFunc(CancellationToken cancel)
        {
            try
            {
                //時間がかかる処理
                for (int i = 0; i < 100; i++)
                {
                    Thread.Sleep(100);

                    cancel.ThrowIfCancellationRequested();
                    ProgressPercent = i;
                }
                MessageBox.Show("Finish!!");
            }
            catch (OperationCanceledException ex)
            {
                MessageBox.Show("Cancelされました",
                                "エラー",
                                MessageBoxButton.OK,
                                MessageBoxImage.Error);
            }
        }
    }





}









2016/10/29

WPF(Livet)でBehaviorを利用してViewからViewModelにデータを渡す

LivetでViewからViewModelにデータを渡すとき、
例えばViewModelCommandを利用して、「ボタンを押したときにTextの値を読み込む」等が例として挙げられていますが、
そのようなやり方ではなく、ViewからViewModelにトリガ経由で値を渡したくなる時があります。
例えば、
・画像をクリックしたとき
・マウスのホイールを動かしたとき
・マウスのカーソルを動かしたとき
等々。。。

そんな時はBehaviorを利用して、値を渡します。
次の例は「表示した画像の中でマウスカーソルが示すPixel数をTextに表示させる」というものです。




 ファイルの構成は以下のようになります。Behaviorフォルダを切って、MouseMoveBehavior.csを新規追加します。


まずは、XAMLです。今回はGrid外にも編集しているので、全部載せます。
xmlns:b="clr-namespace:LivetSample.Behaviors"
でXAMLからMouseMoveBehavior.csにアクセスできるようにしているので、
<b:MouseMoveAction として、使用できるっていうことですね。






    
        
    

    
        
            
        

        
            
        
    

    
        
        



次にViewModelです。
ここでは、MouseMoveのトリガを受けるListenerCommandを定義しています。
そこでViewにBindingしているMousePosに値を代入しています。
using Microsoft.Win32;
using System.Windows;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace LivetSample.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        public void Initialize()
        {
        }

        #region FileUri変更通知プロパティ
        private string _FileUri;

        public string FileUri
        {
            get
            { return _FileUri; }
            set
            { 
                if (_FileUri == value)
                    return;
                _FileUri = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        #region MousePos変更通知プロパティ
        private Point _MousePos;

        public Point MousePos
        {
            get
            { return _MousePos; }
            set
            { 
                if (_MousePos == value)
                    return;
                _MousePos = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        #region WBitmap変更通知プロパティ
        private WriteableBitmap _WBitmap;

        public WriteableBitmap WBitmap
        {
            get
            { return _WBitmap; }
            set
            { 
                if (_WBitmap == value)
                    return;
                _WBitmap = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        #region FileOpenCommand
        private ViewModelCommand _FileOpenCommand;

        public ViewModelCommand FileOpenCommand
        {
            get
            {
                if (_FileOpenCommand == null)
                {
                    _FileOpenCommand = new ViewModelCommand(FileOpen);
                }
                return _FileOpenCommand;
            }
        }

        public void FileOpen()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "ファイルを開く";
            dlg.Filter = "画像ファイル|*.jpg";
            if (dlg.ShowDialog() == true)
            {
                FileUri = dlg.FileName;
                using (var img = new IplImage(FileUri))
                {
                    WBitmap = img.ToWriteableBitmap();
                }
            }
        }
        #endregion

        #region MouseMoveCommand
        private ListenerCommand _MouseMoveCommand;

        public ListenerCommand MouseMoveCommand
        {
            get
            {
                if (_MouseMoveCommand == null)
                {
                    _MouseMoveCommand = new ListenerCommand(MouseMove);
                }
                return _MouseMoveCommand;
            }
        }

        public void MouseMove(Point parameter)
        {
            MousePos = parameter;
        }
        #endregion
    }
}


最後にMouseMoveBehavior.csです。ここでBehaviorを定義しています。
これは使用したいBehaviorごとに自分で作っていかないといけないみたいですね。。。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Livet;
using Livet.Commands;
using Livet.Messaging;
using Livet.Messaging.IO;
using Livet.EventListeners;
using Livet.Messaging.Windows;

using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Input;

namespace LivetSample.Behaviors
{
    public class MouseMoveAction : TriggerAction
    {
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
            "Command", typeof(ICommand), typeof(MouseMoveAction), new UIPropertyMetadata(null)
            );

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        protected override void Invoke(object parameter)
        {
            var eventArgs = parameter as MouseEventArgs;
            var element = AssociatedObject as IInputElement;

            if (Command == null || eventArgs == null || element == null) return;
            var position = eventArgs.GetPosition(element);

            if (Command.CanExecute(position))
            {
                Command.Execute(position);
            }
        }
    }
}