summaryrefslogtreecommitdiff
path: root/src/faces/FaceLocation.vala
blob: 0f4e383e9f89d842e3ff6bf0735cc5263e5d399d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* Copyright 2016 Software Freedom Conservancy Inc.
 *
 * This software is licensed under the GNU Lesser General Public License
 * (version 2.1 or later).  See the COPYING file in this distribution.
 */

// Encapsulate geometry and pixels of a Face
public struct FaceLocationData {
    public string geometry;
    public string vec;
}
public class FaceLocation : Object {
    
    private static Gee.Map<FaceID?, Gee.Map<PhotoID?, FaceLocation>> face_photos_map;
    private static Gee.Map<PhotoID?, Gee.Map<FaceID?, FaceLocation>> photo_faces_map;
    
    private FaceLocationID face_location_id;
    private FaceID face_id;
    private PhotoID photo_id;
    private FaceLocationData face_data;

    private FaceLocation(FaceLocationID face_location_id, FaceID face_id, PhotoID photo_id,
            FaceLocationData face_data) {
        this.face_location_id = face_location_id;
        this.face_id = face_id;
        this.photo_id = photo_id;
        this.face_data = face_data;
    }
    
    public static FaceLocation create(FaceID face_id, PhotoID photo_id, FaceLocationData face_data) {
        FaceLocation face_location = null;
        
        // Test if that FaceLocation already exists (that face in that photo) ...
        Gee.Map<PhotoID?, FaceLocation> photos_map = face_photos_map.get(face_id);
        Gee.Map<FaceID?, FaceLocation> faces_map = photo_faces_map.get(photo_id);
        
        if (photos_map != null && faces_map != null && faces_map.has_key(face_id)) {
            
            face_location = faces_map.get(face_id);
            
            if (face_location.get_serialized_geometry() != face_data.geometry) {
                face_location.set_face_data(face_data);
                
                try {
                    FaceLocationTable.get_instance().update_face_location_face_data(face_location);
                } catch (DatabaseError err) {
                    AppWindow.database_error(err);
                }
            }
            
            return face_location;
        }
        
        // ... or create a new FaceLocation.
        try {
            face_location =
                FaceLocation.add_from_row(
                    FaceLocationTable.get_instance().add(face_id, photo_id, face_data.geometry, face_data.vec));
        } catch (DatabaseError err) {
            AppWindow.database_error(err);
        }
        
        return face_location;
    }
    
    public static void destroy(FaceID face_id, PhotoID photo_id) {
        Gee.Map<PhotoID?, FaceLocation> photos_map = face_photos_map.get(face_id);
        Gee.Map<FaceID?, FaceLocation> faces_map = photo_faces_map.get(photo_id);
        
        assert(photos_map != null);
        assert(faces_map != null);
        
        faces_map.unset(face_id);
        if (faces_map.size == 0)
            photo_faces_map.unset(photo_id);
        
        photos_map.unset(photo_id);
        if (photos_map.size == 0)
            face_photos_map.unset(face_id);
        
        try {
            FaceLocationTable.get_instance().remove_face_from_source(face_id, photo_id);
        } catch (DatabaseError err) {
            AppWindow.database_error(err);
        }
    }
    
    public static FaceLocation add_from_row(FaceLocationRow row) {
        
        FaceLocation face_location =
            new FaceLocation(row.face_location_id, row.face_id, row.photo_id,
                { row.geometry, row.vec });
        
        Gee.Map<PhotoID?, FaceLocation> photos_map = face_photos_map.get(row.face_id);
        if (photos_map == null) {photos_map = new Gee.HashMap<PhotoID?, FaceLocation>
            ((Gee.HashDataFunc)FaceLocation.photo_id_hash, (Gee.EqualDataFunc)FaceLocation.photo_ids_equal);
            face_photos_map.set(row.face_id, photos_map);
        }
        photos_map.set(row.photo_id, face_location);
        
        Gee.Map<FaceID?, FaceLocation> faces_map = photo_faces_map.get(row.photo_id);
        if (faces_map == null) {faces_map = new Gee.HashMap<FaceID?, FaceLocation>
            ((Gee.HashDataFunc)FaceLocation.face_id_hash, (Gee.EqualDataFunc)FaceLocation.face_ids_equal);
            
            photo_faces_map.set(row.photo_id, faces_map);
        }
        faces_map.set(row.face_id, face_location);
        
        return face_location;
    }
    
