Intereting Posts
Как проверить, подключен ли кто-либо через IPv6 / IPv4 Как получить корневой каталог на PHP Нужно ли мне избегать данных для защиты от SQL-инъекций при использовании bind_param () в MySQLi? Разработка базы данных MySql для программы викторины Альтернатива для использования CURLOPT_SSL_VERIFYHOST, 2 ReflectionClass :: getProperty для частного свойства в унаследованном классе Продолжайте получать «Ежедневный лимит неисполненного использования превышен. Продолжение использования требует регистрации "при попытке Google плюс вход в мое веб-приложение Как изменить размер загруженных изображений? как делить строку, хранящуюся в базе данных myadmin, и искать слово из этой строки в любой последовательности? Как включить https (localhost) url на сервере WAMP (v2.5)? SQL и PHP – что быстрее mysql_num_rows () или «select count ()»? Вызов функции-члена функции () для не-объекта при вставке данных с использованием PHP mySQL simpleHTMLDOM Производительность: запрос JSON и визуализация в JS или запрос всего HTML? как восстановить резервную копию файла mysql в php Laravel: действительно ли фасады создают новые объекты при вызове методов?

Размер и положение одного изображения в другом через PHP

У меня два изображения (маленькие и большие). Большая содержит небольшой. Например, если маленькая фотография, а большая – это страница из фотоальбома.

Как получить координаты этого маленького изображения в большом, используя PHP? А также мне нужно знать размер этого изображения в большом … так что просто (x, y) координата любого угла и размеров сторон этого представления маленького изображения …

(x, y, ширина, высота)

Я уже задал этот вопрос так и получил блестящий ответ ( здесь ), но я забыл упомянуть там, что размер небольшого изображения может отличаться от размера этого изображения в большом изображении …

А также, если можно иметь дело с представлением о том, что небольшое изображение в большом изображении может иметь что-то, покрывающее один из его углов … Как в этом примере:

Маленькое изображение: маленькое изображение

Большое изображение: большое изображение

Маленькое изображение всегда имеет прямоугольную форму.

Хорошо, этот ответ не совсем отвечает на вопрос, но он должен дать вам хорошее начало! Я знаю, что повторяю себя в коде, но моя цель состояла в том, чтобы просто заставить что-то работать, чтобы вы могли на нем работать, это не производственный код!

Предпосылками

Начиная с большой картины:

большой

Нам нужно найти как можно лучше положение этой другой картины:

введите описание изображения здесь

Я решил разбить этот процесс на многие подшаги, которые вы могли бы улучшить или удалить в зависимости от того, что вы хотите сделать.

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

Начнем с:

function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } $time_start = microtime_float(); $largeFilename = "large.jpg"; $small = imagecreatefromjpeg("small.jpg"); $large = imagecreatefromjpeg($largeFilename); 

а также

 imagedestroy($small); imagedestroy($large); $time_end = microtime_float(); echo "in " . ($time_end - $time_start) . " seconds\n"; 

Чтобы иметь хорошую идею о наших выступлениях. К счастью, большая часть алгоритма была довольно быстрой, поэтому мне не пришлось оптимизировать больше.

