みんなでプロ生ちゃんになる
(みんなで誰かになるブックマークレット・ジェネレーター)
キモいタイトルですが、・・・Twitterのサイトで全員一緒にプロ生ちゃんになるためのブックマークレットを作ることができます。 まあ、プロ生ちゃんでなくても、友利奈緒でも、うまるちゃんでも、福山雅治でも、好きな名前で構いません。 何故このようなものを作ったのか自分でもわかりません。
※ ウェブブラウザでのみ動作します。動作確認は、Windows 8.1/Chrome,IE11 です。
1.下の欄に好きな名前を入力します。仮にここでは「プロ生ちゃん」とします。
2.下の欄の文字を選択し(トリプルクリックが早いですね)、クリップボードにコピーします。
javascript:var a=document.querySelector("li#user-dropdown img.avatar").getAttribute("data-user-id"),b="a.ProfileHeaderCard-nameLink",h="div.DashboardProfileCard-name>a",f="プロ生ちゃん",d=["a .fullname","a.js-user-profile-link>b","div .QuoteTweet-fullname","li.ScrollBump a.js-user-profile-link","p.social-proof a.js-user-profile-link","div .ProfileNameTruncated-link","div.dashboard p.metadata a.js-nav",h,b],g=document.querySelector(b)?document.querySelector(b).innerText:document.querySelector(h)?document.querySelector(h).innerText:"",c=function(){var e=document.querySelectorAll(d.join(",")),t=document.querySelectorAll(".ProfileUserList a, .ProfileCard a, .DashboardProfileCard-content a, a.js-tooltip, img.js-user-profile-link"),r=document.querySelectorAll(".ScrollBump-title span");e&&[].forEach.call(e,function(e){e.innerText=f}),t&&[].forEach.call(t,function(e){e.setAttribute("data-original-title",f),e.setAttribute("original-title",f),e.setAttribute("title",f),e.setAttribute("alt",f)}),r&&[].forEach.call(r,function(e){e.innerHTML=e.innerHTML.replace(g,f)})};setInterval("c()",100),document.title=document.title.replace(g,f),c();
3.どこか適当なウェブサイトをブックマークに保存します。
4.保存したブックマークを編集し、URL欄にさきほどコピーした文字を貼り付けて保存します。ブックマークの名前は自由に決めてください。
5.Twitterサイトでブックマークをクリックして、「プロ生ちゃん」で満たされた画面を堪能します。
アニメは終了してしまいましたが、もちろん「友利奈緒」でも構いません。「友利奈緒」にすると、かなりウザい感じが楽しめます。お好みでどうぞ。
適当に楽しんでいただければ幸いです。同様のサービスがすでにあるかもしれませんが、これはそんな大層なものではありません。飽きたらさっさとブックマークを捨てましょう。
Ubuntu 14.04 で OpenVPN
はじめに
この記事は、OpenVPNを使用して外部から内部ネットワークにアクセスする仕組みを構築する手順をメモしています。いろいろ他のサイトを参考にしています。
Business VPN | Next-Gen VPN | OpenVPN
ところで
公式サイトに行くと VPN Solution という項目があって、ここから OpenVPN Access Serverという管理ツールをダウンロードできるのですが、GUIが使えたりしていろいろ便利そうではあるのですが、有料製品のようです。今回は、あくまで無料でVPNサーバーを構築する予定ですので、こちらではなく。必要なドキュメント等は、Communityのほうにあります。
前提となる環境
今回、サーバーには、Ubuntu 14.04 LTS Server を使用しています。これは、すでにインストールされ、内部ネットワーク環境に置かれているものとします。 内部環境のネットワークアドレスは 192,168.1.0/24 とし、ゲートウェイは 192.168.1.1、VPNサーバーのアドレスは 192.168.1.10 としています。 外部のクライアントPCから、内部のAPサーバーに アドレス 192.168.1.20 でアクセスできるところまでが目標です。
iPhoneからも接続することを考えていますので、接続方式は ブリッジ ではなく NAT で、仮想ネットワークドライバは TAP ではなく TUN を使用します。
作業前に
以下の作業のほとんどは、root権限で行う必要があります。Ubuntuの場合、普段は sudo で実行しますが、面倒なのであらかじめ root 権限を取得して作業しました。
$ sudo -i
OpenVPN を Ubuntu にインストールする
まずは、レポジトリの情報を最新にアップデートします。
# apt-get update
そして、パッケージをインストールします。 *1
# apt-get install openvpn easy-rsa libssl-dev openssl
サーバー証明書を発行する
サーバー用に、公開鍵(.crt)、秘密鍵(.key)、証明書署名要求(.csr)、DHパラメータ(.pem)を作成します。
証明書作成用のディレクトリを作成
証明書発行用のディレクトリが必要になります。Ubuntuには専用のコマンド make-cadir が用意されているようなのでこれを使います。場所は、通常 /etc/openvpn の下に、easy-rsa ディレクトリを作成するようです。作成後、そのディレクトリに移動します。
# make-cadir /etc/openvpn/easy-rsa # cd /etc/openvpn/easy-rsa
/etc/openvpn │ └─ east-rsa ←
Ubuntu Manpage: make-cadir - create certificates dir
証明書作成用の環境変数を設定する
移動先の easy-rsa ディレクトリに、vars というファイルがあります。このファイルの中に、必要な情報を入力していきます。
# vi ./vars
export KEY_COUNTRY="JP" # 国 export KEY_PROVINCE="Tokyo" # 都道府県 export KEY_CITY="Shinjuku" # 市区町村 export KEY_ORG="My Company Co.Ltd." # 組織 export KEY_EMAIL="admin@my-company.com" # メールアドレス export KEY_OU="Sales" # 組織単位
変更した varsファイルを読み込みます。
# . ./vars
サーバー証明書を作成する
以下のコマンド(実行可能ファイル)を順に実行してください。
# ./clean-all
# ./build-dh
# ./pkitool --initca
# ./pkitool --server server
# cd ./keys # openvpn --genkey --secret
以上の作業で、keysディレクトリに以下の鍵ファイルが作成されていると思います。
/etc/openvpn │ └─ east-rsa │ └─ keys ca.crt ← ca.key ← server.crt ← server.csr ← server.key ← dh2048.pem ← ta.key ←
これらのファイルを /etc/openvpn ディレクトリの下へコピーします。
# cp ca.crt server.crt server.keydh1024.pem ta.key /etc/openvpn
/etc/openvpn │ ca.crt ← │ server.crt ← │ server.key ← │ dh2048.pem ← │ ta.key ← │ └─ east-rsa │ └─ keys ca.crt ca.key server.crt server.csr server.key dh2048.pem ta.key
OpenVPNを設定する
設定ファイルの作成
OpenVPNの設定を環境に合わせて変更していきます。設定ファイルは、まだ作成されていません。/usr/share/doc/openvpn にサンプル設定ファイルがありますので、以下のコマンドを実行してこれを server.conf として /etc/openvpn にコピーします。
# gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf
/etc/openvpn │ ca.crt │ server.crt │ server.key │ dh2048.pem │ ta.key │ server.conf ← │ └─ east-rsa │ └─ keys
この server.conf が設定ファイルになりますので、これを編集します。
# vi /etc/openvpn/server.conf
VPN通信の設定(規定値のまま)
ポート番号とプロトコルを指定します。必要がなければ、規定値のままで構いません。クライアント側のルーターで既定のポート番号が制限されている場合などに、tcp/443を使用することなどが可能とのことです。
port 1194
;proto tcp proto udp
接続用の仮想ネットワークデバイスを指定します。今回の例では、iPhoneからの接続を考慮して TUN を選択しています。規定で TUN となっているので、変更は必要ありません。
;dev tap dev tun
VPN通信に使用するネットワークアドレスを指定します。クライアント・サーバー間はこのアドレスで通信が行われ、サーバーで内部ネットワークアドレスに変換されます。今回の場合、規定値をそのまま使用しています。後の手順で、内部ネットワーク側のルーター設定でこのアドレスを指定する必要があります。
server 10.8.0.0 255.255.255.0
証明書の指定(規定値のまま)
- で作成した証明書を指定します。今回の場合、とくに変更は必要ありません。
ca ca.crt cert server.crt key server.key
DHパラメータの指定
- で作成した dh2048.pem を指定します。今回の場合、以下のように変更する必要があります*2
;dh dh1024.pem dh dh2048.pem
アクセス先となる内部ネットワークの設定
外部からアクセスしたいネットワークのアドレスを入力します。push "route ..." の箇所を変更してください。今回の例 192.168.1.0/24 の場合は、以下のように指定します。
push "route 192.168.1.0 255.255.255.0"
次に、push "redirect-gateway ..."の箇所のコメントアウトを外して有効化します。これを有効にすることで、WebブラウジングやDNS参照など、VPN ClientのすべてのトラフィックをVPNサーバー経由にするそうです。
push "redirect-gateway def1 bypass-dhcp"
そして、DNSの設定を変更します。push "dhcp-option DNS..."の箇所を変更します。今回の例では、DNSサーバーとして192.168.1.1を指定します。
push "dhcp-option DNS 192.168.1.1"
TLSの設定
ファイルの末尾に以下の情報を追加します。
mode server tls-server
TLS暗号化を有効化します。
tls-auth ta.key 0
通信暗号化の設定
サーバー・クライアント間で用いる暗号化の方式を指定します。ここで指定した方式を、クライアント側からも指定する必要があります。ここが、サーバーとクライアントで異なっていると、クライアントからの接続時にエラーとなります。
cipher AES-256-CBC
サービス実行ユーザーの変更
規定では、OpenVPN は root ユーザーとして動作するため、これを nobody:nogroup に変更し、セキュリティを高めておきます。
user nobody group nogroup
その他のオプション
クライアント間通信の有効化
client-to-client
複数台のクライアントで、同じ証明書を使い回す(非推奨)。
;duplicate-cn
VPN通信の圧縮。今回、これを有効にするとiPhoneからの接続がうまくいかなかったため、コメントアウトしました。
;comp-lzo
設定の反映
server.conf を保存したら、openvpnサービスを再起動します。
# service openvpn restart
参考サイト
- How To Set Up an OpenVPN Server on Ubuntu 14.04 | DigitalOcean
- http://blog.chibatching.com/blog/2014/09/08/construct-openvpn-server-in-ubuntu/
- Ubuntu Server 14.04をVPNサーバーにしてみた。 - Qiita
内部ネットワークの他のサーバーにアクセスするための設定をする
以上の設定のみで手順を進めると、クライアント側からOpenVPNサーバー自体(図の192.168.1.10あるいは10.8.0.1)にはアクセス可能となりますが、内部ネットワークの他のサーバー等(イントラネット上の業務アプリケーションや共有フォルダなど)にアクセスしようとしても、パケットが届かないためエラーとなってしまいます。そこで、OpenVPNサーバー上で、クライアントから 10.8.0.0/24 経由で来たパケットを 192.168.1.0/24 に転送する必要があります。
フォワーディングの有効化
まず、IPフォワーディングを有効にします。カーネルパラメータを変更しますので、sysctl.conf を開きます。
# vi /etc/sysctl.conf
そして、以下の行のコメントアウトを解除して保存します。
net.ipv4.ip_forward = 1
設定を即座に反映するために、以下のコマンドを実行します。
# sysctl -p
ルーティング設定の追加
そして、フォワーディングの設定をします。他のサイトでは ufw を使用した例が多く示されていますが、ここでは iptables を利用しています。なお、iptablesは、すべてACCEPTとなっており、とくにフィルタリング等は行われていない(ファイアウォール等は別途設けられている)ケースを想定しています。
まず、iptables の設定を永続化するためのツールを導入します。
# apt-get install iptables-persistent
次に、iptables に以下の設定を追加します。ifconfig コマンドで、物理ネットワークデバイスのデバイス名を確認しておいてください。
# ifconfig eth0 Link encap:Ethernet HWaddr 00:0B:CD:1C:18:5A inet addr:192.168.1.10 Bcast:192.168.1.255 Mask:255.255.255.0 ... lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 ... tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:10.8.0.1 P-t-P:10.8.0.2 Mask:255.255.255.255 ...
そのうえで、以下のコマンドを入力してください。今回は、仮想ネットワークデバイスが tun0(10.8.0.0/24)、物理ネットワークデバイス eth0(192.168.1.0/24)であるとします。
# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
設定を保存します。
# service iptables-persistent save
参考サイト
- VPNを構築しよう・その6・ルーティングモードでのOpenVPNサーバセットアップ:ぴろにっき:SSブログ
- http://web.mit.edu/rhel-doc/4/RH-DOCS/rhel-sg-ja-4/s1-firewall-ipt-fwd.html
- http://iwashi.co/2015/01/16/ubuntu-iptables-persistent/
ルーターを設定する
OpenVPNサーバー上での設定は以上ですが、内部ネットワークのゲートウェイとなっているルーター機器で、いくつか設定を行います。
ポート開放を許可する
外部から udp/1194へのアクセスを許可します。ルーターのフィルタリングなどを使用します。
NTT OG410Xiの例
規定で、何もフィルタリングされていない場合は空欄。
外部からの udp/1194 宛てのパケットを VPNサーバーに送る
次に、外部のVPNクライアントからのアクセスを、VPNサーバーを経由するように設定します。ルーターの静的IPマスカレードを使用します。
NTT OG410Xiの例
内部からの 10.8.0.0/24 宛てのパケットを VPNサーバーに送る
そして、内部から、10.8.0.0/24 に宛てたパケットがすべて VPNサーバーを経由するように設定します。ルーターのルーティングなどを使用します。
NTT OG410Xiの例
参考サイト
クライアント証明書の作成
以上で、サーバー用の設定は完了です。あとは、外部から接続するクライアント端末ごとに、証明書を発行します。この証明書を用いて、各クライアントが内部ネットワークにアクセスします。ここでは、仮にアクセス先の グローバルIPアドレスが 123.456.78.9 であるとしています。
(ログアウトしていれば再度OpenVPNサーバーにログインし、)2. で作成した /etc/openvpn/easy-rsa ディレクトリに移動します。
# cd /etc/openvpn/easy-rsa
/etc/openvpn │ └─ east-rsa ←
作成済みの環境変数を読み込みます。
# . ./vars
作成したいクライアントの識別名を任意に命名します。ここでは、 client1 とします。以下のコマンドを実行してください。
# ./pkitool client1
もし、パスワードを設定したい場合には、--passオプションを使用します。証明書作成中にパスワードを聞かれますので、入力してください(確認を含めて2回)。ただし、4文字以上でないと怒られます。
# ./pkitool --pass client1
これで、keysディレクトリの中に、クライアント用の証明書が作成されているはずです。
/etc/openvpn │ └─ east-rsa │ └─ keys ca.crt ca.key server.crt server.csr server.key dh2048.pem ta.key client1.crt ← client1.csr ← client1.key ←
作成されたファイルのうち、client1.crt、client1.key、ca.crt、ta.key を何らかの手段でクライアント端末に送ります。セキュリティに配慮して、できるだけ安全な方法で受け渡しをしてください。
/etc/openvpn │ └─ east-rsa │ └─ keys ca.crt ← ca.key server.crt server.csr server.key dh2048.pem ta.key ← client1.crt ← client1.csr client1.key ←
VPNクライアントの設定(Windows)
アプリケーションの入手
プラスシステムズ株式会社の提供するサイトから、vpnux Clientをダウンロードし、手順に従ってインストールします。*3こちらのソフトウェアの使用が、公式サイトでも推奨されているようです。
設定
インストールが完了したら、vpnux Client を起動し、メニューの [プロファイル] → [追加]
で設定画面を開きます。
上図のように設定をしたら、[保存]
します。作成したプロファイルが、一覧画面に表示されたら完了です。
接続
起動時の画面から、作成済みのプロファイルを選択し、[接続]
をクリックするだけです。非常に簡単です。
VPNクライアントの設定(iPhone)
ovpnファイルの作成
新規に、テキストファイルを作成します。拡張子は .ovpnとしてください。ファイル名は仮に client1.ovpnとします。ファイルの内容は、以下のようなものになります。remoteの値には、接続先のグローバルIPアドレス、もしくはホスト名を入力します。 cipherの値を、2. で server.conf に指定した値と揃えることに注意してください。
tls-client key-direction 1 remote 123.456.78.9 cipher AES-256-CBC port 1194 proto udp <ca> ----- ca.crt の BEGIN CERTIFICATE ~ END CERTIFICATE までをペースト ----- </ca> <cert> ----- client1.crt の BEGIN CERTIFICATE ~ END CERTIFICATE までをペースト ----- </cert> <key> ----- client1.key の BEGIN ENCRYPTED PRIVATE KEY ~ END ENCRYPTED PRIVATE KEY までをペースト ----- </key> <tls-auth> ----- ta.key の BEGIN OpenVPN Static key V1 ~ END OpenVPN Static key V1 までをペースト ----- </tls-auth>
上記のようなファイルを作成したら、<ca>
、<cert>
、<key>
、<tls-auth>
各タグの中に該当する証明書(上記コメントのとおり)の鍵文字列を張り付けて保存します。
アプリの入手
iPhoneから App Store にアクセスし、OpenVPN Connect という無料アプリをダウンロードします。
設定のインポート
このアプリに、iTunes等の手段で、上記で作成した client1.ovpnを渡します。セキュアかどうか微妙なところですが、iPhoneのメールクライアントやファイラー等から .ovpnファイルを OpenVPN Connectで開くこともできます。
アプリでファイルを開くと、以下のような画面が表示されますので、+ボタンをタップします。これで、設定が完了します。
接続の開始
VPN接続を開始するには、下記の場所をタップしてください。
ステータスが Connected に変われば、接続完了です。
設定に問題がなければ、Safari等のブラウザやSMBに対応したファイラー等で、内部ネットワーク環境にアクセスできるはずです。下のアプリで、内部ネットワーク上の共有フォルダにアクセスることができました。
参考サイト
- http://www.marbacka.net/misc/arkiv/openvpn_iphone.html
- How To Set Up an OpenVPN Server on Ubuntu 14.04 | DigitalOcean
トラブルシューティング
もし、ステータスが接続済になっているにも関わらず、内部ネットワークにアクセスできない場合は、server.conf もしくは クライアントソフトウェア(あるいは client1.ovpn)の設定が間違っているか、ルーティングが正しく行われていないことが考えられます。
クライアント側の接続ログは、以下の場所から確認できますので、参考にしてください。
OpenVPN Connectの場合
サーバー側のログは、server.conf で何も設定を変更していなければ、システムログを確認します。
# view /var/log/syslog
以下に、私の引っかかったポイントをいくつか挙げておきます。
OpenVPNサーバーで、正しくIPフォワーディングの設定が出来ているか。とくに、sysctl.conf を変更し、反映したか。
- VPNサーバー自体(192.168.1.10)にはアクセスできるが、他のサーバー(192.168.1.20)にはアクセスできない状態の場合、これが該当する可能性が高いです。
クライアントとサーバーで、暗号化方式を揃えたか。
- サーバーで cipher を AES-256-CBC としたなら、クライアント側でも同じにする必要があります。
内部ネットワークからのレスポンスが正しく OpenVPNサーバーに戻ってくるか
- ルーター 192.168.1.1 で、10.8.0.0/24 宛てのパケットを正しくルーティングする必要があります。
俳句プログラミング投稿作品について
ブログをつくってみました。いろいろ燻っているので、IT関連のテーマでこちらに書いていこうと思います。
まずは、「プロ生ちゃん 俳句プログラミング プチコンテスト」に応募したコードの解説から。ちょっと分かりにくかったかな、という反省を込めて。コンテストの詳細は、以下のとおりです。楽しそうだったので、なんとなくノリで参加してしまいました。
今度はこれするって! 「プロ生ちゃん #俳句プログラミング プチコンテスト 2015」開催! | プログラミング生放送 http://t.co/nFj3GA6QVa
— プロ生ちゃん(暮井 慧)🍍 (@pronama) 2015年2月26日
応募作品概要
応募作品は、こんな感じです。
JavaScriptでもうひとつ作りました。俳句は画像の中に隠れています!https://t.co/N0WxAoIF91@pronama #俳句プログラミング pic.twitter.com/EEWd7esdwN
— たけまる (@felis_catus_) 2015年2月28日
@pronama 日本語をUTF-8で表すと3バイトなので、RGBのカラーコードから変換できるのでは、と考えてみました。ソース中にも別の俳句を仕込んであります。http://t.co/2VRNjCyWHK#俳句プログラミング pic.twitter.com/46PAmNnhrI
— たけまる (@felis_catus_) 2015年3月1日
ソースコードはこちらで公開しています。
プログラミング生放送のサイトでも取り上げて頂いています。
書いたよ! 「#俳句プログラミング プチコンテスト 2015」ピックアップ Vol. 2 | プログラミング生放送 http://t.co/LXU0UCKH48
— プロ生ちゃん(暮井 慧)🍍 (@pronama) 2015年3月3日
何をやっているのか
作品の説明に日本語をUTF-8で表すと3バイトなので、RGBのカラーコードから変換と書いたのですが、これだけだと何をやっているのか、いまひとつ伝わりにくいですね。なので、ここでもう少し詳細に説明しようと思います。
UTF-8では、日本語は3バイト
ウェブではすっかり標準化した文字コード UTF-8 ですが、Windows標準のShift_JIS等と違い、日本語に使われる漢字や仮名は2バイトではなく3バイト(24ビット)のコードで表されます。
たとえば、「あ」という文字をエンコードすると「%E3%81%82」になります。つまり、E38182という3バイトの16進数で表現可能ということです。
【みんなの知識 ちょっと便利帳】URLエンコード/デコードツール = UTF-8
カラーコードも3バイト
ところで、ウェブやPC上で扱われる「色」は、通常RGBつまり、赤・緑・青の光の三原色で表されます。とくに一般的なトゥルーカラーでは、赤・緑・青それぞれの要素が10進数で0~255の1バイト(8ビット)で表されます。3つの色要素を合わせて3バイト(24ビット)です。
RGB - Wikipedia, フルカラー - Wikipedia
たとえば、CSSなどで文字色や背景色を赤にしたい場合、#FF0000と表記しますが、これはそのRGBを16進数で表現したものです。
日本語を色に置き換えた画像を用意する
つまり、
- 日本語は3バイトのコードで表現できる
- 色は3バイトのコードで表現できる
ということです。ここで、「文字」⇒「3バイトのコード」⇒「色」と変換できるということを思いつきました(逆ではありません)。
では、早速、俳句を色に直してみます。例として芭蕉の句である
さまざまの 事思ひ出す 桜かな
なんてどうでしょう? 末尾に作者名も付けましょう。
文字 | コード | 色 | 文字 | コード | 色 | 文字 | コード | 色 |
---|---|---|---|---|---|---|---|---|
さ | E58FA4 | ■ | 事 | E38284 | ■ | 桜 | E38284 | ■ |
ま | E6B1A0 | ■ | お | E38284 | ■ | か | E38284 | ■ |
ざ | E38284 | ■ | も | E38284 | ■ | な | E38284 | ■ |
ま | E38284 | ■ | ひ | E38284 | ■ | |||
の | E38284 | ■ | 出 | E38284 | ■ | 芭 | E88AAD | ■ |
す | E38284 | ■ | 蕉 | E89589 | ■ |
おお!!見事に桜の雰囲気を表すピンク色のイメージとなりました。ついでに空白文字「 」をコード化すると、%20 となります。
文字 | コード | 色 |
---|---|---|
000020 | ■ |
それでは、これらの文字(色)を各所に散りばめた画像を作ってみます。せっかくなので、桜をイメージした画像にしました。プロ生ちゃんにも入ってもらいました。
呼び出したい文字(色)の座標をコード化する
さて、次は、上で用意した画像から俳句を抽出するのですが、各文字に対応する色は、桜の花びらとしてランダムな位置に散りばめてしまいました。なので、必要とする色が画像のどこにあるのかを座標を使って指定する必要があります。
たとえば、最初の「さ」という文字(%E5%8F%A4)は、画像の左上を原点とした座標上で x=450px, y=40px の位置にある花びらの色(#E58FA4)で表されます。つまり、「さ」という文字を、450, 040 という2つの数値で置き換えることができます。
同様にして、俳句を構成する文字列をすべて座標に置き換えると、
文字 | 座標 | 文字 | 座標 | 文字 | 座標 |
---|---|---|---|---|---|
さ | (450, 040) | 事 | (320, 060) | 桜 | (122, 044) |
ま | (260, 040) | お | (030, 030) | か | (234, 122) |
ざ | (060, 170) | も | (160, 160) | な | (170, 120) |
ま | (240, 020) | ひ | (180, 180) | ||
の | (044, 090) | 出 | (444, 144) | 芭 | (270, 070) |
す | (030, 130) | 蕉 | (260, 100) |
文字 | 座標 |
---|---|
(026, 192) |
というようになります。プログラム中でこの座標を利用して画像のピクセルデータを取得し、そのカラーコードを解析すれば、該当する文字が復元可能なはずです。
JavaScriptで画像解析を行うためには、HTML5 Canvasの機能を利用します。ごく簡単に述べると、以下のメソッドを利用して、(x, y) の座標から 任意の [width, height] のピクセルデータを配列として取得できるのです。
var id = context.getImageData(x, y, w, h);
取得した配列は、以下のような1ピクセル分のデータの繰り返しになっています。
添字 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
色 | 赤(R) | 緑(G) | 青(B) | アルファ |
配列の各要素の値は10進数ですが、次の方法で16進数に変換することができます。
var dec = 10; var hex = dec.toString(16);
JavaScriptによる画像解析の方法は、wata_htn様の記事に分かりやすく説明されています。
[3][HTML][canvas]canvasで画像解析 - wataメモ
座標コードも画像に埋め込む
これで、「座標」⇒「色」⇒「文字」という順で復元が可能になりました。この座標データを、数値の配列などの形でプログラムコードの中に保持すれば、その値を走査することで元の俳句の文字列が取得できるという算段です。
しかし、それでは、「結局のところコード中に俳句と等価なデータを持っている」ことになり、なんだか片手落ちな気がしました。できれば、俳句データはすべて画像が保持しており、(理論上は)コードを変更しなくても画像を差し替えれば別の俳句が表示されるようにしたかったのです。その無意味なこだわりのため、座標データも画像中に埋め込んでしまいました。
座標データは、画像の y=0 の行に埋め込みます。桜の画像の左上部分を拡大すると、下のように1ピクセルごとの色が並んでいます。
この色の並びを左から順にカラーコード、さらに座標に該当する文字に直すと、以下のようになっています。
ピクセル | カラーコード | 座標 | 文字 | |
---|---|---|---|---|
■ | R | 225 | 450 | さ |
G | 020 | 040 | ||
B | 130 | 260 | ま | |
A | 255 | |||
■ | R | 020 | 040 | |
G | 030 | 060 | ざ | |
B | 085 | 170 | ||
A | 255 | |||
■ | R | 120 | 240 | ま |
G | 010 | 020 | ||
B | 022 | 044 | の | |
A | 255 | |||
■ | R | 045 | 090 | |
G | 013 | 026 | (空白) | |
B | 096 | 192 | ||
A | 255 |
この調子で最後まで走査します。
ただし、やむを得ずいくつかの制約を設けてあります。
- 画像のサイズが 256×256 を越えているため、プログラムでは実際にはRGBの各値×2を実際の座標値として解釈しています。そのため座標は偶数でなければなりません。
- アルファ値は意図通りにデータとして拾い難いので、座標として採用せずスキップしています。
- 値が0の場合は無視します。x=0 あるいは y=0 の位置は、文字情報として使わないことにします。
こうして、走査したピクセルデータを基にプログラムで「座標」⇒「色」⇒「文字」と復元すれば、もとの俳句を得ることができます。あとは、得られた文字列を適当に加工して、Canvas 上に配置すればいいだけです。
JavaScriptコード解説
canvas の準備
まず変数の宣言、そしてHTML5 Canvasを準備します。
var haiku, id; var canvas = document.createElement("canvas"); var ctx = canvas.getContext('2d');
次に、Imageオブジェクトで画像をCanvasに読み込みます。
var img = new Image(); // JSFiddleに画像を添付するのは難しそうなので、画像は Dropbox から読み込む img.crossOrigin = "Anonymous"; img.src = "https://dl.dropboxusercontent.com/u/7521936/pronamachan_sakura.png"; // 画像が完全に読み込まれたら、画像をCanvasに取り込む img.onload = function(){ canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); document.body.appendChild(canvas); // 以降の処理 };
座標⇒色⇒文字の変換
Canvasに画像が読み込まれたら、先ほど登場した getImageData メソッドを使って、画像の先頭1行のピクセルデータを取得します。
id = ctx.getImageData(0,0,img.width,1).data;
目的である色情報は、取得した ImageData オブジェクトの data プロパティに格納されています。ただし、この配列は Uint8ClampedArray という特殊な配列であるため、map, reduce等で扱いやすいように普通の配列に変換しています。
haiku = Array(id.length).join(" ").split("")
さらに、値×2を座標として解釈する制約のため、すべての要素の値を2倍しています。
.map(function(e,i){ return id[i] * 2 })
アルファ値は座標値として採用しないため、4番目、8番目、・・・の要素を除外します。また、値が 0 となった場合も切り捨てます。
.filter(function(e,i){ return i % 4 < 3 && e > 0 })
この配列を [ [x1,y1], [x2,y2], ... ] という形の二次元配列に直しています。
.reduce(function(a,b){ if (a[a.length-1].length > 1) a.push([]); a[a.length-1].push(b); return a; }, [[]])
最後に、この二次元配列を座標と解釈し、各座標にあるピクセルの色情報を取得、そのカラーコードをデコードしてUTF-8の文字列に変換します。
.map(function(e){ // 座標 (e[0], e[1]) の1ピクセル分のデータを取得 var coord = ctx.getImageData(e[0], e[1], 1, 1).data; try { // R G B var code = [ coord[0], coord[1], coord[2] ] // の値を 16進数 に変換したうえで、UTF-8としてデコード可能な形式に変換 .reduce(function(a,b){ return a || b+0>0? a + "%"+b.toString(16) : a; }, ""); // 各要素を、文字と解釈してデコード return decodeURIComponent(code); } // 文字と解釈できないコードは無視 catch (e) { return null; } });
Canvas上に俳句を表示
こうして得られた文字列は、俳句として「一句め 二句め 三句め 作者」という形になっているものとします。最後に、この文字列を空白文字で分割し、位置を適度に調整してCanvas上に時間差で配置しています。
haiku.forEach(function(s, i){ setTimeout(function(){ ctx.font = "bold 30px '游明朝','ヒラギノ明朝 ProN W3','HG明朝'"; ctx.fillText( haiku[i], 20 + 80 * Math.pow(i, 1.45), // x座標 i==haiku.length-1 ? 180 : 35 + i * 40 // y座標 ); }, 1000 + i * 1200)});
これで、画像から俳句を読みって文字として表示するプログラムが出来ました。
もう一句!
mapやreduceやfilterといったメソッドの引数である無名関数を外出しし、わざわざ複雑なオブジェクト内に定義して、名前ををつけてみました。こうすることで無理矢理、コードの中に別の俳句をゴリ押しすることができました。
花の雲 鐘は上野か 浅草か
ちなみに、こちらも芭蕉の詠んだ桜がテーマの俳句です。
haiku = Array(id.length).join(" ").split("") .map ( Hana.No.Kumo ) .filter( Kane.Wa ) .reduce( Ueno.Ka, Asakusa.Ka ) .map ( Matsuo.Basho ).join("").split(" ");
コードの可読性からすると、完全に蛇足でしかないのですが・・・
最後に
桜をRGBに変換すると桜色っぽくなるよ!
@pronama
— たけまる (@felis_catus_) 2015年3月1日
櫻(%e6%ab%bb),桜(%e6%a1%9c)をRGBに変換すると桜色っぽくなるよ pic.twitter.com/mISH4upREj
(ネタバレ)
oO( つまるところ、有効な UTF-8 で表現した 3 バイト 1 コードポイントは、最初のバイトとそれ以外のバイトの値域があるため、どれも [平均的には] ピンクがかることになる。 )
— Tsukasa #01 (fully vaccinated) (@a4lg) 2015年3月1日
仰るとおりです(笑)
どういうことかというと、そうですね、たとえば「古池や 蛙飛び込む 水の音」を変換してみましょうか。まずは、「古池や」から・・・
文字 | コード | 色 |
---|---|---|
古 | E58FA4 | ■ |
池 | E6B1A0 | ■ |
や | E38284 | ■ |
ああ、残念ながらどれもピンクっぽいですね(笑)。 どう頑張っても、蛙や池のイメージである青や緑は出てきません。 そんなわけで、必然的にテーマは「桜」となった訳でした(梅でもよかったのですが)。