Tamaño y posición de una imagen en otra a través de PHP

Tengo dos imágenes (pequeña y grande). El grande contiene uno pequeño. Como si la pequeña es una foto y una grande es una página del álbum de fotos.

¿Cómo consigo las coordenadas de esa imagen pequeña en la grande usando PHP? Y también necesito saber el tamaño de esa imagen en grande … tan solo una coordenada (x, y) de cualquier ángulo y tamaño de lados de esa presentación de la imagen pequeña …

(x, y, ancho, alto)

Ya hice esa pregunta y recibí una respuesta shiny ( aquí ) pero me olvidé de mencionar que el tamaño de una imagen pequeña podría ser diferente del tamaño de esa imagen en la imagen grande …

Y también si es posible tratar con una presentación de esa pequeña imagen en la imagen grande puede tener algo que cubra uno de sus angularjs … Como en este ejemplo:

Imagen pequeña: imagen pequeña

Imagen grande: imagen grande

La imagen pequeña siempre tiene solo una forma rectangular.

    Bien, esta respuesta no responde perfectamente a la pregunta, ¡pero debería darte un buen comienzo! Sé que me repito en el código, pero mi objective era simplemente hacer que funcione algo para que pueda construir sobre él, ¡esto no es código de producción!

    Condiciones previas

    Comenzando con la imagen grande:

    Grande

    Necesitamos encontrar lo mejor posible la posición de esta otra imagen:

    enter image description here

    Decidí dividir el proceso en muchos subpasos, que podría mejorar o eliminar dependiendo de lo que desea que haga el código.

    Para fines de prueba, probé mi algoritmo en diferentes imágenes de entrada para que veas una variable que define qué archivo cargar …

    Comenzamos con:

    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); 

    y

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

    Para tener una buena idea sobre nuestras actuaciones. Afortunadamente, la mayor parte del algoritmo era bastante rápido, así que no tuve que optimizar más.

    Detección de fondo

    Empecé por detectar el color de fondo. Supuse que el color de fondo sería el color más presente en la imagen. Para hacer esto, solo conté cuántas referencias de cada color pude encontrar en la imagen grande, lo ordené con valores descendentes y tomé el primero como color de fondo (debería permitir que el código sea adaptable si cambió las imágenes de origen)

     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 

    Partición

    Mi primer paso fue intentar encontrar todas las regiones donde no había píxeles de fondo. Con un poco de relleno, pude agrupar regiones en regiones más grandes (de modo que un párrafo sería una sola región en lugar de múltiples letras individuales). Comencé con un relleno de 5 y obtuve resultados lo suficientemente buenos, así que me quedé con eso.

    Esto se divide en llamadas a múltiples funciones, así que aquí vamos:

     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); 

    Aquí, iteramos en cada píxel de la imagen, si su color de fondo, lo descartamos, de lo contrario, verificamos si su posición ya está presente en una región que encontramos, si ese es el caso, también la omitiremos. Ahora, si no omitimos el píxel, significa que se trata de un píxel coloreado que debería formar parte de una región, por lo que iniciamos ExpandRegionFrom este píxel.

    El código para verificar si estamos dentro de una región es bastante simple:

     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; } 

    Ahora, el código de expansión intentará hacer crecer la región en cada dirección y lo hará siempre que encuentre nuevos píxeles para agregar a la región:

     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; } 

    Los métodos ShouldExpand podrían haber sido escritos de una manera más limpia, pero ShouldExpand por algo rápido para hacer un prototipo con:

     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; } 

    Ahora, para ver si el algoritmo fue exitoso, agregué un código de depuración.

    Representación de depuración

    Creé una segunda imagen para almacenar información de depuración y almacenarla en el disco para que luego pudiera ver mi progreso.

    Usando el siguiente código:

     $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"); 

    Podría validar que mi código de particionado estaba haciendo un trabajo decente:

    Particiones

    Relación de aspecto

    Decidí filtrar algunas regiones en función de la relación de aspecto (la relación entre el ancho y la altura). Se podría aplicar otro filtro, como el color de píxel promedio o algo así, pero la verificación de la relación de aspecto fue muy rápida, así que lo usé.

    Simplemente definí una "ventana" donde se guardarían las regiones, si su relación de aspecto estaba entre un valor mínimo y máximo;

     $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"); 

    Al agregar la llamada DrawRegions , ahora pinto en azul las regiones que aún están en la lista como posibles posiciones:

    Relación de aspecto

    Como puede ver, ¡solo queda 4 posiciones!

    Encontrar las esquinas

    ¡Ya casi hemos terminado! Ahora, lo que estoy haciendo es mirar los colores en las cuatro esquinas de la imagen pequeña e intentar encontrar el mejor píxel coincidente en las esquinas de las regiones restantes. Este código tiene el mayor potencial de falla, por lo que si tiene que invertir tiempo para mejorar la solución, este código sería un buen candidato.

     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); } 

    Traté de encontrar el color correspondiente aumentando "radialmente" (básicamente cuadrados) desde las esquinas hasta que encuentro un píxel coincidente (dentro de una tolerancia):

     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; } 

    Como puede ver, no soy un experto en PHP, no sabía que había una función integrada para obtener los canales rgb, ¡vaya!

    Llamada final

    Ahora que el algoritmo se ejecutó, veamos qué encontró usando el siguiente código:

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

    El resultado (que está muy cerca de la solución real):

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

    Dando esta imagen (el rectángulo entre 108,380 y 867,827 se dibuja en verde)

    Final

    ¡Espero que esto ayude!

    Mi solución funciona si no hay color (excepto blanco y negro alrededor de la imagen, pero puedes modificar la secuencia de comandos para que funcione de manera diferente)

      $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); } } } 

    Y obtienes las coordenadas de tu imagen. Tienes que cortarlo después de esto.