Exemplo n.º 1
0
 def __start_stage(self, count):
     log.info(
         f'Stage {self.__status["state"]} for {count} steps started')
     self.__status['count'] = count
     self.__status['current'] = 0
     self.__status['faces_count'] = 0
     self.__status['faces_per_second'] = 0
     self.__status['starttime'] = time.time()
Exemplo n.º 2
0
def createCacheDB(cfg):
    cachedb_file = cfg.get_path('files', 'cachedb')
    if cachedb_file:
        log.info(f'Using cachedb: {cachedb_file}')
        return CacheDB(cachedb_file)
    else:
        log.info(f'Not using cachedb')
        return None
Exemplo n.º 3
0
    def __save(self):
        log.info('Patterns saving')
        data = {'files': self.__files, 'persons': self.__persons}
        dump = pickle.dumps(data)

        with open(self.__pickle_file, 'wb') as f:
            f.write(dump)

        log.info(f'Patterns done: {self.__pickle_file} ({len(dump)} bytes)')
Exemplo n.º 4
0
 def __check_basename(self, filename):
     basename = os.path.basename(filename)
     if basename in self.__basenames:
         dup_filename = self.fullpath(self.__basenames[basename])
         log.info(f'Duplicate file detected: {basename}')
         self.__remove_file(dup_filename)
         try:
             os.remove(dup_filename)
         except FileNotFoundError:
             log.exception('Skip file removing')
         self.__save()
Exemplo n.º 5
0
 def add_file_data(self, name, filename, data, bad=False):
     out_folder = os.path.join(self.__folder, name)
     if bad:
         out_folder = os.path.join(out_folder, BAD_FOLDERNAME)
     os.makedirs(out_folder, exist_ok=True)
     out_filename = os.path.join(out_folder,
                                 self.__calc_out_filename(filename))
     self.__check_basename(out_filename)
     log.info(f'adding data of {filename} to {out_filename}')
     with open(out_filename, 'wb') as f:
         f.write(data)
Exemplo n.º 6
0
    def recognize_image(self, filename):
        log.info(f'recognize image: {filename}')

        image = tools.LazyImage(filename, self.__max_size)

        encoded_faces = self.encode_faces(image.get())

        if self.__match_faces(encoded_faces):
            return encoded_faces, image
        else:
            return [], None
Exemplo n.º 7
0
 def __analyze_duplicates(self, print_out):
     log.info(f'Analyze duplicates')
     fset = {}
     for f in self.__files:
         f = self.fullpath(f)
         filename = os.path.split(f)[1]
         if filename in fset:
             log.warning(f'Duplicate pattern file: {f} ({fset[filename]})')
             if print_out:
                 print(f)
                 print(fset[filename])
         else:
             fset[filename] = f
Exemplo n.º 8
0
    def reencode_image(self, filename, encoded_faces):
        log.info(f'reencode image: {filename}')

        image = tools.LazyImage(filename, self.__max_size)

        boxes = [f['box'] for f in encoded_faces]
        encodings, landmarks, profile_angles = self.__encoder.encode(
            image.get(), boxes)

        for i in range(len(encoded_faces)):
            encoded_faces[i]['encoding'] = encodings[i]
            encoded_faces[i]['landmarks'] = landmarks[i]
            encoded_faces[i]['profile_angle'] = profile_angles[i]
Exemplo n.º 9
0
 def __create_tags(self):
     for name in self.__names:
         tag = TAG_PREFIX + name
         if not self.__plexdb.tag_exists(tag, plexdb.TAG_TYPE_PHOTO):
             self.__plexdb.create_tag(tag,
                                      plexdb.TAG_TYPE_PHOTO,
                                      commit=False)
             log.info(f'Photo tag "{tag}" added to plex db')
         if not self.__plexdb.tag_exists(tag, plexdb.TAG_TYPE_VIDEO):
             self.__plexdb.create_tag(tag,
                                      plexdb.TAG_TYPE_VIDEO,
                                      commit=False)
             log.info(f'Video tag "{tag}" added to plex db')
     self.__plexdb.commit()
