CloudFlash をデジカメではない環境で動かそう

image

先日、CloudFlash というのを知って、ちょっと買ってみました。デジカメに入れて WiFi でデータを飛ばして PC で受け取ることができる、というものです。以前話題になったときに興味はあったのですが、デジカメもあまり使わなくなったので、忘れていました。日経 Linux 2014年08月号 に @netbuffalo さんの記事があって、この CloudFlash をホットスポットにつなげて使うという記事があり、中身が Linux ということが分かりました。なるほど、Linux + WiFi 環境ならば単体で Linux 環境が手にはいるのでは?と思ったわけです。

■電源供給だけで動くのか?

CloudFlash と同じ製品はいくつかあって、もともとがデジカメに差し込んで使う製品です。中身の Linux を弄ろうとする人は稀らしくて、デジカメで動くけど、PC のハブに差し込んだらどうなのか? 電源だけならどうなのか?という話はあまりないんですよね。

結論から言うと

  • 電源供給だけでは(たぶん)動きません。
    コンセントに差し込んだ、充電用ハブに SD カードアダプタを差し込んだけど、CloudFlash はうんともすんとも言いません。
  • PC のハブに差し込むときは、SD カードアダプタとの相性があります。
    私の場合、一番左のサンワサプライ ADR-SDU2L だけが動きました。
    真ん中の2つは100円ショップで買ったものですが、動きませんでした。
  • デジカメの場合は、全然大丈夫です。

私の場合、一番右の SD カードアダプタを持っていました。100円ショップで買ったもので、普通に SD メモリが読めていたから、これでいけるのでは?と思っていたものの、動かず…ひょっとしたら、デジカメオンリーなのかと思って @netbuffalo さん に尋ねたところ、SD カードアダプタでも動くことが判明。そんな訳で、上記の SD カードアダプタを買ったわけです。

相性が悪いのかどうかわかりませんが、サンワの ADR-SDU2L(500円で買った)は動いて、100円ショップの3つは動きません。正確には、左から2番目の SD カードリーダーは、最初動いたような感じなのですが、後から動かなくなりました。

電源供給の場合には動かず、SD カードリーダーやデジカメで動く。またデジカメがスリープした状態では動かない(WiFi が切れる)ところを見ると、CloudFlash 内蔵のチップとデジカメ(あるいは PC )がやり取りをしていて、それが切れるとスリープ状態に入るようです。あと、おそらくデジカメのほうが電力は安定しているでしょうから、デジカメのメモリ程度の安定性が必要なのかもしれません。

■試しに Raspberry Pi の USB につなげてみると

CloudFlash となんらかのやり取りをしていれば良いのであれば、Raspberry Pi にそれを肩代わりsてい良いわけです…ってことで、Raspberry Pi の USB につなげてみました。

結果は、CloudFlash のホストは見れるのですが(iPhone から Wifi の探索でチェックしています)、いざ繋げて写真などをダウンロードしようとすると WiFi が落ちます。RasPi に無線 LAN をつなげていることもよくある現象なので、これと同じ感じですね。たぶん、途中で電力が足りなくなって WiFi が落ちてしまっているようです。外部電源のハブをつなげるとうまくいくかもしれません。

■telnet がつながる

ip が 192.168.1.1 固定ですが、telnet がつながります。

image

ただ、これだといちいち CloudFlash をホストにして接続しないといけないので、結構面倒です。ホットスポット(自宅の無線LANネットワーク)に参加させる方法は、先の日経 Lunix の記事にあるので後で試してみます。ルートに autorun.sh を作ってやる方法なので、汎用性があってよいと思います。

CloudFlash に差し込むメモリ自体は、/mnt/sd  で覗くことができます。ftp もつながるそうなので、比較的簡単に内部にはアクセスできそうです。Linux コマンドは BusyBox が使われていました。

BusyBoxって何ぞや?:組み込みLinuxで際立つ「BusyBox」の魅力 (1/2) – MONOist(モノイスト)
http://monoist.atmarkit.co.jp/mn/articles/0802/04/news114.html

ただし、vi も動かない貧弱な環境なので、この環境のままで何かやるのは難しそうです。

image

image

デジカメ内部でRubyを動かす狂気!無線LAN内蔵SDカードアダプタPQI Air Cardの間違った使い方
http://hitoriblog.com/?p=12627

Ruby が動くのだから Mono も動かしたい、と思ったり。
# この記事をみると、電源供給だけで動いているのですが…私の手元のやつだと動かないんですよね。PQI だから動くのかも。

~~

追記:2014/09/16
グリーンハウス SDXC対応USB2.0カードリーダ/ライタ ホワイト GH-CRSDXC と CloudFlash の組み合わせで行けました。さすが「あつをリーダー」ッ!!!
これで、電源供給だけで CloudFlash を操作できます。

カテゴリー: 雑談 | CloudFlash をデジカメではない環境で動かそう はコメントを受け付けていません

F#でOpenCvSharpを使ってカメラキャプチャする

OpenCvSharp を使ってカメラキャプチャをする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6258

の F# 版です。WPF で作ってあります。

module MainApp

open System
open System.Windows
open System.Windows.Controls
open FsXaml
open OpenCvSharp.CPlusPlus
open OpenCvSharp.Extensions

type MainWindow = XAML<"MainWindow.xaml">

let mutable _frame:Mat = null

/// <summary>
/// カメラを表示
/// </summary>
/// <param name="id"></param>
let camera(id:int) =
    let cap = new VideoCapture()
    cap.Open(id)
    if cap.IsOpened() then
        let fs = FrameSource.CreateCameraSource(0)
        let win = new Window("camera")
        let frame = new Mat()
        _frame <- frame
        let mutable loop = true
        while loop do
            cap.Read( frame )
            if frame.Empty() = false then
                win.ShowImage( frame )
                let key = Cv2.WaitKey(100)
                if key = 27 then
                    loop <- false
            else
                loop <- false

let loadWindow() =
   let window = MainWindow().CreateRoot()
   let accessor = MainWindow.Accessor(window)
   // ボタンクリック時のイベント
   accessor.btnCamera.Click.Add( fun e -> camera(0) )
   accessor.btnCapture.Click.Add( fun e -> 
        accessor.img.Source <- WriteableBitmapConverter.ToWriteableBitmap(_frame)
       )
   // ウィンドウを返す
   window

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

