From e47b63338a1ef446f24e2d2a4cb649f1145f8b2a Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Tue, 18 Jun 2024 11:11:26 -0300 Subject: [PATCH 01/13] Update composer version, added left and right eye to FacialArea --- composer.json | 2 +- src/Data/FacialArea.php | 2 ++ src/DeepFace.php | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 61e22ca..1afaaef 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "php": "^8.1", "ext-json": "*", "ext-spl": "*", - "symfony/process": "^6.0" + "symfony/process": "^7.0" }, "require-dev": { "laravel/pint": "^1.13" diff --git a/src/Data/FacialArea.php b/src/Data/FacialArea.php index 3ed2375..8ef2c56 100644 --- a/src/Data/FacialArea.php +++ b/src/Data/FacialArea.php @@ -11,6 +11,8 @@ public function __construct( public readonly int $y, public readonly int $w, public readonly int $h, + public readonly int|array|null $left_eye, + public readonly int|array|null $right_eye, ) { } diff --git a/src/DeepFace.php b/src/DeepFace.php index 37c9784..933f138 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -239,6 +239,8 @@ public function find( y: $output['source_y'][$i], w: $output['source_w'][$i], h: $output['source_h'][$i], + left_eye: null, + right_eye: null ), model: $model_name, detector_backend: $detector_backend, From fa84f122212a0fac43c6685f346656a949baad6c Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Tue, 18 Jun 2024 12:47:42 -0300 Subject: [PATCH 02/13] Added anti_spoofing support --- scripts/analyze.py | 24 ++++--- scripts/extract_faces.py | 22 +++--- scripts/find.py | 29 ++++---- scripts/verify.py | 26 +++++--- src/DeepFace.php | 141 +++++++++++++++++++++++++-------------- 5 files changed, 151 insertions(+), 91 deletions(-) diff --git a/scripts/analyze.py b/scripts/analyze.py index d857345..02d3964 100644 --- a/scripts/analyze.py +++ b/scripts/analyze.py @@ -1,13 +1,17 @@ import json; +import sys; from deepface import DeepFace; -result = DeepFace.analyze( - img_path = "{{img_path}}", - actions={{actions}}, - enforce_detection={{enforce_detection}}, - detector_backend="{{detector_backend}}", - align={{align}}, - silent={{silent}}, -); - -print(json.dumps(result, default=str)) +try: + result = DeepFace.analyze( + img_path = "{{img_path}}", + actions={{actions}}, + enforce_detection={{enforce_detection}}, + anti_spoofing={{anti_spoofing}}, + detector_backend="{{detector_backend}}", + align={{align}}, + silent={{silent}}, + ); + print(json.dumps(result, default=str)) +except ValueError as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) diff --git a/scripts/extract_faces.py b/scripts/extract_faces.py index 1b6eeba..19f0993 100644 --- a/scripts/extract_faces.py +++ b/scripts/extract_faces.py @@ -1,13 +1,17 @@ import json; from deepface import DeepFace; -result = DeepFace.extract_faces( - img_path = "{{img_path}}", - target_size={{target_size}}, - enforce_detection={{enforce_detection}}, - detector_backend="{{detector_backend}}", - align={{align}}, - grayscale={{grayscale}}, -); +try: + result = DeepFace.extract_faces( + img_path = "{{img_path}}", + target_size={{target_size}}, + enforce_detection={{enforce_detection}}, + anti_spoofing={{anti_spoofing}}, + detector_backend="{{detector_backend}}", + align={{align}}, + grayscale={{grayscale}}, + ); -print(json.dumps(result, default=str)) + print(json.dumps(result, default=str)) +except ValueError as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) diff --git a/scripts/find.py b/scripts/find.py index 9b86f81..1dbf89d 100644 --- a/scripts/find.py +++ b/scripts/find.py @@ -1,15 +1,20 @@ from deepface import DeepFace; +import sys; -result = DeepFace.find( - img_path = "{{img_path}}", - db_path = "{{db_path}}", - model_name = "{{model_name}}", - distance_metric = "{{distance_metric}}", - enforce_detection = {{enforce_detection}}, - detector_backend = "{{detector_backend}}", - align = {{align}}, - normalization = "{{normalization}}", - silent = {{silent}} -); +try: + result = DeepFace.find( + img_path = "{{img_path}}", + db_path = "{{db_path}}", + model_name = "{{model_name}}", + distance_metric = "{{distance_metric}}", + enforce_detection = {{enforce_detection}}, + anti_spoofing={{anti_spoofing}}, + detector_backend = "{{detector_backend}}", + align = {{align}}, + normalization = "{{normalization}}", + silent = {{silent}} + ); -print(result[0].to_json()) + print(result[0].to_json()) +except ValueError as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) diff --git a/scripts/verify.py b/scripts/verify.py index a674fbf..312eefa 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -1,15 +1,19 @@ import json; from deepface import DeepFace; -result = DeepFace.verify( - img1_path = "{{img1_path}}", - img2_path = "{{img2_path}}", - enforce_detection = {{enforce_detection}}, - align = {{align}}, - model_name = "{{model_name}}", - detector_backend = "{{detector_backend}}", - distance_metric = "{{distance_metric}}", - normalization = "{{normalization}}" -); +try: + result = DeepFace.verify( + img1_path = "{{img1_path}}", + img2_path = "{{img2_path}}", + enforce_detection = {{enforce_detection}}, + anti_spoofing={{anti_spoofing}}, + align = {{align}}, + model_name = "{{model_name}}", + detector_backend = "{{detector_backend}}", + distance_metric = "{{distance_metric}}", + normalization = "{{normalization}}" + ); -print(json.dumps(result, default=str)) + print(json.dumps(result, default=str)) +except ValueError as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) diff --git a/src/DeepFace.php b/src/DeepFace.php index 933f138..06af365 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -18,11 +18,14 @@ use Astrotomic\DeepFace\Enums\Normalization; use Astrotomic\DeepFace\Enums\Race; use BadMethodCallException; +use Exception; use InvalidArgumentException; use SplFileInfo; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; +class DeepFaceAnalysisException extends Exception {} + class DeepFace { protected ?string $python = null; @@ -54,6 +57,7 @@ public function verify( bool $enforce_detection = true, bool $align = true, Normalization $normalization = Normalization::BASE, + bool $anti_spoofing = false, ): VerifyResult { $img1 = new SplFileInfo($img1_path); $img2 = new SplFileInfo($img2_path); @@ -65,19 +69,25 @@ public function verify( throw new InvalidArgumentException("The path [{$img2_path}] for image#2 is not a file."); } - $output = $this->run( - filepath: __DIR__.'/../scripts/verify.py', - data: [ - '{{img1_path}}' => $img1->getRealPath(), - '{{img2_path}}' => $img2->getRealPath(), - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{align}}' => $align ? 'True' : 'False', - '{{model_name}}' => $model_name->value, - '{{detector_backend}}' => $detector_backend->value, - '{{distance_metric}}' => $distance_metric->value, - '{{normalization}}' => $normalization->value, - ], - ); + try{ + $output = $this->run( + filepath: __DIR__.'/../scripts/verify.py', + data: [ + '{{img1_path}}' => $img1->getRealPath(), + '{{img2_path}}' => $img2->getRealPath(), + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{align}}' => $align ? 'True' : 'False', + '{{model_name}}' => $model_name->value, + '{{detector_backend}}' => $detector_backend->value, + '{{distance_metric}}' => $distance_metric->value, + '{{normalization}}' => $normalization->value, + ], + ); + } catch(DeepFaceAnalysisException $e){ + return array("error" => $e->getMessage()); + } return new VerifyResult( verified: $output['verified'] === 'True', @@ -104,6 +114,7 @@ public function analyze( Detector $detector_backend = Detector::OPENCV, bool $align = true, bool $silent = false, + bool $anti_spoofing = false, ): array { $img = new SplFileInfo($img_path); @@ -120,17 +131,22 @@ public function analyze( $actions ); - $output = $this->run( - filepath: __DIR__.'/../scripts/analyze.py', - data: [ - '{{img_path}}' => $img->getRealPath(), - '{{actions}}' => '['.implode(',', array_map(fn (AnalyzeAction $action) => "'{$action->value}'", $actions)).']', - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{silent}}' => $silent ? 'True' : 'False', - ], - ); + try{ + $output = $this->run( + filepath: __DIR__.'/../scripts/analyze.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{actions}}' => '['.implode(',', array_map(fn (AnalyzeAction $action) => "'{$action->value}'", $actions)).']', + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{silent}}' => $silent ? 'True' : 'False', + ], + ); + } catch(DeepFaceAnalysisException $e){ + return array("error" => $e->getMessage()); + } return array_map( fn (array $result) => new AnalyzeResult( @@ -159,6 +175,7 @@ public function extractFaces( bool $enforce_detection = true, bool $align = true, bool $grayscale = false, + bool $anti_spoofing = false, ): array { $img = new SplFileInfo($img_path); @@ -166,17 +183,22 @@ public function extractFaces( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - $output = $this->run( - filepath: __DIR__.'/../scripts/extract_faces.py', - data: [ - '{{img_path}}' => $img->getRealPath(), - '{{target_size}}' => '['.implode(',', $target_size).']', - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{grayscale}}' => $grayscale ? 'True' : 'False', - ], - ); + try{ + $output = $this->run( + filepath: __DIR__.'/../scripts/extract_faces.py', + data: [ + '{{img_path}}' => $img->getRealPath(), + '{{target_size}}' => '['.implode(',', $target_size).']', + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{grayscale}}' => $grayscale ? 'True' : 'False', + ], + ); + } catch(DeepFaceAnalysisException $e){ + return array("error" => $e->getMessage()); + } return array_map( fn (array $result) => new ExtractFaceResult( @@ -202,6 +224,7 @@ public function find( bool $align = true, Normalization $normalization = Normalization::BASE, bool $silent = false, + bool $anti_spoofing = false, ): array { $img = new SplFileInfo($img_path); $db = new SplFileInfo($db_path); @@ -214,20 +237,25 @@ public function find( throw new InvalidArgumentException("The path [{$db_path}] for database is not a directory."); } - $output = $this->run( - filepath: __DIR__.'/../scripts/find.py', - data: [ - '{{img_path}}' => $img->getRealPath(), - '{{db_path}}' => $db->getRealPath(), - '{{model_name}}' => $model_name->value, - '{{distance_metric}}' => $distance_metric->value, - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{normalization}}' => $normalization->value, - '{{silent}}' => $silent ? 'True' : 'False', - ], - ); + try{ + $output = $this->run( + filepath: __DIR__.'/../scripts/find.py', + data: [ + '{{img_path}}' => $img->getRealPath(), + '{{db_path}}' => $db->getRealPath(), + '{{model_name}}' => $model_name->value, + '{{distance_metric}}' => $distance_metric->value, + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{normalization}}' => $normalization->value, + '{{silent}}' => $silent ? 'True' : 'False', + ], + ); + } catch(DeepFaceAnalysisException $e){ + return array("error" => $e->getMessage()); + } $result = []; foreach ($output['identity'] as $i => $identity) { @@ -302,6 +330,21 @@ protected function run(string $filepath, array $data): array|bool ->mustRun() ->getOutput(); + $errorOutput = $process->getErrorOutput(); + + if(!empty($errorOutput)){ + if (preg_match_all('/\{(?:[^{}]|(?R))*\}/', $errorOutput, $matches)) { + $lastJson = end($matches[0]); + $errorResult = json_decode($lastJson, true); + + if ($errorResult !== null && isset($errorResult['error'])) { + throw new DeepFaceAnalysisException($errorResult['error']); // should return 'Spoof detected in the given image' + } else { + throw new DeepFaceAnalysisException("Failed to parse error message: " . $lastJson); + } + } + } + $lines = array_values(array_filter(explode(PHP_EOL, $output), function (string $line): bool { json_decode($line, true); From e53c63b04da8a09da282fc8176b8fe9c12f911cb Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Wed, 19 Jun 2024 11:00:02 -0300 Subject: [PATCH 03/13] Fix Unicode characters --- src/DeepFace.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index 06af365..c873f1a 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -73,8 +73,8 @@ public function verify( $output = $this->run( filepath: __DIR__.'/../scripts/verify.py', data: [ - '{{img1_path}}' => $img1->getRealPath(), - '{{img2_path}}' => $img2->getRealPath(), + '{{img1_path}}' => str_replace('\\', '/', $img1->getRealPath()), + '{{img2_path}}' => str_replace('\\', '/', $img2->getRealPath()), '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', @@ -187,7 +187,7 @@ public function extractFaces( $output = $this->run( filepath: __DIR__.'/../scripts/extract_faces.py', data: [ - '{{img_path}}' => $img->getRealPath(), + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), '{{target_size}}' => '['.implode(',', $target_size).']', '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', @@ -241,7 +241,7 @@ public function find( $output = $this->run( filepath: __DIR__.'/../scripts/find.py', data: [ - '{{img_path}}' => $img->getRealPath(), + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), '{{db_path}}' => $db->getRealPath(), '{{model_name}}' => $model_name->value, '{{distance_metric}}' => $distance_metric->value, @@ -300,7 +300,7 @@ public function represent( $output = $this->run( filepath: __DIR__.'/../scripts/represent.py', data: [ - '{{img_path}}' => $img->getRealPath(), + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), '{{model_name}}' => $model_name->value, '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', '{{detector_backend}}' => $detector_backend->value, From 22273e2939458bc331be160040fca4c3a26f448d Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Wed, 19 Jun 2024 11:22:45 -0300 Subject: [PATCH 04/13] Fixed return wrong result --- src/DeepFace.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index c873f1a..9f4732b 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -90,7 +90,7 @@ public function verify( } return new VerifyResult( - verified: $output['verified'] === 'True', + verified: $output['verified'], distance: $output['distance'], threshold: $output['threshold'], model: FaceRecognitionModel::from($output['model']), From 38b80a65e2fb3e8bc9f2c6da96a463f9c585f32a Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Wed, 19 Jun 2024 11:48:18 -0300 Subject: [PATCH 05/13] Create Exceptions --- src/DeepFace.php | 15 +++++++-------- src/Exceptions/DeepFaceGenericException.php | 10 ++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 src/Exceptions/DeepFaceGenericException.php diff --git a/src/DeepFace.php b/src/DeepFace.php index 9f4732b..886527b 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -17,6 +17,7 @@ use Astrotomic\DeepFace\Enums\Gender; use Astrotomic\DeepFace\Enums\Normalization; use Astrotomic\DeepFace\Enums\Race; +use Astrotomic\DeepFace\Exceptions\DeepFaceException; use BadMethodCallException; use Exception; use InvalidArgumentException; @@ -24,8 +25,6 @@ use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; -class DeepFaceAnalysisException extends Exception {} - class DeepFace { protected ?string $python = null; @@ -85,7 +84,7 @@ public function verify( '{{normalization}}' => $normalization->value, ], ); - } catch(DeepFaceAnalysisException $e){ + } catch(DeepFaceException $e){ return array("error" => $e->getMessage()); } @@ -144,7 +143,7 @@ public function analyze( '{{silent}}' => $silent ? 'True' : 'False', ], ); - } catch(DeepFaceAnalysisException $e){ + } catch(DeepFaceException $e){ return array("error" => $e->getMessage()); } @@ -196,7 +195,7 @@ public function extractFaces( '{{grayscale}}' => $grayscale ? 'True' : 'False', ], ); - } catch(DeepFaceAnalysisException $e){ + } catch(DeepFaceException $e){ return array("error" => $e->getMessage()); } @@ -253,7 +252,7 @@ public function find( '{{silent}}' => $silent ? 'True' : 'False', ], ); - } catch(DeepFaceAnalysisException $e){ + } catch(DeepFaceException $e){ return array("error" => $e->getMessage()); } @@ -338,9 +337,9 @@ protected function run(string $filepath, array $data): array|bool $errorResult = json_decode($lastJson, true); if ($errorResult !== null && isset($errorResult['error'])) { - throw new DeepFaceAnalysisException($errorResult['error']); // should return 'Spoof detected in the given image' + throw new DeepFaceException($errorResult['error']); // should return 'Spoof detected in the given image' } else { - throw new DeepFaceAnalysisException("Failed to parse error message: " . $lastJson); + throw new DeepFaceException("Failed to parse error message: " . $lastJson); } } } diff --git a/src/Exceptions/DeepFaceGenericException.php b/src/Exceptions/DeepFaceGenericException.php new file mode 100644 index 0000000..b975509 --- /dev/null +++ b/src/Exceptions/DeepFaceGenericException.php @@ -0,0 +1,10 @@ + Date: Wed, 19 Jun 2024 11:51:38 -0300 Subject: [PATCH 06/13] Add anti_spoofing to represent function --- scripts/represent.py | 1 + src/DeepFace.php | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/scripts/represent.py b/scripts/represent.py index 06fa8a7..a7c675f 100644 --- a/scripts/represent.py +++ b/scripts/represent.py @@ -5,6 +5,7 @@ img_path = "{{img_path}}", model_name = "{{model_name}}", enforce_detection = {{enforce_detection}}, + anti_spoofing = "{{anti_spoofing}}", detector_backend = "{{detector_backend}}", align = {{align}}, normalization = "{{normalization}}" diff --git a/src/DeepFace.php b/src/DeepFace.php index 886527b..5bb2b4e 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -289,6 +289,7 @@ public function represent( Detector $detector_backend = Detector::OPENCV, bool $align = true, Normalization $normalization = Normalization::BASE, + bool $anti_spoofing = false, ): array { $img = new SplFileInfo($img_path); @@ -296,17 +297,22 @@ public function represent( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - $output = $this->run( - filepath: __DIR__.'/../scripts/represent.py', - data: [ - '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), - '{{model_name}}' => $model_name->value, - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{normalization}}' => $normalization->value, - ], - ); + try{ + $output = $this->run( + filepath: __DIR__.'/../scripts/represent.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{model_name}}' => $model_name->value, + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{normalization}}' => $normalization->value, + ], + ); + } catch(DeepFaceException $e){ + return array("error" => $e->getMessage()); + } return array_map( fn (array $result) => new RepresentResult( From 50389b88d28513affafe676f1098fb9ca8fdd4c7 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Wed, 19 Jun 2024 12:16:46 -0300 Subject: [PATCH 07/13] Update scripts and verification function --- scripts/extract_faces.py | 1 + scripts/represent.py | 1 + scripts/verify.py | 1 + src/DeepFace.php | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/extract_faces.py b/scripts/extract_faces.py index 19f0993..e4fad15 100644 --- a/scripts/extract_faces.py +++ b/scripts/extract_faces.py @@ -1,4 +1,5 @@ import json; +import sys; from deepface import DeepFace; try: diff --git a/scripts/represent.py b/scripts/represent.py index a7c675f..b14364f 100644 --- a/scripts/represent.py +++ b/scripts/represent.py @@ -1,4 +1,5 @@ import json; +import sys; from deepface import DeepFace; result = DeepFace.represent( diff --git a/scripts/verify.py b/scripts/verify.py index 312eefa..cd6d9ec 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -1,4 +1,5 @@ import json; +import sys; from deepface import DeepFace; try: diff --git a/src/DeepFace.php b/src/DeepFace.php index 5bb2b4e..657568f 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -57,7 +57,7 @@ public function verify( bool $align = true, Normalization $normalization = Normalization::BASE, bool $anti_spoofing = false, - ): VerifyResult { + ): VerifyResult|array { $img1 = new SplFileInfo($img1_path); $img2 = new SplFileInfo($img2_path); @@ -85,6 +85,7 @@ public function verify( ], ); } catch(DeepFaceException $e){ + // if any of these images fails the spoof detection, it will throw 'Exception while processing imgX_path' return array("error" => $e->getMessage()); } From 49d3b833153a74bb674cd639e9dbaaaa02c4efb4 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:14:58 -0300 Subject: [PATCH 08/13] Composer fix --- src/DeepFace.php | 22 ++++++++++----------- src/Exceptions/DeepFaceGenericException.php | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index 213ff43..8f85629 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -68,7 +68,7 @@ public function verify( throw new InvalidArgumentException("The path [{$img2_path}] for image#2 is not a file."); } - try{ + try { $output = $this->run( filepath: __DIR__.'/../scripts/verify.py', data: [ @@ -84,7 +84,7 @@ public function verify( '{{normalization}}' => $normalization->value, ], ); - } catch(DeepFaceException $e){ + } catch(DeepFaceException $e) { // if any of these images fails the spoof detection, it will throw 'Exception while processing imgX_path' return array("error" => $e->getMessage()); } @@ -131,7 +131,7 @@ public function analyze( $actions ); - try{ + try { $output = $this->run( filepath: __DIR__.'/../scripts/analyze.py', data: [ @@ -144,7 +144,7 @@ public function analyze( '{{silent}}' => $silent ? 'True' : 'False', ], ); - } catch(DeepFaceException $e){ + } catch(DeepFaceException $e) { return array("error" => $e->getMessage()); } @@ -183,7 +183,7 @@ public function extractFaces( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - try{ + try { $output = $this->run( filepath: __DIR__.'/../scripts/extract_faces.py', data: [ @@ -196,7 +196,7 @@ public function extractFaces( '{{grayscale}}' => $grayscale ? 'True' : 'False', ], ); - } catch(DeepFaceException $e){ + } catch(DeepFaceException $e) { return array("error" => $e->getMessage()); } @@ -237,7 +237,7 @@ public function find( throw new InvalidArgumentException("The path [{$db_path}] for database is not a directory."); } - try{ + try { $output = $this->run( filepath: __DIR__.'/../scripts/find.py', data: [ @@ -253,7 +253,7 @@ public function find( '{{silent}}' => $silent ? 'True' : 'False', ], ); - } catch(DeepFaceException $e){ + } catch(DeepFaceException $e) { return array("error" => $e->getMessage()); } @@ -298,7 +298,7 @@ public function represent( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - try{ + try { $output = $this->run( filepath: __DIR__.'/../scripts/represent.py', data: [ @@ -311,7 +311,7 @@ public function represent( '{{normalization}}' => $normalization->value, ], ); - } catch(DeepFaceException $e){ + } catch(DeepFaceException $e) { return array("error" => $e->getMessage()); } @@ -338,7 +338,7 @@ protected function run(string $filepath, array $data): array|bool $errorOutput = $process->getErrorOutput(); - if(!empty($errorOutput)){ + if(!empty($errorOutput)) { if (preg_match_all('/\{(?:[^{}]|(?R))*\}/', $errorOutput, $matches)) { $lastJson = end($matches[0]); $errorResult = json_decode($lastJson, true); diff --git a/src/Exceptions/DeepFaceGenericException.php b/src/Exceptions/DeepFaceGenericException.php index b975509..ac34bb5 100644 --- a/src/Exceptions/DeepFaceGenericException.php +++ b/src/Exceptions/DeepFaceGenericException.php @@ -6,5 +6,4 @@ class DeepFaceException extends Exception { - } From 6e7637e04a568960f7aab6ce89167a43898932b9 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:15:44 -0300 Subject: [PATCH 09/13] Removed duplicated parameter --- src/DeepFace.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index 8f85629..b1998e6 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -74,7 +74,6 @@ public function verify( data: [ '{{img1_path}}' => str_replace('\\', '/', $img1->getRealPath()), '{{img2_path}}' => str_replace('\\', '/', $img2->getRealPath()), - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', '{{align}}' => $align ? 'True' : 'False', From a8406dcea64ca57a15524881a643b3ef6e5b4185 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:16:37 -0300 Subject: [PATCH 10/13] Changed Exception class filename --- .../{DeepFaceGenericException.php => DeepFaceException.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Exceptions/{DeepFaceGenericException.php => DeepFaceException.php} (100%) diff --git a/src/Exceptions/DeepFaceGenericException.php b/src/Exceptions/DeepFaceException.php similarity index 100% rename from src/Exceptions/DeepFaceGenericException.php rename to src/Exceptions/DeepFaceException.php From ed5d740c9a35da3c9acc082ff13618aba03caa12 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:17:52 -0300 Subject: [PATCH 11/13] Added try-catch --- scripts/represent.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/represent.py b/scripts/represent.py index b14364f..18855dc 100644 --- a/scripts/represent.py +++ b/scripts/represent.py @@ -2,14 +2,18 @@ import sys; from deepface import DeepFace; -result = DeepFace.represent( - img_path = "{{img_path}}", - model_name = "{{model_name}}", - enforce_detection = {{enforce_detection}}, - anti_spoofing = "{{anti_spoofing}}", - detector_backend = "{{detector_backend}}", - align = {{align}}, - normalization = "{{normalization}}" -); +try: + result = DeepFace.represent( + img_path = "{{img_path}}", + model_name = "{{model_name}}", + enforce_detection = {{enforce_detection}}, + anti_spoofing = "{{anti_spoofing}}", + detector_backend = "{{detector_backend}}", + align = {{align}}, + normalization = "{{normalization}}" + ); + + print(json.dumps(result, default=str)) +except ValueError as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) -print(json.dumps(result, default=str)) From e34ddfc30a56309c6365f51365ff3c9c59129cf7 Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:35:23 -0300 Subject: [PATCH 12/13] Removed comment --- src/DeepFace.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index b1998e6..8c8fa2d 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -343,7 +343,7 @@ protected function run(string $filepath, array $data): array|bool $errorResult = json_decode($lastJson, true); if ($errorResult !== null && isset($errorResult['error'])) { - throw new DeepFaceException($errorResult['error']); // should return 'Spoof detected in the given image' + throw new DeepFaceException($errorResult['error']); } else { throw new DeepFaceException("Failed to parse error message: " . $lastJson); } From 56e47e7c30e84b0ff640af7a002a7790fd80e33a Mon Sep 17 00:00:00 2001 From: Pablo Gabarro Date: Mon, 24 Jun 2024 09:42:07 -0300 Subject: [PATCH 13/13] Removed try-catch from methods --- src/DeepFace.php | 154 ++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 88 deletions(-) diff --git a/src/DeepFace.php b/src/DeepFace.php index 8c8fa2d..265ada3 100644 --- a/src/DeepFace.php +++ b/src/DeepFace.php @@ -19,7 +19,6 @@ use Astrotomic\DeepFace\Enums\Race; use Astrotomic\DeepFace\Exceptions\DeepFaceException; use BadMethodCallException; -use Exception; use InvalidArgumentException; use SplFileInfo; use Symfony\Component\Process\ExecutableFinder; @@ -57,7 +56,7 @@ public function verify( bool $align = true, Normalization $normalization = Normalization::BASE, bool $anti_spoofing = false, - ): VerifyResult|array { + ): VerifyResult { $img1 = new SplFileInfo($img1_path); $img2 = new SplFileInfo($img2_path); @@ -68,25 +67,20 @@ public function verify( throw new InvalidArgumentException("The path [{$img2_path}] for image#2 is not a file."); } - try { - $output = $this->run( - filepath: __DIR__.'/../scripts/verify.py', - data: [ - '{{img1_path}}' => str_replace('\\', '/', $img1->getRealPath()), - '{{img2_path}}' => str_replace('\\', '/', $img2->getRealPath()), - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', - '{{align}}' => $align ? 'True' : 'False', - '{{model_name}}' => $model_name->value, - '{{detector_backend}}' => $detector_backend->value, - '{{distance_metric}}' => $distance_metric->value, - '{{normalization}}' => $normalization->value, - ], - ); - } catch(DeepFaceException $e) { - // if any of these images fails the spoof detection, it will throw 'Exception while processing imgX_path' - return array("error" => $e->getMessage()); - } + $output = $this->run( + filepath: __DIR__.'/../scripts/verify.py', + data: [ + '{{img1_path}}' => str_replace('\\', '/', $img1->getRealPath()), + '{{img2_path}}' => str_replace('\\', '/', $img2->getRealPath()), + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{align}}' => $align ? 'True' : 'False', + '{{model_name}}' => $model_name->value, + '{{detector_backend}}' => $detector_backend->value, + '{{distance_metric}}' => $distance_metric->value, + '{{normalization}}' => $normalization->value, + ], + ); return new VerifyResult( verified: $output['verified'], @@ -130,22 +124,18 @@ public function analyze( $actions ); - try { - $output = $this->run( - filepath: __DIR__.'/../scripts/analyze.py', - data: [ - '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), - '{{actions}}' => '['.implode(',', array_map(fn (AnalyzeAction $action) => "'{$action->value}'", $actions)).']', - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{silent}}' => $silent ? 'True' : 'False', - ], - ); - } catch(DeepFaceException $e) { - return array("error" => $e->getMessage()); - } + $output = $this->run( + filepath: __DIR__.'/../scripts/analyze.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{actions}}' => '['.implode(',', array_map(fn (AnalyzeAction $action) => "'{$action->value}'", $actions)).']', + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{silent}}' => $silent ? 'True' : 'False', + ], + ); return array_map( fn (array $result) => new AnalyzeResult( @@ -182,22 +172,18 @@ public function extractFaces( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - try { - $output = $this->run( - filepath: __DIR__.'/../scripts/extract_faces.py', - data: [ - '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), - '{{target_size}}' => '['.implode(',', $target_size).']', - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{grayscale}}' => $grayscale ? 'True' : 'False', - ], - ); - } catch(DeepFaceException $e) { - return array("error" => $e->getMessage()); - } + $output = $this->run( + filepath: __DIR__.'/../scripts/extract_faces.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{target_size}}' => '['.implode(',', $target_size).']', + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{grayscale}}' => $grayscale ? 'True' : 'False', + ], + ); return array_map( fn (array $result) => new ExtractFaceResult( @@ -236,25 +222,21 @@ public function find( throw new InvalidArgumentException("The path [{$db_path}] for database is not a directory."); } - try { - $output = $this->run( - filepath: __DIR__.'/../scripts/find.py', - data: [ - '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), - '{{db_path}}' => $db->getRealPath(), - '{{model_name}}' => $model_name->value, - '{{distance_metric}}' => $distance_metric->value, - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{normalization}}' => $normalization->value, - '{{silent}}' => $silent ? 'True' : 'False', - ], - ); - } catch(DeepFaceException $e) { - return array("error" => $e->getMessage()); - } + $output = $this->run( + filepath: __DIR__.'/../scripts/find.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{db_path}}' => $db->getRealPath(), + '{{model_name}}' => $model_name->value, + '{{distance_metric}}' => $distance_metric->value, + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{normalization}}' => $normalization->value, + '{{silent}}' => $silent ? 'True' : 'False', + ], + ); $result = []; foreach ($output['identity'] as $i => $identity) { @@ -297,22 +279,18 @@ public function represent( throw new InvalidArgumentException("The path [{$img_path}] for image is not a file."); } - try { - $output = $this->run( - filepath: __DIR__.'/../scripts/represent.py', - data: [ - '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), - '{{model_name}}' => $model_name->value, - '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', - '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', - '{{detector_backend}}' => $detector_backend->value, - '{{align}}' => $align ? 'True' : 'False', - '{{normalization}}' => $normalization->value, - ], - ); - } catch(DeepFaceException $e) { - return array("error" => $e->getMessage()); - } + $output = $this->run( + filepath: __DIR__.'/../scripts/represent.py', + data: [ + '{{img_path}}' => str_replace('\\', '/', $img->getRealPath()), + '{{model_name}}' => $model_name->value, + '{{enforce_detection}}' => $enforce_detection ? 'True' : 'False', + '{{anti_spoofing}}' => $anti_spoofing ? 'True' : 'False', + '{{detector_backend}}' => $detector_backend->value, + '{{align}}' => $align ? 'True' : 'False', + '{{normalization}}' => $normalization->value, + ], + ); return array_map( fn (array $result) => new RepresentResult(