подскажите, как вырезать середину изображения.

  • Автор темы Духовность™
  • Дата начала

Adelf

Administrator
Команда форума
в твоем коде $xpos может быть отрицательными, и тогда все должно сломаеться. тут нужен if на коэфициент. Нужно посчитать ratio 120/80 и изображения. Сравнить их. если одно больше другого - то вычислять xpos, если меньше, то ypos и чуть другой вызов imageCopyResampled.
Блин :) простая геометрия, а народ осилить не может. был на работе один коллега - тоже полдня бился над этой же проблемой :)) Чтото у вас есть общее. Разговариваете о высоком, а на простых вещах зубы ломаете.
 

HraKK

Мудак
Команда форума
гг, в предвкушении следующего топика на 99 постов "Как реализовать сортировку пузырком???"
 

Активист

Активист
Команда форума
Adelf
Код в котором отношение сторон двух прямоугольников может быть отрицательно - не код, двоешники, отношение сторон прямоугольников не может быть отрицательно, оно может быть больше 1 или меньше 1 в зависимости от того, какую сторону мы берем за делимое и какую за делитель.

PHP:
<?php

		// Информация об исходном изобржение
		$src = "/var/tmp/img.png";
		$imageInfo = getimagesize($src); 
		if (!$imageInfo) {
			return false;
		}
		 
		$srcWidth  = $imageInfo[0];
		$srcHeight = $imageInfo[1];
		
 		switch ($imageInfo[2]) {
 			case 1:
 				$src = imagecreatefromgif($src);
 				$ext = ".gif";
 			break;

 			case 2:
 				$src = imagecreatefromjpeg($src);
 				$ext = ".jpg";
 			break;

 			case 3:
 				$src = imagecreatefrompng($src);
 				$ext = ".png";
 			break;

 			default:
 				return false;
 			break;
 		}
 		
 		$maxHeight = 80;
 		$maxWidth = 120;
 		
 		$kWidth = $srcWidth/$maxWidth;
		$kHeight = $srcHeight/$maxHeight;
		// посольку коэфициент расчитан путем деления большей стороны на меньшую, то выбираем меньший (k>1),
		// если бы делили меньшую на большую, то выбирали бы макс. значение (k < 1)
		$k = min($kHeight, $kWidth);  

		// тут мы получим ширину и высоту большего изображения, чем нужно *что бы было что отрезать от других сторон 
 		$tmpWidth = round($srcWidth/$k);
 		$tmpHeight = round($srcHeight/$k);

 		// вычисляем размеры конечного изображения
 		$dstWidth = $tmpWidth - ($tmpWidth-$maxWidth);
 		$dstHeight = $tmpHeight - ($tmpHeight-$maxHeight);
 		
 		// вичисляем сдвиг (т.е., это ровно половина разницы между соот. сторонами временной миниатюрой и нужной нам миниатурой) 
		$wOffset = $tmpWidth > $maxWidth  ? ($tmpWidth-$maxWidth)/2 : 0;
		$hOffset = $tmpHeight > $maxHeight  ? ($tmpHeight-$maxHeight)/2 : 0;

		// Напримепр, нам нужно получить изображение которое должно быть строго 120x80
		// наше исходное изобржение 1000x1000,
		// таким образом в $tmp будет лежать фото размером 120x120
		$tmp = imagecreatetruecolor($tmpWidth, $tmpHeight); 
		// а тут будет фото 120x80
		$dst = imagecreatetruecolor($dstWidth, $dstHeight); // сюда ложим 
		// ресайзим (120x120) 			
		imagecopyresampled($tmp, $src, 0, 0, 0, 0, $tmpWidth, $tmpHeight, $srcWidth, $srcHeight); // ресайзим

		// копируем область с параметрами
		// с параметрами do, to, dx, dy, tx, ty, dw, dh, где
		// do = $dst (GD Object)
		// to = $tmp (GD Object)
		// dx,dy - координаты левого верхнего угла области на конечном полотне, куда будет всталена скопированная область, в нашем случае всегда 0,0
		// tx, ty - кординаты левого верхнего угла обалсти на временном полотне, откуда будет скорпированна область, в нашем случае это 0,20
		// dw, dh - ширина и высота прямоугольника, в нашем случае 120, 80
		imagecopy ($dst, $tmp, 0, 0, $wOffset, $hOffset, $dstWidth, $dstHeight); 

		switch ($ext) {
 			case ".jpg": $result = imagejpeg($dst,"/var/tmp/humb_120x80".$ext, 100);break;
 			case ".png": $result = imagepng($dst,"/var/tmp/humb_120x80".$ext, 5);break;
 			case ".gif": $result = imagegif($dst, "/var/tmp/humb_120x80".$ext, 100);break;
 		}
 		
		var_dump($result);
 

