Bug 374225

Summary: Add possibility to remove face identities by removing tag and remove person tags but preserving the tags themselves [patch]
Product: [Applications] digikam Reporter: Simon <freisim93>
Component: Faces-WorkflowAssignee: Digikam Developers <digikam-bugs-null>
Status: RESOLVED FIXED    
Severity: wishlist CC: caulier.gilles, mario.frank, metzpinguin
Priority: NOR    
Version: 5.4.0   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed In: 5.5.0
Sentry Crash Report:
Attachments: Possibility to remove face identity along with face tag
The patch - includes Simons patch.
Mario's patch with changes and minus my patch
New patch for removing face tags

Description Simon 2016-12-27 20:07:40 UTC
When deleting a face-tag in the people part of the left sidebar a new dialog asks whether the connected face identity should be removed from the database. 

Use case:
If a person was face-tagged by mistake or is no longer of interest, removing the face tags from images and deleting the tag itself does not remove this person from the face database. Thus when face recognition is done again, there will be new proposed face-tags for the delete person. The only way to get rid of it is to remove all training data. With this patch only a specific identity (and its training data) can be removed.

Detail about implementation:
The methods identityForTags and tagForIdentity of FaceUtils should probably be static, shouldn't it?
They don't use any other class members. I had to create an instance of FaceUtils just to call one of these methods. The only place one of these methods is used in existing codes the same has been done. That seems unnecessary and wasteful. Or is this a normal practice in c++?
Comment 1 Simon 2016-12-27 20:08:40 UTC
Created attachment 103026 [details]
Possibility to remove face identity along with face tag

Forgot the patch.
Comment 2 caulier.gilles 2016-12-27 20:21:49 UTC
Why forgot the patch ???

Gilles Caulier
Comment 3 Simon 2016-12-27 20:42:44 UTC
> --- Comment #2 from caulier.gilles@gmail.com ---
> Why forgot the patch ???
>
> Gilles Caulier
>
Sorry, that was misleading, everything is fine.
I forgot to add the patch to the initial comment, the same as with the
usual follow up email when you forget your attachments. However with the
KDE bug tracker it is normal to add attachments in an additional
comment, so my remark was completely unnecessary.
Comment 4 Mario Frank 2017-01-10 16:36:13 UTC
Hi Simon,

I would go even further. Removing the identity when deleting the tag is definitely a good idea. But I see another use-case that hit me multiple times.
I assigned a tag as face erroneously to some image. But removing the face from the image did not delete the ImageProperty that states that this tag is a person tag. The bug https://bugs.kde.org/show_bug.cgi?id=262168 is somehow connected to this.

Thus, deleting a person tag or a list of person tags but preserving them as ordinary tags is helpful to clean the DB. Otherwise, users would have to dig into the DB data which is not really nice for most of them.

I introduced a new context menu item "Delete People Tag" which works for singleton people tags and also for lists.
This action removes the autodetected face and region property for all images for the people tags and if wished (by confirmation) also the connection between the tag and the image, i.e. untaggs the image.
Also, the person, faceEngineId and faceEngineUuid property of the tags is removed and if existent, the Identity from the recognition.db. The recognition db entry is found by the uuid.

By the way, I had some TagProperties entries with property kfaceId and kfaceUuid, but did not find that in source code. May it be that the properties were renamed?
Comment 5 Mario Frank 2017-01-10 16:37:44 UTC
Created attachment 103331 [details]
The patch - includes Simons patch.
Comment 6 caulier.gilles 2017-01-10 17:19:12 UTC
>By the way, I had some TagProperties entries with property kfaceId and >kfaceUuid, but did not find that in source code. May it be that the properties >were renamed?

Yes exactly. kface* is the legacy of libkface. These properties are obsolete and come certain from DK4. These value can be dropped if they exists.

Gilles Caulier
Comment 7 Mario Frank 2017-01-11 17:16:55 UTC
Okay, I thought so.
Should I commit the changes?
Comment 8 caulier.gilles 2017-01-11 18:02:08 UTC
I don't yet tested the patch. I will do it after to complete my external similarity search test.

