我的一些视觉工具(1)之动作预测--逐行详解C3D代码(Pytorch)

article/2025/10/14 15:32:22

该博客主要参考这个工程提供的代码
我们在他们代码的基础之上进行魔改
当然了,既然要魔改人家的数据,肯定要先把人家的思路看明白。
对于3D神经网络来讲,好多小伙伴最关心的肯定还是数据的喂入方法和数据集的格式喽。
下第一个部分就是详细第一个部分就是

1. 输入数据–数据预处理

1.1 输入数据的格式

C3D时直接处理视频数据的,但是这并不意味着C3D模型的输入数据就是视频。实际上,C3D模型的输入也是一系列的图像,这里面图像的格式是: [ c , l , h , w ] [c,l,h,w] [c,l,h,w],其中, c c c是图像通道数,一般是3。 l l l是模型需要处理的帧数,也就是一次输入多少张图像。在C3D模型中, l = 16 l=16 l=16。h和w是每帧图像的高和宽。

1.2 分析dataset.py函数的流程

主体函数大家感兴趣可以根据上面的链接下载下来看,在这里我是挨个函数分析

随机翻转图像函数:randomflip():
这个函数作用就是以0.5的概率让图像翻转。这个函数中的一个很关键的变量是buffer,想知道它是什么,还需要看后面的load_frames()函数
说实话,中间的连续两个水平翻转是什么意思,搞不懂,这不就是回到原来的状态了么,不信可以自己试试。

    def randomflip(self, buffer):"""随机水平翻转给定图像和地面真相,概率为0.5."""if np.random.random() < 0.5:#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。#这个buffer参考前面。这个enumerate()就是把buffer中的图像按组合为一个索引序列for i, frame in enumerate(buffer):frame = cv2.flip(buffer[i], flipCode=1)#cv2.flip翻转操作,flipCode=1水平,flipCode=0垂直翻转,flipCode=-1水平和垂直同时翻转buffer[i] = cv2.flip(frame, flipCode=1)return buffer

正则化图像函数normalize()
说实话,这个正则化方法也是够暴力的,暴力到我都不知道到底有没有作用

    def normalize(self, buffer):#正则化for i, frame in enumerate(buffer):frame -= np.array([[[90.0, 98.0, 102.0]]])buffer[i] = framereturn buffer

转换图像为张量格式,并调整通道的函数to_tensor()

    def to_tensor(self, buffer):#转换成张量,并让其适应于torch的格式return buffer.transpose((3, 0, 1, 2))

这个没啥说的了,就是把最后一个维度调整到了最前面,变成 [ c , l , h , w ] [c,l,h,w] [c,l,h,w]
下载图像帧的函数load_frames()
这个函数的输入是文件路径file_dir,

    def load_frames(self, file_dir):frames = sorted([os.path.join(file_dir, img) for img in os.listdir(file_dir)])frame_count = len(frames)buffer = np.empty((frame_count, self.resize_height, self.resize_width, 3), np.dtype('float32'))for i, frame_name in enumerate(frames):frame = np.array(cv2.imread(frame_name)).astype(np.float64)buffer[i] = framereturn buffer

裁剪函数crop()
这个函数的作用就是在时间上和空间上同时裁剪图像。首先,在空间上就是随机裁剪112*112的图像。在时间上就是随机选择16张图像。

    def crop(self, buffer, clip_len, crop_size):# 随机选择时间索引进行时间抖动+time_index = np.random.randint(buffer.shape[0] - clip_len)# 随机选择开始索引以裁剪视频height_index = np.random.randint(buffer.shape[1] - crop_size)width_index = np.random.randint(buffer.shape[2] - crop_size)# 使用索引对视频进行裁剪和抖动。空间裁剪是在整个阵列上执行的,因此每个帧都在同一位置进行裁剪。时间抖动通过连续帧的选择发生buffer = buffer[time_index:time_index + clip_len,height_index:height_index + crop_size,width_index:width_index + crop_size, :]return buffer

