コード例 #1
0
class Evaluate:
    def __init__(self, arguments, cuda=False):
        """
        Evaluate
        :param arguments: argparse options
        :param cuda: gpu speed up
        """
        self.args = arguments
        location = self.args.lightcnn
        self.lightcnn_inst = utils.load_lightcnn(location, cuda)
        self.cuda = cuda
        self.parsing = self.args.parsing_checkpoint
        self.max_itr = arguments.total_eval_steps
        self.learning_rate = arguments.eval_learning_rate
        self.losses = []
        self.prev_path = "./output/eval"
        self.model_path = "../unity/models"
        self.clean()
        self.imitator = Imitator("neural imitator", arguments, clean=False)
        self.l2_c = (torch.ones((512, 512)), torch.ones((512, 512)))
        if cuda:
            self.imitator.cuda()
        self.imitator.eval()
        self.imitator.load_checkpoint(args.imitator_model, False, cuda=cuda)

    def _init_l1_l2(self, y):
        """
        init reference photo l1 & l2
        :param y: input photo, numpy array [H, W, C]
        """
        y_ = cv2.resize(y, dsize=(128, 128), interpolation=cv2.INTER_LINEAR)
        y_ = np.swapaxes(y_, 0, 2).astype(np.float32)
        y_ = np.mean(y_, axis=0)[np.newaxis, np.newaxis, :, :]
        y_ = torch.from_numpy(y_)
        if self.cuda:
            y_ = y_.cuda()
        self.l1_y = y_
        y = y[np.newaxis, :, :, ]
        y = np.swapaxes(y, 1, 2)
        y = np.swapaxes(y, 1, 3)
        y = torch.from_numpy(y)
        if self.cuda:
            y = y.cuda()
        self.l2_y = y / 255.

    def discrim_l1(self, y_):
        """
        content loss evaluated by lightcnn
        :param y_: generated image, torch tensor [B, C, W, H]
        :return: l1 loss
        """
        y_ = F.max_pool2d(y_, kernel_size=(4, 4), stride=4)  # 512->128
        y_ = torch.mean(y_, dim=1).view(1, 1, 128, 128)  # gray
        return utils.discriminative_loss(self.l1_y, y_, self.lightcnn_inst)

    def discrim_l2(self, y_):
        """
        facial semantic feature loss
        evaluate loss use l1 at pixel space
        :param y_: generated image, tensor  [B, C, W, H]
        :return: l1 loss in pixel space
        """
        # [eyebrow,eye,nose,teeth,up lip,lower lip]
        w_r = [1.1, 1.1, 1., 0.7, 1., 1.]
        w_g = [1.1, 1.1, 1., 0.7, 1., 1.]
        part1, _ = faceparsing_tensor(self.l2_y,
                                      self.parsing,
                                      w_r,
                                      cuda=self.cuda)
        y_ = y_.transpose(2, 3)
        part2, _ = faceparsing_tensor(y_, self.parsing, w_g, cuda=self.cuda)
        self.l2_c = (part1 * 10, part2 * 10)
        return F.l1_loss(part1, part2)

    def evaluate_ls(self, y_):
        """
        评估损失Ls
        由于l1表示的是余弦距离的损失, 其范围又在0-1之间 所以这里使用1-l1
        (余弦距离越大 表示越接近)
        :param y_:  generated image, tensor [b,c,w,h]
        :return: ls, description
        """
        l1 = self.discrim_l1(y_)
        l2 = self.discrim_l2(y_)
        alpha = self.args.eval_alpha
        ls = alpha * (1 - l1) + l2
        info = "l1:{0:.3f} l2:{1:.3f} ls:{2:.3f}".format(l1, l2, ls)
        self.losses.append((l1.item(), l2.item() / 3, ls.item()))
        return ls, info

    def itr_train(self, y):
        """
        iterator train
        :param y: numpy array, image [H, W, C]
        """
        param_cnt = self.args.params_cnt
        t_params = 0.5 * torch.ones((1, param_cnt), dtype=torch.float32)
        if self.cuda:
            t_params = t_params.cuda()
        t_params.requires_grad = True
        self.losses.clear()
        lr = self.learning_rate
        self._init_l1_l2(y)
        m_progress = tqdm(range(1, self.max_itr + 1))
        for i in m_progress:
            y_ = self.imitator(t_params)
            loss, info = self.evaluate_ls(y_)
            loss.backward()
            if i == 1:
                self.output(t_params, y, 0)
            t_params.data = t_params.data - lr * t_params.grad.data
            t_params.data = t_params.data.clamp(0., 1.)
            t_params.grad.zero_()
            m_progress.set_description(info)
            if i % self.args.eval_prev_freq == 0:
                x = i / float(self.max_itr)
                lr = self.learning_rate * (1 - x) + 1e-2
                self.output(t_params, y, i)
                self.plot()
        self.plot()
        log.info("steps:{0} params:{1}".format(self.max_itr, t_params.data))
        return t_params

    def output(self, x, refer, step):
        """
        capture for result
        :param x: generated image with grad, torch tensor [b,params]
        :param refer: reference picture
        :param step: train step
        """
        self.write(x)
        y_ = self.imitator(x)
        y_ = y_.cpu().detach().numpy()
        y_ = np.squeeze(y_, axis=0)
        y_ = np.swapaxes(y_, 0, 2) * 255
        y_ = y_.astype(np.uint8)
        im1 = self.l2_c[0]
        im2 = self.l2_c[1]
        np_im1 = im1.cpu().detach().numpy()
        np_im2 = im2.cpu().detach().numpy()
        f_im1 = ops.fill_gray(np_im1)
        f_im2 = ops.fill_gray(np_im2)
        image_ = ops.merge_4image(refer, y_, f_im1, f_im2, transpose=False)
        path = os.path.join(self.prev_path, "eval_{0}.jpg".format(step))
        cv2.imwrite(path, image_)

    def write(self, params):
        """
        生成二进制文件 能够在unity里还原出来
        :param params: 捏脸参数 tensor [batch, params_cnt]
        """
        np_param = params.cpu().detach().numpy()
        np_param = np_param[0]
        list_param = np_param.tolist()
        dataset = self.args.path_to_dataset
        shape = utils.curr_roleshape(dataset)
        path = os.path.join(self.model_path, "eval.bytes")
        f = open(path, 'wb')
        write_layer(f, shape, list_param)
        f.close()

    def clean(self):
        """
        clean for new iter
        """
        ops.clear_files(self.prev_path)
        ops.clear_files(self.model_path)

    def plot(self):
        """
        plot loss
        """
        count = len(self.losses)
        if count > 0:
            plt.style.use('seaborn-whitegrid')
            x = range(count)
            y1 = []
            y2 = []
            for it in self.losses:
                y1.append(it[0])
                y2.append(it[1])
            plt.plot(x, y1, color='r', label='l1')
            plt.plot(x, y2, color='g', label='l2')
            plt.ylabel("loss")
            plt.xlabel('step')
            plt.legend()
            path = os.path.join(self.prev_path, "loss.png")
            plt.savefig(path)
            plt.close('all')