Exemplo n.º 10
0
    def run(self):
        os.nice(10)
        try:
            from face_rec_tools import recdb  # noqa
            from face_rec_tools import config  # noqa
            from face_rec_tools import cachedb  # noqa
            from face_rec_tools import patterns  # noqa
            from face_rec_tools import recognizer  # noqa

            cfg = config.Config(self.__config)

            patterns = patterns.createPatterns(cfg)
            patterns.load()

            db = recdb.RecDB(cfg.get_path('files', 'db'))
            cdb = cachedb.createCacheDB(cfg)
            cuda_memory_limit = int(cfg['processing']['cuda_memory_limit'])

            log.info(f'Run in process: {self.__method}{self.__args}')

            if self.__method == 'recognize_folder':
                tools.cuda_init(cuda_memory_limit)
                recognizer = recognizer.createRecognizer(
                    patterns, cfg, cdb, db, self.__status)
                recognizer.recognize_folder(*self.__args)
            elif self.__method == 'match':
                tools.cuda_init(cuda_memory_limit)
                recognizer = recognizer.createRecognizer(
                    patterns, cfg, cdb, db, self.__status)
                recognizer.match(*self.__args)
            elif self.__method == 'clusterize':
                recognizer = recognizer.createRecognizer(
                    patterns, cfg, cdb, db, self.__status)
                recognizer.clusterize(*self.__args)
            elif self.__method == 'save_faces':
                recognizer = recognizer.createRecognizer(
                    patterns, cfg, cdb, db, self.__status)
                recognizer.save_faces(*self.__args)
            elif self.__method == 'get_faces_by_face':
                tools.cuda_init(cuda_memory_limit)
                recognizer = recognizer.createRecognizer(
                    patterns, cfg, cdb, db, self.__status)
                recognizer.get_faces_by_face(*self.__args)
            log.info(f'Process done: {self.__method}')
            self.__status['state'] = 'done'
        except Exception as ex:
            log.exception(ex)
            self.__status['state'] = 'error'
            self.__status['error'] = str(ex)
Exemplo n.º 11
0
 def update_filepaths(self, oldfolder, newfolder):
     newfiles = self.__filenames_to_dict(
         tools.list_files(newfolder, tools.IMAGE_EXTS))
     oldfiles = self.__filenames_to_dict(self.get_files(oldfolder))
     for name, oldpath in oldfiles.items():
         if name not in newfiles:
             old = os.path.join(oldpath, name)
             log.info(f'removing unexists file: {old}')
             self.remove(old, commit=False)
         elif newfiles[name] != oldpath:
             old = os.path.join(oldpath, name)
             new = os.path.join(newfiles[name], name)
             log.info(f'move file {old} to {new}')
             self.move(old, new, commit=False)
     self.commit()
Exemplo n.º 12
0
 def remove_folder(self, folder):
     if self.__init_stage('remove_folder', locals()):
         return
     count, files_faces = self.__db.get_folder(folder)
     for ff in files_faces:
         log.info(f"remove from DB: {ff['filename']}")
         self.__db.remove(ff['filename'], False)
         if self.__cdb is not None:
             for face in ff['faces']:
                 self.__cdb.remove_face(face['face_id'])
     # delete files without faces
     files = self.__db.get_files(folder)
     for f in files:
         log.info(f"remove image: {f}")
         self.__db.remove(f, False)
     self.__end_stage()
Exemplo n.º 13
0
    def optimize(self):
        encoder = self.__get_encoder()

        # get encodings and reverse to preserve old patterns
        encs, names, files = self.encodings()
        encs.reverse()
        names.reverse()
        files.reverse()

        # convert to numpy array and get length for optimization reasons
        encs = np.array(encs)
        encs_len = len(encs)

        to_remove = []
        while 0 < encs_len:
            log.debug(f'to optimize check: {encs_len}')
            name = names.pop()
            fname = files.pop()

            # numpy array pop()
            enc, encs = encs[-1], encs[:-1]
            encs_len -= 1

            dists = encoder.distance(encs, enc)
            i = 0
            while i < encs_len:
                if dists[i] < self.__threshold_equal:
                    if name != names[i]:
                        fn1 = self.fullpath(fname)
                        fn2 = self.fullpath(files[i])
                        log.warning(
                            f'Different persons {dists[i]} "{fn1}" "{fn2}"')
                    else:
                        to_remove.append(self.fullpath(files[i]))
                        log.info(f'eq: {fname} {files[i]}')

                        names.pop(i)
                        files.pop(i)

                        encs = np.delete(encs, i, axis=0)
                        dists = np.delete(dists, i, axis=0)
                        encs_len -= 1
                i += 1

        self.remove_files(to_remove)
        log.info(f'{len(to_remove)} files was optimized.')
