{%note warning icon%}
本文尚未完成XD
{%endnote%}
关于神经网络的初步学习笔记在这里
在百度百科中寻找“图像滤波器”会得到一段看起来非常让人困惑的文字:
由于成像系统、传输介质和记录设备等的不完善,数字图像在其形成、传输记录过程中往往会受到多种噪声的污染。另外,在图像处理的某些环节当输入的像对象并不如预想时也会在结果图像中引入噪声。这些噪声在图像上常表现为一引起较强视觉效果的孤立像素点或像素块。一般,噪声信号与要研究的对象不相关它以无用的信息形式出现,扰乱图像的可观测信息。对于数字图像信号,噪声表为或大或小的极值,这些极值通过加减作用于图像像素的真实灰度值上,对图像造成亮、暗点干扰,极大降低了图像质量,影响图像复原、分割、特征提取、图像识别等后继工作的进行。要构造一种有效抑制噪声的滤波器必须考虑两个基本问题:能有效地去除目标和背景中的噪声;同时,能很好地保护图像目标的形状、大小及特定的几何和拓扑结构特征 。
理解图像滤波器概念,其实质有二:
图像在计算机里是按照每个位置的像素值存储的,每个像素的颜色,可以用红、绿、蓝、透明度四个值描述,大小范围都是0 ~ 255
,比如黑色是[0, 0, 0, 255]
,白色是[255, 255, 255, 255]
。(也就是我们常用的rgba)
把每一行的像素的rgb值以折线形式绘制出来,就会得到一段图像:
所以可以理解为:图像就是色彩的波动:波动大,就是色彩急剧变化;波动小,就是色彩平滑过渡。
而滤波器的功能,就是将这些波动的变化进行削弱或者放大,例如物理中的:
低通滤波器过滤高频信号,曲线将变得平滑;高通滤波器放大了高频信号,曲线保留下曲折尖锐的部分
在图像中的表现则是:
低通滤波器:图像变得模糊(锐度下降)
高通滤波器:图像只剩下锐度极高的部分,其他部分的色彩丢失
虽然实际应用的滤镜比单纯的高通低通滤波器复杂,但本质上应该也是附加规则的高低通滤波器的组合。
PIL库中的滤镜算法主要涉及到卷积滤镜,即在数字图像的像素矩阵中使用一个n*n的矩阵来滤波(该矩阵即卷积核kernal),以这个矩阵为单位对图像像素进行遍历,每个输出的像素都是区域像素按照一定权重组合计算出的结果,遍历之后输出的图像就是输出的图像。(即依据“规则”通过每个像素点附近的像素值来修改当前像素点的值,遍历修改后就完成了滤波)
这张Gif很好的描述了卷积算法的过程,所以被我偷了下来,嘻嘻
阅读Doc容易发现,在python的PIL库中,ImageFilter类下有许多滤波器可以使用:
• BLUR:模糊滤波
• CONTOUR:轮廓滤波
• DETAIL:细节滤波
• EDGE_ENHANCE:边界增强滤波
• EDGE_ENHANCE_MORE:边界增强滤波(程度更深)
• EMBOSS:浮雕滤波
• FIND_EDGES:寻找边界滤波
• SMOOTH:平滑滤波
• SMOOTH_MORE:平滑滤波(程度更深)
• SHARPEN:锐化滤波
• GaussianBlur(radius=2):高斯模糊
>radius指定平滑半径。
• UnsharpMask(radius=2, percent=150, threshold=3):反锐化掩码滤波
>radius指定模糊半径;
>percent指定反锐化强度(百分比);
>threshold控制被锐化的最小亮度变化。
• Kernel(size, kernel, scale=None, offset=0):核滤波
当前版本只支持核大小为3x3和5x5的核大小,且图像格式为“L”和“RGB”的图像。
>size指定核大小(width, height);
>kernel指定核权值的序列;
>scale指定缩放因子;
>offset指定偏移量,如果使用,则将该值加到缩放后的结果上。
• RankFilter(size, rank):排序滤波
>size指定滤波核的大小;
>rank指定选取排在第rank位的像素,若大小为0,则为最小值滤波;若大小为size * size / 2则为中值滤波;若大小为size * size - 1则为最大值滤波。
• MedianFilter(size=3):中值滤波
>size指定核的大小
• MinFilter(size=3):最小值滤波器
>size指定核的大小
• MaxFilter(size=3):最大值滤波器
>size指定核的大小
• ModeFilter(size=3)**:波形滤波器
选取核内出现频次最高的像素值作为该点像素值,仅出现一次或两次的像素将被忽略,若没有像素出现两次以上,则保留原像素值。
>size指定核的大小
一段简单的代码可以测试两个滤波器:
x1from PIL import ImageFilter
2from PIL import Image
3
4im = Image.open("test.png")
5im_blur = im.filter(ImageFilter.GaussianBlur(radius=5)) # *高斯模糊
6im_contour = im.filter(ImageFilter.CONTOUR) # *轮廓滤波
7im.show()
8im_blur.show()
9im_contour.show()
可以看到不同滤波器带来的不同的改变:
初始化设置卷积核:
xxxxxxxxxx
31core = np.array([[1, 1, 0],
2 [1, 1, -1],
3 [0, -1, -1]]) # *预设值的卷积核
读取图像二维列表形式存储的像素值,并分离三通道:
xxxxxxxxxx
81img = plt.imread("t1.jpg")
2core_line = core.shape[0] // 2 # *获取卷积核行数
3core_row = core.shape[1] // 2 # *获取卷积核列数
4# *分别在行前后添加i行,在列前后添加j列,第三维不填充,填充值为0(不会影响原图像)
5img = np.pad(img, ((core_line, core_line), (core_row, core_row), (0, 0)), 'constant')
6channel_r = img[:, :, 0] # *R通道像素值
7channel_g = img[:, :, 1] # *G通道像素值
8channel_b = img[:, :, 2] # *B通道像素值
执行遍历获取新的像素值并存储到新的列表中:
xxxxxxxxxx
181def calculate(img, core):
2 result = (img * core).sum() # *矩阵乘法获得结果像素值
3 if(result < 0): # *过滤无效像素值
4 result = 0
5 elif result > 255:
6 result = 255
7 return result
8
9channel_line = img.shape[0] - core_line + 1 # *获取图像像素点列数
10channel_row = img.shape[1] - core_row + 1 # *获取图像像素点行数
11
12new_img = np.zeros((channel_line, channel_row),
13 dtype='uint8') # *初始化一个和原图像大小相同的用0填充的图像矩阵
14
15for i in trange(channel_line):
16 for j in range(channel_row):
17 # *调用calculate函数完成每个像素点的滤波计算并赋值给新图像
18 new_img[i][j] = calculate(img[i:i+core_line, j:j+core_row], core)
最后把三通道合并,将函数分块获得最终代码:
xxxxxxxxxx
571import matplotlib.pyplot as plt
2import pylab
3import numpy as np
4from tqdm import trange
5
6
7def collect_channel(img, core):
8 core_line = core.shape[0] // 2 # *获取卷积核行数
9 core_row = core.shape[1] // 2 # *获取卷积核列数
10
11 # *分别在行前后添加i行,在列前后添加j列,第三维不填充,填充值为0(不会影响原图像)
12 img = np.pad(img, ((core_line, core_line), (core_row, core_row), (0, 0)), 'constant')
13 channel_r = convolution(img[:, :, 0], core) # *提取R通道数据并执行卷积
14 channel_g = convolution(img[:, :, 1], core) # *提取G通道数据并执行卷积
15 channel_b = convolution(img[:, :, 2], core) # *提取B通道数据并执行卷积
16
17 dstack = np.dstack([channel_r, channel_g, channel_b])
18 return dstack # *合并三个颜色通道
19
20
21def convolution(img, core):
22
23 core_line = core.shape[0] # *获取卷积核行数
24 core_row = core.shape[1] # *获取卷积核列数
25
26 channel_line = img.shape[0] - core_line + 1 # *获取图像像素点列数
27 channel_row = img.shape[1] - core_row + 1 # *获取图像像素点行数
28
29 new_img = np.zeros((channel_line, channel_row),
30 dtype='uint8') # *初始化一个和原图像大小相同的用0填充的图像矩阵
31
32 for i in trange(channel_line):
33 for j in range(channel_row):
34 # *调用calculate函数完成每个像素点的滤波计算并赋值给新图像
35 new_img[i][j] = calculate(img[i:i+core_line, j:j+core_row], core)
36 return new_img
37
38
39def calculate(img, core):
40 result = (img * core).sum() # *矩阵乘法获得结果像素值
41 if(result < 0): # *过滤无效像素值
42 result = 0
43 elif result > 255:
44 result = 255
45 return result
46
47
48img = plt.imread("t1.jpg")
49
50core = np.array([[1, 1, 0],
51 [1, 1, -1],
52 [0, -1, -1]]) # *预设的卷积核
53
54result = collect_channel(img, core)
55plt.imshow(result)
56plt.imsave("D:/0aJotang/#6/results.jpg", result)
57pylab.show()
对一个憨憨表情包处理后的结果如下:
要使用滤镜达到模糊的效果,我们可以理解为“图像细节的丢失”,但这种丢失不是简单的丢失了像素点,而是像素点和附近像素点的像素值差降低了,也就是更加“平滑”了,这一点和前面提到的“低通滤波器”比较类似。
要降低像素差值,我们可以对每个像素取附近像素点的平均值,这样每个像素值之间的差值就相应减少了。
均值模糊
直接在卷积遍历过程中求整个矩阵的平均值并赋值给对应像素点:
修改求和函数即可做到
{% note default icon%}
效果如图:
{%endnote%}
xxxxxxxxxx
71def calculate(img, core):
2 result = (img * core).sum() / 9 # *矩阵乘法获得结果像素值并求平均值
3 if(result < 0): # *过滤无效像素值
4 result = 0
5 elif result > 255:
6 result = 255
7 return result
效果举例:
高斯模糊
因为图像像素点分布实际上不是简单分布,每个像素点附近的像素点存在一定的连续性,距离越远,连续性就越不明显,这样的分布特点和正态分布一致,于是有使用正态分布(高斯函数)的方式来模糊处理图像的算法,这样的模糊方法因为过渡更加符合现实情况,在实拍的图片中使用效果会显得更加真实。
因为是二维的图像,所以需要使用到二维高斯函数:
其中,σ为模糊量,因为在正态分布中σ为方差,其值越大曲线越扁平,相应的模糊过渡越平滑,所以模糊量越大
此外,卷积核的半径与模糊程度也呈现正相关关系
实现起来也并不难,只需要计算出高斯函数的值并转化sum(core)=1就完成了:
xxxxxxxxxx
811import matplotlib.pyplot as plt
2from matplotlib.pyplot import pause
3import pylab
4import numpy as np
5from tqdm import trange
6
7# *设置全局变量
8def public():
9 global core_line, core_row, sigma
10 core_line = 10
11 core_row = 10
12 sigma = 5
13
14
15# *计算二维高斯函数值
16def gaussian(sigma, x, y):
17 z = 1/(2 * np.pi * (sigma**2)) * np.exp(-(x**2+y**2)/(2 * sigma**2))
18 return z
19
20
21def collect_channel(img, core):
22 public()
23 # *分别在行前后添加i行,在列前后添加j列,第三维不填充,填充值为0(不会影响原图像)
24 img = np.pad(img, ((core_line, core_line),
25 (core_row, core_row), (0, 0)), 'constant')
26 channel_r = convolution(img[:, :, 0], core) # *提取R通道数据并执行卷积
27 channel_g = convolution(img[:, :, 1], core) # *提取G通道数据并执行卷积
28 channel_b = convolution(img[:, :, 2], core) # *提取B通道数据并执行卷积
29
30 dstack = np.dstack([channel_r, channel_g, channel_b])
31 return dstack # *合并三个颜色通道
32
33
34def convolution(img, core):
35 public()
36
37 channel_line = img.shape[0] - core_line + 1 # *获取图像像素点列数
38 channel_row = img.shape[1] - core_row + 1 # *获取图像像素点行数
39
40 new_img = np.zeros((channel_line, channel_row),
41 dtype='uint8') # *初始化一个和原图像大小相同的用0填充的图像矩阵
42
43 for i in trange(channel_line):
44 for j in range(channel_row):
45 # *调用calculate函数完成每个像素点的滤波计算并赋值给新图像
46 new_img[i][j] = calculate(img[i:i+core_line, j:j+core_row], core)
47 return new_img
48
49
50def calculate(img, core):
51
52 result = (img * core).sum() # *矩阵乘法获得结果像素值
53 if(result < 0): # *过滤无效像素值
54 result = 0
55 elif result > 255:
56 result = 255
57 return result
58
59
60def gaussian_cal():
61 public()
62 # *初始化卷积核
63 core = [[1.0 for i in range(core_line)] for j in range(core_row)]
64 sums = 0
65 # *计算卷积核半径
66 for i in range(core_line):
67 for j in range(core_row):
68 x = abs(j - (core_row // 2))
69 y = abs(i - (core_line // 2))
70 core[i][j] = gaussian(sigma, x, y)
71 sums = sums + core[i][j]
72 core = core / sums # *保证卷积核元素总和为1
73 return core
74
75
76img = plt.imread("D:/0aJotang/#6/nice.jpg")
77plt.imshow(img)
78result = collect_channel(img, gaussian_cal())
79plt.imshow(result)
80plt.imsave("D:/0aJotang/#6/results1.jpg", result)
81pylab.show()
{% note default icon%}
效果如图:
{%endnote%}
锐化类滤镜:主要通过强化中心像素值(即赋予高权重)的方式来增强边缘区域的特征
浮雕滤镜
轮廓提取
更多滤镜,魔改卷积核······
所谓图片风格迁移,是指利用程序算法学习特定图片的风格,然后再把这种风格应用到另外一张图片上的技术。
传统的方法是分析某类风格的图像,对其图像特征进行建模,再通过这个模型来应用到目标图像上,缺点是只能针对每一类图像单独建模,而且不同风格的图像建模的方法差异也很大:
后来出现了基于神经网络学习的风格迁移算法,让程序使用任意一张图片的风格进行风格迁移成为可能:
基于神经网络的风格迁移算法,大致可以描述为:
定义两个表示距离的变量,一个表示输入图片和内容图片的距离(Dc),一个表示输入图片和样式图片的距离(Ds).即Dc测量输入和内容图片的内容差异的距离,Ds则测量输入和样式图片之间样式的差异距离.优化Dc和Ds使之最小,即完成图像风格转移
使用CNN(卷积神经网络)提取内容图片的内容和风格图片的风格,然后将这两项特征输入到一张新的图像中。对输入的图像提取出内容和风格与CNN提取的内容和风格进行Loss计算,用MSE度量,然后逐步对Loss进行优化,使Loss值达到最理想,将被优化的参数进行输出,这样输出的图片就达到了风格迁移的目的。
xxxxxxxxxx
241# *输出图像大小
2imsize = 512
3loader = transforms.Compose([
4 transforms.Resize(imsize), # *拉伸调整输入图像尺寸
5 transforms.ToTensor()]) # *将图像转换为torch张量
6
7# *图像载入
8
9def image_loader(image_name):
10 image = Image.open(image_name)
11 image = loader(image).unsqueeze(0) # *添加伪维数以满足神经网络要求的输入维数
12 return image.to(device, torch.float)
13
14# *转换并绘制图像
15
16def imshow(tensor, title=None):
17 unloader = transforms.ToPILImage()
18 image = tensor.cpu().clone() # *clone张量
19 image = image.squeeze(0) # *移除添加的伪维
20 image = unloader(image) # *转换回PIL图像
21 plt.imshow(image) # *绘制图像
22 if title is not None:
23 plt.title(title)
24 plt.pause(0.001) # *暂停使plot子图更新
内容损失
xxxxxxxxxx
111# *内容损失
2
3class ContentLoss(nn.Module):
4 def __init__(self, target,):
5 super(ContentLoss, self).__init__()
6 self.target = target.detach()
7
8 def forward(self, input):
9 self.loss = F.mse_loss(input, self.target) # *调用mse_loss计算矩阵均方损失
10 return input
11
风格损失
style loss取自原始image和生成的image在神经网络中的Gram matrix的MSE(Gram矩阵可以在一定程度上反映原始图像的“风格”):
因为公式字母太多写LaTex太麻烦于是就用了图片XD
xxxxxxxxxx
261# *计算Gram矩阵
2
3def gram_matrix(input):
4 a, b, c, d = input.size() # a=batch size(=1)
5 # *特征映射 b=number
6 # *(c,d)=dimensions of a f. map (N=c*d)
7
8 features = input.view(a * b, c * d) # *将矩阵F_XL重塑为\hat F_XL
9
10 G = torch.mm(features, features.t()) # *计算gram积
11
12 # *归一化gram矩阵的值.
13 return G.div(a * b * c * d)
14
15# *风格损失计算(与内容损失计算类似)
16
17class StyleLoss(nn.Module):
18
19 def __init__(self, target_feature):
20 super(StyleLoss, self).__init__()
21 self.target = gram_matrix(target_feature).detach()
22
23 def forward(self, input):
24 G = gram_matrix(input)
25 self.loss = F.mse_loss(G, self.target)
26 return input
xxxxxxxxxx
31 def get_input_optimizer(input_img):
2 optimizer = optim.LBFGS([input_img.requires_grad_()])
3 return optimizer
xxxxxxxxxx
101class Normalization(nn.Module):
2 def __init__(self, mean, std):
3 super(Normalization, self).__init__()
4 # *计算均值、均方差以归一化矩阵
5 self.mean = torch.tensor(mean).view(-1, 1, 1)
6 self.std = torch.tensor(std).view(-1, 1, 1)
7
8 def forward(self, img):
9 # *归一化矩阵
10 return (img - self.mean) / self.std
xxxxxxxxxx
601# *获得风格模型和损失量
2
3
4def get_style_model_and_losses(cnn, normalization_mean, normalization_std,
5 style_img, content_img,
6 content_layers=content_layers_default,
7 style_layers=style_layers_default):
8 cnn = copy.deepcopy(cnn)
9
10 # *规范化模块
11 normalization = Normalization(
12 normalization_mean, normalization_std).to(device)
13 # *只是为了拥有可迭代的访问权限或列出内容/系统损失
14 content_losses = []
15 style_losses = []
16
17 # *创建一个新的nn.Sequential来放入按序激活的模块
18 model = nn.Sequential(normalization)
19
20 i = 0 # *每次检视层的增量
21 for layer in cnn.children():
22 if isinstance(layer, nn.Conv2d):
23 i += 1
24 name = 'conv_{}'.format(i)
25 elif isinstance(layer, nn.ReLU):
26 name = 'relu_{}'.format(i)
27 # *在下面插入的ContentLoss和StyleLoss的本地版本不能很好地发挥作用。所以在这里替换不合适的
28 layer = nn.ReLU(inplace=False)
29 elif isinstance(layer, nn.MaxPool2d):
30 name = 'pool_{}'.format(i)
31 elif isinstance(layer, nn.BatchNorm2d):
32 name = 'bn_{}'.format(i)
33 else:
34 raise RuntimeError('Unrecognized layer: {}'.format(
35 layer.__class__.__name__))
36
37 model.add_module(name, layer)
38
39 if name in content_layers:
40 # *加入内容损失:
41 target = model(content_img).detach()
42 content_loss = ContentLoss(target)
43 model.add_module("content_loss_{}".format(i), content_loss)
44 content_losses.append(content_loss)
45
46 if name in style_layers:
47 # *加入风格损失:
48 target_feature = model(style_img).detach()
49 style_loss = StyleLoss(target_feature)
50 model.add_module("style_loss_{}".format(i), style_loss)
51 style_losses.append(style_loss)
52
53 # *移除添加的损失层
54 for i in range(len(model) - 1, -1, -1):
55 if isinstance(model[i], ContentLoss) or isinstance(model[i], StyleLoss):
56 break
57
58 model = model[:(i + 1)]
59
60 return model, style_losses, content_losses
xxxxxxxxxx
2531from __future__ import print_function
2
3import torch
4import torch.nn as nn
5import torch.nn.functional as F
6import torch.optim as optim
7
8from PIL import Image
9import matplotlib.pyplot as plt
10
11import torchvision.transforms as transforms
12import torchvision.models as models
13
14import copy
15# *指定计算设备
16device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
18# *输出图像大小
19imsize = 512
20loader = transforms.Compose([
21 transforms.Resize(imsize), # *拉伸调整输入图像尺寸
22 transforms.ToTensor()]) # *将图像转换为torch张量
23
24# *图像载入
25
26
27def image_loader(image_name):
28 image = Image.open(image_name)
29 # *添加伪维数以满足神经网络要求的输入维数
30 image = loader(image).unsqueeze(0)
31 return image.to(device, torch.float)
32
33
34style_img = image_loader("D:/0aJotang/#6/py_fi/input_style/style.jpg") # *读取风格图像
35content_img = image_loader("D:/0aJotang/#6/py_fi/input_content/content.jpg") # *读取内容图像
36# *确认传入内容和风格图像尺寸一致
37assert style_img.size() == content_img.size(), \
38 "we need to import style and content images of the same size"
39
40unloader = transforms.ToPILImage() # *将图像转换回PIL—image以在plot绘制
41
42plt.ion()
43
44# *用于转换并绘制图像
45
46
47def imshow(tensor, title=None):
48 image = tensor.cpu().clone() # *clone张量
49 image = image.squeeze(0) # *移除添加的伪维
50 image = unloader(image) # *重新转换回PIL图像
51 plt.imshow(image) # *绘制图像
52 if title is not None:
53 plt.title(title)
54 plt.pause(0.001) # *暂停使plot子图更新
55
56
57# *内容损失
58
59
60class ContentLoss(nn.Module):
61 def __init__(self, target,):
62 super(ContentLoss, self).__init__()
63 self.target = target.detach()
64
65 def forward(self, input):
66 self.loss = F.mse_loss(input, self.target) # *计算损失
67 return input
68
69
70# *计算gram矩阵
71
72def gram_matrix(input):
73 a, b, c, d = input.size() # a=batch size(=1)
74 # *特征映射 b=number
75 # *(c,d)=dimensions of a f. map (N=c*d)
76
77 features = input.view(a * b, c * d) # *将矩阵F_XL重塑为\hat F_XL
78
79 G = torch.mm(features, features.t()) # *计算gram积
80
81 # *归一化gram矩阵的值.
82 return G.div(a * b * c * d)
83
84
85# *风格损失计算
86
87class StyleLoss(nn.Module):
88
89 def __init__(self, target_feature):
90 super(StyleLoss, self).__init__()
91 self.target = gram_matrix(target_feature).detach()
92
93 def forward(self, input):
94 G = gram_matrix(input)
95 self.loss = F.mse_loss(G, self.target)
96 return input
97
98
99# *导入预训练模型
100cnn = models.vgg19(pretrained=True).features.to(device).eval()
101
102cnn_normalization_mean = torch.tensor([0.485, 0.456, 0.406]).to(device)
103cnn_normalization_std = torch.tensor([0.229, 0.224, 0.225]).to(device)
104
105# *规范化输入图像以导入nn.Sequential
106
107
108class Normalization(nn.Module):
109 def __init__(self, mean, std):
110 super(Normalization, self).__init__()
111 # *计算均值、均方差以归一化矩阵
112 self.mean = torch.tensor(mean).view(-1, 1, 1)
113 self.std = torch.tensor(std).view(-1, 1, 1)
114
115 def forward(self, img):
116 # *归一化矩阵
117 return (img - self.mean) / self.std
118
119
120# *计算样式/内容损失的期望深度层:
121content_layers_default = ['conv_4']
122style_layers_default = ['conv_1', 'conv_2', 'conv_3', 'conv_4', 'conv_5']
123
124# *获得风格模型和损失量
125
126
127def get_style_model_and_losses(cnn, normalization_mean, normalization_std,
128 style_img, content_img,
129 content_layers=content_layers_default,
130 style_layers=style_layers_default):
131 cnn = copy.deepcopy(cnn)
132
133 # *规范化模块
134 normalization = Normalization(
135 normalization_mean, normalization_std).to(device)
136 # *只是为了拥有可迭代的访问权限或列出内容/系统损失
137 content_losses = []
138 style_losses = []
139
140 # *创建一个新的nn.Sequential来放入按序激活的模块
141 model = nn.Sequential(normalization)
142
143 i = 0 # *每次检视层的增量
144 for layer in cnn.children():
145 if isinstance(layer, nn.Conv2d):
146 i += 1
147 name = 'conv_{}'.format(i)
148 elif isinstance(layer, nn.ReLU):
149 name = 'relu_{}'.format(i)
150 # *对于我们在下面插入的ContentLoss和StyleLoss,
151 # *本地版本不能很好地发挥作用。所以我们在这里替换不合适的
152 layer = nn.ReLU(inplace=False)
153 elif isinstance(layer, nn.MaxPool2d):
154 name = 'pool_{}'.format(i)
155 elif isinstance(layer, nn.BatchNorm2d):
156 name = 'bn_{}'.format(i)
157 else:
158 raise RuntimeError('Unrecognized layer: {}'.format(
159 layer.__class__.__name__))
160
161 model.add_module(name, layer)
162
163 if name in content_layers:
164 # *加入内容损失:
165 target = model(content_img).detach()
166 content_loss = ContentLoss(target)
167 model.add_module("content_loss_{}".format(i), content_loss)
168 content_losses.append(content_loss)
169
170 if name in style_layers:
171 # *加入风格损失:
172 target_feature = model(style_img).detach()
173 style_loss = StyleLoss(target_feature)
174 model.add_module("style_loss_{}".format(i), style_loss)
175 style_losses.append(style_loss)
176
177 # *移除添加的损失层
178 for i in range(len(model) - 1, -1, -1):
179 if isinstance(model[i], ContentLoss) or isinstance(model[i], StyleLoss):
180 break
181
182 model = model[:(i + 1)]
183
184 return model, style_losses, content_losses
185
186
187input_img = content_img.clone() # *读取并clone内容图片
188
189
190# *降低梯度
191
192def get_input_optimizer(input_img):
193 optimizer = optim.LBFGS([input_img.requires_grad_()])
194 return optimizer
195
196# *运行风格迁移
197def run_style_transfer(cnn, normalization_mean, normalization_std,
198 content_img, style_img, input_img, num_steps=300,
199 style_weight=1000000, content_weight=1):
200 print('Building the style transfer model..')
201 model, style_losses, content_losses = get_style_model_and_losses(cnn,
202 normalization_mean, normalization_std, style_img, content_img)
203 optimizer = get_input_optimizer(input_img)
204
205 print('Optimizing..')
206 run = [0]
207 while run[0] <= num_steps:
208
209 def closure():
210 # *修正更新的输入图像的值
211 input_img.data.clamp_(0, 1)
212
213 optimizer.zero_grad()
214 model(input_img)
215 style_score = 0
216 content_score = 0
217
218 for sl in style_losses:
219 style_score += sl.loss
220 for cl in content_losses:
221 content_score += cl.loss
222
223 style_score *= style_weight
224 content_score *= content_weight
225
226 loss = style_score + content_score
227 loss.backward()
228
229 run[0] += 1
230 if run[0] % 50 == 0:
231 print("run {}:".format(run))
232 print('Style Loss : {:4f} Content Loss: {:4f}'.format(
233 style_score.item(), content_score.item()))
234 print()
235
236 return style_score + content_score
237
238 optimizer.step(closure)
239
240 # *修正张量为(0,1)
241 input_img.data.clamp_(0, 1)
242
243 return input_img
244
245
246output = run_style_transfer(cnn, cnn_normalization_mean, cnn_normalization_std,
247 content_img, style_img, input_img)
248
249plt.figure()
250imshow(output, title='Output Image')
251plt.savefig("D:/0aJotang/#6/py_fi/output/output.png")
252plt.ioff()
253plt.show()
输入图片效果示例:
{%note warning icon%}
这一部分有可能要弃坑了,看的俺脑阔痛QAQ(其实是摸鱼之心突然崛起了)
{%endnote%}