nomurabbitのブログ

nomurabbitのブログはITを中心にした技術ブログです。

nomurabbitのブログ

PhotoCameraを使ってみる

<はじめに>
スマホアプリにカメラは外せないので調べてみました。写真を撮ってカメラロールに保存するだけの簡単なプログラムを書いてみました。


<プログラム>

public partial class MainPage : PhoneApplicationPage
{
    //-----------------------------------
    // インスタンス変数
    //
    int photoCounter = 0;
    PhotoCamera photoCamera;
    MediaLibrary mediaLibrary;

    //-----------------------------------
    // コンストラクタ
    //
    public MainPage()
    {
        InitializeComponent();
        InitializePhotoCamera();
        InitializeMediaLibrary();
    }

    //-----------------------------------
    // photoCamera初期化
    //
    public void InitializePhotoCamera()
    {
        photoCamera = new PhotoCamera(Microsoft.Devices.CameraType.FrontFacing);

        viewfinderBrush.SetSource(photoCamera);

        photoCamera.CaptureImageAvailable += new EventHandler<ContentReadyEventArgs>(photoCamera_CaptureImageAvailable);
    }

    //-----------------------------------
    // mediaLibrary初期化
    //
    private void InitializeMediaLibrary()
    {
        mediaLibrary = new MediaLibrary();
    }

