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

2018/05/03

Xamarin.Formsで画像を表示する

Xamarin.Formsで画像を表示する方法はググるとたくさん出てきますが、
なぜか自分の環境だと上手く表示されません。
https://qiita.com/suzu-king/items/a0fe98e7b371e13e49b5
http://oxamarin.com/control-image/
ちなみに自分の環境は下記です。
  • Windows10 64bit
  • Visual Studio Community 2017
  • Xamarin.Forms(UWP + Android + iOS)
  • Prism Template Packでひな形を作成
画像が表示されなかったり、"System.ArgumentNullException: 'Value cannot be null.'とエラーが出てしまいます。(おそらく画像へのPATHが当たっていないと思うのですが、なかなか解決できません。)

よって、ちょっと違う方法で画像を表示させることができたのでその方法を書きます。
まずはXamlのコードを張ります。


    
        
Prism Templateをベースとしているので、余計なコードも入っていますが、重要なのはImageタグでSourceをImgにバインディングしていることだけです。
 次はViewModelのコードです。
namespace ShowImage.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        private ImageSource img;
        public ImageSource Img
        {
            get { return img; }
            set { SetProperty(ref img, value); }
        }

        public MainPageViewModel(INavigationService navigationService) 
            : base (navigationService)
        {
            Title = "Main Page";
            Img = ImageSource.FromFile("lena.png");
            
        }
    }
}
重要な点は、ImageSource.FromFileを使用している点です。他の記事ではFromResourceを使用しているものが多いですが、Resourceとしてではなく、ただのファイルとして指定します。
Prismを用いているので、ImgはSetProertyで変更通知を行っています。

次に画像ファイルをプロジェクトに登録します。
ここで1つ目のポイントはForms用の画像ファイルについて
  • ビルドアクションは"コンテンツ"(なしでもいいと思います)
  • 出力ディレクトリにコピーは"新しい場合はコピーする”

2つ目のポイントはAndroid, iOS, UWPのプロジェクトに画像ファイルを追加するときには、Formsに追加した画像ファイルを"リンクとして追加"することです。

これで各プロジェクトに画像ファイルが登録されますが、実体はFormsに登録してある1つのみにすることができます。
このようにすることでFormsに登録してある画像1つを変更すればすべてのPlatformにおいて変更されます。

初めの方で紹介したURLのやり方がもしできるのであればそれの方がシンプルでよいと思いますが、もしできない場合はこちらの方法も試してもらえればと思います。

参考にしたサイト

http://matatabi-ux.hateblo.jp/entry/2015/05/18/120000

2017/02/05

WPF PrismとOpenCVSharpでWebカメラの画像表示

OpenCVSharpを使ってWebカメラの画像を取得し、WPFのImageで表示します。
プロジェクトは以下のようになっていて、View、Model、ViewModelそれぞれ1ファイルづつあります。

初めに、ViewのMainWindow.xamlです。

    
        
        



次にViewModelのMainWindowViewModel.cs。
画像の表示は別タスクにしていて、async/awaitで非同期処理を行っています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CaptureCamera.Model;

using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.Commands;
using System.Windows.Media.Imaging;

namespace CaptureCamera.ViewModel
{
    class MainWindowViewModel: BindableBase
    {
        Camera camera;
        bool isTask = true;

        public MainWindowViewModel()
        {
        }

        /// 
        /// Change Property
        /// 
        private WriteableBitmap bmp;
        public WriteableBitmap Bmp
        {
            get { return bmp; }
            set { SetProperty(ref bmp, value); }
        }


        /// 
        /// DelegateCommand
        /// 
        private DelegateCommand startCaptureCommand;
        public DelegateCommand StartCaptureCommand
        {
            get { return startCaptureCommand ?? (startCaptureCommand = new DelegateCommand(StartCapture)); }
        }
        private async void StartCapture()
        {
            camera = new Camera();
            await ShowImage();
        }

