【生成式网络】入门篇(四):CycleGAN 的 代码和结果记录

news2024/11/29 1:30:20

CycleGAN是一个里程碑式的工作,开启了unpaired的风格迁移的先河,斑马转马的效果还是很震惊。
具体原理可以参考 https://zhuanlan.zhihu.com/p/402819206

在这里插入图片描述
老习惯,直接上code,然后按照code进行一些解释
代码参考自 https://github.com/aitorzip/PyTorch-CycleGAN 相对比较简洁,我进行了一些小修改

import os
# os.chdir(os.path.dirname(__file__))
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torchvision
from torchvision import transforms
from torchvision import datasets
from torchvision import models
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image
import argparse
from glob import glob
import random
import itertools

## from https://github.com/aitorzip/PyTorch-CycleGAN

sample_dir = 'samples_cycle_gan'
if not os.path.exists(sample_dir):
    os.makedirs(sample_dir, exist_ok=True)

writer = SummaryWriter(sample_dir)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
np.random.seed(0)
torch.manual_seed(0)

class ImageDataset(torch.utils.data.Dataset):
    def __init__(self, root, transforms=None, unaligned=False, mode='train'):
        self.transforms = transforms
        self.unaligned = unaligned
        self.files_A = sorted(glob(os.path.join(root, mode, 'A', '*.*')))
        self.files_B = sorted(glob(os.path.join(root, mode, 'B', '*.*')))

    def __getitem__(self, idx):
        img = Image.open(self.files_A[idx % len(self.files_A)]).convert('RGB')
        itemA = self.transforms(img)

        if self.unaligned:
            rand_idx = random.randint(0, len(self.files_B)-1)
            img = Image.open(self.files_B[rand_idx]).convert('RGB')
            itemB = self.transforms(img)
        else:
            img = Image.open(self.files_B[idx % len(self.files_B)]).convert('RGB')
            itemB = self.transforms(img)

        return {
            'A' : itemA,
            'B' : itemB
        }

    def __len__(self):
        return max(len(self.files_A), len(self.files_B))


class ResidualBlock(nn.Module):
    def __init__(self, in_features):
        super(ResidualBlock, self).__init__()

        self.conv_block = nn.Sequential(
            nn.ReflectionPad2d(1),
            nn.Conv2d(in_features, in_features, 3),
            nn.InstanceNorm2d(in_features),
            nn.ReLU(inplace=True),
            nn.ReflectionPad2d(1),
            nn.Conv2d(in_features, in_features, 3),
            nn.InstanceNorm2d(in_features) 
        )

    def forward(self, x):
        return x + self.conv_block(x)

class Generator(nn.Module):
    def __init__(self, input_nc, output_nc, n_res_blocks=9):
        super(Generator, self).__init__()

        # init basic conv block
        model = [
            nn.ReflectionPad2d(3),
            nn.Conv2d(input_nc, 64, 7),
            nn.InstanceNorm2d(64),
            nn.ReLU(inplace=True)
        ]

        # downsampling
        in_features = 64
        out_features = in_features * 2
        for _ in range(2):
            model += [
                nn.Conv2d(in_features, out_features, 2, stride=2, padding=1),
                nn.InstanceNorm2d(out_features),
                nn.ReLU(inplace=True)
            ]
            in_features = out_features
            out_features = in_features * 2
        
        # residual blocks
        for _ in range(2):
            model += [ResidualBlock(in_features)]   

        # upsampling
        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
        
        # output layer
        model += [
            nn.ReflectionPad2d(3),
            nn.Conv2d(64, output_nc, 11),
            nn.Tanh()
        ]

        self.model = nn.Sequential(*model)

    def forward(self, x):
        return self.model(x)