视频处理函数process_video()
这个函数应该是算这个Class的主要函数了,就是将视频分割成图像
函数的输入是video:待处理的视频, action_name:动作名,也就是类名, save_dir:保存图片的路径

    def process_video(self, video, action_name, save_dir):# Initialize a VideoCapture object to read video data into a numpy array#这个video时存储所有视频的总的文件夹。video_filename = video.split('.')[0]#提取每个视频文件的名字if not os.path.exists(os.path.join(save_dir, video_filename)):#判断保存视频的文件夹是存在os.mkdir(os.path.join(save_dir, video_filename))#如果不存在则需要生成相应文件夹capture = cv2.VideoCapture(os.path.join(self.root_dir, action_name, video))#读取本地图像(video中的)frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))#计算这个视频一共有多少帧。frame_width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))#计算帧的宽度frame_height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))#计算帧的高度# 确保分割的视频至少有16帧###############################下面这部分很重要,################################3EXTRACT_FREQUENCY = 4#提取频率。也就是每四帧提取一帧。这个也很好理解,其实就是如果连续提取的话,每帧之间没有太大的变化,这个可能会影响训练。if frame_count // EXTRACT_FREQUENCY <= 16:EXTRACT_FREQUENCY -= 1if frame_count // EXTRACT_FREQUENCY <= 16:EXTRACT_FREQUENCY -= 1if frame_count // EXTRACT_FREQUENCY <= 16:EXTRACT_FREQUENCY -= 1count = 0#计数器,计算这个视频中的帧的个数,读取的帧达到这个视频的帧的总数时,就停止循环i = 0#这个是用来命名图像时用的retaining = Truewhile (count < frame_count and retaining):#当count < frame_count也就是说,retaining, frame = capture.read()#读取视频帧,retaining表示是否还存在帧, frame表示视频帧if frame is None:continueif count % EXTRACT_FREQUENCY == 0:#取模 - 返回除法的余数。也就是总数与EXTRACT_FREQUENCY成倍数时,则执行下面的操作if (frame_height != self.resize_height) or (frame_width != self.resize_width):#判断图像尺寸是否满足要求frame = cv2.resize(frame, (self.resize_width, self.resize_height))#如果不满足要求,则再resize一下cv2.imwrite(filename=os.path.join(save_dir, video_filename, '0000{}.jpg'.format(str(i))), img=frame)#把图像保存下来#把图像一个个的保存到图像中i += 1count += 1# Release the VideoCapture once it is no longer neededcapture.release()#释放内存

预处理函数preprocess()函数的,这个函数主要是穿件一些文件夹,用于保存已经处理好的train val test数据

    #os.path.join()函数:连接两个或更多的路径名组件,使用'\'#os.listdir()返回指定文件中所有文件名。def preprocess(self):if not os.path.exists(self.output_dir):#判断是否存在这个路径文件,如果不存在则创建相应的文件。os.mkdir(self.output_dir)os.mkdir(os.path.join(self.output_dir, 'train'))os.mkdir(os.path.join(self.output_dir, 'val'))os.mkdir(os.path.join(self.output_dir, 'test'))# Split train/val/test setsfor file in os.listdir(self.root_dir):#读取UCF视频文件中所有动作类的文件名,即标签。file_path = os.path.join(self.root_dir, file)#生成每个动作类的文件路径video_files = [name for name in os.listdir(file_path)]#读取每个视频的名字,也就是动作名train_and_valid, test = train_test_split(video_files, test_size=0.2, random_state=42)#按照8:2分割训练集和测试集train, val = train_test_split(train_and_valid, test_size=0.2, random_state=42)#按照8:2分割训练集和验证#注意,上面两行分割的还是路径名,不是实际的图像train_dir = os.path.join(self.output_dir, 'train', file)#在train下面再生成每个动作的文件val_dir = os.path.join(self.output_dir, 'val', file)#在val下面再生成每个动作的文件test_dir = os.path.join(self.output_dir, 'test', file)#在test下面再生成每个动作的文件if not os.path.exists(train_dir):#判断是否创建完成train_dir = train_dir.replace("\\ ", "/")  # 在windows下使用这行代码,我是在win10下训练,所以使用这个#train_dir = train_dir.replace("\ ", "\\")  # 在Linux下使用这行代码print("train " + train_dir)os.mkdir(train_dir)#如果上面的文件不存在则需要创建一个文件if not os.path.exists(val_dir):val_dir = val_dir.replace("\\ ", "/")#val_dir = val_dir.replace("\ ", "\\")os.mkdir(val_dir)if not os.path.exists(test_dir):test_dir = test_dir.replace("\\ ", "/")#test_dir = test_dir.replace("\ ", "\\ ")os.mkdir(test_dir)for video in train:self.process_video(video, file, train_dir)for video in val:self.process_video(video, file, val_dir)for video in test:self.process_video(video, file, test_dir)print('Preprocessing finished.')

