วันพุธที่ 14 กุมภาพันธ์ พ.ศ. 2561

[PHP] Resize, Crop และ ใส่ลายน้ำให้กับรูป


          กลับมาอีกแล้วสำหรับบทความเขียนโปรแกรม (อันน่าปวดหัว) ในบทความนี้เราจะมาเขียนโปรแกรมภาษา PHP เพื่อทำการย่อ-ขยาย ตัด แล้วก็ใส่ลายน้ำลงไปในรูปกัน ซึ่งโปรแกรมจะให้เรากำหนดขนาดกว้างและยาวของรูปที่ต้องการจะตัดออกมาจากรูปต้นฉบับ จากนั้นก็จะทำการย่อรูปให้อยู่ในขนาดที่ต้องการโดยยังคงอัตราส่วนกว้างต่อยาวของรูปต้นฉบับไว้อยู่ด้วย แล้วจึงทำการตัดรูปในส่วนที่ต้องการออกมา สุดท้ายก็ใส่ลายน้ำลงไปในรูป (อ่านดูแล้วอาจจะงง ๆ แต่เดี๋ยวลงมือทำแล้วก็จะเข้าใจเอง)

          ข้อดีของเจ้าโปรแกรมนี้คือเราสามารถประยุกต์เอาโค้ดบางส่วนแยกไปใช้งานได้ เช่น ใช้ในการย่อ-ขยายรูปโดยที่อัตราส่วนของรูปยังเหมือนเดิม หรือใช้เพิ่มลายน้ำลงไปในรูป เป็นต้น .....เกริ่นกันพอแล้วเราก็มาเริ่มเขียนโปรแกรมกันเลยดีกว่าเนอะ เริ่มจากไปสร้างโฟลเดอร์สำหรับเก็บโค้ดก่อน จากนั้นก็สร้างไฟล์ PHP เปล่า ๆ ขึ้นมา ตั้งชื่อว่า processImage.php แล้วเพิ่มโค้ด

$original_image = 'xxxxx.png';
$name_array = explode('.', $original_image);
$result_image = 'result_'.$name_array[0].'.'.$name_array[1];
$watermark_image = 'yyyyy.png';
          โค้ดในส่วนนี้ใช้สำหรับกำหนดไฟล์รูปที่จะใช้เป็นรูปต้นฉบับ โดยตัวแปรที่ต้องตั้งค่ามีอยู่ 2 ตัว ได้แก่ $original_image ซึ่งจะเก็บค่าชื่อของไฟล์รูปที่จะใช้ ให้เพื่อน ๆ เปลี่ยนไปใส่ชื่อไฟล์รูปต้นฉบับที่ต้องการได้เลย แต่มีข้อแม้ว่าไฟล์นั้นจะต้องอยู่ในโฟล์เดอร์เดียวกับไฟล์ processImage.php ด้วย ส่วนอีกตัวก็คือ $watermark_image ซึ่งเก็บชื่อของไฟล์รูปที่จะใช้เป็นลายน้ำ (รูปลายน้ำควรจะมีสีพิ้นหลังใส ๆ ไม่งั้นรูปลายน้ำจะไปบังรูปต้นฉบับ)

$result_image_width = 640;
$result_image_height = 480;
$quality = 100;
          ถัดไปเป็นการกำหนดขนาดของรูปที่จะทำการตัดออกมา โดย $result_image_width คือค่าความกว้าง และ $result_image_height คือค่าความสูง ซึ่งตรงนี้เพื่อน ๆ สามารถกำหนดได้ตามใจชอบเลย

$info = getimagesize($original_image);
$imgtype = image_type_to_mime_type($info[2]);

switch ($imgtype) {
 case 'image/jpeg':
  $source = imagecreatefromjpeg($original_image);
  break;
 case 'image/gif':
  $source = imagecreatefromgif($original_image);
  break;
 case 'image/png':
  $source = imagecreatefrompng($original_image);
  break;
 default:
  die('Invalid image type.');
}
          ใช้ฟังก์ชัน getimagesize() หาประเภทของรูป จากนั้นก็ทำการสร้างรูปขึ้นมาภายในโปรแกรม PHP โดยใช้ฟังก์ชันที่แตกต่างกันไปขึ้นอยู่กับประเภทของรูป (เช่น รูปประเภท png ก็จะใช้ฟังก์ชัน imagecreatefrompng() ในการสร้างรูป เป็นต้น) ที่ต้องสร้างรูปขึ้นมาในโปรแกรมก็เพื่อให้โปรแกรมรู้จักรูปและสามารถเข้าถึงรายละเอียดของรูปได้