class Discriminator(nn.Module):
    def __init__(self, input_nc):
        super(Discriminator, self).__init__()

        # A bunch of convolutions one after another
        self.model = nn.Sequential(
            nn.Conv2d(input_nc, 64, 4, stride=2, padding=1),
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(64, 128, 4, stride=2, padding=1),
            nn.InstanceNorm2d(128), 
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(128, 256, 4, stride=2, padding=1),
            nn.InstanceNorm2d(256), 
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(256, 512, 4, padding=1),
            nn.InstanceNorm2d(512), 
            nn.LeakyReLU(0.2, inplace=True),

            nn.Conv2d(512, 1, 4, padding=1)
        )

    def forward(self, x):
        x = self.model(x)
        # average pooling and flatten
        return F.avg_pool2d(x, x.size()[2:]).view(x.size()[0], -1)

class ReplayBuffer():
    def __init__(self, max_size=50):
        assert (max_size > 0), 'Empty buffer or trying to create a black hole. Be careful.'
        self.max_size = max_size
        self.data = []

    def push_and_pop(self, data):
        to_return = []
        for element in data.data:
            element = torch.unsqueeze(element, 0)
            if len(self.data) < self.max_size:
                self.data.append(element)
                to_return.append(element)
            else:
                if random.uniform(0,1) > 0.5:
                    i = random.randint(0, self.max_size-1)
                    to_return.append(self.data[i].clone())
                    self.data[i] = element
                else:
                    to_return.append(element)
        return torch.cat(to_return)

class LambdaLR():
    def __init__(self, n_epochs, offset, decay_start_epoch):
        assert ((n_epochs - decay_start_epoch) > 0), "Decay must start before the training session ends!"
        self.n_epochs = n_epochs
        self.offset = offset
        self.decay_start_epoch = decay_start_epoch

    def step(self, epoch):
        return 1.0 - max(0, epoch + self.offset - self.decay_start_epoch)/(self.n_epochs - self.decay_start_epoch)

def weights_init_normal(m):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        torch.nn.init.normal(m.weight.data, 0.0, 0.02)
    elif classname.find('BatchNorm2d') != -1:
        torch.nn.init.normal(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant(m.bias.data, 0.0)


def denorm(x):
    out = (x+1)/2
    return out.clamp(0, 1)

# Networks
input_nc = 3
output_nc = 3
learning_rate = 0.0002
n_epochs = 200
decay_epoch = 100
start_epoch = 0
batch_size = 16
input_size = 256
dataroot = 'data/cycle_gan/datasets/horse2zebra'

netG_A2B = Generator(input_nc, output_nc).to(device)
netG_B2A = Generator(output_nc, input_nc).to(device)
netD_A = Discriminator(input_nc).to(device)
netD_B = Discriminator(output_nc).to(device)

netG_A2B.apply(weights_init_normal)
netG_B2A.apply(weights_init_normal)
netD_A.apply(weights_init_normal)
netD_B.apply(weights_init_normal)

# Losses
criterion_GAN = torch.nn.MSELoss()
criterion_cycle = torch.nn.L1Loss()
criterion_identity = torch.nn.L1Loss()

# optimizer
optimizer_G = torch.optim.Adam(itertools.chain(netG_A2B.parameters(), netG_B2A.parameters()), 
                            lr=learning_rate, betas=(0.5, 0.999))
optimizer_D_A = torch.optim.Adam(netD_A.parameters(), lr=learning_rate, betas=(0.5, 0.999))
optimizer_D_B = torch.optim.Adam(netD_B.parameters(), lr=learning_rate, betas=(0.5, 0.999))

# lr schedulers
lr_scheduler_G = torch.optim.lr_scheduler.LambdaLR(optimizer_G, lr_lambda=LambdaLR(n_epochs, start_epoch, decay_epoch).step)
lr_scheduler_D_A = torch.optim.lr_scheduler.LambdaLR(optimizer_D_A, lr_lambda=LambdaLR(n_epochs, start_epoch, decay_epoch).step)
lr_scheduler_D_B = torch.optim.lr_scheduler.LambdaLR(optimizer_D_B, lr_lambda=LambdaLR(n_epochs, start_epoch, decay_epoch).step)

# Inputs & targets memory allocation
target_real = torch.ones(batch_size, requires_grad=False).to(device)
target_fake = torch.zeros(batch_size, requires_grad=False).to(device)


# Dataset loader
transforms_data = transforms.Compose([ 
                transforms.Resize(int(input_size*1.12), Image.BICUBIC), 
                transforms.RandomCrop(input_size), 
                transforms.RandomHorizontalFlip(),
                transforms.ToTensor(),
                transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) 
                ])

