Passed
Push — imaginary ( eaed66...5e9e61 )
by Matias
06:36 queued 12s
created

DlibCnnModel::detectFaces()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

Changes 0
Metric Value
cc 3
eloc 9
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 14
ccs 9
cts 10
cp 0.9
crap 3.009
rs 9.9666
1
<?php
2
/**
3
 * @copyright Copyright (c) 2021, Matias De lellis <[email protected]>
4
 * @copyright Copyright (c) 2018, Branko Kokanovic <[email protected]>
5
 *
6
 * @author Branko Kokanovic <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OCA\FaceRecognition\Model\DlibCnnModel;
26
27
use OCP\IDBConnection;
28
29
use OCA\FaceRecognition\Helper\MemoryLimits;
30
31
use OCA\FaceRecognition\Service\FileService;
32
use OCA\FaceRecognition\Service\ModelService;
33
use OCA\FaceRecognition\Service\SettingsService;
34
35
use OCA\FaceRecognition\Model\IModel;
36
37
class DlibCnnModel implements IModel {
38
39
	/*
40
	 * Model files.
41
	 */
42
	const FACE_MODEL_ID = -1;
43
	const FACE_MODEL_NAME = "";
44
	const FACE_MODEL_DESC = "";
45
	const FACE_MODEL_DOC = "";
46
47
	/** Relationship between image size and memory consumed */
48
	const MEMORY_AREA_RELATIONSHIP = -1;
49
	const MINIMUM_MEMORY_REQUIREMENTS = -1;
50
51
	const FACE_MODEL_BZ2_URLS = array();
52
	const FACE_MODEL_FILES = array();
53
54
	const I_MODEL_DETECTOR = 0;
55
	const I_MODEL_PREDICTOR = 1;
56
	const I_MODEL_RESNET = 2;
57
58
	const PREFERRED_MIMETYPE = 'image/png';
59
60
	/** @var \CnnFaceDetection */
0 ignored issues
show
Bug introduced by
The type CnnFaceDetection 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...
61
	private $cfd;
62
63
	/** @var \FaceLandmarkDetection */
0 ignored issues
show
Bug introduced by
The type FaceLandmarkDetection 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...
64
	private $fld;
65
66
	/** @var \FaceRecognition */
0 ignored issues
show
Bug introduced by
The type FaceRecognition 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...
67
	private $fr;
68
69
	/** @var IDBConnection */
70
	private $connection;
71
72
	/** @var FileService */
73
	private $fileService;
74
75
	/** @var ModelService */
76
	private $modelService;
77
78
	/** @var SettingsService */
79
	private $settingsService;
80
81
82
	/**
83
	 * DlibCnnModel __construct.
84
	 *
85
	 * @param IDBConnection $connection
86
	 * @param FileService $fileService
87
	 * @param ModelService $modelService
88
	 * @param SettingsService $settingsService
89
	 */
90 1
	public function __construct(IDBConnection   $connection,
91
	                            FileService     $fileService,
92
	                            ModelService    $modelService,
93
	                            SettingsService $settingsService)
94
	{
95 1
		$this->connection       = $connection;
96 1
		$this->fileService      = $fileService;
97 1
		$this->modelService     = $modelService;
98 1
		$this->settingsService  = $settingsService;
99
	}
100
101 4
	public function getId(): int {
102 4
		return static::FACE_MODEL_ID;
103
	}
104
105
	public function getName(): string {
106
		return static::FACE_MODEL_NAME;
107
	}
108
109
	public function getDescription(): string {
110
		return static::FACE_MODEL_DESC;
111
	}
112
113
	public function getDocumentation(): string {
114
		return static::FACE_MODEL_DOC;
115
	}
116
117 4
	public function isInstalled(): bool {
118 4
		if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]))
119 1
			return false;
120 3
		if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]))
121
			return false;
122 3
		if (!$this->modelService->modelFileExists($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]))
123
			return false;
124 3
		return true;
125
	}
126
127
	public function meetDependencies(string &$error_message): bool {
128
		if (!extension_loaded('pdlib')) {
129
			$error_message = "The PDlib PHP extension is not loaded";
130
			return false;
131
		}
132
		$availableMemory = $this->settingsService->getAssignedMemory();
133
		if ($availableMemory < 0) {
134
			$error_message = "Seems that you still have to configure the assigned memory for image processing.";
135
			return false;
136
		}
137
		if ($availableMemory < static::MINIMUM_MEMORY_REQUIREMENTS) {
138
			$error_message = "Your system does not meet the minimum memory requirements.";
139
			return false;
140
		}
141
		return true;
142
	}