コード例 #2
0
ファイル: main.py プロジェクト: bluesea/face-nn
     imitator = Imitator("neural imitator", args)
     if cuda:
         imitator.cuda()
     imitator.batch_train(cuda)
 elif args.phase == "train_extractor":
     log.info('feature extractor train mode')
     extractor = Extractor("neural extractor", args)
     if cuda:
         extractor.cuda()
     extractor.batch_train(cuda)
 elif args.phase == "inference_imitator":
     log.info("inference imitator")
     imitator = Imitator("neural imitator", args, clean=False)
     if cuda:
         imitator.cuda()
     imitator.load_checkpoint(args.imitator_model, True, cuda=cuda)
 elif args.phase == "prev_imitator":
     log.info("preview imitator")
     imitator = Imitator("neural imitator", args, clean=False)
     imitator.load_checkpoint(args.imitator_model, False, cuda=False)
     dataset = FaceDataset(args)
     name, param, img = dataset.get_picture()
     param = np.array(param, dtype=np.float32)
     b_param = param[np.newaxis, :]
     log.info(b_param.shape)
     t_param = torch.from_numpy(b_param)
     output = imitator(t_param)
     output = output.cpu().detach().numpy()
     output = np.squeeze(output, axis=0)
     output = output.swapaxes(0, 2) * 255
     cv2.imwrite('./output/{0}.jpg'.format(name), output)