dataset = ImageDataset(dataroot, transforms=transforms_data, unaligned=True)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=16, drop_last=True)


fake_A_buffer = ReplayBuffer()
fake_B_buffer = ReplayBuffer()


###### Training ######
cnt = 0
log_step = 10
for epoch in range(start_epoch, n_epochs):
    for i, batch in enumerate(dataloader):
        # set model input
        real_A = batch['A'].to(device)
        real_B = batch['B'].to(device)

        ###### Generators ######
        # generators A2B and B2A
        optimizer_G.zero_grad()

        ### identity loss
        # G_A2B(B) should equal B if real B is fed
        same_B = netG_A2B(real_B)
        loss_identity_B = criterion_identity(same_B, real_B) * 5.0
        # G_B2A(A) should equal A if real A is fed
        same_A = netG_B2A(real_A)
        loss_identity_A = criterion_identity(same_A, real_A) * 5.0    

        ### GAN loss
        fake_B = netG_A2B(real_A)
        pred_fake = netD_B(fake_B)
        loss_GAN_A2B = criterion_GAN(pred_fake, target_real)

        fake_A = netG_B2A(real_B)
        pred_fake = netD_A(fake_A)
        loss_GAN_B2A = criterion_GAN(pred_fake, target_real)

        ### Cycle loss
        recovered_A = netG_B2A(fake_B)
        loss_cycle_ABA = criterion_cycle(recovered_A, real_A) * 10.0

        recovered_B = netG_A2B(fake_A)
        loss_cycle_BAB = criterion_cycle(recovered_B, real_B) * 10.0

        # Total loss
        loss_G = loss_identity_A + loss_identity_B + loss_GAN_A2B + loss_GAN_B2A + loss_cycle_ABA + loss_cycle_BAB
        loss_G.backward()
        
        optimizer_G.step()
        ###################################

        ###### Discriminator A ######
        optimizer_D_A.zero_grad()
        # real loss
        pred_real = netD_A(real_A)
        loss_D_real = criterion_GAN(pred_real, target_real)
        # fake loss
        fake_A = fake_A_buffer.push_and_pop(fake_A)
        pred_fake = netD_A(fake_A)
        loss_D_fake = criterion_GAN(pred_fake, target_fake)
        # total loss
        loss_D_A = (loss_D_real + loss_D_fake) * 0.5
        loss_D_A.backward()
        optimizer_D_A.step()

        ###### Discriminator B ######
        optimizer_D_B.zero_grad()
        # real loss
        pred_real = netD_B(real_B)
        loss_D_real = criterion_GAN(pred_real, target_real)
        # fake loss
        fake_B = fake_B_buffer.push_and_pop(fake_B)
        pred_fake = netD_B(fake_B)
        loss_D_fake = criterion_GAN(pred_fake, target_fake)
        # total loss
        loss_D_B = (loss_D_real + loss_D_fake) * 0.5
        loss_D_B.backward()
        optimizer_D_B.step()

        cnt += 1
        if cnt % log_step == 0:
            print('Epoch [{}/{}], Step [{}], LossG: {:.4f}, loss_D_A: {:.4f}, loss_D_B: {:.4f}'.\
                format(epoch, n_epochs, cnt, loss_G.item(), loss_D_A.item(), loss_D_B.item()))

            writer.add_scalar('LossG', loss_G.item(), global_step=cnt)
            writer.add_scalar('loss_D_A', loss_D_A.item(), global_step=cnt)
            writer.add_scalar('loss_D_B', loss_D_B.item(), global_step=cnt)
        if cnt % 100 == 0:
            writer.add_images('real_A', denorm(real_A), global_step=cnt)
            writer.add_images('fake_A', denorm(fake_A), global_step=cnt)
            writer.add_images('recovered_A', denorm(recovered_A), global_step=cnt)
            writer.add_images('real_B', denorm(real_B), global_step=cnt)
            writer.add_images('fake_B', denorm(fake_B), global_step=cnt)
            writer.add_images('recovered_B', denorm(recovered_B), global_step=cnt)

    # Update learning rates
    lr_scheduler_G.step()
    lr_scheduler_D_A.step()
    lr_scheduler_D_B.step()
    # Save models checkpoints
    torch.save(netG_A2B.state_dict(), sample_dir + '/netG_A2B.pth')
    torch.save(netG_B2A.state_dict(), sample_dir + '/netG_B2A.pth')
    torch.save(netD_A.state_dict(), sample_dir + '/netD_A.pth')
    torch.save(netD_B.state_dict(), sample_dir + '/netD_B.pth')

