という話

技術ブログにしたい

HTML5で背面カメラを取得する

HTML5が世に出て久しく立ちますがあまりカメラを使ったサービスって見ないですね。
ピントが合わせることが出来なかったり対応してるブラウザが少ないのが原因だと思いますが。。。

HTML5 カメラ」とかで検索するとgetUserMediaを使ってカメラ映像を取得しよう!みたいな記事が多いんですが、スマホなどで2つ以上カメラがある場合どちらか一方(ほとんどが内側カメラ)しか取得出来ないんですね。

なので背面カメラを取得したいと思います。

MediaStreamTrack

端末にあるカメラとかマイクの一覧を取得するAPIとしてMediaStreamTrackというものがあります。
MediaStreamTrack.getSourcesというAPIを使用するとカメラやマイクのIDを取得することが出来ます。

MediaStreamTrack.getSources(function(data)
{
  console.log(data);
});

という感じで呼び出すと、対応したブラウザでかつカメラやマイクがあるならコンソールに以下の様な感じで吐き出されます。

0: SourceInfo
  facing: ""
  id: "bf9f10e6cf64b14a4c5b544ccda64932e9565706316e78c2f97864ed2fd0338a"
  kind: "audio"
  label: ""
1: SourceInfo
  facing: ""
  id: "b65d00ea51cf8ea7cd33c31fc0dfc7e08ae7ee6b89b55b21d9454bf43efc16b7"
  kind: "audio"
  label: ""
2: SourceInfo
  facing: ""
  id: "3d75291cbeaffcdd1907f3fb9ec933c1846991f74795f02ad7ac72201b06f186"
  kind: "video"
  label: ""

これはMacbookAirでやった場合です。
このidがマイクやカメラへアクセスするときに必要になります。

背面カメラを取得する

getUserMediaで{video:true}みたいな感じでカメラを取得すると端末のデフォルト?のカメラが勝手に取得されますが、先ほど取得したIDを変数などに保持しといて以下のようにすると、対象のカメラを取得できます

navigator.getUserMedia({
  video: {
    optional: [{sourceId: VIDEO_SOURCE_ID}]
  }
});

VIDEO_SOURCE_IDの部分を変更してください。
マイクも一緒に取りたい場合は以下のようにしてください。

audio: {
 optional: [{sourceId: AUDIO_SOURCE_ID}]
},
video: {
  optional: [{sourceId: VIDEO_SOURCE_ID}]
}

デモ

Androidの最新版Chromeなどで見てください。Android標準ブラウザは対応していません。
iPhoneはどのバージョンでも対応してません。
HTML5で背面カメラを取得する - 遊び場


参考

ここに書いてあることのほとんどはここを参考にしました。というかほぼ一緒です。
simpl/getusermedia/sources/js/main.js at master · samdutton/simpl · GitHub

日本語の文章が少ないので増えて欲しいところですねー

FuelPHPでEXIF情報を読み画像を回転させる

iPhoneで撮った画像をサーバーにあげて表示すると、全て横向きで表示されます。
これはEXIFの情報で縦にしてるからなんですね。知らなかったです。

ということでEXIF情報を読み込んでFuelPHPで角度にあった回転をさせます。

public static function exif_rotate($filepath)
{
  try
  {
    $exif = exif_read_data($filepath);
  }
  catch(Exception $e)
  {
    return 0;
  }

  if (isset($exif['Orientation']) && is_numeric($exif['Orientation']))
  {
    $rotate = 0;
    switch ($exif['Orientation'])
    {
      case 1:
      case 4:
      break;

      case 2:
      case 3:
        $rotate = 180;
      break;

      case 5:
      case 8:
        $rotate = 270;
      break;

      case 6:
      case 7:
        $rotate = 90;
      break;
    }

    return $rotate;
  }

  return 0;
}

こんな感じの関数を用意しました。
これにファイルパスを投げると画像に必要な回転の角度が返ってきます。

使い方はこんな感じ

$filepath = 'image.jpg';
Image::forge()->load($filepath)->rotate(self::exif_rotate($filepath))->output($filepath);


exif_read_dataでEXIF情報を読み込みます。
Orientationキーが回転を表す数値です。
詳しいことは下のURLを参考にさせてもらいました。
JPEGのExifタグ情報のOrientaionの定義の早見表 - DQNEO起業日記

EXIF情報はjpegTIFFとかいう画像にしか存在しないので、それ以外のファイルが指定された時は0を返すようになっています。

本当は左右反転、上下反転ていうのが定義されてるのですが、GDには反転するメソッドは用意されていないし、わざわざ反転する人も殆ど居ないので無視しちゃってます。

Imageクラスのでrotateメソッドを使って回転させます。
Image - クラス - FuelPHP ドキュメント



これでiPhoneで撮った画像も正しい角度で表示されます

料理を美味しく見せるためのCSSフィルター

CSSフィルターって名前だけは聞いたことあったんですが、こんなに色々出来るんですね。。

・グレースケール
・セピア
・色相
・明度
・彩度
・透明度
・階調の反転
コントラスト
・ドロップシャドウ
・ぼかし