小函数讲完了,开始从同开始梳理dataset()函数的

    def __init__(self, dataset='ucf101', split='train', clip_len=16, preprocess=False):#clip_len 需要根据要求修改大小#preprocess 在运行将视频分割成图像时,这个参数设置为True。训练的时候设置为False#这个Path.db_dir是我们自己写的函数,在pypath.py函数中。这个函数的作用就是输出ucf101视频数据#的路径,和处理好的图像序列的保存位置self.root_dir, self.output_dir = Path.db_dir(dataset)folder = os.path.join(self.output_dir, split)#组合路径,这个路径就是视频数据的文件self.clip_len = clip_len#C3D模型每次处理的视频的长度是16,可以自己根据自己的需求和你的模型的特性设置self.split = split# 以下三个参数的选择如第4.1节所述self.resize_height = 128#图像resize后的高self.resize_width = 171#图像resize后的宽self.crop_size = 112#图像被裁剪后的尺寸112*112if not self.check_integrity():raise RuntimeError('Dataset not found or corrupted.' +' You need to download it from official website.')if (not self.check_preprocess()) or preprocess:print('Preprocessing of {} dataset, this will take long, but it will be done only once.'.format(dataset))self.preprocess()# 获取所有类文件夹中文件的所有文件名# 一次浏览一个类文件夹#os.path.join()函数:连接两个或更多的路径名组件,使用'\'#os.listdir()返回指定文件中所有文件名。self.fnames, labels = [], []#读取视频图像帧和对应的标签for label in sorted(os.listdir(folder)):#读取folder文件夹下的标签名,ucf将每个动作的视频放在一个文件夹中,因此文件夹名即是标签for fname in os.listdir(os.path.join(folder, label)):#读取指定文件下的图像的名字并合并成路径,然后读取每个子文件夹下面的视频self.fnames.append(os.path.join(folder, label, fname))#将读取子文件夹下面的视频,并添加到列表fnameslabels.append(label)#把每个视频的标签添加到列表labelsassert len(labels) == len(self.fnames)#判断视频数量和标签数量是否一样,如果不一样则触发异常print('Number of {} videos: {:d}'.format(split, len(self.fnames)))# 准备标签名称(字符串)和索引(整数)之间的映射self.label2index = {label: index for index, label in enumerate(sorted(set(labels)))}# 将标签名称列表转换为标签索引数组self.label_array = np.array([self.label2index[label] for label in labels], dtype=int)if dataset == "ucf101":if not os.path.exists('dataloaders/ucf_labels.txt'):with open('dataloaders/ucf_labels.txt', 'w') as f:for id, label in enumerate(sorted(self.label2index)):f.writelines(str(id+1) + ' ' + label + '\n')#writelines() 方法用于向文件中写入一序列的字符串。#其实是将标签id和标签名按行一次写在一个txt文件中elif dataset == 'hmdb51':if not os.path.exists('dataloaders/hmdb_labels.txt'):with open('dataloaders/hmdb_labels.txt', 'w') as f:for id, label in enumerate(sorted(self.label2index)):f.writelines(str(id+1) + ' ' + label + '\n')

