Skip to content

Commit

Permalink
Merge pull request #750 from matiasdelellis/model6
Browse files Browse the repository at this point in the history
Add the sixth model to the application. Aka DlibTaguchiHog model. =)
  • Loading branch information
matiasdelellis authored May 16, 2024
2 parents abc830a + 2beb9bd commit 7a7da61
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 61 deletions.
23 changes: 5 additions & 18 deletions lib/Model/DlibCnnHogModel/DlibCnnHogModel.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (c) 2020-2023, Matias De lellis <mati86dl@gmail.com>
* @copyright Copyright (c) 2020-2024, Matias De lellis <mati86dl@gmail.com>
*
* @author Matias De lellis <mati86dl@gmail.com>
*
Expand Down Expand Up @@ -28,7 +28,6 @@
use OCA\FaceRecognition\Model\IModel;

use OCA\FaceRecognition\Model\DlibCnnModel\DlibCnn5Model;
use OCA\FaceRecognition\Model\DlibHogModel\DlibHogModel;

class DlibCnnHogModel implements IModel {

Expand All @@ -43,20 +42,14 @@ class DlibCnnHogModel implements IModel {
/** @var DlibCnn5Model */
private $dlibCnn5Model;

/** @var DlibHogModel */
private $dlibHogModel;

/**
* DlibCnnHogModel __construct.
*
* @param DlibCnn5Model $dlibCnn5Model
* @param DlibHogModel $dlibHogModel
*/
public function __construct(DlibCnn5Model $dlibCnn5Model,
DlibHogModel $dlibHogModel)
public function __construct(DlibCnn5Model $dlibCnn5Model)
{
$this->dlibCnn5Model = $dlibCnn5Model;
$this->dlibHogModel = $dlibHogModel;
}

public function getId(): int {
Expand All @@ -78,8 +71,6 @@ public function getDocumentation(): string {
public function isInstalled(): bool {
if (!$this->dlibCnn5Model->isInstalled())
return false;
if (!$this->dlibHogModel->isInstalled())
return false;
return true;
}

Expand All @@ -88,10 +79,6 @@ public function meetDependencies(string &$error_message): bool {
$error_message = "This Model depend on Model 1 and must install it.";
return false;
}
if (!$this->dlibHogModel->isInstalled()) {
$error_message = "This Model depend on Model 3 and must install it.";
return false;
}
return true;
}

Expand All @@ -107,7 +94,7 @@ public function getPreferredMimeType(): string {
* @return void
*/
public function install() {
// This model reuses models 1 and 3 and should not install anything.
// This model reuses models 1 and should not install anything.
}

/**
Expand All @@ -125,7 +112,7 @@ public function detectFaces(string $imagePath, bool $compute = true): array {
return $detectedFaces;
}

$hogFaces = $this->dlibHogModel->detectFaces($imagePath, false);
$hogFaces = dlib_face_detection($imagePath);

foreach ($cnnFaces as $proposedFace) {
$detectedFaces[] = $this->validateFace($proposedFace, $hogFaces);
Expand Down Expand Up @@ -163,7 +150,7 @@ private function validateFace($proposedFace, array $validateFaces) {
* These are bad to compare, so we lower the confidence, to avoid clustering.
*/
$confidence = $proposedFace['detection_confidence'];
$proposedFace['detection_confidence'] = $confidence * 0.8;
$proposedFace['detection_confidence'] = $confidence * 0.6;

return $proposedFace;
}
Expand Down
22 changes: 12 additions & 10 deletions lib/Model/DlibCnnModel/DlibCnn5Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ class DlibCnn5Model extends DlibCnnModel implements IModel {
/*
* Model files.
*/
const FACE_MODEL_BZ2_URLS = [
'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2',
'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_5_face_landmarks.dat.bz2',
'https://github.com/davisking/dlib-models/raw/2a61575dd45d818271c085ff8cd747613a48f20d/dlib_face_recognition_resnet_model_v1.dat.bz2'
];

const FACE_MODEL_FILES = [
'mmod_human_face_detector.dat',
'shape_predictor_5_face_landmarks.dat',
'dlib_face_recognition_resnet_model_v1.dat'
'detector' => [
'url' => 'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2',
'filename' => 'mmod_human_face_detector.dat'
],
'predictor' => [
'url' => 'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_5_face_landmarks.dat.bz2',
'filename' => 'shape_predictor_5_face_landmarks.dat',
],
'resnet' => [
'url' => 'https://github.com/davisking/dlib-models/raw/2a61575dd45d818271c085ff8cd747613a48f20d/dlib_face_recognition_resnet_model_v1.dat.bz2',
'filename' => 'dlib_face_recognition_resnet_model_v1.dat'
]
];

}
22 changes: 12 additions & 10 deletions lib/Model/DlibCnnModel/DlibCnn68Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,18 @@ class DlibCnn68Model extends DlibCnnModel implements IModel {
/*
* Model files.
*/
const FACE_MODEL_BZ2_URLS = [
'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2',
'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_68_face_landmarks.dat.bz2',
'https://github.com/davisking/dlib-models/raw/2a61575dd45d818271c085ff8cd747613a48f20d/dlib_face_recognition_resnet_model_v1.dat.bz2'
];

const FACE_MODEL_FILES = [
'mmod_human_face_detector.dat',
'shape_predictor_68_face_landmarks.dat',
'dlib_face_recognition_resnet_model_v1.dat'
'detector' => [
'url' => 'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2',
'filename' => 'mmod_human_face_detector.dat'
],
'predictor' => [
'url' => 'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_68_face_landmarks.dat.bz2',
'filename' => 'shape_predictor_68_face_landmarks.dat',
],
'resnet' => [
'url' => 'https://github.com/davisking/dlib-models/raw/2a61575dd45d818271c085ff8cd747613a48f20d/dlib_face_recognition_resnet_model_v1.dat.bz2',
'filename' => 'dlib_face_recognition_resnet_model_v1.dat'
]
];

}
29 changes: 12 additions & 17 deletions lib/Model/DlibCnnModel/DlibCnnModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,8 @@ class DlibCnnModel implements IModel {
const MEMORY_AREA_RELATIONSHIP = -1;
const MINIMUM_MEMORY_REQUIREMENTS = -1;

const FACE_MODEL_BZ2_URLS = array();
const FACE_MODEL_FILES = array();

const I_MODEL_DETECTOR = 0;
const I_MODEL_PREDICTOR = 1;
const I_MODEL_RESNET = 2;

const PREFERRED_MIMETYPE = 'image/png';

/** @var \CnnFaceDetection */
Expand Down Expand Up @@ -112,11 +107,11 @@ public function getDocumentation(): string {
}

public function isInstalled(): bool {
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]))
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES['detector']['filename']))
return false;
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]))
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES['predictor']['filename']))
return false;
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]))
if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES['resnet']['filename']))
return false;
return true;
}
Expand Down Expand Up @@ -159,14 +154,14 @@ public function install() {
$this->modelService->prepareModelFolder($this->getId());

/* Download and install models */
$detectorModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_DETECTOR]);
$this->compressionService->bunzip2($detectorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]));
$detectorModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_FILES['detector']['url']);
$this->compressionService->decompress($detectorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['detector']['filename']));

$predictorModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_PREDICTOR]);
$this->compressionService->bunzip2($predictorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));
$predictorModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_FILES['predictor']['url']);
$this->compressionService->decompress($predictorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['predictor']['filename']));

$resnetModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_RESNET]);
$this->compressionService->bunzip2($resnetModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));
$resnetModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_FILES['resnet']['url']);
$this->compressionService->decompress($resnetModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['resnet']['filename']));

/* Clean temporary files */
$this->downloadService->clean();
Expand All @@ -176,9 +171,9 @@ public function install() {
* @return void
*/
public function open() {
$this->cfd = new \CnnFaceDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]));
$this->fld = new \FaceLandmarkDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));
$this->fr = new \FaceRecognition($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));
$this->cfd = new \CnnFaceDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['detector']['filename']));
$this->fld = new \FaceLandmarkDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['predictor']['filename']));
$this->fr = new \FaceRecognition($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES['resnet']['filename']));
}

public function detectFaces(string $imagePath, bool $compute = true): array {
Expand Down
4 changes: 2 additions & 2 deletions lib/Model/DlibHogModel/DlibHogModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ public function install() {

/* Download and install models */
$predictorModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_PREDICTOR]);
$this->compressionService->bunzip2($predictorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));
$this->compressionService->decompress($predictorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));

$resnetModelBz2 = $this->downloadService->downloadFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_RESNET]);
$this->compressionService->bunzip2($resnetModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));
$this->compressionService->decompress($resnetModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));

/* Clean temporary files */
$this->downloadService->clean();
Expand Down
110 changes: 110 additions & 0 deletions lib/Model/DlibTaguchiHogModel/DlibTaguchiHogModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/**
* @copyright Copyright (c) 2020-2024, Matias De lellis <mati86dl@gmail.com>
*
* @author Matias De lellis <mati86dl@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\FaceRecognition\Model\DlibTaguchiHogModel;

use OCA\FaceRecognition\Helper\FaceRect;

use OCA\FaceRecognition\Model\DlibCnnModel\DlibCnnModel;
use OCA\FaceRecognition\Model\IModel;

class DlibTaguchiHogModel extends DlibCnnModel implements IModel {

/*
* Model files.
*/
const FACE_MODEL_ID = 6;
const FACE_MODEL_NAME = "DlibTaguchiHog";
const FACE_MODEL_DESC = "Extends the Taguchi model, doing a face validation with the Hog detector";
const FACE_MODEL_DOC = "https://github.com/matiasdelellis/facerecognition/wiki/Models#model-6";

