Exemplo n.º 1
0
    def _process(self, task):
        self.logger.info('processing task {}: {}, {}, {}...'.format(
            task.task_id, task.func_group, task.func_name, task.func_params))

        process_ok = False

        if not self._engine:
            self._engine_init()

        if self._engine:
            image = load_task_obj_data(app_config.SAVE_FILE_NAME['image_save'],
                                       task.m_id, app_config, logger)
            face_data = load_task_obj_data(
                app_config.SAVE_FILE_NAME['face_data'], task.m_id, app_config,
                logger)
            # 根据 task 内容选择相应引擎处理函数,对图片进行处理
            image_output = image_process_wrap(image,
                                              face_data,
                                              self._func_process_image,
                                              self._engine,
                                              task.func_class,
                                              task.func_group,
                                              task.func_name,
                                              task.func_params,
                                              worker=self)
            image.close()

            if image_output:
                save_task_obj_data(image_output,
                                   app_config.SAVE_FILE_NAME['image_temp'],
                                   task.m_id, app_config, logger)
                process_ok = True
                self.logger.info('task {} process done.'.format(task.task_id))

                # 对于 demo 图片,定期更新缓存结果
                if 'img_hash' in face_data:
                    img_hash = face_data['img_hash']
                    if img_hash in app_config.IMG_HASH_APP_DEMO:  # APP 前端所用的四个 demo 图片之一
                        if task.func_params and len(task.func_params) > 0:
                            func_args = str(task.func_params[0])
                        else:
                            func_args = None
                        cache_file_name = make_cache_file_name(
                            task.func_class, task.func_group, task.func_name,
                            func_args)
                        save_cache_demo_image(image_output, img_hash,
                                              app_config.DEMO_CACHE_DIR,
                                              cache_file_name)
            else:
                self.logger.info(
                    'task {} process failed. Unsupported picture?'.format(
                        task.task_id))

        return process_ok