Maik, Simon, Can you test this patch please ?

Gilles
Comment 9 Maik Qualmann 2017-01-11 18:52:59 UTC
Patch looks good, people are removed from the DB. I think the 2. QMessageBox should have Yes and No as a possibility, instead of Yes and Cancel. Commit this, small code polishes make we later.

Maik
Comment 10 Simon 2017-01-11 20:09:28 UTC
Can you hold of from committing for tonight?
My part is not suitable and I have some almost finished changes and
questions still. I will detail it later this evening.
Comment 11 Simon 2017-01-11 21:20:21 UTC
Created attachment 103361 [details]
Mario's patch with changes and minus my patch

I should have written that I don't consider my patch suitable anymore, for the reasons you stated, because it prompts the user for confirmation for every deleted tag when multiple face tags are deleted and it doesn't remove the actual tag regions from faces.

The attached patch removes my part, as Mario's part is self contained and mine not ready to be pushed. I also introduced the following changes:
The slotMultipleFaceTagDel and slotFaceTagDelete methods are mostly redundant, so I replaced the latter with a call to the former. This introduces a tiny overhead, which is IMO worth it for less duplicity and thus easier maintenance.
I also did some purely cosmetic changes (mostly line breaks). If you actually prefer long lines, please tell me and I will stop doing this in the future.

Some conceptual questions:
The naming "Delete person tag" could be misunderstood as actually deleting the tag, not just its "affiliation" to a face. Something like "Delete face (identity)"/"Remove person from tag" would IMO be clearer, but it doesn't completely convince me either. I think it is necessary to clarify this, any ideas?
To implement the part I already implemented (incompletely): My preferred option would be to extend the existing function that removes a tag to also remove the face identity and regions when the deleted tag is a person tag, also if the people sidebar is not active. This seems the cleanest way, as otherwise we have information "hanging around" in the db and metadata, that is not accessible from the UI (the user can still keep this stuff). The downside: It adds even more potential popup questions the user has to answer. Opinions?
Comment 12 caulier.gilles 2017-01-11 21:40:36 UTC
Some conceptual questions:
>The naming "Delete person tag" could be misunderstood as actually deleting the >tag, not just its "affiliation" to a face. Something like "Delete face
>(identity)"/"Remove person from tag" would IMO be clearer, but it doesn't >completely convince me either. I think it is necessary to clarify this, any >ideas?

In all case, the action title must be short in all case. Sometime it's complex to found the right words. By chance tooltip ans whatthis feature can help to describe with long phrase an action not well defined.

In this case, i vote for "Remove Face Tag" or something like that. "Face Tags" is used everywhere in GUI, not identity. In fact we ifentify a persone by a face, so the term is the most right to use here.  

>To implement the part I already implemented (incompletely): My preferred >option would be to extend the existing function that removes a tag to also >remove the face identity and regions when the deleted tag is a person tag, >also if the people sidebar is not active. This seems the cleanest way, as >otherwise we have information "hanging around" in the db and metadata, that is >not accessible from the UI (the user can still keep this stuff). The downside: >It adds even more potential popup questions the user has to answer. Opinions?

Yes this is the right way. But take a care this have side efeect with images metadata to sync with DB contents :

1/ The user must do it himself trough Maintenance tool
2/ The user enable to "Use Lazy Synchronisation" option from metadata settings panel to place items to synchronize in a queue. This one is processed when user use Caption sidebar tab and options on the bottom or at end of digiKam session.
3/ All items are all processed when face tag is removed.

In all cases, processing can take a while.

Gilles
Comment 13 Simon 2017-01-11 23:03:46 UTC
(In reply to caulier.gilles from comment #12)
> Yes this is the right way. But take a care this have side efeect with images
> metadata to sync with DB contents :
> 
> 1/ The user must do it himself trough Maintenance tool
> 2/ The user enable to "Use Lazy Synchronisation" option from metadata
> settings panel to place items to synchronize in a queue. This one is
> processed when user use Caption sidebar tab and options on the bottom or at
> end of digiKam session.
> 3/ All items are all processed when face tag is removed.
> 

I didn't think about that. This patch leaves the database and metadata out of sync. This should never happen on purpose, right?
Actually the existing slotTagDelete method does that too. It uses AlbumManager's deleteTAlbum method and I don't see any connection to file metadata and testing confirms, that the tag is only removed from database. This means the user has to do your point 1): manually sync through maintenance.

