Passed
Push — master ( 7063d6...9e7aeb )
by Matias
04:08
created

DlibCnnHogModel   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Test Coverage

Coverage 5.77%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 45
c 4
b 1
f 0
dl 0
loc 139
ccs 3
cts 52
cp 0.0577
rs 10
wmc 22

14 Methods

Rating   Name   Duplication   Size   Complexity  
A meetDependencies() 0 10 3
A getName() 0 2 1
A __construct() 0 5 1
A getId() 0 2 1
A validateFace() 0 28 3
A getPreferredMimeType() 0 2 1
A getDescription() 0 2 1
A detectFaces() 0 15 3
A open() 0 2 1
A isInstalled() 0 6 3
A getDocumentation() 0 2 1
A compute() 0 2 1
A getMaximumArea() 0 2 1
A install() 0 1 1
1
<?php
2
/**
3
 * @copyright Copyright (c) 2020-2023, Matias De lellis <[email protected]>
4
 *
5
 * @author Matias De lellis <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\FaceRecognition\Model\DlibCnnHogModel;
25
26
use OCA\FaceRecognition\Helper\FaceRect;
27
28
use OCA\FaceRecognition\Model\IModel;
29
30
use OCA\FaceRecognition\Model\DlibCnnModel\DlibCnn5Model;
31
use OCA\FaceRecognition\Model\DlibHogModel\DlibHogModel;
32
33
class DlibCnnHogModel implements IModel {
34
35
	/*
36
	 * Model files.
37
	 */
38
	const FACE_MODEL_ID = 4;
39
	const FACE_MODEL_NAME = "DlibCnnHog5";
40
	const FACE_MODEL_DESC = "Extends the main model, doing a face validation with the Hog detector";
41
	const FACE_MODEL_DOC = "https://github.com/matiasdelellis/facerecognition/wiki/Models#model-4";
42
43
	/** @var IDBConnection */
0 ignored issues
show
Bug introduced by
The type OCA\FaceRecognition\Mode...nHogModel\IDBConnection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
44
	private $connection;
0 ignored issues
show
introduced by
The private property $connection is not used, and could be removed.
Loading history...
45
46
	/** @var DlibCnn5Model */
47
	private $dlibCnn5Model;
48
49
	/** @var DlibHogModel */
50
	private $dlibHogModel;
51
52
	/**
53
	 * DlibCnnHogModel __construct.
54
	 *
55
	 * @param DlibCnn5Model $dlibCnn5Model
56
	 * @param DlibHogModel $dlibHogModel
57
	 */
58 1
	public function __construct(DlibCnn5Model   $dlibCnn5Model,
59
	                            DlibHogModel    $dlibHogModel)
60
	{
61 1
		$this->dlibCnn5Model    = $dlibCnn5Model;
62 1
		$this->dlibHogModel     = $dlibHogModel;
63
	}
64
65
	public function getId(): int {
66
		return static::FACE_MODEL_ID;
67
	}
68
69
	public function getName(): string {
70
		return static::FACE_MODEL_NAME;
71
	}
72
73
	public function getDescription(): string {
74
		return static::FACE_MODEL_DESC;
75
	}
76
77
	public function getDocumentation(): string {
78
		return static::FACE_MODEL_DOC;
79
	}
80
81
	public function isInstalled(): bool {
82
		if (!$this->dlibCnn5Model->isInstalled())
83
			return false;
84
		if (!$this->dlibHogModel->isInstalled())
85
			return false;
86
		return true;
87
	}
88
89
	public function meetDependencies(string &$error_message): bool {
90
		if (!$this->dlibCnn5Model->isInstalled()) {
91
			$error_message = "This Model depend on Model 1 and must install it.";
92
			return false;
93
		}
94
		if (!$this->dlibHogModel->isInstalled()) {
95
			$error_message = "This Model depend on Model 3 and must install it.";
96
			return false;
97
		}
98
		return true;
99
	}
100
101
	public function getMaximumArea(): int {
102
		return $this->dlibCnn5Model->getMaximumArea();
103
	}
104
105
	public function getPreferredMimeType(): string {
106
		return $this->dlibCnn5Model->getPreferredMimeType();
107
	}
108
109
	/**
110
	 * @return void
111
	 */
112
	public function install() {
113
		// This model reuses models 1 and 3 and should not install anything.
114
	}
115
116
	/**
117
	 * @return void
118
	 */
119
	public function open() {
120
		$this->dlibCnn5Model->open();
121
	}
122
123
	public function detectFaces(string $imagePath, bool $compute = true): array {
124
		$detectedFaces = [];
125
126
		$cnnFaces = $this->dlibCnn5Model->detectFaces($imagePath);
127
		if (count($cnnFaces) === 0) {
128
			return $detectedFaces;
129
		}
130
131
		$hogFaces = $this->dlibHogModel->detectFaces($imagePath, false);
132
133
		foreach ($cnnFaces as $proposedFace) {
134
			$detectedFaces[] = $this->validateFace($proposedFace, $hogFaces);
135
		}
136
137
		return $detectedFaces;
138
	}
139
140
	public function compute(string $imagePath, array $face): array {
141
		return $this->dlibCnn5Model->compute($imagePath, $face);
142
	}
143
144
	private function validateFace($proposedFace, array $validateFaces) {
145
		foreach ($validateFaces as $validateFace) {
146
			$overlapPercent = FaceRect::overlapPercent($proposedFace, $validateFace);
147
			/**
148
			 * The weak link in our default model is the landmark detector that
149
			 * can't align profile or rotate faces correctly.
150
			 *
151
			 * The Hog detector also fails and cannot detect these faces. So, we
152
			 * consider if Hog detector can detect it, to infer when the predictor
153
			 * will give good results.
154
			 *
155
			 * If Hog detects it (Overlap > 35%), we can assume that landmark
156
			 * detector will do it too. In this case, we consider the face valid,
157
			 * and just return it.
158
			 */
159
			if ($overlapPercent >= 0.35) {
160
				return $proposedFace;
161
			}
162
		}
163
164
		/**
165
		 * If Hog don't detect this face, they are probably in profile or rotated.
166
		 * These are bad to compare, so we lower the confidence, to avoid clustering.
167
		 */
168
		$confidence = $proposedFace['detection_confidence'];
169
		$proposedFace['detection_confidence'] = $confidence * 0.8;
170
171
		return $proposedFace;
172
	}
173
174
}
175