みずぴー日記

人間の再起動ボタンはハワイのビーチにある

🍓 橘ありす分類器

GWに日光に開発合宿に行ってきた。

機械学習で遊ぼう」をテーマにしたので、橘ありす分類器を@と作成した。

成果物

m@gic-with-arisu from MIZUNO Hiroki on Vimeo.

ありすには青枠を、それ以外には赤枠を表示している。

宿

日光東照宮至近の炭火焼きの宿・ペンションはじめのいっぽに宿泊した。開発合宿に理解のある宿でよかった。ただ、理解があるあまり「昨日は成果でましたか?」と聞かれてつらい思いもした。 進捗を聞かれるより、成果を聞かれるほうが厳しい。

また旧型のiMacがあってよかった。起動したらバージョン1のSafariや、Mac版のIEが入ってて楽しかった。

f:id:mzp:20160525221012j:plain

ありすの調達

@デレステのガシャを引きまくって、SSRありすを調達した。

学習データの作成

学習データとしてAngelBreezeTulipのMVを撮影したのち、顔画像を切り出して学習データを作成した。

顔画像の切り出しはOpenCVによるアニメ顔検出ならlbpcascade_animeface.xml - デーを用いた。また毎フレームを切り出すと多くなりすぎるため、ある程度間引いている。

切り出した顔画像がありすかそうでないかを入力するために、専用のiPhoneアプリを作成した。データの更新や反映を容易にするようDropboxと連携できるようになっている。

このタイミングで大量の顔画像が手に入って楽しかった。

f:id:mzp:20160525222111p:plain

学習

ありす画像が117枚、非ありす画像が107枚得られたので、機械学習によって分類器を作成した。このとき用いたモデルは4コマ漫画の画像管理✨ - みずぴー日記と同様のものである。

分類

学習に用いたMVとは別にM@GICのMVを撮影し、各フレームの各顔画像に対してありす・非ありすの分類を行なった。 そして得られた結果を元に、動画を合成した。

その他

see_no_evil

せっかくの日光なので🙈(:see_no_evil:)の実物を見て、闇のようなコードと戦う気持ちを新たにした。

いちご

www.instagram.com

ありすといえばいちご、ということで完成したあとにいちごを食べに行った。

より正確には、インターネットでいちごパフェを出している店を調べて向ったがすでに潰れたあとで、どうしようかなぁとふらふらしてたら、たまたまいちごを路上販売してたので買って食べた。

レポジトリ名の由来

レポジトリ名である arisu-in-fact は、ありすの曲であるin factに由来している。

iTerm2 + AquaSKK

要約

f:id:mzp:20160515142324j:plain

経緯

iTerm2 + AquaSKKには、l/L/q/Qで入力モードを切り替えるとその文字も入力されてしまう問題がある。(参考: iTerm2/Apple TerminalでAquaSKKを使う - みずぴー日記)

これを修正するPull requestを @__tai2__が作ってくれたので、取り込むための各種議論に参加していた。

そのPull requestが本日(2016/5/15)masterに取り込まれたので、次回のNightly BuildsやTest Releasesとともにリリースされるはず。

🇯🇵 SJIS/EUC-JPのテキストファイルをQuick Lookする

SJIS/EUC-JPのテキストファイルをQuick Lookできないのが不便なので、プラグインを書いた。

ダウンロード

https://github.com/mzp/qltext-jp

実装

Quick Lookされたときにエンコードの自動判定を行ない、その後の表示はシステムにまかせている。

実装は以下のプラグインを参考にした。

また文字コードの判定は以下のコードを用いている。

他の方法

xattr

xattr で拡張属性を設定すれば特にプラグインをいれなくても、Quick Lookできる。(参考: Mac の Quick Look をちょっとだけ快適に – xattr 編 – (フェンリル | デベロッパーズブログ))

xattr -w com.apple.TextEncoding "SHIFT_JIS;2561" README.txt

が、毎回これをやるのは大変なので、Quick Lookプラグインを作成した。

quicklook-jptxt

