অনেক সময় পিএইচপি-তে ছবি রিসাইজ করে সেভ করার দরকার হয়। এক্ষেত্রে আমরা একটা সাধারণ ক্লাস লিখে রাখতে পারি যেখানে খুঁটিনাটি কাজগুলিকে ব্যবহারকারীর কাছ থেকে দূরে রেখে দু’একটি মেথড কল করেই আমরা পুরো কাজটা করতে পারি। এই ক্লাসটিকে আমরা পরবর্তীতে সম্প্রসারণও করতে পারব।

ভূমিকা:

ক্লাসটির উদ্দেশ্য সম্পর্কে এক নজর দেখা যাক:

  • সহজে ব্যবহার করা যাবে
  • বিভিন্ন ফরম্যাটের ছবি একই ভাবে ওপেন, রিসাইজ বা সেভ করতে পারবে
  • রিসাইজ করার ফলে ছবির মান খারাপ হবে না

১। প্রস্তুতি:

সার্ভারের ওয়ার্কিং ডিরেক্টরিতে দুটি ফাইল তৈরি করতে হবে — index.php এবং resize-class.php

২। অবজেক্ট কল করা:

প্রথমে আমরা ঠিক করে নেব কোডে আমরা এই ক্লাসের কোন অবজেক্টকে কীভাবে কল করব। index.php ফাইলে এই কোডগুলি লিখতে হবে:

// *** Include the class

include("resize-class.php");

// *** 1) Initialize / load image

$resizeObj = new resize('sample.jpg');

// *** 2) Resize image (options: exact, portrait, landscape, auto, crop)

$resizeObj -> resizeImage(150, 100, 'crop');

// *** 3) Save image

$resizeObj -> saveImage('sample-resized.gif', 100);

আমরা কী করব তা দেখাই যাচ্ছে, ছবির ফাইল ওপেন করব, কোন সাইজে এবং কী ধরনের রিসাইজ করব সেটা বলে দেব, এবং রিসাইজড ছবিটাকে পছন্দ অনুযায়ী ফরম্যাট এবং কোয়ালিটিতে সেভ করব।

৩। ক্লাসের গঠন:

এই ক্লাসটা লিখতে আমরা অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং (OOP)-এর ধারণা ব্যবহার করব। ক্লাসটাকে পুনর্ব্যবহারযোগ্য করে তৈরি করা হবে যেন কোড না পরিবর্তন করে বিভিন্ন ধরনের ছবির জন্য ক্লাসটাকে কেবল পূর্বোক্ত মেথডগুলি কল করেই ব্যবহার করা যায়।

রিসাইজ ক্লাসটা লেখা যাক। resize-class.php ফাইলে নিচের মত করে ক্লাসটাকে ডিক্লেয়ার করা যাক। পরবর্তীতে এ ক্লাসটাকে সম্প্রসারিত করা হবে।


Class resize

{

// *** Class variables

public function __construct()

{

}

}

কনস্ট্রাক্টর মেথডটি কোন নতুন অবজেক্ট তৈরি করার সময় ব্যবহার করা হবে।

৪। কনস্ট্রাক্টর:

কনস্ট্রাক্টরে আমরা আমাদের যে ইমেজ ফাইলকে রিসাইজ করা হবে তার পাথ দিয়ে দিব। অতঃপর প্রথমে সেই ফাইল ওপেন করতে হবে যেটার জন্য একটা কাস্টম মেথড openImage ব্যবহার করা হবে (এর জন্য পিএইচপির GD Library ব্যবহার করা হবে)। ওপেন করা ছবিটাকে একটা মেম্বার ভ্যারিয়েবলে সংরক্ষণ করা হবে, এবং এই ভ্যারিয়েবলটি Private হিসেব ডিক্লেয়ার করা হবে যেন কেবল এই ক্লাস থেকেই তাকে অ্যাকসেস করা যায়। একই সঙ্গে ছবির প্রস্থ এবং উচ্চতার সংরক্ষণ করা হবে।


Class resize

{

// *** Class variables

private $image;

private $width;

private $height;

function __construct($fileName)

{

// *** Open up the file

$this->image = $this->openImage($fileName);

// *** Get width and height

$this->width  = imagesx($this->image);

$this->height = imagesy($this->image);

}

}

imagesx এবং imagesy ফাংশনগুলি GD Library এর বিল্টইন যারা ছবির প্রস্থ এবং উচ্চতা রিটার্ন করে।

৫। ছবি ওপেন করা:

পূর্বে যে openImage মেথডটি ব্যবহার করা হয়েছে সেটি এখন ডিফাইন করা হবে। আমরা ফাইল এক্সটেনশনের উপর ভিত্তি করে ঠিক করব ফাইলের ধরন কী এবং সে অনুযায়ী GD Library থেকে ফাংশন কল করে ছবিটি ওপেন করব। মেথডটি ওপেন করা ফাইলরিসোর্সটি রিটার্ন করবে।


private function openImage($file)