Adelf

Administrator
Команда форума
Код в котором отношение сторон двух прямоугольников может быть отрицательно - не код, двоешники
Читай внимательно :) отношение всегда больше 0. А некое xPos, которое высчитывает товарищ Духовность, может быть отрицательным и это ломает вызов imageCopyResampled, который не может взять отрицательную координату.

В твоем коде довольно интересно релизована логика.. if который у меня был описан у тебя реализовывается с помощью этой строки:
$k = min($kHeight, $kWidth);
Т.е. вместо if и небольшого(двух-трех строчного) копипаста ты предпочел реализовать этот алгоритм более обще. Ничего плохого сказать не могу, но этот код будет сложнее обьяснять.

P.S. да и вызова функций копирования картинки - два. Можно одним обойтись.
 

Активист

Активист
Команда форума
Adelf
Не знаю, чего сложного в функциях min, max. Предполагаю, что if вы делате тоже самое, но при этом при этом завязываетесь к всяким вертикальным/горизонтальным/квадратам/макс/минимальным, допустить ошибку там можно быстрее.
В моем случае
* кропим - выбраем min(kx, ky);
* просто уменьшаем до мак. допустимых размеров max(kx,ky);

Да, код рабочий, проверил с разными вариациями, единственное, нужны небольшие доработки при условии, что миниатюра будет больше исходного изобржаения.
 

Adelf

Administrator
Команда форума
Не знаю, чего сложного в функциях min, max.
Давай общаться без этих ораторских приемчиков. Ты наверняка понял что я имею ввиду. твое решение - классическое, математически правильное, общее. Мое - с рассмотрением двух частных случаев, которые покрывают почти все множество вариантов(кроме исключений типа отрицательной необходимой ширины). Зато с одним копированием картинки.. и как мне кажется более простое для обьяснения. Я бы его назвал "программистским".

PHP:
	function lib_imageTumbnail($srcFileName, $destFileName, 
		$thumbWidth/*120 for example*/, $thumbHeight/*80 for example*/, $quality = 70)
	 {
	 	// получаем картинковый обьект
		$srcImage = getImageObject($srcFileName);
		
		if(!$srcImage) return false;
	
		$srcWidth = imagesx($srcImage);
		$srcHeight = imagesy($srcImage);
		
		$srcRatio = $srcWidth / $srcHeight; // коеф фотки
		
		$destRatio = $thumbWidth / $thumbHeight; // коеф стандартный
		
		if($srcRatio > $destRatio)
		{ 
		  // фотка больше, чем надо, в ширину. Вычисляем отступ по горизонтали.
		  $cropWidth = round($srcHeight * $destRatio);
		  $cropHeight = $srcHeight;
		  $cropLeft   = round( ($srcWidth - $cropWidth) / 2);
		  $cropTop    = 0;		
		}
		else
		{
		  // фотка больше, чем надо, в высоту. Вычисляем отступ по вертикали.
		  $croppedHeight = round($srcWidth / $destRatio);
		  $croppedWidth = $srcWidth;
		  $cropTop = round( ($srcHeight - $croppedHeight) / 2);
		  $cropLeft = 0;
		}
		
		$destImage = imagecreatetruecolor($thumbWidth, $thumbHeight);
			  
		imagecopyresampled($destImage, $srcImage, 0, 0, $cropLeft, $cropTop,  
			$thumbWidth, $thumbHeight,  $croppedWidth, $croppedHeight);
		
		imagejpeg($destImage, $destFileName, $quality);
	 	
		imagedestroy($srcImage);
		imagedestroy($destImage);
		
		return true;
	 }
 