    public static Gee.Map<FaceID?, FaceLocation>? get_locations_by_photo(Photo photo) {
        return photo_faces_map.get(photo.get_photo_id());
    }
    
    public static Gee.Map<PhotoID?, FaceLocation>? get_locations_by_face(Face face) {
        return face_photos_map.get(face.get_face_id());
    }
    
    public static Gee.Set<PhotoID?>? get_photo_ids_by_face(Face face) {
        Gee.Map<PhotoID?, FaceLocation>? photos_map = face_photos_map.get(face.get_face_id());
        if (photos_map == null)
            return null;
        
        return photos_map.keys;
    }
    
    public static FaceLocation? get_face_location(FaceID face_id, PhotoID photo_id) {
        Gee.Map<FaceID?, FaceLocation>? faces_map = photo_faces_map.get(photo_id);
        if (faces_map == null)
            return null;
        
        return faces_map.get(face_id);
    }
    
    public static bool photo_ids_equal(void *a, void *b) {
        PhotoID *aid = (PhotoID *) a;
        PhotoID *bid = (PhotoID *) b;
    
        return aid->id == bid->id;
    }
    
    public static bool face_ids_equal(void *a, void *b) {
        FaceID *aid = (FaceID *) a;
        FaceID *bid = (FaceID *) b;
    
        return aid->id == bid->id;
    }
    
    public static uint photo_id_hash(void *p) {
        // Rotating XOR hash
        uint8 u8 = (uint8) ((PhotoID *) p)->id;
        uint hash = 0;
        for (int ctr = 0; ctr < (sizeof(int64) / sizeof(uint8)); ctr++) {
            hash = (hash << 4) ^ (hash >> 28) ^ (u8++);
        }
        
        return hash;
    }
    
    public static uint face_id_hash(void *p) {
        // Rotating XOR hash
        uint8 u8 = (uint8) ((FaceID *) p)->id;
        uint hash = 0;
        for (int ctr = 0; ctr < (sizeof(int64) / sizeof(uint8)); ctr++) {
            hash = (hash << 4) ^ (hash >> 28) ^ (u8++);
        }
        
        return hash;
    }

    public static void init(ProgressMonitor? monitor) {
        face_photos_map = new Gee.HashMap<FaceID?, Gee.HashMap<PhotoID?, FaceLocation>>
            ((Gee.HashDataFunc)face_id_hash, (Gee.EqualDataFunc)face_ids_equal);
        photo_faces_map = new Gee.HashMap<PhotoID?, Gee.HashMap<FaceID?, FaceLocation>>
            ((Gee.HashDataFunc)photo_id_hash, (Gee.EqualDataFunc)photo_ids_equal);
        
        // scoop up all the rows at once
        Gee.List<FaceLocationRow?> rows = null;
        try {
            rows = FaceLocationTable.get_instance().get_all_rows();
        } catch (DatabaseError err) {
            AppWindow.database_error(err);
        }
        
        // turn them into FaceLocation objects
        int count = rows.size;
        for (int ctr = 0; ctr < count; ctr++) {
            FaceLocation.add_from_row(rows.get(ctr));
            
            if (monitor != null)
                monitor(ctr, count);
        }
    }
    
    public static void terminate() {
    }
    
    public FaceLocationID get_face_location_id() {
        return face_location_id;
    }
    
    public string get_serialized_geometry() {
        return face_data.geometry;
    }

    public string get_serialized_vec() {
        return face_data.vec;
    }

    public FaceLocationData get_face_data() {
        return face_data;
    }

    public PhotoID get_photo_id() {
        return photo_id;
    }
    
    private void set_face_data(FaceLocationData face_data) {
        this.face_data = face_data;
    }
}