我们来根据代码进行解读, 首先一个样本里是包含了A和B两张图,称为real_A 和 real_B。
定义了生成网络netG_A2B和 netG_B2A

先看Generators 部分

        ###### Generators ######
        # generators A2B and B2A
        optimizer_G.zero_grad()

        ### identity loss
        # G_A2B(B) should equal B if real B is fed
        same_B = netG_A2B(real_B)
        loss_identity_B = criterion_identity(same_B, real_B) * 5.0
        # G_B2A(A) should equal A if real A is fed
        same_A = netG_B2A(real_A)
        loss_identity_A = criterion_identity(same_A, real_A) * 5.0    

        ### GAN loss
        fake_B = netG_A2B(real_A)
        pred_fake = netD_B(fake_B)
        loss_GAN_A2B = criterion_GAN(pred_fake, target_real)

        fake_A = netG_B2A(real_B)
        pred_fake = netD_A(fake_A)
        loss_GAN_B2A = criterion_GAN(pred_fake, target_real)

        ### Cycle loss
        recovered_A = netG_B2A(fake_B)
        loss_cycle_ABA = criterion_cycle(recovered_A, real_A) * 10.0

        recovered_B = netG_A2B(fake_A)
        loss_cycle_BAB = criterion_cycle(recovered_B, real_B) * 10.0

        # Total loss
        loss_G = loss_identity_A + loss_identity_B + loss_GAN_A2B + loss_GAN_B2A + loss_cycle_ABA + loss_cycle_BAB
        loss_G.backward()
        
        optimizer_G.step()

生成网络包含三部分的loss

  • identity loss。netG_A2B 是把A风格图像转换为B风格,那么我们应该保证把B风格图像丢进去,出来的依然是B风格的原图,这部分loss就叫 identity loss,同理,对于netG_B2A也由此约束。
  • GAN loss。就是场景的generator的loss,对亮哥generator而言,生成的fake图像应该让他label误判为real 的label。
  • cycle loss。把A丢进netG_A2B,生成B风格图后,再丢进netG_B2A,理论上应该转换回A风格,这部分约束就是cycle loss,同理,对于netG_B2A也由此约束。

再看DiscriminatorA 部分, DiscriminatorB同理。
就是正常GAN里的Discriminator loss,应该把真的识别为真,假的识别为假。

        optimizer_D_A.zero_grad()
        # real loss
        pred_real = netD_A(real_A)
        loss_D_real = criterion_GAN(pred_real, target_real)
        # fake loss
        fake_A = fake_A_buffer.push_and_pop(fake_A)
        pred_fake = netD_A(fake_A)
        loss_D_fake = criterion_GAN(pred_fake, target_fake)
        # total loss
        loss_D_A = (loss_D_real + loss_D_fake) * 0.5
        loss_D_A.backward()
        optimizer_D_A.step()

可以再看看Generator的网络部分,
整体结构跟fast style transfer 非常像,也是先降采样,再residual,最后上采样,并且也用了ReflectionPad2d。
并且代码里用的是nn.InstanceNorm2d

Discriminator就没太多可说的了,几层卷积下来,变成一个batchsize * 1 * h * w 的tensor,最后用一个avg_pool2d得到batchsize * 1 的分类结果,没有用全连接层。