143
144
	public function getMaximumArea(): int {
145
		$assignedMemory = $this->settingsService->getAssignedMemory();
146
		return intval($assignedMemory/static::MEMORY_AREA_RELATIONSHIP);
147
	}
148
149 4
	public function getPreferredMimeType(): string {
150 4
		return static::PREFERRED_MIMETYPE;
151
	}
152
153
	/**
154
	 * @return void
155
	 */
156 4
	public function install() {
157 4
		if ($this->isInstalled()) {
158 3
			return;
159
		}
160
161
		// Create main folder where install models.
162 1
		$this->modelService->prepareModelFolder($this->getId());
163
164
		/* Download and install models */
165 1
		$detectorModelBz2 = $this->fileService->downloaldFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_DETECTOR]);
166 1
		$this->fileService->bunzip2($detectorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]));
167
168 1
		$predictorModelBz2 = $this->fileService->downloaldFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_PREDICTOR]);
169 1
		$this->fileService->bunzip2($predictorModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));
170
171 1
		$resnetModelBz2 = $this->fileService->downloaldFile(static::FACE_MODEL_BZ2_URLS[self::I_MODEL_RESNET]);
172 1
		$this->fileService->bunzip2($resnetModelBz2, $this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));
173
174
		/* Clean temporary files */
175 1
		$this->fileService->clean();
176
177
		// Insert on database and enable it
178 1
		$qb = $this->connection->getQueryBuilder();
179 1
		$query = $qb->select($qb->createFunction('COUNT(' . $qb->getColumnName('id') . ')'))
180 1
			->from('facerecog_models')
181 1
			->where($qb->expr()->eq('id', $qb->createParameter('id')))
182 1
			->setParameter('id', $this->getId());
183 1
		$resultStatement = $query->execute();
0 ignored issues
show
Deprecated Code introduced by
The function OCP\DB\QueryBuilder\IQueryBuilder::execute() has been deprecated: 22.0.0 Use executeQuery or executeStatement ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

183
		$resultStatement = /** @scrutinizer ignore-deprecated */ $query->execute();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
184 1
		$data = $resultStatement->fetch(\PDO::FETCH_NUM);
185 1
		$resultStatement->closeCursor();
186
187 1
		if ((int)$data[0] <= 0) {
188
			$query = $this->connection->getQueryBuilder();
189
			$query->insert('facerecog_models')
0 ignored issues
show
Deprecated Code introduced by
The function OCP\DB\QueryBuilder\IQueryBuilder::execute() has been deprecated: 22.0.0 Use executeQuery or executeStatement ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

189
			/** @scrutinizer ignore-deprecated */ $query->insert('facerecog_models')

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
190
			->values([
191
				'id' => $query->createNamedParameter($this->getId()),
192
				'name' => $query->createNamedParameter($this->getName()),
193
				'description' => $query->createNamedParameter($this->getDescription())
194
			])
195
			->execute();
196
		}
197
	}
198
199
	/**
200
	 * @return void
201
	 */
202 4
	public function open() {
203 4
		$this->cfd = new \CnnFaceDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_DETECTOR]));
204 4
		$this->fld = new \FaceLandmarkDetection($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_PREDICTOR]));
205 4
		$this->fr = new \FaceRecognition($this->modelService->getFileModelPath($this->getId(), static::FACE_MODEL_FILES[self::I_MODEL_RESNET]));
206
	}
207
208 2
	public function detectFaces(string $imagePath, bool $compute = true): array {
209 2
		$faces_detected = $this->cfd->detect($imagePath, 0);
210
211 2
		if (!$compute)
212
			return $faces_detected;
213
214 2
		foreach ($faces_detected as &$face) {
215 1
			$landmarks = $this->fld->detect($imagePath, $face);
216 1
			$descriptor = $this->fr->computeDescriptor($imagePath, $landmarks);
217
218 1
			$face['landmarks'] = $landmarks['parts'];
219 1
			$face['descriptor'] = $descriptor;
220
		}
221 2
		return $faces_detected;
222
	}
223
224
	public function compute(string $imagePath, array $face): array {
225
		$landmarks = $this->fld->detect($imagePath, $face);
226
		$descriptor = $this->fr->computeDescriptor($imagePath, $landmarks);
227
228
		$face['landmarks'] = $landmarks['parts'];
229
		$face['descriptor'] = $descriptor;
230
231
		return $face;
232
	}
233
234
}
235