精华内容
下载资源
问答
  • EasyExcel自定义Converter解决性别转换问题

    千次阅读 多人点赞 2020-04-28 11:26:51
    那么这篇文章继续介绍一下性别转换的问题,一般我们代码中都会使用**1/0 分别代表 男/女,可是Excel中都是用"男","女"表示的,怎么做才能单独将性别转化为1和0**呢?没错还是自定义转换器! 因为这个案例有...

    上篇文章已经介绍了自定义LocalDateTime转换器解决了LocalDateTime导入导出的问题!

    那么这篇文章继续介绍一下性别转换的问题,一般我们代码中都会使用 1/0 分别代表 男/女 ,可是Excel中都是用"男","女"表示的,怎么做才能 单独将性别转化为1和0 呢?没错还是自定义转换器!
    因为这个案例有LocalDateTime,所以请查看上篇文章采取任意一种解决方案,建议采用第二种:


    新建User类,然后在类上添加EasyExcel的注解

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    	@ExcelProperty(value = "姓名", index = 0)
    	private String name;
    	@ExcelProperty(value = "年龄", index = 1)
    	private Integer age;
        // 1 男   0 女
    	@ExcelProperty(value = "性别", index = 2)
    	private Integer sex;
    	@ExcelProperty(value = "创建时间", index = 3)
    	private LocalDateTime createTime;
    }
    

    新建Controller用于测试

    @Slf4j
    @RestController
    public class ExcelController {
    
    	@PostMapping("/importData")
    	public void importData(@RequestParam("file") MultipartFile file) throws IOException {
    		if (file == null) return;
    		ArrayList<Object> list = new ArrayList<>();
    		AnalysisEventListener listener = new AnalysisEventListener() {
    			@Override
    			public void invoke(Object data, AnalysisContext context) {
    				list.add(data);
    			}
    
    			@Override
    			public void doAfterAllAnalysed(AnalysisContext context) {
    				log.info("导入数据完毕");
    			}
    		};
    		try {
    			EasyExcel.read(file.getInputStream(), User.class, listener).sheet(0).doRead();
    		} catch (IOException e) {
    			log.error("导入出错:{}", e.getMessage());
    		}
    		list.forEach(System.out::println);
    	}
    
    	@PostMapping("/exportData")
    	public void exportData(HttpServletResponse response) {
    		List<User> list = getList();
    		try {
    			response.setContentType("application/vnd.ms-excel; charset=utf-8");
    			response.setCharacterEncoding("utf-8");
    			String fileName = "三好学生表";
    			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8") + ".xlsx");
    			EasyExcel.write(response.getOutputStream(), User.class).registerConverter(new LocalDateTimeConverter()).sheet("test").doWrite(list);
    		} catch (Exception e) {
    			log.error("下载报表异常:{}", e.getMessage());
    			throw new RuntimeException("下载报表异常");
    		}
    	}
    
    	private List<User> getList() {
    		List<User> list = new ArrayList<>();
    		LocalDateTime now = LocalDateTime.now();
    		User xd = new User("熊大", 10, 1, now);
    		User ne = new User("牛二", 20, 0, now);
    		User zs = new User("张三", 30, 1, now);
    		User ls = new User("李四", 40, 1, now);
    		User ww = new User("王五", 50, 0, now);
    		list.add(xd);
    		list.add(ne);
    		list.add(zs);
    		list.add(ls);
    		list.add(ww);
    		return list;
    	}
    }
    

    访问exportData接口会发现不是我们想要的结果:为什么导出的文件性别那一列只有0 1 没有男 女呢?好吧!那我们就自定义转换器,因为我们要转换0和1即Integer,所以编写如下转换器!注意要实现的是com.alibaba.excel.converters.Converter接口,别引用错包了!

    public class SexConverter implements Converter<Integer> {
    	@Override
    	public Class supportJavaTypeKey() {
    		return Integer.class;
    	}
    
    	@Override
    	public CellDataTypeEnum supportExcelTypeKey() {
    		return CellDataTypeEnum.STRING;
    	}
    
    	@Override
    	public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
    		return "男".equals(cellData.getStringValue()) ? 1 : 0;
    	}
    
    	@Override
    	public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
    		return new CellData(value.equals(1) ? "男" : "女");
    	}
    }
    

    划重点!Sex的converter编写完了,怎么才能让它生效呢?有一种解决方案!

    1. 找到需要转换的字段,在@ExcelProperty上添加converter属性

    修改代码完毕之后再重启就可以了,不管是导入还是导出都没问题!Excel中的男/女导入之后在代码中会自动变为1/0,代码中的1/0导出之后会自动变为男/女!赶快试试吧!

    细心的小伙伴已经发现了,LocalDateTime的converter有三种生效方案,但是sex的converter只有一种!点击查看Converter使用总结并带你追踪一下源码并给出具体的解决方案!

    加油!

    展开全文
  • 如何利用CycleGAN实现男女性别转换

    千次阅读 2019-11-26 22:17:29
    介绍 CycleGAN网络具有很强大的风格迁移功能。能够实现非常深层次的风格转换。比如男性图片女性化或者...可以直接运行,实现图片中头像识别和对应性别转换。可以体验一下生成对抗网络的趣味。 对网络感兴趣,以及想...

    介绍

    CycleGAN网络具有很强大的风格迁移功能。能够实现非常深层次的风格转换。比如男性图片女性化或者女性图片男性化。

    先上效果图:
    在这里插入图片描述
    下面简单谈一谈实现原理。

    网络结构

    在这里插入图片描述
    网络结构如图所示,通过两个循环使用的生成器来进行风格迁移。由此实现了非常神奇的效果。

    下面结合代码来详细解释一下网络结构。训练生成对抗网络的深度学习框架为Pytorch

    1. 残差模块定义

    class ResidualBlock(nn.Module):
        def __init__(self, in_features):
            super(ResidualBlock, self).__init__()
    
            # 残差模块不改变shape
            conv_block = [  nn.ReflectionPad2d(1),  # 构建残差模块的时候使用映射填充的形式
                            nn.Conv2d(in_features, in_features, 3),
                            nn.InstanceNorm2d(in_features),     # 不使用BatchNorm而是使用InstanceNorm
                            nn.ReLU(inplace=True),
                            nn.ReflectionPad2d(1),
                            nn.Conv2d(in_features, in_features, 3),
                            nn.InstanceNorm2d(in_features)  ]
    
            self.conv_block = nn.Sequential(*conv_block)
    
        def forward(self, x):
            return x + self.conv_block(x)
    

    残差模块的定义没有太多需要说明的地方,就是有一点需要注意的是。我们在风格迁移中,不再使用BatchNorm而是使用InstanceNorm。
    在这里插入图片描述
    BN是将每一个batch的每一个通道的每一组图片求mean和var, IN是将单独一个图片的一个通道的数据求mean和var。 区别就是一个是对batch求,一个是对一个图片求。风格迁移中,为了保证风格,通常都对每一个图片单独处理。 CycleGAN网络中,每一个batch只有一张 图片,所以使用InstanceNorm。

    2. 定义生成器

    class Generator(nn.Module):
        def __init__(self, input_nc, output_nc, n_residual_blocks=9):
            """
            定义生成网络
            参数:
                input_nc                    --输入通道数
                output_nc                   --输出通道数
                n_residual_blocks           --残差模块数量
            """
            super(Generator, self).__init__()
    
            # 初始化卷积模块
            # 因为使用ReflectionPad扩充
            # 所以输入是3*256*256
            # 输出是64*256*256
            model = [   nn.ReflectionPad2d(3),
                        nn.Conv2d(input_nc, 64, 7),
                        nn.InstanceNorm2d(64),
                        nn.ReLU(inplace=True) ]
    
            # 进行下采样
            # 第一个range:输入是64*256*256,输出是128*128*128
            # 第二个range:输入是128*128*128,输出是256*64*64
    
            in_features = 64
            out_features = in_features*2
            for _ in range(2):
                model += [  nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),
                            nn.InstanceNorm2d(out_features),
                            nn.ReLU(inplace=True) ]
                in_features = out_features
                out_features = in_features*2
    
            # 使用残差模块
            # 输入输出都是256*64*64
            for _ in range(n_residual_blocks): # 默认添加9个残差模块
                model += [ResidualBlock(in_features)]
    
            # 进行上采样
            # 第一个range:输入是256*64*64,输出是128*128*128
            # 第二个range:输入是128*128*128,输出是64*256*256       
            out_features = in_features//2
            for _ in range(2):
                model += [  nn.ConvTranspose2d(in_features, out_features, 3, stride=2, padding=1, output_padding=1),
                            nn.InstanceNorm2d(out_features),
                            nn.ReLU(inplace=True) ]
                in_features = out_features
                out_features = in_features//2
    
            # 最后输出层
            # 输入是64*256*256
            # 输出是3*256*256
            model += [  nn.ReflectionPad2d(3),
                        nn.Conv2d(64, output_nc, 7),
                        nn.Tanh() ]
    
            self.model = nn.Sequential(*model)
    
        def forward(self, x):
            return self.model(x)
    

    生成器的结构就是最初那幅图中的右侧的样子。进行下采样之后接一个残差模块,再之后进行上采样。生成器期望可以学到比较复杂的特征构造方法,所以网络结构更深,更复杂。判别器结构相对来说要简单很多。

    3. 判别器

    class Discriminator(nn.Module):
        def __init__(self, input_nc):
            super(Discriminator, self).__init__()
    
            # 构建卷积分类器
            # 输入为3*256*256
            # 输出为64*128*128
            model = [   nn.Conv2d(input_nc, 64, 4, stride=2, padding=1),
                        nn.LeakyReLU(0.2, inplace=True) ]
    
            # 输入为64*128*128
            # 输出为128*64*64
            model += [  nn.Conv2d(64, 128, 4, stride=2, padding=1),
                        nn.InstanceNorm2d(128), 
                        nn.LeakyReLU(0.2, inplace=True) ]
     
            # 输入为128*64*64
            # 输出为256*32*32
            model += [  nn.Conv2d(128, 256, 4, stride=2, padding=1),
                        nn.InstanceNorm2d(256), 
                        nn.LeakyReLU(0.2, inplace=True) ]
    
            # 输入为256*32*32
            # 输出为512*31*31
            model += [  nn.Conv2d(256, 512, 4, padding=1),
                        nn.InstanceNorm2d(512), 
                        nn.LeakyReLU(0.2, inplace=True) ]
    
            # 全卷积分类层
            # 输入为输出为512*31*31
            # 输出为1*30*30
            model += [nn.Conv2d(512, 1, 4, padding=1)]
    
            self.model = nn.Sequential(*model)
    
        def forward(self, x):
            x =  self.model(x)
            # 使用平均池化的办法输出预测值
            # avg_pool2d(input,kernel_size),这里kernel_size为30
            return F.avg_pool2d(x, x.size()[2:]).view(x.size()[0], -1)
    

    就是一个比较普通的分类网络。通过步长为2来逐步缩小尺寸。可能值得注意的是,相比于传统的分类神经网络。我们这里使用全局平均池化的方式进行最终输出预测。没有使用全连接层,减小了网络尺寸。

    此外,我还做了一个exe交互程序。可以直接运行,实现图片中头像识别和对应性别转换。可以体验一下生成对抗网络的趣味。

    在这里插入图片描述

    对网络感兴趣,以及想要详细了解原理是具体如何用代码实现,或者想用有趣数据集做出创意应用的功能的话,可以参考这个视频课程:点击链接

    展开全文
  • 深度有趣 | 20 CycleGAN性别转换

    千次阅读 2018-09-20 10:22:53
    介绍可用于实现多种非配对图像翻译任务的CycleGAN模型,并完成性别转换任务 原理 和pix2pix不同,CycleGAN不需要严格配对的图片,只需要两类(domain)即可,例如一个文件夹都是苹果图片,另一个文件夹都是橘子图片 ...

    简介

    介绍可用于实现多种非配对图像翻译任务的CycleGAN模型,并完成性别转换任务

    原理

    和pix2pix不同,CycleGAN不需要严格配对的图片,只需要两类(domain)即可,例如一个文件夹都是苹果图片,另一个文件夹都是橘子图片

    使用A和B两类图片,就可以实现A到B的翻译和B到A的翻译

    论文官方网站上提供了详细的例子和介绍,https://junyanz.github.io/CycleGAN/,例如苹果和橘子、马和斑马、夏天和冬天、照片和艺术作品等

    CycleGAN非配对图像翻译示例

    以及论文的官方Github项目,https://github.com/junyanz/CycleGAN,使用PyTorch实现

    CycleGAN由两个生成器G和F,以及两个判别器Dx和Dy组成

    CycleGAN模型结构

    G接受真的X并输出假的Y,即完成X到Y的翻译;F接受真的Y并输出假的X,即完成Y到X的翻译;Dx接受真假X并进行判别,Dy接受真假Y并进行判别

    CycleGAN的损失函数和标准GAN差不多,只是写两套而已

    LGAN(G,DY,X,Y)=Eypy[logDY(y)]+Expx[log(1DY(G(x)))] L_{GAN}(G,D_Y,X,Y)=\mathbb{E}_{y\sim p_y}[\log D_Y(y)]+\mathbb{E}_{x\sim p_x}[\log(1-D_Y(G(x)))]

    LGAN(F,DX,Y,X)=Expx[logDX(x)]+Eypy[log(1DX(F(y)))] L_{GAN}(F,D_X,Y,X)=\mathbb{E}_{x\sim p_x}[\log D_X(x)]+\mathbb{E}_{y\sim p_y}[\log(1-D_X(F(y)))]

    除此之外,为了避免mode collasp问题,CycleGAN还考虑了循环一致损失(Cycle Consistency Loss)

    Lcyc(G,F)=Expx[F(G(x))x1]+Eypy[G(F(y))y1] L_{cyc}(G,F)=\mathbb{E}_{x\sim p_x}[\left \| F(G(x))-x \right \|_1]+\mathbb{E}_{y\sim p_y}[\left \| G(F(y))-y \right \|_1]

    因此CycleGAN的总损失如下,G、F、Dx、Dy分别需要min、max其中的部分损失项

    L(G,F,DX,DY)=LGAN(G,DY,X,Y)+LGAN(F,DX,Y,X)+λLcyc(G,F) L(G,F,D_X,D_Y)=L_{GAN}(G,D_Y,X,Y)+L_{GAN}(F,D_X,Y,X)+\lambda L_{cyc}(G,F)

    实现

    在论文的具体实现中,使用了两个tricks

    • 使用Least-Square Loss即最小平方误差代替标准的GAN损失
    • 以G为例,维护一个历史假Y图片集合,例如50张。每次G生成假Y之后将其加到集合中,再从集合中随机地取出一张假Y,和一张真Y一起输入给判别器进行判别。这样一来,假Y集合代表了G根据X生成Y的平均能力,使得训练更加稳定

    使用以下项目训练CycleGAN模型,https://github.com/vanhuyz/CycleGAN-TensorFlow,主要包括几个代码:

    • build_data.py:将图片数据整理为tfrecords文件
    • ops.py:定义了一些小的网络模块
    • generator.py:生成器的定义
    • discriminator.py:判别器的定义
    • model.py:使用生成器和判别器定义CycleGAN
    • train.py:训练模型的代码
    • export_graph.py:将训练好的模型打包成.pd文件
    • inference.py:使用打包好的.pb文件翻译图片,即使用模型进行推断

    生成器和判别器结构如下,如果感兴趣可以进一步阅读项目源码

    CycleGAN模型细节

    性别转换

    使用CelebA中的男性图片和女性图片,训练一个实现性别转换的CycleGAN

    将CelebA数据集中的图片处理成256*256大小,并按照性别保存至male和female两个文件夹,分别包含84434张男性图片和118165张女性图片

    # -*- coding: utf-8 -*-
    
    from imageio import imread, imsave
    import cv2
    import glob, os
    from tqdm import tqdm
    
    data_dir = 'data'
    male_dir = 'data/male'
    female_dir = 'data/female'
    
    if not os.path.exists(data_dir):
        os.mkdir(data_dir)
    if not os.path.exists(male_dir):
        os.mkdir(male_dir)
    if not os.path.exists(female_dir):
        os.mkdir(female_dir)
    
    WIDTH = 256
    HEIGHT = 256
    
    def read_process_save(read_path, save_path):
        image = imread(read_path)
        h = image.shape[0]
        w = image.shape[1]
        if h > w:
            image = image[h // 2 - w // 2: h // 2 + w // 2, :, :]
        else:
            image = image[:, w // 2 - h // 2: w // 2 + h // 2, :]    
        image = cv2.resize(image, (WIDTH, HEIGHT))
        imsave(save_path, image)
    
    target = 'Male'
    with open('list_attr_celeba.txt', 'r') as fr:
        lines = fr.readlines()
        all_tags = lines[0].strip('\n').split()
        for i in tqdm(range(1, len(lines))):
            line = lines[i].strip('\n').split()
            if int(line[all_tags.index(target) + 1]) == 1:
                read_process_save(os.path.join('celeba', line[0]), os.path.join(male_dir, line[0])) # 男
            else:
                read_process_save(os.path.join('celeba', line[0]), os.path.join(female_dir, line[0])) # 女
    

    使用build_data.py将图片转换成tfrecords格式

    python CycleGAN-TensorFlow/build_data.py --X_input_dir data/male/ --Y_input_dir data/female/ --X_output_file data/male.tfrecords --Y_output_file data/female.tfrecords
    

    使用train.py训练CycleGAN模型

    python CycleGAN-TensorFlow/train.py --X data/male.tfrecords --Y data/female.tfrecords --image_size 256
    

    训练开始后,会生成checkpoints文件夹,并根据当前日期和时间生成一个子文件夹,例如20180507-0231,其中包括用于显示tensorboard的events.out.tfevents文件,以及和模型相关的一些文件

    使用tensorboard查看模型训练细节,运行以下命令后访问6006端口即可

    tensorboard --logdir=checkpoints/20180507-0231
    

    以下是迭代185870次之后,tensorboard的IMAGES页面

    CycleGAN模型训练tensorboard细节

    模型训练没有迭代次数限制,所以感觉效果不错或者迭代次数差不多了,便可以终止训练

    使用export_graph.py将模型打包成.pb文件,生成的文件在pretrained文件夹中

    python CycleGAN-TensorFlow/export_graph.py --checkpoint_dir checkpoints/20180507-0231/ --XtoY_model male2female.pb --YtoX_model female2male.pb --image_size 256
    

    通过inference.py使用模型处理图片

    python CycleGAN-TensorFlow/inference.py --model pretrained/male2female.pb --input Trump.jpg --output Trump_female.jpg --image_size 256
    
    python CycleGAN-TensorFlow/inference.py --model pretrained/female2male.pb --input Hillary.jpg --output Hillary_male.jpg --image_size 256
    

    在代码中使用模型处理多张图片

    # -*- coding: utf-8 -*-
    
    import tensorflow as tf
    import numpy as np
    from model import CycleGAN
    from imageio import imread, imsave
    import glob
    import os
    
    image_file = 'face.jpg'
    W = 256
    result = np.zeros((4 * W, 5 * W, 3))
    
    for gender in ['male', 'female']:
        if gender == 'male':
            images = glob.glob('../faces/male/*.jpg')
            model = '../pretrained/male2female.pb'
            r = 0
        else:
            images = glob.glob('../faces/female/*.jpg')
            model = '../pretrained/female2male.pb'
            r = 2
    
        graph = tf.Graph()
        with graph.as_default():
            graph_def = tf.GraphDef()
            with tf.gfile.FastGFile(model, 'rb') as model_file:
                graph_def.ParseFromString(model_file.read())
                tf.import_graph_def(graph_def, name='')
    
            with tf.Session(graph=graph) as sess:
                input_tensor = graph.get_tensor_by_name('input_image:0')
                output_tensor = graph.get_tensor_by_name('output_image:0')
    
                for i, image in enumerate(images):
                    image = imread(image)
                    output = sess.run(output_tensor, feed_dict={input_tensor: image})
    
                    with open(image_file, 'wb') as f:
                        f.write(output)
    
                    output = imread(image_file)
                    maxv = np.max(output)
                    minv = np.min(output)
                    output = ((output - minv) / (maxv - minv) * 255).astype(np.uint8)
    
                    result[r * W: (r + 1) * W, i * W: (i + 1) * W, :] = image
                    result[(r + 1) * W: (r + 2) * W, i * W: (i + 1) * W, :] = output
    
    os.remove(image_file)
    imsave('CycleGAN性别转换结果.jpg', result)
    

    CycleGAN性别转换结果

    视频性别转换

    对一段视频,识别每一帧可能包含的人脸,检测人脸对应的性别,并使用CycleGAN完成性别的双向转换

    使用以下项目实现性别的检测,https://github.com/yu4u/age-gender-estimation,通过Keras训练模型,可以检测出人脸的性别和年龄

    举个例子,使用OpenCV获取摄像头图片,通过dlib检测人脸,并得到每一个检测结果对应的年龄和性别

    # -*- coding: utf-8 -*-
    
    from wide_resnet import WideResNet
    import numpy as np
    import cv2
    import dlib
    
    depth = 16
    width = 8
    img_size = 64
    model = WideResNet(img_size, depth=depth, k=width)()
    model.load_weights('weights.hdf5')
    
    def draw_label(image, point, label, font=cv2.FONT_HERSHEY_SIMPLEX, font_scale=1, thickness=2):
        size = cv2.getTextSize(label, font, font_scale, thickness)[0]
        x, y = point
        cv2.rectangle(image, (x, y - size[1]), (x + size[0], y), (255, 0, 0), cv2.FILLED)
        cv2.putText(image, label, point, font, font_scale, (255, 255, 255), thickness)
    
    detector = dlib.get_frontal_face_detector()
    cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
    
    while True:
        ret, image_np = cap.read()
        image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)
        img_h = image_np.shape[0]
        img_w = image_np.shape[1]
    
        detected = detector(image_np, 1)
        faces = []
    
        if len(detected) > 0:
            for i, d in enumerate(detected):
                x0, y0, x1, y1, w, h = d.left(), d.top(), d.right(), d.bottom(), d.width(), d.height()
                cv2.rectangle(image_np, (x0, y0), (x1, y1), (255, 0, 0), 2)
    
                x0 = max(int(x0 - 0.25 * w), 0)
                y0 = max(int(y0 - 0.45 * h), 0)
                x1 = min(int(x1 + 0.25 * w), img_w - 1)
                y1 = min(int(y1 + 0.05 * h), img_h - 1)
                w = x1 - x0
                h = y1 - y0
                if w > h:
                    x0 = x0 + w // 2 - h // 2
                    w = h
                    x1 = x0 + w
                else:
                    y0 = y0 + h // 2 - w // 2
                    h = w
                    y1 = y0 + h
                faces.append(cv2.resize(image_np[y0: y1, x0: x1, :], (img_size, img_size)))
    
            faces = np.array(faces)
            results = model.predict(faces)
            predicted_genders = results[0]
            ages = np.arange(0, 101).reshape(101, 1)
            predicted_ages = results[1].dot(ages).flatten()
    
            for i, d in enumerate(detected):
                label = '{}, {}'.format(int(predicted_ages[i]), 'F' if predicted_genders[i][0] > 0.5 else 'M')
                draw_label(image_np, (d.left(), d.top()), label)
    
        cv2.imshow('gender and age', cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR))
    
        if cv2.waitKey(25) & 0xFF == ord('q'):
            cap.release()
            cv2.destroyAllWindows()
            break
    

    将以上项目和CycleGAN应用于视频的双向性别转换,首先提取出视频中的人脸,记录人脸出现的帧数、位置以及对应的性别,视频共830帧,检测出721张人脸

    # -*- coding: utf-8 -*-
    
    from wide_resnet import WideResNet
    import numpy as np
    import cv2
    import dlib
    import pickle
    
    depth = 16
    width = 8
    img_size = 64
    model = WideResNet(img_size, depth=depth, k=width)()
    model.load_weights('weights.hdf5')
    
    detector = dlib.get_frontal_face_detector()
    cap = cv2.VideoCapture('../friends.mp4')
    
    pos = []
    frame_id = -1
    
    while cap.isOpened():
        ret, image_np = cap.read()
        frame_id += 1
        if len((np.array(image_np)).shape) == 0:
            break
    
        image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)
        img_h = image_np.shape[0]
        img_w = image_np.shape[1]
        detected = detector(image_np, 1)
    
        if len(detected) > 0:
            for d in detected:
                x0, y0, x1, y1, w, h = d.left(), d.top(), d.right(), d.bottom(), d.width(), d.height()
                x0 = max(int(x0 - 0.25 * w), 0)
                y0 = max(int(y0 - 0.45 * h), 0)
                x1 = min(int(x1 + 0.25 * w), img_w - 1)
                y1 = min(int(y1 + 0.05 * h), img_h - 1)
                w = x1 - x0
                h = y1 - y0
                if w > h:
                    x0 = x0 + w // 2 - h // 2
                    w = h
                    x1 = x0 + w
                else:
                    y0 = y0 + h // 2 - w // 2
                    h = w
                    y1 = y0 + h
                
                face = cv2.resize(image_np[y0: y1, x0: x1, :], (img_size, img_size))
                result = model.predict(np.array([face]))
                pred_gender = result[0][0][0]
    
                if pred_gender > 0.5:
                    pos.append([frame_id, y0, y1, x0, x1, h, w, 'F'])
                else:
                    pos.append([frame_id, y0, y1, x0, x1, h, w, 'M'])
    
    print(frame_id + 1, len(pos))
    
    with open('../pos.pkl', 'wb') as fw:
        pickle.dump(pos, fw)
        
    cap.release()
    cv2.destroyAllWindows()
    

    再使用CycleGAN,将原视频中出现的人脸转换成相反的性别,并写入新的视频文件

    # -*- coding: utf-8 -*-
    
    import tensorflow as tf
    import numpy as np
    from model import CycleGAN
    from imageio import imread
    import os
    import cv2
    import pickle
    from tqdm import tqdm
    
    with open('../pos.pkl', 'rb') as fr:
        pos = pickle.load(fr)
    
    cap = cv2.VideoCapture('../friends.mp4')
    ret, image_np = cap.read()
    out = cv2.VideoWriter('../output.mp4', -1, cap.get(cv2.CAP_PROP_FPS), (image_np.shape[1], image_np.shape[0]))
    
    frames = []
    while cap.isOpened():
        ret, image_np = cap.read()
        if len((np.array(image_np)).shape) == 0:
            break
        frames.append(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB))
    
    image_size = 256
    image_file = 'face.jpg'
    for gender in ['M', 'F']:
        if gender == 'M':
            model = '../pretrained/male2female.pb'
        else:
            model = '../pretrained/female2male.pb'
    
        graph = tf.Graph()
        with graph.as_default():
            graph_def = tf.GraphDef()
            with tf.gfile.FastGFile(model, 'rb') as model_file:
                graph_def.ParseFromString(model_file.read())
                tf.import_graph_def(graph_def, name='')
    
            with tf.Session(graph=graph) as sess:
                input_tensor = graph.get_tensor_by_name('input_image:0')
                output_tensor = graph.get_tensor_by_name('output_image:0')
    
            for i in tqdm(range(len(pos))):
                fid, y0, y1, x0, x1, h, w, g = pos[i]
                if g == gender:
                    face = cv2.resize(frames[fid - 1][y0: y1, x0: x1, :], (image_size, image_size))
                    output_face = sess.run(output_tensor, feed_dict={input_tensor: face})
    
                    with open(image_file, 'wb') as f:
                        f.write(output_face)
    
                    output_face = imread(image_file)
                    maxv = np.max(output_face)
                    minv = np.min(output_face)
                    output_face = ((output_face - minv) / (maxv - minv) * 255).astype(np.uint8)
    
                    output_face = cv2.resize(output_face, (w, h))
                    frames[fid - 1][y0: y1, x0: x1, :] = output_face
    
    for frame in frames:
        out.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
                
    os.remove(image_file)
    cap.release()
    out.release()
    cv2.destroyAllWindows()
    

    生成的视频文件只有图像、没有声音,可以使用ffmpeg进一步处理

    如果没有ffmpeg则下载并安装,http://www.ffmpeg.org/download.html

    进入命令行,从原始视频中提取音频

    ffmpeg -i friends.mp4 -f mp3 -vn sound.mp3
    

    将提取的音频和生成的视频合成在一起

    ffmpeg -i output.mp4 -i sound.mp3 combine.mp4
    

    其他

    项目还提供了四个训练好的模型,https://github.com/vanhuyz/CycleGAN-TensorFlow/releases,包括苹果到橘子、橘子到苹果、马到斑马、斑马到马,如果感兴趣可以尝试一下

    用CycleGAN不仅可以完成两类图片之间的转换,也可以实现两个物体之间的转换,例如将一个人翻译成另一个人

    可以考虑从一部电影中提取出两个角色对应的图片,训练CycleGAN之后,即可将一个人翻译成另一个人

    还有一些比较大胆的尝试,提高驾驶技术:用GAN去除(爱情)动作片中的马赛克和衣服

    参考

    视频讲解课程

    深度有趣(一)

    展开全文
  • 最近,Snapchat推出了一款可以改变性别的滤镜,不用再化妆换衣服戴假发,只需要一键就能实现性别转换。类似滤镜其实已经有很多,但是snapchat这款新品非常真实,保持了用户原有的颜值特征,并且变装效果非常真实。...

    大数据文摘出品

    作者:蒋宝尚


    可能每个男生心灵最深处都有个角落在期待变身“女装大佬”。


    最近,Snapchat推出了一款可以改变性别的滤镜,不用再化妆换衣服戴假发,只需要一键就能实现性别转换。


    类似滤镜其实已经有很多,但是snapchat这款新品非常真实,保持了用户原有的颜值特征,并且变装效果非常真实。并且据介绍,这个滤镜是实时工作的,并且不需要联网也能运行。


    比如男生变身女装大佬,会把整体画质和五官变得柔和,磨皮美白,并且自带发型和妆容,连戴帽子时候对头发的渲染处理都非常完美。



    Snapchat此功能一出,瞬间被玩坏~


    比如,用“女变男”的功能给拥抱自己的男朋友一个”惊喜“,让他转头忽然发型自己女朋友变成胡茬大汉?



    看他那生无可恋的表情,心中一万匹草泥马飞奔而过。


    还有对权游的照片处理,三傻的男装有点像哈利波特是怎么回事!不过虽然改变了性别,但是“他”那忧郁的眼神却没有改变,依然饱含深情~


    雪诺变身女装也非常惊艳了?


    有人还对音乐圈的大佬进行了一番实验,结果喜人。


    肖邦秒变美少女。


    巴赫用了滤镜之后,变成了金发妇人。


    除了性别转换之外,Snapchat还推出了一秒变宝宝功能。如果复仇者联盟里面的英雄都变成了宝宝,会是什么样子?例如~



    当然,很皮的文摘菌也在编辑部实验了一把这款滤镜,效果的确惊艳,玩到停不下来。


    这是文摘菌变身后的“女装”扮相,简直要对自己动心了☹☹☹。?



    来看一下美女编辑的“男装”变形过程~


    女变男的滤镜也很真实,比如“文文”的脸型大小似乎没有改变,只是整体的色调被调黑,下颌变宽,加上了一撮小胡子,以及把长发藏了起来。


    买家测评:整体比较满意


    不仅素人,这款滤镜还吸引了谷歌大脑工程师Eric Jang,他也很认真的对这项功能做了全方面的测评。



    中间的照片是一张浴室自拍的原始照片。左边是“男性”滤镜的效果,右边是“女性”滤镜的效果。


    Eric接下来对这款滤镜效果进行了全面测评。


    可能是只在特定的线框内才能生效,当Eric Jang转头的时候,滤镜并未发挥效果。



    随后,其进行了遮挡测试。在脸上滑动一个物体。当仅有半边脸被遮挡时,滤镜就能正常工作,但如果脸部被遮挡的太多,滤镜不发挥作用。如下所示~



    然后是下垂直遮挡。在测试的过程中,白色的瓶子变模糊了。而且,当Eric Jang把瓶子放在视线中央时,头发变成了金黄色。


    Eric Jang认为这个效果一定是机器学习在起作用,因为它会从训练过的数据中提取一些数据进行渲染。


    当敷上面膜会是什么效果?面膜遮住一半可以,但是如果超过一定比例,滤镜转换失败。


    遮挡小部分,滤镜工作没问题。



    接下来是对头发的测试~



    当用手对头发进行抚摸时出现了鬼畜,据Eric Jang介绍,头发上还有一个清晰的分割面罩,可以让脸部显露出来。Snapchat可能正在进行头部的跟踪,通过计算添加长发效果。


    背后原理猜想:CycleGAN大显神威


    关于背后的原理,Eric Jang猜测可能采用了CycleGAN架构,数据集采用的是Snapchat运营这8年用户上传的数据。初步估计,光自拍照就有数十亿张!


    在介绍CycleGAN之前,我们先介绍一下GAN。


    生成对抗网络 由两个相互博弈的神经网络组成,即生成器和鉴别器。生成器负责根据输入生成数据(输入可以是噪声,也可以是一些其他的数据)。鉴别器负责分析数据,并区分这些数据是真实的(来自数据集),或者是虚假的(来自生成器)。在形式上可以看做武学中的左右互博。




    上面公式下标 G 和 D 分别代表生成器 G(Generator)和鉴别器 D(Discriminator)。生成器的工作是将方程的值最小化,而鉴别器负责将这个值最大化。生成器 G 和鉴别器 D 会一直博弈,直到达到我们的满意。


    CycleGAN本质上是两个镜像对称的GAN,构成了一个环形网络。


    两个GAN共享两个生成器,并各自带一个判别器,即共有两个判别器和两个生成器。一个单向GAN两个loss,两个即共四个loss。


    CycleGAN原理


    CycleGAN的创新点就在于其能够在没有成对训练数据的情况下,将图片内容从源域迁移到目标域。CycleGAN在训练时,只需要将源域的图片和目标域的图片作为输入即可,这里并不要求源域跟目标域的图像内容是匹配的。


    简而言之,CycleGAN可以帮助你训练一个网络,这个网络可以把一组数据集(输入域)中同样风格的图片转换成另一组数据集(目标域)里纹理的图片。


    CycleGAN 解决了pix2pix 必须使用成对数据进行训练的问题,原理比较简单,但非常有效。只需要不同域的一系列图片即可进行训练。类似的工作还有DualGAN,DiscoGAN。三者的想法和模型基本一样,发在了不同的地方。


    CycleGAN在转换图像类型方面确实做的非常好,下面是一些网友用CycleGAN做的实例~


    将猫变成狗(图片来自网络)


    让图片中的人露出笑容(图片来自网络)


    将男人变成女人(图片来自网络)


    在猜想的同时,Eric Jang也提出了自己的疑问,主要集中在以下几点:


    1.他们训练的图像转换器真的是无需成对图像的吗?鉴于CycleGAN存在很多问题,做到这件事情几乎不可能。


    2.在数据集方面,他们可能有一些数据是手工设计的。作用是增强数据。


    3.头发和面部的变换似乎是各自独立合成的,或者可能是一起合成的,并在渲染之前分割成不同的图层。


    4.Snapchat确实有一些平滑的功能,它可以根据遮挡物体的状态改变高光和头发颜色,这表明颜色可能部分是从数据中习得的。


    5.另一方面,头发非常稳定,或许不是由GAN合成的。可能存在一个大型的haridos模板库,并且用一些机器学习模型进行了改进。


    6.如何明确CycleGAN在这么大的数据集上训练以后到底收敛没有?


    7.动态图的分辨率是多少,如果在有限的计算资源下运行神经网络?


    8.如果它确实是一个CycleGAN,那么将男性滤镜应用于女性滤镜图像的时候应该恢复成原始图像才对呀?


    小伙伴们如果有自己关于原理的猜想,可以在下文给出哟~


    相关报道:

    https://www.jianshu.com/p/64bf39804c80

    https://yq.aliyun.com/articles/229300

    https://www.reddit.com/r/artificial/comments/bo8pam/snapchat_male_to_female_baby_filter_snapchat_new/

    https://www.reddit.com/r/MachineLearning/comments/bo4orw/d_is_the_new_snapchat_gender_filter_ganbased/

    展开全文
  • 2019独角兽企业重金招聘Python工程师标准>>> select decode(性别,'男',1,2) from 表名 转载于:https://my.oschina.net/u/2336787/blog/414176
  • SELECT CASE(字段) WHEN 1 then '是' ELSE '否' END AS 字段 FROM 表
  • 性别转换的查询语句

    2007-10-14 21:02:00
    select a= case sex when 1 then 男 when 0 then 女 else 中 end from users 
  • 配置性别枚举转换

    2019-10-16 21:09:47
    数据库性别存入0(女),1(男) 返回显示男女 @Getter public enum Gender { MALE(0, "男"), FEMALE(1, "女"); Gender(int code, String desc) { this.code = code; this.desc = desc; } //配置枚举...
  • 性别 数据类型转换 String转Byte

    千次阅读 2019-05-17 10:44:30
    数据库中性别数据类型为tinyint,而从...因为性别只用一位数表示,所以我使用了强制数据类型转换: JSONObject userInfoJO = JSONObject.fromObject(userInfoJson); String user_sex = userInfoJO.getString("sex");;...
  • 两种转换mysql数据编码的方法背景:某个系统的mysql数据库dnname采用默认的latin1字符集,系统升级需要将所有数据转换成utf-8格式,目标数据库为newdbname(建库时使用utf8)方法一:步骤一 命令行执行:mysqldump --...
  • bitsCN.comMYSQL转换编码的解决方法一、在utf8的mysql下得到中文‘游客’的gbk下的16进制编码mysql> SELECT hex(CONVERT( '游客' USING gbk ));-> D3CEBFCD反推gbk的16进制编码,取回中文mysql> SELECT ...
  • 《Mysql实例mysql编码转换 mysql编码设置详解》要点:本文介绍了Mysql实例mysql编码转换 mysql编码设置详解,希望对您有用。如果有疑问,可以联系我们。导读:查看mysql编码:一、mysql> show variables like ...
  • ) 之前一直用这个方法转换mysql 的编码 从Latin1 到 UTF8 ,这次有一个70多w条的数据库,要把数据的编码从latin1转换到utf8,没想到使用这个方法,随着记录的增多,数据的插入越来越慢。 当把新表的主键删掉后,速度...
  • 打算把一个数据库编码为 utf8 的数据库,转换成gbk 来存储。以目前mysql的版本,先做一个转换测试..在数据很大量的情况下,我认为很有必要呵呵。 记下来备忘了。目前mysql状态mysql 版本: Server version: 5.1.41-...
  • (1)创建对象类 using System; using System.Collections.Generic; using System.Linq; using System.Text;...namespace _查询结果转换 { public class Student { //TSId, TSName, TSGender, TSAddress, TSAge,
  • 要求通过旺旺号获得对方的店铺shopid和基本资料,虽然官方有这样的接口,但是已经停批了,根本申请不下来,在这种情况下要想实现旺旺号名称或者userid转到shopid、LOGO、公司名称、店铺动态评分、城市地址、性别(C...
  • 举例table: 将table标签及内容放在repeater的HeaderTemplate、ItemTemplate、FooterTemplate中使用;
  • SQL语句中,0 1 转换为 男 女 性别

    千次阅读 2010-05-28 10:51:07
    新的数据库设计中,用1 0 代表男 女 性别,这就造成了一个客观需要:将数字字符或者字母转换为中文的意思或者含义。 也就是说需要将缩写、简写和代写,转换为原有的意思。经过查询和测验,特将合用的SQL语句列出: ...
  • <p>How to convert val(sex) from javascript to ... <p></p> <pre class="snippet-code-html lang-html prettyprint-override"><code> <... $('#edit_gv').on('show.bs.modal', function (e) { ... </div>

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,069
精华内容 427
关键字:

性别转换