This is a bug, isn't it? No matter for what reason a tag is altered on an image, if digikam is configured to write tags to metadata this should always be done.
Comment 14 caulier.gilles 2017-01-12 05:19:20 UTC
>I didn't think about that. This patch leaves the database and metadata out of >sync. This should never happen on purpose, right?

yes

>This is a bug, isn't it? No matter for what reason a tag is altered on an >image, if digikam is configured to write tags to metadata this should always >be done.

yes it is.

Gilles
Comment 15 Mario Frank 2017-01-12 06:27:52 UTC
Simon, polishing my patches is completely okay.
Just to be safe. Deleting the region, the identity and the connection from tag to image is okay?
Should I sync the metadata or not? What should I adopt in my patch?
Comment 16 Simon 2017-01-12 09:10:18 UTC
> --- Comment #15 from Mario Frank <mario.frank@uni-potsdam.de> ---
> Simon, polishing my patches is completely okay.
> Just to be safe. Deleting the region, the identity and the connection from tag
> to image is okay?
I think deleting the region and identity by default, and asking about
the tag is a sane behaviour.
> Should I sync the metadata or not? What should I adopt in my patch?
>
Yes, metadata should be synced (always). However this is not done in
many (if not all, I didn't check) methods of tagmodificationhelper. The
other methods delete tags via AlbumManager and looking at the right tag
sidebar, syncing metadata involves some fileworker interfaces. So this
seems quite a big and separate fix that is not directly related to this.
I discuss/solve that part in a new issue.

Do you agree on Gilles proposition to change "Delete person tag" to
"Remove Face Tag"?

Another question: The patch introduced a new method with an sql query.
In the existing code such queries are sometimes done directly via
execSql, at other places via get-/exec- DBAction, so from
dbconfig.xml.cmake.in .
What decides whether a query gets into dbconfig.xml.cmake.in or whether
it doesn't?

And only for my education, so ignore at will:
In the tagmodificationhelper methods, arguments are sometimes passed by
reference (lists), sometimes not (single album) while both underlying
types are pointers. Any particular reason for that?
Comment 17 Mario Frank 2017-01-12 10:32:32 UTC
(In reply to Simon from comment #16)
> > --- Comment #15 from Mario Frank <mario.frank@uni-potsdam.de> ---
> > Simon, polishing my patches is completely okay.
> > Just to be safe. Deleting the region, the identity and the connection from tag
> > to image is okay?
> I think deleting the region and identity by default, and asking about
> the tag is a sane behaviour.
Okay.
> > Should I sync the metadata or not? What should I adopt in my patch?
> >
> Yes, metadata should be synced (always). However this is not done in
> many (if not all, I didn't check) methods of tagmodificationhelper. The
> other methods delete tags via AlbumManager and looking at the right tag
> sidebar, syncing metadata involves some fileworker interfaces. So this
> seems quite a big and separate fix that is not directly related to this.
> I discuss/solve that part in a new issue.
Okay, I will look how to sync the metadata.
> 
> Do you agree on Gilles proposition to change "Delete person tag" to
> "Remove Face Tag"?
Yes, I agree. I will adopt that.
> 
> Another question: The patch introduced a new method with an sql query.
> In the existing code such queries are sometimes done directly via
> execSql, at other places via get-/exec- DBAction, so from
> dbconfig.xml.cmake.in .
> What decides whether a query gets into dbconfig.xml.cmake.in or whether
> it doesn't?
I cannot give a statement to this one.
> 
> And only for my education, so ignore at will:
> In the tagmodificationhelper methods, arguments are sometimes passed by
> reference (lists), sometimes not (single album) while both underlying
> types are pointers. Any particular reason for that?
That's not easy to explain since there may be many reasons. Some are technical, some
ideological.

The difference is subtile. While complex (big) structures like single albums are handled in heap a list of 
X (e.g. pointers) is usually located in the stack.

Consider that.
A list of pointers with each pointer being 64 bit aka 8 byte does not take much space.
Even if you store 100000, it's only 800000 byte aka 800 kilobyte. that's okay.
But passing a list of this size by copy is expensive. Thus, you need a reference, to 
reduce the overhead. Also, you can modify the list in the called function. 
Whether you take a pointer or a ref does not make a difference (both collapse to 8 byte).
But refs cannot be null, while pointers can. And if you know that you have an at least empty list,
pointers do not make sense here.
Passing a const pointer only forbids the manipulation of the pointer, but not the contents of the object.
Passing a const ref list forbids altering the list. This is far more secure than passing a const pointer.

Also, using pointers may indicate for other developers that the passed object is located in heap which may not
be always the case. The developer could try to deallocate the object stored in stack which will impose errors.
Though I do not think that this would compile.
Comment 18 Mario Frank 2017-01-12 16:15:18 UTC
Created attachment 103373 [details]
New patch for removing face tags

Okay, I had to fix the dialog texts and some other stuff.
Now, the actions are called "Remove Face Tag" and "Remove Face Tags", respectively.
The dialog for untagging the tagged images after removing the face property has now Yes and No as options. 
The dialogs now explicitely state which face tags are still connected to some images and which face tags have subtags that are face tags, too. 
I also set What's this texts but they are not shown which is weird.

Furthermore, in people sidebar, only "Remove Face Tag" and "Remove Face Tags" can be selected as actions. 
I removed "Delete Tag" and "Delete Tags" since these actions should be located only in tags sidebar. 
Otherwise, users may be confused about the semantics. And I think we should keep functionality where it fits best semantically.
Thus, delete tags in tags sidebar and remove faces in people sidebar.

Finally, I sync the tags metadata to the files - only the tags metadata.
I get the image info for the files, load the metadata with metadata hub and use the function writeTags.
I hope this is an appropriate way.
I tested this functionality with showFoto and it worked.

So,
Please take a look.
Comment 19 caulier.gilles 2017-01-14 16:57:04 UTC
To Simon, from comment #16

"Another question: The patch introduced a new method with an sql query.
In the existing code such queries are sometimes done directly via
execSql, at other places via get-/exec- DBAction, so from
dbconfig.xml.cmake.in .
What decides whether a query gets into dbconfig.xml.cmake.in or whether
it doesn't?"

==> SQL code can be portable between SQL engines. Currently mysql, mariadb, and sqlite. This is true for simple queries in tables.

For complex queries, especially which touch the dB table structures, data join, or other SQL love, we need to wrap this code in a XML list with versioning rules, depending of DB structure version. 

So, if your SQL code is simple, and work everywhere, it can still in source code as well, else, it's more complex...
Comment 20 caulier.gilles 2017-01-14 17:00:39 UTC
To Mario, from comment #18:

"Finally, I sync the tags metadata to the files - only the tags metadata.
I get the image info for the files, load the metadata with metadata hub and use the function writeTags.
I hope this is an appropriate way.
I tested this functionality with showFoto and it worked."

This want mean that tag are always sync in DB, and in file metadata if option is turned on, through MetadataHub ? If yes, it's the right way.

I supose that by "testing with Showfoto" want mean to check metadata contents after to change tags inside digiKam. Showfoto do not support database and tags from digiKam.

Gilles
Comment 21 Mario Frank 2017-01-14 18:59:49 UTC
(In reply to caulier.gilles from comment #20)
Hey Gilles,

> To Mario, from comment #18:
> 
> "Finally, I sync the tags metadata to the files - only the tags metadata.
> I get the image info for the files, load the metadata with metadata hub and
> use the function writeTags.
> I hope this is an appropriate way.
> I tested this functionality with showFoto and it worked."
> 
> This want mean that tag are always sync in DB, and in file metadata if
> option is turned on, through MetadataHub ? If yes, it's the right way.
When the user removes some face tag from people sidebar, I ask him if the association from the images
to the tag shall also be removed (if existent). If he confirms that, I delete the association of the
tag to the image and sync the new tags for the images from database to the image.

Like this:
1) Unassign the tag:
imageTagAssociation.unAssignTag();

2) get the modified image info, i.e. the new metadata:

metadataHub.load(info);

3) write the new tags to the image file:
metadataHub.writeTags(info.filePath())

> 
> I supose that by "testing with Showfoto" want mean to check metadata
> contents after to change tags inside digiKam. Showfoto do not support
> database and tags from digiKam.
Exactly. After modifying the image in digiKam, i opened the Image in Showfoto
and check the metadata. Result: everything as expected.
> 
> Gilles
Comment 22 Mario Frank 2017-01-16 09:26:42 UTC
Git commit 511ee541a76d22cef63ac9138dbe6f2cd037f808 by Mario Frank.
Committed on 16/01/2017 at 09:20.
Pushed by mfrank into branch 'master'.

It is now possible to remove tags from people sidebar. This actions are called "Remove Face Tag" and "Remove Face Tags", respectively.
The adoptions check if multiple face tags in the potential subtrees would be removed and asks the user for confirmation.
Also, the adoptions tell the user which face tags are connected to images.
The user can choose to remove not only the face tags but also the connection from the underlying tag to the affected images (untagging).
If he confirms, the metadata is synced to the image files (with writeToMetadata) directly (if lazy sync is off). If lazy sync is activated, the metadata
changes are enqueued.

Furthermore, in people sidebar, only "Remove Face Tag" and "Remove Face Tags" can be selected as actions.
I removed "Delete Tag" and "Delete Tags" since these actions should be located only in tags sidebar.
Otherwise, users may be confused about the semantics. And I think we should keep functionality where it fits best semantically.
Thus, delete tags in tags sidebar and remove faces in people sidebar.

M  +1    -1    NEWS
M  +22   -0    app/utils/contextmenuhelper.cpp
M  +6    -0    app/utils/contextmenuhelper.h
M  +1    -0    app/views/leftsidebarwidgets.cpp
M  +31   -0    libs/database/coredb/coredb.cpp
M  +10   -0    libs/database/coredb/coredb.h
M  +18   -0    libs/facesengine/facedb/facedb.cpp
M  +1    -0    libs/facesengine/facedb/facedb.h
M  +24   -2    libs/tags/tagfolderview.cpp
M  +9    -0    libs/tags/tagfolderview.h
M  +232  -0    libs/tags/tagmodificationhelper.cpp
M  +39   -0    libs/tags/tagmodificationhelper.h

https://commits.kde.org/digikam/511ee541a76d22cef63ac9138dbe6f2cd037f808
Comment 23 Mario Frank 2017-01-16 09:31:32 UTC
(In reply to Mario Frank from comment #21)

> > This want mean that tag are always sync in DB, and in file metadata if
> > option is turned on, through MetadataHub ? If yes, it's the right way.

To make it more precise: The tags are always in sync with DB.
Now I write the metadata after confirmation with writeToMetadata.
I think this is cleaner than with writeTags since writeTags does not evaluate
the lazySync option.
If lazy sync is off, the metadata is written directly to the file.
I tested this with loading the image with showFoto.
If lazySync option is turned on, the affected files are enqueued for later
sync.

I did not close this file since the deletion of metadata in tags sidebar
is still open and part of this file.

Cheers,
Mario
Comment 24 Mario Frank 2017-02-22 15:10:48 UTC
Git commit 2f8ddd42ef62d7aea9e490cdb05ffcc644810c81 by Mario Frank.
Committed on 22/02/2017 at 15:05.
Pushed by mfrank into branch 'master'.

Merged the current state of the garbage collection branch which improves the database cleanup stage of the maintenance
and improves the reactiveness of the maintenance overall. We ported the way items are processed to a queue based method
that can use the CPUs more effectively and does not create thousands of threads.
Related: bug 283062, bug 216895, bug 351658, bug 362023, bug 329353
FIXED-IN: 5.5.0

M  +17   -12   NEWS

https://commits.kde.org/digikam/2f8ddd42ef62d7aea9e490cdb05ffcc644810c81