Skip to content
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

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- **🚀 Build your own thing:** FaceRecognition app is just a basic building block. Through FaceRecognition API, you can build your advanced scenarios - automatically add tags to images, connect contacts and persons, share images from specific person… We want to hear your ideas!
]]>
</description>
<version>0.5.15</version>
<version>0.5.16</version>
<licence>agpl</licence>
<author>Matias De lellis</author>
<author>Branko Kokanovic</author>
Expand Down
49 changes: 47 additions & 2 deletions lib/BackgroundJob/Tasks/CreateClustersTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
Expand All @@ -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) {
Expand Down Expand Up @@ -343,4 +353,39 @@ public function mergeClusters(array $oldCluster, array $newCluster): array {
}
return $result;
}

private function fillFaceRelationsFromPersons(string $userId) {
Copy link
Collaborator

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)

$deviation = $this->settingsService->getDeviation();
if (!version_compare(phpversion('pdlib'), '1.0.2', '>=') || ($deviation === 0.0))
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe.. Probably in the next nightly version keep these guards.
But I am seriously thinking to indicate that 1.0.2 will be necessary for the first public release.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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);
}
}
}
}
}

}
68 changes: 68 additions & 0 deletions lib/Db/Relation.php
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;

}
75 changes: 75 additions & 0 deletions lib/Db/RelationMapper.php
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;
}*/

}
66 changes: 66 additions & 0 deletions lib/Migration/Version000516Date20200420003814.php
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) {
}
}