Exemplo n.º 14
0
 def __save_faces(self, files_faces, debug_out_folder):
     for ff in files_faces:
         if self.__step_stage():
             break
         filename = ff['filename']
         log.info(f"save faces from image: {filename}")
         media = tools.load_media(filename,
                                  self.__max_size,
                                  self.__max_video_frames,
                                  self.__video_frames_step)
         debug_out_file_name = self.__extract_filename(filename)
         is_video = tools.get_low_ext(filename) in tools.VIDEO_EXTS
         self.__save_debug_images(
             ff['faces'], media,
             debug_out_folder, debug_out_file_name, is_video=is_video)
         self.__step_stage_face(len(ff['faces']))
     self.__end_stage()
Exemplo n.º 15
0
 def add_files(self, name, filenames, new=False, move=False, bad=False):
     out_folder = os.path.join(self.__folder, name)
     if bad:
         out_folder = os.path.join(out_folder, BAD_FOLDERNAME)
     if new:
         os.makedirs(out_folder, exist_ok=True)
     else:
         if not os.path.exists(out_folder):
             raise Exception(f"Name {name} not exists")
     for filename in filenames:
         filename = self.__calc_filename(filename)
         out_filename = os.path.join(out_folder,
                                     self.__calc_out_filename(filename))
         log.info(f'adding {filename} to {out_filename}')
         shutil.copyfile(filename, out_filename)
         self.__check_basename(out_filename)
         if move:
             os.remove(filename)
Exemplo n.º 16
0
 def __analyze_encodings_size(self, print_out):
     log.info(f'Analyze encodings')
     dct = collections.defaultdict(list)
     for f, (enc, name, time, tp) in self.__files.items():
         f = self.fullpath(f)
         dct[len(enc)].append(f)
     if len(dct) != 1:
         log.warning('Inconsistent encoding: ' + str(dct.keys()))
         max_key = list(dct.keys())[0]
         for key in dct:
             if len(dct[max_key]) < len(dct[key]):
                 max_key = key
         del dct[max_key]
         for lst in dct.values():
             for f in lst:
                 log.warning(f'wrong encoding: {f}')
                 if print_out:
                     print(f)
Exemplo n.º 17
0
    def recognize_video(self, filename):
        log.info(f'recognize video: {filename}')
        video = tools.LazyVideo(filename,
                                self.__max_size,
                                self.__max_video_frames,
                                self.__video_frames_step)

        all_frames = list(video.frames().items())

        batched_encoded_faces = []
        cnt = 0
        while cnt < len(all_frames):
            if self.__step_stage(step=0):
                return [], None

            frame_numbers, frames = zip(
                *all_frames[cnt: cnt + self.__video_batch_size])

            batched_boxes = face_recognition.batch_face_locations(
                list(frames), batch_size=len(frames))

            for image, boxes, frame_num in zip(frames,
                                               batched_boxes,
                                               frame_numbers):
                if self.__step_stage_face(len(boxes)):
                    return [], None
                encodings, landmarks, profile_angles = self.__encoder.encode(
                    image, boxes)
                encoded_faces = [
                    {'encoding': e,
                     'box': b,
                     'frame': frame_num,
                     'landmarks': l,
                     'profile_angle': pa}
                    for e, l, b, pa in zip(encodings, landmarks,
                                           boxes, profile_angles)]
                encoded_faces = self.__filter_encoded_faces(encoded_faces)

                self.__match_faces(encoded_faces)
                batched_encoded_faces += encoded_faces
                cnt += 1

        log.info(f'done {cnt} frames: {filename}')
        return batched_encoded_faces, video
Exemplo n.º 18
0
    def set_tags(self, resync=False):
        log.info(f'Set tags started ({resync})')
        self.__create_tags()

        if resync:
            count, files_faces = self.__recdb.get_all()
        else:
            count, files_faces = self.__recdb.get_unsynced()

        images_count = 0
        faces_count = 0
        for ff in tools.reduce_faces_from_videos(files_faces,
                                                 self.__min_video_face_count):
            filename = ff['filename']
            self.__plexdb.clean_tags(filename,
                                     tag_prefix=TAG_PREFIX,
                                     commit=False)
            tags = []
            for face in ff['faces']:
                name = face['name']
                if name in self.__names:
                    tags.append(TAG_PREFIX + name)

            log.debug(f"sync tags for image: {filename}: " + str(tags))
            if len(tags) != 0:
                ext = tools.get_low_ext(filename)
                if ext in tools.IMAGE_EXTS:
                    self.__plexdb.set_tags(filename,
                                           tags,
                                           plexdb.TAG_TYPE_PHOTO,
                                           commit=False)
                elif ext in tools.VIDEO_EXTS:
                    self.__plexdb.set_tags(filename,
                                           tags,
                                           plexdb.TAG_TYPE_VIDEO,
                                           commit=False)
            self.__recdb.mark_as_synced(filename, commit=False)
            images_count += 1
            faces_count += len(tags)
        self.__plexdb.commit()
        self.__recdb.commit()

        log.info(f'Set tags done: images={images_count} faces={faces_count}')