Обнаружение фона

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

 function FindBackgroundColor($image) { // assume that the color that's present the most is the background color $colorRefcount = array(); $width = imagesx($image); $height = imagesy($image); for($x = 0; $x < $width; ++$x) { for($y = 0; $y < $height; ++$y) { $color = imagecolorat($image, $x, $y); if(isset($colorRefcount[$color])) $colorRefcount[$color] = $colorRefcount[$color] + 1; else $colorRefcount[$color] = 1; } } arsort($colorRefcount); reset($colorRefcount); return key($colorRefcount); } $background = FindBackgroundColor($large); // Should be white 

Partitionning

Мой первый шаг состоял в том, чтобы попытаться найти все области, где были нефонические пиксели. С небольшим заполнением я смог группировать регионы в более крупные регионы (так что абзац был бы единым регионом, а не несколькими отдельными буквами). Я начал с заполнения 5 и получил хорошие результаты, поэтому я застрял с ним.

Это разбито на несколько вызовов функций, поэтому мы идем:

 function FindRegions($image, $backgroundColor, $padding) { // Find all regions within image where colors are != backgroundColor, including a padding so that adjacent regions are merged together $width = imagesx($image); $height = imagesy($image); $regions = array(); for($x = 0; $x < $width; ++$x) { for($y = 0; $y < $height; ++$y) { $color = imagecolorat($image, $x, $y); if($color == $backgroundColor) { continue; } if(IsInsideRegions($regions, $x, $y)) { continue; } $region = ExpandRegionFrom($image, $x, $y, $backgroundColor, $padding); array_push($regions, $region); } } return $regions; } $regions = FindRegions($large, $background, 5); 

Здесь мы повторяем на каждом пикселе изображения, если его цвет фона, мы отбрасываем его, иначе мы проверяем, присутствует ли его позиция в найденной нами области, если это так, мы также пропустим ее. Теперь, если мы не пропустили пиксель, это означает, что это цветной пиксель, который должен быть частью области, поэтому мы запускаем ExpandRegionFrom этого пикселя.

Код для проверки, находится ли мы внутри региона, довольно прост:

 function IsInsideRegions($regions, $x, $y) { foreach($regions as $region) { if(($region["left"] <= $x && $region["right"] >= $x) && ($region["bottom"] <= $y && $region["top"] >= $y)) { return true; } } return false; } 

Теперь расширяющийся код попытается развить регион в каждом направлении и будет делать это, пока он найдет новые пиксели для добавления в регион:

 function ExpandRegionFrom($image, $x, $y, $backgroundColor, $padding) { $width = imagesx($image); $height = imagesy($image); $left = $x; $bottom = $y; $right = $x + 1; $top = $y + 1; $expanded = false; do { $expanded = false; $newLeft = ShouldExpandLeft($image, $backgroundColor, $left, $bottom, $top, $padding); if($newLeft != $left) { $left = $newLeft; $expanded = true; } $newRight = ShouldExpandRight($image, $backgroundColor, $right, $bottom, $top, $width, $padding); if($newRight != $right) { $right = $newRight; $expanded = true; } $newTop = ShouldExpandTop($image, $backgroundColor, $top, $left, $right, $height, $padding); if($newTop != $top) { $top = $newTop; $expanded = true; } $newBottom = ShouldExpandBottom($image, $backgroundColor, $bottom, $left, $right, $padding); if($newBottom != $bottom) { $bottom = $newBottom; $expanded = true; } } while($expanded == true); $region = array(); $region["left"] = $left; $region["bottom"] = $bottom; $region["right"] = $right; $region["top"] = $top; return $region; } 

Методы ShouldExpand могли быть написаны более чистым способом, но я пошел на что-то быстрое, чтобы прототип:

 function ShouldExpandLeft($image, $background, $left, $bottom, $top, $padding) { // Find the farthest pixel that is not $background starting at $left - $padding closing in to $left for($x = max(0, $left - $padding); $x < $left; ++$x) { for($y = $bottom; $y <= $top; ++$y) { $pixelColor = imagecolorat($image, $x, $y); if($pixelColor != $background) { return $x; } } } return $left; } function ShouldExpandRight($image, $background, $right, $bottom, $top, $width, $padding) { // Find the farthest pixel that is not $background starting at $right + $padding closing in to $right $from = min($width - 1, $right + $padding); $to = $right; for($x = $from; $x > $to; --$x) { for($y = $bottom; $y <= $top; ++$y) { $pixelColor = imagecolorat($image, $x, $y); if($pixelColor != $background) { return $x; } } } return $right; } function ShouldExpandTop($image, $background, $top, $left, $right, $height, $padding) { // Find the farthest pixel that is not $background starting at $top + $padding closing in to $top for($x = $left; $x <= $right; ++$x) { for($y = min($height - 1, $top + $padding); $y > $top; --$y) { $pixelColor = imagecolorat($image, $x, $y); if($pixelColor != $background) { return $y; } } } return $top; } function ShouldExpandBottom($image, $background, $bottom, $left, $right, $padding) { // Find the farthest pixel that is not $background starting at $bottom - $padding closing in to $bottom for($x = $left; $x <= $right; ++$x) { for($y = max(0, $bottom - $padding); $y < $bottom; ++$y) { $pixelColor = imagecolorat($image, $x, $y); if($pixelColor != $background) { return $y; } } } return $bottom; } 

Теперь, чтобы убедиться, что алгоритм был успешным, я добавил код отладки.

Отладка рендеринга

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

Используя следующий код:

 $large2 = imagecreatefromjpeg($largeFilename); $red = imagecolorallocate($large2, 255, 0, 0); $green = imagecolorallocate($large2, 0, 255, 0); $blue = imagecolorallocate($large2, 0, 0, 255); function DrawRegions($image, $regions, $color) { foreach($regions as $region) { imagerectangle($image, $region["left"], $region["bottom"], $region["right"], $region["top"], $color); } } DrawRegions($large2, $regions, $red); imagejpeg($large2, "regions.jpg"); 

Я мог бы подтвердить, что мой код разбиения делал достойную работу:

Перегородки

Соотношение сторон

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

Я просто определил «окно», где регионы будут сохранены, если их аспектный рацион был между минимальным и максимальным значением;

 $smallAspectRatio = imagesx($small) / imagesy($small); function PruneOutWrongAspectRatio($regions, $minAspectRatio, $maxAspectRatio) { $result = array(); foreach($regions as $region) { $aspectRatio = ($region["right"] - $region["left"]) / ($region["top"] - $region["bottom"]); if($aspectRatio >= $minAspectRatio && $aspectRatio <= $maxAspectRatio) { array_push($result, $region); } } return $result; } $filterOnAspectRatio = true; if($filterOnAspectRatio == true) { $regions = PruneOutWrongAspectRatio($regions, $smallAspectRatio - 0.1 * $smallAspectRatio, $smallAspectRatio + 0.1 * $smallAspectRatio); DrawRegions($large2, $regions, $blue); } imagejpeg($large2, "aspectratio.jpg"); 

Добавив призыв DrawRegions , теперь я рисую синим цветом области, которые все еще находятся в списке как потенциальные позиции:

Соотношение сторон

Как вы можете видеть, осталось только 4 позиции!

Поиск углов

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

 function FindCorners($large, $small, $regions) { $result = array(); $bottomLeftColor = imagecolorat($small, 0, 0); $blColors = GetColorComponents($bottomLeftColor); $bottomRightColor = imagecolorat($small, imagesx($small) - 1, 0); $brColors = GetColorComponents($bottomRightColor); $topLeftColor = imagecolorat($small, 0, imagesy($small) - 1); $tlColors = GetColorComponents($topLeftColor); $topRightColor = imagecolorat($small, imagesx($small) - 1, imagesy($small) - 1); $trColors = GetColorComponents($topRightColor); foreach($regions as $region) { $bottomLeft = null; $bottomRight = null; $topLeft = null; $topRight = null; $regionWidth = $region["right"] - $region["left"]; $regionHeight = $region["top"] - $region["bottom"]; $maxRadius = min($regionWidth, $regionHeight); $topLeft = RadialFindColor($large, $tlColors, $region["left"], $region["top"], 1, -1, $maxRadius); $topRight = RadialFindColor($large, $trColors, $region["right"], $region["top"], -1, -1, $maxRadius); $bottomLeft = RadialFindColor($large, $blColors, $region["left"], $region["bottom"], 1, 1, $maxRadius); $bottomRight = RadialFindColor($large, $brColors, $region["right"], $region["bottom"], -1, 1, $maxRadius); if($bottomLeft["found"] && $topRight["found"] && $topLeft["found"] && $bottomRight["found"]) { $left = min($bottomLeft["x"], $topLeft["x"]); $right = max($bottomRight["x"], $topRight["x"]); $bottom = min($bottomLeft["y"], $bottomRight["y"]); $top = max($topLeft["y"], $topRight["y"]); array_push($result, array("left" => $left, "right" => $right, "bottom" => $bottom, "top" => $top)); } } return $result; } $closeOnCorners = true; if($closeOnCorners == true) { $regions = FindCorners($large, $small, $regions); DrawRegions($large2, $regions, $green); } 

Я попытался найти соответствующий цвет, увеличив «радиально» (его квадраты) по углам, пока не найду соответствующий пиксель (в пределах допуска):

 function GetColorComponents($color) { return array("red" => $color & 0xFF, "green" => ($color >> 8) & 0xFF, "blue" => ($color >> 16) & 0xFF); } function GetDistance($color, $r, $g, $b) { $colors = GetColorComponents($color); return (abs($r - $colors["red"]) + abs($g - $colors["green"]) + abs($b - $colors["blue"])); } function RadialFindColor($large, $color, $startx, $starty, $xIncrement, $yIncrement, $maxRadius) { $result = array("x" => -1, "y" => -1, "found" => false); $treshold = 40; for($r = 1; $r <= $maxRadius; ++$r) { $closest = array("x" => -1, "y" => -1, "distance" => 1000); for($i = 0; $i <= $r; ++$i) { $x = $startx + $i * $xIncrement; $y = $starty + $r * $yIncrement; $pixelColor = imagecolorat($large, $x, $y); $distance = GetDistance($pixelColor, $color["red"], $color["green"], $color["blue"]); if($distance < $treshold && $distance < $closest["distance"]) { $closest["x"] = $x; $closest["y"] = $y; $closest["distance"] = $distance; break; } } for($i = 0; $i < $r; ++$i) { $x = $startx + $r * $xIncrement; $y = $starty + $i * $yIncrement; $pixelColor = imagecolorat($large, $x, $y); $distance = GetDistance($pixelColor, $color["red"], $color["green"], $color["blue"]); if($distance < $treshold && $distance < $closest["distance"]) { $closest["x"] = $x; $closest["y"] = $y; $closest["distance"] = $distance; break; } } if($closest["distance"] != 1000) { $result["x"] = $closest["x"]; $result["y"] = $closest["y"]; $result["found"] = true; return $result; } } return $result; } 

Как вы можете видеть, я не эксперт по PHP, я не знал, что есть встроенная функция для получения каналов rgb, oops!

Последний вызов

Итак, теперь, когда алгоритм работает, давайте посмотрим, что он нашел, используя следующий код:

 foreach($regions as $region) { echo "Potentially between " . $region["left"] . "," . $region["bottom"] . " and " . $region["right"] . "," . $region["top"] . "\n"; } imagejpeg($large2, "final.jpg"); imagedestroy($large2); 

Выход (который довольно близок к реальному решению):

 Potentially between 108,380 and 867,827 in 7.9796848297119 seconds 

Предоставление этого изображения (прямоугольник между 108,380 и 867,827 рисуется зеленым цветом)

окончательный

Надеюсь это поможет!

Мое решение работает, если нет цвета (кроме белого и черного вокруг изображения, но вы можете изменить скрипт, чтобы заставить его работать по-другому)

  $width = imagesx($this->img_src); $height = imagesy($this->img_src); // navigate through pixels of image for ($y = 0; $y < $height; $y++) { for ($x=0; $x < $width; $x++) { list($r, $g, $b) = imagergbat($this->img_src, $x, $y); $black = 0.1; $white = 0.9; // calculate if the color is next to white or black, if not register it as a good pixel $gs = (($r / 3) + ($g / 3) + ($b / 3); $first_pixel = array(); if ($gs > $white && $gs < $black) { // get coordinate of first pixel (left top) if (empty($first_pixel)) $first_pixel = array($x, $y); // And save last_pixel each time till the last one $last_pixel = array($x, $y); } } } 

И вы получите координаты своего изображения. После этого вы должны обрезать его.