Passed
Push — migrations ( ce91af )
by Matias
05:33
created

MigrateCommand::getFaceRect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 7
nc 1
nop 1
dl 0
loc 8
ccs 0
cts 8
cp 0
crap 2
rs 10
c 1
b 0
f 1
1
<?php
2
/**
3
 * @copyright Copyright (c) 2020, Matias De lellis <[email protected]>
4
 * @copyright Copyright (c) 2019, 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
namespace OCA\FaceRecognition\Command;
25
26
use OCP\IUserManager;
27
28
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Command\Command 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...
29
use Symfony\Component\Console\Input\InputOption;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputOption 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...
30
use Symfony\Component\Console\Input\InputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputInterface 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...
31
use Symfony\Component\Console\Output\OutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Output\OutputInterface 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...
32
33
use Symfony\Component\Console\Helper\ProgressBar;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Helper\ProgressBar 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...
34
35
use OCA\FaceRecognition\Db\Face;
36
use OCA\FaceRecognition\Db\FaceMapper;
37
38
use OCA\FaceRecognition\Db\Image;
39
use OCA\FaceRecognition\Db\ImageMapper;
40
41
use OCA\FaceRecognition\Model\ModelManager;
42
43
use OCA\FaceRecognition\Service\FaceManagementService;
44
use OCA\FaceRecognition\Service\FileService;
45
46
use OCP\Image as OCP_Image;
47
48
class MigrateCommand extends Command {
49
50
	/** @var FaceManagementService */
51
	protected $faceManagementService;
52
53
	/** @var FileService */
54
	protected $fileService;
55
56
	/** @var IUserManager */
57
	protected $userManager;
58
59
	/** @var ModelManager */
60
	protected $modelManager;
61
62
	/** @var FaceMapper */
63
	protected $faceMapper;
64
65
	/** @var ImageMapper Image mapper*/
66
	protected $imageMapper;
67
68
	/**
69
	 * @param FaceManagementService $faceManagementService
70
	 * @param IUserManager $userManager
71
	 */
72
	public function __construct(FaceManagementService $faceManagementService,
73
	                            FileService           $fileService,
74
	                            IUserManager          $userManager,
75
	                            ModelManager          $modelManager,
76
	                            FaceMapper            $faceMapper,
77
	                            ImageMapper           $imageMapper)
78
	{
79
		parent::__construct();
80
81
		$this->faceManagementService = $faceManagementService;
82
		$this->fileService           = $fileService;
83
		$this->userManager           = $userManager;
84
		$this->modelManager          = $modelManager;
85
		$this->faceMapper            = $faceMapper;
86
		$this->imageMapper           = $imageMapper;
87
	}
88
89
	protected function configure() {
90
		$this
91
			->setName('face:migrate')
92
			->setDescription(
93
				'Migrate the faces found in a model and analyze with the current model.')
94
			->addOption(
95
				'model',
96
				'm',
97
				InputOption::VALUE_REQUIRED,
98
				'The identifier number of the model to migrate',
99
				null,
100
			)
101
			->addOption(
102
				'user_id',
103
				'u',
104
				InputOption::VALUE_REQUIRED,
105
				'Migrate data for a given user only. If not given, migrate everything for all users.',
106
				null,
107
			);
108
	}
109
110
	/**
111
	 * @param InputInterface $input
112
	 * @param OutputInterface $output
113
	 * @return int
114
	 */
