From 8b0459c0f68d15c0341d477bbb2d5ac13da54857 Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Wed, 14 Nov 2018 19:00:15 +0100 Subject: [PATCH] Simple but major performance fixes In particular for level 1 scoring & image loading. Now that we have a fuller db of images we can be stricker in the range we select. --- src/Api/InterfaceControllerProvider.php | 7 +- src/Models/Hit.php | 95 +++++++++++++------------ src/Models/HitRepository.php | 48 +++++++++---- 3 files changed, 85 insertions(+), 65 deletions(-) diff --git a/src/Api/InterfaceControllerProvider.php b/src/Api/InterfaceControllerProvider.php index c2f43c0..ca01064 100644 --- a/src/Api/InterfaceControllerProvider.php +++ b/src/Api/InterfaceControllerProvider.php @@ -30,7 +30,7 @@ class InterfaceControllerProvider implements ControllerProviderInterface $http_origin = $_SERVER['HTTP_ORIGIN'] ?? null; if ($http_origin == "https://emotionhero.com" || $http_origin == "http://emotionhero.com") - { + { header("Access-Control-Allow-Origin: $http_origin"); // header("Access-Control-Allow-Origin: https://emotionhero.com"); header("Access-Control-Allow-Methods: POST, GET, OPTIONS"); @@ -121,7 +121,7 @@ class InterfaceControllerProvider implements ControllerProviderInterface $http_origin = $_SERVER['HTTP_ORIGIN'] ?? null; if ($http_origin == "https://emotionhero.com" || $http_origin == "http://emotionhero.com") - { + { header("Access-Control-Allow-Origin: $http_origin"); // header("Access-Control-Allow-Origin: https://emotionhero.com"); header("Access-Control-Allow-Methods: POST, GET, OPTIONS"); @@ -138,7 +138,8 @@ class InterfaceControllerProvider implements ControllerProviderInterface $hit = $hitRepo->getClosestHitWithImage($emotion, $i); $img = "data:image/x-icon;base64,".$hit->getFeatureImgAsString($feature); $score = $hit->getEmotions()->getEmotionScore($emotion); - $percentage = sprintf("%.0f %%",$score); + // $percentage = sprintf("%.0f %%",$score); + $percentage = sprintf("%.0f",$score); // use :after to append % character $blocks[$position] = [ 'hit_id' => $hit->getId(), 'img_data' => $img, diff --git a/src/Models/Hit.php b/src/Models/Hit.php index dc0cb7e..094e13e 100644 --- a/src/Models/Hit.php +++ b/src/Models/Hit.php @@ -17,6 +17,7 @@ use Doctrine\Common\Collections\ArrayCollection; * @ORM\Index(name="imgJoy", columns={"hasImage", "joy"}), * @ORM\Index(name="imgSadness", columns={"hasImage", "sadness"}), * @ORM\Index(name="imgSurprise", columns={"hasImage", "surprise"}), + * @ORM\Index(name="targetScore", columns={"target", "score"}), * }) * @ORM\Entity * @ORM\Entity(repositoryClass="EmotionHero\Models\HitRepository") @@ -190,14 +191,14 @@ class Hit $this->score = $score; return $this; } - + /** * @return boolean */ public function hasImage() { return (bool) $this->hasImage; } - + /** * @param bool $hasImage * @return self @@ -206,9 +207,9 @@ class Hit $this->hasImage = $hasImage; return $this; } - + public static $FEATURES = ['brows','nose','mouth_left','mouth_right']; - + /** * @param string $feature * @return string|false false if not exists @@ -218,11 +219,11 @@ class Hit if(!in_array($feature, static::$FEATURES)) { throw new \Exception("Invalid feature!"); } - + return realpath(__DIR__ . "/../../files/hits/".$feature) ; } - + /** * @param string $feature * @return string|false false if not exists @@ -230,12 +231,12 @@ class Hit */ public function getFeatureFilename(string $feature) { $dir = $this->getFeatureDirname($feature); - + return $dir . '/'.$this->getId().'.jpg'; } - + /** * @param string $feature * @return string|null @@ -245,7 +246,7 @@ class Hit if(!file_exists($filename)) { return null; } - + $contents = file_get_contents($filename); $string = base64_encode($contents); if ($string === false) { @@ -253,7 +254,7 @@ class Hit } return $string; } - + } @@ -802,7 +803,7 @@ class Expressions { if(in_array($expression, $expressions)) { $this->$expression = $value > 100 ? 100 : $value; } - return $this; + return $this; } public function getExpressionScore(string $expression){ @@ -816,7 +817,7 @@ class Expressions { /** * Get difference between two expressions objects * Get only for those that can be used in 2nd person - * + * * @param Expressions $expressions To compare wiht * @return array Key: expression, value, diff */ @@ -827,7 +828,7 @@ class Expressions { } return $diffs; } - + /** * @param string $expression * @return string @@ -912,29 +913,29 @@ class Points{ /** @ORM\Embedded(class="Point", columnPrefix="point_33") @JMS\SerializedName("33") */ private $point33; - + /* - * A full list of facial landmarks from Affectiva docs + * A full list of facial landmarks from Affectiva docs * http://developer.affectiva.com/fpi/ - * - * 0 Right Top Jaw - * 1 Right Jaw Angle - * 2 Tip of Chin - * 3 Left Jaw Angle - * 4 Left Top Jaw - * 5 Outer Right Brow Corner - * 6 Right Brow Center - * 7 Inner Right Brow Corner - * 8 Inner Left Brow Corner - * 9 Left Brow Center - * 10 Outer Left Brow Corner - * 11 Nose Root - * 12 Nose Tip - * 13 Nose Lower Right Boundary - * 14 Nose Bottom Boundary - * 15 Nose Lower Left Boundary - * 16 Outer Right Eye + * + * 0 Right Top Jaw + * 1 Right Jaw Angle + * 2 Tip of Chin + * 3 Left Jaw Angle + * 4 Left Top Jaw + * 5 Outer Right Brow Corner + * 6 Right Brow Center + * 7 Inner Right Brow Corner + * 8 Inner Left Brow Corner + * 9 Left Brow Center + * 10 Outer Left Brow Corner + * 11 Nose Root + * 12 Nose Tip + * 13 Nose Lower Right Boundary + * 14 Nose Bottom Boundary + * 15 Nose Lower Left Boundary + * 16 Outer Right Eye * 17 Inner Right Eye * 18 Inner Left Eye * 19 Outer Left Eye @@ -951,9 +952,9 @@ class Points{ * 30 Upper Corner Right Eye * 31 Lower Corner Right Eye * 32 Upper Corner Left Eye - * 33 Lower Corner Left Eye + * 33 Lower Corner Left Eye */ - + /** * Gets the value of point_0y. @@ -1300,9 +1301,9 @@ class Points{ $this->{"point".$nr} = new Point($x, $y); } return $this; - + } - + /** * @return array[Point] */ @@ -1313,7 +1314,7 @@ class Points{ } return $points; } - + /** * Fits points within a 100x100 scale * @return array[Point] @@ -1325,13 +1326,13 @@ class Points{ $minx = min($xs); $maxx = max($xs); - + $miny = min($ys); $maxy = max($ys); - + $sizex = $maxx-$minx; $sizey = $maxy-$miny; - + $scale = max($sizex, $sizey); // divide by largest diff. //used to center the face within the square: @@ -1342,10 +1343,10 @@ class Points{ foreach($points as $i => $point) { $x = (($point->getX() - $minx) / $scale) * 100 + $diffx; $y = (($point->getY() - $miny) / $scale) * 100 + $diffy; - + $normalised_points[$i] = new Point($x, $y); } - + return $normalised_points; } } @@ -1363,25 +1364,25 @@ class Point{ * @var float Facial landmark * @ORM\Column(type="float") */ - private $y; + private $y; public function __construct($x, $y) { $this->x = $x; $this->y = $y; } - + /** * @return float */ public function getX() { return $this->x; } - + /** * @return float */ public function getY() { return $this->y; } -} \ No newline at end of file +} diff --git a/src/Models/HitRepository.php b/src/Models/HitRepository.php index 73048e2..93f461e 100644 --- a/src/Models/HitRepository.php +++ b/src/Models/HitRepository.php @@ -7,27 +7,41 @@ use EmotionHero\Tools\Position; class HitRepository extends EntityRepository { /** - * + * * @param \EmotionHero\Models\Hit $hit * @return Hit The hit that is considered better than the given one (used for hints) */ public function getBetterHit(Hit $hit) { - // we want only the slightly better hit... - $query = $this->_em->createQuery( - "SELECT h, SUM(h.score) as HIDDEN totalScore FROM ".Hit::class." h WHERE h.target IN (:targets) GROUP BY h.game HAVING totalScore > :score ORDER BY totalScore DESC" - ) - ->setMaxResults(1) - ->setParameters([ - 'score' => $this->getTotalScoreForHit($hit), - 'targets' => $this->getTargetSetForTarget($hit->getTarget()), - ]); - $betterHit = $query->getOneOrNullResult(); + $targets = $this->getTargetSetForTarget($hit->getTarget()); + if(count($targets) > 1) { + // we want only the slightly better hit... + $query = $this->_em->createQuery( + "SELECT h, SUM(h.score) as HIDDEN totalScore FROM ".Hit::class." h WHERE h.target IN (:targets) GROUP BY h.game HAVING totalScore > :score ORDER BY totalScore DESC" + ) + ->setMaxResults(1) + ->setParameters([ + 'score' => $this->getTotalScoreForHit($hit), + 'targets' => $targets, + ]); + $betterHit = $query->getOneOrNullResult(); + } else { // run a faster query if there's just on target (no grouping needed) + // we want only the slightly better hit... + $query = $this->_em->createQuery( + "SELECT h FROM ".Hit::class." h WHERE h.target = :target AND h.score > :score ORDER BY h.score DESC" + ) + ->setMaxResults(1) + ->setParameters([ + 'score' => $this->getTotalScoreForHit($hit), + 'target' => array_shift($targets), + ]); + $betterHit = $query->getOneOrNullResult(); + } return $betterHit; } - + /** - * + * * @param string $emotionField * @param float $score * @return Hit @@ -38,12 +52,16 @@ class HitRepository extends EntityRepository throw new \Exception("Invalid emotion!"); } + $scoreMin = $score < 0.1 ? 0 : $score - 0.3; + $scoreMax = $score < 0.1 ? 0 : $score + 0.3; $query = $this->_em->createQuery( // add BETWEEN so we can use _some_ keying to improve performance. - "SELECT h, ABS(:score - h.emotions.$emotionField) as HIDDEN distance, RAND() AS HIDDEN lala FROM ".Hit::class." h WHERE h.hasImage = :has AND h.emotions.$emotionField BETWEEN :score - 2 AND :score + 2 ORDER BY distance ASC, lala" + "SELECT h, ABS(:score - h.emotions.$emotionField) as HIDDEN distance, RAND() AS HIDDEN lala FROM ".Hit::class." h WHERE h.hasImage = :has AND h.emotions.$emotionField BETWEEN :score_min AND :score_max ORDER BY distance ASC, lala" ) ->setParameters([ 'score' => $score, + 'score_min' => $scoreMin, + 'score_max' => $scoreMax, 'has' => true, ]) ->setMaxResults(1); @@ -84,4 +102,4 @@ class HitRepository extends EntityRepository ]) ->getSingleScalarResult(); } -} \ No newline at end of file +}