最后处理好的UCF的类别txt文件是这样的:
在这里插入图片描述
获取fnames视频的个数

    def __len__(self):#读取视频的个数return len(self.fnames)

这个__getitem__()在python的Class中很有意思,一般来讲,它决定这个类最终的返回值

    def __getitem__(self, index):# 加载和预处理。buffer = self.load_frames(self.fnames[index])#读取fnames中的图像buffer = self.crop(buffer, self.clip_len, self.crop_size)labels = np.array(self.label_array[index])if self.split == 'test':# Perform data augmentationbuffer = self.randomflip(buffer)buffer = self.normalize(buffer)buffer = self.to_tensor(buffer)return torch.from_numpy(buffer), torch.from_numpy(labels)

在这里插入图片描述
在这里插入图片描述

dataset.py这块一开始看着挺乱的,其实主要思想无非就是:在第一次运行时,将processes=True
在这里插入图片描述
这个过程就是把所有视频按照每4帧提取一帧的频率将视频分割,然后按照8:1:1的比例分割成train,val,test三个数据集。
处理好之后考试训练,训练的时候,在train中随机抽取16帧作为训练数据。
就是这么简单。

1.3 C3D模型的处理数据的流程

第一步:从本地读取视频

    video = 'data/UCF-101/Punch/v_Punch_g01_c04.avi'#存放视频的路径,根据自己情况修改cap = cv2.VideoCapture(video)#读取本地视频retaining = True#用于判断是否读取图像

第二步,创建一个帧用于存放图像序列

 clip = []#创建一个容器用于存储帧,这个是列表

第三步,从视频cap中读取图像

retaining, frame = cap.read()
#retaining 为True 或者False,代表有没有读取到图片,第二个参数frame表示截取到一帧的图片

第四步,设置图像尺寸,裁剪需要的区域,并添加到容器clip中

        tmp_ = center_crop(cv2.resize(frame, (171, 128)))#作者先使用cv2.resize把图像#通过center_crop函数截取图像期望的部分,这边可以根据自己的需求修改tmp = tmp_ - np.array([[[90.0, 98.0, 102.0]]])#这个据说是正则化clip.append(tmp)#把处理好的图像存储到clip容器中,后面再逐帧提取。

第五步,将列表转换成适合pytorch的计算图

if len(clip) == 16:#因为每次要吃里16帧图像,所以要求时16,如果你想处理不同的帧,则可以设置不同的大小。修改了这个16.后面的都要修稿inputs = np.array(clip).astype(np.float32)inputs = np.expand_dims(inputs, axis=0)inputs = np.transpose(inputs, (0, 4, 1, 2, 3))#把图像存储格式修改成正常的合适inputs = torch.from_numpy(inputs)#转换为张量inputs = torch.autograd.Variable(inputs, requires_grad=False).to(device)#构建神经网络的计算图时,需用orch.autograd.Variable将Tensor包装起来,形成计算图中的节点。#backward()自动计算出所有需要的梯度。来针对某个变量执行grad获得想要的梯度值。with torch.no_grad():outputs = model.forward(inputs)probs = torch.nn.Softmax(dim=1)(outputs)label = torch.max(probs, 1)[1].detach().cpu().numpy()[0]cv2.putText(frame, class_names[label].split(' ')[-1].strip(), (20, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.6,(0, 0, 255), 1)cv2.putText(frame, "prob: %.4f" % probs[0][label], (20, 40),cv2.FONT_HERSHEY_SIMPLEX, 0.6,(0, 0, 255), 1)#在图像上显示文字clip.pop(0)cv2.imshow('result', frame)cv2.waitKey(30)

裁剪函数:center_crop()

def center_crop(frame):#图像切片,得到112*112的图像,这边可以根据自己的需求修改图像的修剪范围frame = frame[8:120, 30:142, :]return np.array(frame).astype(np.uint8)

这个函数很简单,就是裁剪函数。这里是固定模式裁剪了112*112的区域。当然,为了

2. C3D模型搭建

我觉得这个没啥讲的,就是儿童搭建积木。

