<?php
    //$input_path = "./20200221183948_001.jpg"; // OK
    $input_path = "./20200221142143_002.jpg"; // OK
    //$input_path = "./20200221142143_001.jpg"; // FALLO DEBIDO AL ESCANEO
    $output_path = "./out.png";
    
    $img = imagecreatefromstring(file_get_contents($input_path));
    // convertir la imagen a dos colores (alto contraste)
    imagefilter($img, IMG_FILTER_GRAYSCALE);
    imagetruecolortopalette($img, false, 2);
    
    if (alignForm($img)){
        $output = "TODO OK";
        //drawPoint($img, 3, 50);
        getCode($img);
    }else{
        $output = "FALLO";
    }
     
    
    // salida
    imagepng($img, $output_path, 9);
    imagedestroy($img);
    
    
    function getCode(&$img){
        $digit0 =   array(
                        0=>array("x"=>2.9, "y"=>23),
                        1=>array("x"=>5.8, "y"=>27),
                        2=>array("x"=>8.6, "y"=>30),
                        3=>array("x"=>11.4, "y"=>33),
                        4=>array("x"=>14.3, "y"=>36)
                    );
        for ($i=0; $i<count($digit0); $i++){
            drawPoint($img, $digit0[$i]["x"], $digit0[$i]["y"]);
        }
    }
    
    function alignForm(&$img, $justDetect = false){
        
        // buscar rectángulo superior izquierdo
        $recttopleft = detectRentangle($img, 0,0,25,25);
            
        if (isset($recttopleft)){
            if ($justDetect)
                drawRectangleCutLines($img, $recttopleft);

            // buscar rectángulo superior derecho
            $recttopright = detectRentangle($img, 85,0,100,25);
            if (isset($recttopright)){
                if ($justDetect)
                    drawRectangleCutLines($img, $recttopright);

                // buscar rectángulo superior medio
                $rectmiddle = detectRentangle($img, 50,0,85,25);
                if (isset($rectmiddle)){
                    if ($justDetect){
                        drawRectangleCutLines($img, $rectmiddle);
                    }else{
                        // Corregir inclinación
                        $img = alignImage($img, $recttopleft, $recttopright);
                    }
                }else{
                    if (!$justDetect){
                        // Corregir inclinación
                        $img = alignImage($img, $recttopleft, $recttopright);
                        
                        // no se consiguió el central se rota la página completamente
                        $img = imagerotate($img, 180, 1);
                    }
                }
                
                if (!$justDetect){
                    // CROPPING
                    // volver a llevar a dos colores
                    imagefilter($img, IMG_FILTER_GRAYSCALE);
                    imagetruecolortopalette($img, false, 2);
                    // buscar de nuevo rectángulo superior izquierdo
                    $recttopleft = detectRentangle($img, 0,0,25,25);
                    if (isset($recttopleft)){
                        //drawRectangleCutLines($img, $recttopleft);

                        $recttopright = detectRentangle($img, 85,0,100,25);
                        if (isset($recttopright)){
                            // pegar imagen al top+left
                            $rect1 = pct2pxl($img, $recttopleft);
                            $rect2 = pct2pxl($img, $recttopright);
                            $img = imagecrop($img, array("x"=>$rect1["left"], "y"=>$rect1["top"], "width"=>$rect2["right"]-$rect1["left"]+1, "height"=>imagesy($img)));
                        }
                    }
                }
                $salida = true;
            }else{
                $salida = false;
            }
        }else{
            $salida = false;
        }
        
        return($salida);

    }

    
    ////////////////////////////////////////////
    
    // esta función detecta un rectángulo en un rango de coordenadas porcentuales
    // Ej. Busca rectangulo en X entre 10% y 20% y Y entre 30% y 40% detectRectangle($img,10,20,30,40);
    //     Devuelve un array(left, top, right, bottom) todos valores porcentuales
    function detectRentangle($img, $minx, $miny, $maxx, $maxy){
        $left = 0;
        $right = 0;
        $top = 0;
        $bottom = 0;
        
        $validwidth = 3;    // porcentaje de anchura del recuadro
        $validheight = 3;   // porcentaje de altura del recuadro
        $tolerance = 1;     // tolerancia +/- pct
        
        if ($maxx<$minx||$maxy<$miny){
            die("---");
            return(null);
        }
        
        $alto = imagesy($img);
        $ancho = imagesx($img);
        
        $retry = true;
        
        $xstartorg = (integer) $minx*$ancho/100;
        $xendorg = (integer) $maxx*$ancho/100;
        $ystartorg = (integer) $miny*$alto/100;
        $yendorg = (integer) $maxy*$alto/100;
        
        $xdisp = 0;     // Desplazamiento en busqueda horizontal
        $ydisp = 0;     // Desplazamiento en busqueda vertical
        
        // Reintentos por ubicación equivocada
        $cnt = 0;
        while ($retry){
            $cnt++;
            $retry = false;
            $xstart = $xstartorg + $xdisp;
            $xend = $xendorg;
            $ystart = $ystartorg + $ydisp;
            $yend = $yendorg;
            $topdetect = false;
            $lefdetect = false;
            $botdetect = false;
            $rigdetect = false;
            // barrido vertical, como las coordenadas vienen porcentuales se convierten a absolutas
            for ($y=$ystart;$y<$yend;$y++){
                // barrido horizontal, como las coordenadas vienen porcentuales se convierten a absolutas
                for ($x=$xstart;$x<$xend;$x++){
                    if (!$topdetect){
                        if (imagecolorat ($img, $x, $y) == 0){      // detección de top+left
                            // mover inicio horizontal al left encontrado
                            $xstart = $x+1;
                            
                            $top = $y;
                            $topdetect = true;
                            $left = $x;
                            $lefdetect = true;
                            
                            // abortar recorrido horzontal para iniciar próxima linea
                            $x = $xend;
                        }
                    }else{
                        if (!$rigdetect){
                            if (imagecolorat ($img, $x, $y) == 1){      // deteccion de right
                                // liminar barrido de ancho hasta nueva detección
                                $xend = $x;
                                
                                $right = $x;
                                $rigdetect = true;
                            }
                        }else{
                            if (imagecolorat ($img, $x, $y) == 1){      // deteccion de bottom
                                $bottom = $y;
                                $botdetect = true;
    
                                // forzar salida del cliclo
                                $y = $yend;
                                $x = $xend;
                                
                            }
                        }
                    }
                }
            }
            
            // convertir a porcentuales
            $left = $left * 100 / $ancho;
            $right = $right * 100 / $ancho;
            $top = $top * 100 / $alto;
            $bottom = $bottom * 100 / $alto;
            
            $actualwidth = intval($right-$left);
            $actualheight = intval($bottom-$top);
            
            // validación de aceptación de que es un rectangulo adecuado
            if ($actualwidth <= $validwidth+$tolerance && $actualwidth >= $validwidth-$tolerance &&
                $actualheight <= $validheight+$tolerance && $actualheight >= $validwidth-$tolerance){
                return (array("left"=>$left,"top"=>$top,"right"=>$right,"bottom"=>$bottom));
            }else{
                // falla el rectángulo se desplaza el left y el top de inicio y se reintenta
                if ($xstartorg + $xdisp < $xendorg){
                    $xdisp++;
                }else{
                    $xdisp = 0;
                    $ydisp++;
                }
                $retry = true;
                if ($ystartorg + $ydisp >= $yendorg){
                    $retry = false;
                }
                
            }
            
            // evitar infinite loop
            if ($cnt> 100000)
                die("XX");
        }

        return (null);
    }
    
    // dibuja una linea vertical porcentual a la imagen
    function drawVerticalLine($img, $xpct){
        $ancho = imagesx($img);
        $alto = imagesy($img);
        $xtopleft = ($xpct*$ancho/100);
        imagefilledrectangle($img, $xtopleft, 0, $xtopleft+1, $alto, 0);
    }
    
    // dibuja una linea horizontal porcentual a la imagen
    function drawHorizontalLine($img, $ypct){
        $alto = imagesy($img);
        $ancho = imagesx($img);
        $xbottomright = ($ypct*$alto/100);
        imagefilledrectangle($img, 0, $xbottomright, $ancho, $xbottomright+1, 0);
    }
    
    // muestra las lineas de corte de un rectángulo
    function drawRectangleCutLines($img, $rect){
        drawHorizontalLine($img, $rect["top"]);
        drawVerticalLine($img, $rect["left"]);
        drawHorizontalLine($img, $rect["bottom"]);
        drawVerticalLine($img, $rect["right"]);
    }
    
    function drawPoint($img, $x, $y){
        drawHorizontalLine($img, $y);
        drawVerticalLine($img, $x);
    }
    
    // pct a pixeles
    function pct2pxl($img, $rect){
        $alto = imagesy($img);
        $ancho = imagesx($img);
        $left = $rect["left"] * $ancho / 100;
        $right = $rect["right"] * $ancho / 100;
        $top = $rect["top"] * $alto / 100;
        $bottom = $rect["bottom"] * $alto / 100;
        return (array("left"=>$left,"top"=>$top,"right"=>$right,"bottom"=>$bottom));
    }
    
    function alignImage($img, $recttopleft, $recttopright){
        // Corregir inclinación
        $rect1 = pct2pxl($img, $recttopleft);
        $rect2 = pct2pxl($img, $recttopright);
        if ($rect1["bottom"] < $rect2["bottom"]){  // clockwise rotation
            $c2 = $rect2["bottom"] - $rect1["bottom"] + 1;
            $c1 = $rect2["right"] - $rect1["left"] + 1;
            $angle = rad2deg(atan($c2/$c1));
            $img = imagerotate($img, $angle, 1);
        }
        if ($rect1["bottom"] > $rect2["bottom"]){  // clockwise rotation
            $c2 = $rect1["bottom"] - $rect2["bottom"] + 1;
            $c1 = $rect2["right"] - $rect1["left"] + 1;
            $angle = rad2deg(-1*atan($c2/$c1));
            $img = imagerotate($img, $angle, 1);

        }
        return($img);
    }
    
?>


<!DOCTYPE html>
<html>
    <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Prueba de identificación de imágenes</title>
    </head>    
    <body>
        <div>
            <div>
                <span>
                    Salida: <?=$output?>
                </span>
            </div>
            <div>
                <div>
                    <img src="http://totalsoftware.la/~ormv/dev/customer/testimg/out.png" />
                </div>
            </div>
        </div>
    </body>
</html>