里面还需要提一下的,是用了一个ReplayBuffer机制,我的理解是在做分类的时候把fakeA和fakeB扔进buffer里,然后取出一个buffer里存的来,这样做分类的时候引入了别的batch里的数据,我猜测可能是为了避免discriminator能力集中在区分这种一对对的样本上,而是变得可以见到更多正负样本对。

不过也是因为这个机制,导致我训练的时候打印出的原图和fake图不是一一对应的,不方便看效果,不过这个很容易修改,我就偷懒了。

我们看效果 A是普通马,B是斑马
转换之后,这是变普遍马的效果
在这里插入图片描述

这是变斑马的效果

在这里插入图片描述
不算特别好,比文章的效果差远了,应该还有很多地方需要调优的,建议想要文章效果的童鞋试试官方代码 https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/49317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自托管书签管理器LinkAce

本文完成于 9 月下旬&#xff0c;当时的版本是 v1.10.4&#xff0c;发稿时最新版本为 v1.10.5 什么是 LinkAce &#xff1f; LinkAce 是一个自托管档案&#xff0c;用于收集您喜爱的网站的链接&#xff0c;并保存文章以供日后阅读。LinkAce 提供了一个长期存档来存储指向网站、…

NVIDIA 7th SkyHackathon(二)开发套件的安装与测试

1.NeMo 开源工具包 1.1 关于 NeMo NeMo&#xff08;Neural Modules&#xff09;是 NVIDIA 发布的基于 PyTorch 的开源工具包&#xff0c;它允许开发者快速构建、训练和微调会话式人工智能模型 NeMo 由 NeMo Core 和 NeMo Collection 组成&#xff0c;NeMo Core 为所有模型和…

[附源码]Python计算机毕业设计Django的中点游戏分享网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

内容理解之情感计算

导语 概念定义&#xff1a;情感计算是自然语言处理领域的重要研究方向之一&#xff0c;其目标是赋予计算机类似于人一样的观察、理解和生成各种情感表达的能力&#xff0c;它是一个高度综合化的跨学科领域&#xff0c;涉及计算机科学、心理学、社会学和认知科学等。通过多学科…

物联网卡有哪些类型

伴随着科学技术的不断升级&#xff0c;不断发展&#xff0c;5G和物联网必定成为未来生活的主角&#xff0c;而现在5G已经慢慢在我们生活越来越常见&#xff0c;这是我们肉眼可以看见的&#xff0c;其实物联网如同5G一样&#xff0c;在我们生活中也随处可见&#xff0c;如我们平…

谈谈Go语言中函数的本质

在很多编程语言中&#xff0c;都会有函数一说&#xff0c;今天我们来聊聊Go语言中的函数。 废话不多说&#xff0c;咱们直接上代码~ 代码 package mainimport "fmt"func main() {fmt.Printf("%T\n", function1)fmt.Printf("%T\n", function2)…

跨越速运如何构建实时统一的运单分析

作者&#xff1a;张杰&#xff0c;跨越速运大数据架构师&#xff08;本文为作者在 StarRocks Summit Asia 2022 上的分享&#xff09; 作为大型现代化综合速运企业&#xff0c;跨越速运拥有 3000 多家服务网点 &#xff0c;日均处理 30 多万票运单。海量运单数据涌来&#xff…

博科交换机使用

博科交换机使用 ip查询 博科交换机的默认IP地址是10.77.77.77&#xff0c;用户名admin&#xff0c;密码&#xff1a;password。 ipaddrshowip修改 ipaddrset端口查询 交换机的端口表示为&#xff08;A,B&#xff09;或者&#xff08;A,B;C,D&#xff09;。 A,C表示交换机的…

使用JLINK给GD32下载程序

使用JLINK给GD32下载程序关于GD32单片机需要的工具和软件包①Jlink仿真器一个②相关软件包下载准备①选择好芯片②在DEBUG中选择JLINK下载现象总结关于GD32单片机 GD32是兆易创新基于Arm Cortex-M内核和RISC-V内核&#xff0c;推出的32位通用微控制器&#xff0c;对比了下两者…

