Ninadjeret
5/20/2018 - 9:09 AM

POGO Image Analyzer

<?php

class POGO_imageAnalyzer {

    var $debug = true;
    var $image = false;
    var $image_path = false;
    
    /**
     * 
     * @param type $url
     */
    function __construct( $url ) {
        
        require_once( get_stylesheet_directory() . '/vendor/autoload.php');
        $this->tesseract = new Ddeboer\Tesseract\Tesseract();
        
        $this->result = (object) array(
            'gym' => false,
            'eggLevel' => false,
            'pokemon'   => false,
            'date' => false,
            'error' => false,            
        );
        
        $this->start = microtime(true);
        if( $this->debug ) $this->_log('========== Début du traitement '.$url.' ==========');
 
        $this->image_url = $url;
        $this->_saveImage();
        $this->_createImageForBasicIdentification();       
        if( ( $this->_isRaidAnnounceImg() ) && empty( $this->result->error ) ) {
            $this->_createImageForGymSearch();
            $this->_createImageForTimeSearch(); 
            if( $this->_isPokemonImg()  ) {
                $this->_createImageForPokemonsearch();
            }
            $this->_perform();                   
        } 
        $this->_close();            

        
    }
 
    /**
     * =========================================================================
     * GENERATES IMAGES & MAIN METHODS
     * =========================================================================
     */
    
    private function _log( $text ) {
        if( is_array( $text ) ) {
            error_log( print_r($text, true) );
        } else {
            error_log( $text );
        }
    }
    
    /**
     * 
     */
    private function _saveImage() {
        
        $ext = pathinfo($this->image_url, PATHINFO_EXTENSION);
        if( $this->debug ) $this->_log('Img extension : ' . $ext);
        
        $this->filename = 'capture-' . time();
        $this->image_path = get_stylesheet_directory() . '/captures/' . $this->filename . '.jpg';
        
        if( $ext == "jpg" || $ext == "jpeg" ) {
            $this->image = imagecreatefromjpeg($this->image_url);
        } elseif( $ext == "png" ) {
            $this->image = imagecreatefrompng($this->image_url);
        } else {
            $this->result->error = 'File type not allowed';
        }
        
        imagejpeg($this->image, $this->image_path);
        $size = getimagesize($this->image_path);
        $this->image_height = $size[1];
        $this->image_width = $size[0];
        
        if( $this->_getImageRatio() >= 1 ) {
            $this->result->error = 'Le ratio de l\'image ne semble pas correct';
        }
        
    }
    
    /**
     * 
     */
    private function _createImageForBasicIdentification() {
        if( !$this->image_path ) {
            return false;
        }
        $this->image_basic = imagecreatefromjpeg($this->image_path);
        $this->image_basic_path =  get_stylesheet_directory() . '/captures/' . $this->filename . '-basic.jpg'; 
        imagefilter($this->image_basic, IMG_FILTER_PIXELATE, $this->image_width / 2 );
        imagejpeg($this->image_basic, $this->image_basic_path); 
        imagedestroy($this->image_basic);         
    }
    
    /**
     * 
     */
    private function _createImageForGymSearch() {       
        $this->image_gym = imagecrop($this->image, ['x' => $this->image_width * 0.2037, 'y' => $this->image_height * 0.03125, 'width' => $this->image_width - $this->image_width * 0.2037, 'height' => $this->image_height * 0.1]);
        $this->image_gym_path =  get_stylesheet_directory() . '/captures/' . $this->filename . '-gym.jpg';  
        imagejpeg($this->image_gym, $this->image_gym_path); 
        imagedestroy($this->image_gym);         
    }
    
    /**
     * 
     */
    private function _createImageForPokemonsearch() {       
        $this->image_pokemon = imagecrop($this->image, ['x' => 0, 'y' => $this->image_height * 0.2474, 'width' => $this->image_width, 'height' => $this->image_height * 0.072916]);
        $this->image_pokemon_path =  get_stylesheet_directory() . '/captures/' . $this->filename . '-pokemon.jpg';  
        imagejpeg($this->image_pokemon, $this->image_pokemon_path); 
        imagedestroy($this->image_pokemon);         
    }
    