Exemplo n.º 2
0
    def run_worker(self):
        """定时循环获取新任务,对任务图片进行预处理,并将处理完的任务节点进行迁移"""

        # 首先执行人脸检测和特征识别辅助引擎初始化
        self.logger.info("initializing face detector engine...")
        self.face_detector = FaceDetector()
        self.logger.info("initializing face attribute engine...")
        self.face_attribute = face_attribute_adapter.init_model()

        func_list = self._mm_client.shared_obj_func_list('task_controller')
        self.logger.info('task controller functions: {}'.format(func_list))
        func_list = self._mm_client.shared_obj_func_list('engine_controller')
        self.logger.info('engine controller functions: {}'.format(func_list))

        while True:
            time.sleep(0.2)  # 间隔定时处理,避免空耗CPU

            result = self._mm_client.shared_obj_func_call(
                'task_controller', 'new_task_get')
            if result and result['return'] == 'success' and result['data']:
                task = result['data']
                self.logger.info('got a new task: {}'.format(task.task_id))
                task_stat = TaskStat.SUCCESS  # 预处理成功后的状态
                task_msg = None

                # 1、读取新提交的任务图片
                image = load_task_obj_data(
                    app_config.SAVE_FILE_NAME['image_base'], task.m_id,
                    app_config, logger)

                # 2、对图片进行归一化处理,并保存到文件
                image_output = image_process_wrap(image)  # 不指定引擎函数即只做归一化
                for ff in (app_config.SAVE_FILE_NAME['image_norm'],
                           app_config.SAVE_FILE_NAME['image_temp'],
                           app_config.SAVE_FILE_NAME['image_save']):
                    save_task_obj_data(image_output, ff, task.m_id, app_config,
                                       logger)
                image.close()

                # 3、调用引擎获取人脸地图及人脸特征,保存到文件
                #(大部分 AI 模型只支持正方形的面部图片,所以需要进行人脸识别,并截取)
                # 不管是否有人脸图均返回成功,对于无人脸图片引擎处理时再判断报错

                face_data = {}

                image_output_np = np.asarray(image_output)
                # 获取人脸位置及图像(如果人脸部分尺寸小于模型尺寸,则返回指定的最小尺寸为模型尺寸)
                faces_images = self.face_detector.get_faces_images(
                    image_output_np, app_config.FACE_MIN_SIZE)
                if len(faces_images) == 0:
                    task_msg = "no face found in this picture."
                    self.logger.info(task_msg)
                else:
                    self.logger.info("got %d faces_images" % len(faces_images))
                    # 识别第一张人脸的特征,并保存 #FIXME: 或者取中间的人脸?
                    for (x, y, w, h, face_image) in faces_images:
                        attr_all = face_attribute_adapter.get_face_attrs(
                            self.face_attribute, face_image)
                        self.logger.info(
                            'face_attribute_adapter get_face_attrs: %s' %
                            attr_all)

                        # 从 celeba 全部 40 个属性中选取所需的部分
                        name_all = "5_o_Clock_Shadow Arched_Eyebrows Attractive Bags_Under_Eyes Bald Bangs Big_Lips Big_Nose Black_Hair Blond_Hair Blurry Brown_Hair Bushy_Eyebrows Chubby Double_Chin Eyeglasses Goatee Gray_Hair Heavy_Makeup High_Cheekbones Male Mouth_Slightly_Open Mustache Narrow_Eyes No_Beard Oval_Face Pale_Skin Pointy_Nose Receding_Hairline Rosy_Cheeks Sideburns Smiling Straight_Hair Wavy_Hair Wearing_Earrings Wearing_Hat Wearing_Lipstick Wearing_Necklace Wearing_Necktie Young".split(
                        )
                        # default attrs: Bald Bangs Black_Hair Blond_Hair Brown_Hair Bushy_Eyebrows Eyeglasses Male Mouth_Slightly_Open Mustache No_Beard Pale_Skin Young
                        attr = [
                            attr_all[name_all.index(n)]
                            for n in app_config.ATTRS_ENABLED
                        ]
                        attr_dict = {}
                        for i in range(len(attr)):
                            if attr[i] == 1:
                                attr_dict[app_config.ATTRS_ENABLED[i]] = True
                            else:
                                attr_dict[app_config.ATTRS_ENABLED[i]] = False
                        self.logger.info(
                            'selected attr: {}, attr_dict: {}'.format(
                                attr, attr_dict))

                        face_data['x'] = x
                        face_data['y'] = y
                        face_data['w'] = w
                        face_data['h'] = h
                        face_data['attr'] = attr  # 人脸属性(0 或 1 的列表)
                        face_data['attr_dict'] = attr_dict  # 人脸属性(名称、键值字典)
                        face_data['face_image'] = face_image  # 截取的人脸图片
                        #print('--> task.m_id:', task.m_id) # 20200107-07-45-32f6e7262b0e3c9b99c59f710d652b2a
                        face_data['img_hash'] = task.m_id.split('-')[-1]
                        #cv2.imwrite(join(app_config.SAVE_FILE_NAME['face_data'], '.jpg'), face_image)

                        task_msg = face_data['attr_dict']
                        break  # 只处理第一张人脸

                # 保存人脸图片及位置等信息到 pickle 文件
                save_task_obj_data(face_data,
                                   app_config.SAVE_FILE_NAME['face_data'],
                                   task.m_id, app_config, logger)

                # 4、更新任务节点状态
                result = self._mm_client.shared_obj_func_call(
                    'task_controller', 'task_update',
                    (task.task_id, TaskQueue.NEW, {
                        'task_stat': task_stat,
                        'msg': task_msg
                    }))
                self.logger.info('task_update {} result: {}'.format(
                    task.task_id, result))

                # 5、迁移到”处理任务队列“(任务预处理引擎已在 new_task_get 中做迁移了)
                result = self._mm_client.shared_obj_func_call(
                    'task_controller', 'task_move', (
                        task.task_id,
                        TaskQueue.NEW,
                        TaskQueue.PROCESS,
                    ))
                self.logger.info('task_move {} result: {}'.format(
                    task.task_id, result))

                # 对于 demo 图片,定期更新缓存结果
                if task_stat != TaskStat.FAIL and 'img_hash' in face_data:
                    img_hash = face_data['img_hash']
                    if img_hash in app_config.IMG_HASH_APP_DEMO:  # APP 前端所用的四个 demo 图片之一
                        cache_file_name = make_cache_file_name()
                        save_cache_demo_image(image_output, img_hash,
                                              app_config.DEMO_CACHE_DIR,
                                              cache_file_name)
                        # 上述预处理文件,也需要拷贝到缓存目录下
                        dir_from = os.path.join(app_config.UPLOAD_FOLDER,
                                                task.m_id.replace('-', '/'))
                        img_hash = task.m_id.split('-')[-1]
                        for ff in (app_config.SAVE_FILE_NAME['image_norm'],
                                   app_config.SAVE_FILE_NAME['image_temp'],
                                   app_config.SAVE_FILE_NAME['image_save'],
                                   app_config.SAVE_FILE_NAME['face_data']):
                            file_from = os.path.join(dir_from, ff)
                            file_to = os.path.join(app_config.DEMO_CACHE_DIR,
                                                   img_hash, ff)
                            shutil.copyfile(file_from, file_to)

            else:
                #print('no new task found.')
                pass