[附源码]Python计算机毕业设计SSM流浪宠物申领信息平台(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java中进制基础知识与算法题

本篇文章旨在给大家普及下计算机内部数据的机器级表示方式&#xff0c;即&#xff1a;二进制、八进制、十进制、十六进制… 对于进制&#xff0c;我们从小最先接触的是十进制&#xff0c;这个也是我们日常生活中应用最多的数值统计方式。然而&#xff0c;现实中我们感觉到的媒体…

python的opencv操作记录(十)——图像融合

文章目录前言opencv中的一个方法泊松融合图像梯度图像散度融合图像散度通过散度场进行图像重建泊松融合的一般逻辑前言 最近碰到一个项目上的难题&#xff0c;是要从电动显微镜对焦的多张图像进行融合。因为&#xff0c;显微镜物镜的景深范围较小&#xff0c;可能在同一视野中…

地理空间数据共享资源大汇总

1.全国12.5米分辨率ALOS高程数据 全国12.5米分辨率DEM数据&#xff0c;该数据由锐多宝的地理空间提供并进行分省裁剪与镶嵌&#xff0c;由测绘营地进行影像坐标系转换。 ALOS-12.5m高程数据参数&#xff1a; 覆盖范围&#xff1a;全国&#xff08;仅有小部分区域存在数据空白…

本机使用python操作hdfs搭建及常见问题

一.虚拟机安装CentOS7并配置共享文件夹 二.CentOS 7 上hadoop伪分布式搭建全流程完整教程 三.本机使用python操作hdfs搭建及常见问题 四.mapreduce搭建 五.mapper-reducer编程搭建 本机使用python操作hdfs搭建及常见问题一、环境搭建1.打开虚拟机系统&#xff0c;打开hadoop2.修…

【JavaScript 逆向】极验四代滑块验证码逆向分析

前言 相较于三代滑块&#xff0c;四代的逻辑流程更简短&#xff0c;底图没混淆&#xff0c;某些点校验不严格 声明 本文章中所有内容仅供学习交流&#xff0c;相关链接做了脱敏处理&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 案例目标 滑动验证码&…

[NAS] QNAP/威联通 常用设置和操作

&#x1f341;简介 QNap 产品是一种可扩展的数据存储解决方案。它们包括具有 1 到 30 个驱动器托架的设备&#xff0c;并提供 HDMI、Thunderbolt 2 和 USB 3.1 等连接选项&#xff0c;以及 802.11ac/a/n Wi-Fi 和高达每秒 40 Gb 的以太网。内置软件提供基本服务&#xff0c;例如…

WeetCode2滑动窗口系列

一丶[无重复字符的最长子串](3. 无重复字符的最长子串 - 力扣&#xff08;Leetcode&#xff09;)# 思路:# 维护一个窗口&#xff0c;窗口中不存在重复的字符&#xff0c;窗口右边界从第一个字符移动到最后&#xff0c;使用一个变量记录窗口大小的最大值 那么问题就变成了&…

浅谈h264和h265的区别

相比h264&#xff0c;压缩同样的视频获得同样的质量的情况下&#xff0c;h265可以做到压缩后的大小为前者的一半&#xff0c;但压缩时间复杂度增加。h264编码单元为宏块(MB)&#xff0c;最大划分为16x16&#xff0c;而h265编码单元为编码树单元(CTU)&#xff0c;最大划分为64x6…

C++ 不知算法系列之深入动态规划算法思想

1. 前言 前面写过一篇博文&#xff0c;介绍了什么是动态规划算法。动态规划算法的最大特点&#xff0c;原始问题可以通过分解成规模更小的子问题来解决&#xff0c;子问题之间互成依赖关系&#xff0c;先计算出来的子问题的结果会影响到后续子问题的结果。 有点类似于武侠片中…

Python——变量以及基础数据类型练习题

要求&#xff1a;注意变量名的命名规范问题&#xff01;&#xff01;&#xff01;不能再出现没有意义的变量名&#xff01;&#xff01;&#xff01;一行一注释&#xff0c;用下划线命名法。 请使用相对应的数据类型&#xff0c;不能全部使用字符串&#xff01;&#xff01;&a…