やっていることは、C# 版と同じで、カメラ用のウィンドウを開くのとボタンを押したときにキャプチャする処理が入っています。ファイルに保存する処理はほとんど同じなので省略。

F# で WPF を扱うときは FsXaml のタイププロバイダを使います。ボタンイベントとかはボタン自身に名前を付けて Add.Click 等で追加すれば ok.
フォームの場合は Bitmap に落とすのに BitmapConverter.ToBitmap を使いますが、WPF の Image.Source に対しては WriteableBitmapConverter.ToWriteableBitmap を使って Mat から WritableBitmap に落とします。
Mat と Bitmap の相互変換と、float/double で処理した自前の Pixcels クラスを使えば、画像解析のトライアンドエラーがしやすいかなと、奮闘中です。もう少しできたら github にでも。

ひとまず、動作サンプルはこちら。直ぐ動くように opencv の dll も含んでいるのでちょっと大きいです。

カテゴリー: F#, OpenCV | F#でOpenCvSharpを使ってカメラキャプチャする はコメントを受け付けていません

クラス名はUMLスタンダードに則って作るのがベター

 

気になったので、ラショナルプロセス風なUMLスタンダードパターンを紹介しておきます。

Amazon.co.jp: ラショナル統一プロセス入門 第3版 (ASCII Software Engineering Series): フィリップ クルーシュテン, Philippe Kruchten, 藤井 拓: 本

  1. ユースケース等で対象のオブジェクトを抽出する。
  2. オブジェクトを辞書化する。
  3. 辞書を整理して、オブジェクト図を作成する。
  4. オブジェクト図を抽象化して、クラス図を作成する。
  5. クラス図を作成した後に、GoF などのパターンがあれば、それに準じる。

統一プロセス自体には 5 番目のパターン適用はありません(GoF の前のため)。このため、GoF などのパターン(特に Java のエンタープライズパターンなど)が既にある状態での、現在では Manager などの「パターン特有の単語」が頻出していますが、もともとはパターン適用時の単語なので変更しては駄目なんですよ。

最初にユースケースからオブジェクトを抽出するプロセスでは、具象的な名前を使います。これは、オブジェクト図(具象図)とクラス図(抽象図)を明確に区別するためでもあり、いきなりモデル化してしまうことにより、間違った抽象化を避けるためです。今でいえば、ドメイン駆動などがあるので、業務ドメインに特化した言語/単語を使いつつ、クラス図にて抽象化(一般化した名前)に落ち着けるか、ドメインに特化した名前をそのまま使うか、の判断を行います。このプロセスは、2 の辞書化の部分にもあてはまります。

そんな訳で、「今」しか学ばないと名称に混乱するでしょうが、「歴史」的なGoF 等のパターンにでてくる名称と統一プロセスを知っておくと、特に悩むことはありません。抽象化と具象化の部分が区別されますからね。なんか、ちょっと Twitter でのコメントが気になったので、書いておきます。

~~

余談ですが、名称をつけるときのテクニックは「明確」にあります。昔よりも IDE のインテリセンス/補完機能が働いているので、統一的な名称を単語の前に出すことで、うまくマッチせることができます。たとえば、本に関係するメソッドは、

  • ListBook
  • SortByBookID
  • GetBookTitle

とするよりも

  • BookList
  • BookSortByID
  • BookTitleGet

な風にすると揃えられます。Get/Set のように動詞を前にするか後ろにするかは異論があるでしょうが、それは業務ドメインによって判断してください。もちろん、この「Book」の場合、Book クラスを作っておいて、メソッド/プロパティでまとめる、ってのが王道ですが(そのあたりが、オブジェクト指向なんですけど)。

カテゴリー: 開発 | クラス名はUMLスタンダードに則って作るのがベター はコメントを受け付けていません

OpenCvSharp を使ってカメラキャプチャをする

画像解析まわりを F# で組もうとする場合、OpenCV の Mat と相互変換しなくちゃいけません。OpenCV 自体は C++ ベースなのでロジックを C++ で組むのがベータなのですが、ちょっとトライ&エラーがやりづらいんですよね。OpenCV の機能自体を使うのであれば、そのまま OpenCvSharp を使えばいいのですが、内部ロジックを弄る必要がある(特にテンプレートマッチの関数とか特徴量の関数とか)予定なので、できるだけデータ変換はおこしたくないな、と思っていました。

■OpenCvSharp は導入が簡単である

shimat/opencvsharp
http://github.com/shimat/opencvsharp
OpenCvSharpをつかう その17(NuGetで導入) – schima.hatenablog.com
http://schima.hatenablog.com/entry/2013/12/15/110513

OpenCvSharp 自体を知ったのは検討したのは3年前ぐらいで、その頃ちょうど .NETラボで亀川さんが講演していて、OpenCvSharp あたりを使った画像解析の話だったんですよね。データの変換自体が、C++ のネイティブなポインタと、.NET Framework のラッピングされたポインタでは、点単位ではひどく遅くなるだろうという会話をしたのを覚えています。その頃は、導入が結構面倒(OpenCvのDLL自体が面倒)だったので、C++ で組んでも C# で組んでもあまり変わりがなかったのですが、いつの間にか(失礼)、NuGet で設定ができるようになっていました。

Nuget で「OpenCvSharp」で検索してインストールすると、必要な DLL とプロジェクトの設定が自動的に行われます。

現状の OpenCV は 2.49(3のほうは、まだアルファ版です)なのですが、これらが一気に dll フォルダにダウンロードされます。

C++ プロジェクトで組むときに、最初に面倒なのがこの DLL を導入する(いちいちバージョンを変えないといけない)ことなので、プログラミング時の敷居がぐっと下がっています。

■カメラの画像をキャプチャする

サンプル自体は、先の github の中
https://github.com/shimat/opencvsharp/tree/master/sample

にあるので、目的のものを動かしてみるとよいでしょう。もともと OpenCV に含まれているサンプルが C#/VB 版に書き直されている感じです。

サンプルコードはこちら
http://1drv.ms/1lREkUV

■カメラへの接続

