Passed
Pull Request — master (#313)
by Matias
07:10 queued 05:26
created

DlibCnnHogModel::getId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2020, 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 OCP\IDBConnection;
27
28
use OCA\FaceRecognition\Helper\FaceRect;
29
30
use OCA\FaceRecognition\Model\IModel;
31
32
use OCA\FaceRecognition\Model\DlibCnnModel\DlibCnn5Model;
33
use OCA\FaceRecognition\Model\DlibHogModel\DlibHogModel;
34
35
class DlibCnnHogModel implements IModel {
36
37
	/*
38
	 * Model files.
39
	 */
40
	const FACE_MODEL_ID = 4;
41
	const FACE_MODEL_NAME = "DlibCnnHog5";
42
	const FACE_MODEL_DESC = "Extends the main model, doing a face validation with the Hog detector";
43
	const FACE_MODEL_DOC = "";
44
45
	/** @var IDBConnection */
46
	private $connection;
47
48
	/** @var DlibCnn5Model */
49
	private $dlibCnn5Model;
50
51
	/** @var DlibHogModel */
52
	private $dlibHogModel;
53
54
	/**
55
	 * DlibCnnHogModel __construct.
56
	 *
57
	 * @param IDBConnection $connection
58
	 * @param DlibCnn5Model $dlibCnn5Model
59
	 * @param DlibHogModel $dlibHogModel
60
	 */
61 1
	public function __construct(IDBConnection   $connection,
62
	                            DlibCnn5Model   $dlibCnn5Model,
63
	                            DlibHogModel    $dlibHogModel)
64
	{
65 1
		$this->connection       = $connection;
66 1
		$this->dlibCnn5Model    = $dlibCnn5Model;
67 1
		$this->dlibHogModel     = $dlibHogModel;
68 1
	}
69
70
	public function getId(): int {
71
		return static::FACE_MODEL_ID;
72
	}
73
74
	public function getName(): string {
75
		return static::FACE_MODEL_NAME;
76
	}
77
78
	public function getDescription(): string {
79
		return static::FACE_MODEL_DESC;
80
	}
81
82
	public function getDocumentation(): string {
83
		return static::FACE_MODEL_DOC;
84
	}
85
86
	public function isInstalled(): bool {
87
		if (!$this->dlibCnn5Model->isInstalled())
88
			return false;
89
		if (!$this->dlibHogModel->isInstalled())
90
			return false;
91
		return true;
92
	}
93
94
	public function meetDependencies(string &$error_message): bool {
95
		if (!$this->dlibCnn5Model->meetDependencies($error_message)) {
96
			$error_message .= " This Model depend on Model 1 and must install it.";
97
			return false;
98
		}
99
		if (!$this->dlibHogModel->meetDependencies($error_message)) {
100
			$error_message .= " This Model depend on Model 3 and must install it.";
101
			return false;
102
		}
103
		return true;
104
	}
105
106
	public function getMaximumArea(): int {
107
		return $this->dlibCnn5Model->getMaximumArea();
108
	}
109
110
	public function getPreferredMimeType(): string {
111
		return $this->dlibCnn5Model->getPreferredMimeType();
112
	}
113
114
	public function install() {
115
		if ($this->isInstalled()) {
116
			return;
117
		}
118
119
		// Insert on database and enable it
120
		$qb = $this->connection->getQueryBuilder();
121
		$query = $qb->select($qb->createFunction('COUNT(' . $qb->getColumnName('id') . ')'))
122
			->from('facerecog_models')
123
			->where($qb->expr()->eq('id', $qb->createParameter('id')))
124
			->setParameter('id', $this->getId());
125
		$resultStatement = $query->execute();
126
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
127
		$resultStatement->closeCursor();
128
129
		if ((int)$data[0] <= 0) {
130
			$query = $this->connection->getQueryBuilder();
131
			$query->insert('facerecog_models')
132
			->values([
133
				'id' => $query->createNamedParameter($this->getId()),
134
				'name' => $query->createNamedParameter($this->getName()),
135
				'description' => $query->createNamedParameter($this->getDescription())
136
			])
137
			->execute();
138
		}
139
	}
140
141
	public function open() {
142
		$this->dlibCnn5Model->open();
143
	}
144
145
	public function detectFaces(string $imagePath): array {
146
		$detectedFaces = [];
147
148
		$cnnFaces = $this->dlibCnn5Model->detectFaces($imagePath);
149
		if (count($cnnFaces) === 0) {
150
			return $detectedFaces;
151
		}
152
153
		$hogFaces = $this->dlibHogModel->detectFaces($imagePath);
154
155
		foreach ($cnnFaces as $proposedFace) {
156
			$detectedFaces[] = $this->validateFace($proposedFace, $hogFaces);
157
		}
158
159
		return $detectedFaces;
160
	}
161
162
	public function detectLandmarks(string $imagePath, array $rect): array {
163
		return $this->dlibCnn5Model->detectLandmarks($imagePath, $rect);
164
	}
165
166
	public function computeDescriptor(string $imagePath, array $landmarks): array {
167
		return $this->dlibCnn5Model->computeDescriptor($imagePath, $landmarks);
168
	}
169
170
	private function validateFace($proposedFace, $validateFaces) {
171
		foreach ($validateFaces as $validateFace) {
172
			$overlayPercent = FaceRect::getOverlayPercent($proposedFace, $validateFace);
173
			/**
174
			 * The weak link in our default model is the landmark detector that
175
			 * can't align profile or rotate faces correctly.
176
			 *
177
			 * The Hog detector also fails and cannot detect these faces. So, we
178
			 * consider if Hog detector can detect it, to infer when the predictor
179
			 * will give good results.
180
			 *
181
			 * If Hog detects it (Overlay > 35%), we can assume that landmark
182
			 * detector will do it too. In this case, we consider the face valid,
183
			 * and just return it.
184
			 */
185
			if ($overlayPercent >= 0.35) {
186
				return $proposedFace;
187
			}
188
		}
189
190
		/**
191
		 * If Hog don't detect this face, they are probably in profile or rotated.
192
		 * These are bad to compare, so we lower the confidence, to avoid clustering.
193
		 */
194
		$confidence = $proposedFace['detection_confidence'];
195
		$proposedFace['detection_confidence'] = $confidence * 0.8;
196
197
		return $proposedFace;
198
	}
199
200
}
201