$source_width = imagesx($source);
$source_height = imagesy($source);
$source_ratio = $source_width / $source_height;
          เมื่อโปรแกรมสามารถเข้าถึงข้อมูลรูปได้แล้ว ต่อไปก็จะทำการย่อ-ขยายรูป โดยเริ่มจากหาอัตราส่วนของรูปเพื่อเอาไปคำนวณหาความกว้างและความสูงของรูปที่ถูกย่อ-ขยาย

if ($result_image_width/$result_image_height > $source_ratio) {
 $new_image_width = $result_image_width;
 $new_image_height = $result_image_width/$source_ratio;
} else {
 $new_image_width = $result_image_height*$source_ratio;
 $new_image_height = $result_image_height;
}
          คำนวณความกว้างและความสูงของรูปใหม่ที่ถูกย่อ-ขยายมาจากรูปต้นฉบับ

$new_image = imagecreatetruecolor(round($new_image_width), round($new_image_height));
imagecopyresampled($new_image, $source, 0, 0, 0, 0, $new_image_width, $new_image_height, $source_width, $source_height);
          ใช้ฟังก์ชัน imagecreatetruecolor() เพื่อสร้างรูปสี่เหลี่ยมใหม่ โดยใส่พารามิเตอร์เป็นความกว้าง ($new_image_width) และความสูง ($new_image_height) ของรูปที่ถูกย่อ-ขยาย จากนั้นจึงใช้ฟังก์ชัน imagecopyresampled() เพื่อก๊อบปี้จากภาพนึงไปยังอีกภาพนึง โดยใส่พารามิเตอร์เป็น
  • resource ของรูปใหม่ที่ถูกย่อ-ขยาย ($new_image)
  • resource ของรูปต้นฉบับ ($source)
  • ตำแหน่งเริ่มต้นในแนวแกน x ของรูปใหม่ที่ถูกย่อ-ขยาย (0)
  • ตำแหน่งเริ่มต้นในแนวแกน y ของรูปใหม่ที่ถูกย่อ-ขยาย (0)
  • ตำแหน่งเริ่มต้นในแนวแกน x ของรูปต้นฉบับ (0)
  • ตำแหน่งเริ่มต้นในแนวแกน y ของรูปต้นฉบับ (0)
  • ความกว้างของรูปใหม่ที่ถูกย่อ-ขยาย ($new_image_width)
  • ความสูงของรูปใหม่ที่ถูกย่อ-ขยาย ($new_image_height)
  • ความกว้างของรูปต้นฉบับ ($source_width)
  • ความสูงของรูปต้นฉบับ($source_height)
เท่านี้เราก็จะได้ภาพใหม่ที่ผ่านการย่อ-ขยายตามอัตราส่วนของขนาดความกว้างและความสูงที่กำหนดไว้ในตอนแรกแล้ว อันดับถัดไปเราจะทำการหาตำแหน่งที่จะตัด (Crop) รูปออกมา

$new_x_mid = $new_image_width / 2;
$new_y_mid = $new_image_height / 2;
$old_x_mid = $result_image_width / 2;
$old_y_mid = $result_image_height / 2;
          โค้ดส่วนนี้จะทำการหาพิกัดแนวแกน x และแกน y เพื่อทำการตัดรูป

$final_source = imagecreatetruecolor($result_image_width, $result_image_height);
imagecopyresampled($final_source, $new_image, 0, 0, ($new_x_mid - $old_x_mid), ($new_y_mid - $old_y_mid), $result_image_width, $result_image_height, $result_image_width, $result_image_height);
          ทำการตัดรูปออกมาเท่ากับขนาดที่เรากำหนดไว้ในตอนแรกด้วยการใช้ฟังก์ชัน imagecopyresampled() โดยใส่พิกัดที่ได้คำนวณไว้แล้ว ซึ่งผลลัพธ์จะทำให้เราได้รูปที่มีขนาดตามที่ต้องการและยังคงอัตราส่วนของรูปเดิมไว้อยู่

