24/11/22 项目拆解 艺术风格转移

我们有时候想把两种艺术风格整合,创造更具艺术特色的艺术品,人很难办到,但是人工智能可以,比如下面将艺术画的风格转移到照片上。


我们先来初步了解一下实现上述功能的数学原理

所谓艺术风格,其实就是边缘,颜色,纹理等各种要素的组合,因此可以通过卷积网络对图片识别后,所得的结果就已经包含了图片的艺术风格,因此我们可以通过卷积网络将构成图片风格的各种要素抽取出来。

风格转移这一过程可以通过以下几个关键步骤实现:

  1. 内容和风格的表示

    • 内容表示:通常使用深度卷积神经网络(如VGG19)的中间层来提取图像的内容特征。网络的深层能够捕捉到图像的高级语义信息,即内容。
    • 风格表示:风格则通过计算特征图的格拉姆矩阵(Gram matrix)来表示,它描述了特征图之间的相关性,即纹理和颜色分布等风格信息。
  2. 损失函数

    • 风格迁移的目标是最小化一个损失函数,该函数由两部分组成:风格损失和内容损失。
    • 风格损失:计算生成图像与风格图像在每个层的特征图的格拉姆矩阵之间的差异。
    • 内容损失:计算生成图像与内容图像在相应层的特征图之间的差异。
    • 通过最小化这些损失,生成图像逐渐接近目标风格和内容。
  3. 优化过程

    • 从一个随机噪声图像或内容图像开始,使用梯度下降算法不断优化图像,以最小化损失函数。
    • 通常使用L-BFGS算法或Adam优化器进行优化。
  4. 总变差损失(Total Variation Loss)

    • 为了保持生成图像的平滑性,通常会添加一个总变差损失项,它惩罚图像中的高频信息,即图像中的噪声和细节

        网络从输入图像中抠出一小片做卷积运算,之后得到一个向量。抠出的一小片是二维的,由于每一个像素点被计算成一个向量,因此卷积运算后得到一个三维的”一小块“。

        我们假设抠出的这一小片映射到第 L 层后,对应的”小块“其宽度为 W ,高度为  H ,深度为NL,如果我们将它压扁,从三维降为二维,把它的一个面积为 W * H平面拉长成一条长度为 ML = W * H的一条线,于是一个三维向量就转化为二维向量,用数学表达式表示为:F^{_{l}}\varepsilon R^{_{Nl\cdot Ml}}

        同时我们用F_{ij}^{l}表示把深度为 i 的切片拉成一条直线后的第 j 个元素。通过前面讲述的卷积操作的内容就知道,F^{l}包含了输入图片的某些内容。如果反过来先想,给定了 F^{l}后,我们如何还原它所蕴含的画面信息呢?

        做法是拿一张白色图片,每个像素点的值都是255,让它经过卷积网络运算后,我们拿出同处于 l 层的结果,这个结果记为P^{l}。接着调整白色图片像素点的值,使得P^{l}F^{l}不断接近,当他们足够接近时,白色图片的像素点发生转变后,就呈现出与输入图片相似的图案。

我们用差值的平方和来表示”接近“:

L_{conent} = \frac{1}{2}\sum (F_{ij}^{l}-P_{ij}^{l})^2                                 (1)

        当公式(1)的值越小时,白色图片转换出的画面内容与输入图片越相似。因此我们只要把白色图片中的像素点按照公式(1)的值越来越小的方向调整即可。

        我们知道P^{_{ij}^{l}}是白色图片中某个像素点x经过卷积运算后得到的结果,于是x与P^{_{ij}^{l}}之间存在函数关系P^{_{ij}^{l}}(x),当像素点x的值变动后,P^{_{ij}^{l}}也会跟着变动。因此要想调整x使得公式(1)最小,我们只要将公式(1)对x求偏导即可:

\frac{\partial L_{content}}{\partial x} = \frac{\partial P_{ij}}{\partial x}\cdot (F^{l}-P^{l})_{ij}                              (2)

        其中,\frac{\partial P_{ij}}{\partial x}可以i通过对VGG网络进行梯度下降法来获取。根据公式(2)我们就知道了如何调整每个像素点 x 的值,使得白色图片转变为与输入图片内容相同的画风。现在内容有了,风格如何实现呢?

         我们把抽取风格的绘画图片输入VGG网络中,在卷积层中已经包含了“风格”信息。要想对所谓的风格进行量化,我们需要计算对应卷积层的格莱姆矩阵。我们用 G^{l} 表示第 l 层卷积层对应的格莱姆矩阵,那么有:G^{l}=\sum _{k}F_{ik}^{l}\cdot F_{jk}^{l}                         (3) 

        也就是说格莱姆矩阵第 i 行第 j 列个元素就是把卷积层对应的第 i 行和第 j 行做点乘后得到。公式(3)中G^{l}对应的就是对输入图片“风格”的量化。与前面描述类似,我们要想把一张白色图片转换成给定图片风格相近的图片,就需要对下面的公式进行量化。

E^{_{l}}=\frac{1}{4\cdot N_{l}^{2}\cdot M_{l}^{2}}\sum _{i,j}(G_{ij}^{l}-A_{ij}^{l})^2                                    (4)

        其中,A_{ij}^{l}是将白色图片输入到VGG网络后,将第L层卷积层转换后的格莱姆矩阵,我们选L个卷积层的格莱姆之和作为“风格”的量化:

L_{style} = \sum _{l=0}^{L}w_{l}\cdot E_{l}                                                       (5)

        于是要调整像素点的值使得公式(5)最小,其中w_{l}对应每个卷积层权重,一般而言,权重会平均分配给每个卷积层。

        对于像素点x,它的调整幅度为:

\frac{\partial L_{style}}{\partial x}=\sum _{l=0}^{L}w_{l}\cdot \frac{1}{N_{l}^{2}\cdot M_{l}^{2}}((F^{l})^{T}\cdot (G^{l}-A_{l}))\frac{\partial A^{l}}{\partial x})_{ij}    (6)

        现在出现一个问题,对内容进行调整时,我们对像素点x进行更改;对风格进行调整时,我们又会对像素点进行更改,如何将两种更改统一起来呢?我们对两种更改赋予不同的权重:

\bigtriangleup x = \alpha \cdot \frac{\partial L_{content}}{\partial x}+\beta \cdot \frac{\partial L_{style}}{\partial x}                                         (7)

        如果 \alpha 系数比较大,那么我们就使白色图片的改变偏向于内容;如果系数 \beta 比较大,那么我们就让图片的调整偏向于风格。于是像素点的调整只要把像素点x的值减去 \eta \cdot \bigtriangleup x 即可,其中 \eta 是调整率,它跟我们前面提到过的学习率是一致的

接下来我们1启动代码加以理解。

代码理解
 

1.导入函数库
import tensorflow as tf
#import tensorflow.contrib.eager as tfe
#from tensorflow.python.keras.preprocessing import image as kp_image
from tensorflow.python.keras import models
from tensorflow.python.keras import losses
from tensorflow.python.keras import layers
from tensorflow.python.keras import backend as K
import IPython.display
#启动eager
#tf.enable_eager_execution()
#print("Enger execution:{}".format(tf.executing_eagerly()))
2.加载内容图片和风格图片
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['figure.figsize'] = (10,10)
mpl.rcParams['axes.grid'] = False
import numpy as np
import time
import functoolscontent_path = "C:\\Users\\91144\\Pictures\\Screenshots\\屏幕截图 2024-09-23 214511.png"
style_path = "C:\\Users\\91144\\Pictures\\Screenshots\\屏幕截图 2024-09-26 184416.png"# 加载显示图片
def load_img(path):max_dim = 512                #设置图像的最大尺寸为512像素img = Image.open(path)       #打开图片long = max(img.size)         #计算图像最长边scale = max_dim / long       #计算缩放比例if scale < 1:                    # 使用 Image.Resampling.LANCZOSimg = img.resize((int(img.size[0] * scale), int(img.size[1] * scale)), Image.Resampling.LANCZOS)        #使用LANCZOS算法缩放图像img = np.array(img)          #转为numpy数组img = np.expand_dims(img, axis=0)#增加一个维度,使其符合深度学习模型输入的要求return img                   def show_img(img, title=None):   #定义显示图像的函数out = np.squeeze(img, axis=0)#从图像数组中移除批次维度out = out.astype('uint8')    #将图像数组的数据类型转为无符号整型,这是图像显示的标准格式plt.imshow(out)              if title is not None:     plt.title(title)plt.figure(figsize=(10,10))      #加载图像内容  
content = load_img(content_path)
style = load_img(style_path)
plt.subplot(1, 2, 1)
show_img(content, 'Content Image')
plt.subplot(1, 2, 2)
show_img(style, 'Style Image')
plt.show()