Активист

Активист
Команда форума
Ммм, вы свой код проверяли? Советую проверить.

$srcRatio = $srcWidth / $srcHeight; // это же отношение X:Y вашей изначальной фигуры
$destRatio = $thumbWidth / $thumbHeight; // это же отношение X:Y вашей миниатюры
if($srcRatio > $destRatio)

Вопрос - Как отношение собственных сторон одной фигуры может влиять на коэффициент масштаба изображения?

PHP:
$croppedHeight = round($srcWidth / $destRatio); // это что? 
$croppedWidth = $srcWidth;
 

Духовность™

Guest
Активист
Большое спасибо! Обязательно разберусь, как этот код работает!
 

Adelf

Administrator
Команда форума
Код рабочий. Там есть пара опечаток. Надо везде $cropWidth => $croppedWidth и $cropHeight => $croppedHeight.

Вопрос - Как отношение собственных сторон одной фигуры может влиять на коэффициент масштаба изображения?
Чего ты все переиначиваешь? :) Есть - отношение сторон исходной фигуры и отношение сторон требуемое.
Если картинка "ширее", чем нужно(значит коэфициент у нее будет больше, потому что это коэфициенты ширина/высота), значит нужно отсечь справа и слева. И поэтому вычисляем отступы по горизонтали.
Если картинка "высотее", чем нужно, значит режем сверху и снизу...
Вот картинка для наглядности. серый блок там с соотношением 120 на 80
 

Активист

Активист
Команда форума
Ваш код примера на 120 x 80 - счастливое стечение обстятельств
PHP:
lib_imageTumbnail("img.jpg", "humb.jpg", 120, 80); // работет
lib_imageTumbnail("img.jpg", "humb.jpg", 120, 100); // черный прямоугольник
lib_imageTumbnail("img.jpg", "humb.jpg", 120, 120); // черный квадрат
lib_imageTumbnail("img.jpg", "humb.jpg", 110, 120); // черный прямоугольник
lib_imageTumbnail("img.jpg", "humb.jpg", 80, 120); // черный прямоугольник
Источник - 1024x768, Хризантема.jpg из набора винды.

if($srcRatio > $destRatio)
Это расшифровывается как "Если пропорциональное отношение сторон источка больше ропорционального отношение сторон миниатюры, то", и что?

Или это опять опечатки в коде?

Покажите рабочий код вашей идеи, очень интересно, действительно, что вы имели ввиду и как это работает.
 

Adelf

Administrator
Команда форума
http://pastebin.com/22HRn8kV

>>и что?
Если больше, то источник шире, чем нужно. Ведь главная проблема у нас - как раз несовпадение этих пропорций. Если нужен тумбнэйл 120 на 80, а картинки все будут 1200 на 800 - то никакой проблемы ведь вообще нет. Проблема в том, что картинки могут быть шире чем нужно(1600 на 800), или выше(800 на 800). Вот эти два варианта и рассматриваются в ветках if.

Активист
Мне както неудобно :) Ты ко мне на "вы", а я на "ты". тут вроде не как хабр. Тыкаем без проблем.
 

Активист

Активист
Команда форума
Сейчас работает, но код все равно какой-то нелогичный.

В общем, решил ради обучения сделать ресайз и ее обрезку только функцией imagecopyresampled.
PHP:
<?php
$src = "avatar.jpg";

$imageInfo = getimagesize($src); 
$srcWidth  = $imageInfo[0];
$srcHeight = $imageInfo[1];

$src = imagecreatefromjpeg($src);
         
$maxWidth = 100;
$maxHeight = 100;

$k = min($srcHeight/$maxHeight, $srcWidth/$maxWidth);  
$k = $k > 1 ? $k : 1; 
		
$xDiff = $srcWidth/$k - $maxWidth > 0 ? $srcWidth/$k - $maxWidth : 0;  
$yDiff = $srcHeight/$k - $maxHeight > 0 ? $srcHeight/$k - $maxHeight: 0;
		