$watermark_info = getimagesize($watermark_image);
$imgtype = image_type_to_mime_type($watermark_info[2]);
switch ($imgtype) {
  case 'image/jpeg':
   $watermark_source = imagecreatefromjpeg($watermark_image);
   break;
  case 'image/gif':
   $watermark_source = imagecreatefromgif($watermark_image);
   break;
  case 'image/png':
   $watermark_source = imagecreatefrompng($watermark_image);
   break;
  default:
   die('Invalid watermark type.');
}
          ลำดับต่อไปจะทำการใส่ลายน้ำลงไปที่รูป ให้ทำการสร้างรูปลายน้ำในโปรแกรมเหมือนที่ทำกับรูปต้นฉบับในตอนแรกก่อน

$watermark_image_width = imagesx($watermark_source);
$watermark_image_height = imagesy($watermark_source);
imagecopy($final_source, $watermark_source, $result_image_width - $watermark_image_width, $result_image_height - $watermark_image_height, 0, 0, $result_image_width , $result_image_height);
          ในส่วนของการใส่ลายน้ำก็ไม่มีอะไรมาก หาขนาดของความกว้างและความสูงของรูปลายน้ำเพื่อเอาไปใช้คำนวณตำแหน่งของรูปลายน้ำบนรูปหลัก แล้วใช้ฟังก์ชัน imagecopy() เพื่อก๊อปปี้รูปลายน้ำลงไปบนรูปหลัก (อย่าลืมว่ารูปลายน้ำต้องมีพื้นหลังใสนะ ไม่งั้นจะบังรูปหลักเอาได้) โดยใส่พารามิเตอร์เป็น
  • resource ของรูปหลัก ($final_source)
  • resource ของรูปลายน้ำ ($watermark_source)
  • ตำแหน่งในแนวแกน x ของรูปหลักที่จะนำรูปลายน้ำมาใส่ ($result_image_width - $watermark_image_width)
  • ตำแหน่งในแนวแกน y ของรูปหลักที่จะนำรูปลายน้ำมาใส่ ($result_image_height - $watermark_image_height)
  • ตำแหน่งเริ่มต้นในแนวแกน x ของรูปลายน้ำ (0)
  • ตำแหน่งเริ่มต้นในแนวแกน y ของรูปลายน้ำ (0)
  • ความกว้างของรูปลายน้ำ ($result_image_width)
  • ความสูงของรูปลายน้ำ ($result_image_height)
เหตุที่กำหนดความกว้างและความสูงของรูปลายน้ำให้เท่ากับรูปหลักก็เพื่อให้รูปลายน้ำทับลงไปบนรูปหลักพอดี แต่แค่นี้เรายังไม่ได้รูปออกมาจริง ๆ เพราะเมื่อทำถึงขั้นตอนนี้แล้ว เราจะได้แค่รูปในโปรแกรมเท่านั้น ต้องทำการสร้างรูปออกมาเป็นไฟล์ด้วย

imagejpeg($final_source, $result_image, $quality);
imagedestroy($final_source);
          ใช้ฟังก์ชัน imagejpeg() เพื่อสร้างไฟล์รูป โดยใส่ใส่พารามิเตอร์เป็น
  • resource ของรูปที่จะทำการสร้าง ($final_source)
  • ตำแหน่งที่เก็บไฟล์รูปที่จะทำการบันทึก ($result_image)
  • คุณภาพของรูปที่จะทำการสร้าง ($quality)
จากนั้นก็ใช้ฟังก์ชัน imagedestroy() ลบรูปออกจากโปรแกรมเพื่อคืนหน่วยความจำของเครื่อง


          ผลลัพธ์ที่ได้ก็จะประมาณนี้ เพียงเท่านี้เราก็จะได้โปรแกรมย่อ-ขยาย ตัด และใส่ลายน้ำให้กับรูปแล้ว เพื่อน ๆ สามารถตัดการทำงานบางส่วนแล้วนำไปประยุกต์ใช้งานต่อได้ ซึ่งผมขอแนะนำให้เพื่อน ๆ แก้ไขค่าบางค่าแล้วลองดูผลการทำงานเพื่อทำความเข้าใจโค้ดให้มากยิ่งขึ้น