{

// *** Get extension

$extension = strtolower(strrchr($file, '.'));

switch($extension)

{

case '.jpg':

case '.jpeg':

$img = @imagecreatefromjpeg($file);

break;

case '.gif':

$img = @imagecreatefromgif($file);

break;

case '.png':

$img = @imagecreatefrompng($file);

break;

default:

$img = false;

break;

}

return $img;

}

৬। রিসাইজ কীভাবে করা হবে:

এইধাপে আমরা ব্যাখ্যা করব রিসাইজ করার প্রক্রিয়াটা কী হবে। আমরা একটা পাবলিক মেথড ডিফাইন করব যাতে যে প্রস্থ আর উচ্চতায় রিসাইজ করতে চাই এবং যে পদ্ধতি ব্যবহার করতে চাই তা পাস করা হবে। কথা হল, আমরা ঠিক যে প্রস্থ আর উচ্চতা দিব ঠিক তাতেই সবসময় রিসাইজ করলে বিভিন্ন অ্যাসপেক্ট রেশিওর ছবিগুলিতে কমবেশি মাত্রায় ডিস্টরশন দেখা দেবে, যেটা সাধারণত আমরা চাইব না। এই ডিস্টরশন এড়াতে হলে আমরা :

  • অ্যাসপেক্ট রেশিও ঠিক রেখে প্রদত্ত প্রস্থ ও উচ্চতার যত কাছাকাছি সম্ভব রিসাইজ করতে পারি।
  • প্রদত্ত প্রস্থ ও উচ্চতার যত কাছাকাছি সম্ভব রিসাইজ করে বাড়তি অংশ কেটে বা ক্রপ করে বাদ দিয়ে দিতে পারি।

আমরা সবগুলি অপশনই রাখার চেষ্টা করব। সুতরাং সবগুলি অপশন হল:

  • ঠিক প্রদত্ত প্রস্থ ও উচ্চতাতেই রিসাইজ
  • প্রদত্ত প্রস্থ ঠিক রেখে অ্যাসপেক্ট রেশিও অনুযায়ী উচ্চতা
  • প্রদত্ত উচ্চতা ঠিক রেখে অ্যাসপেক্ট রেশিও অনুযায়ী প্রস্থ
  • ২ ও ৩-এর ভিতর যেটা ভাল হয় নিজেই ঠিক করবে, অনেক গুলি ছবি একসাথে রিসাইজ করতে গেলে এটা দরকার হবে।
  • রিসাইজ করে বাড়তি অংশ কেটে বাদ দিবে।

৭। রিসাইজ:

রিসাইজ করার কাজটা দুই ধাপে করা হবে। প্রথমে রিসাইজের জন্য উপযুক্ত প্রস্থ এবং উচ্চতা ঠিক করা। তারপর রিসাইজ করে ফেলা, যার জন্য GD Library-র ফাংশন ব্যবহার করা হবে (imagecreatetruecolor এবং imagecopyresampled)। এই ফাংশনগুলির রিটার্নড ভ্যালুগুলিও আমরা সংরক্ষণ করব।


// *** Add to class variables

private $imageResized;

এবং

public function resizeImage($newWidth, $newHeight, $option="auto")

{

// *** Get optimal width and height - based on $option

$optionArray = $this->getDimensions($newWidth, $newHeight, strtolower($option));

$optimalWidth  = $optionArray['optimalWidth'];

$optimalHeight = $optionArray['optimalHeight'];

// *** Resample - create image canvas of x, y size

$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);

imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);

// *** if option is 'crop', then crop too

if ($option == 'crop') {

$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);

}

}

৮। ডিসিশন ট্রি:

এই ধাপে বিভিন্ন অপশনের উপর ভিত্তি করে অপটিমাল রিসাইজের জন্য আমরা ঠিক কী করব সেটা ঠিক করব।


private function getDimensions($newWidth, $newHeight, $option)

{

switch ($option)

{

case 'exact':

$optimalWidth = $newWidth;

$optimalHeight= $newHeight;

break;

case 'portrait':

$optimalWidth = $this->getSizeByFixedHeight($newHeight);

$optimalHeight= $newHeight;

break;

case 'landscape':

$optimalWidth = $newWidth;

$optimalHeight= $this->getSizeByFixedWidth($newWidth);

break;

case 'auto':

$optionArray = $this->getSizeByAuto($newWidth, $newHeight);

$optimalWidth = $optionArray['optimalWidth'];

$optimalHeight = $optionArray['optimalHeight'];

break;

case 'crop':

$optionArray = $this->getOptimalCrop($newWidth, $newHeight);

$optimalWidth = $optionArray['optimalWidth'];

$optimalHeight = $optionArray['optimalHeight'];

break;

}

return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);

}

৯। প্রস্থ ও উচ্চতার সঠিক পরিমাণ নির্ধারণ:

