风格迁移(Style Transfer)

1. 什么是风格迁移(Style Transfer)

        简单介绍风格迁移的概念,指的是将一张图像的内容与另一张图像的艺术风格结合起来,从而生成一个新的图像。例如,将一张风景图像的内容与一幅著名艺术作品(如梵高的《星夜》)的风格结合。

  • 应用场景:风格迁移常用于图像生成、艺术创作和增强现实等领域。
  • 目标:本文将讲解如何使用 PyTorch 和 VGG19 模型实现风格迁移,并展示其核心代码。

2.风格迁移的原理

在这一部分,深入介绍风格迁移的工作原理,帮助读者理解为什么该方法有效。

  • 内容损失与风格损失:风格迁移的核心思想是计算两个损失:
    • 内容损失:保留目标图像的内容结构不变。
    • 风格损失:保留目标图像的风格元素(如色调、纹理等)。
  • VGG19 模型的作用:VGG19 是一种常用的预训练卷积神经网络,用于提取图像的高层特征。我们通过它提取图像的内容特征和风格特征,进而计算损失。

3. 环境设置与依赖安装

介绍所需的依赖和环境设置。为了能够运行代码,读者需要:

  • Python 3
  • PyTorch 和 torchvision
  • matplotlib
  • PIL(Python Imaging Library
pip install torch torchvision matplotlib Pillow

4. 代码实现

这部分是技术博文的核心部分,逐步解析代码。可以分为以下几个小节来讲解:

4.1 设置计算设备
  • 解释为什么需要选择计算设备(CPU 或 GPU),以及如何在 PyTorch 中设置。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
4.2 定义风格层和内容层
  • 解释什么是“风格层”和“内容层”,这些层是从 VGG19 模型中提取的特定卷积层,用于计算内容损失和风格损失
style_layers = ['0', '5', '10', '19', '28']
content_layers = ['21']
import torch
import torch.optim as optim
import torchvision
from torchvision import transforms
from PIL import Image
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import torch.nn.functional as F

# 设置计算设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# ==================== 定义特征层 ====================
style_layers = ['0', '5', '10', '19', '28']  # 风格层
content_layers = ['21']  # 内容层


# ==================== 图像加载处理 ====================
def image_loader(image_name, max_size=400):
    loader = transforms.Compose([
        transforms.Resize((max_size, max_size)),  # 固定尺寸
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406],
                             std=[0.229, 0.224, 0.225])
    ])
    image = Image.open(image_name).convert("RGB")
    image = loader(image).unsqueeze(0)
    return image.to(device, torch.float)


# ==================== 模型加载 ====================
def load_vgg():
    weights = torchvision.models.VGG19_Weights.IMAGENET1K_V1
    vgg = torchvision.models.vgg19(weights=weights).features
    for param in vgg.parameters():
        param.requires_grad_(False)
    return vgg.to(device).eval()


vgg = load_vgg()


# ==================== 特征提取 ====================
def get_features(image, model, layers):
    features = []
    x = image
    for name, layer in model._modules.items():
        x = layer(x)
        if name in layers:
            features.append(x)
    return features


# ==================== 损失计算 ====================
def content_loss(content, target):
    # 调整目标张量的尺寸以匹配输入张量
    if content.shape != target.shape:
        target = F.interpolate(target, size=content.shape[2:], mode='bilinear', align_corners=False)
    return F.mse_loss(content, target)


def gram_matrix(input_tensor):
    a, b, c, d = input_tensor.size()
    features = input_tensor.view(a * b, c * d)
    G = torch.mm(features, features.t())
    return G.div(a * b * c * d)


def style_loss(style, target):
    return F.mse_loss(gram_matrix(style), gram_matrix(target))


# ==================== 优化迁移过程 ====================
def run_style_transfer(content_img, style_img, model,
                       style_layers, content_layers,  # 添加层参数
                       num_steps=300,
                       style_weight=1e6,
                       content_weight=1):
    target = content_img.clone().requires_grad_(True)
    optimizer = optim.Adam([target], lr=0.02)

    # 预提取特征
    with torch.no_grad():
        style_features = get_features(style_img, model, style_layers)
        content_features = get_features(content_img, model, content_layers)

    for step in range(num_steps):
        def closure():
            optimizer.zero_grad()
            target_features = get_features(target, model, style_layers + content_layers)

            # 计算内容损失
            content_loss_value = content_loss(
                target_features[len(style_layers)],
                content_features[0]
            )

            # 计算风格损失
            style_loss_value = 0
            for i in range(len(style_layers)):
                style_loss_value += style_loss(
                    target_features[i],
                    style_features[i]
                )

            total_loss = content_weight * content_loss_value + style_weight * style_loss_value
            total_loss.backward()

            if step % 50 == 0:
                print(f"Step {step}, Total Loss: {total_loss.item():.2f}")
            return total_loss

        optimizer.step(closure)

    return target


# ==================== 主流程 ====================
if __name__ == "__main__":
    # 加载图像
    content_img = image_loader("img_1.png")
    style_img = image_loader("img.png", max_size=content_img.shape[2])

    # 执行风格迁移(传递层参数)
    output_img = run_style_transfer(
        content_img,
        style_img,
        vgg,
        style_layers,  # 传递风格层
        content_layers,  # 传递内容层
        num_steps=300
    )


    # 结果可视化
    def imshow(tensor, save_path="output.jpg"):
        image = tensor.cpu().clone().squeeze(0)
        image = image.permute(1, 2, 0).detach().numpy()
        image = image * np.array([0.229, 0.224, 0.225]) + np.array([0.485, 0.456, 0.406])
        image = np.clip(image, 0, 1)
        plt.imshow(image)
        plt.axis('off')
        plt.savefig(save_path, bbox_inches='tight', pad_inches=0)
        plt.close()


    imshow(output_img)
    print("风格迁移完成,结果已保存为output.jpg")

Copy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值