    /**
     * 
     */
    private function _createImageForTimeSearch() {
        $margin_top = $this->image_height * 0.185;
        if( $this->_hasAndroidNavigationBar() ) {
            $margin_top -= ($this->image_height * 0.075) * 0.3;
        }
        $this->image_time = imagecrop($this->image, ['x' => $this->image_width * 0.3, 'y' => $margin_top, 'width' => $this->image_width * 0.4, 'height' => $this->image_height * 0.0625]);
        $this->image_time_path =  get_stylesheet_directory() . '/captures/' . $this->filename . '-time.jpg';  
        imagefilter($this->image_time, IMG_FILTER_BRIGHTNESS, -130 );
        imagefilter($this->image_time, IMG_FILTER_GRAYSCALE );
        imagefilter($this->image_time, IMG_FILTER_CONTRAST, -70 );        
        imagejpeg($this->image_time, $this->image_time_path); 
        imagedestroy($this->image_time);        
    } 
    
    /**
     * 
     * @return type
     */
    private function _perform() {        
        $this->result->gym = $this->_getGym();
        $this->result->eggLevel = $this->_getEggLevel();
        $this->result->date = $this->_getTime();
        
        if( $this->image_pokemon_path ) {
            $this->result->pokemon = $this->_getPokemon();
        }
    }
    
    /**
     * 
     */
    private function _close() {
        imagedestroy($this->image); 
        $time_elapsed_secs = microtime(true) - $this->start;
        if( $this->debug ) $this->_log('========== Fin du traitement '.$this->image_url.' ('.round($time_elapsed_secs, 3).'s) ==========');
    }
    
    /**
     * =========================================================================
     * EXTRACT DATA METHODS
     * =========================================================================
     */
    