コード例 #3
0
ファイル: evaluate.py プロジェクト: bluesea/face-nn
class Evaluate:
    def __init__(self, arguments, cuda=False):
        """
        Evaluate
        :param arguments: argparse options
        :param cuda: gpu speed up
        """
        self.args = arguments
        location = self.args.lightcnn
        self.lightcnn_inst = utils.load_lightcnn(location, cuda)
        self.cuda = cuda
        self.parsing = self.args.parsing_checkpoint
        self.max_itr = arguments.total_eval_steps
        self.learning_rate = arguments.eval_learning_rate
        self.losses = []
        self.prev_path = "./output/eval"
        self.clean()
        self.imitator = Imitator("neural imitator", arguments, clean=False)
        if cuda:
            self.imitator.cuda()
        self.imitator.eval()
        self.imitator.load_checkpoint(args.imitator_model, False, cuda=cuda)

    def discrim_l1(self, y, y_):
        """
        content loss evaluated by lightcnn
        :param y: input photo, numpy array [H, W, C]
        :param y_: generated image, torch tensor [B, C, W, H]
        :return: l1 loss
        """
        y = cv2.resize(y, dsize=(128, 128), interpolation=cv2.INTER_LINEAR)
        y = np.swapaxes(y, 0, 2).astype(np.float32)
        y = np.mean(y, axis=0)[np.newaxis, np.newaxis, :, :]
        y = torch.from_numpy(y)
        if self.cuda:
            y = y.cuda()
        y_ = F.max_pool2d(y_, kernel_size=(4, 4), stride=4)  # 512->128
        y_ = torch.mean(y_, dim=1).view(1, 1, 128, 128)  # gray
        return utils.discriminative_loss(y, y_, self.lightcnn_inst)

    def discrim_l2(self, y, y_, step):
        """
        facial semantic feature loss
        evaluate loss use l1 at pixel space
        :param y: input photo, numpy array  [H, W, C]
        :param y_: generated image, tensor  [B, C, W, H]
        :param step: train step
        :return: l1 loss in pixel space
        """
        img1 = parse_evaluate(y.astype(np.uint8),
                              cp=self.parsing,
                              cuda=self.cuda)
        y_ = y_.cpu().detach().numpy()
        y_ = np.squeeze(y_, axis=0)
        y_ = np.swapaxes(y_, 0, 2) * 255
        img2 = parse_evaluate(y_.astype(np.uint8),
                              cp=self.parsing,
                              cuda=self.cuda)
        edge_img1 = utils.img_edge(img1).astype(np.float32)
        edge_img2 = utils.img_edge(img2).astype(np.float32)
        w_g = 1.0
        w_r = 1.0

        if step % self.args.eval_prev_freq == 0:
            path = os.path.join(self.prev_path, "l2_{0}.jpg".format(step))
            edge1_v3 = 255. - ops.fill_grey(edge_img1)
            edge2_v3 = 255. - ops.fill_grey(edge_img2)
            merge = ops.merge_4image(y,
                                     y_,
                                     edge1_v3,
                                     edge2_v3,
                                     transpose=False)
            cv2.imwrite(path, merge)
        return np.mean(np.abs(w_r * edge_img1 - w_g * edge_img2))

    def evaluate_ls(self, y, y_, step):
        """
        评估损失Ls
        :param y: input photo, numpy array
        :param y_:  generated image, tensor [b,c,w,h]
        :param step: train step
        :return: ls, description
        """
        l1 = self.discrim_l1(y, y_)
        l2 = self.discrim_l2(y, y_, step)
        alpha = self.args.eval_alpha
        ls = alpha * l1 + l2
        info = "l1:{0:.3f} l2:{1:.3f} ls:{2:.3f}".format(alpha * l1, l2, ls)
        self.losses.append((l1.item() * alpha, l2.item(), ls.item()))
        return ls, info

    def itr_train(self, y):
        """
        iterator train
        :param y: numpy array, image [H, W, C]
        """
        param_cnt = self.args.params_cnt
        t_params = 0.5 * torch.ones((1, param_cnt), dtype=torch.float32)
        if self.cuda:
            t_params = t_params.cuda()
        t_params.requires_grad = True
        self.losses.clear()
        lr = self.learning_rate
        progress = tqdm(range(self.max_itr), initial=0, total=self.max_itr)
        for i in progress:
            y_ = self.imitator.forward(t_params)
            loss, info = self.evaluate_ls(y, y_, i)
            loss.backward()
            t_params.data = t_params.data - lr * t_params.grad.data
            t_params.data = t_params.data.clamp(0., 1.)
            t_params.grad.zero_()
            progress.set_description(info)
            if self.max_itr % 100 == 0:
                x = i / float(self.max_itr)
                lr = self.learning_rate * (x**2 - 2 * x + 1) + 1e-4
        self.plot()
        log.info("steps:{0} params:{1}".format(self.max_itr, t_params.data))
        return t_params

    def output(self, x, refer):
        """
        capture for result
        :param x: generated image with grad, torch tensor [b,params]
        :param refer: reference picture
        """
        self.write(x)
        y_ = self.imitator.forward(x)
        y_ = y_.cpu().detach().numpy()
        y_ = np.squeeze(y_, axis=0)
        y_ = np.swapaxes(y_, 0, 2) * 255.
        y_ = y_.astype(np.uint8)
        image_ = ops.merge_image(refer, y_, transpose=False)
        path = os.path.join(self.prev_path, "eval.jpg")
        cv2.imwrite(path, image_)

    def write(self, params):
        """
        生成二进制文件 能够在unity里还原出来
        :param params: 捏脸参数 tensor [batch, 95]
        """
        np_param = params.cpu().detach().numpy()
        np_param = np_param[0]
        list_param = np_param.tolist()
        dataset = self.args.path_to_dataset
        shape = utils.curr_roleshape(dataset)
        f = open("../unity/models/eval.bytes", 'wb')
        write_layer(f, shape, list_param)
        f.close()

    def clean(self):
        """
        clean for new iter
        """
        ops.clear_files(self.prev_path)

    def plot(self):
        plt.style.use('seaborn-whitegrid')
        x = range(self.max_itr)
        y1 = []
        y2 = []
        for it in self.losses:
            y1.append(it[0])
            y2.append(it[1])
        plt.plot(x, y1, color='r', label='l1')
        plt.plot(x, y2, color='g', label='l2')
        plt.ylabel("loss")
        plt.xlabel('step')
        plt.legend()
        path = os.path.join(self.prev_path, "eval.png")
        plt.savefig(path)