    //-----------------------------------
    // キャンバスをタップ
    //
    private void canvas1_Tap(object sender, GestureEventArgs e)
    {
        try
        {
            photoCamera.CaptureImage();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    //-----------------------------------
    // イメージの取得
    //
    void photoCamera_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
    {
        try
        {
            photoCounter++;

            string fileName = photoCounter + ".jpg";

            mediaLibrary.SavePictureToCameraRoll(fileName, e.ImageStream);

        }
        catch (Exception ex)
        {
            Dispatcher.BeginInvoke(() => MessageBox.Show(ex.Message));
        }
    }
}


<まとめ>
やってることはphotoCameraのインスタンスを生成して、CaptureImageAvailableイベントに画像を保存するメソッドを定義して、photoCameraのCaptureImageメソッドを呼び出しているだけです。ただし、これだけだとカメラで取得した画像の上下がわけわかんなくなってしまうので、WP自体の回転に応じて画面も回転させてやらないといけません。

Compass、Motion、ときどきGyroscope

<はじめに>
WP7で使えるセンサーについて調べてみました。フレームワークで使えそうなクラスにGyroscope、Compass、Motionがあったので、それぞれを使って実装を試してみました。


<プログラム>

public partial class MainPage : PhoneApplicationPage
{
    //--------------------------------------------
    // インスタンス変数
    //
    Gyroscope gyroscope;
    Compass compass;
    Motion motion;

    //--------------------------------------------
    // コンストラクタ
    //
    public MainPage()
    {
        InitializeComponent();

        GyroscopeInitialize();
        CompassInitialize();
        MotionInitialize();
    }

    #region センサー初期化メソッド

    //--------------------------------------------
    // ジャイロスコープ
    //
    public void GyroscopeInitialize()
    {
        gyroscope = new Gyroscope();

        gyroscope.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<GyroscopeReading>>(gyroscope_CurrentValueChanged);

        //gyroscope.Start();
    }

    //--------------------------------------------
    // コンパス
    //
    public void CompassInitialize()
    {
        compass = new Compass();

        compass.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<CompassReading>>(compass_CurrentValueChanged);

        compass.Start();
    }

    //--------------------------------------------
    // モーション
    //
    public void MotionInitialize()
    {
        motion = new Motion();

        motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);

        motion.Start();
    }

    #endregion 

    #region イベント用メソッド

    //--------------------------------------------
    // ジャイロスコープ
    //
    void gyroscope_CurrentValueChanged(object sender, SensorReadingEventArgs<GyroscopeReading> e)
    {
        GyroscopeReading gyroscopeReading = e.SensorReading;

        Vector3 rotationRate = gyroscopeReading.RotationRate;

        Dispatcher.BeginInvoke(() => this.textBlockRotationX_value.Text = rotationRate.X.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockRotationY_value.Text = rotationRate.Y.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockRotationZ_value.Text = rotationRate.Z.ToString());
    }

    //--------------------------------------------
    // コンパス
    //
    void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
    {
        CompassReading compassReading = e.SensorReading;

        double headingAccuracy     = compassReading.HeadingAccuracy;
        double magneticHeading     = compassReading.MagneticHeading;
        Vector3 magnetometerRating = compassReading.MagnetometerReading;
        double trueHeading         = compassReading.TrueHeading;

        Dispatcher.BeginInvoke(() => this.textBlockheadingAccuracy_value.Text = headingAccuracy.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockMagnetic_value.Text        = magneticHeading.ToString());

        Dispatcher.BeginInvoke(() => this.textBlockMagnetX_value.Text = magnetometerRating.X.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockMagnetY_value.Text = magnetometerRating.Y.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockMagnetZ_value.Text = magnetometerRating.Z.ToString());

        Dispatcher.BeginInvoke(() => this.textBlockTrueHeading_value.Text     = trueHeading.ToString());
    }

    //--------------------------------------------
    // モーション
    //
    void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
    {
        MotionReading motionReading = e.SensorReading;

        AttitudeReading attitude   = motionReading.Attitude;
        Vector3 deviceAcceleration = motionReading.DeviceAcceleration;
        Vector3 deviceRotatingRate = motionReading.DeviceRotationRate;
        Vector3 Gravity            = motionReading.Gravity;

        Dispatcher.BeginInvoke(() => this.textBlockPitch_value .Text = attitude.Pitch.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockYaw_value.Text    = attitude.Yaw.ToString());
        Dispatcher.BeginInvoke(() => this.textBlockRoll_value.Text   = attitude.Roll.ToString());
    }

    #endregion
}


<まとめ>
端末に機能がないのかGyroscopeは上手く使えなかったのですが、CompassとMotionからは値の取得ができました。センサー関連のクラスは使い方がほぼ同じなのでいろいろ活用できそうです。

加速度センサーとInvokeについての覚書

<はじめに>
WP7で加速度センサーを使ってみようと思います。Accelerometerのインスタンスを生成すると、CurrentValueChangedイベントとReadingChangedイベントがあるのですが、「ReadingChangedは古いよ!」って注意されるのでCurrentValueChangedにて実装します。

<プログラム>

public partial class MainPage : PhoneApplicationPage
{
    private Accelerometer acceleroter;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainPage()
    {
        InitializeComponent();

        acceleroter = new Accelerometer();

        acceleroter.TimeBetweenUpdates   = TimeSpan.FromMilliseconds(1000);
        acceleroter.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(acceleroter_CurrentValueChanged);

        acceleroter.Start();
    }

    /// <summary>
    /// acceleroter変化時に呼び出されるメソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void acceleroter_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
    {
        try
        {
            AccelerometerReading accelerometer = e.SensorReading;

            Vector3 acceleration = accelerometer.Acceleration;

            Debug.WriteLine("X:" + acceleration.X.ToString() + " Y:" + acceleration.Y.ToString() + " Z:" + acceleration.Z.ToString());
        }
        catch (Exception ex)
        {
            Dispatcher.BeginInvoke(() => this.textBlock1.Text = ex.Message);
        }
    }
}


<まとめ>
特にこれと言って難しいところはありませんでしたが、しいて挙げるとすればAccelerometerのイベントは別スレッドで発生するようなので、Invokeを使ってUIスレッドに処理を渡すところは気をつけないといけません。

BackgroundWorkerについて(WP7)

<はじめに>
ストアアプリにはasync/awaitを始めとした非同期処理は必須という話をよく聞きます。それならばWindowsPhoneだって必須だと思うのですが、やり方を知らなかったので調べてみました。どうやらBackgroundWorkerを使って処理をバックグラウンドにまわせるようです。


<プログラム>

public partial class MainPage : PhoneApplicationPage
{
    /// <summary>
    /// クラス変数
    /// </summary>
    private BackgroundWorker bw = new BackgroundWorker();

