Simple-fasterrcnn源码学习笔记(4)
这里主要是utils下的部分文件的补充注释
文章目录
- config.py
- array_tool.py
- eval_tool.py
- vis_tool.py
- 不熟悉的numpy,pytorch,python操作
- data.detach()
- data.numpy()
- t.from_numpy(data)
- data.reshape(1)[0]
- data.item()
- from collections import defaultdict
- np.unique
- iter(pred_bboxes)#生成迭代器
- np.logical_not()
- np.cumsum(match_l==1)#统计累计match_1=1的个数
- np.nan_to_num
- np.maximum.accumulate(mpre[::-1])[::-1]
config.py
from pprint import pprint#打印出来更美观
class Config:#datavoc_data_dir='/home/wrc/yuyijie/KITTI/VOCdevkit/VOC2007'min_size=600max_size=1000num_works=8test_num_works=8rpn_sigma=3.roi_sigma=1.# for optimizerwd=0.0005lr_decay=0.1lr=1e-3#visenv='faster-rcnn'port=8097#visdom 端口plot_every=40#presetdata='voc'pretrained_model='vgg16'epoch=14use_adam=Falseuse_chainer=Falseuse_drop=False#debugdebug_file='/tmp/debugf'test_num=10000#modelload_path=Nonecaffe_pretrain=Falsecaffe_pretrain_path='checkpoints/vgg16_caffe.pth'def _parse(self,kwargs):#解析并设置用户设定的参数state_dict=self._state_dict()#读取Config类所有参数dict{para_name:para_value}for k,v in kwargs.items():#遍历用户传来的dictif k not in state_dict:raise ValueError('Unknow option:"--%s"'%k)setattr(self,k,v)#设置参数print('=============user config=========')pprint(self._state_dict())#打印参数print('=============end=========')def _state_dict(self):return {k:getattr(self,k) for k ,_ in Config.__dict__.items() if not k.startswith('_')}#字典解析,字典解析,Config.__dict__.items() 取出类中所有的函数、全局变量以及一些内置的属性# 前面我们设定的都是全局变量(键值对:比如min_size = 600),没有函数,而系统内置属性都是_打头的,# 所以我们要not k.startswith('_') 返回结果dict{para_name0:para_value0,para_name1:para_value1,....}
opt=Config()#创建config对象
array_tool.py
import torch as t
import numpy as npdef tonumpy(data):#将数据转化为Numpyif isinstance(data,np.ndarray):#如果数据是numpy类型 直接返回return dataif isinstance(data,t.Tensor):return data.detach().cpu().numpy()#将变量从图中分离(使得数据独立,以后你再如何操作都不会对图,对模型产生影响)#如果是gpu类型还要转化为cpu,再转化为numpydef totensor(data,cuda=True):#将数据转化为Tensor或者cudaif isinstance(data,np.ndarray):tensor=t.from_numpy(data)if isinstance(data,t.Tensor):tensor=data.detach() #隔离变量if cuda:tensor=tensor.cuda()return tensordef scalar(data):#取出数据的值if isinstance(data,np.ndarray):return data.reshape(1)[0]#如果是numpy类型(必须为1个数据 几维都行) 取出这个数据的值if isinstance(data,t.Tensor):return data.item()#如果是tensor类型 调用pytorch常用的item方法 取出tensor的值
eval_tool.py
from __future__ import division
from collections import defaultdict
#这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,
# 其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,而且具有默认值。
# 比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,
# 而且就算是一个不存在的key, d[key] 也有一个默认值,这个默认值是int()的默认值0.
import itertools
import numpy as np
import six
from model.utils.bbox_tools import bbox_ioudef eval_detection_voc(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults=None,iou_thresh=0.5,use_07_metric=False):#根据PASCAL VOC evaluation#所有参数都是list# test_num张图片(图片数据来自测试数据testdata)的预测框,标签,分数,和真实的框,标签和分数。所有参数都是list# len(list)=opt.test_num(default=10000)# pred_boxes: [(A, 4), (B, 4), (C, 4)....共test_num个]# 输入源gt_数据# 经过train.predict函数预测出的结果框# pred_labels[(A,), (B,), (C,)...共test_num个]# pred_scores同pred_labels# A, B, C, D是由nms决定的个数,即预测的框个数,不确定。# gt_bboxes:[(a, 4), (b, 4)....共test_num个]# a b...是每张图片标注真实框的个数# gt_labels与gt_difficults同理prec,rec=calc_detection_voc_prec_rec(#计算每个label类别的准确率和召回率pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults,iou_thresh=iou_thresh)ap=calc_detection_voc_ap(prec,rec,use_07_metric=use_07_metric)#根据prec和rec计算map和apreturn {'ap':ap,'map':np.nanmean(ap)}def calc_detection_voc_prec_rec(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults=None,iou_thresh=0.5):pred_bboxes=iter(pred_bboxes)#生成迭代器pred_labels=iter(pred_labels)#生成迭代器pred_scores=iter(pred_scores)#生成迭代器gt_bboxes=iter(gt_bboxes)#生成迭代器gt_labels=iter(gt_labels)#生成迭代器if gt_difficults is None:gt_difficults=itertools.repeat(None)#itertools.repeat生成一个重复的迭代器 None是每次迭代获得的数值else:gt_difficults=iter(gt_difficults)n_pos=defaultdict(int)#defaultdict当key不存在时,dict[key]=default(int)=0 default(list)=[]score=defaultdict(list)match=defaultdict(list)for pred_bbox,pred_label,pred_score,gt_bbox,gt_label,gt_difficult in six.moves.zip(pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficults):if gt_difficults is None:gt_difficults=np.zeros(gt_bbox.shape[0],dtype=bool)#全部设置为非difficult#遍历一边图片中,所有出现的labelfor l in np.unique(np.concatenate((pred_label,gt_label)).astype(int)):#拼接后返回无重复的从小到大排序的一维numpy 如[2,3,4,5,6]# 并遍历这个一维数组,即遍历这张图片出现过的标签数字(gt_label+pred_label)#>>> np.unique([1, 1, 2, 2, 3, 3])# array([1, 2, 3])# >>> a = np.array([[1, 1], [2, 3]])# >>> np.unique(a)# array([1, 2, 3])pred_mask_l=pred_label==l#//广播pred_mask_l=[eg. T,F,T,T,F,F,F,T..] 所有预测label中等于L的为T 否则Fpred_bbox_l=pred_bbox[pred_mask_l]#选出label=L的所有pre_boxpred_score_l=pred_scores[pred_mask_l]#label=L 对应所有pre_score#sort by scoreorder=pred_score_l.argsort()[::-1]#获得score降序排序索引pred_bbox_l=pred_bbox_l[order]pred_score_l=pred_score_l[order]gt_mask_l=gt_label==l#同理gt_bbox_l=gt_bbox[gt_mask_l]gt_difficult_l=gt_difficult[gt_mask_l]n_pos[l]+=np.logical_not(gt_difficult_l).sum()#对T,F取反求和,统计出difficult=0的个数score[l].extend(pred_score_l)#score={l:predscore_l,...} extend是针对defaultdict中是list的情况if len(pred_bbox_l)==0:#没有预测的label=L的box,即真实label有L,我们全没有预测到continue#跳过这张图片 此时没有对match字典操作,之前score[l].extend操作也为空 保持了match和score的形状一致if len(gt_bbox_l)==0:#没有真实的label=L的情况 即预测中有L 真实中没有 全都预测错了match[l].extend((0,)*pred_bbox_l.shape[0])#match{L:[0,0,0...n_pred_box个0]}# VOC evaluation follows integer typed bounding boxes.# 作者给的注释是follows integer typed bounding boxes# 但是只改变了ymax, xmax的值,重要的是这样做并不能转化为整数# pred_bbox和gt_bbox只# 参与了IOU计算且后面没有参与其他计算pred_bbox_l=pred_bbox_l.copy()pred_bbox_l[:,2:]+=1#ymax,xmax+=1gt_bbox_l=gt_bbox_l.copy()gt_bbox_l[:,2:]+=1iou=bbox_iou(pred_bbox_l,gt_bbox_l)#计算两个box的iougt_index=iou.argmax(axis=1)#有len(pred_bbox_l)个索引,第i个索引值n表示gt_box[n]与pred_box[i]iou最大#比如 4个pred_box 3 个gtbox# gt0 gt1 gt2 最大对应用*表示 这里的gt_index输出将是[1,0,0,2] 第0个索引值1 表示gt1和pred_box[0]iou最大# A *# B *# C *# D *#要注意 这里的gt_index是会重复的 因为同一个gt会与很多pred_box拥有最大iou# #计算iou 将会是(N,K)纬度的输出,如果所有tl都大于br的话gt_index[iou.max(axis=1)<iou_thresh]=-1#这里则是在上面的基础上把小于阈值的gt_index设置为-1#将gt_box与pred_box iou<thresh的索引值置为-1# 即针对每个pred_bbox,与每个gt_bbox IOU的最大值 如果最大值小于阀值则置为-1# 即我们预测的这个box效果并不理想 后续会将index=-1的 matchlabel=0del iouselec=np.zeros(gt_bbox_l.shape[0],dtype=bool)for gt_idx in gt_index:#遍历gt_index索引值if gt_index>=0:#即iou满足条件的bboxif gt_difficult_l[gt_idx]:#对应的gt_difficult=1即困难标记match[l].append(-1)#match[l] 后面追加一个-1else:#这里的做法可能有个问题 就是比如gt1同时根B,C两个prebox拥有超过阈值的iou这时候的排序就很关键 ,要先按照分数来进行排序if not selec[gt_idx]:#没有被选国,select[idx]=0的时候match[l].append(1)else:#对应的gt_box已经被选国一次,即已经和前面的某个pre_box iou最大match[l].append(0)selec[gt_idx]=True#一个gt被选过之后,则要设置为Trueelse:#不满足iou>threshmatch[l].append(0)#追加0 很重复匹配的结果一样#我们注意到上面为每个pred_box都打了label 0, 1, -1#len(match[l]) = len(score[l]) = len(pred_bbox_l)for iter_ in ( # 上面的 six.moves.zip遍历会在某一iter遍历到头后停止,由于pred_bboxes等是全局iter对象,#我们此时继续调用next取下一数据,如果有任一数据不为None,那么说明他们的len是不相等的 有悖常理,数据错误pred_bboxes,pred_labels,pred_scores,gt_bboxes,gt_labels,gt_difficult):if next(iter_,None) is not None:#next(iter_,None)表示调用next 如果已经遍历到了头 不抛出异常而是返回Noneraise ValueError("Length of input iterables need to be same")n_fg_class=max(n_pos.keys())+1#有n_fg_class个类prec=[None]*n_fg_classrec=[None]*n_fg_classfor l in n_pos.keys():#遍历所有labelscore_l=np.array(score[l])match_l=np.array(match[l],dtype=np.int8)order=score_l.argsort()[::-1]match_l=match_l[order]#对应match按照score由大到小排序tp=np.cumsum(match_l==1)#统计累计match_1=1的个数# 比如 match_l =[1,1,1,0,1,1] np.cumsum(match)=[1,2,3,3,4,5]# tp=[1,2,3,3,4,5] fp=[0,0,0,1,1,1] 长度是所有predbox的总数 prec[l]=[1,1,1,0.75,0.8,0.833]fp=np.cumsum(match_l==0)prec[l]=tp/(tp+fp)if n_pos[l]>0:#如果n_pos[l]=0 那么rec[l]=Nonerec[l]=tp/n_pos[l]#这里干嘛不用所有的gt[l]的总数return prec,rec
def calc_detection_voc_ap(prec,rec,use_07_metric=False):n_fg_class=len(prec)ap=np.empty(n_fg_class)for l in six.moves.range(n_fg_class):#遍历每个labelif prec[l] is None or rec[l] is None:#如果为NOne 则ap设置为np.nanap[l]=np.nancontinueif use_07_metric:# 11 point metricap[l]=0for t in np.arange(0.,1.1,0.1):#t=0 0.1 0.2...1.0if np.sum(rec[l]>=t)==0:#这个标签召回率没有大于阈值的p=0else:p=np.max(np.nan_to_num(prec[l])[rec[l]>=t])#p=(rec>=t时,对应index:prec中的最大值) np.nan_to_num是为了#让None=0一边计算ap[l]+=p/11else:mpre=np.concatenate(([0],np.nan_to_num(prec[l]),[0]))#头尾添加0mrec=np.concatenate(([0],rec[l],[1]))#头添加0 尾添加1mpre=np.maximum.accumulate(mpre[::-1])[::-1]#获得从小到大的累计最大值#>>> np.add.accumulate([2, 3, 5])# array([ 2, 5, 10])# >>> np.multiply.accumulate([2, 3, 5])# array([ 2, 6, 30])# 我们知道# 我们是按score由高到低排序的# 而且我们给box打了label# 0, 1, -1# score高时1的概率会大,所以pre是累计降序的# 而rec是累积升序的,那么此时将pre倒序再maxuim.ac# 获得累积最大值,再倒序后# 从小到大排序的累积最大值i=np.where(mrec[1:]!=mrec[:-1])[0]#差位比较,看哪里改变了recall的值,记录index(x轴)#and sum (\Delta recall)*precap[l]=np.sum((mrec[i+1]-mrec[i])*mrec[i+1])#差值*mpre_max的值,(x轴之差*ymax)return ap
vis_tool.py
import timeimport numpy as np
import matplotlib
import torch as t
import visdommatplotlib.use('Agg')
from matplotlib import pyplot as plot# from data.voc_dataset import VOC_BBOX_LABEL_NAMESVOC_BBOX_LABEL_NAMES = ('fly','bike','bird','boat','pin','bus','c','cat','chair','cow','table','dog','horse','moto','p','plant','shep','sofa','train','tv',
)
def vis_image(img,ax=None):#img是经过你标准化的0-255的rgb图像 ax是传来的matplotlib.axes,Axis对象,告诉我们画在那里if ax is None:#如果没有这个对象 创建一个(1,1,1)的fig=plot.figure()ax=fig.add_subplot(1,1,1)#CHW-》HWCimg=img.transpose(1,2,0)ax.imshow(img.astype(np.uint8))#matplotlib 支持0-1的图像显示,也支持0-255的图像显示,转成uint8是为了告诉它我们的图像是0-255的return ax #返回axis对象后续使用,vis_box会继续调用,在此图片基础上画框等def vis_bbox(img,bbox,label=None,score=None,ax=None):label_names=list(VOC_BBOX_LABEL_NAMES)+['bg']if label is not None and not len(bbox)==len(label):#框个数不等于标签个数raise ValueError('The length of label must be same as that of bbox')if score is not None and not len(bbox)==len(score):#score个数不等于标签个数raise ValueError('The length of score must be same as that of bbox')ax=vis_image(img,ax=ax)if len(bbox)==0:#没有框的话return axfor i,bb in enumerate(bbox):#遍历一张图中所有的框xy=(bb[1],bb[0])#左上角height=bb[2]-bb[0]width=bb[3]-bb[1]ax.add_patch(plot.Rectangle(xy,width,height,fill=False,edgecolor='red',linewidth=2))#画矩形caption=list()if label is not None and label_names is not None:lb=label[i]#此框对应的labelif not (-1<=lb<len(label_names)):#label名字超过边界raise ValueError('No corresponding name is given')caption.append(label_names[lb])#物体名字加入caption listif score is not None:sc=score[i] #找到此框对应分数caption.append('{:.2f}'.format(sc))#加入caption listif len(caption)>0:ax.text(bb[1],bb[0],':'.join(caption),style='italic',bbox={'facecolor':'white','alpha':0.5,'pad':0})#bbox是框住字体的框的一些参数return ax
def fig2data(fig):#将matplotlib的figure图像 转化为RGBA形式的NUmpy返回fig.canvas.draw()#画图,渲染 fig.canvas.draw()重绘所有内容。这是你的瓶颈。就你而言,你不需要重新绘制轴边界,刻度标签等等w,h=fig.canvas.get_width_height()#得到figure的w,hbuf=np.fromstring(fig.canvas.tostring_argb(),dtype=np.uint8)#得到numpy形式的argb图像buf.shape=(w,h,4)#(w,h,argb)buf=np.roll(buf,3,axis=2)#(w,h,rgba) 在第二轴水平向右滚动3个距离return buf.reshape(h,w,4)#(h,w,4)
def fig4vis(fig):#将matplotlib的figure图像转化为pytorch 常用的3通道RGB CHW 0-1图像ax=fig.get_figure()img_data=fig2data(ax).astype(np.int32)plot.close()return img_data[:,:,:3].transpose((2,0,1))/255#(H,W,RGBA)-》(H,W,RGB)-》(RGB,H,W)即CHW/255 -》0-1def visdom_bbox(*args,**kwargs):#我们要使用的函数 包含上面所有的函数调用,传入img box score 画成一张图像 以numpy的形式返回fig=vis_bbox(*args,**kwargs)data=fig4vis(fig)return dataclass Visualizer(object): #Visdom可视化部分def __init__(self, env='default', **kwargs):self.vis = visdom.Visdom(env=env, use_incoming_socket=False, **kwargs) #visdom对象self._vis_kw = kwargs #参数self.index = {}self.log_text = ''def reinit(self, env='default', **kwargs):#可能由于用户要改变kwargs初始化参数 而重新初始化visdom对象self.vis = visdom.Visdom(env=env, **kwargs)return selfdef plot_many(self, d):#plot#参数 d: dict (name,value) i.e. ('loss',0.11)for k, v in d.items():if v is not None:self.plot(k, v) #调用下面的plot函数 画折线图(各种损失折线图,训练map折线图)def img_many(self, d): #参数 d: dict (name,value) i.e. ('loss',0.11)for k, v in d.items():self.img(k, v) #调用下面的img函数 画图def plot(self, name, y, **kwargs): #画一条直线 title=namex = self.index.get(name, 0)self.vis.line(Y=np.array([y]), X=np.array([x]),win=name,opts=dict(title=name),update=None if x == 0 else 'append',**kwargs)self.index[name] = x + 1def img(self, name, img_, **kwargs): #画图片 title=nameself.vis.images(t.Tensor(img_).cpu().numpy(),win=name,opts=dict(title=name),**kwargs)def log(self, info, win='log_text'): #打印log日志 时间+infoself.log_text += ('[{time}] {info} <br>'.format(time=time.strftime('%m%d_%H%M%S'), \info=info))self.vis.text(self.log_text, win)def __getattr__(self, name): #按name获得一个属性return getattr(self.vis, name)def state_dict(self): #返回现有参数 dictreturn {'index': self.index,'vis_kw': self._vis_kw,'log_text': self.log_text,'env': self.vis.env}def load_state_dict(self, d): #参数 d: dict (name,value) i.e. ('loss',0.11) 初始化参数self.vis = visdom.Visdom(env=d.get('env', self.vis.env), **(self.d.get('vis_kw')))self.log_text = d.get('log_text', '')self.index = d.get('index', dict())return self
不熟悉的numpy,pytorch,python操作
data.detach()
将tensor变量从图中分离(使得数据独立,以后你再如何操作都不会对图,对模型产生影响)
data.numpy()
tensor2numpy
t.from_numpy(data)
numpy2tensor
data.reshape(1)[0]
如果是numpy类型(必须为1个数据 几维都行) 取出这个数据的值
data.item()
如果是tensor类型 调用pytorch常用的item方法 取出tensor的值
from collections import defaultdict
这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,
其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,而且具有默认值。 比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,
而且就算是一个不存在的key, d[key] 也有一个默认值,这个默认值是int()的默认值0.
这在计算map等eval指标的时候非常有用
比如
if not selec[gt_idx]:#没有被选国,select[idx]=0的时候 match[l].append(1)
就能把所有的1都放到l对应的list中 而且即使没有对应的值,该list也会存在只是为空
{‘8’:[1,1,1,1]}这种形式
np.unique
#拼接后返回无重复的从小到大排序的一维numpy 如[2,3,4,5,6]# 并遍历这个一维数组,即遍历这张图片出现过的标签数字(gt_label+pred_label)#>>> np.unique([1, 1, 2, 2, 3, 3])# array([1, 2, 3])# >>> a = np.array([[1, 1], [2, 3]])# >>> np.unique(a)# array([1, 2, 3])
iter(pred_bboxes)#生成迭代器
把一个list可以变成迭代器,相比直接用list去做循环能节省很多内存
迭代器的节省内存的原因:
一个是生成器函数,外表看上去像是一个函数,但是没有用return语句一次性的返回整个结果对象列表,取而代之的是使用yield语句一次返回一个结果。另一个是生成器表达式,类似于上一小节的列表解析,但是方括号换成了圆括号,他们返回按需产生的一个结果对象,而不是构建一个结果列表。
生成器函数他通过yield关键字返回一个值后,还能从其退出的地方继续运行,因此可以随时间产生一系列的值。他们自动实现了迭代协议,并且可以出现在迭代环境中。运行的过程是这样的:生成器函数返回一个迭代器,for循环等迭代环境对这个迭代器不断调用next函数,不断的运行到下一个yield语句,逐一取得每一个返回值,直到没有yield语句可以运行,最终引发StopIteration异常。
np.logical_not()
输入一组bool型的数据
对T,F取反
np.cumsum(match_l==1)#统计累计match_1=1的个数
cumsum(a, axis=None, dtype=None, out=None):
Axis along which the cumulative sum is computed. The default
(None) is to compute the cumsum over the flattened array.
Return the cumulative sum of the elements along a given axis.
比如 match_l =[1,1,1,0,1,1] np.cumsum(match==1)=[1,2,3,3,4,5]
tp=[1,2,3,3,4,5] fp=[0,0,0,1,1,1] 长度是所有predbox的总数 prec[l]=[1,1,1,0.75,0.8,0.833]
Examples-------->>> a = np.array([[1,2,3], [4,5,6]])>>> aarray([[1, 2, 3],[4, 5, 6]])>>> np.cumsum(a)array([ 1, 3, 6, 10, 15, 21])>>> np.cumsum(a, dtype=float) # specifies type of output value(s)array([ 1., 3., 6., 10., 15., 21.])>>> np.cumsum(a,axis=0) # sum over rows for each of the 3 columnsarray([[1, 2, 3],[5, 7, 9]])>>> np.cumsum(a,axis=1) # sum over columns for each of the 2 rowsarray([[ 1, 3, 6],[ 4, 9, 15]])"""
np.nan_to_num
Replace NaN with zero and infinity with large finite numbers
-------->>> np.nan_to_num(np.inf)1.7976931348623157e+308>>> np.nan_to_num(-np.inf)-1.7976931348623157e+308>>> np.nan_to_num(np.nan)0.0>>> x = np.array([np.inf, -np.inf, np.nan, -128, 128])>>> np.nan_to_num(x)array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, # may vary-1.28000000e+002, 1.28000000e+002])>>> np.nan_to_num(x, nan=-9999, posinf=33333333, neginf=33333333)array([ 3.3333333e+07, 3.3333333e+07, -9.9990000e+03, -1.2800000e+02, 1.2800000e+02])>>> y = np.array([complex(np.inf, np.nan), np.nan, complex(np.nan, np.inf)])array([ 1.79769313e+308, -1.79769313e+308, 0.00000000e+000, # may vary-1.28000000e+002, 1.28000000e+002])>>> np.nan_to_num(y)array([ 1.79769313e+308 +0.00000000e+000j, # may vary0.00000000e+000 +0.00000000e+000j,0.00000000e+000 +1.79769313e+308j])>>> np.nan_to_num(y, nan=111111, posinf=222222)array([222222.+111111.j, 111111. +0.j, 111111.+222222.j])"""
np.maximum.accumulate(mpre[::-1])[::-1]
获得从小到大的累计最大值
Simple-fasterrcnn源码学习笔记(4)相关推荐
- Java多线程之JUC包:Semaphore源码学习笔记
若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...
- Vuex 4源码学习笔记 - 通过Vuex源码学习E2E测试(十一)
在上一篇笔记中:Vuex 4源码学习笔记 - 做好changelog更新日志很重要(十) 我们学到了通过conventional-changelog来生成项目的Changelog更新日志,通过更新日志 ...
- Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)
在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二) 我们通过运行npm run dev命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个 ...
- jquery源码学习笔记三:jQuery工厂剖析
jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...
- 雷神FFMpeg源码学习笔记
雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...
- Apache log4j-1.2.17源码学习笔记
(1)Apache log4j-1.2.17源码学习笔记 http://blog.csdn.net/zilong_zilong/article/details/78715500 (2)Apache l ...
- PHP Yac cache 源码学习笔记
YAC 源码学习笔记 本文地址 http://blog.csdn.net/fanhengguang_php/article/details/54863955 config.m4 检测系统共享内存支持情 ...
- Vuex 4源码学习笔记 - 通过dispatch一步步来掌握Vuex整个数据流(五)
在上一篇笔记中:Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) 我们通过查看Store 构造函数的源代码可以看到主要做了三件事情: 初始化一些内部变量以外 执行installMod ...
- RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法
前言 RecyclerView已经出来很久,现在几乎应该都会用RecyclerView代替Listview,虽然我觉得大多数人应该还是不太清楚这两者之前的区别的,或者说RecyclerView相对于L ...
最新文章
- 力扣(LeetCode)刷题,简单+中等题(第29期)
- java mobile phone games_j2me100-src Java
- linux shell 变量 管道,linux下shell,变量,管道,重定向等基础知识及技巧
- WebDriver(C#)之十点使用心得
- Java EE 7是最终版本。 思想,见解和进一步的指针。
- C语言的inline
- Codeforces Round #249 (Div. 2) A. Queue on Bus Stop
- 亚马逊靠“新闻稿”推动创新,跃居市值第一
- logback整合Logstash
- 飞秋命令行发送消息和文件
- wifi上行下行速度测试_测试网速_测试网速wifi在线测试
- flog和flag,FLAG标签和3xFLAG标签的序列
- Katana中设置全局变量
- win7用友u8安装教程_如何在win7系统中安装用友u8(图文)
- 使用rem,使字体大小自适应屏幕
- 2020知道python答案_2020知道智慧树Python程序设计答案
- Unity有哪些适合拿来练手的游戏项目?
- 国际歌 英特纳雄耐尔一定要实现
- java 切换系统输入法,Android7.0更换系统默认输入法
- tomcat的宏观架构
热门文章
- TP-LINK WR720N升级8M Flash 64M Ram,加USB-TTL,烧openwrt官方固件,接蓝牙适配器
- 换零钱程序c语言,《SICP》换零钱的递归法与迭代法
- 前端优化系列:DNS预获取 dns-prefetch 提升页面载入速度
- Latch及latch冲突
- 基于seq2seq的中国古诗词自动生成技术
- WordPress常见问题及其解决方法
- linux驱动 .c文件,linux驱动程序在.c文件和make文件都正确的时候为什么在键入命令mak...
- mysql 概念与命令总结(贼全)
- windows找不到文件cmd解决方法
- 使用click创建完美的Python命令行程序