Exemplo n.º 19
0
 def __analyze_landmarks(self, print_out):
     log.info(f'Analyze landmarks')
     for f in self.__files:
         f = self.fullpath(f)
         descr = tools.load_face_description(f)[0]
         if descr is None:
             log.warning(f'missed description: {f}')
             if print_out:
                 print(f)
             continue
         if 'landmarks' not in descr:
             log.warning(f'missed landmarks: {f}')
             if print_out:
                 print(f)
             continue
         if not tools.test_landmarks(descr['landmarks']):
             log.warning(f'wrong landmarks: {f}')
             if print_out:
                 print(f)
Exemplo n.º 20
0
def cuda_init(tf_memory_limit=0):
    if not has_cuda():
        log.debug('cuda disabled')
        return

    try:
        import tensorflow as tf

        log.debug('cuda init')
        gpu = tf.config.experimental.list_physical_devices('GPU')[0]
        if tf.config.experimental.get_memory_growth(gpu):
            return
        tf.config.experimental.set_memory_growth(gpu, True)
        if tf_memory_limit:
            log.debug(f'set cuda memory limit: {tf_memory_limit}')
            tf.config.experimental.set_virtual_device_configuration(
                gpu,
                [tf.config.experimental.VirtualDeviceConfiguration(
                    memory_limit=tf_memory_limit)])
    except ModuleNotFoundError as ex:
        log.info('tensorflow disabled, skip initialisation')
Exemplo n.º 21
0
def main():
    args = args_parse()

    cfg = config.Config(args.config)

    if args.logfile:
        logfile = args.logfile
    else:
        logfile = cfg.get_path('server', 'log_file')

    log.initLogger(logfile)

    try:
        server = FaceRecServer(cfg)
        log.info("Face rec server up.")
        server.serve_forever()
    except KeyboardInterrupt:
        server.stop(False)
        server.status()
        server.server_close()
        log.info("Face rec server down.")
Exemplo n.º 22
0
 def sync_deleted(self, folders):
     log.info(f'Sync deleted started')
     for folder in folders:
         log.info(f'Check {folder}')
         plex_files = set(self.__plexdb.get_files(folder))
         rec_files = set(self.__recdb.get_files(folder))
         filenames = sorted(rec_files - plex_files)
         if len(filenames) != 0:
             for filename in filenames:
                 log.info(f'removing from recdb: {filename}')
                 self.__recdb.remove(filename, commit=False)
         else:
             log.info(f'No files to remove from {folder}')
         self.__recdb.commit()
Exemplo n.º 23
0
 def __end_stage(self):
     if self.__status.get('save', True):
         log.info(f'Commit transaction')
         if self.__db is not None:
             self.__db.commit()
         if self.__cdb is not None:
             self.__cdb.commit()
     else:
         log.info(f'Rollback transaction')
         if self.__db is not None:
             self.__db.rollback()
         if self.__cdb is not None:
             self.__cdb.rollback()
     self.__status['stop'] = False
     self.__status['endtime'] = time.time()
     log.info(f'end stage: {self.__status}')
Exemplo n.º 24
0
 def __match_files_faces(
         self, files_faces, debug_out_folder,
         save_all_faces=False, skip_face_gen=False):
     cnt_all = 0
     cnt_changed = 0
     for ff in files_faces:
         if self.__step_stage():
             break
         filename = ff['filename']
         log.info(f"match image: {filename}")
         is_video = tools.get_low_ext(filename) in tools.VIDEO_EXTS
         if not self.__match_faces(ff['faces']):
             continue
         for face in ff['faces']:
             if self.__step_stage_face():
                 break
             cnt_all += 1
             changed = False
             if 'oldname' in face and face['oldname'] != face['name']:
                 self.__db.set_name(face['face_id'], face['name'],
                                    face['dist'], face['pattern'],
                                    commit=False)
                 cnt_changed += 1
                 changed = True
                 log.info(
                     f"face {face['face_id']} in file '{ff['filename']}' " +
                     f"changed '{face['oldname']}' -> '{face['name']}'")
             if debug_out_folder and (changed or save_all_faces):
                 media = tools.load_media(filename,
                                          self.__max_size,
                                          self.__max_video_frames,
                                          self.__video_frames_step)
                 debug_out_file_name = self.__extract_filename(filename)
                 self.__save_debug_images(
                     (face,), media,
                     debug_out_folder, debug_out_file_name,
                     is_video=is_video,
                     skip_face_gen=skip_face_gen)
     self.__end_stage()
     log.info(f'match done: count: {cnt_all}, changed: {cnt_changed}')
