スケルトン・エピ

letsspeakのブログです。

javascriptのcanvasで画像を自由にトリミングしてアップロードする機能を作ってみた

今回はjavascriptで画像をアップロードする際にトリミングするコードをcanvasを使って書いてみました。


趣味でゲームのスクリーンショットをアップロードしてみんなで見せ合うようなWebアプリケーションを作っていたのですが、元の画像サイズやファイル形式って人によってまちまちだったりします。


ゲームということもあり、デジカメのような大きすぎる画像サイズになることはないのですが、どうしてもプライベートなチャット内容が入ってしまうのは嫌です。できれば自分の見せたい範囲だけをアップロードしたいものです。


また無圧縮のpngやbmpなんかでアップロードされた場合、数MBの画像をやりとりする分けにはいきません。そこですこし劣化したjpeg等の圧縮された画像形式に変換することになりますが、これをサーバー側で行うと、プレビュー画像をユーザーに見せるタイミングがサーバーで処理を行った後になってしまい、ユーザーの時間とサーバーリソースが無駄に消費されてしまいます。


そんな訳ですべてjsのcanvasでやってしまおう!という感じで実験しつつ作ってみました!

trimming_upload.js
https://github.com/letsspeak/trimming_upload.js


行っていることは、

1.ファイルが選択されたら画像を読み込む
2.この際、画面表示用のlayer1には設定された最大サイズで比率を維持して読み込む
3.また裏のbaseに元画像のサイズのまま読み込んでおく
4.画面表示部分がクリックされた際にlayer2にトリミング用の矩形を描画する
5.画面には常にlayer1とlayer2を合成して表示する
6.トリミング確定後、アップロードボタンが押されたら元サイズのbaseボタンからトリミングサイズに基づいた比率で切り抜いてデータを送信する。

という感じです。

f:id:letsspeak:20131122020607p:plain


こんな感じで設定した最大さいずでプレビューしつつ、トリミングは元画像の比率で行うようになっています。

今回のハマりポイント
context.drawImage() の際の第一引数になるcanvasサイズが width, height ともに 0 の場合に例外が発生するようです。これに気づかず20分程はまってしまいました...。


書いていて気がつきましたが、元のトリミングサイズのままアップロードしたい場合の処理がありませんねwこの辺を次の課題としつつ今回はこの辺で終わりとさせていただきます。

ではよいお年を。

電脳化の話メモってなかったので

忘れる前に書いておく。
インプットにせよアウトプットにせよ、その信号が何を表すかというのには個体差があるのではないだろうか。
全ての存在する概念について人間個体の脳結合要素を洗い出してるうちに人生が終わりそう。

と思って結構悲観的に考えてたけれども、今思ってみると概念まで直接伝達する必要は微塵もないな。
むしろそれは200年後の話で、とりあえずのところは5感にインプットすればいいじゃん。はい解決。

ec2にhaskell-platformをインストールする

噂のyesodさんを試してみたいと思いつつ、手元のmacで失敗した経緯をもとにインスタンスをすぐ消したりできるec2を使って試してみました。

インスタンスの立ち上げ方については詳しいページがあると思うので割愛!
最初はさくらVPSで使っているCentOSと同じRed Hat Enterprise Linux Server release 6.4 (Santiago) を選択しましたが、haskell-platformのmakeで失敗しました。
はっきりいって初心者の自分には何が理由で失敗したのかさっぱり分らなかったので、気を取り直してAMIでインスタンスを建て直し、試しにRed Hat と同じコマンドを打ってみたところhaskell-platformのmakeが順調に進み....謎の失敗。

ググってみると下記のブログがヒット!

AWSのアカウントを作ってみた
http://amkkun.hatenablog.com/entry/2013/02/06/183901

どうやらマイクロインスタンスではメモリが足りないという事で、一時的にスモールインスタンスに変更してmakeを実行します。いつもいつも先駆者の方々に感謝です。

macであれほどうまくいかなかったcabal-installについても、一度は失敗したものの先述のmake時と同様にスモールインスタンスで実行したところ無事成功しました。
一通りの手順は下記に纏めておきました。

set up yesod on ec2 Amazon Linux AMI 2013.03.1 64bit
https://gist.github.com/letsspeak/5650158

haskellぐぬぬって感じは未だに拭えないです。
無事yesod環境構築できた暁には、AMIのスナップショットを保存しておきたいなー

ファイルの所有権について

ファイルシステムって概念自体を理解する事が難しい。

