【论文笔记】图像修复Learning Joint Spatial-Temporal Transformations for Video Inpainting
论文地址:https://arxiv.org/abs/2007.10247
源码地址:GitHub - researchmm/STTN: [ECCV'2020] STTN: Learning Joint Spatial-Temporal Transformations for Video Inpainting
一、项目介绍
当下SITA的方法大多采用注意模型,通过搜索参考帧中缺失的内容来完成一帧,并进一步逐帧完成整个视频。然而,这些方法在空间和时间维度上的注意结果可能会不一致,这往往会导致视频中的模糊和时间伪影。
本文提出时空转换网络STTN(Spatial-Temporal Transformer Network)。具体来说,是通过自注意机制同时填补所有输入帧中的缺失区域,并提出通过时空对抗性损失来优化STTN。为了展示该模型的优越性,我们使用标准的静止掩模和更真实的运动物体掩模进行了定量和定性的评价。
二、STTN
模型输入是图像帧序列和masks序列,图像帧序列经过Encoder、Mask经过scale变化成原来的1/4,然后一起送入Spatial-Temporal Transformer模块;Spatial-Temporal Transformer模块由8个TransformerBlock组成;最后Decoder模块负责将特征还原成图像帧序列。STTN的整体结构图如下:
图1
1.Encoder
Frame-Level Encoder帧级编码器,通过叠加二维卷积层来构建的,目的是为每一帧的低级别像素的深度特征,就是四个卷积层提取单帧图像特征,要素不多,结构图如下:
图2
代码如下:
# 位置model/sttn.py
self.encoder = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(128, channel, kernel_size=3, stride=1, padding=1),nn.LeakyReLU(0.2, inplace=True),)
2.Spatial-Temporal Transformer Network
这是STTN的核心部分,通过一个多头 patch-based attention模块沿着空间和时间维度进行搜索。transformer的不同头部计算不同尺度上对空间patch的注意力。这样的设计允许我们处理由复杂的运动引起的外观变化。例如,对大尺寸的patch(例如,帧大小H×W)旨在修复固定的背景;对小尺寸的patch(如H/10×W/10)有助于在视频的任意位置捕捉移动的前景信息。
(1)TranformerBlock
TransformerBlock由Embedding、Matching和Attending组成,代码中Matching和Attending被放在一起合成了MultiHeadedAttention。输入是帧序列特征和masks。
帧序列的特征平分成四部分,每个部分经过Embedding映射为四种尺度的Key、Query、Value,从而对应不同尺度的patch。masks经过变换也变成四个尺度。将四个尺度的Key、Query、Value和四个尺度masks分别送入MultiHeadedAttention,然后将结果Concat到一起,经过FeedForward层进一步分特征融合,得到融合了时间维度上不同尺度空间patch的特征。结构图如下:
图3
代码如下:
# 位置model/sttn.py
class TransformerBlock(nn.Module):"""Transformer = MultiHead_Attention + Feed_Forward with sublayer connection"""def __init__(self, patchsize, hidden=128):super().__init__()self.attention = MultiHeadedAttention(patchsize, d_model=hidden)self.feed_forward = FeedForward(hidden)def forward(self, x):x, m, b, c = x['x'], x['m'], x['b'], x['c']x = x + self.attention(x, m, b, c)x = x + self.feed_forward(x)return {'x': x, 'm': m, 'b': b, 'c': c}
(2)KQV Formatting
图3中的KQV Formatting结构如下图:
图4
TranformerBlock输入的帧序列特征,被平分成四个部分,每个部分经过变换,变成四种尺度patch的特征。
代码如下:
# 位置model/sttn.py
query = query.view(b, t, d_k, out_h, height, out_w, width)
query = query.permute(0, 1, 3, 5, 2, 4, 6).contiguous().view(b, t*out_h*out_w, d_k*height*width)
key = key.view(b, t, d_k, out_h, height, out_w, width)
key = key.permute(0, 1, 3, 5, 2, 4, 6).contiguous().view(b, t*out_h*out_w, d_k*height*width)
value = value.view(b, t, d_k, out_h, height, out_w, width)
value = value.permute(0, 1, 3, 5, 2, 4, 6).contiguous().view(b, t*out_h*out_w, d_k*height*width)
(3)Mask Formatting
KQV Formatting将帧序列变成四种尺度,masks也需要对应的变成四种尺度,结构如下:
图5
代码如下:
# 位置model/sttn.py
mm = m.view(b, t, 1, out_h, height, out_w, width)
mm = mm.permute(0, 1, 3, 5, 2, 4, 6).contiguous().view(b, t*out_h*out_w, height*width)
mm = (mm.mean(-1) > 0.5).unsqueeze(1).repeat(1, t*out_h*out_w, 1)
(4)Attention
图3中的Attention层其实包括了论文中的Matching和Attending,结构图如下:
图6
图6中的K*Q/sqrt(Q.size(-1))是在计算各个patch的相似性,对应论文中公式,第i个斑块与第j个patch的相似性记为::
图6中的masked_fill(Mask, -1e9)是将图像中的损坏部分mask掉,意思是只学习图像中完整的部分,坏的就不要学习了。
论文中的Attention对应图6中的matmul,负责计算相关patches的value加权和得到输出patch的query。公式如下:
代码如下:
# 位置model/sttn.py
class Attention(nn.Module):"""Compute 'Scaled Dot Product Attention"""def forward(self, query, key, value, m):scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(query.size(-1))scores.masked_fill(m, -1e9)p_attn = F.softmax(scores, dim=-1)p_val = torch.matmul(p_attn, value)return p_val, p_attn
3.Decoder
frame-level decoder: 帧级解码器,把特征解码成帧。期间特征图经过了两次的膨胀,中间穿插几个2d卷积,整体过程有点像Encoder倒过来,结构图如下:
图7
代码如下:
# 位置model/sttn.py
self.decoder = nn.Sequential(deconv(channel, 128, kernel_size=3, padding=1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1),nn.LeakyReLU(0.2, inplace=True),deconv(64, 64, kernel_size=3, padding=1),nn.LeakyReLU(0.2, inplace=True),nn.Conv2d(64, 3, kernel_size=3, stride=1, padding=1))
三、损失函数
本文使用GAN来对模型进行优化,G模型选择了一个像素级的重建损失即L1Loss,D网络使用T-PatchGAN来优化。
1.G模型损失函数
G模型图像破坏区域的L1Loss:
G模型图像有效区域的L1Loss:
STTN的对抗性损失:
上式看上去很复杂,其实就是将恢复的图像送入D模型,然后送入损失函数(可选nsgan、lsgan、hinge)
总结上面三个式子,得出G模型的损失函数,其中三个权重官方推荐
2.D网络的损失函数
对抗性的损失在提高视频绘制的感知质量和时空一致性方面显示出了良好的效果。公式如下:
看山去还是很复杂,其实就是将原图和复原图分别送入损失函数(可选nsgan、lsgan、hinge),然后求和,代码中是取均值,不过应该影响不大。
三、训练流程
下面是我根据官方代码梳理的整个训练过程:
1.从数据集选取数据,同时为选取的数据随机带有破坏图案的masks
2.根据masks将原图的破坏部分变成0,得到masked_frame
3.将masked_frame和masks送入G模型(生成模型,即STTN),得出估计pred_img
4.根据pred_img修复图像,得到comp_img
5.将原图和comp_img分别送入D模型,分别得到输出的特征 real_vid_feat和fake_vid_feat
6.使用real_vid_feat和fake_vid_feat对D模型进行优化(损失函数可选nsgan、lsgan、hinge)
7.使用原图、comp_img和gen_vid_feat对G模型进行优化(L1Loss)
代码如下:
# 位置core/trainer.pydef _train_epoch(self, pbar):device = self.config['device']for frames, masks in self.train_loader:self.adjust_learning_rate()self.iteration += 1frames, masks = frames.to(device), masks.to(device)b, t, c, h, w = frames.size()masked_frame = (frames * (1 - masks).float())# 将masked_frame和masks送入G模型(生成模型,即STTN),得出估计pred_imgpred_img = self.netG(masked_frame, masks)frames = frames.view(b*t, c, h, w)masks = masks.view(b*t, 1, h, w)# 根据pred_img修复图像,得到comp_imgcomp_img = frames*(1.-masks) + masks*pred_imggen_loss = 0dis_loss = 0# 将原图和comp_img分别送入D模型,分别得到输出的特征 real_vid_feat和fake_vid_featreal_vid_feat = self.netD(frames)fake_vid_feat = self.netD(comp_img.detach())# 计算D网络的损失dis_real_loss = self.adversarial_loss(real_vid_feat, True, True)dis_fake_loss = self.adversarial_loss(fake_vid_feat, False, True)dis_loss += (dis_real_loss + dis_fake_loss) / 2self.add_summary(self.dis_writer, 'loss/dis_vid_fake', dis_fake_loss.item())self.add_summary(self.dis_writer, 'loss/dis_vid_real', dis_real_loss.item())self.optimD.zero_grad()dis_loss.backward()# 使用real_vid_feat和fake_vid_feat对D模型进行优化self.optimD.step()# G模型的对抗性损失gen_vid_feat = self.netD(comp_img)gan_loss = self.adversarial_loss(gen_vid_feat, True, False)gan_loss = gan_loss * self.config['losses']['adversarial_weight']gen_loss += gan_lossself.add_summary(self.gen_writer, 'loss/gan_loss', gan_loss.item())# G模型图像破坏区域的L1Losshole_loss = self.l1_loss(pred_img*masks, frames*masks)hole_loss = hole_loss / torch.mean(masks) * self.config['losses']['hole_weight']gen_loss += hole_loss self.add_summary(self.gen_writer, 'loss/hole_loss', hole_loss.item())# G模型图像有效区域的L1Lossvalid_loss = self.l1_loss(pred_img*(1-masks), frames*(1-masks))valid_loss = valid_loss / torch.mean(1-masks) * self.config['losses']['valid_weight']gen_loss += valid_loss self.add_summary(self.gen_writer, 'loss/valid_loss', valid_loss.item())self.optimG.zero_grad()gen_loss.backward()# 使用原图、comp_img和gen_vid_feat对G模型进行优化self.optimG.step()# 日志if self.config['global_rank'] == 0:pbar.update(1)pbar.set_description((f"d: {dis_loss.item():.3f}; g: {gan_loss.item():.3f};"f"hole: {hole_loss.item():.3f}; valid: {valid_loss.item():.3f}"))# saving modelsif self.iteration % self.train_args['save_freq'] == 0:self.save(int(self.iteration//self.train_args['save_freq']))if self.iteration > self.train_args['iterations']:break
接下来代码中有些重点,需要简单说明一下:
1.准备数据集
项目中用到Davis或youtube-vos数据集,两个数据集其实都是为segmentation任务设计的,代码中都只使用图像数据,不使用标注数据。我们以davis数据集为例,davis数据集由90个视频组成,每个视频已经拆帧成图片,数据集下载完每个视频一个文件夹,但是程序需要每个视频这图片打成zip文件,下面的程序可以用来完成这个工作:
import os
import zipfiledef zipDir(dirpath, out_full_name):zipname = zipfile.ZipFile(out_full_name, 'w', zipfile.ZIP_DEFLATED)for path, dirnames, filenames in os.walk(dirpath):fpath= path.replace(dirpath, '')for filename in filenames:zipname.write(os.path.join(path, filename), os.path.join(fpath, filename))zipname.close()if __name__=="__main__":org_dir = r'datasets/davis/JPEGImages_org'zip_dir = r'datasets/davis/JPEGImages'g = os.walk(org_dir)for path, dir_list, file_list in g:for dir_name in dir_list:input_path = os.path.join(path, dir_name)output_path = os.path.join(zip_dir, dir_name+'.zip')print(input_path, '\n', output_path)zipDir(input_path, output_path)
2.数据选取策略
数据是从90个视频中随机挑一个,然后在这个视频中选取sample_length张图片,最终每个视频都会选取一个图片组,在论文中提到有两种数据选取策略,就是下面这个公式:
其中代表以t为中心n为半径的连续帧序列,代码实现是50%概率用一个长度为sample_length的框随机滑动选取;表示从以s采样率的视频中均匀采样的远处帧,代码中并未使用这种方式,而是50%概率随机选取帧,这样也许是为了解决缓解数据不够多的问题。
选图片组的代码如下:
# 位置:core/dataset.py
def get_ref_index(length, sample_length):# 50%概率随机选取帧if random.uniform(0, 1) > 0.5:ref_index = random.sample(range(length), sample_length)ref_index.sort()else:# 50%概率用一个长度为sample_length的框随机滑动选取pivot = random.randint(0, length-sample_length)ref_index = [pivot+i for i in range(sample_length)]return ref_index
3.生成随机masks
有了图片组,还需要为每个图片组随机生成masks。其中0代表背景,1代表破坏部分。代码如下,注释已经很清楚:
# 位置:core/utils.py
def create_random_shape_with_random_motion(video_length, imageHeight=240, imageWidth=432):# 生成的破坏图案宽高占原图的1/3到100%height = random.randint(imageHeight//3, imageHeight-1)width = random.randint(imageWidth//3, imageWidth-1)# 生成不规则的破坏图案edge_num = random.randint(6, 8)ratio = random.randint(6, 8)/10region = get_random_shape(edge_num=edge_num, ratio=ratio, height=height, width=width)region_width, region_height = region.size# 随机放置破坏图案x, y = random.randint(0, imageHeight-region_height), random.randint(0, imageWidth-region_width)velocity = get_random_velocity(max_speed=3)m = Image.fromarray(np.zeros((imageHeight, imageWidth)).astype(np.uint8))m.paste(region, (y, x, y+region.size[0], x+region.size[1]))masks = [m.convert('L')]# 50%概率所有的mask一样if random.uniform(0, 1) > 0.5:return masks*video_length# 50%概率mask中的破坏图案会移动for _ in range(video_length-1):x, y, velocity = random_move_control_points(x, y, imageHeight, imageWidth, velocity, region.size, maxLineAcceleration=(3, 0.5), maxInitSpeed=3)m = Image.fromarray(np.zeros((imageHeight, imageWidth)).astype(np.uint8))m.paste(region, (y, x, y+region.size[0], x+region.size[1]))masks.append(m.convert('L'))return masks
相关文章:
【论文笔记】图像修复Learning Joint Spatial-Temporal Transformations for Video Inpainting
论文地址:https://arxiv.org/abs/2007.10247 源码地址:GitHub - researchmm/STTN: [ECCV2020] STTN: Learning Joint Spatial-Temporal Transformations for Video Inpainting 一、项目介绍 当下SITA的方法大多采用注意模型,通过搜索参考帧…...
代码随想录算法训练营第二天 | 977.有序数组的平方 、209.长度最小的子数组 、59.螺旋矩阵II、总结
打卡第二天,认真做了两道题目,顶不住了好困,明天早上练完车回来再重新看看。 今日任务 第一章数组 977.有序数组的平方209.长度最小的子数组59.螺旋矩阵II 977.有序数组的平方 给你一个按 非递减顺序 排序的整数数组 nums,返回 每…...
Python pickle模块:实现Python对象的持久化存储
Python 中有个序列化过程叫作 pickle,它能够实现任意对象与文本之间的相互转化,也可以实现任意对象与二进制之间的相互转化。也就是说,pickle 可以实现 Python 对象的存储及恢复。值得一提的是,pickle 是 python 语言的一个标准模…...
【C++】C/C++内存管理
文章目录1. C/C内存分布2. C语言当中的动态内存管理3. C 内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型4. operator new 和operator delete 函数5. new和delete的实现原理5.1 内置类型5.2 自定义类型6. 定位new表达式(placement-new)7. 常见面试题7.1 …...
【测试】自动化测试02
努力经营当下,直至未来明朗! 文章目录前言 回顾 预告一、常见的元素操作1. 输入文本sendKeys()2. 点击click3. 提交submit(通过回车键提交)4. 清除clear5. 获取文本getText()6. 获取属性对应的值getAttribute()7. 查看title和ur…...
Python空间分析| 02 利用Python计算空间局部自相关(LISA)
局部空间自相关 import esda import numpy as np import pandas as pd import libpysal as lps import geopandas as gpd import contextily as ctx import matplotlib.pyplot as plt from geopandas import GeoDataFrame from shapely.geometry import Point from pylab im…...
idea快捷编码:生成for循环、主函数、判空非空、生成单例方法、输出;自定义快捷表达式
前言 idea可根据输入的简单表达式进行识别,快速生成语句 常用的快捷编码:生成for循环、主函数、判空非空、生成单例方法、输出 自定义快捷表达式 博客地址:芒果橙的个人博客 【http://mangocheng.com】 一、idea默认的快捷表达式查看 Editor…...
【Spring】@Value注入配置文件 application.yml 中的值失败怎么办
本期目录一、 问题背景二、 问题原因三、 解决方法一、 问题背景 今天碰到的问题是用 Value 注解无法注入配置文件 application.yml 中的配置值。 检查过该类已经交给 Spring 容器管理了,即已经在类上加了 Configuration 和 ConfigurationProperties(prefix &quo…...
CleanMyMac清理工具软件功能优势介绍
CleanMyMac更新最新版本x4.12,完美适配新版系统macOS10.14,拥有全新的界面。CleanMyMac可以让您安全、智能地扫描和清理整个系统,删除大型未使用的文件,减少iPod库的大小,最精确的应用程序卸载,卸载不必要的…...
【面试题】对JS中的事件冒泡、事件捕获、事件委托的理解
大厂面试题分享 面试题库后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。Dom标准事件流的触发的先…...
SAP 理解合并会计报表
随着企业集团的发展,集团内部会出现越来越多的公司;复杂的公司结构和复杂的集团内业务,使得集团内部管理困难重重,信息渠道严重失灵。除了内部管理的需要,企业还有义务向相关方提供详细的和及时的信息。ERP中的合并会计…...
Ubuntu 命令常用命令——定时启动程序
crontab -e 语法 crontab[ -u user ] file或 crontab[ -u user ] { -l | -r | -e }说明: crontab是用来让使用者在固定时间或固定间隔执行程序之用,换句话说,也就是类似使用者的时程表。 -U Lser 是指设定指定user的时程表,这个前提是你必…...
笔试题(十三):走迷宫
# 描述 # 定义一个二维数组 N*M ,如 5 5 数组下所示: # int maze[5][5] { # 0, 1, 0, 0, 0, # 0, 1, 1, 1, 0, # 0, 0, 0, 0, 0, # 0, 1, 1, 1, 0, # 0, 0, 0, 1, 0,}; # 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路&#…...
Gradle相关的知识学习
这里有一套博客文章写的比较通俗易懂:https://www.jianshu.com/p/8e1ddd19083a...
SpringMVC的工作原理
SpringMVC的工作原理流程图 SpringMVC流程 1、 用户发送请求至前端控制器DispatcherServlet。 2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。 3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截…...
问卷数据分析流程
文章目录一、数据合并1. 读取数据2. 数据预览二、数据清洗1. 检验ID是否重复,剔除ID重复项2. 剔除填写时间小于xx分钟的值3.处理 量表题 一直选一个选项的问题三、数据清洗1.1 将问卷单选题的选项code解码,还原成原来的选项1.2 自动获取单选题旧的选项列…...
【观察】Solidigm P44 Pro SSD评测:原厂品质+软硬兼施=性能怪兽
众所周知,目前SSD(固态硬盘)已取代HDD(机械硬盘)成为电脑中常见的存储设备,特别是在技术创新的持续推动下,如今SSD的速度和效率都在不断地提高,从SATA2 3GB发展到SATA3 6GBÿ…...
String对象的创建和比较
String类的概述 String类:代表字符串。 Java 程序中的所有字符串字面值(如 “abc” )都作 为此类的实例实现。 String是JDK中内置的一个类:java.lang.string 。 String表示字符串类型,属于引用数据类型,不…...
09 OpenCV图形检测
1 轮廓描边 cv2.findContours() 函数是OpenCV中用于寻找轮廓的函数之一。它可以用于在二值图像中查找并检测出所有的物体轮廓,以及计算出这些轮廓的各种属性,例如面积、周长、质心等。 cv2.findContours() 函数的语法如下: contours, hiera…...
解密Teradata与中国市场“分手”背后的原因!国产数据库能填补空白吗?
2月15日,西方的情人节刚刚过去一天,国内IT行业就爆出一个大瓜。 继Adobe、甲骨文、Tableau、Salesforce之后,又一个IT巨头要撤离中国市场。 Teradata天睿公司官宣与中国市场“分手”,结束在中国的直接运营。目前,多家…...
Bernstein-Vazirani算法
B-V算法 (1) 问题描述 给定布尔函数f:{0,1}n→0,1f:{\left\{ {0,1} \right\}^n} \to{0,1}f:{0,1}n→0,1, 函数fff的值是由输入比特串xxx和确定的比特串sss做模2意义下的内积:f(x)x⋅s(mod2),f\left( x \right) x \cdot s\left( {\bmod 2} \right),f(x)x⋅s(mod2),…...
华为OD机试 - 相对开音节 | 备考思路,刷题要点,答疑 【新解法】
最近更新的博客 【新解法】华为OD机试 - 关联子串 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 停车场最大距离 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试 - 任务调度 | 备考思路,刷题要点,答疑,od Base 提供【新解法】华为OD机试…...
MyBatis
一、MyBatis环境搭建创建工程启动idea开发工具,选择工具栏中的“file”--“new”--“project”选项弹出“new project”对话框,编辑项目名称 选择maven项目,项目路径 单击 create 创建即可。引入相关依赖<dependencies><dependency&…...
良好的作息表
今天给大家带来“传说中”的“世界上最健康的作息时间表”(仅供参考),随时提醒自己吧,毕竟身体可是自己的哦。 7:30 起床:英国威斯敏斯特大学的研究人员发现,那些在早上5:22-7:21分起床的人,其血液中有一种能引起心脏病…...
【郭东白架构课 模块一:生存法则】01|模块导学:是什么在影响架构活动的成败?
你好,我是郭东白。这节课是我们模块一的导入部分,我会先来介绍模块的主要内容,以及为什么我要讲生存法则这个话题。 一名软件架构师要为相对复杂的业务制定,并且引导实施一个结构化的软件方案。这个发现最终方案和推动实施的过程&…...
webshell免杀之函数与变量玩法
webshell免杀之函数与变量玩法 前言 前文列举了一些用符号免杀的例子,此篇文章就以函数和变量来尝试下免杀。 本文以PHP为例,用PHP中函数和变量及语法特性,在不隐藏函数关键字情况下进行免杀。 动态函数 PHP中支持一个功能叫 variable fu…...
【新解法】华为OD机试 - 去重求和 | 备考思路,刷题要点,答疑,od Base 提供
华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 去重求和 | 备考思路,刷题要点,答疑,od Base 提供 给定一个数组,编写一个函数, 计算他的最大N个数和最小N个数的和, 需要对数组进行去重。 输入 第一行输入M,M表示数组大小 第二行输入M个数,表…...
MySQL 服务正在启动.MySQL 服务无法启动.服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。总结较全 (已解决)
输入以下命令启动mysql: net start mysql出现以下错误提示: MySQL 服务正在启动 .MySQL 服务无法启动。服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。 出现这个问题的话,一般有几个情况: 一、MySQL安装文…...
【数据结构与算法】数组2:双指针法 二分法(螺旋矩阵)
文章目录今日任务1.Leetcode977:有序数列的平方(1)题目(2)思路(3)暴力排序(4)双指针法2.Leetcode209:长度最小的子数组(1)题目&#x…...
librtmp优化
librtmp是一个RTMP的开源库,很多地方用它来做推流、拉流。它是RTMPDump开源软件里的一部分,librtmp的下载地址:RTMPDump,目前最新版是V2.3。本文重点介绍librtmp优化。 1、调整网络输出块大小。 RTMP_Connect0函数中LibRTMP是关…...
吉水县建设局网站/seo技术培训课程
kill,就是这个人还可以挣扎的出来,给他家人打个电话,告诉他们他就要死了,不能回家吃晚饭了.kill -9,就是那个人死在马桶上,隔间还锁着,外面等候的人也不知道他死了.kill -0 PID 向某一进程发送一个无效的信号,如果该进程存在(能够接收信号),echo $?为0&…...
网站建设seo/每日财经要闻
大家好,我是“前端点线面”,一位新生代农民工,欢迎关注我获取最新前端知识和《前端百题斩》pdf版。1. 前言大家好,我是若川。最近组织了源码共读活动。每周读 200 行左右的源码。很多第一次读源码的小伙伴都感觉很有收获ÿ…...
商务网站如何推广/百度收录刷排名
数据库的读写分离的好处? 1. 将读操作和写操作分离到不同的数据库上,避免主服务器出现性能瓶颈; 2. 主服务器进行写操作时,不影响查询应用服务器的查询性能,降低阻塞,提高并发; 3. 数据拥有多个…...
那些是flash做的网站/西安霸屏推广
作为一名程序员,尤其是开发Android应用程序的程序员,不会自己折腾ROM简直就是一件不好意思的事情,于是乎我就费劲巴力的上网看资料,找工具,准备自己开始制作有“中国特色”的ROM,以下是解决的一些问题&…...
可以完成交易的网站 做/公司推广方案
4、常见函数的解读 Thread类的方法: sleep(long millis):让当前正在执行的线程休息多久,暂时让出cpu给其他线程执行,但是不会释放它所占有的对象锁。join():一个正在执行的线程调用了另外一个线程的join方法…...
广告平台源码/seo可以从哪些方面优化
PyInstaller的原理简介PyInstaller其实就是把python解析器和你自己的脚本打包成一个可执行的文件,和编译成真正的机器码完全是两回事,所以千万不要指望成打包成一个可执行文件会提高运行效率,相反可能会降低运行效率,好处就是在运…...