という話

技術ブログにしたい

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とか噛ませるのがナウいみたいですね。

ネットワーク素人が、さくらクラウドで負荷分散構築した時のメモ4【Fileサーバー編】

前回のネットワーク素人が、さくらクラウドで負荷分散構築した時のメモ3【DBサーバー編】 - なりせなるてずからひき続きです。
今回で最終回です。

前回までの構成で以下のようになりました。
f:id:ichiy:20140509143327p:plain


今回はファイルサーバを構築します。

NFSとは

NFSとはWindowsで言うところの共有フォルダです。
Sambaとか、あとはデフォルトのグループ機能とかみたいなもんですね。

元々1台のサーバで運営してた頃はwebもDBもファイルも全部1台だったので気にしなくてよかったのですが、Webサーバーを複数にしたので例えば画像をアップロードするとロードバランサで振り分けられた方のWebサーバのみにしか画像は存在しない事になります。
それでは画像がある方のWebサーバに接続された時しか画像が表示されてないのでNFSの出番です。

NFSサーバに複数のwebサーバからアクセスすれば問題ありません。

rsyncで同期すればいいんじゃね?とか思ってたんですが、お互いがMasterになるのでちゃんと同期出来ないみたいです。
Nix::WebLab : lsyncd の双方向同期はヤメておけ!
実はNFSを導入前に試そうと思ったんですが、上記の記事見てガクブルしたので辞めました。

Fileサーバーの設定

NFSのインストール

さくらクラウドのコントロールパネルで当該サーバを選び、コンソールを開き次を入力します。

# yum -y install nfs-utils

インストールはこれだけです。簡単!

接続先の変更

もうyumを使わないので、ローカルネットワークに接続先を変更します。
当該サーバの電源操作からシャットダウンし、NICの接続先をローカル用のスイッチに変更します。
前回同様なのではしょります。

共有するディレクトリを作る
# mkdir -p /export/kyoyu

/exportディレクトリはNFSの共有用のディレクトリらしいです。
Windowsユーザーに教えるLinuxの常識(2):各ディレクトリの役割を知ろう(ルートディレクトリ編) (1/2) - @IT
「kyoyu」の部分は好きな名前にしてください

オーナーとグループの変更
# chown -R nfsnobody:nfsnobody /export
NFSの設定
# vi /etc/exports

設定ファイルを開いたら以下を書き込みます

/export/kyoyu 192.168.0.0/28(rw,all_spuash,sync) 

「/export/kyoyu」ディレクトリを192.168.0.0/28のIPアドレス帯域のユーザーに読み書き可能にし、読み書きするユーザーを「nfsnobody」にすると言った意味になります。
rwが読み書き可能
all_spuashがユーザーをnfsnobodyにする。上でディレクトリのオーナーをnfsnobodyに変更したのはこのためです。
syncは遅延書き込みを無効にします。

ポートの固定

NFSで使用するポートの固定をします

# vi /etc/sysconfig/nfs

設定ファイルを開いたら*PORT=数字となってる部分のコメントを解除します


NFSの起動

NFSはrpcbindが必要なので起動します

# /etc/rc.d/init.d/rpcbind start

nfs、nfslockを起動します

# /etc/rc.d/init.d/nfslock start
# /etc/rc.d/init.d/nfs start

自動起動するように設定します

# chkconfig rpcbind on
# chkconfig nfslock on
# chkconfig nfs on


FIleサーバー側の設定は以上です。

クライアント側の設定

今回はWebからのみFileサーバーにアクセスするので以下の設定をwebサーバに行います

rpcbindのインストール
# yum install rpcbind
iptablesの設定
# vim /etc/sysconfig/iptables  

以下を貼り付け

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 32765:32768 -j ACCEPT  
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 2049 -j ACCEPT  
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 111 -j ACCEPT  
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 747 -j ACCEPT  
rpcbindの起動
# /etc/init.d/rpcbind start
rpcbindの自動起動設定
# chkconfig rpcbind on
マウント用ディレクトリの作成
# mkdir /home/mount

ディレクトリの場所は任意です

マウントする
# mount -t nfs 192.168.0.10:/export/kyoyu /home/mount

IPアドレスはFileサーバーのIPアドレスにしてください。



確認

$ df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/vda3              97G   20G   72G  22% /
tmpfs                 1.9G     0  1.9G   0% /dev/shm
/dev/vda1              98M   52M   41M  57% /boot
192.168.0.10:/export/kyoyu
                       97G  4.5G   88G   5% /home/mount

マウントされた!(∩´∀`)∩ワーイ

あとは画像とかWebサーバー間で共有が必要なファイルのパスをマウント先にしてやることで読み書きが出来ます!


参考にしたサイト
はじめての自宅サーバ構築 - Fedora/CentOS - ファイルサーバの構築(nfs)
[CentOS6] NFSサーバ構築 - OSSでLinuxサーバ構築


最終回のまとめ

今回で、一番初めに貼った画像と同じ構成になりました。
f:id:ichiy:20140425121049p:plain

ここまで構成するのに勉強+調査+構築で5日もかかりました。
そもそもLinux触りだして1年半のペーペーに任せるべきことでは無いと思うのですが、僕と同じような境遇のプログラマーの皆さんの参考になればと思います。