デジタルネイティブなら普通に理解していたりして、現状のままでも良いのかもしれないけど、もうちょっと理解しやすいファイルシステム無いのかなーとか、なんで理解しづらいのかなーとか、ファイル名以外からファイルを特定する手順についてほんの少しだけ考えてみた。

そこで思ったのが所有権。

ファイルから名前という属性を取った時に一番最初に思うのは、○○さんから貰った○○。
今のPCってファイルを端末間でコピーしたら所有権は基本的に委譲されるけど、これってかなり重大な設計ミスなんじゃなかろうかー

真逆の発想をとると、ファイルの所有権なんて無視してすべてのファイル誰でもいつでも見れるようにしてしまう。

私の煩雑なデスクトップをコンピュータさんが自動的に整理するためには、どちらかが必要なんじゃないかなと、なんとなく思ったのであった。

追記:

ファイル共有で動画でもアニメでも音楽でもなんでも無料で配信してしまってから、支援者がお金を払うシステムっていいんじゃねってちょっと前に思っていたけれども、人間無料で手に入れられるものは無料で手に入れてしまう。(それによって相対的に自分の価値を下げてしまうにもかかわらず。)

で、これの問題点を解消するべく、お金を払って得た人と、はらわずに得た人を晒しものにしてしまえば良いのではなかろうか。などと思ったところで、ニコ生ってすげえなと思いつつ寝るのであった。

ビジュアルプログラミング

ビジュアルプログラミングがいま熱い!

enchantMOONの登場でさらに加熱して、自分ならタブレット端末でどんな風にプログラミングできる環境を作るかを妄想しながら寝た。

まずもって時が小さかったり情報が詰まってるのはアウト。
よく使う制御構造や機能は簡単な記号の手書き認識化。
挿入文字列はすべて別の辞書化。
制御構造の逐次記述は多くても1画面4行程度。

関数型言語をまじめに勉強していないので、
逐次記述がほんとうに必要なのかどうなのかをもう少し煮詰めて、
モジュール化とその関係性のビジュアル化の可能性を突き詰める必要あり。

起きてから、MOON block 見てみたけど...
ごちゃごちゃしてる作業場にするならタッチペン使う必要ないじゃん...。
ほんとうにさっと端末を取り出して、さっと書けるようにするのが理想。

宇宙の外側

宇宙の外側がどうなってるのかってのは、人間として生きてる以上、気になって夜も眠れないレベルの問題なんだけれども、

地球上のもの < 地球
地球 < 太陽系
太陽系 < 銀河系
銀河系 < 銀河団

みたいな感じでスケールして、宇宙すげー!
ってことは

宇宙 < ???(宇宙がたくさんある?)

っていう考えはまず間違いであることに気づいた。
というのも次元で考えると全部同じ範囲に収まるからで、

地球上のもの < 地球 < ... < 宇宙 < ????

という比較は、

もの < 宇宙 < ????

と変わらず「もの」の中の規則をそのまま外にあてがうのは間違っている。
あまりにも宇宙ヤバイインパクトが強すぎて目の前が見えなくなってしまっていた。
比べることで規則性を見いだすのであれば、

素粒子のふるまい < 宇宙 < ????

の方がよっぽど妥当じゃないか、と風呂に入りながら考えた。

CentOSにGrowthForecastをいれて色々表示してみた

最近CentOSでFuel先生とごにょごにょしているのですが、データがあまりにも大量なため基本的には静的コンテンツをタスク生成してリクエスト発生時はSELECTかmemcachedからフェッチするだけーみたいな感じで動かしていました。

そうなるとfuel/app/tasksにタスクが増える増える。crontab先生大活躍な訳なんですが、はたから見てるとちゃんと動いているのかどうかまったく分からないので、統計をとってみようとfluentdとか調べ始めたところでGrowthForecastというグラフ生成Webツールを発見して、さっそく入れてみました!

ついでに、先日の作業中0時頃にとつぜんフリーズでBrokenPipeした件も調べたいと思い、CPU使用率、メモリ使用率も集計してみることにしました。

GrowthForecastのインストール

ここが一番大変でした。
最初はこちらのgist installing_growth_forecast の通り進めたのですが、root権限でインストールしてこれで良かったのか..。と思っていた所に良い記事を発見したので下記記事を参考に進めました。

GrowthForecast を CentOS 6.3 にインストールして Supervisor で管理してみた

主にcpanmとGrowthForecastのインストールに結構時間がかかりました。