GitHub - ento/quicklook-jptxt: Quick Look plugin for public.plain-text with better encoding handling. を使えば同等のことができる。文字コード判定が微妙に違うくらい。

これでダメな理由は特にないが、まあ作りかけてしまったので完成させてしまった。

その他

最近はSJISのファイルってあまりないよね。

AquaSKK 4.4.0: 互換性のための設定画面追加

AquaSKKが正しく動作しないアプリケーションのための設定画面を追加し、AquaSKK 4.4.0をリリースした。

f:id:mzp:20160507185441p:plain

ダウンロード

https://github.com/codefirst/aquaskk/releases/tag/4.4.0

変更点

一部のアプリケーションでは、l/Lによる入力モード切り替えが動作しないため、特別な回避策が必要となる。

これまでのバージョンでは、回避策を適用するアプリケーションリストをコード中に直接記述していた。 そのため、アプリケーションリストを更新するためにAquaSKK本体の更新が必要となっていた。

この対応がだんだんと厳しくなってきたので、設定画面からアプリケーションリストを更新できるようにした。

余談: 名称の由来

基本的に回避策は意味がよくわからないことをしているため、設定画面のラベル名を決めるのが大変だった。 最終的に結城(@)氏の案を採用した。

余談: お願い

この設定はあくまで回避策なので、できる限りそのアプリケーション側にバグレポートを送信してほしい。 闇に闇を積み重ねるのはよくない。

SKK辞書の闇への対応状況

SKK-JISYO.lispで書いたようにSKKの辞書形式にはいくつかの闇(=歴史的経緯による複雑な仕様)を抱えている。

この闇に対して、SKKの各実装がどう対応をしているかを調べた。

f:id:mzp:20160502100018p:plain

調べた実装

concatへの対応

SKK辞書の書式上、/; を含む変換候補は登録できない。 そのため、L辞書には以下のようにconcatを用いて登録されている。

dosv /(concat "DOS\057V")/  

対応済

concatによるエスケープを理解し、意図した変換結果を出力する。

未対応

変換結果に(concat ".....") がそのままでてしまう。

/や;を含んだ単語の登録

ユーザが/;を含んだ単語を登録した場合もconcatによるエスケープが必要である。

対応済

対応済?

以下のように独自の形式でエスケープを行なう。 単語登録は行なえるが、他のSKK実装とは互換性がない。

dosv /DOS[2f]V"/

未対応

/; をそのまま登録してしまう。単語を登録しても変換できない。

concat以外のEmacsLisp式

SKKの辞書は任意のEmacsLisp式を含めることができる。そのためlisp辞書には以下のようなエントリが登録されている。

time /(current-time-string)/ 

部分対応

pwdなどの一部のエントリには対応していないが、Lisp辞書の大半に対応している。

  • CorvusSKK
  • SKK日本語入力FEP + SKKGate

部分対応 その2

current-time-string、pwd、skk-versionの3つの関数のみに対応している。 CorvusSKK等に比べるとかなり限定的。

未対応

数値エントリ

SKKの辞書は # を含む見出し語を特別扱いする。例えば、以下のエントリは「3かい」や「10かい」とマッチする。

#かい /#1回/#0回/#3回/#2回/ 

変換時、 #<n> は以下のように変換される。

  • #0: 半角数字。(例: 1024)
  • #1: 全角数字。(例: 1024)
  • #2: 漢数字で位取りあり。(例: 一〇二四)
  • #3: 漢数字で位取りなし。(例: 千二十四)
  • #4: 再変換。見出し語中の数字そのものをキーとして辞書を再検索する。*2
  • #5: 大字。(例: 壱阡弐拾四)
  • #8: 桁区切り。(例: 1,234) *3
  • #9: 将棋の棋譜入力用。(例: 8五)

詳細はSKK Manual: 数値変換に記載されている。

対応済

  • CorvusSKK
  • SKK日本語入力FEP + SKKGate

部分対応

  • #0#1#2#3#5#9: AquaSKK, uim-skk
  • #0#1#2#3: fcitx-skk, FlickSKK

未対応

数値エントリの変換に対応していない。