TIPs:

上面有一个LANCZOS算法,它主要用于求解稀疏矩阵的特征值问题,我们这里用它缩放图像。

LANCZOS算法的原理

Lanczos 插值使用 Lanczos 核函数来计算插值后的像素值。Lanczos 核函数是一种低通滤波器,可以消除缩放过程中产生的混叠现象。

Lanczos 核函数定义如下:

其中,sinc(x) = sin(πx) / (πx),a 是核函数的宽度。

Lanczos 插值的过程如下:

  1. 确定插值点的位置。
  2. 以插值点为中心,在原图像中取一个窗口。
  3. 对窗口中的每个像素,使用 Lanczos 核函数计算其权重。
  4. 将窗口中所有像素的权重和插值值相乘,得到插值点的最终值。

Lanczos 插值公式:

Lanczos 插值的优点

  • 与其他插值方法相比,Lanczos 插值可以产生更清晰、更平滑的图像。
  • Lanczos 插值可以有效地抑制混叠现象,尤其是在图像缩小的情况下。

Lanczos 插值的缺点

  • 与其他的插值方法相比,Lanczos 插值的计算量更大。
  • Lanczos 插值可能会产生轻微的振铃效应,尤其是在图像放大边缘处

3.用vgg卷积网络抽取特征
import tensorflow as tf
from tensorflow.keras.applications import vgg19
from tensorflow.keras.preprocessing import image as kp_image
from tensorflow.keras.models import Modeldef load_and_process_img(path):# 加载图片img = kp_image.load_img(path, target_size=(224, 224))  # 确保图片大小与 VGG19 输入一致# 将图片转换为数组img = kp_image.img_to_array(img)# 扩展维度,因为 VGG19 需要批次维度img = np.expand_dims(img, axis=0)# 预处理图片img = vgg19.preprocess_input(img)return img# 定义用于内容调整的卷积层
content_layers = ['block5_conv2']  # 使用存在的层名称
# 定义用于风格计算的卷积层
style_layers = ['block1_conv1','block2_conv1','block3_conv1','block4_conv1','block5_conv1']
num_content_layers = len(content_layers)
num_style_layers = len(style_layers)# 加载 VGG19 网络,让它识别内容
def get_model():# 我们不需要最后的全连接层vgg = vgg19.VGG19(include_top=False, weights='imagenet')# 获取用于计算风格的卷积层style_outputs = [vgg.get_layer(name).output for name in style_layers]# 获取用于计算内容的卷积层content_outputs = [vgg.get_layer(name).output for name in content_layers]model_outputs = style_outputs + content_outputsreturn Model(vgg.input, model_outputs)# 获取模型
model = get_model()

TIPs:

我们来解释一下里面的VGG卷积网络和block5_conv2,block1_conv2这些网络层的含义

在VGG网络层中,每个卷积块包含若干卷积层,后面跟着一个最大池化层,这些卷积块被编号为

block1block2block3block4 和 block5。

block5_conv2:这是VGG网络中第五个卷积块的第二个卷积层,在VGG-19中,block5包含三个卷积层(conv5_1conv5_2conv5_3),其中conv5_2对应于block5_conv2。

这些层的名称通常用于风格迁移任务中,用于提取图像的内容特征和风格特征,block5_conv2 由于其在网络的较深位置,能够捕捉到图像的高级语义信息,适合作为内容特征层。而block1_conv1等较浅层的卷积层则能捕捉到图像的纹理和颜色信息,适合作为风格特征层。

