Intereting Posts
Как вставить изображение в php-почту? Нужен пример того, как получить предпочтительный язык из заголовка запроса Accept-Language php получает уникальные значения многомерного массива Истекшее время из заданного времени в базе данных Передача параметров URL с помощью Jquery в php Узнайте, как используется ширина изображения jQuery Замена обратных косых черт косой чертой с помощью str_replace () в php Как проверить, является ли переменная массивом? … или что-то вроде массива Есть ли что-то вроде Dwoo-s {with} или {loop} в Smarty 3 или раньше? Синтаксис PHP для массивов: различное поведение между версиями PHP Как я могу получить доступ к роли пользователя в классе Form Builder в Symfony2 Эластичный поиск и Codeigniter (PHP) Использование PHP vs ASP.net PHP – Инициализация элементов объекта с параметром массива Может ли php прослушивать изменение или обновление базы данных mysql? … мышление в реальном времени, живой поиск здесь

Определить положение одного изображения в другом с помощью PHP

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

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

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

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

Код, который я предлагаю здесь, скорее всего, будет медленным, если производительность вызывает беспокойство, вы можете оптимизировать его или использовать ярлыки. Надеюсь, код поставит вас на правильный путь!

Во-первых, позволяет выполнять итерацию на наших фотографиях

 $small = imagecreatefrompng("small.png"); $large = imagecreatefrompng("large.png"); $smallwidth = imagesx($small); $smallheight = imagesy($small); $largewidth = imagesx($large); $largeheight = imagesy($large); $foundX = -1; $foundY = -1; $keepThreshold = 20; $potentialPositions = array(); for($x = 0; $x <= $largewidth - $smallwidth; ++$x) { for($y = 0; $y <= $largeheight - $smallheight; ++$y) { // Scan the whole picture $error = GetImageErrorAt($large, $small, $x, $y); if($error["avg"] < $keepThreshold) { array_push($potentialPositions, array("x" => $x, "y" => $y, "error" => $error)); } } } imagedestroy($small); imagedestroy($large); echo "Found " . count($potentialPositions) . " potential positions\n"; 

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

Теперь, откуда эта ошибка?

Получение вероятности

То, что я здесь сделал, это перебрать маленькую картинку и «окно» на большом снимке, проверяя, какая разница на red , green и blue каналах:

 function GetImageErrorAt($haystack, $needle, $startX, $startY) { $error = array("red" => 0, "green" => 0, "blue" => 0, "avg" => 0); $needleWidth = imagesx($needle); $needleHeight = imagesy($needle); for($x = 0; $x < $needleWidth; ++$x) { for($y = 0; $y < $needleHeight; ++$y) { $nrgb = imagecolorat($needle, $x, $y); $hrgb = imagecolorat($haystack, $x + $startX, $y + $startY); $nr = $nrgb & 0xFF; $hr = $hrgb & 0xFF; $error["red"] += abs($hr - $nr); $ng = ($nrgb >> 8) & 0xFF; $hg = ($hrgb >> 8) & 0xFF; $error["green"] += abs($hg - $ng); $nb = ($nrgb >> 16) & 0xFF; $hb = ($hrgb >> 16) & 0xFF; $error["blue"] += abs($hb - $nb); } } $error["avg"] = ($error["red"] + $error["green"] + $error["blue"]) / ($needleWidth * $needleHeight); return $error; } 

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

Сортировка

Теперь нам просто нужно отсортировать наши лучшие матчи и сохранить лучший, скорее всего, где находится наша маленькая картинка:

 function SortOnAvgError($a, $b) { if($a["error"]["avg"] == $b["error"]["avg"]) { return 0; } return ($a["error"]["avg"] < $b["error"]["avg"]) ? -1 : 1; } if(count($potentialPositions) > 0) { usort($potentialPositions, "SortOnAvgError"); $mostLikely = $potentialPositions[0]; echo "Most likely at " . $mostLikely["x"] . "," . $mostLikely["y"]; } 

пример

Учитывая две следующие фотографии:

большой

а также

Маленький

Вы должны иметь следующий результат:

 Found 5 potential positions Most likely at 288,235 

Что точно соответствует положению нашей утки. 4 других положения – 1 пиксель вверх, вниз, влево и вправо.

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

редактировать

Во-первых, прежде чем делать что-либо, чтобы «оптимизировать» код, нам нужны числа, поэтому я добавил

 function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); } $time_start = microtime_float(); 

а также

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

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

Предварительным, что не было успешным, было кэширование RGB из $needle чтобы ускорить функцию GetImageErrorAt , но это ухудшило время.

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