/** Relationship between image size and memory consumed */
const MEMORY_AREA_RELATIONSHIP = 1 * 1024;
const MINIMUM_MEMORY_REQUIREMENTS = 1 * 1024 * 1024 * 1024;

/*
* Model files.
*/
const FACE_MODEL_FILES = [
'detector' => [
'url' => 'https://github.com/davisking/dlib-models/raw/94cdb1e40b1c29c0bfcaf7355614bfe6da19460e/mmod_human_face_detector.dat.bz2',
'filename' => 'mmod_human_face_detector.dat'
],
'predictor' => [
'url' => 'https://github.com/davisking/dlib-models/raw/4af9b776281dd7d6e2e30d4a2d40458b1e254e40/shape_predictor_5_face_landmarks.dat.bz2',
'filename' => 'shape_predictor_5_face_landmarks.dat',
],
'resnet' => [
'url' => 'https://github.com/TaguchiModels/dlibModels/raw/main/taguchi_face_recognition_resnet_model_v1.7z',
'filename' => 'taguchi_face_recognition_resnet_model_v1.dat'
]
];

public function detectFaces(string $imagePath, bool $compute = true): array {
$detectedFaces = [];

$cnnFaces = parent::detectFaces($imagePath);
if (count($cnnFaces) === 0) {
return $detectedFaces;
}

$hogFaces = dlib_face_detection($imagePath);

foreach ($cnnFaces as $proposedFace) {
$detectedFaces[] = $this->validateFace($proposedFace, $hogFaces);
}

return $detectedFaces;
}

private function validateFace($proposedFace, array $validateFaces) {
foreach ($validateFaces as $validateFace) {
$overlapPercent = FaceRect::overlapPercent($proposedFace, $validateFace);
/**
* The weak link in our default model is the landmark detector that
* can't align profile or rotate faces correctly.
*
* The Hog detector also fails and cannot detect these faces. So, we
* consider if Hog detector can detect it, to infer when the predictor
* will give good results.
*
* If Hog detects it (Overlap > 35%), we can assume that landmark
* detector will do it too. In this case, we consider the face valid,
* and just return it.
*/
if ($overlapPercent >= 0.35) {
return $proposedFace;
}
}

/**
* If Hog don't detect this face, they are probably in profile or rotated.
* These are bad to compare, so we lower the confidence, to avoid clustering.
*/
$confidence = $proposedFace['detection_confidence'];
$proposedFace['detection_confidence'] = $confidence * 0.6;

return $proposedFace;
}

}
15 changes: 13 additions & 2 deletions lib/Model/ModelManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use OCA\FaceRecognition\Model\DlibHogModel\DlibHogModel;

use OCA\FaceRecognition\Model\DlibCnnHogModel\DlibCnnHogModel;
use OCA\FaceRecognition\Model\DlibTaguchiHogModel\DlibTaguchiHogModel;

use OCA\FaceRecognition\Model\ExternalModel\ExternalModel;

Expand Down Expand Up @@ -65,6 +66,9 @@ class ModelManager {
/** @var ExternalModel */
private $externalModel;

/** @var DlibTaguchiHogModel */
private $dlibTaguchiHogModel;

/**
* @patam IUserManager $userManager
* @param SettingsService $settingsService
Expand All @@ -73,14 +77,16 @@ class ModelManager {
* @param DlibHogModel $dlibHogModel
* @param DlibCnnHogModel $dlibCnnHogModel
* @param ExternalModel $externalModel
* @param DlibTaguchiHogModel $dlibTaguchiHogModel
*/
public function __construct(IUserManager $userManager,
SettingsService $settingsService,
DlibCnn5Model $dlibCnn5Model,
DlibCnn68Model $dlibCnn68Model,
DlibHogModel $dlibHogModel,
DlibCnnHogModel $dlibCnnHogModel,
ExternalModel $externalModel)
ExternalModel $externalModel,
DlibTaguchiHogModel $dlibTaguchiHogModel)
{
$this->userManager = $userManager;
$this->settingsService = $settingsService;
Expand All @@ -90,6 +96,7 @@ public function __construct(IUserManager $userManager,
$this->dlibHogModel = $dlibHogModel;
$this->dlibCnnHogModel = $dlibCnnHogModel;
$this->externalModel = $externalModel;
$this->dlibTaguchiHogModel = $dlibTaguchiHogModel;
}

/**
Expand All @@ -113,6 +120,9 @@ public function getModel(int $version): ?IModel {
case ExternalModel::FACE_MODEL_ID:
$model = $this->externalModel;
break;
case DlibTaguchiHogModel::FACE_MODEL_ID:
$model = $this->dlibTaguchiHogModel;
break;
default:
$model = null;
break;
Expand All @@ -137,7 +147,8 @@ public function getAllModels(): array {
$this->dlibCnn68Model,
$this->dlibHogModel,
$this->dlibCnnHogModel,
$this->externalModel
$this->externalModel,
$this->dlibTaguchiHogModel
];
}

Expand Down
Loading

0 comments on commit 7a7da61

Please sign in to comment.