4.计算内容对应卷积层的差值,以便后面调整图片的像素点
def get_content_loss(base_content,target):return tf.reduce_mean(tf.square(base_content - target))
5.计算风格差异
#计算风格差异
def gram_matrix(input_tensor):               #格拉姆矩阵channels = int(input_tensor.shape[-1])'''这里的-1相当于把卷积层对应的平面拉成一条直线'''a = tf.reshape(input_tensor,[-1,channels])n = tf.shape(a)[0]gram = tf.matmul(a,a,transpose_a = True)  #将第一个张量a进行转置,然后与第二个张量a进行矩阵乘法return gram/tf.cast(n,tf.float32)#计算风格差异
def get_style_loss(base_style,gram_target):gram_style = gram_matrix(base_style)return tf.reduce_mean(tf.square(gram_style - gram_target))

TIPs:

这里有一个格拉姆矩阵,图像处理和风格迁移的上下文中,格拉姆矩阵用于捕捉和表示图像的风格特征,特别是纹理和颜色分布。

定义

对于一个给定的矩阵 A,其格拉姆矩阵 G 定义为 A 与其自身的内积。如果 A 是一个 m×n 的矩阵,那么格拉姆矩阵 G 是一个 n×n 的矩阵,计算方式如下:

其中 A^T 是 A 的转置。

图像风格迁移中的应用

在图像风格迁移中,格拉姆矩阵用于表示风格图像的特征图(feature maps)。特征图是深度卷积神经网络(如VGG)中某一层的输出,它捕捉了图像的局部特征。格拉姆矩阵则将这些局部特征转换为全局风格特征,因为它包含了特征图之间的相关性信息。

计算步骤

  1. 提取特征图

    • 从风格图像中提取特征图。这通常是通过将风格图像输入到预训练的深度学习模型(如VGG)中,并选择特定层的输出来完成的。
  2. 计算格拉姆矩阵

    • 对于每个特征图,计算其格拉姆矩阵。如果特征图的形状是 [1,h,w,c][1,h,w,c](其中 hh 是高度,ww 是宽度,cc 是通道数),那么格拉姆矩阵的形状将是 [c,c][c,c]。
  3. 风格损失

    • 在风格迁移的损失函数中,格拉姆矩阵用于计算生成图像的风格特征与目标风格图像之间的差异。这种差异通常通过格拉姆矩阵的元素之间的差异来衡量。
  4. 计算差异:

               1.使用均方误差衡量两矩阵的差异:

均方误差是衡量两个矩阵之间差异的一种简单方法,定义为两个矩阵元素差的平方的平均值:

               2.交叉相关:在风格迁移中,我们通常使用一种特殊的交叉相关来衡量风格差异,这实际上是格拉姆矩阵的差异。如果 GAGA​ 和 GBGB​ 分别是两个特征图 AA 和 BB 的格拉姆矩阵,那么它们的差异可以表示为:

其中 NN 是特征图的通道数,MM 是格拉姆矩阵的维度(通常是特征图通道数)。