Я изменил функцию ошибки, чтобы взять в качестве параметра, как увеличивать x и y

 function GetImageErrorAt($haystack, $needle, $startX, $startY, $increment) { $needleWidth = imagesx($needle); $needleHeight = imagesy($needle); $error = array("red" => 0, "green" => 0, "blue" => 0, "avg" => 0, "complete" => true); for($x = 0; $x < $needleWidth; $x = $x + $increment) { for($y = 0; $y < $needleHeight; $y = $y + $increment) { $hrgb = imagecolorat($haystack, $x + $startX, $y + $startY); $nrgb = imagecolorat($needle, $x, $y); $nr = $nrgb & 0xFF; $hr = $hrgb & 0xFF; $ng = ($nrgb >> 8) & 0xFF; $hg = ($hrgb >> 8) & 0xFF; $nb = ($nrgb >> 16) & 0xFF; $hb = ($hrgb >> 16) & 0xFF; $error["red"] += abs($hr - $nr); $error["green"] += abs($hg - $ng); $error["blue"] += abs($hb - $nb); } } $error["avg"] = ($error["red"] + $error["green"] + $error["blue"]) / ($needleWidth * $needleHeight); return $error; } 

Например, передача 2 приведет к возврату функции в 4 раза быстрее, поскольку мы пропускаем значения x и y .

Я также добавил stepSize для основного цикла:

 $stepSize = 10; for($x = 0; $x <= $largewidth - $smallwidth; $x = $x + $stepSize) { for($y = 0; $y <= $largeheight - $smallheight; $y = $y + $stepSize) { // Scan the whole picture $error = GetImageErrorAt($large, $small, $x, $y, 2); if($error["complete"] == true && $error["avg"] < $keepThreshold) { array_push($potentialPositions, array("x" => $x, "y" => $y, "error" => $error)); } } } 

Сделав это, я смог сократить время выполнения с 2657 секунд до 7 секунд с ценой точности. Я увеличил keepThreshold чтобы получить больше «потенциальных результатов».

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

 Found 8 potential positions Most likely at 290,240 

Как вы можете видеть, мы находимся рядом с нашей желаемой позицией, но это не совсем правильно.

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

Теперь я меняю нижнюю часть скрипта на:

 if(count($potentialPositions) > 0) { usort($potentialPositions, "SortOnAvgError"); $mostLikely = $potentialPositions[0]; echo "Most probably around " . $mostLikely["x"] . "," . $mostLikely["y"] . "\n"; $startX = $mostLikely["x"] - $stepSize + 1; // - $stepSize was already explored $startY = $mostLikely["y"] - $stepSize + 1; // - $stepSize was already explored $endX = $mostLikely["x"] + $stepSize - 1; $endY = $mostLikely["y"] + $stepSize - 1; $refinedPositions = array(); for($x = $startX; $x <= $endX; ++$x) { for($y = $startY; $y <= $endY; ++$y) { // Scan the whole picture $error = GetImageErrorAt($large, $small, $x, $y, 1); // now check every pixel! if($error["avg"] < $keepThreshold) // make the threshold smaller { array_push($refinedPositions, array("x" => $x, "y" => $y, "error" => $error)); } } } echo "Found " . count($refinedPositions) . " refined positions\n"; if(count($refinedPositions)) { usort($refinedPositions, "SortOnAvgError"); $mostLikely = $refinedPositions[0]; echo "Most likely at " . $mostLikely["x"] . "," . $mostLikely["y"] . "\n"; } } 

Что теперь дает мне выход вроде:

 Found 8 potential positions Most probably around 290,240 Checking between X 281 and 299 Checking between Y 231 and 249 Found 23 refined positions Most likely at 288,235 in 13.960182189941 seconds 

Это действительно правильный ответ, примерно в 200 раз быстрее, чем исходный сценарий.

Изменить 2

Теперь мой тестовый пример был слишком простым … Я изменил его на поиск изображений в Google:

Поиск изображений Google

Ищет это изображение (он находится по адресу 718,432 )

Утка

Учитывая большие размеры изображений, мы можем ожидать более длительное время обработки, но алгоритм нашел изображение в правильном положении:

 Found 123 potential positions Most probably around 720,430 Found 17 refined positions Most likely at 718,432 in 43.224536895752 seconds 

Редактировать 3

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

Я добавил этот код до первого цикла:

 $smallresizedwidth = $smallwidth / 2; $smallresizedheight = $smallheight / 2; $largeresizedwidth = $largewidth / 2; $largeresizedheight = $largeheight / 2; $smallresized = imagecreatetruecolor($smallresizedwidth, $smallresizedheight); $largeresized = imagecreatetruecolor($largeresizedwidth, $largeresizedheight); imagecopyresized($smallresized, $small, 0, 0, 0, 0, $smallresizedwidth, $smallresizedheight, $smallwidth, $smallheight); imagecopyresized($largeresized, $large, 0, 0, 0, 0, $largeresizedwidth, $largeresizedheight, $largewidth, $largeheight); 

И для них основной цикл я повторил на измененных размерах с измененной шириной и высотой. Затем, добавляя к массиву, я удваиваю x и y , давая следующее:

 array_push($potentialPositions, array("x" => $x * 2, "y" => $y * 2, "error" => $error)); 

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

 imagedestroy($smallresized); imagedestroy($largeresized); 

Используя эту версию кода, с результатом изображения Google, у меня было:

 Found 18 potential positions Most around 720,440 Found 17 refined positions Most likely at 718,432 in 11.499078989029 seconds 

Увеличение производительности в 4 раза!

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

Используйте ImageMagick .

Эта страница даст вам ответ: как я могу определить / рассчитать, есть ли маленькие снимки внутри большего изображения?