追記

CorvusSKKの対応状況に誤認があったので、修正した。

追記 その2

SKK日本語入力FEP拡張機能のことを認識していなかったので、追記した。

所感

  • Lisp辞書と棋譜変換はSKK辞書の二大闇だと思っていたが、やはり闇っぽかった。 CorvusSKK/SKK日本語入力FEPはすごい。
  • concatへの対応はAquasKK/FlickSKKにいれてもいいかもしれない。

*1:libskkを用いているのでibus-skkも同じ挙動をするはず

*2:正しい挙動を理解していないので、今回は調べていない。

*3:SKK日本語入力FEPが先行実装し、L辞書に取り込まれたらしい。 https://twitter.com/coexe/status/726983576516853762

SKK-JISYO.lisp

NL名古屋 - connpassSKK辞書の話をした。 要約すると、特定の文字をエスケープするだけでEmacsLispが必要になってつらいという話だった。

Transiruで発表したので、発表時の音声等はこちらで聞ける。終盤早口になってしまったので恥かしい。声優吹き替えオプションが欲しい。

原稿

前提

自己紹介

こんにちは。 mzpです。 タイトルにあるように日本語入力システムとLispについて話したいと思います。日本語のNと、LispのLでNLです。

何の話?

一言で日本語入力システムといってもたくさんの種類がありますし、Lispにもたくさんの種類がありますね。

ので、具体的に言うと、今日はSKKとEmacsLispの話をします。LispといってEmacsLispを持ちだすのは微妙な気がしますが、まあLispと名乗ってるわけですしOKでしょう。

SKKとは

このSKKについてですが、これはだいぶ独特な日本語入力システムです。通常の日本語入力システムがやるような作業を利用者におしつけることで、高速かつ高精度の日本語変換を実現しています。

入力例

分かりづらいと思うので、入力例を見てみます。例として「今日は雨です」と入力してみる場合です。

普通の日本語入力システムでは「きょうはあめです」と入力したのち、スペースを押して、変換候補を表示します。お馴染みですね。

SKKでは、漢字変換を始める前にShiftを押します。

Kyou

そして、複数の単語をまとめて変換することはできないので、ここでスペースをおして候補を表示します。

ひらがなはそのまま入力すればOKです。特に変換は必要ありません。

ha

また漢字なのでShiftを押します。

Ame

そして、スペースを押して候補を表示し、確定させます。

そして、またひらがなはそのまま入力します。

desu

SKKの特徴

このように

  • 単語ごとでしか変換できない
  • 漢字に変換するかどうかを自動で判断しない

といった特徴があります。

一見使いづらいだけのように見えますが、変換する単語を完全に自分で制御できるので、慣れれば離れられなくなる中毒性があります。

SKKの実装

中毒性が高いソフトウェアのため、現在では様々なプラットフォームで利用できます。

このAquaSKKとFlickSKKは僕がメンテしてます。

SKKの歴史

元となったSKK(通称: 本家SKK)は、1987年に、東北大学教授(当時)佐藤雅彦によって開発されました。

この本家SKKは、Emacs上のプログラムとして開発されました。 つまりEmacsLispを用いて開発されていました。

いやー、Emacsはすばらしいソフトウェアですね。 実にすばらしい。

辞書ファイル

Emacsを十分に褒めたところでつらい話をはじめます。

ひらがなを漢字に変換するためには、ひらがなと漢字の対応表が必要になります。この対応表は辞書と呼んでいます。

そしてこの辞書ファイルが闇を貯めこんでいます。

辞書ファイルの書式

辞書ファイルはテキストファイルになっており、各行が以下のような書式になっています。

なごや /名古屋;愛知/那古屋/

左から順に「見出し語」「変換候補」「アノテーション(要するに補足情報)」となっています。変換候補間は/で区切られていて、アノテーションとは;で区切られています。

自然な感じがしますね。

エスケープ

しかし、この書式をじっと見てると、いくつか使えない文字があることが気づきます。そう、変換候補として「;」や「/」を含むことができないのです。