6.把内容图片和风格图片输入到VGG19卷积网络,获得相应卷积层的输出结果
def get_feature_representations(model,content_path,style_path):#把图片内容和风格图片输入到vgg网络,然后获取相应的卷积计算结果content_img = load_and_process_img(content_path)style_img = load_and_process_img(style_path)style_outputs = model(style_img)content_outputs = model(content_img) #抽取风格图片对应的卷积层输出style_features = [style_layer[0] for style_layer in style_outputs[:num_style_layers]]content_features = [content_layer[0] for content_layer in content_outputs[num_style_layers:]]return style_features,content_features
7.计算公式(2),和公式(6),最后根据公式(7)来调整白色图片的像素点
def computer_loss(model, loss_weights, init_image, gram_style_features, content_features):style_weight, content_weight = loss_weights# 计算白色图片对应卷积层的输出model_outputs = model(init_image)# 获得白色图片对应风格卷积层的计算结果style_output_features = model_outputs[:num_style_layers]content_output_features = model_outputs[num_style_layers:]style_score = 0content_score = 0# 计算风格对应卷积层权重weight_per_style_layer = 1.0 / float(num_style_layers)for target_style, comb_style in zip(gram_style_features, style_output_features):style_score += weight_per_style_layer * get_style_loss(comb_style[0], target_style)# 计算内容差值weight_per_content_layer = 1.0 / float(num_content_layers)
#将两个列表组合在一起,使得每次迭代都能同时访问目标内容特征(target_content)和生成图像的内容特征(comb_content)for target_content, comb_content in zip(content_features, content_output_features):content_score += weight_per_content_layer * get_content_loss(comb_content[0], target_content)# 根据公式7计算像素点的调节style_score *= style_weight          #调整风格得分content_score *= content_weight      #调整内容得分loss = style_score + content_score   #计算总损失return loss, style_score, content_score
8.根据公式(2)和公式(6)我们还得计算像素点相对于卷积层输出结果的偏导,把差值和偏导结合在一起才得到像素点的调整幅度
#把差值和偏导结合在一起才得到像素点的调整幅度
def compute_grads(cfg):with tf.GradientTape() as tape:all_loss = computer_loss(**cfg)total_loss = all_loss[0]#根据差值反向对像素点求偏导gradient = tape.gradient(total_loss,cfg['init_image'])#计算梯度return gradient,all_loss
9.最后我们把所有计算流程连接起来,不断调整白色图片像素点的值,让它慢慢变成具体有相应内容和风格的新的图片
import numpy as np
import tensorflow as tf
from tensorflow import keras
from PIL import Image
import IPython
import time#用于将处理过的图像(经过 VGG 网络预处理的图像)转换回可显示的图像格式。
def deprocess_img(processed_img):x = processed_img.copy()if len(x.shape) == 4:x = np.squeeze(x, 0)          #去掉批次维度assert len(x.shape) == 3x[:, :, 0] += 103.939             #将vgg网络均值加回到每个颜色通道上,以撤销预处理步骤x[:, :, 1] += 116.779x[:, :, 2] += 123.68x = x[:, :, ::-1]                 #将通道顺序从RBGR转换到RGBx = np.clip(x, 0, 255).astype('uint8')#将像素点限制在0-255,并转化为无符号整型,这是图像显示的标准格式return x#实现风格迁移的主要逻辑。
def run_style_transfer(content_path, style_path,num_iterations=1000,content_weight=1e3,style_weight=1e-2):model = get_model()  # 假设这个函数返回一个预训练的VGG模型for layer in model.layers:layer.trainable = Falsestyle_features, content_features = get_feature_representations(model, content_path, style_path)gram_style_features = [gram_matrix(style_feature) for style_feature in style_features]init_image = load_and_process_img(content_path)init_image = tf.Variable(init_image, dtype=tf.float32)opt = tf.keras.optimizers.Adam(learning_rate=5, beta_1=0.99, epsilon=1e-1)#优化器iter_count = 1best_loss, best_img = float('inf'), Noneloss_weights = (style_weight, content_weight)#配置字典cfg = {'model': model,'loss_weights': loss_weights,'init_image': init_image,'gram_style_features': gram_style_features,'content_features': content_features}num_rows = 2num_cols = 5display_interval = num_iterations / (num_rows * num_cols)start_time = time.time()global_start = time.time()norm_means = np.array([103.939, 116.779, 123.68])min_vals = -norm_meansmax_vals = 255 - norm_meansimgs = []#在迭代过程中,使用 compute_grads 函数(未提供)计算损失相对于初始图像的梯度,并使用优化器更新图像for i in range(num_iterations):grads, all_loss = compute_grads(cfg)                      #计算梯度和损失loss, style_score, content_score = all_lossopt.apply_gradients([(grads, init_image)])                #使用优化器clipped = tf.clip_by_value(init_image, min_vals, max_vals)#使用 tf.clip_by_value 将图像的像素值限制在合法范围内,防止梯度更新导致的像素值超出 [0, 255] 的范围。init_image.assign(clipped)                     #将裁剪后的图像值赋回 init_imageend_time = time.time()#如果当前损失小于最佳损失,则更新最佳损失和最佳图像。if loss < best_loss:best_loss = lossbest_img = deprocess_img(init_image.numpy())if i % display_interval == 0:start_time = time.time()plot_img = init_image.numpy()plot_img = deprocess_img(plot_img)imgs.append(plot_img)IPython.display.clear_output(wait=True)    #清除之前的输出,以便显示新的图像。IPython.display.display_png(Image.fromarray(plot_img)) #显示图像print('Iteration: {}'.format(i))print('Total loss: {:.4e}, style loss: {:.4e}, content loss: {:.4e}, time: {: .4f}s'.format(loss, style_score, content_score, time.time() - start_time))print('Total time: {:.4f}s'.format(time.time() - global_start))return best_img, best_loss
10.最后跑出来
run_style_transfer(content_path,style_path,num_iterations = 1000)

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

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