import torch
import torch.nn as nn
from mypath import Pathclass C3D(nn.Module):"""The C3D network."""def __init__(self, num_classes, pretrained=False):super(C3D, self).__init__()self.conv1 = nn.Conv3d(3, 64, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.pool1 = nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2))self.conv2 = nn.Conv3d(64, 128, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.pool2 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))self.conv3a = nn.Conv3d(128, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.conv3b = nn.Conv3d(256, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.pool3 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))self.conv4a = nn.Conv3d(256, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.conv4b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.pool4 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))self.conv5a = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.conv5b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))self.pool5 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 1, 1))self.fc6 = nn.Linear(8192, 4096)self.fc7 = nn.Linear(4096, 4096)self.fc8 = nn.Linear(4096, num_classes)self.dropout = nn.Dropout(p=0.5)self.relu = nn.ReLU()self.__init_weight()if pretrained:self.__load_pretrained_weights()def forward(self, x):x = self.relu(self.conv1(x))x = self.pool1(x)x = self.relu(self.conv2(x))x = self.pool2(x)x = self.relu(self.conv3a(x))x = self.relu(self.conv3b(x))x = self.pool3(x)x = self.relu(self.conv4a(x))x = self.relu(self.conv4b(x))x = self.pool4(x)x = self.relu(self.conv5a(x))x = self.relu(self.conv5b(x))x = self.pool5(x)x = x.view(-1, 8192)x = self.relu(self.fc6(x))x = self.dropout(x)x = self.relu(self.fc7(x))x = self.dropout(x)logits = self.fc8(x)return logitsdef __load_pretrained_weights(self):"""Initialiaze network."""corresp_name = {# Conv1"features.0.weight": "conv1.weight","features.0.bias": "conv1.bias",# Conv2"features.3.weight": "conv2.weight","features.3.bias": "conv2.bias",# Conv3a"features.6.weight": "conv3a.weight","features.6.bias": "conv3a.bias",# Conv3b"features.8.weight": "conv3b.weight","features.8.bias": "conv3b.bias",# Conv4a"features.11.weight": "conv4a.weight","features.11.bias": "conv4a.bias",# Conv4b"features.13.weight": "conv4b.weight","features.13.bias": "conv4b.bias",# Conv5a"features.16.weight": "conv5a.weight","features.16.bias": "conv5a.bias",# Conv5b"features.18.weight": "conv5b.weight","features.18.bias": "conv5b.bias",# fc6"classifier.0.weight": "fc6.weight","classifier.0.bias": "fc6.bias",# fc7"classifier.3.weight": "fc7.weight","classifier.3.bias": "fc7.bias",}p_dict = torch.load(Path.model_dir())s_dict = self.state_dict()for name in p_dict:if name not in corresp_name:continues_dict[corresp_name[name]] = p_dict[name]self.load_state_dict(s_dict)def __init_weight(self):for m in self.modules():if isinstance(m, nn.Conv3d):# n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels# m.weight.data.normal_(0, math.sqrt(2. / n))torch.nn.init.kaiming_normal_(m.weight)elif isinstance(m, nn.BatchNorm3d):m.weight.data.fill_(1)m.bias.data.zero_()def get_1x_lr_params(model):"""This generator returns all the parameters for conv and two fc layers of the net."""b = [model.conv1, model.conv2, model.conv3a, model.conv3b, model.conv4a, model.conv4b,model.conv5a, model.conv5b, model.fc6, model.fc7]for i in range(len(b)):for k in b[i].parameters():if k.requires_grad:yield kdef get_10x_lr_params(model):"""This generator returns all the parameters for the last fc layer of the net."""b = [model.fc8]for j in range(len(b)):for k in b[j].parameters():if k.requires_grad:yield kif __name__ == "__main__":inputs = torch.rand(1, 3, 16, 112, 112)net = C3D(num_classes=101, pretrained=True)outputs = net.forward(inputs)print(outputs.size())

http://chatgpt.dhexx.cn/article/EZD3Yiub.shtml

相关文章

C3D-network论文笔记

前几天一直在看Faster-Rcnn源码和YOLOv3&#xff08;C语言&#xff09;源码&#xff0c;感觉时间浪费了不少&#xff0c;但是一个都没有看懂&#xff0c;总结出一句话就是&#xff1a;这TM写的是个啥&#xff1f;我为啥一个都看不懂&#xff0c;原理理解是一回事&#xff0c;看…

c3d代码

##c3d代码 原文链接&#xff1a;https://blog.csdn.net/ZJRN1027/article/details/80199248 cross_entropy_mean tf.reduce_mean( tf.nn.sparse_softmax_cross_entropy_with_logits(labelslabels,logitslogit) ) tf.summary.scalar( name_scope ‘_cross_entropy’, cross_en…

【C3D代码运行步骤】

1. 克隆或者下载项目 方法一&#xff1a;在github上直接下载项目&#xff1a;项目地址 方法二&#xff1a;使用git克隆&#xff08;推荐&#xff09;,相关内容参考Git的安装及github远程仓库ssh连接 git clone https://github.com/Niki173/C3D.git https://github.com/jfzhan…

行为识别C3D代码(pytorch)实现过程及常见错误

行为识别C3D代码(pytorch)实现过程及常见错误 1.C3D网络代码 C3D&#xff08;pytorch&#xff09;实现代码链接&#xff1a; C3D代码 2.C3D代码复现过程 &#xff08;1&#xff09;环境版本要求 pytorch&#xff1a;3.5及以上 opencv&#xff1a;3.4.2&#xff08;我是这样…

C3D的视频分类

很久之前做了C3D的视频分类&#xff0c;现在详细把整个项目的细节描述一下。 首先介绍一下C3D&#xff1a;对于一段视频来说&#xff0c;它是连续的帧图像叠加起来的&#xff0c;所以可以考虑在生成通道图像的时候&#xff0c;把多帧图像叠加的特性讨论进去。 一个视频段输入&…

C3D源码解读(基于3D卷积的动作识别)

UCF数据集下载地址:https://www.crcv.ucf.edu/data/UCF101.php 1.推理效果与项目配置 执行inference.py&#xff0c;需要指定3个参数&#xff0c;第一个是标签文件地址&#xff0c;存储了各个标签的含义&#xff0c;第二个是权重文件地址&#xff0c;第三个是要进行推理的视频…

C3D代码总结(Pytorch)

C3D代码总结&#xff08;Pytorch&#xff09; github&#xff1a;https://github.com/Niki173/C3D 介绍数据介绍文件介绍具体操作流程运行结果 介绍&#xff1a; 本次C3D模型用的是pytorch框架&#xff0c;我们在UCF101和HMDB51数据集上训练这些模型&#xff0c;本次实验以U…

C3D论文笔记

论文链接&#xff1a;http://vlg.cs.dartmouth.edu/c3d/c3d_video.pdf 代码链接&#xff1a;https://github.com/jfzhang95/pytorch-video-recognition 1. C3D是什么&#xff1f; C3D&#xff0c;全称Convolutional 3D&#xff0c;即3D卷积。3D卷积方法是把视频划分成很多固定…

C3D网络介绍

1. 模型简介 C3D模型广泛用于3D视觉任务。C3D网络的构造类似于常见的2D卷积网&#xff0c;主要区别在于C3D使用像卷积3D这样的3D操作&#xff0c;而2D卷积网则是通常的2D架构。要了解有关C3D网络的更多信息&#xff0c;您可以阅读原始论文学习3D卷积网络的时空特征。 3D卷积图…

视频分析模型(行为识别):C3D

C3D 文章目录 C3D1. 简介1.1 背景1.2 C3D特点1.3 视频描述符1.4 C3D的结果 2. 架构2.1 工作流程2.2 网络结构2.3 3D卷积和池化2.4 kernel 的时间深度 3. 可视化3.1 特征图3.2 特征嵌入 4. 应用场景4.1 动作识别4.2 动作相似度标注4.3 场景和目标识别4.4 运行时间分析 1. 简介 …

C3D论文精读

论文地址:https://vlg.cs.dartmouth.edu/c3d/c3d_video.pdf Abstract 作者的研究结果有三个方面&#xff1a; 1)与二维相比&#xff0c;三维卷积网更适合时空特征学习&#xff1b;2)所有层具有333的小卷积核的同构架构是3D卷积网的最佳架构之一&#xff1b;3)学习到的特征&am…

基于C3D网络的视频分析与动作识别

卷积神经网络&#xff08;CNN&#xff09;被广泛应用于计算机视觉中&#xff0c;包括分类、检测、分割等任务。这些任务一般都是针对图像进行的&#xff0c;使用的是二维卷积&#xff08;即卷积核的维度为二维&#xff09;。而对于基于视频分析的问题&#xff0c;2D convolutio…

《QDebug 2022年12月》

一、Qt Widgets 问题交流 二、Qt Quick 问题交流 1、在 C 中关联 QQuickWindow 的 closing 信号提示 "使用了未定义类型QQuickCloseEvent" 因为 closing 信号中的参数类型是 private 模块中定义的&#xff0c;但是通过第二句提示我们知道找到了完整定义才能使用 Q_…

4.4 案例8 用qDebug()输出信息

本案例对应的源代码目录&#xff1a;src/chapter04/ks04_04。 在开发C/S&#xff08;Client/Server&#xff0c;客户端/服务端&#xff09;模式的软件时&#xff0c;服务端程序&#xff08;有时也称作服务&#xff09;经常运行在两种模式下。 &#xff08;1&#xff09;终端模…

Qt扫盲-QDebug理论总结

QDebug理论使用总结 一、概述二、使用1. 基础使用2. 格式化选项3.将自定义类型写入流 一、概述 每当开发人员需要将调试或跟踪信息写入设备、文件、字符串或控制台时&#xff0c;都会使用QDebug。这个就可以方便我们调试&#xff0c;基本上Qt所有的内容都能通过调试打印出来&a…

Qt重定向QDebug,自定义一个简易的日志管理类

0.前言 相对于第三方的日志库&#xff0c;在 Qt 中使用 QDebug 打印更便捷&#xff0c;有时候也需要对 QDebug 输出进行重定向&#xff0c;如写入文件等。 在 Qt4 中使用 qInstallMsgHandler 函数设置重定向的函数指针&#xff1a; typedef void (*QtMsgHandler)(QtMsgType,…

qDebug 控制台输出

做个小笔记:qDebug 控制台输出 Ⅰ&#xff1a;*.pro文件中添加 win32:CONFIG console Ⅱ&#xff1a;配置项目运行设置&#xff0c;将Run in terminal 复选框打勾 Ⅲ&#xff1a;添加头文件 #include <QDebug> Ⅳ&#xff1a;用qDebug()<<"xxxx";输…

Qt ——debug调试

程序调试&#xff1a; 方法一&#xff1a;断点调试法方法二&#xff1a;使用qDebug()函数 方法一&#xff1a;断点调试法 我们可以在程序加断点&#xff0c;然后再利用单步调试查看变量的值是否异常。 1. 设置断点。 可以左击相应的代码行前的区域&#xff08;下图用红色框标…

jadx反编译—下载和使用(傻瓜教程,非常详细)

原文地址 一、在GitHub上直接下载 下载地址 可以下这个版本&#xff1a; 二、运行图形化界面 1、将zip文件解压后定位到在lib文件夹中&#xff0c;在此处打开命令行 2、运行jadx-gui-0.7.1.jar&#xff08;前提是已经装好了JDK1.8&#xff09; 命令如下&#xff1a; <sp…

Android APK 反编译工具 JADX

文章目录 JADX 介绍JADX 安装JADX 使用补充APK 目录结构含义APK 打包流程 JADX 介绍 GitHub 地址&#xff1a;https://github.com/skylot/jadx JADX 支持将 APK, dex, aar, zip 中的 dalvik 字节码反编译为 Java 代码&#xff0c;也支持反编译 AndroidManifest.xml 和 resource…