Completed
Push — master ( 76fe59...4ad739 )
by Matias
17s queued 14s
created

ExternalModel::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2021, 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\ExternalModel;
25
26
use OCA\FaceRecognition\Service\SettingsService;
27
28
use OCA\FaceRecognition\Model\IModel;
29
30
class ExternalModel implements IModel {
31
	/*
32
	 * Model description
33
	 */
34
	const FACE_MODEL_ID = 5;
35
	const FACE_MODEL_NAME = 'ExternalModel';
36
	const FACE_MODEL_DESC = 'External model to separate image processing from the web server';
37
	const FACE_MODEL_DOC = 'https://github.com/matiasdelellis/facerecognition-external-model#run-service';
38
39
	/** This model practically does not consume memory. Directly set the limits. */
40
	const MINIMUM_MEMORY_REQUIREMENTS = 128 * 1024 * 1024;
41
42
	/** @var String|null model api endpoint */
43
	private $modelUrl = null;
44
45
	/** @var String|null model api key */
46
	private $modelApiKey = null;
47
48
	/** @var String|null preferred mimetype */
49
	private $preferredMimetype = null;
50
51
	/** @var int maximun image area */
52
	private $maximumImageArea = -1;
53
54
	/** @var SettingsService */
55
	private $settingsService;
56
57
	/**
58
	 * ExternalModel __construct.
59
	 *
60
	 * @param SettingsService $settingsService
61
	 */
62 1
	public function __construct(SettingsService $settingsService)
63
	{
64 1
		$this->settingsService = $settingsService;
65
	}
66
67
	public function getId(): int {
68
		return static::FACE_MODEL_ID;
69
	}
70
71
	public function getName(): string {
72
		return static::FACE_MODEL_NAME;
73
	}
74
75
	public function getDescription(): string {
76
		return static::FACE_MODEL_DESC;
77
	}
78
79
	public function getDocumentation(): string {
80
		return static::FACE_MODEL_DOC;
81
	}
82
83
	public function isInstalled(): bool {
84
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
85
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
86
		return !is_null($this->modelUrl) && !is_null($this->modelApiKey);
87
	}
88
89
	public function meetDependencies(string &$error_message): bool {
90
		if (is_null($this->settingsService->getExternalModelUrl())) {
91
			$error_message = "You still need to configure the URL of the service running the model.";
92
			return false;
93
		}
94
		if (is_null($this->settingsService->getExternalModelApiKey())) {
95
			$error_message = "You still need to configure the API KEY of the service running the model.";
96
			return false;
97
		}
98
		return true;
99
	}
100
101
	public function getMaximumArea(): int {
102
		if ($this->maximumImageArea < 0) {
103
			throw new \Exception('It seems that the model did not open correctly');
104
		}
105
		return $this->maximumImageArea;
106
	}
107
108
	public function getPreferredMimeType(): string {
109
		if (is_null($this->preferredMimetype)) {
110
			throw new \Exception('It seems that the model did not open correctly');
111
		}
112
		return $this->preferredMimetype;
113
	}
114
115
	/**
116
	 * @return void
117
	 */
118
	public function install() {
119
		$this->open();
120
		return;
121
	}
122
123
	/**
124
	 * @return void
125
	 */
126
	public function open() {
127
		$this->modelUrl = $this->settingsService->getExternalModelUrl();
128
		$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
129
130
		$ch = curl_init();
131
		if ($ch === false) {
132
			throw new \Exception('Curl error: unable to initialize curl');
133
		}
134
135
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/open');
136
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
137
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
138
139
		$response = curl_exec($ch);
140
		if (is_bool($response)) {
141
			throw new \Exception('Cannot connect to external model: ' . curl_error($ch));
142
		}
143
144
		$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
145
		if ($httpCode !== 200) {
146
			throw new \Exception('Can\'t connect with external model. HTTP status code: ' . $httpCode);
147
		}
148
149
		$jsonResponse = json_decode($response, true);
150
151
		$this->maximumImageArea = intval($jsonResponse['maximum_area']);
152
		$this->preferredMimetype = $jsonResponse['preferred_mimetype'];
153
154
		curl_close($ch);
155
	}
156
157
	public function detectFaces(string $imagePath, bool $compute = true): array {
158
		$ch = curl_init();
159
		if ($ch === false) {
160
			throw new \Exception('Curl error: unable to initialize curl');
161
		}
162
163
		$cFile = curl_file_create($imagePath);
164
		$post = array('file'=> $cFile);
165
166
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/detect');
167
		curl_setopt($ch, CURLOPT_POST, true);
168
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
169
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
170
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
171
172
		$response = curl_exec($ch);
173
		if (is_bool($response)) {
174
			throw new \Exception('External model dont response: ' . curl_error($ch));
175
		}
176
177
		curl_close($ch);
178
179
		$jsonResponse = json_decode($response, true);
180
181
		if ($jsonResponse['faces-count'] == 0)
182
			return [];
183
184
		return $jsonResponse['faces'];
185
	}
186
187
	public function compute(string $imagePath, array $face): array {
188
		$ch = curl_init();
189
		if ($ch === false) {
190
			throw new \Exception('Curl error: unable to initialize curl');
191
		}
192
193
		$cFile = curl_file_create($imagePath, $this->preferredMimetype, basename($imagePath));
194
		$post = [
195
			'file' => $cFile,
196
			'face' => json_encode($face),
197
		];
198
199
		curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/compute');
200
		curl_setopt($ch, CURLOPT_POST, true);
201
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
202
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key:' . $this->modelApiKey]);
203
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
204
205
		$response = curl_exec($ch);
206
		if (is_bool($response)) {
207
			throw new \Exception('External model dont response: ' . curl_error($ch));
208
		}
209
210
		curl_close($ch);
211
212
		$jsonResponse = json_decode($response, true);
213
214
		return $jsonResponse['face'];
215
	}
216
217
}
218