相关文章

Unity图形学之CubeMap立方体贴图

1.CubeMap&#xff1a;有六个面的贴图组成 2. 假反射&#xff1a;反射天空盒子 &#xff08;1&#xff09;正常UV采样&#xff1a; &#xff08;2&#xff09;Cube的采样&#xff1a;利用反射角采样&#xff0c;反射角X和Cube的交点采样 Shader "Custom/TestReflect"…

LLM Inference Unveiled

题目&#xff1a;LLM Inference Unveiled: Survey and Roofline Model Insights 链接&#xff1a;https://arxiv.org/abs/2402.16363 这也是一篇推理加速的综述&#xff0c;重点关注一下它的组织结构吧&#xff0c;也就是它对推理加速方法的分类 论文给的全文结构图&#xff…

Cesium的ClearCommand的流程

ClearCommand是在每帧渲染前可以将显存的一些状态置为初始值&#xff0c;就如同把擦黑板。当然也包括在绘制过程中擦掉部分的数据&#xff0c;就如同画家在开始绘制的时候会画导览线&#xff08;如透视线&#xff09;&#xff0c;轮廓出来后这些导览线就会被擦除。 我画了一个…

【Linux】重定向,dup

目录 文件描述符分配规则 重定向 dup ​编辑 输出重定向 追加重定向 输入重定向。 重定向会影响后面的程序替换吗&#xff1f; 1号文件和2号文件 2号文件输出重定向 下标之间的重定向 文件描述符分配规则 重定向 把显示器文件关闭后&#xff0c;本来应该写给显示器…

大语言模型---梯度的简单介绍;梯度的定义;梯度计算的方法

1. 梯度介绍 如果我们在一座山上&#xff08;一个山的坡度有很多&#xff0c;陡峭的&#xff0c;平缓的&#xff09;&#xff0c;想要从山顶下山。而梯度就像告诉我们如何沿着最陡的下坡路线走&#xff0c;以尽快到达山脚&#xff08;最低点&#xff09;。 2. 梯度的定义 梯度…

【JAVA】一次操蛋的nginx镜像之旅

一、前言 由于我们的项目中使用到了nginx&#xff0c;同时我们的nginx是通过docker镜像进行安装的&#xff0c;由于nginx出现了问题&#xff0c;需要重新安装。于是。。。 二、通过docker进行安装 docker pull nginx:latest 1.5.2 脚本文件 在/home/docker/script路径下创…

ubuntu24挂载硬盘记录

1、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff1a; sudo fdisk -l 找到自己硬盘的分区 我的地址/dev/sda 2、显示硬盘及所属分区情况。在终端窗口中输入如下命令&#xff0c;格式化自己硬盘&#xff1a; sudo mkfs -t ext4 /dev/sda 3、在终端窗口中输入如下…

业务架构、数据架构、应用架构和技术架构

TOGAF(The Open Group Architecture Framework)是一个广泛应用的企业架构框架&#xff0c;旨在帮助组织高效地进行架构设计和管理。 TOGAF 的核心就是由我们熟知的四大架构领域组成:业务架构、数据架构、应用架构和技术架构。 企业数字化架构设计中的最常见要素是4A 架构。 4…

苹果Siri将搭载大型语言模型,近屿智能抢占AIGC大模型人才培养高地

据媒体报道&#xff0c;苹果公司正在研发一款全新升级、更加智能且对话能力显著提升的Siri&#xff0c;意在超越OpenAI的ChatGPT及其他语音服务。 报道指出&#xff0c;新一代Siri将搭载更为先进的大型语言模型&#xff08;LLM&#xff09;&#xff0c;苹果期望其能够进行连续…