        private DelegateCommand stopCaptureCommand;
        public DelegateCommand StopCaptureCommand
        {
            get { return stopCaptureCommand ?? (stopCaptureCommand = new DelegateCommand(StopCapture)); }
        }
        private void StopCapture()
        {
            isTask = false;
        }


        /// 
        /// Task
        /// 
        /// 
        private async Task ShowImage()
        {
            while (isTask)
            {
                Bmp = camera.Capture();
                if (Bmp == null) break;

                await Task.Delay(30);
            }
        }
    }
}


最後にModelのCamera.cs。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Win32;
using System.Windows.Media.Imaging;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.Extensions;

namespace CaptureCamera.Model
{
    class Camera
    {
        VideoCapture capture;
        Mat frame;

        public Camera()
        {
            capture = new VideoCapture(0);
            if (!capture.IsOpened())
                throw new Exception("capture initialization failed");
            frame = new Mat();
        }

        public WriteableBitmap Capture()
        {
            capture.Read(frame);
            if (frame.Empty())
                return null;

            return frame.ToWriteableBitmap();
        }
    }
}


WPF PrismとOpenCVSharpを使って画像の表示

先ほど、Prismを使って画像を表示するWPFアプリケーションを作成しました。

○○○○に怖いものなんてない!!: WPF(Prism)で画像の表示
http://kowaimononantenai.blogspot.jp/2017/02/wpfprism.html

画像処理等にはOpenCVSharpを使用することが多いので、
画像入力はOpenCVSharpを使用して、Matで管理するサンプルプログラムに変更しました。
でも、画像の表示はWPFのImageで表示しています。

プログラムは同じようにView,ViewModel,Modelに分けて管理しています。

まずはDisplayImageWindow.xamlです。

    
        
        



次はDisplayImageWindowViewModel.cs。
さきほどと違うのはBitmapImageからWriteableBitmapに変更になったところです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.Commands;
using DisplayImage.Model;

using System.Windows.Media.Imaging;

namespace DisplayImage.ViewModel
{
    class DisplayImageWindowViewModel: BindableBase
    {
        public DisplayImageWindowViewModel()
        {
            Displayer = new ImageDisplayer();
        }

        ImageDisplayer Displayer;

        //=================
        //Change Property
        //=================
        private string filePath;
        public string FilePath
        {
            get { return filePath; }
            set { SetProperty(ref filePath, value); }
        }

        private WriteableBitmap bmp;
        public WriteableBitmap Bmp
        {
            get { return bmp; }
            set { SetProperty(ref bmp, value); }
        }

        //=================
        //DeligateCommand
        //=================
        private DelegateCommand fileOpenCommand;
        public DelegateCommand FileOpenCommand
        {
            get { return fileOpenCommand ?? (fileOpenCommand = new DelegateCommand(FileOpen)); }
        }
        public void FileOpen()
        {
            Displayer.FileOpen();

            FilePath = Displayer.FilePath;
            Bmp = Displayer.GetImage();
        }
    }
}


最後にImageDisplayer.csです。
OpenCVSharpで画像を入力した後、表示するときだけWriteableBitmapに変換して渡します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Win32;
using System.Windows.Media.Imaging;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.Extensions;

namespace DisplayImage.Model
{
    class ImageDisplayer
    {
        public ImageDisplayer()
        {
        }

        public string FilePath { get; set; }
        private Mat Img;

        public void FileOpen()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Open Image File.";
            dlg.Filter = "Image File|*.jpg";

            if (dlg.ShowDialog() == true)
            {
                FilePath = dlg.FileName;
                Img = new Mat(FilePath);
            }
        }

        public WriteableBitmap GetImage()
        {
            return Img.ToWriteableBitmap();
        }
    }
}





WPF(Prism)で画像の表示

Prismを利用したWPFアプリケーションで、選択した画像を表示するプログラムを作成します。

実際に作ったのは下図で、このような機能があります。
①Openボタンでファイル選択ダイヤログが開き、jpgファイルを選択する
②TextBoxにファイルのパスが表示される
③画像が下に表示される


