-
-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proof of concept: Suggest similar person to rename them #262
base: master
Are you sure you want to change the base?
Changes from 1 commit
493fd25
333d649
e73d272
200d3c8
1dc82a3
3431b13
218439c
18b209e
d95067e
db8a672
e726cff
a578af5
f7a85f9
776de52
0b22b79
8189e17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,9 @@ | |
use OCA\FaceRecognition\Db\ImageMapper; | ||
use OCA\FaceRecognition\Db\PersonMapper; | ||
|
||
use OCA\FaceRecognition\Db\Relation; | ||
use OCA\FaceRecognition\Db\RelationMapper; | ||
|
||
use OCA\FaceRecognition\Helper\Euclidean; | ||
|
||
use OCA\FaceRecognition\Service\SettingsService; | ||
|
@@ -48,6 +51,9 @@ class CreateClustersTask extends FaceRecognitionBackgroundTask { | |
/** @var FaceMapper Face mapper*/ | ||
private $faceMapper; | ||
|
||
/** @var RelationMapper Relation mapper*/ | ||
private $relationMapper; | ||
|
||
/** @var SettingsService Settings service*/ | ||
private $settingsService; | ||
|
||
|
@@ -60,13 +66,15 @@ class CreateClustersTask extends FaceRecognitionBackgroundTask { | |
public function __construct(PersonMapper $personMapper, | ||
ImageMapper $imageMapper, | ||
FaceMapper $faceMapper, | ||
RelationMapper $relationMapper, | ||
SettingsService $settingsService) | ||
{ | ||
parent::__construct(); | ||
|
||
$this->personMapper = $personMapper; | ||
$this->imageMapper = $imageMapper; | ||
$this->faceMapper = $faceMapper; | ||
$this->relationMapper = $relationMapper; | ||
$this->settingsService = $settingsService; | ||
} | ||
|
||
|
@@ -207,8 +215,10 @@ private function createClusterIfNeeded(string $userId) { | |
$this->logInfo('Deleted ' . $orphansDeleted . ' persons without faces'); | ||
} | ||
|
||
// Prevents not create/recreate the clusters unnecessarily. | ||
// Fill relation table with new clusters. | ||
$this->fillFaceRelationsFromPersons($userId); | ||
|
||
// Prevents not create/recreate the clusters unnecessarily. | ||
$this->settingsService->setNeedRecreateClusters(false, $userId); | ||
$this->settingsService->setForceCreateClusters(false, $userId); | ||
} | ||
|
@@ -228,7 +238,6 @@ private function getCurrentClusters(array $faces): array { | |
|
||
private function getNewClusters(array $faces): array { | ||
// Create edges for chinese whispers | ||
$euclidean = new Euclidean(); | ||
$sensitivity = $this->settingsService->getSensitivity(); | ||
$min_confidence = $this->settingsService->getMinimumConfidence(); | ||
$edges = array(); | ||
|
@@ -250,6 +259,7 @@ private function getNewClusters(array $faces): array { | |
} | ||
} | ||
} else { | ||
$euclidean = new Euclidean(); | ||
for ($i = 0, $face_count1 = count($faces); $i < $face_count1; $i++) { | ||
$face1 = $faces[$i]; | ||
if ($face1->confidence < $min_confidence) { | ||
|
@@ -343,4 +353,39 @@ public function mergeClusters(array $oldCluster, array $newCluster): array { | |
} | ||
return $result; | ||
} | ||
|
||
private function fillFaceRelationsFromPersons(string $userId) { | ||
$deviation = $this->settingsService->getDeviation(); | ||
if (!version_compare(phpversion('pdlib'), '1.0.2', '>=') || ($deviation === 0.0)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking aloud... should you put some print statement here? So, we can see from logs that it is not getting executed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe.. Probably in the next nightly version keep these guards. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking aloud... should you put some print statement here? So, we can see from logs that it is not getting executed? |
||
return; | ||
|
||
$sensitivity = $this->settingsService->getSensitivity(); | ||
$modelId = $this->settingsService->getCurrentFaceModel(); | ||
|
||
// Get the representative faces of each person | ||
$mainFaces = array(); | ||
$persons = $this->personMapper->findAll($userId, $modelId); | ||
foreach ($persons as $person) { | ||
$mainFaces[] = $this->faceMapper->findRepresentativeFromPerson($userId, $person->getId(), $sensitivity, $modelId); | ||
} | ||
|
||
// Get similar faces taking into account the deviation and insert new relations | ||
for ($i = 0, $face_count1 = count($mainFaces); $i < $face_count1; $i++) { | ||
$face1 = $mainFaces[$i]; | ||
for ($j = $i+1, $face_count2 = count($mainFaces); $j < $face_count2; $j++) { | ||
$face2 = $mainFaces[$j]; | ||
$distance = dlib_vector_length($face1->descriptor, $face2->descriptor); | ||
if ($distance < ($sensitivity + $deviation)) { | ||
$relation = new Relation(); | ||
$relation->setFace1($face1->getId()); | ||
$relation->setFace2($face2->getId()); | ||
$relation->setState(RELATION::PROPOSED); | ||
if (!$this->relationMapper->exists($relation)) { | ||
$this->relationMapper->insert($relation); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
/** | ||
* @copyright Copyright (c) 2020, 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\Db; | ||
|
||
use OCP\AppFramework\Db\Entity; | ||
|
||
/** | ||
* Relation represents one relation beetwen two faces | ||
* | ||
* @method int getFace1() | ||
* @method int getFace2() | ||
* @method int getState() | ||
* @method void setFace1(int $face1) | ||
* @method void setFace2(int $face2) | ||
* @method void setState(int $state) | ||
*/ | ||
class Relation extends Entity { | ||
|
||
/** | ||
* Possible values of the state of a face relation | ||
*/ | ||
public const PROPOSED = 0; | ||
public const ACCEPTED = 1; | ||
public const REJECTED = 2; | ||
|
||
/** | ||
* Face id of a face of a person related with $face2 | ||
* | ||
* @var int | ||
* */ | ||
protected $face1; | ||
|
||
/** | ||
* Face id of a face of a person related with $face1 | ||
* | ||
* @var int | ||
* */ | ||
protected $face2; | ||
|
||
/** | ||
* State of two face relation. These are proposed, and can be accepted | ||
* as as the same person, or rejected. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add as comment - "Rejected relations are never proposed again" |
||
* | ||
* @var int | ||
* */ | ||
protected $state; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
/** | ||
* @copyright Copyright (c) 2020, 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\Db; | ||
|
||
use OC\DB\QueryBuilder\Literal; | ||
|
||
use OCP\IDBConnection; | ||
use OCP\AppFramework\Db\QBMapper; | ||
use OCP\AppFramework\Db\DoesNotExistException; | ||
use OCP\DB\QueryBuilder\IQueryBuilder; | ||
|
||
class RelationMapper extends QBMapper { | ||
|
||
public function __construct(IDBConnection $db) { | ||
parent::__construct($db, 'facerecog_relations', '\OCA\FaceRecognition\Db\Relation'); | ||
} | ||
|
||
public function exists(Relation $relation): bool { | ||
$qb = $this->db->getQueryBuilder(); | ||
$query = $qb | ||
->select(['id']) | ||
->from($this->getTableName()) | ||
->where($qb->expr()->eq('face1', $qb->createParameter('face1'))) | ||
->andWhere($qb->expr()->eq('face2', $qb->createParameter('face2'))) | ||
->setParameter('face1', $relation->getFace1()) | ||
->setParameter('face2', $relation->getFace2()); | ||
|
||
$resultStatement = $query->execute(); | ||
$row = $resultStatement->fetch(); | ||
$resultStatement->closeCursor(); | ||
|
||
return ($row !== false); | ||
} | ||
|
||
/*public function findFromPerson(string $userId, int $personId, int $model, int $state = null): array { | ||
$qb = $this->db->getQueryBuilder(); | ||
$qb->select('r.id', 'r.face1', 'r.face1', 'r.state') | ||
->from($this->getTableName(), 'r') | ||
->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id')) | ||
->innerJoin('f', 'facerecog_images' ,'i', $qb->expr()->eq('f.image', 'i.id')) | ||
->where($qb->expr()->eq('user', $qb->createNamedParameter($userId))) | ||
->andWhere($qb->expr()->eq('person', $qb->createNamedParameter($personId))) | ||
->andWhere($qb->expr()->eq('model', $qb->createNamedParameter($model))); | ||
|
||
if (!is_null($state)) { | ||
$qb->andWhere($qb->expr()->eq('state', $qb->createNamedParameter($state))); | ||
} | ||
|
||
$relations = $this->findEntities($qb); | ||
|
||
return $relations; | ||
}*/ | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OCA\FaceRecognition\Migration; | ||
|
||
use Closure; | ||
use OCP\DB\ISchemaWrapper; | ||
use OCP\Migration\SimpleMigrationStep; | ||
use OCP\Migration\IOutput; | ||
|
||
class Version000516Date20200420003814 extends SimpleMigrationStep { | ||
|
||
/** | ||
* @param IOutput $output | ||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` | ||
* @param array $options | ||
*/ | ||
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { | ||
} | ||
|
||
/** | ||
* @param IOutput $output | ||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` | ||
* @param array $options | ||
* @return null|ISchemaWrapper | ||
*/ | ||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { | ||
/** @var ISchemaWrapper $schema */ | ||
$schema = $schemaClosure(); | ||
|
||
if (!$schema->hasTable('facerecog_relations')) { | ||
$table = $schema->createTable('facerecog_relations'); | ||
$table->addColumn('id', 'integer', [ | ||
'autoincrement' => true, | ||
'notnull' => true, | ||
'unsigned' => true, | ||
]); | ||
$table->addColumn('face1', 'integer', [ | ||
'notnull' => true, | ||
'length' => 4, | ||
]); | ||
$table->addColumn('face2', 'integer', [ | ||
'notnull' => true, | ||
'length' => 4, | ||
]); | ||
$table->addColumn('state', 'integer', [ | ||
'notnull' => true, | ||
'length' => 4, | ||
]); | ||
|
||
$table->setPrimaryKey(['id']); | ||
$table->addIndex(['face1'], 'relation_faces_1_idx'); | ||
$table->addIndex(['face2'], 'relation_faces_2_idx'); | ||
} | ||
return $schema; | ||
} | ||
|
||
/** | ||
* @param IOutput $output | ||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` | ||
* @param array $options | ||
*/ | ||
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider passing $modelId here - less complexity in this method and feels more natural (to me)