【1.4 Getting Started--->Support Matrix】

主页&#xff1a;支持矩阵 这些支持矩阵概述了 TensorRT API、解析器和层支持的平台、特性和硬件功能。 Support Matrix Abstract 这些支持矩阵概述了 TensorRT API、解析器和层所支持的平台、功能和硬件功能。 有关之前发布的 TensorRT 文档&#xff0c;请参阅 TensorRT 档…

WPF中如何让Textbox显示为一条直线

由于Textbox直接使用是一条直线 设置如下代码 可以让Textbox变为直线输入 <Style TargetType"TextBox"x:Key"UsernameTextBoxStyle"><Setter Property"Template"><Setter.Value><ControlTemplate TargetType"{x:Typ…

Mac 修改默认jdk版本

当前会话生效 这里演示将 Java 17 版本降低到 Java 8 查看已安装的 Java 版本&#xff1a; 在终端&#xff08;Terminal&#xff09;中运行以下命令&#xff0c;查看已安装的 Java 版本列表 /usr/libexec/java_home -V设置默认 Java 版本&#xff1a; 找到 Java 8 的安装路…

K8S + Jenkins 做CICD

前言 这里会做整体CICD的思路和流程的介绍&#xff0c;会给出核心的Jenkins pipeline脚本&#xff0c;最后会演示一下 实验/实操 结果 由于整体内容较多&#xff0c;所以不打算在这里做每一步的详细演示 - 本文仅作自己的实操记录和日后回顾用 要看保姆式教学的可以划走了&…

使用 前端技术 创建 QR 码生成器 API1

前言 QR码&#xff08;Quick Response Code&#xff09;是一种二维码&#xff0c;于1994年开发。它能快速存储和识别数据&#xff0c;包含黑白方块图案&#xff0c;常用于扫描获取信息。QR码具有高容错性和快速读取的优点&#xff0c;广泛应用于广告、支付、物流等领域。通过扫…

基于Java Springboot高校工作室管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

【读书】复杂性意义结构框架——Cynefin框架

Cynefin框架 《代码大全》的作者史蒂夫麦克康奈尔&#xff08;Steve McConnell&#xff09;在《卓有成效的敏捷》这本书里&#xff0c;探讨了用于理解不确定性和复杂性的Cynefin框架。 Cynefin框架是戴维斯诺登&#xff08;David Snowden&#xff09;20世纪90年代的在IBM时创…

ZYNQ-7020嵌入式系统学习笔记(1)——使用ARM核配置UART发送Helloworld

本工程实现调用ZYNQ-7000的内部ARM处理器&#xff0c;通过UART给电脑发送字符串。 硬件&#xff1a;正点原子领航者-7020 开发平台&#xff1a;Vivado 2018、 SDK 1 Vivado部分操作 1.1 新建工程 设置工程名&#xff0c;选择芯片型号。 1.2 添加和配置PS IP 点击IP INTEGR…

全面击破工程级复杂缓存难题

目录 一、走进业务中的缓存 &#xff08;一&#xff09;本地缓存 &#xff08;二&#xff09;分布式缓存 二、缓存更新模式分析 &#xff08;一&#xff09;Cache Aside Pattern&#xff08;旁路缓存模式&#xff09; 读操作流程 写操作流程 流程问题思考 问题1&#…

SpringSecurity创建一个简单的自定义表单的认证应用

1、SpringSecurity 自定义表单 在 Spring Security 中创建自定义表单认证应用是一个常见的需求&#xff0c;特别是在需要自定义登录页面、认证逻辑或添加额外的表单字段时。以下是一个详细的步骤指南&#xff0c;帮助你创建一个自定义表单认证应用。 2、基于 SpringSecurity 的…

用python简单集成一个分词工具

本部分记录如何利用Python进行分词工具集成&#xff0c;集成工具可以实现运行无环境要求&#xff0c;同时也更方便。 该文章主要是记录&#xff0c;知识点不是特别多&#xff0c;欢迎访问个人博客&#xff1a;https://blog.jiumoz.top/archives/fen-ci-gong-ju-ji-cheng 成品展…