CSSさんナメてましたごめんなさい。

CSS3のフィルター効果をまとめた | おぼめも
上記URLでとっても素晴らしい効果の数々を確認出来ます。綺麗なサイトですなぁ。

料理を美味しく見せるには

暖色系を使う

図る〜色と食欲の関係
人間には暖色系の色(赤とかオレンジ)を使うと食べ物がより美味しく見えるようです。

入った瞬間に売れなそうな店だなー、とか美味しくないのでは・・・と思う店ってあるんですが、そういうとこって大体青白系の蛍光灯を使ってるんですね。
よく中華料理屋とかラーメン屋とかで見かけます。

記憶色に近づける

用語解説辞典 | NTTPCコミュニケーションズ
人間は目で見たものを記憶するとき、より鮮やかな色として記憶するそうです。
つまり実際の色と同じ色の写真を見せるより、鮮やかな色にすることで記憶色に近い写真になるといえます。

CSSフィルター

元の写真。僕がどこかのラーメン屋でiPhone5Sで撮りました。
f:id:ichiy:20140618163321p:plain
これだけでもラーメン好きの僕としてはかなり来るんですが、もっと美味しそうに見せられます。

明るさを足す

f:id:ichiy:20140618163548p:plain
店内は大抵暗くて明るさも足りないので、明るくします。

CSS

img {
  -webkit-filter: brightness(1.2);
  -moz-filter: brightness(1.2);
  -o-filter: brightness(1.2);
  -ms-filter: brightness(1.2);
  filter: brightness(1.2);
}
彩度を足す

f:id:ichiy:20140618163917p:plain
記憶色に近づけるため鮮やかな色合いにします

CSS

img {
  -webkit-filter: brightness(1.2) saturate(150%);
  -moz-filter: brightness(1.2) saturate(150%);
  -o-filter: brightness(1.2) saturate(150%);
  -ms-filter: brightness(1.2) saturate(150%);
  filter: brightness(1.2) saturate(150%);
}

ぐっと美味しそうになりましたね


暖色系にする

f:id:ichiy:20140618164135p:plain
色相を弄って暖色系にします。

CSS

img {
  -webkit-filter: brightness(1.2) saturate(150%) hue-rotate(-10deg);
  -moz-filter: brightness(1.2) saturate(150%) hue-rotate(-10deg);
  -o-filter: brightness(1.2) saturate(150%) hue-rotate(-10deg);
  -ms-filter: brightness(1.2) saturate(150%) hue-rotate(-10deg);
  filter: brightness(1.2) saturate(150%) hue-rotate(-10deg);
}

このhue-rotateってパラメータをどんどん弄ってくと、逆に青系とかの色にも出来るのでまずそうな写真にも出来ます。


元画像と比較

f:id:ichiy:20140618164423p:plain
全然印象が違いますね!
個人差はあると思いますが僕は圧倒的に右の方が美味しそうに見えます。
てかラーメン食いたくなった!クソ!

各パラメータは画像によって調整が必要ですが、今あげた3つのフィルターをかけるだけでも結構効果が見込めますね。


まとめ

料理の写真を多く扱うサービスを運営してて、ユーザーが投稿したクオリティの低い写真をどうにか美味しそうに見せられないかと調査したときのメモなのですが、CSSフィルターの本気に驚きました。

色味が違うだけでここまで印象に差があるので、料理じゃない写真にもどんどん使っていこうと思います。
お肉屋さんが、肉を赤く見せるために謎の液体を付ける理由も理解できます(遠目)

色々書きましたが、僕はエンジニアなので色彩のこととかよく分かりません。
本当にクオリティの高い修正をしようと思ったらPhotoShopを扱えるデザイナーさんに頼むのがいいと思います。

InstagramAPIのタグ検索で次のページを表示する方法

instagramのAPIを使って写真を取得する方法 : matsudam blog
このページを参考にアプリケーションを登録します。

この情報だとちょっと古くてアクセストークンが取得できないので別のやり方で取得します。

Instagramにログインした状態で以下のページにアクセスします

https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token

「CLIENT-ID」と「REDIRECT-URI」を自分のアプリケーションに置き換えてアクセスしてください。

画面が表示されたら「Authorize」ボタンをクリック。
するとリダイレクト先に「access_token」というパラメータ付きでリダイレクトされます。

APIにアクセス

アクセストークンがGET出来たので早速InstagramのタグAPIにアクセスします。
Tag Endpoints • Instagram Developer Documentation

上記のリファレンスを参考にアクセスします

https://api.instagram.com/v1/tags/snow/media/recent?access_token=ACCESS-TOKEN

JSONが返ってきますね。
上記URLの「snow」ってなってる部分を変更することで好きなタグにアクセス出来ます。

で、表題の次のページに行く方法なのですがレファレンスにはこう書いてあります

Get a list of recently tagged media. Note that this media is ordered by when the media was tagged with this tag, rather than the order it was posted. Use the max_tag_id and min_tag_id parameters in the pagination response to paginate through these objects. Can return a mix of image and video types.

Google翻訳さんの力を借りると、ページ毎にアクセスするにはmax_tag_idとmin_tag_idを付けてくださいってことらしい。

