はじめに
はじめまして、エンジニアの@f_yamagamiといいます。
All About Group(株式会社オールアバウト) Advent Calendar 2018 21日目です!
明日から三連休なので、『今夜はお酒でも飲みながら、API接続しているSNSに、投稿予約をたくさんしよう!』と考えている方も多いと思います。
しかし、酔った勢いで投稿予約していた画像に、写ってはいけないものが写ってしまっていることに後になって気づき、画像を手元に落として加工して上げなおすのは面倒だし、ボタン1つで切り落としたい!といったことも多々ありますよね?
そこで、PHPを使ってボタン1つで画像の上下左右を切り落とす実装を試みました。
imagecopyresampled 関数
画像を再サンプリングするにはimagecopyresampled関数が使えそうなので、今回はこちらを使います。
imagecopyresampled — 再サンプリングを行いイメージの一部をコピー、伸縮する
bool imagecopyresampled ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )
>imagecopyresampled() は、イメージの矩形の部分 を別のイメージにコピーします。同時にピクセル値を滑らかに補間を行い、 このため、特にサイズを小さくした場合には鮮明さが維持されます。
>言い換えると、imagecopyresampled() は src_image の座標 (src_x,src_y) にある 幅 src_w、高さ src_h の矩形領域を受け取って、それを dst_image の座標 (dst_x,dst_y) にある幅 dst_w、高さ dst_h の矩形領域に配置します。
>dst_image:コピー先の画像リンクリソース。
src_image:コピー元の画像リンクリソース。
dst_x:コピー先の x 座標。
dst_y:コピー先の y 座標。
src_x:コピー元の x 座標。
src_y:コピー元の y 座標。
dst_w:コピー先の幅。
dst_h:コピー先の高さ。
src_w:コピー元の幅。
src_h:コピー元の高さ。
引用:http://php.net/manual/ja/function.imagecopyresampled.php
# **実装**
```image_cuter.php
$image = 画像のパス;
$cut_value = 切りたい長さ;
$direction = 切りたい上下左右;
if ($direction == '下') {
画像切る関数 ($image, 0, $cut_value, 0, 0);
}
if ($direction == '上' || $direction == '下') {
画像切る関数 ($image, $cut_value, 0, 0, 0);
}
if ($direction == '右') {
画像切る関数 ($image, 0, 0, 0, $cut_value);
}
if ($direction == '左' || $direction == '右') {
画像切る関数 ($image, 0, 0, $cut_value, 0);
}
function 画像切る関数 ($image, $old_y, $new_y, $old_x, $new_x) {
//画像ファイルの情報を得る
list($width,$height,$type) = @getimagesize($image);
//空の背景画像(黒)を作成
$new_width = $width - $old_x;
$new_height = $height - $old_y;
$resize_image = @imagecreatetruecolor($new_width,$new_height);
$new_image = @imagecreatefromjpeg($image);
//画像を再サンプリング
@imagecopyresampled(
// 背景画像
$resize_image,
// 元画像
$new_image,
// 背景画像の x 座標
$new_x,
// 背景画像の y 座標
$new_y,
// 元画像の x 座標
$old_x,
// 元画像の y 座標
$old_y,
// 背景画像の幅
$new_width,
// 背景画像の高さ
$new_height,
// 元画像ファイルの幅
$new_width,
// 元画像ファイルの高さ
$new_height
);
// 品質値 指定しなければ75
$quality = 100;
// jpeg形式で画像を保存
@imagejpeg($resize_image,$image,$quality);
//resize_imageを破棄
@imagedestroy($resize_image);
//new_imageを破棄
@imagedestroy($new_image);
}
上と左を切り落とす処理はシンプルですが、下と右を切り落とす処理は違和感を感じると思います。しかし、この方法でしか私は実装ができませんでした。
試しに、下記のように処理をまとめてみます。
$image = 画像のパス;
$cut_value = 切りたい長さ;
$direction = 切りたい上下左右;
if ($direction == '上') {
画像切る関数 ($image, $cut_value, 0, 0, 0);
}
if ($direction == '下') {
画像切る関数 ($image, 0, $cut_value, 0, 0);
}
if ($direction == '左') {
画像切る関数 ($image, 0, 0, $cut_value, 0);
}
if ($direction == '右') {
画像切る関数 ($image, 0, 0, 0, $cut_value);
}
function 画像切る関数 ($image, $old_y, $new_y, $old_x, $new_x) {
//画像ファイルの情報を得る
list($width,$height,$type) = @getimagesize($image);
//空の背景画像(黒)を作成
$new_width = $width - $old_x - $new_x;
$new_height = $height - $old_y - $new_y;
$resize_image = @imagecreatetruecolor($new_width,$new_height);
$new_image = @imagecreatefromjpeg($image);
//画像を再サンプリング
@imagecopyresampled(
// 背景画像
$resize_image,
// 元画像
$new_image,
// 背景画像の x 座標
$new_x,
// 背景画像の y 座標
$new_y,
// 元画像の x 座標
$old_x + $new_x,
// 元画像の y 座標
$old_y + $new_y,
// 背景画像の幅
$new_width,
// 背景画像の高さ
$new_height,
// 元画像ファイルの幅
$new_width,
// 元画像ファイルの高さ
$new_height
);
~省略~
}
結果がこちらです。
下: 右:
※上と左の処理には影響なし
処理の順番の問題か、反対側の画像情報が落ちてしまうようです。
そのため、今回の実装のように下と右を切り落とす処理は2回関数を呼ぶことで実現しました。
画像の動きは以下のようになっています
補足
わたしは画像を選択した後に、切りたい方向と長さを指定したボタンを並べて、ボタンを押すと切り落とした状態の画像が確認できるようにしました。
~省略~
<button type='button' onclick='location.href='./image_cuter.php?画像パス=".$予約投稿情報['画像パス']."&方向='上'&長さ='1'>上1</button>
~省略(その他のボタン)~
// 画像を切り落としてリダイレクトされた際に画像キャッシュをクリアするために、date('His')を末尾につける
<img src='".$予約投稿情報['画像パス']."?".date('His')."' border='1'>
~省略~
~画像を切り落とす処理~
// 元のページにリダイレクト
$uri = $_SERVER['HTTP_REFERER'];
header("Location: ".$uri, true, 303);
※画像の反転はimageflip
関数で簡単にできます。
参考:http://php.net/manual/ja/function.imageflip.php
最後に
コードに関しては
ツッコミどころ満載のコードであることは重々承知ですが、ご了承ください。
また、「下や右を切り落とす処理をもっとシンプルにできるよ!」
といった方は是非是非アドバイスお願いします。
今回の実装では、1度切り落とした部分は2度と元には戻りません。
不安な方は、保存の際に別名で保存し、確認画面を挟むと行った処理があれば安心安全に切り落とすことができると思います。
※私は確認が煩わしいので、不退転の覚悟で切り落とす方を選びます。
サンプルに使った将棋の画像ですが、
- 手前:石田流
- 奥側:四間飛車
と共に飛車を振った「振り飛車」と呼ばれる攻め方です。
同じ振り飛車ですが、手前の石田流の方が形が美しいと思いませんか?
自陣がガラ空きになることも厭わずに飛角桂銀を攻めに振り切り、
美濃囲いでありながら右端の歩をついて逃げ道を確保することもなく
相手の飛車先を玉頭で受ける積極的かつ強気の姿勢。
私事はございますが、今年は新卒らしい積極的な姿勢をあまり出せませんでした。
来年は、積極的に事業貢献できるように頑張ろうと思います。