カメラへの接続は VideoCapture で設定すれば ok です。capture.Read あたりも C++ 版と同じなので、OpenCv を使っている場合はだいたい想像がつくでしょう。このウィンドウは OpenCv が勝手に作るウィンドウなので、Cv2.WaitKey でキー待ちをしてエスケープキーで閉じるようにしておきます。また、このウィンドウはスレッドで動いているらしく、async/await とか createwindows しなくても、モーダレスで動きます。これはこれで便利。

void Camera()
{
    var capture = new VideoCapture();
    // 最初のカメラを取得
    capture.Open(0);
    if (!capture.IsOpened())
        throw new Exception("capture initialization failed");
    var fs = FrameSource.CreateCameraSource(0);
    using (var normalWindow = new Window("normal"))
    {
        var normalFrame = new Mat();
        var srFrame = new Mat();
        while (true)
        {
            capture.Read(normalFrame);
            if (normalFrame.Empty())
                break;

            _frame = normalFrame;
            normalWindow.ShowImage(normalFrame);
            int key = Cv2.WaitKey(100);
            if (key == 27) break;   // ESC キーで閉じる
        }
    }
}

■カメラから画像をキャプチャ

カメラから Bitmap へ落とすのも非常に簡単です。BitmapConverter.ToBitmap でフレームを渡してやれば Bitmap で落とせます。これをそのままピクチャボックスに張り付ければ画像が出ます。ここの BitmapConverter は以前とは違って OpenCvSharp.Extensions の中に入っているそうです。

private void button2_Click(object sender, EventArgs e)
{
    // カメラからキャプチャ
    Bitmap bitmap = BitmapConverter.ToBitmap(_frame);
    this.pictureBox1.Image = bitmap;
}

■ファイルに保存する

以前作ったツールから流用して、Bitmap を PNG 形式にして保存します。日付と連番でファイル名を付けるので

/// <summary>
/// マイピクチャに保存
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
    var bmp = (Bitmap)pictureBox1.Image;
    var fmt = ImageFormat.Png;
    var path = MakeFileName( fmt );
    bmp.Save(path);
    label1.Text = Path.GetFileName(path);
}

string FOLDER = @"d:workblogimage";

string MakeFileName(ImageFormat fmt)
{
    string path = FOLDER;
    DateTime dt = DateTime.Now;
    string dname = string.Format("{0:0000}{1:00}{2:00}", dt.Year, dt.Month, dt.Day);
    var files = System.IO.Directory.GetFiles(FOLDER);
    var file = files.Where(f => f.IndexOf(dname + "_") > 0).Max();
    var ext = fmt.ToString();
    if (ext == ImageFormat.Jpeg.ToString()) // 拡張子を変更
    {
        ext = "jpg";
    }
    if (file == null)
    {
        path += dname + "_01." + ext;
    }
    else
    {
        var mat = new Regex(@"_(\d+)").Match(file);
        int num = int.Parse(mat.Groups[1].Value);
        num++;
        path += dname + "_" + num.ToString("00") + "." + ext;
    }
    return path;
}

と、こんな感じにしておけば、手軽に Web カメラから画像へ落とせるようになります。
あとで、テンプレートマッチング用の関数のスピードも確認しておきましょう。これができると、ほぼ F# で組めるので。

カテゴリー: C#, OpenCV | 2件のコメント

画像認識のプレ加工に F# を使う

最終的には OpenCV と組み合わせるはずなのですが、いちいち C++ で組むのも面倒なので、事前加工の部分を F# で組みます。

image

こんな画像をグレースケールに変えたり、2値化するだけですね。

imageimage

imageimage

■画像加工にパイプを使う

C++ で OpenCV を使っていた頃から考えていたのですが、画像加工のフィルタ部分はパイプ(>>)を使うとスムースです。当然フィルタなんだから、画像関係の多種の filter と同じなので当然といえば当然。

        let path = @"D:\work\PiVistion\src\PiVision\FsVision.Test\data\001.bmp"
        let pi = FsVision.LoadForm( path )
        // グレースケール
        pi |> Pixel.toGray 
           |> FsVision.SavePixel( @"D:\work\PiVistion\src\PiVision\FsVision.Test\data\001-pi-gray.bmp" )

F# では、こんな風に |> を使ってつなげます。

■PowerPack を使うかどうか?

The Old F# “PowerPack” – Home
https://fsharppowerpack.codeplex.com/
fsprojects/powerpack
https://github.com/fsprojects/powerpack

画像加工の場合、2次元マトリクスが頻発するので、F# powerpack を使うのがベターなんでしょうが、要素として RGBA を使わないといけないので、Matix<RGBA> と定義したいんですよね。が、が、なんとなくうまく動かない。Matrix<‘T> になっているけど、実質 Matirx<float> しか動かないようです。RGBA を別々に扱っても良いのですが、試してみると F# の List#Item は結構遅くて、短時間に 640×480 の画素数を処理するのは難しそうです。シーケンシャルに for it in lst do な感じでイテレータアクセスすれば結構なスピードで動きます。まあ、有限要素法の件もあるし、マトリクス関係は自作しましょう、ということで powerpack 無しで作っています。

どちらかというと、OpenCV の各種関数を F# から使うところに力を入れた感じ。

■RGB/YUV/YIQ/HSV の相互変換

Zaku認識(2) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/601

かつて Zaku 認識(4年も前の記事になるのか)をやろうと思って、色相とか輝度とかを扱ったので、これの F# 版も作っておきます。3×3 か 4×4 のマトリクスなんだが、これをいちいち自前の matrix に通すと遅いので、直接計算します。さほど手間ではないし。

 
type RGB = { R:float; G:float; B:float; }
type YUV = { Y:float; U:float; V:float }
type YIQ = { Y:float; I:float; Q:float }
type HSV = { H:float; S:float; V:float }