APIの中身を見るとpaginationキーの中にそれっぽいものがあります

"pagination": {
        "deprecation_warning": "next_max_id and min_id are deprecated for this endpoint; use min_tag_id and max_tag_id instead", 
        "min_tag_id": "1401794306803592", 
        "next_max_id": "1401794079657725", 
        "next_max_tag_id": "1401794079657725", 
        "next_min_id": "1401794306803592", 
        "next_url": "https://api.instagram.com/v1/tags/snow/media/recent?access_token=ACCESS-TOKEN&_=1401767576393&max_tag_id=1401794079657725"
    }

この「next_max_id」と「next_min_id」を指定すれば次のページ取れるんだなーと思ってjQueryでこんな感じで書きました。

var minTagId = undefind;
var maxTagId = undefind;
var tagName = 'snow';

function getInstagram()
{
  $.ajax({
    type: 'get',
    url: 'https://api.instagram.com/v1/tags/' + tagName + '/media/recent',
    dataType: 'jsonp',
    data: {
      access_token:ACCESS-TOKEN,
      max_tag_id:maxTagId,
      min_tag_id:minTagId
    },
    jsonpCallback: 'instagram',
  })
  .done(function(data)
  {
    maxTagId = data.pagination.next_max_id;
    minTagId = data.pagination.next_min_id;
    Console.log(data);
  });
}

1回目の時はundefindで2回目以降はレスポンスのnext_max_idとnext_min_idを指定するようにしたんですが、これではダメでした。何回やっても同じデータが取得されます。

で、色々ググったんですが見つからなくて途方にくれていたんですが、答えはレスポンスに書いてありました。

_人人人人人人_
> next_url <
 ̄Y^Y^Y^Y^Y ̄

先ほどのJSONにも書いてますがpagination.next_urlというキーで帰って来てるURLにアクセスすれば次のページが取得できます。


next_urlと自分が書いたリクエストurlを比べるとmin_tag_idが無いのでmin_tag_idなしでリクエストしてもダメでした。
レファレンスに書いてあるとおりにやってるのにー!

何か釈然としないのですが、next_urlには&_=********みたいな形で知らないパラメータが追加されてるので、なんかこの辺でゴニョゴニョしてるのかなーとか思うんですがInstagram側のことなので分かりません。

git commit前にGruntでCSSとJSをminifyしてaddしてcommit

GooglePageSpeed Insightsとか使うと必ずJavaScript を縮小するCSS を縮小するって怒られませんか?
僕はよく怒られます。

なのでGruntとかいう凄いツールを使って怒られないよう頑張って見ようと思います。

したいこと

CSSとJSのminify(圧縮)して*.min.css / *.min.jsという名前でそれぞれ保存する
・git hooksを使ってcommit前にGruntを実行し、出来上がったファイルをaddする

Grunt cliのインストール

$ npm install -g grunt-cli

プロジェクトの設定

Gruntはプロジェクトごとにインストールする必要があります

$ cd project_directry
$ npm init

ターミナル上で色々質問されるので入力していくとpackage.jsonとGruntfile.jsが出来上がります。

Gruntのインストール
$ npm install grunt --save-dev
Gruntプラグインのインストール

cssminとuglifyというプラグインをインストールします

$ npm install grunt-contrib-cssmin --save-dev
$ npm install grunt-contrib-uglify --save-dev

Gruntfileの設定

$ vim Gruntfile.js

Gruntfile.js

module.exports = function(grunt) {
  grunt.initConfig({
    pkg : grunt.file.readJSON('package.json'),
    cssmin : {
      css1 : {
        src : 'public/assets/css/1.css',
        dest : 'public/assets/css/1.min.css'
      },
      css2 : {
        src : 'public/assets/css/2.css',
        dest : 'public/assets/css/2.min.css'
      }
    },
    uglify : {
      min : {
        files: {
          "public/assets/js/1.min.js" : ["public/assets/js/1.js"],
          "public/assets/js/2.min.js" : ["public/assets/js/2.js"]
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  grunt.registerTask("default", ["uglify", "cssmin"]);
};

この設定で、1.cssは1.min.cssという名前で圧縮したものが保存されます。
同じ用にjsも1.jsが1.min.jsという名前で圧縮されてます。

cssはまとめることも出来てそのほうがリクエスト数も減るしいいのですが、ページごとに読み込ませるcssを変えてるので今回このようにしました。

Gruntの実行
$ grunt

これでGruntfile.jsに設定されている通りに圧縮などの作業が行われます。


git hooksを使い自動化

gitには特定のタイミングでシェルなどを実行出来る機能があります。
今回はcommit直前なのでpre-commitを使用します

$ cd .git/hooks/
$ vim pre-commit

pre-commit

#!/bin/sh
grunt
git add public/assets/js/*.min.js
git add public/assets/css/*.min.css

pre-commitファイルに実行権限を与えます

$ chmod 775 pre-commit


これでgit commitを実行するとgruntが実行されminファイルがaddされてからcommitされます。
このpre-commitとかにJSHintとか噛ませるのがナウいみたいですね。