まず、NuGetで「Prism.MVVM」をインストールしておいてください。
プロジェクトでは下図のように3つのフォルダでMVVMごとに管理しています。


 まず、DisplayImageWindow.xamlは以下のようになります。

    
        
        

次に、DisplayImageWindowViewModelは以下のようになります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.Commands;
using DisplayImage.Model;

using System.Windows.Media.Imaging;

namespace DisplayImage.ViewModel
{
    class DisplayImageWindowViewModel: BindableBase
    {
        public DisplayImageWindowViewModel()
        {
            Displayer = new ImageDisplayer();
        }

        ImageDisplayer Displayer;

        //=================
        //Change Property
        //=================
        private string filePath;
        public string FilePath
        {
            get { return filePath; }
            set { SetProperty(ref filePath, value); }
        }

        private BitmapImage bmp;
        public BitmapImage Bmp
        {
            get { return bmp; }
            set { SetProperty(ref bmp, value); }
        }

        //=================
        //DeligateCommand
        //=================
        private DelegateCommand fileOpenCommand;
        public DelegateCommand FileOpenCommand
        {
            get { return fileOpenCommand ?? (fileOpenCommand = new DelegateCommand(FileOpen)); }
        }
        public void FileOpen()
        {
            Displayer.FileOpen();

            FilePath = Displayer.FilePath;
            Bmp = Displayer.Bmp;
        }
    }
}


最後にImageDisplayerです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Win32;
using System.Windows.Media.Imaging;

namespace DisplayImage.Model
{
    class ImageDisplayer
    {
        public ImageDisplayer()
        {
        }

        public string FilePath { get; set; }
        public BitmapImage Bmp;

        public void FileOpen()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Open Image File.";
            dlg.Filter = "Image File|*.jpg";

            if (dlg.ShowDialog() == true)
            {
                FilePath = dlg.FileName;

                Bmp = new BitmapImage();
                Bmp.BeginInit();
                Bmp.UriSource = new Uri(FilePath);
                Bmp.EndInit();

            }
        }
    }
}


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);
    }
}


C#で「using Microsoft.DirectX;」をすると参照エラーになる

C#でDirectXをそのまま使用しようとすると、参照エラーになります。
つまり、下のコードがエラーになる。
using Microsoft.DirectX;

解決策としては、
メニューの「プロジェクト」 -> 「参照の追加」 -> 「参照」
で、以下のdllを選択します。

C:\Windows\assembly\GAC\Microsoft.DirectX\1.0.2902.0__31bf3856ad364e3\Microsoft.DirectX.dll

これでおk。

同じようにこれをしたいときには、
using Microsoft.DirectX.Direct3D;

これを追加します。
C:\Windows\assembly\GAC\Microsoft.DirectX.Direct3D\1.0.2902.0__31bf3856ad364e35\Microsoft.DirectX.Direct3D.dll

2016/11/03

WPFで「using System.Windows.Forms」を行うとエラーになる

WPFでもWindows Formを使用することはよくあることなのですが、
たまに
using System.Windows.Forms
をするとエラーになってしまう時があります。


こうなった時には次の対処を行うことで解決できます。

メニュー -> プロジェクト -> 参照の追加

System.Windows.Formsにチェックを入れて、おk。

これでエラーが出なくなりました。

NyARToolKitCSを使ってみる

カメラ画像からマーカーの位置検出を行いたいので、C#で利用できるARライブラリを探してみました。

いろいろあるみたいですが、ARToolKitのC#版であるNyARToolKitCSを利用することにしました。

HPは下です。
NyARToolkit project
http://nyatla.jp/nyartoolkit/wp/

ソースをGithubで公開しているみたいなので、ダウンロードします。
Masterを利用してもいいのですが、キチンとReleaseされたVerのものをダウンロードします。
Releases · nyatla/NyARToolkitCS · GitHub
https://github.com/nyatla/NyARToolkitCS/releases