module Pixel =

    type Conv =
        static member toYUV (rgb:RGB) =
            let y =  0.299 * rgb.R +  0.587 * rgb.G +  0.114 * rgb.B 
            let u = -0.169 * rgb.R -  0.331 * rgb.G +  0.500 * rgb.B 
            let v =  0.500 * rgb.R + -0.419 * rgb.G -  0.081 * rgb.B 
            { Y=y; U=u; V=v }
        static member toRGB (yuv:YUV) =
            let r =  1.000 * yuv.Y +  0.000 * yuv.U +  1.402 * yuv.V 
            let g =  1.000 * yuv.Y -  0.344 * yuv.U -  0.714 * yuv.V 
            let b =  1.000 * yuv.Y +  1.772 * yuv.U +  0.000 * yuv.V 
            { R=r; G=g; B=b }
        static member toYIQ ( rgb:RGB ) =
            let y = 0.2990 * rgb.R + 0.5870 * rgb.G + 0.1140 * rgb.B
            let i = 0.5959 * rgb.R - 0.2750 * rgb.G - 0.3210 * rgb.B
            let q = 0.2065 * rgb.R - 0.4969 * rgb.G + 0.2904 * rgb.B
            { Y=y; I=i; Q=q }
        static member toRGB ( yiq:YIQ ) =
            { R = 1.001 * yiq.Y + 0.955 * yiq.I + 0.622 * yiq.Q;
              G = 0.999 * yiq.Y - 0.272 * yiq.I - 0.648 * yiq.Q;
              B = 1.004 * yiq.Y - 1.106 * yiq.I + 1.704 * yiq.Q; }
        static member toHSV ( yiq:YIQ ) =
            { H = System.Math.Atan2(yiq.I, yiq.Q) ;
              S = Math.Sqrt( yiq.I * yiq.I + yiq.Q * yiq.Q) ;
              V = yiq.Y; }
        static member toYIQ ( hsv:HSV ) =
            { Y = hsv.V; 
              I = hsv.S * Math.Sin( hsv.H ) ;
              Q = hsv.S * Math.Cos( hsv.H ) ; }
        static member norm (rgb:RGB) =
            let r = if rgb.R < 0.0 then 0.0 else if rgb.R > 1.0 then 1.0 else rgb.R
            let g = if rgb.G < 0.0 then 0.0 else if rgb.G > 1.0 then 1.0 else rgb.G
            let b = if rgb.B < 0.0 then 0.0 else if rgb.B > 1.0 then 1.0 else rgb.B
            { R=r; G=g; B=b }

    let map (f:(RGB->RGB)) (pi:pixels) =
        pixels [ 
            for row in pi.Data do
                yield [ for it in row do
                            yield f( it )
                        ]] 
    /// <summary>
    /// gray scale filter
    /// </summary>
    /// <param name="pi"></param>
    let toGray (pi:pixels) =
        pi |> map ( fun x ->
            // 加重平均 
            let c = (0.896 * x.R + 1.76 * x.G + 0.34 * x.B) / 3.0
            { R=c; G=c; B=c } )
    
    let to2Value (ce:float) (pi:pixels) =
        pi |> map ( fun x ->
            let c = (0.896 * x.R + 1.76 * x.G + 0.34 * x.B) / 3.0
            if c <= ce then { R=0.; G=0.; B=0. } else { R=1.; G=1.; B=1. }  
            )

    /// <summary>
    /// sepia filter
    /// </summary>
    /// <param name="pi"></param>
    let toSepia (pi:pixels) =
        pi |> map ( fun x -> 
            let r = 0.393 * x.R + 0.769 * x.G + 0.189 * x.B
            let g = 0.349 * x.R + 0.686 * x.G + 0.168 * x.B
            let b = 0.272 * x.R + 0.534 * x.G + 0.131 * x.B 
            { R=r; G=g; B=b } |> Conv.norm)


こっちはいずれ整理して github に上げる予定です。

カテゴリー: F# | 画像認識のプレ加工に F# を使う はコメントを受け付けていません

麻雀牌認識のプロセスを考える

image

■目的

こんな画像から、14牌を認識する、とする。この画像自身の切り出しをどうするのか?って問題があるけど、麻雀牌自体は手元に置くことが多いし、ある程度画像認識の範囲を絞り込むことができるだろう…という仮定からスタートする。もちろん、これは「仮定」なので、前提条件が間違っている場合も頭の隅に入れておく。

■手順を考察

いきおい、牌の物体認識から入ってしまうと思われるが、それは駄目だ。というか無駄だ。牌の形は「四角」で固定されているし、牌の表面は主に白でおおわれている。さらに背景は、白以外(大抵は緑のマット?)なのだから、わざわざ物体認識という大層なことをやらなくてもいい。白っぽい部分を抽出すれば、牌と特定できるだろう。

さらに、この場合は牌が14個連なっている。多少上下にずれているが、だいたいは真ん中で一直線になっている。牌の数は決まっていて、14個であるのだから、先に大まかな位置が分かったら、単純に14等分すればそれぞれの牌の位置がわかるだろう。もちろん、上下のずれ、左右のずれ(カメラ位置のずれ)などを考慮するために若干ずらした牌の位置でも確認が必要になる。

牌の大まかな位置がわかったら、個別の牌を認識させる。これは特徴量抽出かテンプレートマッチングでよい。たぶん、テンプレートマッチングでも十分なスピードが出るのではないだろうか?牌の種類自体は、たかだか9×3+7=28種 花牌などを入れれば36種しかない。アルファベットよりも少ない。大文字小文字を区別する必要もないし、似ている文字(ゼロとオーとか)もないので、ほとんど誤認識はせずに済むだろう。教師画像は多少の上下の揺れと、上下さかさまの画像を用意しておけばよい。グレースケールのテンプレートマッチング法で十分だと思う。

赤牌をどう認識するかだが、これはテンプレートマッチングした後にあらためて色で識別すればよい。伍萬など決まっているから、それで特定ができる。

牌が特定できたら、後は自由に待ちとかを計算すればOK。

■認識の流れ

  1. 背景色とは異なる、白っぽい部分を特定する。
  2. 白っぽい部分は、横長と仮定する(先行きは、縦長やななめでも良い)
  3. 白っぽい横長の牌並びの、左端と右端の位置を特定する。
  4. 左端と右端で14分割する。
  5. 14分割した牌の位置に対して、テンプレートマッチングする。テンプレートマッチは上下逆さまの場合と上下の位置の揺れも教師画像として用意する。
  6. 牌の特定ができる。

これでやってみよう。

カテゴリー: OpenCV | 麻雀牌認識のプロセスを考える はコメントを受け付けていません

麻雀牌言語を Forth で作ろう

無事、Linq2Twitter で絵文字が出たところで、麻雀牌言語に取り組んでいます。ユニコードには麻雀牌の領域があって、麻雀牌 – Wikipedia http://ja.wikipedia.org/wiki/%E9%BA%BB%E9%9B%80%E7%89%8C を見ると、U+1F000 – U+1F02B が割り当てられています。