Exemplo n.º 3
0
def task_apply_image_save():
    """保存任务处理后图片
    """

    data = {}
    
    if not current_app.config['ENABLE_APPLY_IMAGE_SAVE']:
        data['msg'] = 'Function disabled. Bypass task_apply_image_save().'
        current_app.logger.info(data['msg'])
        return resp_result(data)
    
    m_id = request.args.get('m_id')
    if m_id:
        try:
            demo_cahce_file = None
            img_hash = m_id.split('-')[-1]
            if img_hash in current_app.config['IMG_HASH_APP_DEMO']: # 前端所用四个 demo 图片之一
                current_app.logger.info('task_apply_image_save() got request args: {}'.format(repr(request.args)))
                f_group, f_class, f_name, f_param0 = __get_func_args_from_request_args(request)
                demo_cahce_file = os.path.join(current_app.config['DEMO_CACHE_DIR'], img_hash, make_cache_file_name(f_class, f_group, f_name, f_param0))
            save_task_image_from_selected(m_id, current_app.config, current_app.logger, demo_cahce_file)
        except Exception as e:
            current_app.logger.error('task_apply_image_save failed: {}'.format(e))
            data['m_id'] = m_id
            data['msg'] = str(e)
            return resp_error(data)
    
    return resp_result(data)
Exemplo n.º 4
0
def task_add():
    """根据客户端请求生成图片处理任务"""
    data = {}
    if request.args.get("tk") == "harekrishna10800":  # FIXME: 改为正规的令牌校验机制
        if request.method == "POST":
            m_id, params, msg, is_new_image = get_request_data(request)
            current_app.logger.info('task_add() got post request. params: {}, msg: {}, '
                                    'm_id: {}, is_new_image: {}'
                                    .format(params, msg, m_id, is_new_image))
            if not params or not m_id:
                return resp_error(msg)

            # 根据 m_id 和 params 生成新任务,提交后端进行处理
            task = {}
            task.update(params)
            task['m_id'] = m_id
            #print('--> repr(task):', repr(task))
            # 新提交图片 task 示例:{'user_id': 'zzz123', 'func_group': None, 'm_id': '20200108-10-35-12d2b6c4a62be56c494324809441be41', 'func_class': None, 'func_params': None, 'func_name': None}
            # 图片任务 task 示例 {'func_params': ['avatar', ['face', 'l_ear', 'r_ear', 'nose', 'u_lip', 'l_lip']], 'func_class': 'fc_editor', 'func_name': 'chg_color', 'func_group': 'impression', 'user_id': 'zzz123'}
            
            data['m_id'] = m_id
            
            # 如果是 demo 图片,且请求的处理已经有未过期缓存,则直接返回一个特殊且固定的 task_id
            img_hash = m_id.split('-')[-1]
            if img_hash in current_app.config['IMG_HASH_APP_DEMO']: # 前端所用四个 demo 图片之一
                if task['func_params'] and type(task['func_params']) is list \
                and len(task['func_params']) > 0:
                    func_args = str(task['func_params'][0])
                else:
                    func_args = None
                cache_file = os.path.join(current_app.config['DEMO_CACHE_DIR'], img_hash, make_cache_file_name(task['func_class'], task['func_group'], 
                                     task['func_name'], func_args))
                
                current_app.logger.info('task_add() check cache file {}'.format(cache_file))
                if os.path.isfile(cache_file) and time.time() - os.path.getctime(cache_file) <= current_app.config['DEMO_CACHE_UPDATE_TERM']:
                    data['task_id'] = 'THE_CACHE_TASK_ID' # 其他 task_id 为浮点数
                    current_app.logger.info('task_add() found valid cache, bypass new_task_add.')
                    # 从缓存目录拷贝预处理文件到任务目录下,以便后续没有做过缓存的可以正常处理
                    dir_to = os.path.join(current_app.config['UPLOAD_FOLDER'], 
                                          m_id.replace('-', '/'))
                    for ff in (current_app.config['SAVE_FILE_NAME']['image_norm'],
                            current_app.config['SAVE_FILE_NAME']['image_temp'],
                            current_app.config['SAVE_FILE_NAME']['image_save'],
                            current_app.config['SAVE_FILE_NAME']['face_data']):
                        file_from = os.path.join(current_app.config['DEMO_CACHE_DIR'], 
                                                 img_hash, ff)
                        file_to = os.path.join(dir_to, ff)
                        shutil.copyfile(file_from, file_to)
                    return resp_result(data)
            
            # 判断是否新提交图片,如果是则置任务状态为 NEW,否则为 PENDING
            from ..engine_v1.task import TaskStat
            if is_new_image:
                task['task_stat'] = TaskStat.NEW
            else:
                #TODO: 在此通过后端逻辑判断是否为 VIP 功能,以及检查用户是否有 VIP 权限!(v2版本实现)
                task['task_stat'] = TaskStat.PENDING
            
            with current_app.app_context():
                try:
                    #func_list = current_app.mm_client.shared_obj_func_lists('task_controller')
                    #current_app.logger.warn('task controller functions: {}'.format(func_list))
                    result = current_app.mm_client.shared_obj_func_call(
                                                'task_controller', 'new_task_add', (task,))
                    current_app.logger.info('call new_task_add return: {}'.format(result))
                    if result['return'] == 'success' and result['data'] is not None:
                        data['task_id'] = result['data']['task_id']
                except Exception as e:
                    current_app.logger.error(e)
        else:
            return resp_error("invalid method")
    else:
        return resp_error("invalid tk")
    return resp_result(data)