例えば「owata」の変換候補として「(^o^;)/」 とかを登録したい場合はどうしたらいいでしょう。

対応方法1: エスケープ

いくつかの対応方法が考えられます。 例えば、文字列リテラルのように\でエスケープすればよさそうですね。

owata /\\(^o^\;)\//

対応方法2: 別の文字への置換

あるいは別の文字に置換してしまうのもいいでしょう。

例えば、"[ASCIIコード]"のような記法を採用すると、;のASCIIコードは0x3b、/は0x2fなので以下のようになります。

owata /\(^o^[3b])[2f]/

対応方法3: 全角文字

あきらめてよく似た文字を使うことで誤魔化してもいいです(?)。

owata /\(^o^;)//

いやよくないでしょ...。

正解

みなさんだったらどうしますか?ちょっとだけ考えてみてください。

....

考えましたね。 ではSKKがどうしたかを見てみましょう。

owata /(concat "\(^o^\059)\057")/

S式の文字列リテラルを使ってエスケープする、が正解でした〜。

まじかよ...って感じですね。

その他の便利機能

日本語入力システムを使ってると、いろいろ便利な変換したくなってきます。

例えば、

  • todayを今日の日付に変換したい
  • nowを今の時刻に変換したい
  • 5feetをメートルに変換したい
  • 元号を変換したい

などです。

これらはすべてEmacsLispを使って実現できます! すごい!

まじかよ...って感じですね。

さらにすごいことに以下のような変換もEmacsLispで実現されています。

  • 画面幅いっぱいの線

まじかよ...って感じですね。

実装

Lispの実装 これらの機能は使う分にはまだいいんですが、実装者としては悪夢です。

つまりSKKを実装するには、EmacsLisp処理系を実装しなければいけません。 より具体的に言うと、AquaSKKとFlickSKKはボクがメンテしていく上で、ボクがEmacsLisp処理系を実装しなければいけません。

まじかよ...って感じですね。

SKK-JISYO.lisp

なげいていてもしょうがないので、現実を確認してみましょう。

実は「今日の日付」のような複雑なEmacsLispの式を含む変換候補は、通常の辞書とは別で管理されています。余談ですが、この別に管理されている辞書はLisp辞書と呼ばれていますが、このLisp辞書って単語もだいぶ愉快ですね。

そのため通常の辞書だけを利用すると割り切ってしまえば、フルセットのEmacsを実装者するのは避けれます。通常の辞書に残っているEmacsLispの式はconcatによる文字のエスケープだけなので、これに対応するだけで十分です。

実装状況

SKKの各実装がこれにどのように対応しているか見てみましょう。

AquaSKK/FlickSKK

AquaSKK/FlickSKKは割り切った対応をしています。外部の辞書ファイルに含まれるconcatについては何もしません。つまり変換するとこんな変換結果がでてきます。(例: dosv)

そして、ユーザが入力した内容した結果については、別の文字列に置き換えることでエスケープしています。先ほど述べた別の文字への置換のような形式です。

owata /\(^o^[3b])[2f]/

後者はともかくconcatがでてくるのは微妙なので直したいとは思っています。

ibus-skk/scim-skk

ibus-skk/scim-skkは、concatのみの実装をしていてソツがない感じになっています。

CorvusSKK

一方、CorvusSKKはここの実装をがんばっていて、いくつかの関数もサポートしています。すごいです。

その他

Lispの話はこれくらいですが、実は辞書ファイルにはまだいくつかの闇が存在しています。

辞書の並び順

これまでは辞書の各行の話をしてきましたが、これらの行は見出し語をキーとしてソートされています。いわゆる辞書順です。辞書ですからね。

このときの比較関数としてはEmacsLispの string< が使われます。....またEmacsLispか。

そしてこのstring<は文字コード順で文字列を比較します。 はい、ぞわぞわしてきましたね。

なんの文字コードだよ、って感じですね。SKKでは伝統的にはEUC-JPが使われますが、最近はUTF-8を使うことも多いです。というかボクは使っています。