な感じで表示ができます(残念ながら、Mac/iPhone の場合はフォントがないらしく豆腐になりますが)。

■言語仕様は Forth

Tiny Forthインタープリタ
http://middleriver.chagasi.com/electronics/tforth.html

Forth 言語をご存じでしょうか?実際使ったことはないのですが、Cマガジンにスタックマシン言語として、スタックを最大に利用した(レジスタを使わない)方式のプログラミング言語です。確か、逆ポーランド法の解説だったかコンパイラの解説だったかで出た記事だったと思います。CPU の高速なレジスタに直接アクセスする代わりに、スタックに積んで計算するというちょっと回りくどいのですが、アセンブリ的に覚えやすくて実装しやすいという利点がある、ってことだけ覚えています。

実際、先のリンクをたどると tiny forth が C言語で600行弱しかありません。今の F# などで組めばもっと短くなるはずです。

そこで、リファレンスガイド を麻雀牌に直してプログラム言語化してみようという試みです。

■言語ルールを決める

文法よりも先に「ルール」を決めます。ええ、麻雀牌なんだから、13牌か14牌がいいですよね。

  1. 順子または暗刻が4つ、対子が1つで、1行14牌でプログラムする。
  2. キーワードは暗刻に割り当てる。
  3. ワードは順子に割り当てる。
  4. 数字、文字列はどうするか検討中。
  5. 14牌で、上がっていなければエラーとする。

5番目が重要ですね。きちんと、上がりを作らないとコンパイルエラーになります。

…ってことにしておきます。

■文法に割り与える

Tiny Forth のリファレンスガイドに準じて、適当に暗刻を割り当てます(覚えづらいので、後で割り当て直しますが)。

DRP スタックに積む [hai]111m[/hai]
DUP スタックに複製を積む [hai]222m[/hai]
SWP スワップする [hai]333m[/hai]
>R  パラメータスタックからリターンスタックへ積む [hai]444m[/hai]
<R  リターンスタックからパラメータスタックへ積む [hai]555m[/hai]

+ 加算 [hai]666m[/hai]
– 減算 [hai]777m[/hai]
* 乗算 [hai]888m[/hai]
/ 除算 [hai]999m[/hai]
% あまり [hai]111s[/hai]

AND 論理積 [hai]222s[/hai]
OR  論理和 [hai]333s[/hai]
XOR 排他的論理和 [hai]444s[/hai]

= [hai]555s[/hai]
< [hai]666s[/hai]
> [hai]777s[/hai]
<= 不要かも [hai]888s[/hai]
>= 不要かも [hai]999s[/hai]
<> [hai]111p[/hai]
NOT [hai]222p[/hai]

メモリ操作用
@ 2バイトスタックに積む [hai]333p[/hai]
! 2バイトスタックに積む [hai]444p[/hai]
@@ 1バイトスタックに積む [hai]555p[/hai]
!! 1バイトスタックに積む [hai]666p[/hai]
#面倒なので、char, int に揃えてしまうか。

出力ワード
. 出力 [hai]777p[/hai]

辞書操作
: … ; … ワードを辞書に登録 [hai]HHH FF[/hai]
VAR 変数を定義 [hai]FFF[/hai]
FGR ワードを削除(不要?)[hai]CCC[/hai]

制御文
IF … THEN … [hai]TTT SSS[/hai]
IF … ELSE … THEN … [hai]TTT NNN SSS[/hai]
BEGIN … END … スタック上の1番目のデータが偽ならば繰り返し [hai]PPP NNN[/hai]
BEGIN … WHILE … WEND … … (不要?)[hai]PPP SSS NNN[/hai]
DO … LOOP …   (不要?)[hai]PPP TTT[/hai]
I カウンタ値のアドレスをスタックに積む [hai]NNN [/hai]

ループ関係は冗長なので、ひとつだけ定義すれば OK かも。

■サンプルコード

先の Tiny Forth のページから “3+4*5” の計算が

3 4 5 * + 

となるので、

[hai]345m 456s 567p 888m 77m[/hai]

ではどうだろうか?順子の頭を数字とみなして計算する感じ。答えは、23 なんだが…分かりづらいことおびただしい w 慣れると打ちやすいかも?

■Pi㊥Pad

麻雀牌専用エディタ。先日、キーボード判別できないことが分かったので、キーボード自体はユーザーが設定ってことになりそうです。クリップボード機能とツイート機能だけあれば十分かと。Windows ストアに近日、公開します。

image

■Wordpress の麻雀牌プラグイン

麻雀牌プラグイン | 1000sei WordPress
http://wordpress.1000sei.com/ja/mahjong/