    private function _isRaidAnnounceImg() {
        if( $this->debug ) $this->_log('---------- Check if image is Raid Announce ----------');
        $image = imagecreatefromjpeg($this->image_basic_path);
        $rgb = imagecolorsforindex($image, imagecolorat($image, $this->image_width * 0.1, $this->image_height *0.7 ));
        if( $this->debug ) $this->_log('Test pixel at x:'.$this->image_width * 0.1.' & y:'.$this->image_height *0.7);
        if( 
            ( $rgb['red'] > 40 && $rgb['red'] < 85 )
            && ( $rgb['green'] > 95 && $rgb['green'] < 160 ) 
            && ( $rgb['blue'] > 55 && $rgb['blue'] < 90 ) 
        ) {
            if( $this->debug ) $this->_log('Img seems to be a raid announce');
            return true;
        } 
        
        $result = $this->_isPokemonImg();
        if( $result == true ) {
            if( $this->debug ) $this->_log('Img seems to be a raid announce');
            return true;            
        }
        if( $this->debug ) $this->_log('Img does not seem to be a raid announce');
        $this->result->error = 'Img does not seem to be a raid announce';
        return false;
    }
    
      
    /**
     * 
     * @return type
     */
    private function _getGym() {
        
        if( $this->debug ) $this->_log('---------- Gym Extraction ----------');
        
        //Step 1 - basic OCR
        if( $this->debug ) $this->_log('First attempt (config 1)' );
        $return = $this->tesseract->recognize( $this->image_gym_path, null, 7 );        
        if( $this->debug ) $this->_log('OCR value : ' . $return );
        if( $this->_looksValidGymName($return) ) {
            if( $this->debug ) $this->_log('Value looks valid gym name' );
            $text = strtolower( POGO_helpers::deleteAccents( $return ) );
            if( $this->debug ) $this->_log('Searched text : ' . $text );
            $result = $this->_findExstingGym($text);
            if( $result ) {
                if( $this->debug ) $this->_log('Gym finded in database : ' . $result->getNameFr() );
                return $result;
            }
            if( $this->debug ) $this->_log('Nothing found in database :(' );
        } 
        if( $this->debug ) $this->_log('Value does not seem to be a correct gym name' );
        
        //Step 2
        if( $this->debug ) $this->_log('New attempt with other image settings (config 2)' );
        $image = imagecreatefromjpeg($this->image_gym_path);
        imagefilter($image, IMG_FILTER_EMBOSS );
        $this->image_gym_path2 = get_stylesheet_directory() . '/captures/' . $this->filename . '-gym-2.jpg';
        imagejpeg($image, $this->image_gym_path2); 
        imagedestroy($image);
        $return = $this->tesseract->recognize( $this->image_gym_path2, null, 7 );
        if( $this->debug ) $this->_log('OCR value : ' . $return );
        if( $this->_looksValidGymName($return) ) {
            if( $this->debug ) $this->_log('Value looks valid gym name' );
            $text = strtolower( POGO_helpers::deleteAccents( $return ) );
            if( $this->debug ) $this->_log('Searched text : ' . $text );
            $result = $this->_findExstingGym($text);
            if( $result ) {
                if( $this->debug ) $this->_log('Gym finded in database : ' . $result->getNameFr() );
                return $result;
            }
            if( $this->debug ) $this->_log('Nothing found in database :(' );
        } 
        if( $this->debug ) $this->_log('Value does not seem to be a correct gym name' );
        
        //Step 3
        if( $this->debug ) $this->_log('New attempt with other image settings (config 3)' );
        $image = imagecreatefromjpeg($this->image_gym_path);
        imagefilter($image, IMG_FILTER_BRIGHTNESS, -130 );
        imagefilter($image, IMG_FILTER_GRAYSCALE );
        imagefilter($image, IMG_FILTER_CONTRAST, -70 );
        //imagefilter($image, IMG_FILTER_EMBOSS );
        $this->image_gym_path3 = get_stylesheet_directory() . '/captures/' . $this->filename . '-gym-3.jpg';
        imagejpeg($image, $this->image_gym_path3); 
        imagedestroy($image);
        $return = (new thiagoalessio\TesseractOCR\TesseractOCR($this->image_gym_path3))
            ->whitelist( $this->_getOcrWhiteList() )
            ->psm(7)
            ->run();
        if( $this->debug ) $this->_log('OCR value : ' . $return );
        if( $this->_looksValidGymName($return) ) {
            if( $this->debug ) $this->_log('Value looks valid gym name' );
            $text = strtolower( POGO_helpers::deleteAccents( $return ) );
            if( $this->debug ) $this->_log('Searched text : ' . $text );
            $result = $this->_findExstingGym($text);
            if( $result ) {
                if( $this->debug ) $this->_log('Gym finded in database : ' . $result->getNameFr()  );
                return $result;
            }
            if( $this->debug ) $this->_log('Nothing found in database :(' );
        } 
        if( $this->debug ) $this->_log('Value does not seem to be a correct gym name' );      
        
        //Step 4
        if( $this->debug ) $this->_log('New attempt with other image settings (config 4)' );
        $size = getimagesize($this->image_gym_path);
        $height = $size[1];
        $width = $size[0];
        $image = imagecreatefromjpeg($this->image_gym_path);
        $image_to_save = imagecreatefromjpeg($this->image_gym_path);
        $this->image_gym_path4 =  get_stylesheet_directory() . '/captures/' . $this->filename . '-gym-4.jpg';
        
        // start from the top-left pixel and keep looping until we have the desired effect
        for($y = 0;$y < $height;$y += 1) {    
            for($x = 0;$x < $width;$x += 1) {
            
                // get the color for current pixel
                $rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));

                // get the closest color from palette
                if( $rgb['red'] > 210 && $rgb['blue'] > 210 && $rgb['green'] > 210 ) {
                    $color = imagecolorallocate ( $image , $rgb['red'] , $rgb['green'] , $rgb['blue'] );
                    imagefilledrectangle($image, $x-1, $y-1, $x, $y, $color); 
                } else {
                    $color = imagecolorallocate ( $image , 0 , 0 , 0 );
                    imagefilledrectangle($image, $x-1, $y-1, $x, $y, $color);                   
                }                 
            }       
        }
        
        imagejpeg($image, $this->image_gym_path4); 
        imagedestroy($image);
        $return = (new thiagoalessio\TesseractOCR\TesseractOCR($this->image_gym_path3))
            ->whitelist( $this->_getOcrWhiteList() )
            ->psm(6)
            ->run();
        if( $this->debug ) $this->_log('OCR value : ' . $return );
        if( $this->_looksValidGymName($return) ) {
            if( $this->debug ) $this->_log('Value looks valid gym name' );
            $text = strtolower( POGO_helpers::deleteAccents( $return ) );
            if( $this->debug ) $this->_log('Searched text : ' . $text );
            $result = $this->_findExstingGym($text);
            if( $result ) {
                if( $this->debug ) $this->_log('Gym finded in database : ' . $result );
                return $result;
            }
            if( $this->debug ) $this->_log('Nothing found in database :(' );
        } 
        if( $this->debug ) $this->_log('Value does not seem to be a correct gym name' );     
        
        return false;
    }
    
    /**
     * 
     * @return boolean
     */
    private function _getTime() {
        if( $this->debug ) $this->_log('---------- Date Extraction ----------');
        //$time = $this->tesseract->recognize( $this->image_time_path, null, 7 );
        
        $time = (new thiagoalessio\TesseractOCR\TesseractOCR($this->image_time_path))
            ->psm(7)
            ->run();

        if( $this->debug ) $this->_log( 'OCR Value : ' . $time );
        $time = preg_replace('/[^0-9]/', '',$time);
        if( $this->debug ) $this->_log( 'Sanitized value : ' . $time );
        if( !empty($time) ) {
            $minutes = substr($time, 1, 2 );
            if( $this->debug ) $this->_log( 'Minutes : ' . $minutes );
            $date = new DateTime();
            $date->setTimezone(new DateTimeZone('Europe/Paris'));
            $date->modify('+'.$minutes.' minutes');
            return $date->format('Y-m-d H:i:s');
        }   
        return false;
    }
    
    /**
     * 
     * @return boolean
     */
    private function _getPokemon() {
        if( $this->debug ) $this->_log('---------- Pokemon Extraction ----------');
        
        $size = getimagesize($this->image_pokemon_path);
        $height = $size[1];
        $width = $size[0];
        $image = imagecreatefromjpeg($this->image_pokemon_path);
        $image_to_save = imagecreatefromjpeg($this->image_pokemon_path);
        $this->image_pokemon_path2 =  get_stylesheet_directory() . '/captures/' . $this->filename . '-pokemon-2.jpg';
        
        // start from the top-left pixel and keep looping until we have the desired effect
        for($y = 0;$y < $height;$y += 1) {    
            for($x = 0;$x < $width;$x += 1) {
            
                // get the color for current pixel
                $rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));

                // get the closest color from palette
                if( $rgb['red'] > 250 && $rgb['blue'] > 250 && $rgb['green'] > 250 ) {
                    $color = imagecolorallocate ( $image , $rgb['red'] , $rgb['green'] , $rgb['blue'] );
                    imagefilledrectangle($image, $x-1, $y-1, $x, $y, $color); 
                } else {
                    $color = imagecolorallocate ( $image , 0 , 0 , 0 );
                    imagefilledrectangle($image, $x-1, $y-1, $x, $y, $color);                   
                }                 
            }       
        }
        
        imagejpeg($image, $this->image_pokemon_path2); 
        imagedestroy($image);
        error_log( get_template_directory() . '/ocr/user-words.txt' );
        $pokemon = (new thiagoalessio\TesseractOCR\TesseractOCR($this->image_pokemon_path2))
            ->whitelist( range('a','z'), 'é' )  
            //->lang('en')
            //->userWords( get_template_directory() . '/ocr/en.user-words')
            ->config('load_system_dawg', 'F')
            ->config('load_freq_dawg', 'F')
            ->config('user_words_file', get_template_directory() . '/ocr/en.user-words' )
            ->config('language_model_penalty_non_dict_word', 0.9)
            ->psm(7)
            ->run();
        
        $this->_SearchMatchingPokemon();
        
        if( $this->debug ) $this->_log($pokemon);
        return $pokemon;

    }
    
    /**
     * 
     * @return int
     */
    private function _getEggLevel() {
        $egg_level = 0;
        if( $this->debug ) $this->_log('---------- Egg level Extraction ----------');
        foreach( $this->_getEggLevelCoordinates() as $coor ) {
            if( $this->debug ) $this->_log('Test pixel at x:'.$coor[0].' & y:'.$coor[1]);
            $rgb = imagecolorsforindex($this->image, imagecolorat($this->image, $coor[0], $coor[1] ));
            if( $this->_isEgglevelColor($rgb) ) {
                $egg_level += 1;
                if( $this->debug ) $this->_log('Pixel matches');
            } else {
                if( $this->debug ) $this->_log('Pixel does not match');
            }            
        }
        if( $this->debug ) $this->_log($egg_level . ' matching pixels');
        return $egg_level;
    }
    
    /**
     * =========================================================================
     * HELPER METHODS
     * =========================================================================
     */

    /*
    function optimizeImageForGymSearch() {
        $size = getimagesize($this->image_path);
        $height = $size[1];
        $width = $size[0];
        $this->image_gym = imagecrop($this->image, ['x' => 250, 'y' => 80, 'width' => 800, 'height' => 200]);
        $this->image_gym_path =  get_stylesheet_directory() . '/captures/' . $this->filename . '-gym.jpg';
        
        // start from the top-left pixel and keep looping until we have the desired effect
        for($y = 0;$y < $height;$y += 1) {    
            for($x = 0;$x < $width;$x += 1) {
            
                // get the color for current pixel
                $rgb = imagecolorsforindex($this->image_gym, imagecolorat($this->image, $x, $y));

                // get the closest color from palette
                if( $rgb['red'] > 220 && $rgb['blue'] > 220 && $rgb['green'] > 220 ) {
                    $color = imagecolorallocate ( $this->image_gym , $rgb['red'] , $rgb['green'] , $rgb['blue'] );
                    imagefilledrectangle($this->image_gym, $x-1, $y-1, $x, $y, $color); 
                } else {
                    $color = imagecolorallocate ( $this->image_gym , 0 , 0 , 0 );
                    imagefilledrectangle($this->image_gym, $x-1, $y-1, $x, $y, $color);                   
                }                 
            }       
        }
        
        imagejpeg($this->image_gym, $this->image_gym_path); 
        imagedestroy($this->image_gym);
    } 
    */
    
    private function _ocrGymName( $image_path ) {
        
    }

    
    /**
     * 
     * @return type
     */
    private function _getEggLevelCoordinates() {
        /*return array(
            array( 328, 570 ),
            array( 434, 570 ),
            array( 487, 570 ),
            array( 539, 570 ),
            array( 592, 570 ),
            array( 645, 570 ),
            array( 751, 570 ),
        );*/
        
        $margin_top = $this->image_height * 0.296875;
        if( $this->debug ) $this->_log( 'Margin top : ' . $margin_top );
        
        if( $this->_hasAndroidNavigationBar() ) {            
            $margin_top -= ($this->image_height * 0.075) * 0.25;
            if( $this->debug ) $this->_log('Img has Android navigation bar');
            if( $this->debug ) $this->_log( 'Ajusted margin top : ' . $margin_top );
        }
               
        $return = array(
            array( $this->image_width * 0.3037, $margin_top ),
            array( $this->image_width * 0.4019, $margin_top ),
            array( $this->image_width * 0.4509, $margin_top ),
            array( $this->image_width * 0.4990, $margin_top ),
            array( $this->image_width * 0.5481, $margin_top ),
            array( $this->image_width * 0.5972, $margin_top ),
            array( $this->image_width * 0.6954, $margin_top ),
        );
        return $return;
    }
    
    /**
     * 
     * @param type $rgb
     * @return boolean
     */
    private function _isEgglevelColor( $rgb ) {
        if( $rgb['red'] > 240 && $rgb['green'] > 240 && $rgb['blue'] > 240 ) {
            return true;
        }
        return false;        
    }
    
    /**
     * 
     * @param type $string
     * @return type
     */
    private function _sanitizeTime( $string ) {
        $string = str_replace(array(' ', '('), '', $string);
        return $string;
    } 
    
    /**
     * 
     * @param type $string
     * @return boolean
     */
    private function _looksValidGymName( $string ) {
        if( empty( $string ) ) return false;
        if( strlen($string) < 5 ) return false;
        return true;
    }

    /**
     * 
     * @return type
     */
    private function _getImageRatio() {
        return $this->image_width / $this->image_height;
    }
    
    /**
     * 
     * @return boolean
     */
    private function _hasAndroidNavigationBar() {
        $rgb = imagecolorsforindex($this->image, imagecolorat($this->image, $this->image_width - 1, $this->image_height - 1 ));
        if( $rgb['red'] == 0 && $rgb['green'] == 0 && $rgb['blue'] == 0 ) {
            return true;
        }        
        return false;
    }    
    
    /**
     * 
     * @param type $text
     * @return boolean|\POGO_gym
     */
    private function _findExstingGym( $text ) {
        foreach (POGO_helpers::getGyms() as $gym_id) {
            $gym = new POGO_gym($gym_id);
            foreach( $gym->getSearhPatterns() as $pattern ) {
                if( strstr($text, $pattern) ) {
                    return $gym; 
                }
            }
        }
        return false;
    }
    
    /**
     * 
     * @return string
     */
    private function _getOcrWhiteList() {
        return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMEOPQRSTUVWXYZ;:!-éèêà';
    }
    
    /**
     * 
     * @return boolean
     */
    private function _isPokemonImg() {
        //if( $this->debug ) $this->_log('---------- Check if announce if Egg or Pokemon ----------');
        //if( $this->debug ) $this->_log('Test pixel at x:'.$this->image_width * 0.92592.' & y:'.$this->image_height* 0.611979);
        $rgb = imagecolorsforindex($this->image, imagecolorat($this->image, $this->image_width * 0.92592 , $this->image_height* 0.611979 ));
        if( $rgb['red'] == 255 && $rgb['green'] == 120 && $rgb['blue'] == 55 ) {
            //if( $this->debug ) $this->_log('Img seems to include a pokemon');
            return true;
        }   
        //if( $this->debug ) $this->_log('Img seems to include an egg');
        return false;        
    }
    
    private function _SearchMatchingPokemon( $pokemon ) {
        
        $bosses = POGO_helpers::getRaidBosses();
        foreach( $bosses as $boss_id ) {
            $boss = new POGO_pokemon($boss_id);
        }
        
    }

}