Exemplo n.º 5
0
def task_query():
    """查询任务状态
    备注:为给引擎留出处理时间,可每秒钟重新查询,最多10次(可配置),由于不便在 flask 中使用阻塞式 sleep,需要在前端实现该逻辑。
    """

    data = {}
    
    result = None
    task_id = request.args.get('task_id')
    if task_id:
        got_result_image = False
        data['progress'] = 5

        # 如果是请求 demo 图片处理结果的内定 task_id,则直接读取缓存并返回
        if task_id == 'THE_CACHE_TASK_ID':
            m_id = request.args.get('m_id')
            img_hash = m_id.split('-')[-1]
            
            if img_hash in current_app.config['IMG_HASH_APP_DEMO']: # 前端所用四个 demo 图片之一
                current_app.logger.info('task_query() got request args: {}'.format(repr(request.args)))
                func_group, func_class, func_name, func_param0 = __get_func_args_from_request_args(request)
                cache_file = os.path.join(current_app.config['DEMO_CACHE_DIR'], img_hash, make_cache_file_name(func_class, func_group, func_name, func_param0))
                
                current_app.logger.info('task_query() check cache file {}'.format(cache_file))
                if os.path.isfile(cache_file):
                    result_image = read_local_image(cache_file)
                    if func_class == 'fc_fun':
                        # 演示图片,右下角加 logo 水印
                        result_image = image_add_watermark(result_image)
                    got_result_image = True
                    data['m_id'] = m_id
                    data['msg'] = json.dumps('got cache result.')
                    current_app.logger.info('task_query() loaded cache result for the demo image.')
        
        # 如果没找到缓存,则向后端查询处理结果
        if not got_result_image:
            result = current_app.mm_client.shared_obj_func_call(
                                    'task_controller', 'task_query', (task_id,))
            current_app.logger.info('call task_query {} return: {}'
                                    .format(task_id, result))
            if result['return'] == 'success' and result['data'] is not None:
                data['progress'] = result['data']['progress']
                if data['progress'] == 100:
                    result_image = result['data']['image']
                    got_result_image = True
                    # 做超解析缩放为指定输出大小(已在引擎中处理,故此注释掉)
                    #out_img_w, out_img_h = current_app.config['OUTPUT_SIZE_FUN']
                    #with current_app.app_context():
                    #    result_image = esrgan_adapter.process_image(result_image,       current_app.esrgan, out_img_w, out_img_h)
                    if result['data']['func_class'] == 'fc_fun':
                        # 演示图片,右下角加 logo 水印
                        result_image = image_add_watermark(result_image)
                    data['m_id'] = result['data']['m_id']
                    data['msg'] = json.dumps(result['data']['msg'])
        
        if got_result_image:
            data['progress'] = 100
            data['image'] = image_to_base64(result_image) # 转成 jpg base64 返回

    return resp_result(data)