nextcloud-custom-apps-face-.../git_facerecognition/lib/Model/ExternalModel/ExternalModel.php
2024-09-03 09:12:12 +05:00

234 lines
6.5 KiB
PHP

<?php
/**
* @copyright Copyright (c) 2021, Matias De lellis <mati86dl@gmail.com>
*
* @author Matias De lellis <mati86dl@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\FaceRecognition\Model\ExternalModel;
use OCA\FaceRecognition\Service\SettingsService;
use OCA\FaceRecognition\Model\IModel;
use OCA\FaceRecognition\Model\Exceptions\UnavailableException;
class ExternalModel implements IModel {
/*
* Model description
*/
const FACE_MODEL_ID = 5;
const FACE_MODEL_NAME = 'ExternalModel';
const FACE_MODEL_DESC = 'External model to separate image processing from the web server';
const FACE_MODEL_DOC = 'https://github.com/matiasdelellis/facerecognition-external-model#run-service';
/** This model practically does not consume memory. Directly set the limits. */
const MINIMUM_MEMORY_REQUIREMENTS = 128 * 1024 * 1024;
/** @var String|null model api endpoint */
private $modelUrl = null;
/** @var String|null model api key */
private $modelApiKey = null;
/** @var String|null preferred mimetype */
private $preferredMimetype = null;
/** @var int maximun image area */
private $maximumImageArea = -1;
/** @var SettingsService */
private $settingsService;
/**
* ExternalModel __construct.
*
* @param SettingsService $settingsService
*/
public function __construct(SettingsService $settingsService)
{
$this->settingsService = $settingsService;
}
public function getId(): int {
return static::FACE_MODEL_ID;
}
public function getName(): string {
return static::FACE_MODEL_NAME;
}
public function getDescription(): string {
return static::FACE_MODEL_DESC;
}
public function getDocumentation(): string {
return static::FACE_MODEL_DOC;
}
public function isInstalled(): bool {
$this->modelUrl = $this->settingsService->getExternalModelUrl();
$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
return !is_null($this->modelUrl) && !is_null($this->modelApiKey);
}
public function meetDependencies(string &$error_message): bool {
if (is_null($this->settingsService->getExternalModelUrl())) {
$error_message = "You still need to configure the URL of the service running the model.";
return false;
}
if (is_null($this->settingsService->getExternalModelApiKey())) {
$error_message = "You still need to configure the API KEY of the service running the model.";
return false;
}
return true;
}
public function getMaximumArea(): int {
if ($this->maximumImageArea < 0) {
throw new \Exception('It seems that the model did not open correctly');
}
return $this->maximumImageArea;
}
public function getPreferredMimeType(): string {
if (is_null($this->preferredMimetype)) {
throw new \Exception('It seems that the model did not open correctly');
}
return $this->preferredMimetype;
}
/**
* @return void
*/
public function install() {
$this->open();
return;
}
/**
* @return void
*/
public function open() {
$this->modelUrl = $this->settingsService->getExternalModelUrl();
$this->modelApiKey = $this->settingsService->getExternalModelApiKey();
$ch = curl_init();
if ($ch === false) {
throw new \Exception('Curl error: unable to initialize curl');
}
curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/open');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (is_bool($response)) {
throw new UnavailableException(curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode !== 200) {
throw new \Exception('External model response /open with error. HTTP status code: ' . $httpCode);
}
$jsonResponse = json_decode($response, true);
$this->maximumImageArea = intval($jsonResponse['maximum_area']);
$this->preferredMimetype = $jsonResponse['preferred_mimetype'];
curl_close($ch);
}
public function detectFaces(string $imagePath, bool $compute = true): array {
$ch = curl_init();
if ($ch === false) {
throw new \Exception('Curl error: unable to initialize curl');
}
$cFile = curl_file_create($imagePath);
$post = array('file'=> $cFile);
curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/detect');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->modelApiKey]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (is_bool($response)) {
throw new UnavailableException(curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode !== 200) {
throw new \Exception('External model response /detect with error. HTTP status code: ' . $httpCode);
}
curl_close($ch);
$jsonResponse = json_decode($response, true);
if (!is_array($jsonResponse))
return [];
if ($jsonResponse['faces-count'] == 0)
return [];
return $jsonResponse['faces'];
}
public function compute(string $imagePath, array $face): array {
$ch = curl_init();
if ($ch === false) {
throw new \Exception('Curl error: unable to initialize curl');
}
$cFile = curl_file_create($imagePath, $this->preferredMimetype, basename($imagePath));
$post = [
'file' => $cFile,
'face' => json_encode($face),
];
curl_setopt($ch, CURLOPT_URL, $this->modelUrl . '/compute');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key:' . $this->modelApiKey]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (is_bool($response)) {
throw new UnavailableException(curl_error($ch));
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode !== 200) {
throw new \Exception('External model response /compute with error. HTTP status code: ' . $httpCode);
}
curl_close($ch);
$jsonResponse = json_decode($response, true);
return $jsonResponse['face'];
}
}