Глубоко интересно | 20 Преобразование пола CycleGAN

искусственный интеллект GitHub HTTPS Apple
Глубоко интересно | 20 Преобразование пола CycleGAN

Введение

Представьте модели CycleGAN, которые можно использовать для реализации различных задач перевода непарных изображений и выполнения задач преобразования пола.

принцип

В отличие от pix2pix, CycleGAN не требует строго парных изображений, а нуждается только в двух категориях (доменах), например, одна папка заполнена картинками яблок, а другая - оранжевыми картинками.

Используя два типа изображений, A и B, вы можете реализовать перевод A в B и перевод B в A.

Подробные примеры и введения представлены на официальном сайте газеты,junyanz.github.io/CycleGAN/, такие как яблоки и апельсины, лошади и зебры, лето и зима, фотографии и произведения искусства и т. д.

CycleGAN非配对图像翻译示例

и официальный проект Github для статьи,GitHub.com/military training/C-сторона…, реализованный с помощью PyTorch

CycleGAN состоит из двух генераторов G и F и двух дискриминаторов Dx и Dy.

CycleGAN模型结构

G принимает истинное X и выдает ложное Y, то есть завершает преобразование из X в Y; F принимает истинное Y и выводит ложное X, то есть завершает преобразование из Y в X; Dx принимает истинное и ложное X и различает, и Dy принимает true False Y и различает

Функция потерь CycleGAN аналогична стандартной GAN, но записываются только два набора.

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)))]
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)))]

Кроме того, чтобы избежать проблемы коллапса режима, CycleGAN также учитывает потерю согласованности цикла (потеря согласованности цикла).

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,D_X,D_Y)=L_{GAN}(G,D_Y,X,Y)+L_{GAN}(F,D_X,Y,X)+\lambda L_{cyc}(G,F)

выполнить

В конкретной реализации статьи используются два приема

  • Используйте метод наименьших квадратов потерь, т. е. метод наименьших квадратов вместо стандартных потерь GAN.
  • Взяв в качестве примера G, создайте коллекцию исторических поддельных изображений Y, например, 50 изображений. Каждый раз, когда G генерирует фальшивый Y, он добавляется к набору, а затем фальшивый Y случайным образом берется из набора и вводится в дискриминатор вместе с реальным Y. Таким образом, ложный набор Y представляет собой среднюю способность G генерировать Y из X, что делает обучение более стабильным.

Обучите модель CycleGAN, используя следующий проект:GitHub.com/van Huyanzhuo/C сторона…, в основном включающий несколько кодов:

  • build_data.py: Организация данных изображений в файлы tfrecords.
  • ops.py: определяет несколько небольших сетевых модулей
  • generator.py: Определение генератора
  • discriminator.py: Определение дискриминатора
  • model.py: Определите CycleGAN с генератором и дискриминатором
  • train.py: код для обучения модели
  • export_graph.py: Упакуйте обученную модель в.pdдокумент
  • inference.py: используйте упакованный.pbИзображение перевода файла, т.е. использовать модель для вывода

Структура генератора и дискриминатора следующая, если вам интересно, вы можете прочитать исходный код проекта далее

CycleGAN模型细节

гендерный переход

Тренируйте Cocalgan, который реализует гендерное преобразование, используя мужские картинки и женские фотографии в Celeba

Процесс изображения в наборе Celeba в DataSet256*256размера и сохранены в мужских и женских папках в зависимости от пола, содержащих 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

После начала обучения создается папка контрольных точек и подпапка на основе текущей даты и времени, например.20180507-0231, включая те, которые используются для отображения тензорной доскиevents.out.tfeventsфайл и некоторые файлы, связанные с моделью

Используйте tensorboard для просмотра сведений об обучении модели, выполните следующую команду для доступа к порту 6006.

tensorboard --logdir=checkpoints/20180507-0231

Ниже приведена страница IMAGES tensorboard после 185870 итераций.

CycleGAN模型训练tensorboard细节

Ограничения на количество итераций для обучения модели нет, поэтому, если вы чувствуете, что эффект хороший или количество итераций почти одинаково, вы можете прекратить обучение

использоватьexport_graph.pyупаковать модель в.pbфайлы, сгенерированные файлы находятся в предварительно подготовленной папке

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 для завершения двустороннего преобразования пола.

Используйте следующие элементы для определения пола,GitHub.com/with4U/Возраст-а…, с помощью обучающей модели 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затем скачать и установить,woohoo.ffmpeg.org/download.Contract…

Перейдите в командную строку и извлеките звук из исходного видео.

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

Синтезируйте извлеченный звук и результирующее видео вместе

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

разное

Проект также предоставляет четыре обученные модели,GitHub.com/van Huyanzhuo/C сторона…, в том числе яблоки к апельсинам, апельсины к яблокам, кони к зебрам, зебры к коням, если интересно, можно попробовать

Использование CycleGAN может не только завершить преобразование между двумя типами изображений, но также реализовать преобразование между двумя объектами, например, перевод одного человека в другого человека.

Вы можете рассмотреть возможность извлечения изображений, соответствующих двум персонажам из фильма.После обучения CycleGAN вы можете перевести одного человека в другого.

Есть и более смелые попытки,Улучшение навыков вождения: использование GAN для удаления мозаики и одежды из (любовных) боевиков

Ссылаться на

видеоурок

Глубоко и интересно (1)