    private bool flag = true;

    #region コンストラクタ・初期化メソッド

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainPage()
    {
        InitializeComponent();

        Initialize();
    }

    /// <summary>
    /// 初期化メソッド
    /// </summary>
    private void Initialize()
    {
        bw.WorkerReportsProgress      = true;
        bw.WorkerSupportsCancellation = true;

        bw.DoWork             += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged    += new ProgressChangedEventHandler(bw_ProgressChanged);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    }

    #endregion コンストラクタ・初期化メソッド

    #region イベント用メソッド

    /// <summary>
    /// プロセス実行用メソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        for (int i = 1; (i <= 10); i++)
        {
            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
                break;
            }
            else
            {
                // Perform a time consuming operation and report progress.
                System.Threading.Thread.Sleep(500);
                worker.ReportProgress((i * 10));
            }
        }
    }

    /// <summary>
    /// Process完了メソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if ((e.Cancelled == true))
        {
            this.textBlockProcess.Text = "Canceled!";
        }
        else if (!(e.Error == null))
        {
            this.textBlockProcess.Text = ("Error: " + e.Error.Message);
        }
        else
        {
            this.textBlockProcess.Text = "Done!";
        }
    }

    /// <summary>
    /// Processキャンセルメソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.textBlockProcess.Text = (e.ProgressPercentage.ToString() + "%");
    }

    /// <summary>
    /// 実行ボタンクリックイベント用メソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void buttonJikko_Click(object sender, RoutedEventArgs e)
    {
        if (bw.IsBusy != true)
        {
            bw.RunWorkerAsync();
        }
    }

    /// <summary>
    /// 停止ボタンクリックイベント用メソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void buttonTeishi_Click(object sender, RoutedEventArgs e)
    {
        if (bw.WorkerSupportsCancellation == true)
        {
            bw.CancelAsync();
        }
    }

    /// <summary>
    /// メッセージボタンクリックイベント用メソッド
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void buttonMessage_Click(object sender, RoutedEventArgs e)
    {
        if (flag)
        {
            this.textBlockMessage.Text = "ほげ";
        }
        else
        {
            this.textBlockMessage.Text = "ふが";
        }

        flag = !flag;
    }

    #endregion イベント用メソッド

}


<まとめ>
非同期処理を使って重い処理なんかを極力バックグラウンドにまわすことで、画面が固まることを避けたいわけですが、WindowsPhone7だとこんな感じでかけますという話でした。WindowsPhone8バージョンは実機を手に入れてからぼちぼちやろうかなと。

ジェネリックな型と非ジェネリックな型について

<はじめに>
ジェネリックについて調べていたらこんな記述がありました。

ジェネリック コレクションを使用する状況
通常は、ジェネリック コレクションを使用することをお勧めします。それは、タイプ セーフの利点をすぐに得られるからです。

タイプセーフの利点?いまいちピンとこなかったので、サンプルプログラムを書いてみました。


<プログラム>

class Program
{
    static void Main(string[] args)
    {
        GenericTest<string> genericTest = new GenericTest<string>();
        genericTest.Add("スカルミリョーネ");
        genericTest.Add("カイナッツォ");
        genericTest.Add("バルバリシア");
        genericTest.Add("ルビカンテ");

        NonGenericTest nonGenericTest = new NonGenericTest();
        nonGenericTest.Add(1192);
        nonGenericTest.Add("いいくに作ろう鎌倉幕府");
        nonGenericTest.Add(null);
        nonGenericTest.Add(3.14159265359);

        Console.WriteLine("// ジェネリック");

        //--------------------------------------------------
        // 指定した型のみを扱えばいいのでスッキリ!
        //
        foreach (var item in genericTest)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine("// 非ジェネリック");

        //--------------------------------------------------
        // 何が来るかわからないのでイライラ >_<;
        //
        foreach (var item in nonGenericTest)
        {
            if (item == null)
            {
                continue;
            }

            Console.WriteLine(item.ToString());
        }
    }
}

