1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : database interface, also allowing easy manipulation of face tags
9
* Copyright (C) 2010-2011 by Aditya Bhatt <adityabhatt1991 at gmail dot com>
10
* Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
12
* This program is free software; you can redistribute it
13
* and/or modify it under the terms of the GNU General
14
* Public License as published by the Free Software Foundation;
15
* either version 2, or (at your option)
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* ============================================================ */
25
#include "facetagseditor.h"
34
#include "databaseaccess.h"
35
#include "databaseconstants.h"
36
#include "databaseoperationgroup.h"
38
#include "imageinfo.h"
39
#include "imagetagpair.h"
40
#include "tagproperties.h"
41
#include "tagscache.h"
42
#include "tagregion.h"
47
// --- Constructor / Destructor -------------------------------------------------------------------------------------
49
FaceTagsEditor::FaceTagsEditor()
53
FaceTagsEditor::~FaceTagsEditor()
57
// --- Read from database -----------------------------------------------------------------------------------
59
int FaceTagsEditor::faceCountForPersonInImage(qlonglong imageid, int tagId ) const
61
ImageTagPair pair(imageid, tagId);
62
return pair.values(ImageTagPropertyName::tagRegion()).size();
65
QList<DatabaseFace> FaceTagsEditor::databaseFaces(qlonglong imageId) const
67
return databaseFaces(imageId, DatabaseFace::NormalFaces);
70
QList<DatabaseFace> FaceTagsEditor::unconfirmedDatabaseFaces(qlonglong imageId) const
72
return databaseFaces(imageId, DatabaseFace::UnconfirmedTypes);
75
QList<DatabaseFace> FaceTagsEditor::databaseFacesForTraining(qlonglong imageId) const
77
return databaseFaces(imageId, DatabaseFace::FaceForTraining);
80
QList<DatabaseFace> FaceTagsEditor::confirmedDatabaseFaces(qlonglong imageId) const
82
return databaseFaces(imageId, DatabaseFace::ConfirmedName);
85
QList<DatabaseFace> FaceTagsEditor::databaseFaces(qlonglong imageid, DatabaseFace::TypeFlags flags) const
87
QList<DatabaseFace> faces;
88
QStringList attributes = DatabaseFace::attributesForFlags(flags);
89
foreach (const ImageTagPair& pair, faceImageTagPairs(imageid, flags))
91
foreach (const QString& attribute, attributes)
93
foreach (const QString& regionString, pair.values(attribute))
95
TagRegion region(regionString);
96
kDebug() << "rect found as "<< region << "for attribute" << attribute << "tag" << pair.tagId();
98
if (!region.isValid())
103
faces << DatabaseFace(attribute, imageid, pair.tagId(), region);
111
QList<ImageTagPair> FaceTagsEditor::faceImageTagPairs(qlonglong imageid, DatabaseFace::TypeFlags flags) const
113
QList<ImageTagPair> pairs;
115
QStringList attributes = DatabaseFace::attributesForFlags(flags);
116
foreach (const ImageTagPair& pair, ImageTagPair::availablePairs(imageid))
118
//kDebug() << pair.tagId() << pair.properties();
119
if (!FaceTags::isPerson(pair.tagId()))
124
// UnknownName and UnconfirmedName have the same attribute
125
if (!(flags & DatabaseFace::UnknownName) && FaceTags::isTheUnknownPerson(pair.tagId()))
130
if (!pair.hasAnyProperty(attributes))
141
QList< QRect > FaceTagsEditor::getTagRects(qlonglong imageid) const
143
QList< QRect > rectList;
145
QList<ImageTagPair> pairs = ImageTagPair::availablePairs(imageid);
146
foreach (const ImageTagPair& pair, pairs)
148
QStringList regions = pair.values(ImageTagPropertyName::tagRegion());
149
foreach (const QString& region, regions)
151
QRect rect = TagRegion(region).toRect();
163
int FaceTagsEditor::numberOfFaces(qlonglong imageid) const
165
// Use case for this? Depending on a use case, we can think of an optimization
168
QList<ImageTagPair> pairs = ImageTagPair::availablePairs(imageid);
169
foreach (const ImageTagPair& pair, pairs)
171
QStringList regions = pair.values(ImageTagPropertyName::tagRegion());
172
count += regions.size();
178
// --- Confirming and adding ---
180
DatabaseFace FaceTagsEditor::unknownPersonEntry(qlonglong imageId, const TagRegion& region)
182
return unconfirmedEntry(imageId, -1, region);
185
DatabaseFace FaceTagsEditor::unconfirmedEntry(qlonglong imageId, int tagId, const TagRegion& region)
187
return DatabaseFace(DatabaseFace::UnconfirmedName, imageId,
188
tagId == -1 ? FaceTags::unknownPersonTagId() : tagId, region);
191
DatabaseFace FaceTagsEditor::confirmedEntry(const DatabaseFace& face, int tagId, const TagRegion& confirmedRegion)
193
return DatabaseFace(DatabaseFace::ConfirmedName, face.imageId(),
194
tagId == -1 ? face.tagId() : tagId,
195
confirmedRegion.isValid() ? confirmedRegion : face.region());
198
DatabaseFace FaceTagsEditor::addManually(const DatabaseFace& face)
200
ImageTagPair pair(face.imageId(), face.tagId());
201
addFaceAndTag(pair, face, DatabaseFace::attributesForFlags(face.type()), false);
205
DatabaseFace FaceTagsEditor::confirmName(const DatabaseFace& face, int tagId, const TagRegion& confirmedRegion)
207
DatabaseFace newEntry = confirmedEntry(face, tagId, confirmedRegion);
209
if (FaceTags::isTheUnknownPerson(newEntry.tagId()))
211
kDebug() << "Refusing to confirm unknownPerson tag on face";
215
ImageTagPair pair(newEntry.imageId(), newEntry.tagId());
217
// Remove entry from autodetection
218
if (newEntry.tagId() == face.tagId())
220
removeFaceAndTag(pair, face, false);
224
ImageTagPair pairOldEntry(face.imageId(), face.tagId());
225
removeFaceAndTag(pairOldEntry, face, true);
228
// Add new full entry
229
addFaceAndTag(pair, newEntry,
230
DatabaseFace::attributesForFlags(DatabaseFace::ConfirmedName | DatabaseFace::FaceForTraining),
236
DatabaseFace FaceTagsEditor::add(qlonglong imageId, int tagId, const TagRegion& region, bool trainFace)
238
DatabaseFace newEntry(DatabaseFace::ConfirmedName, imageId, tagId, region);
239
add(newEntry, trainFace);
243
void FaceTagsEditor::add(const DatabaseFace& face, bool trainFace)
245
ImageTagPair pair(face.imageId(), face.tagId());
246
DatabaseFace::TypeFlags flags = DatabaseFace::ConfirmedName;
250
flags |= DatabaseFace::FaceForTraining;
253
addFaceAndTag(pair, face, DatabaseFace::attributesForFlags(flags), true);
256
void FaceTagsEditor::addFaceAndTag(ImageTagPair& pair, const DatabaseFace& face,
257
const QStringList& properties, bool addTag)
259
FaceTags::ensureIsPerson(face.tagId());
261
QString region = face.region().toXml();
262
foreach (const QString& property, properties)
264
pair.addProperty(property, region);
269
addNormalTag(face.imageId(), face.tagId());
273
// --- Removing faces ---
275
void FaceTagsEditor::removeAllFaces(qlonglong imageid)
277
QList<int> tagsToRemove;
278
QStringList attributes = DatabaseFace::attributesForFlags(DatabaseFace::AllTypes);
279
foreach (ImageTagPair pair, faceImageTagPairs(imageid, DatabaseFace::AllTypes))
281
foreach (const QString& attribute, attributes)
283
pair.removeProperties(attribute);
286
if (pair.isAssigned())
288
tagsToRemove << pair.tagId();
292
removeNormalTags(imageid, tagsToRemove);
295
void FaceTagsEditor::removeFace(qlonglong imageid, const QRect& rect)
297
QList<int> tagsToRemove;
298
QStringList attributes = DatabaseFace::attributesForFlags(DatabaseFace::AllTypes);
299
QList<ImageTagPair> pairs = faceImageTagPairs(imageid, DatabaseFace::AllTypes);
301
for (int i=0; i<pairs.size(); ++i)
303
ImageTagPair& pair = pairs[i];
304
foreach (const QString& attribute, attributes)
306
foreach (const QString& regionString, pair.values(attribute))
308
if (rect == TagRegion(regionString).toRect())
310
pair.removeProperty(attribute, regionString);
312
if (pair.isAssigned())
314
tagsToRemove << pair.tagId();
321
removeNormalTags(imageid, tagsToRemove);
324
void FaceTagsEditor::removeFace(const DatabaseFace& face)
331
ImageTagPair pair(face.imageId(), face.tagId());
332
removeFaceAndTag(pair, face, true);
335
void FaceTagsEditor::removeFaces(const QList<DatabaseFace>& faces)
337
foreach (const DatabaseFace& face, faces)
344
ImageTagPair pair(face.imageId(), face.tagId());
345
removeFaceAndTag(pair, face, true);
349
void FaceTagsEditor::removeFaceAndTag(ImageTagPair& pair, const DatabaseFace& face, bool touchTags)
351
QString regionString = TagRegion(face.region().toRect()).toXml();
352
pair.removeProperty(DatabaseFace::attributeForType(face.type()), regionString);
354
if (face.type() == DatabaseFace::ConfirmedName)
356
pair.removeProperty(DatabaseFace::attributeForType(DatabaseFace::FaceForTraining), regionString);
359
// Tag assigned and no other entry left?
362
&& !pair.hasProperty(DatabaseFace::attributeForType(DatabaseFace::ConfirmedName)))
364
removeNormalTag(face.imageId(), pair.tagId());
368
DatabaseFace FaceTagsEditor::changeRegion(const DatabaseFace& face, const TagRegion& newRegion)
370
if (face.isNull() || face.region() == newRegion)
375
ImageTagPair pair(face.imageId(), face.tagId());
376
removeFaceAndTag(pair, face, false);
378
DatabaseFace newFace = face;
379
newFace.setRegion(newRegion);
380
addFaceAndTag(pair, newFace, DatabaseFace::attributesForFlags(face.type()), false);
383
// todo: the Training entry is cleared.
386
// --- Editing normal tags ---
388
void FaceTagsEditor::addNormalTag(qlonglong imageId, int tagId)
390
ImageInfo(imageId).setTag(tagId);
393
void FaceTagsEditor::removeNormalTag(qlonglong imageId, int tagId)
395
ImageInfo(imageId).removeTag(tagId);
398
void FaceTagsEditor::removeNormalTags(qlonglong imageId, QList<int> tagIds)
400
DatabaseOperationGroup group;
401
group.setMaximumTime(200);
402
ImageInfo info(imageId);
403
foreach (int tagId, tagIds)
405
info.removeTag(tagId);
411
} // Namespace Digikam