Exemplo n.º 25
0
    def sync_new(self, cfg, patt, folders, exts):
        log.info(f'Sync new started')
        cachedb_file = cfg.get_path('files', 'cachedb')
        cache_path = cfg.get_path('server', 'face_cache_path')
        if cachedb_file:
            cdb = cachedb.CacheDB(cachedb_file)
        else:
            cdb = None
        rec = recognizer.createRecognizer(patt, cfg, cdb, self.__recdb)

        for folder in folders:
            plex_files = self.__plexdb.get_files(folder)
            plex_files = set(
                filter(lambda f: tools.get_low_ext(f) in exts, plex_files))
            rec_files = set(self.__recdb.get_files(folder))
            filenames = sorted(plex_files - rec_files)
            count = len(filenames)
            if count != 0:
                log.info(f'Adding {count} files from {folder}')
                rec.recognize_files(filenames, cache_path)
            else:
                log.info(f'No files to add from {folder}')
        self.set_tags()
Exemplo n.º 26
0
    def generate(self, regenerate=False):
        log.info(f'Patterns generation: {self.__folder} ({regenerate})')

        image_files = {}
        for image_file in tools.list_files(self.__folder, tools.IMAGE_EXTS):
            if os.path.split(image_file)[1] == FACE_FILENAME:
                continue
            image_files[image_file] = os.stat(image_file).st_mtime

        if not regenerate:
            self.load()
            filtered = {}
            for image_file in image_files:
                filename_exsists = self.relpath(image_file) in self.__files
                if not filename_exsists or \
                   self.__files[self.relpath(image_file)][self.FILES_TIME] != \
                   image_files[image_file]:

                    filtered[image_file] = image_files[image_file]
                    if filename_exsists:
                        self.__remove_file(image_file)

            if len(filtered) == 0:
                log.info('Nothing changed')
                return

            image_files = filtered

        for (i, image_file) in enumerate(image_files):
            splitted = image_file.split(os.path.sep)
            name = splitted[-2]
            if name == BAD_FOLDERNAME:
                name = splitted[-3]
                tp = PATTERN_TYPE_BAD
            elif name == OTHER_FOLDERNAME:
                name = splitted[-3]
                tp = PATTERN_TYPE_OTHER
            else:
                tp = PATTERN_TYPE_GOOD

            log.info(f'{i + 1}/{len(image_files)} file: {image_file}')

            descr, thumbnail = tools.load_face_description(image_file)
            try:
                encoding = descr['encoding']
            except Exception:
                encoding = None

            if encoding is None:
                import face_recognition
                try:
                    image = tools.read_image(image_file, self.__max_size)
                except Exception:
                    log.exception(f'read_image failed')
                    continue

                boxes = face_recognition.face_locations(image,
                                                        model=self.__model)
                if len(boxes) != 1:
                    log.warning(
                        f'{len(boxes)} faces detected in {image_file}. Skip.')
                    continue
                encodings, landmarks = \
                    self.__get_encoder().encode(image, boxes)
                if not tools.test_landmarks(landmarks[0]):
                    log.warning(f'bad face detected in {image_file}. Skip.')
                    continue

                encoding = encodings[0]

            self.__files[self.relpath(image_file)] = \
                [encoding,
                 name,
                 image_files[image_file],
                 tp]

        self.__init_basenames()
        self.__persons = self.__calc_persons()
        self.__save()
Exemplo n.º 27
0
 def stop(self, save=False):
     log.info(f'Stop called ({save})')
     self.__status['stop'] = True
     self.__status['save'] = save
Exemplo n.º 28
0
 def stop(self, save):
     log.info(f'Runner stop called ({save})')
     self.__status['stop'] = True
     self.__status['save'] = save
Exemplo n.º 29
0
 def remove_tags(self):
     log.info(f'Remove tags started')
     self.__plexdb.delete_tags(TAG_PREFIX, cleanup=True)
     log.info(f'Remove tags done')
Exemplo n.º 30
0
 def analyze(self, print_out):
     self.__analyze_duplicates(print_out)
     self.__analyze_encodings_size(print_out)
     self.__analyze_landmarks(print_out)
     log.info(f'Analyze done')