নিচের চারটি মেথডের কাজ আমরা আগেই আলোচনা করেছি; খুব সাধারণ কিছু হিসাব করেই নির্দিষ্ট পরিমাপের ভিতর বেস্ট ফিটিং করা যায় এদের মাধ্যমে:


private function getSizeByFixedHeight($newHeight)

{

$ratio = $this->width / $this->height;

$newWidth = $newHeight * $ratio;

return $newWidth;

}

private function getSizeByFixedWidth($newWidth)

{

$ratio = $this->height / $this->width;

$newHeight = $newWidth * $ratio;

return $newHeight;

}

private function getSizeByAuto($newWidth, $newHeight)

{

if ($this->height width)

// *** Image to be resized is wider (landscape)

{

$optimalWidth = $newWidth;

$optimalHeight= $this->getSizeByFixedWidth($newWidth);

}

elseif ($this->height > $this->width)

// *** Image to be resized is taller (portrait)

{

$optimalWidth = $this->getSizeByFixedHeight($newHeight);

$optimalHeight= $newHeight;

}

else

// *** Image to be resizerd is a square

{

if ($newHeight getSizeByFixedWidth($newWidth);

} else if ($newHeight > $newWidth) {

$optimalWidth = $this->getSizeByFixedHeight($newHeight);

$optimalHeight= $newHeight;

} else {

// *** Sqaure being resized to a square

$optimalWidth = $newWidth;

$optimalHeight= $newHeight;

}

}

return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);

}

private function getOptimalCrop($newWidth, $newHeight)

{

$heightRatio = $this->height / $newHeight;

$widthRatio  = $this->width /  $newWidth;

if ($heightRatio height / $optimalRatio;

$optimalWidth  = $this->width  / $optimalRatio;

return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);

}

১০। ক্রপ (বাড়তি অংশ কেটে বাদ দেয়া):

রিসাইজ করার পর যদি ক্রপ করা দরকার হয়, তবে তার জন্য আরও একটু কাজ করতে হবে। আমরা এখানে মূল ছবির কেন্দ্রবিন্দুকে কেন্দ্র করে ক্রপ করব। রিসাইজ প্যারামিটার ছাড়াও আরও দুটো প্যারামিটার এই মেথডের জন্য প্রয়োজন হবে।


private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)

{

// *** Find center - this will be used for the crop

$cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );

$cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );

$crop = $this->imageResized;

//imagedestroy($this->imageResized);

// *** Now crop from center to exact requested size

$this->imageResized = imagecreatetruecolor($newWidth , $newHeight);

imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);

}

১১। রিসাইজড ছবিকে সেভ করা:

সবশেষে এই ছবিটাকে সেভ করতে হবে। কোন পাথে কী নামে সেভ করতে হবে এবং সেই ছবির কোয়ালিটি কী হবে তার ওপর ভিত্তি করে যথাযথ মেথড কল করতে হবে। ছবির কোয়ালিটি বিষয়ে মনে রাখা প্রয়োজন যে, JPG ছবির কোয়ালিটি ০-১০০ স্কেলে নির্ধারিত হয়, PNG-র ক্ষেত্রে এই স্কেল ০-৯, আবার GIF ছবির এধরনের কোন অপশন নেই। saveImage মেথড কল করার সময় আমরা অবশ্য বিভিন্ন ফরম্যাটের জন্য বিভিন্ন সেটিং মনে রাখার ঝামেলায় যাব না, বরং সব ছবির কোয়ালিটিকেই শতকরা মান হিসেবে বিবেচনা করব।


public function saveImage($savePath, $imageQuality="100")

{

// *** Get extension

$extension = strrchr($savePath, '.');

$extension = strtolower($extension);

switch($extension)

{

case '.jpg':

case '.jpeg':

if (imagetypes() & IMG_JPG) {

imagejpeg($this->imageResized, $savePath, $imageQuality);

}

break;

case '.gif':

if (imagetypes() & IMG_GIF) {

imagegif($this->imageResized, $savePath);

}

break;

case '.png':

// *** Scale quality from 0-100 to 0-9

$scaleQuality = round(($imageQuality/100) * 9);

// *** Invert quality setting as 0 is best, not 9

$invertScaleQuality = 9 - $scaleQuality;

if (imagetypes() & IMG_PNG) {

imagepng($this->imageResized, $savePath, $invertScaleQuality);

}

break;

// ... etc

default:

// *** No extension - No save.

break;

}

imagedestroy($this->imageResized);

}

এখন আমরা ছবির রিসোর্স হিসেবে যে মেমরি ব্যবহার করছিলাম তা খালি করে দিতে পারি। অাবার রিসাইজ করে সেভ করা ইমেজটিকেও রিটার্ন করা যেতে পারে।

নোটঃ পোস্টটি টেকনোলজি টুডে ব্লগে প্রথম প্রকাশিত। পোস্টের সরাসরি লিঙ্ক এখানে

comments

2 কমেন্টস

  1. খুবই ভাল লাগলো । এরকম টিউটরিয়াল আরও চাই ।

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Time limit is exhausted. Please reload the CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.