UTF-8の比較というとアレがありますね。 濁点問題です。NFCとNFDという2種類の形式があります。濁点を合成された文字としてもつか、分解された文字としてもつかの違いです。

世の中的にはNFCを使うことが多いんですが、ボクのメンテしているApple系のプラットフォームだとNFDが主に使われてたりします。

どちらが正しいとか優れているとかはありませんが、並び順には影響するので注意深く扱う必要があります。ボクはドハマりしました。

きっついですね。

辞書のライセンス

SKKの辞書ファイルはGPLで配布されています。 フリーなソフトウェア!! 自由!!

...こういうデータファイルがGPLっていうのはどういう扱いなんでしょう。

wikipediaから引用すると、GPLはおおむね以下のことを許諾するライセンスです。

  • プログラムの実行
  • プログラムの動作を調べ、それを改変すること(ソースコードへのアクセスは、その前提になる)
  • 複製物の再頒布
  • プログラムを改良し、改良を公衆にリリースする権利(ソースコードへのアクセスは、その前提になる)

改変の許可と再配布はいいとして、実行というのは何になるんでしょうね。 あとテキストを読み取ってアレコレするのは動的リンク扱いで派生物扱いになったりするんでしょうか。

謎です。

AquaSKK/FlickSKKのメンテナとしてのポジショントークは、同梱であってリンクではない。そのためGPLである辞書ファイルを読み取ってあれこれするプログラムはGPLである必要性はない、です。

別にアンチGPLというわけではなくて、AppleのAppStoreとGPLの相性が悪いので、俺の書いたコードはGPLではない、ということにしとかないと色々と都合が悪いんです。

まとめ

  • SKKサイコー
  • 辞書ファイルはEmacsLisp由来の闇を溜め込んでいる
  • SKKサイコー

4コマ漫画の画像管理✨

NEW GAME! のコマ検索 - みずぴー日記で作っていたソフトウェアのうち、画像以外の部分を公開した。

f:id:mzp:20160404222443p:plain

レポジトリ

https://github.com/mzp/EagleJumpSystem

検索機能

NEW GAME! のコマ検索 - みずぴー日記で述べたように

  • 台詞
  • 登場キャラクター

によって該当するコマを検索できる。

f:id:mzp:20160404224140p:plain

入力補助機能

検索機能を実現するために、各コマにはメタデータ(台詞、登場キャラクター)を入力する必要がある。 このメタデータの入力を補助する機能もいくつか実装した。

コマ分割

ページを取り込む際に、画像をコマごとに分割する。これはゆゆ式を無限に楽しみたかった話 〜 ゆゆ式 Advent Calendar 2014 20日目 〜 - non117's diaryツールをほぼそのまま利用している。

f:id:mzp:20160404223001p:plain

テキストの自動認識

Google CloudVisionAPIによりコマ中のテキストを自動で認識する。

ただし利用には別途APIキーの取得が必要である。APIキーの取得方法はCloud Vision APIの使い方まとめ (サンプルコード付き)が分かりやすかった。

f:id:mzp:20160404225745p:plain

キャラクターの半自動認識

ある程度の量のキャラクターの分類を手動で行なえば、残りの画像については機械学習により自動でタグづけが行なえる。

ただし、登場回数の少ないキャラクターは学習データが少ないので、そこは手動でタグをつける必要がある。また、顔認識をした上でキャラクターの分類をしているため、「手のみ登場している」といったコマについては対応できない。

この部分のコードはTensorFlowでアニメゆるゆりの制作会社を識別する - kivantium活動日記をかなり参考にしている。

f:id:mzp:20160404225858p:plain

その他

既知のバグ

もしくは面倒で直してない箇所リスト。

  • コマごとの分割の際に、まれに0バイトの画像が生成される。 今は、定期的に手動で find . -size 0 -exec rm {} \; を実行して削除しているが、そもそも生成されないようにしたい。
  • 素朴なデータの格納方法をしているため、画像が増えるとどんどん遅くなっていく。

名前

レポジトリ名のEagleJumpSystemはNEW GAME!に登場するイーグルジャンプ社に由来する。

f:id:mzp:20160404222622p:plain