現時点では、Ver5.0.8がLatest releaseみたいです。
ここではじめはVer5.0.8を使ってみたのですが、エラーが出てしまい、私の力ではビルドできませんでした。
ですので、ここではVer4.1.1の使い方を説明します。
なので、Ver4.1.1をダウンロードします。

ダウンロードしたフォルダの下記ファイルをVisual Studio で開きます。
今回はVisual Studio 2012を使用します。
NyARToolkitCS-4.1.1\NyARToolkitCS-4.1.1\forFW2.0\NyARToolkitCS.sln

次にサンプルプログラムを動かしてみたいと思います。
まずは「CaptureTest」を右クリックして、「スタートアッププロジェクトに追加」します。

そして、ソシューションをリビルドします。
そうすると、下のようにエラーが出てしまいます。。。


ダウンロードしたソースのReadmeをよく見ると。
NyARToolkitCS-4.1.1\readme.ja.txt
「sandboxは実験的なものを集めてあるので、コンパイルできないかもしれません。」とあります。

なので、
sandboxはビルドの対象から外しましょう。
NyARToolKitCS.sandboxを右クリックして、プロジェクトのアンロードをします。

これでおkなので、もう一度ソリューションのビルドをしましょう。
エラーは出なくなったはずです。

それではデバッグで実行しましょう。
これで動くかと思いきや、エラーが出てしまいました。


 これが出る直接的な原因はわかりませんが、
 これはソリューションのプラットフォームを「AnyCPU」から「x86」にするとおkです。

 よし!じゃあ、こんどこそデバッグで実行。
 でも、またまたエラーが出ます。


「camera_para.datが見つからない」っていってます。
これは、先ほどの修正でAnyCPUからx86にしたので、実行ファイルが生成されるフォルダが変わってしまったことが原因です。
AnyCPUであれば、ここに実行ファイルができますが、
NyARToolkitCS-4.1.1\forFW2.0\sample\CaptureTest\bin\Debug
x86はここにできます。フォルダが一階層深いんですね。
NyARToolkitCS-4.1.1\forFW2.0\sample\CaptureTest\bin\x86\Debug

なので、相対パスで指定しているdatファイルの位置がずれて、見つけられなくなっています。
なので、Form1.csの下記コードの部分に赤字を追加します。

        private const String AR_CODE_FILE = "../../../../../../data/patt.hiro";
        private const String AR_CAMERA_FILE = "../../../../../../data/camera_para.dat";

3度目の正直で、もう一度実行します。
今度はきちんと実行されましたね。




このプロジェクトはシンプルでいいんですが、マーカーが表示されたときにグラフィックが描画されません。

次は描画されるやつをビルドします。「SimpleLiteD3d」です。
先ほどと同じように、スタートアップに設定して、
SimpleLiteD3d.cs内のコードも同じように ../ を追加します。

で、ビルドすると成功すると思うので、実行してください。
はい。毎度のことエラーになりましたね(笑)


このエラーをググると下のサイトにあたるので、この対処を行います。
NKY-TECH. ソフトウェア開発部: .NET Framework 4.0で混合モードのアセンブリを読み込めない旨のエラー
http://nky-tech-sdt.blogspot.jp/2010/02/net-framework-40.html

app.configを設定するといいようです。
でも、プロジェクト内にapp.configが見当たりませんので、作る必要があるようです。

作成するときのいい方法が簡単には見つからなかったので、下のようにして作ります。
SimpleLiteD3dプロジェクトを右クリックで「プロパティ」。下のように、適当に値を追加します。
これ自体はどうでもいい変更ですが、変更することでapp.configが作成されるようです。
 


そうするとプロジェクトにapp.configができるので、下をapp.configの2行目と3行目の間に挿入します。

  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>

そして実行です。

おkでしたね。

長かったですが、これでマーカー検出とグラフィックのサンプルを実行できました。