class GenericTest<T> : IEnumerable<T>
{
    //--------------------------------------------------
    // クラス変数(List)
    //
    private List<T> myList = new List<T>();

    //--------------------------------------------------
    // 要素を追加
    //
    public void Add(T tempItem)
    {
        myList.Add(tempItem);
    }

    //--------------------------------------------------
    // IEnumerable<T>.GetEnumeratorメソッド
    //
    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < myList.Count; i++)
        {
            yield return myList[i];
        }
    }

    //--------------------------------------------------
    // IEnumerable.GetEnumeratorメソッド
    //
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class NonGenericTest : IEnumerable
{
    //--------------------------------------------------
    // クラス変数(List)
    //
    private ArrayList myList = new ArrayList();

    //--------------------------------------------------
    // 要素を追加
    //
    public void Add(object tempItem)
    {
        myList.Add(tempItem);
    }

    //--------------------------------------------------
    // IEnumerable.GetEnumeratorメソッド
    //
    IEnumerator IEnumerable.GetEnumerator()
    {
        for (int i = 0; i < myList.Count; i++)
        {
            yield return myList[i];
        }
    }
}


<実行結果>
f:id:nomurabbit:20131018234240p:plain


<まとめ>
ジェネリックコレクションだと要素の型のTが担保されるってことですね!一方非ジェネリックコレクションだと要素の型が担保されないから、クラス内で要素の追加時にhogehogeするか、foreachで回しながらfugafugaするしかないってことですね!

献立の中からカツを探し出すプログラム(LINQ)

<はじめに>
今週の献立の中で何曜日のメニューがカツなのか気になって仕方がない時ありますよね?
そこで、IEnumerableを実装した自作クラスを作って、LINQの恩恵を受けつつカツの曜日を抽出することにしました。


<プログラム>

class Program
{
    static void Main(string[] args)
    {
        MySampleClass<string> mySampleInstance = new MySampleClass<string>();

        mySampleInstance.Add("月:カレー");
        mySampleInstance.Add("火:トンカツ");
        mySampleInstance.Add("水:ラーメン");
        mySampleInstance.Add("木:スパゲティ");
        mySampleInstance.Add("金:メンチカツ");

        Console.WriteLine("// 今週の献立は…");

        //--------------------------------------------------
        // foreachにてデータを取得
        //
        foreach (var item in mySampleInstance)
        {
            Console.WriteLine(item);
        }

        //--------------------------------------------------
        // LINQにてデータを取得
        //
        var list = mySampleInstance.Where(p => p.Contains("カツ")).Select(p => p.Split(':'));

        Console.WriteLine("// カツの曜日は…");

        foreach (var item in list)
        {
            Console.WriteLine(item[0] + "曜日");
        }
    }
}

class MySampleClass<T> : IEnumerable<T>
{
    //--------------------------------------------------
    // クラス変数(List)
    //
    private List<T> myList = new List<T>();

    //--------------------------------------------------
    // 要素を追加
    //
    public void Add(T tempItem)
    {
        myList.Add(tempItem);
    }

    //--------------------------------------------------
    // IEnumerable<T>.GetEnumeratorメソッドを実装(必須)
    //
    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < myList.Count; i++)
        {
            yield return myList[i];
        }
    }

    //--------------------------------------------------
    // IEnumerable.GetEnumeratorメソッドを実装(必須)
    //
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    } 
}


<実行結果>
f:id:nomurabbit:20131018223556p:plain


<まとめ>
IEnumerableを実装するときに注意するべきは、ジェネリック型、非ジェネリック型に対応したGetEnumerator()メソッドを定義することだと思います。このあたり「yieldって何ぞ?」状態だとさっぱりだと思うのですが、何回か自分で書いてるうちになんとなくわかってくると思います。それにしてもLINQ × ラムダ式の組み合わせは感覚的に書けるので気持ちがいいですね~♪