115
	protected function execute(InputInterface $input, OutputInterface $output) {
116
		// Extract user, if any
117
		//
118
		$userId = $input->getOption('user_id');
119
		if ($userId === null) {
120
			$output->writeln("You must specify the user to migrate");
121
			return 1;
122
		}
123
124
		$user = $this->userManager->get($userId);
125
		if ($user === null) {
126
			$output->writeln("User with id <$userId> is unknown.");
127
			return 1;
128
		}
129
130
		$modelId = $input->getOption('model');
131
		if (is_null($modelId)) {
132
			$output->writeln("You must indicate the ID of the model to migrate");
133
			return 1;
134
		}
135
136
		$model = $this->modelManager->getModel($modelId);
137
		if (is_null($model)) {
138
			$output->writeln("Invalid model Id");
139
			return 1;
140
		}
141
142
		if (!$model->isInstalled()) {
143
			$output->writeln("The model <$modelId> is not installed");
144
			return 1;
145
		}
146
147
		$currentModel = $this->modelManager->getCurrentModel();
148
		$currentModelId = (!is_null($currentModel)) ? $currentModel->getId() : -1;
149
150
		if ($currentModelId === $modelId) {
151
			$output->writeln("The proposed model <$modelId> to migrate must be other than the current one <$currentModelId>");
152
			return 1;
153
		}
154
155
		if (!$this->faceManagementService->hasDataForUser($userId, $modelId)) {
156
			$output->writeln("The proposed model <$modelId> to migrate is empty");
157
			return 1;
158
		}
159
160
		if ($this->faceManagementService->hasDataForUser($userId, $currentModelId)) {
161
			$output->writeln("The current model <$currentModelId> already has data. You cannot migrate to a used model.");
162
			return 1;
163
		}
164
165
		/**
166
		 * MIgrate
167
		 */
168
		$currentModel->open();
169
170
		$oldImages = $this->imageMapper->findAll($userId, $modelId);
171
172
		$progressBar = new ProgressBar($output, count($oldImages));
173
		$progressBar->start();
174
175
		foreach ($oldImages as $oldImage) {
176
			$newImage = $this->migrateImage($oldImage, $userId, $currentModelId);
177
			$oldFaces = $this->faceMapper->findFromFile($userId, $modelId, $newImage->getFile());
178
			if (count($oldFaces) > 0) {
179
				$filePath = $this->getImageFilePath($newImage);
180
				foreach ($oldFaces as $oldFace) {
181
					$this->migrateFace($currentModel, $oldFace, $newImage, $filePath);
182
				}
183
				$this->fileService->clean();
184
			}
185
			$progressBar->advance(1);
186
		}
187
188
		$progressBar->finish();
189
	}
190
191
	private function migrateImage($oldImage, $userId, $modelId): Image {
192
		$image = new Image();
193
194
		$image->setUser($userId);
195
		$image->setFile($oldImage->getFile());
196
		$image->setModel($modelId);
197
		$image->setIsProcessed($oldImage->getIsProcessed());
198
		$image->setError($oldImage->getError());
0 ignored issues
show
Bug introduced by
The method setError() does not exist on OCA\FaceRecognition\Db\Image. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

198
		$image->/** @scrutinizer ignore-call */ 
199
          setError($oldImage->getError());
Loading history...
199
		$image->setLastProcessedTime($oldImage->getLastProcessedTime());
0 ignored issues
show
Bug introduced by
The method setLastProcessedTime() does not exist on OCA\FaceRecognition\Db\Image. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

199
		$image->/** @scrutinizer ignore-call */ 
200
          setLastProcessedTime($oldImage->getLastProcessedTime());
Loading history...
200
		$image->setProcessingDuration($oldImage->getProcessingDuration());
0 ignored issues
show
Bug introduced by
The method setProcessingDuration() does not exist on OCA\FaceRecognition\Db\Image. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

200
		$image->/** @scrutinizer ignore-call */ 
201
          setProcessingDuration($oldImage->getProcessingDuration());
Loading history...
201
202
		return $this->imageMapper->insert($image);
203
	}
204
205
	private function migrateFace($model, $oldFace, $image, $filePath) {
206
		$faceRect = $this->getFaceRect($oldFace);
207
208
		$face = Face::fromModel($image->getId(), $faceRect);
209
210
		$landmarks = $model->detectLandmarks($filePath, $faceRect);
211
		$descriptor = $model->computeDescriptor($filePath, $landmarks);
212
213
		$face->landmarks = $landmarks['parts'];
214
		$face->descriptor = $descriptor;
215
216
		$this->faceMapper->insertFace($face);
217
	}
218
219
	private function getImageFilePath(Image $image): ?string {
220
		$file = $this->fileService->getFileById($image->getFile(), $image->getUser());
221
		if (empty($file)) {
222
			return null;
223
		}
224
225
		$localPath = $this->fileService->getLocalFile($file);
226
227
		$image = new OCP_Image();
228
		$image->loadFromFile($localPath);
229
		if ($image->getOrientation() > 1) {
230
			$tempPath = $this->fileService->getTemporaryFile();
231
			$image->fixOrientation();
232
			$image->save($tempPath, 'image/png');
233
			return $tempPath;
234
		}
235
236
		return $localPath;
237
	}
238
239
	private function getFaceRect(Face $face): array {
240
		$rect = [];
241
		$rect['left'] = (int)$face->getLeft();
242
		$rect['right'] = (int)$face->getRight();
243
		$rect['top'] =  (int)$face->getTop();
244
		$rect['bottom'] = (int)$face->getBottom();
245
		$rect['detection_confidence'] = $face->getConfidence();
246
		return $rect;
247
	}
248
249
}
250