Passed
Push — modelV4 ( 9ab4b0...055b02 )
by Matias
05:14
created

DlibCnnHogModel::getPreferredMimeType()   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 0
Metric Value
cc 1
eloc 1
c 0
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\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 = "CnnHog5";
40
	const FACE_MODEL_DESC = "Default Cnn model with Hog validation, and 5 point landmarks preprictor";
41
	const FACE_MODEL_DOC = "";
42
43
	/** @var IDBConnection */
44
	private $connection;
45
46
	/** @var DlibCnn5Model */
47
	private $dlibCnn5Model;
48
49
	/** @var DlibHogModel */
50
	private $dlibHogModel;
51
52
	/**
53
	 * DlibCnnHogModel __construct.
54
	 *
55
	 * @param IDBConnection $connection
56
	 * @param DlibCnn5Model $dlibCnn5Model
57
	 * @param DlibHogModel $dlibHogModel
58
	 */
59 1
	public function __construct(IDBConnection   $connection,
60
	                            DlibCnn5Model   $dlibCnn5Model,
61
	                            DlibHogModel    $dlibHogModel)
62
	{
63 1
		$this->connection       = $connection;
64 1
		$this->dlibCnn5Model    = $dlibCnn5Model;
65 1
		$this->dlibHogModel     = $dlibHogModel;
66 1
	}
67
68
	public function getId(): int {
69
		return static::FACE_MODEL_ID;
70
	}
71
72
	public function getName(): string {
73
		return static::FACE_MODEL_NAME;
74
	}
75
76
	public function getDescription(): string {
77
		return static::FACE_MODEL_DESC;
78
	}
79
80
	public function getDocumentation(): string {
81
		return static::FACE_MODEL_DOC;
82
	}
83
84
	public function isInstalled(): bool {
85
		if (!$this->dlibCnn5Model->isInstalled())
86
			return false;
87
		if (!$this->dlibHogModel->isInstalled())
88
			return false;
89
		return true;
90
	}
91
92
	public function meetDependencies(string &$error_message): bool {
93
		if (!$this->dlibCnn5Model->meetDependencies($error_message)) {
94
			$error_message .= " This Model depend on Model 1 and must install it.";
95
			return false;
96
		}
97
		if (!$this->dlibHogModel->meetDependencies($error_message)) {
98
			$error_message .= " This Model depend on Model 3 and must install it.";
99
			return false;
100
		}
101
		return true;
102
	}
103
104
	public function getMaximumArea(): int {
105
		return $this->dlibCnn5Model->getMaximumArea();
106
	}
107
108
	public function getPreferredMimeType(): string {
109
		return $this->dlibCnn5Model->getPreferredMimeType();
110
	}
111
112
	public function install() {
113
		if ($this->isInstalled()) {
114
			return;
115
		}
116
117
		// Insert on database and enable it
118
		$qb = $this->connection->getQueryBuilder();
119
		$query = $qb->select($qb->createFunction('COUNT(' . $qb->getColumnName('id') . ')'))
120
			->from('facerecog_models')
121
			->where($qb->expr()->eq('id', $qb->createParameter('id')))
122
			->setParameter('id', $this->getId());
123
		$resultStatement = $query->execute();
124
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
125
		$resultStatement->closeCursor();
126
127
		if ((int)$data[0] <= 0) {
128
			$query = $this->connection->getQueryBuilder();
129
			$query->insert('facerecog_models')
130
			->values([
131
				'id' => $query->createNamedParameter($this->getId()),
132
				'name' => $query->createNamedParameter($this->getName()),
133
				'description' => $query->createNamedParameter($this->getDescription())
134
			])
135
			->execute();
136
		}
137
	}
138
139
	public function open() {
140
		return $this->dlibCnn5Model->open();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->dlibCnn5Model->open() targeting OCA\FaceRecognition\Mode...el\DlibCnnModel::open() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
141
	}
142
143
	public function detectFaces(string $imagePath): array {
144
		$detectedFaces = [];
145
146
		$cnnFaces = $this->dlibCnn5Model->detectFaces($imagePath);
147
		$hogFaces = $this->dlibHogModel->detectFaces($imagePath);
148
149
		foreach ($cnnFaces as $proposedFace) {
150
			$detectedFaces[] = $this->validateFace($proposedFace, $hogFaces);
151
		}
152
153
		return $detectedFaces;
154
	}
155
156
	public function detectLandmarks(string $imagePath, array $rect): array {
157
		return $this->dlibCnn5Model->detectLandmarks($imagePath, $rect);
158
	}
159
160
	public function computeDescriptor(string $imagePath, array $landmarks): array {
161
		return $this->dlibCnn5Model->computeDescriptor($imagePath, $landmarks);
162
	}
163
164
	private function validateFace($proposedFace, $validateFaces) {
165
		foreach ($validateFaces as $validateFace) {
166
			$overlayPercent = $this->getOverlayPercent($proposedFace, $validateFace);
167
			/**
168
			 * The weak link in our default model is the landmark detector that
169
			 * can't align profile faces correctly.
170
			 * The Hog detector also fails and cannot detect these faces.
171
			 *
172
			 * So, if Hog detects it (Overlay > 80%), we know that the landmark
173
			 * detector will do it too.
174
			 * Just return it.
175
			 */
176
			if ($overlayPercent > 0.8) {
177
				return $proposedFace;
178
			}
179
		}
180
181
		/**
182
		 * If Hog don't detect this face, they are probably in profile or rotated.
183
		 * These are bad to compare, so we lower the confidence, to avoid clustering.
184
		 */
185
		$confidence = $proposedFace['detection_confidence'];
186
		$proposedFace['detection_confidence'] = $confidence * 0.9;
187
188
		return $proposedFace;
189
	}
190
191
	private function getOverlayPercent($rectP, $rectV): float {
192
		// Proposed face rect
193
		$leftP = $rectP['left'];
194
		$rightP = $rectP['right'];
195
		$topP = $rectP['top'];
196
		$bottomP = $rectP['bottom'];
197
198
		// Validate face rect
199
		$leftV = $rectV['left'];
200
		$rightV = $rectV['right'];
201
		$topV = $rectV['top'];
202
		$bottomV = $rectV['bottom'];
203
204
		// If one rectangle is on left side of other
205
		if ($leftP > $rightV || $leftV > $rightP)
206
			return 0.0;
207
208
		// If one rectangle is above other
209
		if ($topP > $bottomV || $topV > $bottomP)
210
			return 0.0;
211
212
		// Overlap area.
213
		$leftO = max($leftP, $leftV);
214
		$rightO = min($rightP, $rightV);
215
		$topO = max($topP, $topV);
216
		$bottomO = min($bottomP, $bottomV);
217
218
		// Get area of both rect areas
219
		$areaP = ($rightP - $leftP) * ($bottomP - $topP);
220
		$areaV = ($rightV - $leftV) * ($bottomV - $topV);
221
		$overlapArea = ($rightO - $leftO) * ($bottomO - $topO);
222
223
		// Calculate and return the overlay percent.
224
		return floatval($overlapArea / ($areaP + $areaV - $overlapArea));
225
	}
226
227
}
228