~ubuntu-branches/ubuntu/saucy/digikam/saucy

« back to all changes in this revision

Viewing changes to core/libs/database/faces/facetagseditor.cpp

  • Committer: Package Import Robot
  • Author(s): Felix Geyer, Rohan Garg, Philip Muškovac, Felix Geyer
  • Date: 2011-09-23 18:18:55 UTC
  • mfrom: (1.2.36 upstream)
  • Revision ID: package-import@ubuntu.com-20110923181855-ifs67wxkugshev9k
Tags: 2:2.1.1-0ubuntu1
[ Rohan Garg ]
* New upstream release (LP: #834190)
  - debian/control
    + Build with libqtwebkit-dev
 - debian/kipi-plugins-common
    + Install libkvkontakte required by kipi-plugins
 - debian/digikam
    + Install panoramagui

[ Philip Muškovac ]
* New upstream release
  - debian/control:
    + Add libcv-dev, libcvaux-dev, libhighgui-dev, libboost-graph1.46-dev,
      libksane-dev, libxml2-dev, libxslt-dev, libqt4-opengl-dev, libqjson-dev,
      libgpod-dev and libqca2-dev to build-deps
    + Add packages for kipi-plugins, libmediawiki, libkface, libkgeomap and
      libkvkontakte
  - debian/rules:
    + Don't build with gphoto2 since it doesn't build with it.
  - Add kubuntu_fix_test_linking.diff to fix linking of the dngconverter test
  - update install files
  - update kubuntu_01_mysqld_executable_name.diff for new cmake layout
    and rename to kubuntu_mysqld_executable_name.diff
* Fix typo in digikam-data description (LP: #804894)
* Fix Vcs links

[ Felix Geyer ]
* Move library data files to the new packages libkface-data, libkgeomap-data
  and libkvkontakte-data.
* Override version of the embedded library packages to 1.0~digikam<version>.
* Exclude the library packages from digikam-dbg to prevent file conflicts in
  the future.
* Call dh_install with --list-missing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ============================================================
 
2
 *
 
3
 * This file is a part of digiKam project
 
4
 * http://www.digikam.org
 
5
 *
 
6
 * Date        : 2010-08-08
 
7
 * Description : database interface, also allowing easy manipulation of face tags
 
8
 *
 
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>
 
11
 *
 
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)
 
16
 * any later version.
 
17
 *
 
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.
 
22
 *
 
23
 * ============================================================ */
 
24
 
 
25
#include "facetagseditor.h"
 
26
 
 
27
// KDE includes
 
28
 
 
29
#include <kdebug.h>
 
30
#include <klocale.h>
 
31
 
 
32
// Local includes
 
33
 
 
34
#include "databaseaccess.h"
 
35
#include "databaseconstants.h"
 
36
#include "databaseoperationgroup.h"
 
37
#include "facetags.h"
 
38
#include "imageinfo.h"
 
39
#include "imagetagpair.h"
 
40
#include "tagproperties.h"
 
41
#include "tagscache.h"
 
42
#include "tagregion.h"
 
43
 
 
44
namespace Digikam
 
45
{
 
46
 
 
47
// --- Constructor / Destructor -------------------------------------------------------------------------------------
 
48
 
 
49
FaceTagsEditor::FaceTagsEditor()
 
50
{
 
51
}
 
52
 
 
53
FaceTagsEditor::~FaceTagsEditor()
 
54
{
 
55
}
 
56
 
 
57
// --- Read from database -----------------------------------------------------------------------------------
 
58
 
 
59
int FaceTagsEditor::faceCountForPersonInImage(qlonglong imageid, int tagId ) const
 
60
{
 
61
    ImageTagPair pair(imageid, tagId);
 
62
    return pair.values(ImageTagPropertyName::tagRegion()).size();
 
63
}
 
64
 
 
65
QList<DatabaseFace> FaceTagsEditor::databaseFaces(qlonglong imageId) const
 
66
{
 
67
    return databaseFaces(imageId, DatabaseFace::NormalFaces);
 
68
}
 
69
 
 
70
QList<DatabaseFace> FaceTagsEditor::unconfirmedDatabaseFaces(qlonglong imageId) const
 
71
{
 
72
    return databaseFaces(imageId, DatabaseFace::UnconfirmedTypes);
 
73
}
 
74
 
 
75
QList<DatabaseFace> FaceTagsEditor::databaseFacesForTraining(qlonglong imageId) const
 
76
{
 
77
    return databaseFaces(imageId, DatabaseFace::FaceForTraining);
 
78
}
 
79
 
 
80
QList<DatabaseFace> FaceTagsEditor::confirmedDatabaseFaces(qlonglong imageId) const
 
81
{
 
82
    return databaseFaces(imageId, DatabaseFace::ConfirmedName);
 
83
}
 
84
 
 
85
QList<DatabaseFace> FaceTagsEditor::databaseFaces(qlonglong imageid, DatabaseFace::TypeFlags flags) const
 
86
{
 
87
    QList<DatabaseFace> faces;
 
88
    QStringList attributes = DatabaseFace::attributesForFlags(flags);
 
89
    foreach (const ImageTagPair& pair, faceImageTagPairs(imageid, flags))
 
90
    {
 
91
        foreach (const QString& attribute, attributes)
 
92
        {
 
93
            foreach (const QString& regionString, pair.values(attribute))
 
94
            {
 
95
                TagRegion region(regionString);
 
96
                kDebug() << "rect found as "<< region << "for attribute" << attribute << "tag" << pair.tagId();
 
97
 
 
98
                if (!region.isValid())
 
99
                {
 
100
                    continue;
 
101
                }
 
102
 
 
103
                faces << DatabaseFace(attribute, imageid, pair.tagId(), region);
 
104
            }
 
105
        }
 
106
    }
 
107
 
 
108
    return faces;
 
109
}
 
110
 
 
111
QList<ImageTagPair> FaceTagsEditor::faceImageTagPairs(qlonglong imageid, DatabaseFace::TypeFlags flags) const
 
112
{
 
113
    QList<ImageTagPair> pairs;
 
114
 
 
115
    QStringList attributes = DatabaseFace::attributesForFlags(flags);
 
116
    foreach (const ImageTagPair& pair, ImageTagPair::availablePairs(imageid))
 
117
    {
 
118
        //kDebug() << pair.tagId() << pair.properties();
 
119
        if (!FaceTags::isPerson(pair.tagId()))
 
120
        {
 
121
            continue;
 
122
        }
 
123
 
 
124
        // UnknownName and UnconfirmedName have the same attribute
 
125
        if (!(flags & DatabaseFace::UnknownName) && FaceTags::isTheUnknownPerson(pair.tagId()))
 
126
        {
 
127
            continue;
 
128
        }
 
129
 
 
130
        if (!pair.hasAnyProperty(attributes))
 
131
        {
 
132
            continue;
 
133
        }
 
134
 
 
135
        pairs << pair;
 
136
    }
 
137
 
 
138
    return pairs;
 
139
}
 
140
 
 
141
QList< QRect > FaceTagsEditor::getTagRects(qlonglong imageid) const
 
142
{
 
143
    QList< QRect > rectList;
 
144
 
 
145
    QList<ImageTagPair> pairs = ImageTagPair::availablePairs(imageid);
 
146
    foreach (const ImageTagPair& pair, pairs)
 
147
    {
 
148
        QStringList regions = pair.values(ImageTagPropertyName::tagRegion());
 
149
        foreach (const QString& region, regions)
 
150
        {
 
151
            QRect rect = TagRegion(region).toRect();
 
152
 
 
153
            if (rect.isValid())
 
154
            {
 
155
                rectList << rect;
 
156
            }
 
157
        }
 
158
    }
 
159
 
 
160
    return rectList;
 
161
}
 
162
 
 
163
int FaceTagsEditor::numberOfFaces(qlonglong imageid) const
 
164
{
 
165
    // Use case for this? Depending on a use case, we can think of an optimization
 
166
    int count = 0;
 
167
 
 
168
    QList<ImageTagPair> pairs = ImageTagPair::availablePairs(imageid);
 
169
    foreach (const ImageTagPair& pair, pairs)
 
170
    {
 
171
        QStringList regions = pair.values(ImageTagPropertyName::tagRegion());
 
172
        count += regions.size();
 
173
    }
 
174
 
 
175
    return count;
 
176
}
 
177
 
 
178
// --- Confirming and adding ---
 
179
 
 
180
DatabaseFace FaceTagsEditor::unknownPersonEntry(qlonglong imageId, const TagRegion& region)
 
181
{
 
182
    return unconfirmedEntry(imageId, -1, region);
 
183
}
 
184
 
 
185
DatabaseFace FaceTagsEditor::unconfirmedEntry(qlonglong imageId, int tagId, const TagRegion& region)
 
186
{
 
187
    return DatabaseFace(DatabaseFace::UnconfirmedName, imageId,
 
188
                        tagId == -1 ? FaceTags::unknownPersonTagId() : tagId, region);
 
189
}
 
190
 
 
191
DatabaseFace FaceTagsEditor::confirmedEntry(const DatabaseFace& face, int tagId, const TagRegion& confirmedRegion)
 
192
{
 
193
    return DatabaseFace(DatabaseFace::ConfirmedName, face.imageId(),
 
194
                        tagId == -1 ? face.tagId() : tagId,
 
195
                        confirmedRegion.isValid() ? confirmedRegion : face.region());
 
196
}
 
197
 
 
198
DatabaseFace FaceTagsEditor::addManually(const DatabaseFace& face)
 
199
{
 
200
    ImageTagPair pair(face.imageId(), face.tagId());
 
201
    addFaceAndTag(pair, face, DatabaseFace::attributesForFlags(face.type()), false);
 
202
    return face;
 
203
}
 
204
 
 
205
DatabaseFace FaceTagsEditor::confirmName(const DatabaseFace& face, int tagId, const TagRegion& confirmedRegion)
 
206
{
 
207
    DatabaseFace newEntry = confirmedEntry(face, tagId, confirmedRegion);
 
208
 
 
209
    if (FaceTags::isTheUnknownPerson(newEntry.tagId()))
 
210
    {
 
211
        kDebug() << "Refusing to confirm unknownPerson tag on face";
 
212
        return face;
 
213
    }
 
214
 
 
215
    ImageTagPair pair(newEntry.imageId(), newEntry.tagId());
 
216
 
 
217
    // Remove entry from autodetection
 
218
    if (newEntry.tagId() == face.tagId())
 
219
    {
 
220
        removeFaceAndTag(pair, face, false);
 
221
    }
 
222
    else
 
223
    {
 
224
        ImageTagPair pairOldEntry(face.imageId(), face.tagId());
 
225
        removeFaceAndTag(pairOldEntry, face, true);
 
226
    }
 
227
 
 
228
    // Add new full entry
 
229
    addFaceAndTag(pair, newEntry,
 
230
                  DatabaseFace::attributesForFlags(DatabaseFace::ConfirmedName | DatabaseFace::FaceForTraining),
 
231
                  true);
 
232
 
 
233
    return newEntry;
 
234
}
 
235
 
 
236
DatabaseFace FaceTagsEditor::add(qlonglong imageId, int tagId, const TagRegion& region, bool trainFace)
 
237
{
 
238
    DatabaseFace newEntry(DatabaseFace::ConfirmedName, imageId, tagId, region);
 
239
    add(newEntry, trainFace);
 
240
    return newEntry;
 
241
}
 
242
 
 
243
void FaceTagsEditor::add(const DatabaseFace& face, bool trainFace)
 
244
{
 
245
    ImageTagPair pair(face.imageId(), face.tagId());
 
246
    DatabaseFace::TypeFlags flags = DatabaseFace::ConfirmedName;
 
247
 
 
248
    if (trainFace)
 
249
    {
 
250
        flags |= DatabaseFace::FaceForTraining;
 
251
    }
 
252
 
 
253
    addFaceAndTag(pair, face, DatabaseFace::attributesForFlags(flags), true);
 
254
}
 
255
 
 
256
void FaceTagsEditor::addFaceAndTag(ImageTagPair& pair, const DatabaseFace& face,
 
257
                    const QStringList& properties, bool addTag)
 
258
{
 
259
    FaceTags::ensureIsPerson(face.tagId());
 
260
 
 
261
    QString region = face.region().toXml();
 
262
    foreach (const QString& property, properties)
 
263
    {
 
264
        pair.addProperty(property, region);
 
265
    }
 
266
 
 
267
    if (addTag)
 
268
    {
 
269
        addNormalTag(face.imageId(), face.tagId());
 
270
    }
 
271
}
 
272
 
 
273
// --- Removing faces ---
 
274
 
 
275
void FaceTagsEditor::removeAllFaces(qlonglong imageid)
 
276
{
 
277
    QList<int> tagsToRemove;
 
278
    QStringList attributes = DatabaseFace::attributesForFlags(DatabaseFace::AllTypes);
 
279
    foreach (ImageTagPair pair, faceImageTagPairs(imageid, DatabaseFace::AllTypes))
 
280
    {
 
281
        foreach (const QString& attribute, attributes)
 
282
        {
 
283
            pair.removeProperties(attribute);
 
284
        }
 
285
 
 
286
        if (pair.isAssigned())
 
287
        {
 
288
            tagsToRemove << pair.tagId();
 
289
        }
 
290
    }
 
291
 
 
292
    removeNormalTags(imageid, tagsToRemove);
 
293
}
 
294
 
 
295
void FaceTagsEditor::removeFace(qlonglong imageid, const QRect& rect)
 
296
{
 
297
    QList<int> tagsToRemove;
 
298
    QStringList attributes    = DatabaseFace::attributesForFlags(DatabaseFace::AllTypes);
 
299
    QList<ImageTagPair> pairs = faceImageTagPairs(imageid, DatabaseFace::AllTypes);
 
300
 
 
301
    for (int i=0; i<pairs.size(); ++i)
 
302
    {
 
303
        ImageTagPair& pair = pairs[i];
 
304
        foreach (const QString& attribute, attributes)
 
305
        {
 
306
            foreach (const QString& regionString, pair.values(attribute))
 
307
            {
 
308
                if (rect == TagRegion(regionString).toRect())
 
309
                {
 
310
                    pair.removeProperty(attribute, regionString);
 
311
 
 
312
                    if (pair.isAssigned())
 
313
                    {
 
314
                        tagsToRemove << pair.tagId();
 
315
                    }
 
316
                }
 
317
            }
 
318
        }
 
319
    }
 
320
 
 
321
    removeNormalTags(imageid, tagsToRemove);
 
322
}
 
323
 
 
324
void FaceTagsEditor::removeFace(const DatabaseFace& face)
 
325
{
 
326
    if (face.isNull())
 
327
    {
 
328
        return;
 
329
    }
 
330
 
 
331
    ImageTagPair pair(face.imageId(), face.tagId());
 
332
    removeFaceAndTag(pair, face, true);
 
333
}
 
334
 
 
335
void FaceTagsEditor::removeFaces(const QList<DatabaseFace>& faces)
 
336
{
 
337
    foreach (const DatabaseFace& face, faces)
 
338
    {
 
339
        if (face.isNull())
 
340
        {
 
341
            continue;
 
342
        }
 
343
 
 
344
        ImageTagPair pair(face.imageId(), face.tagId());
 
345
        removeFaceAndTag(pair, face, true);
 
346
    }
 
347
}
 
348
 
 
349
void FaceTagsEditor::removeFaceAndTag(ImageTagPair& pair, const DatabaseFace& face, bool touchTags)
 
350
{
 
351
    QString regionString = TagRegion(face.region().toRect()).toXml();
 
352
    pair.removeProperty(DatabaseFace::attributeForType(face.type()), regionString);
 
353
 
 
354
    if (face.type() == DatabaseFace::ConfirmedName)
 
355
    {
 
356
        pair.removeProperty(DatabaseFace::attributeForType(DatabaseFace::FaceForTraining), regionString);
 
357
    }
 
358
 
 
359
    // Tag assigned and no other entry left?
 
360
    if (touchTags
 
361
        && pair.isAssigned()
 
362
        && !pair.hasProperty(DatabaseFace::attributeForType(DatabaseFace::ConfirmedName)))
 
363
    {
 
364
        removeNormalTag(face.imageId(), pair.tagId());
 
365
    }
 
366
}
 
367
 
 
368
DatabaseFace FaceTagsEditor::changeRegion(const DatabaseFace& face, const TagRegion& newRegion)
 
369
{
 
370
    if (face.isNull() || face.region() == newRegion)
 
371
    {
 
372
        return face;
 
373
    }
 
374
 
 
375
    ImageTagPair pair(face.imageId(), face.tagId());
 
376
    removeFaceAndTag(pair, face, false);
 
377
 
 
378
    DatabaseFace newFace = face;
 
379
    newFace.setRegion(newRegion);
 
380
    addFaceAndTag(pair, newFace, DatabaseFace::attributesForFlags(face.type()), false);
 
381
    return newFace;
 
382
 
 
383
    // todo: the Training entry is cleared.
 
384
}
 
385
 
 
386
// --- Editing normal tags ---
 
387
 
 
388
void FaceTagsEditor::addNormalTag(qlonglong imageId, int tagId)
 
389
{
 
390
    ImageInfo(imageId).setTag(tagId);
 
391
}
 
392
 
 
393
void FaceTagsEditor::removeNormalTag(qlonglong imageId, int tagId)
 
394
{
 
395
    ImageInfo(imageId).removeTag(tagId);
 
396
}
 
397
 
 
398
void FaceTagsEditor::removeNormalTags(qlonglong imageId, QList<int> tagIds)
 
399
{
 
400
    DatabaseOperationGroup group;
 
401
    group.setMaximumTime(200);
 
402
    ImageInfo info(imageId);
 
403
    foreach (int tagId, tagIds)
 
404
    {
 
405
        info.removeTag(tagId);
 
406
        group.allowLift();
 
407
    }
 
408
}
 
409
 
 
410
 
 
411
} // Namespace Digikam