GrowthForecastのアクセス制限

動作確認後、新しいホスト名を追加してnginxでBASIC認証かけてポートを閉じます。

nginx.conf
  upstream growthforecast {
    server 127.0.0.1:5125;
  }

    server {
    listen 80;
    server_name growthforecast.yourdomain.com;

    proxy_connect_timeout 60;
    proxy_read_timeout    60;
    proxy_send_timeout    60;


    auth_basic "Secret Area";
    auth_basic_user_file "/home/growthforecast/.htpasswd";

    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_pass http://growthforecast;
      proxy_redirect off;
    }
  }
.htpasswdの作成
htpasswd -c /home/growthforecast/.htpasswd username

CPU使用率をロギングしてみる

GrowthForecastはintegral (整数) でなければ受け付けてくれないので、小数点以下を切り捨てた値を取得するワンライナーを書いて、crontabで1分毎に報告させます。

CPU使用率(パーセント)を整数で表示する
# cpu usage (integral)
cat /proc/loadavg | cut -d ' ' -f 1 | cut -d '.' -f 1
CPU使用率をGrowthForecastに報告するcrontab
# send your cpu usage (integral) to GrowthForecast every 5 minutes via crontab
*/1 * * * * curl -F number=`cat /proc/loadavg | cut -d ' ' -f 1 | cut -d '.' -f 1` http://localhost:5125/api/yourmachine/system/cpu 2>&1

gistにも纏めておきました。
centos_cpu_usage_one_liner.sh

メモリ使用率をロギングしてみる

ワンライナーで書けさえすればGrowthForecastに報告できる!と知ってawkを調べながら書いてみました。実際の使用率については、freeコマンドの見方を参考に計算しています。

メモリ使用率(パーセント)を整数で表示する
// actual memory usage (integral)
// http://open-groove.net/linux-command/free/
free | awk 'NR==2' | awk '{print int(($3-($6+$7))/$2*100)}'

こちらもCPU使用率と同じようにnumber=``でくくってcrontabに登録すれば完了です。
メモリ使用量についてもgistに纏めておいてます。

centos_memory_usage_one_liner.sh

グラフ化結果

f:id:letsspeak:20130330222650p:plain

データを取ってみると、CPU使用率が全然低いのとメモリ使用量がおよそ70%と高めなのが気になります。
もしかすると取得方法が間違っているのかもしれないので、これから他のVPSや新しい環境構築時にログをとって比べてみたいと思います。
もし間違いなどお気づきになりましたら、ご指摘頂けると幸いです。

FuelPHPのタスクと連携

FuelPHPのタスクと連携するためにGrowthForecastにPOSTするための簡単なモデルを作成しました。

fuel/app/classes/model/growthforecast.php
<?php

Class Model_GrowthForecast extends Model
{
  static public function post($section_name = null, $graph_name = null, $number = null, $mode = 'gauge')
  {
    // validation
    if (is_null($section_name)) return;
    if (is_null($graph_name)) return;

    if (is_int($number) === false)
    {   
      if (is_numeric($number) === true)
      {   
        $number = (int)$number;
      }   
      else
      {   
        return;
      }   
    }   

    $url = 'http://localhost:5125/api/yourapplication/'.$section_name.'/'.$graph_name.PHP_EOL;
    $data = array(
      'number' => (string)$number,
      'mode' => $mode,
    );  

    $headers = array(
      "Content-Type: application/x-www-form-urlencoded",
      "Content-Length: ".strlen(http_build_query($data)),
    );  

    $options = array('http' => array(
      'method' => 'POST',
      'content' => http_build_query($data),
      'header' => implode("\r\n", $headers),
    )); 

    $contents = file_get_contents($url, false, stream_context_create($options));
  }
}

これを、こんな感じでTaskから呼び出すだけでどんどんグラフ化してくれます。

    \Model_GrowthForecast::post('crawler', 'source_count', count($sources));

f:id:letsspeak:20130330223350p:plain

やっぱり視覚化されるのは純粋に楽しい!わくわくします。

問題点など

今回ポートを閉じてしまいましたが、特定のサーバーから報告をPOSTする場合、IPアドレス指定のオプションをつけて起動して、ポート自体は開けておいたほうが良さそうです。

また今回作成したモデルではyourapplicationの部分を固定化してしまっていますが、開発環境、テスト環境、プロダクション環境に応じて値が変わるようにmodifyする必要がありそうです!