$dst = imagecreatetruecolor($srcWidth/$k-$xDiff, $srcHeight/$k-$yDiff);
imagecopyresampled($dst, $src, 0, 0, $xDiff/2*$k, $yDiff/2*$k, $srcWidth/$k-$xDiff, $srcHeight/$k-$yDiff, $srcWidth-$xDiff*$k, $srcHeight-$yDiff*$k);

header("Content-type: image/jpeg");
imagejpeg($dst, null, 100);
?>
В ближайшее время вернусь к твоему коду

Adelf
Я понял что ты делаешь,

У вас коэффициент ratio - это соотношение сторон, у меня коэффициент - это коэффициент сжатия.

В контексте задачи откропить - будет работать, хотя математически - этот ход неверный, потому как ты ожидаешь, что фото твое на выходе будет именного того размера, который ожидается, и не вычисляешь размеры конечного изображения.
Твой и мой код отличает одна большая разница - я использую k коэффициент сжатия (масштаба), ты же к нему не привязываешься вообще, если я поменяю функцию вычисления коэффициента сжатия k на

PHP:
$k = max($srcHeight/$maxHeight, $srcWidth/$maxWidth);
То фото будет уменьшено до вписывания ее в заданный максимальную область без кропа, у тебя это вызовет значительные изменения в расчетах.

Ты вычисляешь, горизонтальная у тебя фото или нет, с какой стороны отрезать, впишется ли оно у тебя в заданную область или нет, хотя по идее - не должно волновать, какой по соотношению сторон источник.

Во! Нашел багу, если источник будет меньше миниатюры, то твой код отвалится, он будет увеличивать, с моим все будет ок :)))))
 

Adelf

Administrator
Команда форума
ну мог бы и проверить :) данный код и увеличивает без проблем.

Ты вычисляешь, горизонтальная у тебя фото или нет, с какой стороны отрезать, впишется ли оно у тебя в заданную область или нет, хотя по идее - не должно волновать, какой по соотношению сторон источник.
Верно. Я же говорил - у тебя более правильный и математически выверенный вариант. Но расчеты сложноваты. Ну да ладно :) Пора нам завязывать дискуссию.
 

Активист

Активист
Команда форума
Adelf
Ну вот смотри, сминались - задача, вписать изображение в прямоугольник с максимальными сторонами которого 120x80. не обрезая фото.
 

Adelf

Administrator
Команда форума
Понял о чем ты. Да. у меня изменений будет побольше. Но тоже в 2-4 строчках примерно.

Во! Нашел багу, если источник будет меньше миниатюры, то твой код отвалится, он будет увеличивать, с моим все будет ок
Эт не бага. Это фича :) Цель была - строить изображение заданного мастаба из исходного. Если оно меньше - то увеличиваем... Нелогично? мэйби :)
 

Активист

Активист
Команда форума
Ладно, дальнейший спор не имеет смысла :)

Мой финал этого треда и сливаюсь.
PHP:
<?php
$src = "avatar.jpg";
$crop = true;
$zoom = false;

$src = imagecreatefromjpeg($src);
$srcWidth = imagesx($src);
$srcHeight = imagesy($src);
         
$maxWidth = 120;
$maxHeight = 80;

$k = $crop ? min($srcHeight/$maxHeight, $srcWidth/$maxWidth) :  max($srcHeight/$maxHeight, $srcWidth/$maxWidth); 
$k = !$zoom && $k < 1 ? 1 : $k; 
		
$xDiff = $srcWidth/$k - $maxWidth > 0 ? $srcWidth/$k - $maxWidth : 0;  
$yDiff = $srcHeight/$k - $maxHeight > 0 ? $srcHeight/$k - $maxHeight: 0;
$dst = imagecreatetruecolor($srcWidth/$k-$xDiff, $srcHeight/$k-$yDiff);
imagecopyresampled($dst, $src, 0, 0, $xDiff/2*$k, $yDiff/2*$k, $srcWidth/$k-$xDiff, $srcHeight/$k-$yDiff, $srcWidth-$xDiff*$k, $srcHeight-$yDiff*$k);

header("Content-type: image/jpeg"); imagejpeg($dst, null, 100);
?>
 
Сверху