Unicode の麻雀牌は Live Writer 等で投稿できないので、表示にはプラグインを利用しています(爆

カテゴリー: 雑談 | 麻雀牌言語を Forth で作ろう はコメントを受け付けていません

絵文字を LinqToTwitter で送るための方法

ChuPad を製作している途中で、ツイッターに経過を表示させようと思いつきました。ひとりで作成しているとつまらないで、コピペするよりも直接ツイートできると面白いかと。ChuPad 自体は、Windows ストアアプリで作っているので(内部的にはユニバーサルアプリなので、お次は Windows Phone アプリになります)、People あたりに共有コントラクト…と考えたのですが、People に対しては URL しか共有できないのですね orz なんだかなー。どうやら、公式 Twitter アプリへの共有もできないらしいので、自前で実装することにしました。

で、いくつか探したなかで、LINQ To Twitter を使おうかなと、

LINQ to Twitter – Home
http://linqtotwitter.codeplex.com/

自前のツールは、デスクトップ環境でしか動かないのと、PCL で試してみたかったので、これを使うことに決まました。実際はツイートしかしないので、機能的にはオーバーなんですけどね。

■LinqToTwitterのPCL版を使う

PCL版は NuGet でダウンロードができます。「LinqToTwitter」で検索すれば ok です。

20140903_02

動作確認用のサンプルコードとしては、先の「LINQ to Twitter – Home」にはデモ用のコードがあるので、それを使うといいでしょう。Twitter API ひとつひとつにデモをつけているので、目的のものを探すのに苦労はするのですが。

ストア版の場合は、以下のように「LinqToTwitter.WindowsStore」を使います。なんか、ストア版、WPF版などクラスの名前が変わっているので、ネット上ではサンプルを探すのが苦労するのですが、本家のデモコードを見るとあっさり書いてあります。

using LinqToTwitter.WindowsStore;
using LinqToTwitter;

LinqToTwitter が便利なのは、最初の認証部分も受け持ってくれるところです。認証部分をブラウザに渡したり、WebView で作ったりするのが面倒なので、あらかじめ用意してあるものを使ったのが次のコードです。

private string API_KEY = "";
private string API_SECRET = "";
private string API_CALLBACK = "";
// @chu_lang へ投稿
private string OAUTH_KEY = "";
private string OAUTH_SECRET = "";

/// <summary>
/// ツイッター投稿
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
async void kTwi_Tapped(object sender, TappedRoutedEventArgs e)
{
    WindowsStoreAuthorizer auth;
    if (OAUTH_KEY == "")
    {
        auth = new WindowsStoreAuthorizer()
        {
            CredentialStore = new InMemoryCredentialStore
            {
                ConsumerKey = API_KEY,
                ConsumerSecret = API_SECRET
            },
            Callback = API_CALLBACK

        };
    }
    else
    {
        auth = new WindowsStoreAuthorizer()
        {
            CredentialStore = new InMemoryCredentialStore
            {
                ConsumerKey = API_KEY,
                ConsumerSecret = API_SECRET,
                OAuthToken = OAUTH_KEY,
                OAuthTokenSecret = OAUTH_SECRET
            },
            Callback = API_CALLBACK

        };

    }
    await auth.AuthorizeAsync();

    // int count = auth.Parameters.Count;
    // string oauth_token = auth.CredentialStore.OAuthToken;
    // string oauth_sercret = auth.CredentialStore.OAuthTokenSecret;

    var context = new TwitterContext(auth);
    var msg = text1.Text + "n" + DateTime.Now.ToString();
    await context.TweetAsync(msg);
}

Twitte のデベロッパで取得した API のコードを API_KEY と API_SECRET にいれておきます。まだ認証が終わっていないときには、OAUTH_KEY と OAUTH_SECRET が空なので、Twitter 認証ページを開いてユーザーに認証してもらえます。2回目以降は、OAUTH_KEY と OAUTH_SECRET を保存しておけば ok です。ここでは、ChuPad 専用のために直書きになっていますが、アプリフォルダに適当なフォーマットで保存しておけば ok でしょう。

末尾に DateTime をつけているのは実験用ので外しても ok です。同じツイートをすると、Twitter のほうで弾かれるのでそれを防ぐためです。実験していてあれー、となったので念のために。

■そのままでは絵文字コードが送れない

さて、この組み込みをして、さっくりと動かてみようと思ったのですが、絵文字を送ろうとすると実行エラーになります。Twitter のほうでエラーを返している訳ではなくて、LinqToTwitter の内部でエラーになるのです。自前で作っている投稿専用ツールのほうは絵文字が送れるので、LinqToTwitter の内部で何か変なことをやっているようです。

結論から言うと、Url#PercentEncode メソッドのロジックが未対応でした。このメソッドは、URLエンコードをするために%xxに変換するのですが、絵文字のサロゲートペアは内部的に何か変なことになっているみたいですね。これは、通常版の LinqToTwitter でも同じで、BuildUrlHelper#UrlEncode メソッドの中身が未対応です。
LinqToTwitter は通常版とPCL版とでコードが共有していないので、2箇所直す(しかもメソッド名もクラス名も異なる)のですが、これは .NET Framework 3.5 の頃に対応しようとした名残りなんでしょうね。

■エンコード部分を直す

仕方がないので、エンコード部分を直します。LinqToTwitter 自体は .NET 3.5 から対応しているのですが、これを奥深く入って直すのはしんどいので、.NET 4.5 の関数を使ってしまいます。

Uri.EscapeDataString メソッド (System)
http://msdn.microsoft.com/ja-jp/library/ms131567(v=vs.110).aspx

これ自体は 3.5 にもあるようなのですが、面倒なのでプロジェクト自体も 4.5 にしてしまいます。

本家の LinqToTiwtter からソースコードをダウンロードして、通常版とPCLのソリューションを開きます。

□通常版

namespace LinqToTwitter
{
    public class BuildUrlHelper
    {
...
        public static string UrlEncode(string value)
        {
            return Uri.EscapeDataString(value);
        }
	}
}

□PCL 版

namespace LinqToTwitter.Net
{
    public class Url
    {
...
        public static string PercentEncode(string value)
        {
            return Uri.EscapeDataString(value);
		}
	}
}

それぞれをビルドして NuGet から取ってきたものと摩り替えておけば ok です。こうすると無事、絵文字のツイートも可能になります。

後で、codeplex への pull request を送っておきます。

カテゴリー: C#, WinRT | 絵文字を LinqToTwitter で送るための方法 はコメントを受け付けていません

はじめての chu カッコカリの LT を発表しました

8月末の .NETラボ勉強会で「はじめての chu」をライトニングトークスで発表してきました。

ええ、発端はネタ言語ですが、内実はネタ言語の域を超えている…ってのが目標ですね。普通のエディタでは絵文字が打ちづらいので、専用の IDE で作成できる予定です。手元の環境では、絵文字を打つところまでできたので適当なタイミングでストアなどにアップしましょう。

image

Chu Pad からはツイートができたほうがいいので、左下ののアイコンがツイートアイコンです。Twitter の鳥ではないのは「絵文字」だからですね。それぞれのアイコンも画像ではなくて絵文字だけになっています。

最初は自分のところにばしばしと絵文字を流していたのですが、テストなので専用のアカウントを用意しました。公開するのも、この専用のアカウントに投稿してしまうのがいいかなと思っています。アカウントの乗っ取りのリスクがあるのですが、まあ秘密トークンは多少暗号化しておく形にしましょう。厳密にやれば Web API 作って、それから拾ってくるというのもありだけど。

image

奇特な方で自分のアカウントにも送りたい、って話もあるかなーと思って現在、秘密キー等を保存するところを製作中です(ってことにしておきます)。Twitter への認証自体は LINQ to Twitter にお世話になっています。が…この LINQ to Twitter はそのままでは「絵文字」を通さなかったんですよね。多少、苦労しましたが少しコードを変えることで通るようになったので、その件は別の記事にして残しておきます。

■文字解析等は FsLex/FsYacc を使っているよ

試しに、FsLex/Yacc を使っています。本当は BNFC のツールを使いたかったのですが、なんか吐き出すコードが -fsharp でやっても ocaml のコードがでるので、よくわかりません。ってのと、字句解析部分にひと工夫必要なので lex のところからきちんと書いています。

というのも、懇親会でも話したのですが、この chu-lang には「分かち書き」という概念がありません。絵文字をずらずらと並べて書くということを目標としたかったので、単語の区切りは文脈に沿うスタイルになります。当然、既存言語であってもターミネータ(括弧や四則演算などの特殊記号)と通常のアルファベットの組み合わせで単語の区切りはわかるのですが、そこは日本語のようにずらずらと書きたかったのです。例の富士通の「プログラマ不要」プログラム言語とか、既存の日本語プログラム言語との違いは、分かち書き(空白)を必要としない言語設計にあります(ってことにしておいてください)。まあ、Sketch とか UML プログラミングに近い感じです。手元の meArm とか Raspberry Pi とかにも使う予定です。

で、言語としては手続き言語に近い方にしたかったので関数言語スタイルをとります。そのまま F# にコンバートして .NET ライブラリを扱いやすいというのも、その手段のひとつではありますが、大抵のビジュアルプログラム言語が Alice をはじめとしてオブジェクト指向言語を学ぶスタイルを取っているので、どうせならば関数型言語ではどうでしょう?ってな感じですね。

文法的には F# と LISP を混ぜた感じになるはずです。括弧のような記号が絵文字では作りづらいので、タプルや配列は諦めてリストのみで表現します。まあ、それでもなんとかなるかなと。

  • Implementation Programming Languages
  • 計算機プログラムの構造と解釈

あたりを参考にすればよいでしょう。

■ChuLang は OSS です

コード自体は https://github.com/moonmile/ChuLang にちょっとずつ上げていきます。

懇親会で出た「特許」なり「商標」なりは、一般的に広めるための OSS ってことで、ここに公開しておきます。特許関連の抜け道?としては、最初に公開しておいて「周知の事実」にしていまえばいいんですよね。Java やら VB やらの前例があるので、Chu-Lang は数式ものが特許にならない程度に OSS ってことなのです。まあ、その他諸々は「商品」なのでしょうが、それは捕らぬ狸なので、ここでは触れませむー。

■Pi㊥Pad も作ってみた

その後、がりっちさんと池田さんと池袋に変える途中で、麻雀牌言語で盛り上がっていた(のは俺だけか?)のでパイ㊥パッドも作ってみました。

image

麻雀牌 http://ja.wikipedia.org/wiki/%E9%BA%BB%E9%9B%80%E7%89%8C#cite_ref-16 も Unicode で規定されていて、U+1F000 から U+1F02F まで割り当てられているんですよ(実際には、U+1F02B まで使われています)。ここからコピペコピペすると牌譜が作れるのですが、面倒なので Surface を使います…つーか、作ってみました。

どうせならば、これをプログラム言語に仕立て上げたいので思案中です。

  • 対子、順子/暗刻 の組みあわせで、アセンブラのニーモニック風に仕立て上げる。
  • スタックを扱って Forth 風にすればいいかな。

までは決めてあるのですが、その先は七誌さんあたりに相談しようかなと。

カテゴリー: 勉強会 | はじめての chu カッコカリの LT を発表しました はコメントを受け付けていません

[WinRT] ストアアプリで独自にキーコードを取得する方法

Surface の場合、ソフトウェアキーボードが出るのでぽちぽちと打てばいいのですが、PC 環境でストアプリを使う時にはキーボードを使ったほうが便利です。

が、XAML ではキー自体のイベントを拾うのはフォーカスのあるコントロールしか拾えません。KeyDown, KeyUp のイベントが発生しないんですよね。いきおい、テキストボックスがない画面にキーボードを使って入力するにはどうすればいいのか?と調べてみました。デスクトップアプリのように、フォーム自身(ストアアプリだから Page 自身)からキーイベントをフックしたいわけです。

■結論から言えばできない

Windowsストアアプリ: 極意7: アプリは、キーボード、マウス、タッチで機能する必要がある – Build Insider
http://www.buildinsider.net/mobile/winstoretips/07

色々探したのですが、結論から言えば「できません」。なんらかのフォーカスを持つコントロールを張り付けて、それに対してキーボード操作をすることになります。なので、画面にフォーカスを持つコントロールがあれば、それに Focus を当ててやって、そこからキーボードイベントを拾うことになります。

さて、自作の ChuPad は、TextBlock だけで構成されているためにフォーカスを付けることができません。表示部分なり入力部分なりを TextBox にしてもよいのですが、look & feel が悪いし、絵文字なり画像なりを貼り付けようと思うと結構面倒そうです(画像に関しては、RichTextBoxを使う方法もありますが)。
このパッド自体は、タブレットや Windows Phone を想定するので、本来ならばキーボードが無い環境でつかいます。通常のソフトウェアキーボードを出してもよいのですが、大きさがひどいのと、Chu-lang 特有の絵文字の順番にしたいという事情もあるので、キーボード部分を自作したいのです。

さて、どうすればよいのでしょうか?

■フォーカスを持つ自作 Control を作る

Focus メソッドを持てば良いということが分かったので、となれば、Focus メソッドを持つコントロールを自作してしまえばよいのです。どうせ画面にひとつしたフォーカスがないのですから、そのコントロールにあわせればよいでしょう、という発想をしました。
見ると、Focus メソッドを持つのは、Control クラスなので、自作コントロールを作ります。その中で、KeyDown/KeyUp の処理をすればよいわけです。

マウスや指で別の場所をタップする(この場合は、絵文字キーボード部分をタップする)と、フォーカスが外れれてイベントが取れなくなるので、無理やり戻してやります。タブ移動で止まるように IsTabStop = true にするのを忘れないでください。

//強制的にフォーカスを設定する
this.keycode.Focus(FocusState.Keyboard);
this.keycode.LostFocus += (s, e) => this.keycode.Focus(FocusState.Keyboard);
this.keycode.IsTabStop = true;
this.keycode.OnKeyPush += keycode_OnKeyPush;

ちなみに、自作絵文字キーボードからへの打ち込みは、それぞれの TextBlock に名前を付けておいて、コード上で一気に Tapped イベントを設定します。

var lst = new TextBlock[] {
    kLet, kLeft, kRight, kCat, kDog,
    kIf, kThen, kElse, kEqual, kNEqual,
    kFor, kTo, kFun, kTrue, kFalse,
    kList, kHead, kTail, kPrint, kCase,
};
foreach (var it in lst)
    it.Tapped += it_Tapped;

こんな風に配列を使って設定すればコードが短くなりますね。いちいち new しなくよい F# の影響です。

■KeyDown/KeyUp イベントを処理する

キーボードを押し下げ/離したときのイベントから仮想キーコードが取得できます。

VirtualKey Enumeration
http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.system.virtualkey(v=win.10).aspx

この enum をざっと見てわかるんですが、アルファベットしかなくて記号がありません。「@」とか「”」とかは、どうやって判別すればよいのでしょうか?

WPF の場合には、

KeyInterop.KeyFromVirtualKey Method (System.Windows.Input)
http://msdn.microsoft.com/en-us/library/system.windows.input.keyinterop.keyfromvirtualkey(v=vs.110).aspx

を使ってコンバートするのですが、WinRT では使えない。

仕方がないので、シフトキーの押し下げなどを判別して、JISキーボードをエミュレートします(英語向けなので、ASCIIキーボードも用意しないと駄目ですが)。

void makeKeycode()
{
    KCs = new KC[] {
        new KC( VirtualKey.Number1, "1", "!"),
        new KC( VirtualKey.Number2, "2", """),
        new KC( VirtualKey.Number3, "3", "#"),
        new KC( VirtualKey.Number4, "4", "$"),
        new KC( VirtualKey.Number5, "5", "%"),
        new KC( VirtualKey.Number6, "6", "&"),
        new KC( VirtualKey.Number7, "7", "'"),
        new KC( VirtualKey.Number8, "8", "("),
        new KC( VirtualKey.Number9, "9", ")"),
        new KC( VirtualKey.Number0, "0", ""),
        new KC((VirtualKey)189 , "-", "="),
        new KC((VirtualKey)222, "^", "~"),
        new KC((VirtualKey)220, "", "|"),

...略

        // 特殊キー
        new KC( VirtualKey.Space, " ", " "),
        new KC( VirtualKey.Enter, "ENTER", "ENTER"),
        new KC( VirtualKey.Back, "BS", "BS"),
        new KC( VirtualKey.Insert, "INS", "INS"),
        new KC( VirtualKey.Delete, "DEL", "DEL"),
        new KC( VirtualKey.Home, "HOME", "HOME"),
        new KC( VirtualKey.End, "END", "END"),
        new KC( VirtualKey.PageUp, "PAGEUP", "PAGEUP"),
        new KC( VirtualKey.PageDown, "PAGEDOWN", "PAGEDOWN"),
        new KC( VirtualKey.Up, "UP", "UP"),
        new KC( VirtualKey.Down, "DOWN", "DOWN"),
        new KC( VirtualKey.Left, "LEFT", "LEFT"),
        new KC( VirtualKey.Right, "RIGHT", "RIGHT"),

        // 数字キーパッド
        new KC( VirtualKey.NumberKeyLock, "NUMLOCK", "NUMLOCK"),
        new KC( VirtualKey.Divide, "/", "/"),
        new KC( VirtualKey.Multiply, "*", "*"),
        new KC( VirtualKey.Subtract, "-", "-"),
        new KC( VirtualKey.Add, "+", "+"),
        new KC( VirtualKey.NumberPad0, "0", "0"),
        new KC( VirtualKey.NumberPad1, "1", "1"),
        new KC( VirtualKey.NumberPad2, "2", "2"),
        new KC( VirtualKey.NumberPad3, "3", "3"),
        new KC( VirtualKey.NumberPad4, "4", "4"),
        new KC( VirtualKey.NumberPad5, "5", "5"),
        new KC( VirtualKey.NumberPad6, "6", "6"),
        new KC( VirtualKey.NumberPad7, "7", "7"),
        new KC( VirtualKey.NumberPad8, "8", "8"),
        new KC( VirtualKey.NumberPad8, "9", "9"),
    };
}

こんな風にちまちまとテーブルを使って変換表を作っておいて、LINQ 使って検索します。
KeyDown/KeyUp 時にあれこれとやっているのは、要らないキー処理を捨てているところです。実際試してみると、キーリピートが必要そうなので、後で修正しておきます。

protected override void OnKeyDown(KeyRoutedEventArgs e)
{
    // base.OnKeyDown(e);
    OnKey(e);
}
protected override void OnKeyUp(KeyRoutedEventArgs e)
{
    // base.OnKeyUp(e);
    OnKey(e);
}
private void OnKey(KeyRoutedEventArgs e)
{
    // if (e.KeyStatus.WasKeyDown == true) return;

    if (e.Key == Windows.System.VirtualKey.Shift)
    {
        isShift = !e.KeyStatus.IsKeyReleased;
        return;
    }
    if (e.Key == Windows.System.VirtualKey.Menu)
    {
        isAlt = !e.KeyStatus.IsKeyReleased;
        return;
    }
    if (e.Key == Windows.System.VirtualKey.Control)
    {
        isCtrl = !e.KeyStatus.IsKeyReleased;
        return;
    }
    if (e.KeyStatus.WasKeyDown == true) return;
    if (e.KeyStatus.ScanCode == 0) return;  // Enter外し

    Debug.WriteLine("key {0} {1} {2} {3} {4}",
        e.Key,
        isShift, isCtrl, isAlt,
        e.KeyStatus.ScanCode
        );

    try
    {
        var key = KCs.First(kc => kc.Key == e.Key);
        if (OnKeyPush != null)
        {
            string ch = "";
            if ( key.Normal.Length == 1 ) {
                ch = isShift == false ? key.Normal : key.Shift;
            } else {
                ch = key.Normal;
            }
            OnKeyPush(ch, isShift, isCtrl, isAlt);
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Error: keycode error {0} {1}", e.Key, ex.Message );
    }
}

■サンプルコード

http://github.com/moonmile/ChuLang/tree/master/ChuPad

カテゴリー: C#, WinRT | [WinRT] ストアアプリで独自にキーコードを取得する方法 はコメントを受け付けていません