精华内容
下载资源
问答
  • 代码阅读】PointRCNN网络可视化,代码详解

    万次阅读 多人点赞 2019-06-04 17:43:47
    其实代码中的注释已经写的非常好了,这里就直接写一下都做了些什么: 读取calib,image_shape,pts # lib/datasets/kitti_rcnn_dataset.py def get_rpn_sample ( self , index ) : sample_id = ...

    数据准备

    统计gt_box

    作者使用generate_gt_database.py生成储存了数据集所有Car的gt box的信息的文件,包括每个gt box的:

    • sample_id:gt box所对应的文件名
    • cls_type:gt box的cls type
    • gt_box3d:gt box的3D信息
    • points:gt box中包含的点云
    • intensity:gt box中包含的电云的强度
    • obj:这个gt box对应object所有的信息,例如center,size,angle,occlusion,level等

    dataset

    首先定义kitti_dataset,定义通用接口,初始化data的寻找路径等

    # lib/datasets/kitti_dataset.py
    class KittiDataset(torch_data.Dataset):
        def __init__(self, root_dir, split='train'):
            self.split = split
            is_test = self.split == 'test'
            self.imageset_dir = os.path.join(root_dir, 'KITTI', 'object', 'testing' if is_test else 'training')
    
            split_dir = os.path.join(root_dir, 'KITTI', 'ImageSets', split + '.txt')
            self.image_idx_list = [x.strip() for x in open(split_dir).readlines()]
            self.num_sample = self.image_idx_list.__len__()
    
            self.image_dir = os.path.join(self.imageset_dir, 'image_2')
            self.lidar_dir = os.path.join(self.imageset_dir, 'velodyne')
            self.calib_dir = os.path.join(self.imageset_dir, 'calib')
            self.label_dir = os.path.join(self.imageset_dir, 'label_2')
            self.plane_dir = os.path.join(self.imageset_dir, 'planes')
    
        def get_image(self, idx):
    
        def get_image_shape(self, idx):
    
        def get_lidar(self, idx):
    
        def get_calib(self, idx):
    
        def get_label(self, idx):
    
        def get_road_plane(self, idx):
    
        def __len__(self):
    
        def __getitem__(self, item):
    
    

    然后定义PointRCNN特殊的dataset,主要是完成提取数据,数据增广等操作。这里主要看准备用于训练rpn的数据。其实代码中的注释已经写的非常好了,这里就直接写一下都做了些什么:

    • 读取calib,image_shape,pts
    # lib/datasets/kitti_rcnn_dataset.py
        def get_rpn_sample(self, index):
            sample_id = int(self.sample_id_list[index])
            if sample_id < 10000:
                calib = self.get_calib(sample_id)
                # img = self.get_image(sample_id)
                img_shape = self.get_image_shape(sample_id)
                pts_lidar = self.get_lidar(sample_id)
    
                # get valid point (projected points should be in image)
                # 将pts转换到cam0坐标系内
                pts_rect = calib.lidar_to_rect(pts_lidar[:, 0:3])
                pts_intensity = pts_lidar[:, 3]
            else:
                calib = self.get_calib(sample_id % 10000)
                # img = self.get_image(sample_id % 10000)
                img_shape = self.get_image_shape(sample_id % 10000)
    
                pts_file = os.path.join(self.aug_pts_dir, '%06d.bin' % sample_id)
                assert os.path.exists(pts_file), '%s' % pts_file
                aug_pts = np.fromfile(pts_file, dtype=np.float32).reshape(-1, 4)
                pts_rect, pts_intensity = aug_pts[:, 0:3], aug_pts[:, 3]
    		
    		# 将pts_rect投影到cam2的图像坐标系,pts_imgs为(u,v)坐标
            pts_img, pts_rect_depth = calib.rect_to_img(pts_rect)
            # 将pts_imgs在图像外的去掉,将pts_rect在给定边界外的去掉,得到保留点的flag。图像的外的点通过(u,v)不在图像内获得,pts_rect的点根据cfg获得,x∈[-40,40],y∈[-1,3],z∈[0,70.1]
            pts_valid_flag = self.get_valid_flag(pts_rect, pts_img, pts_rect_depth, img_shape)
    
            pts_rect = pts_rect[pts_valid_flag][:, 0:3]
            pts_intensity = pts_intensity[pts_valid_flag]
    
            if cfg.GT_AUG_ENABLED and self.mode == 'TRAIN':
                # all labels for checking overlapping
                # 去掉是‘DonotCare’的obj
                all_gt_obj_list = self.filtrate_dc_objects(self.get_label(sample_id))
                all_gt_boxes3d = kitti_utils.objs_to_boxes3d(all_gt_obj_list)  # Nx7 (x,y,z,h,w,l,ry)
    
                gt_aug_flag = False
                if np.random.rand() < cfg.GT_AUG_APPLY_PROB:
                    # augment one scene
                    # 添加其他场景中的obj到这个场景。
                    # gt_aug_flag是True代表加入了新的obj
                    # pts_rect, pts_intensity都是加入了新的点之后的点云和强度(if gt_aug_flag)
                    # extra_gt_boxes3d, extra_gt_obj_list是新加入的(if gt_aug_flag)
                    gt_aug_flag, pts_rect, pts_intensity, extra_gt_boxes3d, extra_gt_obj_list = \
                        self.apply_gt_aug_to_one_scene(sample_id, pts_rect, pts_intensity, all_gt_boxes3d)
    
            # generate inputs
            # 将点降采样或者补充成16384个
            if self.mode == 'TRAIN' or self.random_select:
                if self.npoints < len(pts_rect):
                    pts_depth = pts_rect[:, 2]
                    pts_near_flag = pts_depth < 40.0
                    far_idxs_choice = np.where(pts_near_flag == 0)[0]
                    near_idxs = np.where(pts_near_flag == 1)[0]
                    near_idxs_choice = np.random.choice(near_idxs, self.npoints - len(far_idxs_choice), replace=False)
    
                    choice = np.concatenate((near_idxs_choice, far_idxs_choice), axis=0) \
                        if len(far_idxs_choice) > 0 else near_idxs_choice
                    np.random.shuffle(choice)
                else:
                    choice = np.arange(0, len(pts_rect), dtype=np.int32)
                    if self.npoints > len(pts_rect):
                        extra_choice = np.random.choice(choice, self.npoints - len(pts_rect), replace=False)
                        choice = np.concatenate((choice, extra_choice), axis=0)
                    np.random.shuffle(choice)
    
                ret_pts_rect = pts_rect[choice, :]
                ret_pts_intensity = pts_intensity[choice] - 0.5  # translate intensity to [-0.5, 0.5]
            else:
                ret_pts_rect = pts_rect
                ret_pts_intensity = pts_intensity - 0.5
    
            pts_features = [ret_pts_intensity.reshape(-1, 1)]
            ret_pts_features = np.concatenate(pts_features, axis=1) if pts_features.__len__() > 1 else pts_features[0]
    
            sample_info = {'sample_id': sample_id, 'random_select': self.random_select}
    
            if self.mode == 'TEST':
                if cfg.RPN.USE_INTENSITY:
                    pts_input = np.concatenate((ret_pts_rect, ret_pts_features), axis=1)  # (N, C)
                else:
                    pts_input = ret_pts_rect
                sample_info['pts_input'] = pts_input
                sample_info['pts_rect'] = ret_pts_rect
                sample_info['pts_features'] = ret_pts_features
                return sample_info
    
            gt_obj_list = self.filtrate_objects(self.get_label(sample_id))
            if cfg.GT_AUG_ENABLED and self.mode == 'TRAIN' and gt_aug_flag:
                gt_obj_list.extend(extra_gt_obj_list)
            gt_boxes3d = kitti_utils.objs_to_boxes3d(gt_obj_list)
    
            gt_alpha = np.zeros((gt_obj_list.__len__()), dtype=np.float32)
            for k, obj in enumerate(gt_obj_list):
                gt_alpha[k] = obj.alpha
    
            # data augmentation
            aug_pts_rect = ret_pts_rect.copy()
            aug_gt_boxes3d = gt_boxes3d.copy()
            if cfg.AUG_DATA and self.mode == 'TRAIN':
            	# rotation,scale,flip
                aug_pts_rect, aug_gt_boxes3d, aug_method = self.data_augmentation(aug_pts_rect, aug_gt_boxes3d, gt_alpha,
                                                                                  sample_id)
                sample_info['aug_method'] = aug_method
    
            # prepare input
            if cfg.RPN.USE_INTENSITY:
                pts_input = np.concatenate((aug_pts_rect, ret_pts_features), axis=1)  # (N, C)
            else:
                pts_input = aug_pts_rect
    
            if cfg.RPN.FIXED:
                sample_info['pts_input'] = pts_input
                sample_info['pts_rect'] = aug_pts_rect
                sample_info['pts_features'] = ret_pts_features
                sample_info['gt_boxes3d'] = aug_gt_boxes3d
                return sample_info
    
            # generate training labels
            rpn_cls_label, rpn_reg_label = self.generate_rpn_training_labels(aug_pts_rect, aug_gt_boxes3d)
            sample_info['pts_input'] = pts_input
            sample_info['pts_rect'] = aug_pts_rect
            sample_info['pts_features'] = ret_pts_features
            sample_info['rpn_cls_label'] = rpn_cls_label
            sample_info['rpn_reg_label'] = rpn_reg_label
            sample_info['gt_boxes3d'] = aug_gt_boxes3d
            return sample_info
    

    PointRCNN

    PointRCNN是CVPR2019中3D目标检测的文章。3D目标检测是一个计算机视觉中比较新的任务,其他的文献综述可以参考我的另外一篇博客3D Object Detection 3D目标检测综述
    该文章使用two-stage方式,利用PointNet++作为主干网络,先完成segmentation任务,判断每个三维点的label。对分为前景的每个点,使用feature生成框。然后对框进行roi crop,进行框的优化。该论文网络复杂,代码量巨大,真是佩服论文作者的代码功底,自愧不如。本文着重对网络结构的理解。代码来源是文章作者给出的代码,用的是pytorch,github传送门
    接下来,我将先对运算过程进行可视化,然后再列出部分本篇论文我注意到的点。

    PointRCNN网络结构

    由于PointRCNN使用PointNet++作为主干网络,所以对PointNet++的具体网络结构不是很了解的同学可以参考我的另一篇博客PointNet++具体实现详解,其中也是着重对网络结构的理解。先看PointRCNN的网络结构的可视化:

    在这里插入图片描述

    图1 RPN结构

    在这里插入图片描述

    图2 RCNN.ProposalTargetLayer结构

    在这里插入图片描述

    图3 RCNN的分类和回归部分


    • 图的解释
      • 虚线大框:一个虚线框代表一个完整的子网络,对应代码中的一个class
      • 红色小框:每个子网络的名称
      • 蓝色小块:大多数一个蓝色小块代表一个tensor,蓝色小框的第一行为tensor的名称,第二行为tensor的尺寸。少量未标注尺寸的为一个子网络
      • 橘色小块:一个子网络的输出
      • 箭头:一种操作,没有标的大部分为resize或者permutation操作,也有concatenate操作

    • RPN
      • RPN.BackBone
        • 输入:点云(batch,number of points,number of channels)
        • 输出:xyz,每个点的 feature,每个点的分类结果 rpn_cls,每个点对框的回归结果 rpn_reg
        • 三维点云xyz经过主干网络得到point-wise的特征feature
        • feature经过分类头和回归头得到point-wise的分类结果rpn_cls和回归结果rpn_reg,分类头和回归头由Conv1d组成
      • cls_rpn经过sigmoid变换到[0, 1]之间,表示该点为车的score,score大于阈值thres的点被认为是属于车的点,从而构造seg_mask,用于构造RCNN的输入
      • 通过每个点的三维信息xyz计算点距离原点的深度信息depth,用于RCNN的输入
      • RPN.ProposalLayer
        • 输入:rpn_reg,rpn_cls
        • 输出:roi
        • 将rpn_reg分解,并与三维点xyz和anchor计算proposals
        • 使用Distance Proposal 减小proposal的数量。Distance Proposal:
          • 用雷达点的y坐标以40为界分为两块区域,[0, 40] 和 [40, 80]
          • 按照rpn_cls(代表是box的置信度)进行排序,[0, 40]的区域选取6300个框,[40, 80]选取2700个框
          • 将框转为BEV,然后使用nms,两个区域分别选取前358和154个框(nms后如果框的数量少于这两个值就全部选取,用0补足到512个框)
      • 输出每个batch的512个框roi

    • RCNN
      • RCNN.ProposalTargetLayer
        • 输入:roi,gt_boxes,xyz,seg_mask,depth,feature
        • 输出:采样过后的roi,roi_iou,对应的roi_gt_boxes,roi中包括的pts_sample和feature_sample,batch_cls_mask,reg_valid_mask
        • 使用RoISample再次采样RoI,RoISample:
          • 计算所有roi与真值之间的IoU,并按照IoU分为fg(前景),easy bg(简单背景)和 hard bg(困难背景),中sample数64个,fg最多32个,剩余的由bg补充,其中hard bg占比0.8。
          • 然后对roi做augmentation,更新roi和对应的IoU
        • 将xyz,seg_mask,depth和feature进行concatenate,得到pts_feature
        • 对pts_feature进行RoIPooling,每个RoI中取512个点,得到pooled_feature,并得到不包含点的RoI的flag
        • 将pooled_feature中的坐标和feature分离,然后做roi的augmentation,并将坐标系转到roi中心,更新roi中点的三维坐标和gt_box的坐标
        • 计算batch_cls_mask,reg_valid_mask用于计算loss,batch_cls_mask统计roi不为hard bg且其中包含点的mask,作为cls_label在计算loss中使用;reg_valid_mask统计roi属于fg的mask
      • pts_sample和feature_sample重组,提取直接由三维点云获得的信息xyz_feature(包括xyz,seg_mask和depth),然后使用xyz_up_layer进行特征提取,与rpn得到的feature进行concatenate,然后使用merged_down_layer进行merge,得到merged_feature
      • 将merged_feature送入3个PointNet++中提出的SA层中,得到高级特征
      • 然后使用分类头和回归头进行预测

    训练过程

    PointRCNN是two-stage结构的网络,所以训练过程也是先训练RPN,再训练RCNN。

    • RPN
      • label:在通过dataloader构建训练数据的同时,构建label
        • cls_label:将gt_box内的点置1,gt_box之外extended_gt_box之内的点置-1(表示忽略)
        • reg_label:计算gt_box之内的点的reg量
      • loss:SigmoidFocalLoss + Full-bin Loss(CrossEntropyLoss + SmoothL1Loss)
    • RCNN
      • label:
        • cls_label:在RCNN.ProposalTargetLayer中的batch_cls_mask为label
        • reg_label:使用RCNN.ProposalTargetLayer中的roi_gt_boxes计算
      • loss:SigmoidFocalLoss + Full-bin Loss(CrossEntropyLoss + SmoothL1Loss)

    思考

    • 文章使用two-stage的方法,在proposal的过程中,每个个三维点都回归一个proposal,使得理论上所有的box都能够被找到
    • 文章使用bin based回归方式,而且是在所有回归的地方都使用bin based的方式,提高了网络的收敛速度和准确率。
    • 文章使用PointNet++作为主干框架,使得不需要在体素化阶段损失信息
    • 其他3D物体检测的文章可以参考我的另一篇博客另外一篇博客3D Object Detection 3D目标检测综述
    展开全文
  • Caffe代码阅读

    千次阅读 2016-08-19 10:06:15
    Caffe代码阅读——层次结构 - 无痛的机器学习 - 知乎专栏 https://zhuanlan.zhihu.com/p/21796890 Caffe源码阅读——Net组装 - 无痛的机器学习 - 知乎专栏 https://zhuanlan.zhihu.com/p/21875025 Caffe代码...

    转载自:

    Caffe代码阅读——层次结构 - 无痛的机器学习 - 知乎专栏  https://zhuanlan.zhihu.com/p/21796890

    Caffe源码阅读——Net组装 - 无痛的机器学习 - 知乎专栏  https://zhuanlan.zhihu.com/p/21875025

    Caffe代码阅读——Solver - 无痛的机器学习 - 知乎专栏  https://zhuanlan.zhihu.com/p/21800004

    1.Caffe代码阅读——层次结构

    作者:冯超
    链接:https://zhuanlan.zhihu.com/p/21796890
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    Caffe是一款优秀的深度神经网络的开源软件,下面我们来聊聊它的源代码以及它的实现。Caffe的代码整体上可读性很好,架构比较清晰,阅读代码并不算是一件很困难的事情。不过在阅读代码之前还是要回答两个问题:
    1. 阅读代码是为了什么?
    2. 阅读到什么程度?(这个问题实际上和前面的问题相关)

    阅读代码大体上来说有下面几个目的:

    1. 搞清楚代码所实现的算法或者功能。对算法本身不是很了解,希望通过阅读代码了解算法。
    2. 搞清楚代码在实现算法过程中的细节。这种情况下,一般对算法已经有大概的了解,读代码是为了了解代码中对算法细节的考量。当然,如果想使用代码,了解代码细节是很有帮助的。
    3. 扩展代码。在开源代码的基础上,利用已有的框架,增加或者修改功能,来实现自己想要的功能。这个就需要对代码的架构细节有更加深入的了解。

    我们的目标是扩展代码。Caffe中主要的扩展点就是Layer和Solver,当然其他的部分也可以扩展,只不过要改动的代码会多一些。

    当确定了上面第一个问题,下面就是第二个问题了。读代码要读到什么程度?一般来说,我觉得阅读代码这件事情可以用一个Logistic型的函数来表示:

    这个图上,横轴是阅读代码花费的时间,纵轴是阅读代码带来的效果。对于代码量比较大的项目,一开始阅读肯定是蒙的,需要花一定的时间梳理清楚各个文件,各个模块之间的关系。随着结构关系逐渐清晰,读者开始领会代码中所表达的含义,阅读代码的效果直线上升。然而当我们把代码主线和重要支线弄懂后,再读一些小支线的收益就不会太大。所以根据阅读代码的性价比和Caffe代码自身的特点,我们只会将主线和一些重要支线阅读完,估计也就是整体代码量的一半。

    Caffe代码的主线结构抽象

    不同于其他的一些框架,Caffe没有采用符号计算的模式进行编写,整体上的架构以系统级的抽象为主。所谓的抽象,就是逐层地封装一些细节问题,让上层的代码变得更加清晰。那么就让我们来顺着Caffe的抽象层级看看Caffe的主线结构:

    SyncedMem:这个类的主要功能是封装CPU和GPU的数据交互操作。一般来说,数据的流动形式都是:硬盘->CPU内存->GPU内存->CPU内存->(硬盘),所以在写代码的过程中经常会写CPU/GPU之间数据传输的代码,同时还要维护CPU和GPU两个处理端的内存指针。这些事情处理起来不会很难,但是会很繁琐。因此SyncedMem的出现就是把CPU/GPU的数据传输操作封装起来,只需要调用简单的接口就可以获得两个处理端同步后的数据。

    Blob:这个类做了两个封装:一个是操作数据的封装。在这里使用Blob,我们可以操纵高维的数据,可以快速访问其中的数据,变换数据的维度等等;另一个是对原始数据和更新量的封装。每一个Blob中都有data和diff两个数据指针,data用于存储原始数据,diff用于存储反。向传播的梯度更新值。Blob使用了SyncedMem,这样也得到了不同处理端访问的便利。这样Blob就基本实现了整个Caffe数据部分结构的封装,在Net类中可以看到所有的前后向数据和参数都用Blob来表示就足够了。

    数据的抽象到这个就可以了,接下来是层级的抽象。前面我们也分析过,神经网络的前后向计算可以做到层与层之间完全独立,那么每个层只要依照一定的接口规则实现,就可以确保整个网络的正确性。

    Layer:Caffe实现了一个基础的层级类Layer,对于一些特殊种类还会有自己的抽象类(比如base_conv_layer),这些类主要采用了模板的设计模式(Template),也就是说一些必须的代码在基类写好,一些具体的内容在子类中实现。比方说在Layer的Setup中,函数中包括Setup的几个步骤,其中的一些步骤由基类完成,一些步骤由子类完成。还有十分重要的Forward和Backward,基类实现了其中需要的一些逻辑,但是真正的运算部分则交给了子类。这样当我们需要实现一个新的层时,我们不需要管理琐碎的事物,只要关系好层的初始化和前后向即可。

    Net:Net将数据和层组合起来做进一步的封装,对外暴露了初始化和前后向的接口,使得整体看上去和一个层的功能类似,但内部的组合可以是多种多样。同时值得一提的是,每一层的输入输出数据统一保存在Net中,同时每个层内的参数指针也保存在Net中,不同的层可以通过WeightShare共享相同的参数,所以我们可以通过配置实现多个神经网络层之间共享参数的功能,这也增强了我们对网络结构的想象力。

    Solver:有了Net我们实际上就可以进行网络的前向后向计算了,但是关于网络的学习训练的功能还有些缺乏,于是在此之上,Solver类进一步封装了训练和预测相关的一些功能。与此同时,它还开放了两类接口:一个是更新参数的接口,继承Solver可以实现不同的参数更新方法,如大家喜闻乐见的Momentum,Nesterov,Adagrad等。这样使得不同的优化算法能够应用其中。另外一个是训练过程中每一轮特定状态下的可注入的一些回调函数,在代码中这个回调点的直接使用者就是多卡训练算法。

    IO:有了上面的东西就够了?还不够,我们还需要输入数据和参数,正所谓巧妇难为无米之炊,没有数据都是白搭。DataReaderDataTransformer帮助准备输入数据,Filler对参数进行初始化。一些Snapshot方法帮助模型的持久化,这样模型和数据的IO问题也解决了。

    多卡:对于单GPU训练来说,基本的层次关系到这里也就结束了,如果要进行多GPU训练,那么上层还会有InternalThreadP2PSync两个类,这两个类属于最上层的类了,而他们所调用的也只有Solver和一些参数类。

    其实到这里,Caffe的主线也就基本走完了。我们可以画一张图把Caffe的整体层次关系展示出来:

    如果对这张图和图中的一些细节比较清楚的话,那么你对Caffe的了解应该已经不错了。后面关于Caffe源码分析的文章就可以不看了。如果没有,那么我们还是可以继续关注一下。当然如果想真正理解这张图中所表达的含义,还是要真正地读一下代码,去理解一些细节。但是有些细节这里就不做详细的分析了,下一回我们会站在Layer的角度去看一个Layer在训练过程的全部经历。


    2.

    Caffe源码阅读——Net组装

    最近忙着看TI没有及时写文章,今天赶紧补一篇……

    Net是Caffe代码中一个比较核心的类,往下看它封装了所有的Layer,构建起了整个神经网络;往上看它对外提供了前向后向计算,以及核心数据结构的访问结构,使得再上层的Solver可以利用Net比较轻松地实现Train和Test的策略。当然,正是因为它的重要性,组装Net是一个比较复杂的部分。这一回我们就来看看Net的内容。

    当然,说在前面,看Net组装的代码有两个目的:

    1. 了解作为一个成熟的CNN模型框架需要考虑的一些问题;
    2. 如果想对网络结构做扩展,如写一个新的Layer,其中的一些数据是如何在Layer和Net之间流动的

    首先,为了使问题不那么复杂,我们先从训练模型时输出的log看看Net组装的几个关键步骤,然后再把这个过程慢慢展开,了解组装的所有细节。

    Log眼中的Net组装

    为了更好地展示Net组装的一些细节,我们在这里选取了一个实际例子,就是Caffe的examples里面的siamese model。关于这个model的细节这里就不多说了,感兴趣的可以去看官方或者非官方的文档,这里只提一点:这个网络除了包含其他正常网络中的一些特性之外,还具有网络参数复用的特点,在后面的分析中我们会用到。

    下面我们要看的就是Net组装的Log。这段Log一般都是大家在训练网络时一闪而过的大段Log,当然如果它没有一闪而过而是停下来了,有可能是你的网络定义有问题爆出了错误。这段Log内容比较多,总体来说就是Train阶段和Test阶段的两个网络组装起来。我们重点关注其中的几个片段,来大概了解Net组装的一些核心内容,也是那些比较值得打印出来的内容。

    首先是一个正常的卷积层conv1,Log如下所示(以下代码的行号可能会有不同,但位置是相近的):

    layer_factory.hpp:77] Creating layer conv1
    net.cpp:92] Creating Layer conv1
    net.cpp:428] conv1 <- data
    net.cpp:402] conv1 -> conv1
    net.cpp:144] Setting up conv1
    net.cpp:151] Top shape: 64 20 24 24 (737280)
    net.cpp:159] Memory required for data: 3752192
    

    这其中第一行是创建这个Layer实例的代码,具体的创建过程在layer_factory里面。为了方便创建Layer,Caffe采用了工厂方法的设计模式,只要提供Layer的名字(在配置文件中参数叫type),就可以根据名字和对应参数实例化一个Layer。这部分的细节只要认真看一下就会明白。

    第3,4行显示了创建当前层的bottom和top数据的过程。这里涉及到net.cpp中的AppendBottom和AppendTop两个方法,因为每一个bottom blob和top blob都有名字,这里就将他们之间的关系输出在了这里。

    第5行看上去没什么干货,但是它代表了Layer的Setup函数已经调用完成(或者Layer被share)。Layer的Setup函数是Layer初始化的关键函数,这里面涉及到以下几个具体的操作:

    CheckBlobCounts(bottom, top);
    LayerSetUp(bottom, top);
    Reshape(bottom, top);
    SetLossWeights(top);
    

    总结地说,这四句完成了:

    1. 对bottom blob, top blob数量的检查,父类实现。
    2. 对Layer内部相关变量的初始化,由具体的子类实现
    3. 传入时bottom blob的维度已经确定,Layer需要根据自己要做的计算确定top blob的纬度。比方说这一层是卷积层,维度是20*5*5,输入图像是1*28*28,也就是bottom blob的维度,那么输入就是20*24*24,这也是上面log里面算出的结果,只不过还加了一个batch size。这个函数由具体的子类实现。
    4. 对Layer是否输出loss以及输出loss要做的操作进行初始化。父类实现。必须说一句,Caffe中关于Loss Layer中Loss_weight,loss_,top.cpu_diff的数据设定还是有点绕且有点trick的。

    好了回到上面的log。接下来的那一句告诉了我们top层应该输出的维度。这里输出了维度就是为了让不放心的朋友算一下,看看和你想的是否一样。当然,输出这句log的循环不是只做了这件事,它的主要工作就是设置top blob的loss_weight。

    最后一句计算了该层top blob所占用的内存。可以看出截至到这一层,内存消耗大约是3M多,还不算大。

    好,这就是一个最典型的Layer的初始化,下面这个ReLU层就稍微有些不同了:

    layer_factory.hpp:77] Creating layer relu1
    net.cpp:92] Creating Layer relu1
    net.cpp:428] relu1 <- ip1
    net.cpp:389] relu1 -> ip1 (in-place)
    net.cpp:144] Setting up relu1
    net.cpp:151] Top shape: 64 500 (32000)
    net.cpp:159] Memory required for data: 5769472
    

    这里面最不同的就是第4行结尾的(in-place),这说明relu的bottom blob和top blob是同一个数据,这和我们在网络中的定义是一样的。in-place的好处就是减少内存的操作,但是这里在统计内存消耗时并没有考虑in-place带来的节省。

    接下来就是共享网络的conv1_p了:

    layer_factory.hpp:77] Creating layer conv1_p
    net.cpp:92] Creating Layer conv1_p
    net.cpp:428] conv1_p <- data_p
    net.cpp:402] conv1_p -> conv1_p
    net.cpp:144] Setting up conv1_p
    net.cpp:151] Top shape: 64 20 24 24 (737280)
    net.cpp:159] Memory required for data: 8721664
    net.cpp:488] Sharing parameters 'conv1_w' owned by layer 'conv1', param index 0
    net.cpp:488] Sharing parameters 'conv1_b' owned by layer 'conv1', param index 1
    

    这一段最有特点的是最后两句“Sharing”,因为siamese model中拥有参数完全相同的两个网络,所以在构建时候,第二个网络检测到参数名字已经存在,说明该层的参数和其他层共享,于是在这里打印出来告诉用户这一点。当然,这一句之前没有打印出来的内容告诉了我们,实际上Net类中还负责了参数相关的初始化。这部分的内容实际上还挺多,除了参数共享,还有对参数learning rate,weight decay的设定。

    最后是最特别的一层:loss层

    net.cpp:92] Creating Layer loss
    net.cpp:428] loss <- feat
    net.cpp:428] loss <- feat_p
    net.cpp:428] loss <- sim
    net.cpp:402] loss -> loss
    net.cpp:144] Setting up loss
    net.cpp:151] Top shape: (1)
    net.cpp:154]     with loss weight 1
    net.cpp:159] Memory required for data: 10742020
    

    这一层看上去没有什么特别,该有的和前面一样,但是唯一不同的就是它的倒数第二行,这说明这一层是有loss weight的。至于有loss weight有什么用,以后我们会详细说这个事情。这里简单说一下,有loss weight表示这个blob会被用于计算loss。

    前面的log主要解决了网络的组装和前向的一些计算,从log中,我们可以看出Net完成了以下的事情:

    1. 实例化Layer
    2. 创建bottom blob,top blob
    3. Setup Layer(初始化Layer,确定top blob维度)
    4. 确定layer的loss_weight
    5. 确定layer的参数是否共享,不共享则创建新的

    从上面的过程也可以看出,整个网络中所有的流动性变量(bottom blob,top blob)都保存在Net中,同时对于各层的参数,根据各层的共享关系做了标记。这样好处是集中管理了网络中的数据,方便对数据进行操作。

    再往下面,我们可以截取一小段log来:

    net.cpp:220] pool1 needs backward computation.
    net.cpp:220] conv1 needs backward computation.
    net.cpp:222] slice_pair does not need backward computation.
    net.cpp:222] pair_data does not need backward computation.
    net.cpp:264] This network produces output loss
    net.cpp:277] Network initialization done.
    

    接下来是统计一个层次是否需要进行反向传播的计算。一般来说我们的层是都需要计算的,但是也会有一些层不需要计算,比方说数据层,就像上面的log那样,还有就是一些希望固定的层,这个一般在finetune网络的时候用的上。因为反向计算一般比前向计算慢,如果有不需要计算的Layer,直接跳过计算是可以节省时间的。

    最后是整个网络产生的输出,这个输出会在训练迭代中显示出来的。

    了解了这些,我们就对Net装载有了大概的了解,再去看它的代码就会轻松些。

    最后,关于Net类中所有的成员变量与它们之间的关系,我们可以用下面的一张图来理解就好:

    把Net的初始化理解后,其实Net以下的架构方面的问题就不多了。下面我再看看Net以上的东西,Solver以及Caffe里“简单”的多卡训练。

    3.

    Caffe代码阅读——Solver

    前面我们聊了Net组装的内容,接下来我们来看看Solver的内容。Solver主体有两部分:初始化和训练。初始化内容相对比较简单,这里就不说了;下面我们来说说训练中的几个关键函数。

    核心函数:Step

    真正的训练在Step函数内,这里有多卡训练的关键回调函数:on_start()和on_gradient_ready(),具体的调用方法我们后面再说,在这两个回调函数中间有两个重要的过程:ForwardBackward和UpdateSmoothedLoss。在on_gradient_ready之后有一个关键函数ApplyUpdate(),这里面的代码在Sgd_solver中。下面我们详细看一下。

    ForwardBackward

    这里主要调用了Net中的代码,主要完成了前向后向的计算,前向用于计算模型的最终输出和Loss,后向用于计算每一层网络和参数的梯度。对于前向后向的具体内容这里需要详细叙述了,唯一值得一提的是前向的Loss计算,这部分代码实际上实在Layer里面,具体涉及到loss_weight这个参数相关的初始化和loss()的判断,同时还有Loss_Layer在Setup函数中的初始化。

    UpdateSmoothedLoss

    这个函数主要做Loss的平滑。由于Caffe的训练方式是SGD,我们无法把所有的数据同时放入模型进行训练,那么部分数据产生的Loss就可能会和全样本的平均Loss不同,在必要时候将Loss和历史过程中更新的Loss求平均就可以减少Loss的震荡问题。代码中的平滑方法比较简单,大家一看便知。

    下面就是ApplyUpdate函数,这个函数真正完成了参数更新的任务。Caffe的参数更新只利用了模型的梯度信息,没有利用二阶信息。下面就详细介绍下更新参数的几个过程:

    • GetLearningRate
    • ClipGradients
    • Normalize
    • Regularize
    • ComputeUpdateValue

    GetLearningRate

    learning rate的故事我们前面已经聊过了,在CNN训练中这确实是个大问题。Caffe为了让learning rate的设计更灵活,提供了一系列的learning rate方案:

    • fixed:lr永远不变
    • steplr=baselr*gamma^{iter / stepsize}
    • explr=baselr*gamma^{iter}
    • invlr=baselr*(1+gamma*iter)^{-power}
    • multistep:直接写iter在某个范围内时lr应该是多少
    • polylr=baselr*(1-\frac{iter}{maxiter})^{power}
    • sigmoidlr=baselr*\frac{1}{1+e^{-gamma*(iter-stepsize)}}

    这些方案各有优劣,选择自己顺手的就好。

    ClipGradients

    这一步主要是对梯度值做一个限制,如果梯度值过大,那么这里就会对梯度做一个修剪,对所有的参数乘以一个缩放因子,使得所有参数的平方和不超过参数中设定的梯度总值。这个功能感觉上像是对全局函数设置了一个Trust Region,可以防止更新的量过大二导致梯度发散。我认为这一步的想法是很好的,但是实际操作中可能会有问题。实际中可能只有部分参数的梯度比较大,而其他参数的梯度本身比较小,那么对所有的参数乘以相同的因子会让一些本来比较小的参数变得更小,这样会带来一些不公平。

    Normalize

    这一步同样考虑了一些单一Batch不足以完成训练的问题,通过限制每个Batch的更新量来控制更新总量,代码比较简单。

    Regularize

    到这一步终于要计算正则项的梯度了。Caffe提供两种正则方法——L2和L1,其中L2采用了标准的梯度下降方法,L1采用了sub-gradient的计算方法。L2的优化计算比较简单,没有什么好说的,但是L1的计算还是有点值得玩味的地方的。这里采用的sub-gradient方法其实本身没有什么问题,不过Lasso的优化还可以有其他的方法,这个问题以后可以再细聊。

    ComputeUpdateValue

    到这里,我们终于来到了梯度计算的最后一站,这时候我们终于完成了对梯度的计算,下面该考虑lr和梯度结合起来如何计算最终的梯度优化值了。sgd方法主要采用momentum加梯度的优化方法。关于momentum的优势我们前面已经聊过了。除此之外,Caffe还提供了一系列的梯度计算方法,这些优化方法各有特点,以后我们可以慢慢来看。

    当计算完这一步,我们就可以调用Blob中的Update把每个参数的data和diff进行相加,计算出最终的结果。这样,整个优化过程就完成了。至于剩下的一些内容都不是核心过程,就略去不看了。

    如果我们采用单卡训练的策略,那么阅读代码到这里也差不多了。不过多卡训练对于大规模的训练任务来说是必不可少的,所以我们接下来趁热打铁地看看Caffe的多卡训练。

    多卡训练算法

    Caffe的多卡训练算法总体思路是数据并行,我们用不同的GPU处理不同的数据,然后将所有的梯度更新汇总。由于Solver在训练中给了两个回调函数,多卡训练也主要利用了这两个回调函数进行:

    1. on_start():将参数拷贝到每一个GPU中。
    2. ForwardBackward():每个GPU各自计算自己的前向后向结果。
    3. on_gradient_ready():将反向梯度汇总到一起。
    4. ApplyUpdate():在汇总的线程上进行参数更新

    其中第2步由每一个CPU线程和自己的GPU并行完成,第4步由汇总的CPU和自己的GPU完成,剩下的1,3两步主要是完成数据传输的任务,也是多卡计算中主要完成的部分。

    Caffe采用树型结构进行参数传递,其中一个CPU线程和GPU作为树型结构的根,其他的则作为根下面的节点。为了更快地传输GPU数据,树型结构的构建要考虑GPU之间是否相近,比方说两个GPU之间是否可以进行P2P的直传。在前面的翻译博客中我们已经聊过GPU之间数据传输的问题了,这里的树形结构也主要以此做考虑。

    我们假设4块GPU的拓扑结构如下:


    nvidia-smi topo -m
           GPU0   GPU1   GPU2   GPU3  
    GPU0   X     PHB    SOC    SOC    
    GPU1   PHB    X     SOC    SOC    
    GPU2   SOC    SOC    X     PHB   
    GPU3   SOC    SOC    PHB    X     
    

    那么我们构造出的树型结构如下所示,数据传输也是按照这样的结构传输:

    这样1,3的数据传递就解决了,具体的过程请详细阅读代码,这里就不叙述了。

    对Caffe代码的基本介绍就到这里了,我们对代码的整体结构有了比较清晰的认识,下面我们将分析模型中各个部分的特性。



    展开全文
  • 代码阅读工具学习总结

    万次阅读 2016-04-15 12:25:28
    代码阅读工具:Source Navigator和Source Insight 一、Source Insight实用技巧: Source Insight(下文的SI指的也是它)就是这样的一个东西:  Windows下开发人员的至爱,功能强大,界面友好。支持语法高亮、...

    代码阅读工具:Source Navigator和Source Insight



    一、Source Insight实用技巧:
    Source Insight(下文的SI指的也是它)就是这样的一个东西:
      Windows下开发人员的至爱,功能强大,界面友好。支持语法高亮、符号跳转,还支持函数调用关系


    图显示。这是一个专业的编程环境,很多大公司都是使用它的。这个编辑器除支持完善的自定义功能外


    ,几乎所有配置都能作个性化配置。除此之外,它本身带有一套功能强大的宏语言(Macro Language),


    借助这种语言可以很方便的编程实现各种特殊功能,非一般编辑器所能比拟。查找功能支持得也很不错


    。虽然在工程较大时,查找起来有点慢。但是它除了一般编辑器所支持的普通字符串查找、正则表达式


    查找外,还能支持关键字查找-这种方式比前两者快得多,也算是一个弥补。不足之处是列模式较弱,对


    中文支持有缺陷。


    显然,它也像其它任何东西一样的,同时具有优点和缺点的本性的,究竟如何,且听我一一道来。


    官网下载:http://www.sourceinsight.com/down35.html


    序列号(Serial):(任选其一)
    SI3US-279028-11281
    SI3US-772862-51931
    SI3US-465643-84290
    SI3US-176526-66007
    SI3US-060062-28251


    1  开胃菜-初级应用


    1.1  选择美丽的界面享受工作


    虽然不能以貌取人,但似乎从来没有人责备以貌取软件的。SI的华丽界面,绝对符合现代花花世界的人


    的审美趣味。在SI中,我们可以轻松地把各种类型关键字、变量、标志符、函数、宏、注释等定义为不


    同的颜色和显示方式(正体或斜体、加粗或正常、加下划线、放大显示等),总有一种方式能让我们一眼


    就能分辨出这个标识是什么。


    1.1.1  字体选择


    在SI中样式是可以被继承,如果要从根本上改变字体,最简单的方式就是直接修改根样式中的字体,因


    为其它样式都会由此继承而来。选择Options/Document Options页面内的Font Options中的Screen 


    Fonts字体,即可改变根样式中的字体。SI中的默认配置为Verdana字体,是一种非等宽字体 2 ,为了使


    编写的代码在各种编辑器中看起来都有良好的对齐效果,这里强烈建议使用等宽字体,Courier、New 


    Courier和宋体等都是较好的选择。


    1.1.2  颜色定义


    毕竟这是见仁见智的东西,所以从来没有统一的标准3。很多人并不喜欢SI提供的默认配置,那么我们就


    改吧。选择Options/Style Properties页面,就可以在其中修改所有样式了。选择等号(=)表示继承


    Parent Style,也可以选择Pick(或者ON/OFF等)去配置一个新值。这完全视乎个人喜好。


    1.1.3  标识符样式选择


    在与 颜色定义 一节同样的界面内即可完成此项配置。


    1.1.4  背景色选择


    在希望要改变背景色的窗口点击鼠标右键(假定使用的是右手鼠标 4),选择上下文菜单的 xxx Window 


    Properties项,然后点击弹出窗口的Back Color按钮,即可修改该窗口背景色。对于SI的源码主窗口,


    只需选择上下文菜单的Special Window Color项即可完成背景色修改。


    1.2  配置合理的默认值高效工作


    1.2.1  使用合理的缩进


    我始终认为最容易获得认同的是关于这个选项的配置了。选择Options/Document Options页面,点击其


    内的Auto Indent按钮,在弹出的Auto Indenting窗口中,默认配置为 Auto Indent Type选择Smart,且


    勾选了Smart Indent Options中的两个可选项,这样得到的默认缩进效果为


        while (1)
            {
            I
            }


    每次都要手工去调整其缩进,其实只要把两个勾选项去掉,就可以得到


        while (1)
        {
            I
        }


    何乐而不为呢?


    1.2.2  显示坐标


    通常情况下在窗口状态栏左下方,最会显示当前光标所在行列信息,但我总觉得不够明显,于是通常我


    们作如下配置:


    选择Options/Document Options页面,勾选其中的Show line numbers。同时勾选其中的Show right 


    margin,我们就可显示一条右边界,随时提醒我们是否该行代码写得过长了。


    1.3  创建便捷的快捷键快乐工作


    1.3.1  几个较常用的快捷键


    默认情况下,SI已经定义了很多非常实用的快捷键:


    F5
    指定行号,实现行跳转,在遇到编译错误的时候,能特别方便的找到出错行。
    Shift+F8
    高亮显示指定标识,快速浏览标识的使用情况。
    Ctrl+鼠标点击标识
    直接跳转至标识定义处。
    Ctrl+F
    本文件内查找。
    F3
    本文件查找结果的上一个。
    F4
    本文件查找结果的下一个。
    F7
    打开Browse Project Symbols窗口,快速浏览工程内标识定义。
    Ctrl+M
    创建或查找书签,方便下次找回此位置。
    1.3.2  自定义快捷健


    选择Options/Key Assignments,在弹出的Key Assignments窗口中可自由添加自己喜欢的快捷键。比较


    值得推荐的有如下几个快捷键定义:


    Edit: Drag Line Down
    光标当前行下移。
    Edit: Drag Line Up
    光标当前行下移。
    Edit: Join Lines
    当前行和下一行连接成一行。
    1.3.3  更多的快捷键


    如果你正好对SI的Marco语言(下文将会介绍)有研究,那么还可以定义更多有用的快捷键,比如添加文件


    头、函数头、注释等(下文在介绍Marco语言时会介绍如何实现)。


    2  小技巧-中级应用


    2.1  查找与替换


    在SI中支持多种查找及替换方式,除了上文提到的文件内查找外,还支持工程范围内查找、目录查找、


    指定多文件查找等等。


    2.1.1  查找


    Loopup References


    我们最常用的一种查找方式是选择Search/Lookup References或按Ctrl+/组合键再或者鼠标点  R 按钮


    ,在弹出的Loopup References窗口进行查找操作。


    在Search Method中有四种可选的查找方式:Simple String、Regular Expression、 Keyword 


    Expression和Look Up Reference。其中Simple String是最普通的查找方式,可以查找文件中出现的任


    意字符或字符,甚至可以查找 _upap || u 这样的字符串,但是在工程较大时,查找过程会较慢。


    Regular Expression查找方式将在后面讲述正则表达时会介绍到,这里暂时按下不表。


    Keyword Expression和Look Up Reference查找的结果基本相同,但是显示方式略有差异。这两种方式都


    是使用SI预先建立的数据库,查找起来速度相当快。但通常这种速度只对在查找标识符时有明显效果。


    对于像函数名,变量名等的查找,强烈建议使用这两种方式进行查找。


    Search Files
    选择Search/Search Files或按Ctrl+Shift+F组合键,在弹出的Search Files窗口进行查找操作。
    在File Name框中可以填入文件名或文件夹。注意当要查询的文件夹双包含子文件夹时,可以勾选


    Options中的Include Subdirectiories,实现对各层文件的递归搜索。
    Search Project
    选择Search/Search Project,在弹出的Search Project窗口进行查找操作。操作与Loopup References


    几乎完全一致,它们各自保存上次搜索的配置。
    2.1.2  替换


    单文件替换
    选择Search/Replace或按Ctrl+H组合键,在弹出的Replace窗口进行查找操作。在Search项目里勾选


    Selection则仅对当前选中的文档部分进行替换。另外如果勾选了Confirm Replacements则是逐个确认替


    换,否则会同时替换所有符合替换条件内容。
    多文件替换
    选择Search/Replace Files或按Ctrl+Shift+H组合键,在弹出的Replace Files 窗口进行查找操作。除


    了增加New框(替换后的内容)外,其余均与Search Files窗口相同,可参照查找部分的说明进行操作。
    2.2  列操作


    虽然开篇时就说过,SI的列操作功能比较弱,但不等于没有。先按下Alt键,接着就可用鼠标进行列选择


    ,然后就可以删除指定的列。


    2.3  无名技巧


    这里介绍一些小技巧,大多数情况下我们可以无视它们的存在。但如果我们知道这些,某些时候,会有


    效提高工作效率。


    Smart Rename
    在上下文件菜单中选Smart Rename或按Ctrl+'组合键,即可弹出Smart Rename窗口。它有很强大的功能


    ,但最便捷的使用方式是更改函数内局部变量的名字,操作只作用于函数内部,速度非常快。
    Renumber
    使用Ctrl+R将弹出Renumber窗口,这个用于处理数字顺序排列的情况相当有效,比如数组下标。例如现


    有代码
        array[0] = 1;
        array[1] = 2;
        array[2] = 3;


    现在要改为
        array[0] = 0;
        array[1] = 1;
        array[2] = 2;
        array[3] = 3;


    当然可以一个个修改,但最快的方式是在array[0] = 1;之前添加array[0] = 0;,然后列选数组下标,


    使用Renumber功能以 0为起始值重填数值。
    Edit Condition
    很多代码尤其是驱动代码,当中有大量的预编译定义,以实现对不同硬件配置的支持。在阅读这样的代


    码时最痛苦的是不能简单判断程序实际执行的代码分枝。大量分枝同时存在,常常会混淆我们的视听。


    比如对于下面的代码:
        #ifdef DEV1
            /* DEV1代码代码 */
        #else
            /* 其它设备执行代码 */
        #endif


    如果确定我们当前分析的是DEV1的执行情况 5,那么可以选择上下文件菜单的Edit Condition 选项,在


    弹出的Conditional Parsing窗口中把DEV1的值设置为True,那么 #ifdef DEV1就等价于#if 1了,相当


    注释掉了#else分枝的代码。反之,设置为Flase时,则注释掉#ifdef DEV1分枝的代码。
    3  学会偷懒-高级应用


    4  附录1-SI中正则表达式


    由于在查找及替换中,经常会使用用正则表达式6,这里对SI的正则表达式进行简单介绍。


    4.1  通配符


    正则表达式通配符总表:
    Character Matches
    ^ (在表达式开始处) 行的开始部分
    . 任意单个字符
    [abc] 任意属于集合 abc 的单个字符
    [^abc] 任意不属于集合 abc 的单个字符
    * 前面字符的0个或多个重复
    + 前面字符的1个或多个重复
    \t 一个 tab 字符
    \s 一个空格符
    \w 一个空白符(包括 tab 符和空格符)
    $ 行的结束部分
    4.2  表达式中的组


    在执行替换操作时,组将大有用武之地。正则表达式的各个部分可以用\(和\)进行分隔,分隔得到的每


    一项就是一个组。在进行替换时可通过组从匹配内容中抽取出特定串。在正则表达式中每个组都有一个


    编号,自左至右编号从1开始。


    例如:abc\(xyx\)将能匹配 abcxyz ,此时组1就包含了 xyz 串。在进行替换操作时,就可以通过在替


    换后内容框中填入\1来取出这个字符串。推而广之,可以使用\<number>来取得组<number>所包含的串。


    例如:当设定把\(abc\)\(xyz\)替换为\2\1的替换规则时,对于 abcxyz 被替换串,则组1包含 abc,组


    2包含 xyz,而替换后的内容定义为组2内容后跟组1内容(\2\1),因此将得到 xyzabc。


        举个真实的使用例子,相信会增加大家的兴趣。有时为方便调试,代码中到处流浪着各种形式的


    mytrace调用


        mytrace("Create parameter list... ");


     有时希望把它们全部注释掉,而有些时候又希望把它们全部恢复回来。这是个简单的例子,可以使用


        ^\(.*\)\(/\*\)\(.*mytrace.*\)\(\*/\)___FCKpd___6nbsp;==> \1\3


    把它们恢复回来,而使用


        ^\(.*\)\(mytrace\)\(.*\)___FCKpd___7nbsp;==> \1/*\2\3*/


    则完成把它们全部注释掉。


    5  附录2-SI中的宏语言


    我始终认为这是SI中最有趣的部分,这是一种功能强大的编程语言,几乎可以实现在编程过程可能使用


    到的各种功能。 这里不准备对如何实用宏语言进行编程作介绍(可参阅SI帮助文档。),只介绍如何使用


    已编好程序。为方便使用,我已把这些程序都集中放在utils.em文件中,下文就此文件进行论述。


    该宏文件实现了一些在编码过程中可能会用到的功能, 如添加文件头、函数说明(使用时能自动添加文件


    名、函数名和当前日期)和宏定义,代码补全等。 使用说明:


    (1) Project/Open Project...
    打开Base工程(该工程一般在"我的文档/Source Insight/Projects/Base"中);


    (2) Project/Add and Remove Project Files...
    加入宏文件(即utils.em);


    (3) Options/Menu Assignments
    打开Menu Assignments窗口,在Command中输入Macro,选中要使用的宏,添加到合适的菜单中.


    推荐使用的宏:InsFileHeader、InsFunHeader、InsHeaderDef、InsIfdef和AutoExpand (为代码自动补


    全功能,建议建快捷键)。


    关于AutoExpand的举例说明, 当你输入了 switch 且光标正处于switch后面,运行该宏则会得到


        switch (###)
        {
        case
            break;
        default:
        }
    对于InsFunHeader宏,如果有如下函数体


        int nOpenConfigFile(char *pchMemConfig, char *pchFlashConfig,
            int nSize, int nMode)
        {
            I
        }




    光标在函数体内时运行该宏,那么将会在函数体上方得到


    /******************************************************************************
     * nOpenConfigFile -
     * DESCRIPTION:-
     *
     * Input:   N/A
     * Output:  N/A
     * Returns: N/A
     *
     * modification history
     * --------------------
     * 1.00, Apr 19, 2007, T357 written.
     * --------------------
     ******************************************************************************/




    其中的函数名及编写日期自动按实际情况填充,T357串可通过修改utils.em文件,改成你需要的名字。


    6  附录3-推荐格式


    7  结束


    至此,已将我所知的所有关于Source Insight(未包括其附带的Macro语言)知识在此文档中描述出来。


    二、源代码查看工具 Source Navigator 使用心得
    Source Navigator 是Red Hat出品的一款查看源代码的工具,非常好用,与Windows下的Source Insight


    有一敌。但是它的界面不怎么好看,用的不是GTK图形库,所以界面风格与Gnome不一致,操作上也有些


    不同。除了这些,其它功能都非常强大,细数如下。里面的一些简写约定如下:


    Code Area


    cl Classes
    con Constants
    e Enums
    ec Enum Values
    fd Function Declarations
    fr friends
    fu Functions
    gv Global Variables
    iv Instance Variables
    ma Macros
    md Method Definitions
    mi Method Implementations
    t Typedefs
    un Unions
    lv Local variables
    ud Undefined


    Cross-Reference


    r Read
    w Written
    p Passed
    u Unused
    SNav的代码窗口有6个标签页,它们分别是
    “Edit”
    “Hierarchy”
    “Class”
    “Xref”
    “Retriever”
    “Grep”其中,“Edit”区是代码显示和修改的区域,是工作的主要区域;“Hierarchy”和“Class”


    是C++代码的组织工具;“Xref”是最强大的一个工具,它会把一个函数中的所有用到的变量,调用的函


    数,用到的结构全记录并展现出来;“Retriever”也是一个非常有用的工具,用于把一个库代码包中的


    所有定义的符号列出来,并记录相应的属性;“Grep”用于查找某一个符号,可指定搜索的范围,既可


    全局查找也可部分文件查找。阅读代码时常用到的一些键值组合:
    Ctrl+leftarrow | rightarrow | uparrow | downarrow 以词的形式移动光标
    Ctrl+Alt+leftarrow | rightarrow | uparrow | downarrow 以词的形式移动光标并选择走过的区域
    Ctrl+Shift+D 查看选定符号的声明
    Ctrl+Shift+I 查看选定符号的定义代码窗口的工具栏上有7个元素,分别是
    “回退跳转”
    “前进跳转”
    “光标处所在的函数名或结构名”
    “选定并要操作(比如查找)的字符串”
    “在本文件中查找选定的字符串”(比Ctrl+F查找功能好使)
    “在整个工程中查找与选定字符串匹配的代码”
    “在整个工程中以Grep的方式查找与选定字符串匹配的代码”(会跳到Grep标签页中显示结果)注意:


    snavigator在导入目录时,目录名中间不能出现空格。


    三、在ubuntu中安装 Source-Navigator及使用手册 
    2009-09-25 23:19 
    from : http://hi.baidu.com/xiaohu_tiger/blog/item/66d207888431d6b40f2444ec.html


    1 通过apt-get安装(版本较旧) 
    安装命令:sudo apt-get install sourcenav 
    启动命令:snavigator


    2 手动安装最新版本 
    1)到官方网站获取软件包 
    http://sourceforge.net/project/showfiles.php?group_id=51180 
    2)解压缩 
    tar zxvf s ourcenav-6.0.tar.gz 
    ./configure --prefix=/opt/sourcenav (install 文件推荐参数) 
    make 
    make install 
    ps:如果出现权限问题,前面加sudo 
    3)如果碰到下述错误 
    /root/tk8.4.16/unix/../generic/tk3d.c:1279: error: ‘TkBorder’ has no member named 


    ‘resourceRefCount’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1280: error: ‘Tk_FakeWin’ has no member named 


    ‘display’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1280: error: ‘Tk_FakeWin’ has no member named 


    ‘screenNum’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1280: error: ‘TkBorder’ has no member named 


    ‘screen’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1281: error: ‘Tk_FakeWin’ has no member named 


    ‘atts’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1281: error: ‘TkBorder’ has no member named 


    ‘colormap’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1301: error: ‘TkDisplay’ has no member named 


    ‘borderTable’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1301: error: ‘TkDisplay’ has no member named 


    ‘borderTable’ 
    /root/tk8.4.16/unix/../generic/tk3d.c:1306: error: ‘TkBorder’ has no member named 


    ‘nextPtr’ 
    。。。。。。 
    则在终端是输入:sudo apt-get install libx11-dev 
    4) 启动snavigator 
    在终端直接输入:snavigator 
    5) 使用手册(user manual) 
    http://sourcenav.sourceforge.net/online-docs/userguide/index_ug.html 
    还有一个 source insight 和source navigator的比较的帖子: 
    http://fky168.blog.163.com/blog/static/3786097200831033957620/


    ======================================================== 
    运行source navigator的问题 
    方法一、(转载) 
    安装完成后运行 snavigator 提示 
    [root@MagicLinux sourcenav-6.0]# snavigator 
    Can't find a usable tk.tcl in the following directories: 
    /usr/local/share/tk8.3


    /usr/local/share/tk8.3/tk.tcl: no event type or button # or keysym 
    no event type or button # or keysym 
    while executing 
    "bind Listbox <MouseWheel> { 
    %W yview scroll [expr {- (%D / 120) * 4}] units 
    }" 
    (file "/usr/local/share/tk8.3/listbox.tcl" line 182) 
    invoked from within 
    "source [file join $tk_library listbox.tcl]" 
    invoked from within 
    "if {[string compare $tcl_platform(platform) "macintosh"] && \ 
    [string compare {} $tk_library]} { 
    source [file join $tk_library button.tcl] 
    so..." 
    (file "/usr/local/share/tk8.3/tk.tcl" line 30 
    invoked from within 
    "source /usr/local/share/tk8.3/tk.tcl" 
    ("uplevel" body line 1) 
    invoked from within 
    "uplevel \#0 [list source $file]"




    This probably means that tk wasn't installed properly.


    不知其所以然,重新安装tk8.3无效,升级到tk8.5无效


    后来在网上发现需要给下载的Source-Navigator 6.0打补丁 
    到http://bugs.gentoo.org/show_bug.cgi?id=225999 找到 tk-8.4-lastevent.patch 补丁 
    将补丁粘贴到source navigator 源码目录 sourcenav-6.0/tk目录下,再cd 到sourcenav-6.0/ tk 的源


    码目录 
    patch -p0 -i tk-8.4-lastevent.patch 
    删除安装,重新安装,可以运行了(我试了一下不行)。


    方法二、(转载) 
    有找到解法: 
    (http://forums.fedoraforum.org/showthread.php?t=213441) 
    註掉 lines 182-184 in the file /opt/sourcenav/share/tk8.3/listbox.tcl 
    註掉 lines 457-459 in the file /opt/sourcenav/share/tk8.3/text.tcl


    在panel中添加Launcher,在Command中添加/opt/sourcenav/bin/snavigator


    ====== 安裝完後在桌面增加捷徑的方法 ====== 
    我要做捷徑的目標是/opt/sourcenav/bin/snavigator (這是個sh script) 
    裡面要改一行 
    #snbindir=`dirname $0` 
    snbindir=/opt/sourcenav/bin 
    這樣再建一個捷徑到桌面即可: 
    ln -s /opt/sourcenav/bin/snavigator ~/Desktop/


    本文来自CSDN博客,
    http://blog.csdn.net/dian_technology/archive/2010/07/23/5757884.aspx


    ========


    代码分析工具推荐Understand

    之前看ogitor ,第一次看到那么多代码~~不知道从哪里下手,而且好多东西都不会Ogre什么的都不是很


    清楚,对ogitor的代码结构的了解就更不用提了。晕头转向的不知道从哪里下手,一点点的看起来好吃


    力。后来从博客中看到了说understand的 ,当时用了一下可以从源代码生成类图 感觉很不错。不过由


    于考试啊乱七八糟的原因放了一段时间之后又由于系统出了问题重装后把他弄丢了,今天想用结果怎么


    也找不到是什么,连名字都忘了  汗~~以后要及时记录啊  找了几乎一天终于又找回来了 呵呵感觉比


    rational rose好得多吧,rational rose 现在已经没有更新了 下了一个都感觉不好用 win7下直接打不


    开 纠结  这个understan 的ui使用qt开发的呢 呵呵


    下载网址是http://www.scitools.com/


    Understand


                    ---- 源代码分析&度量


    Understand™ 从度量、图表、依赖关系分析、代码检查、等各方面全面管理您的源代码。


    Understand_screen_shot


    Understand™主要特点


     Understand度量


    Understand™对所分析的代码进行高效的度量计算,其计算也可通过命令行自动运行,同时支持表格导出


    、图形显示以及GUI动态浏览;另外,更可以通过Understand Perl API进行度量的自定义。通过


    Understand,还可以产生项目级(包含文件、类、函数或系统架构)的报告。


     代码检查


    Understand检查您的代码是否符合代码的国际标准(如MISRA-C 2004,MISRA-C++ 2008等),或者您自


    己自定义的检查标准,如企业的代码规范等。


     代码编辑器


    Understand提供了可以维护和理解您的代码的编辑器,通过其浏览、对比(文件/目录对比,‘微’对比 


    – 如字符级对比)您的源代码,可以更容易、更高效的进行代码的编辑;同时,该编辑器还支持自定义


    的设定,满足开发人员的工作需要。


     依赖关系分析


    Understand依赖关系浏览器具备以下特点:


    - 文件及结构依赖关系快速浏览


    - 依赖/被依赖关系、Butterfly图(文件/架构)


    - 依赖关系图表导出


    - 最新设计的依赖关系浏览Dock显示所以的依赖信息


    Understand可以生成各种依赖关系图表(如整体架构的继承关系、子系统的继承关系等),


    其结果可以保存为PNG、JPEG、SVG等,更可以直接保存为VISIO文件。


    Understand依赖关系信息可以导出为CSV文件以备您进行进一步的分析。


     即时搜索


    通过Understand的即时搜索能力,您可以在上百万行的代码中立刻找到您需要的信息。


     多种图表


    Understand支持各种图表的生成,所有的图表您都可以将其保存成图片格式作为更广泛的编辑用途。通


    过Understand,您可以生成下列图表:


    - 声明关系图


    - 继承关系图


    - 控制流图


    - 依赖关系图


    - UML类图


    - 树形关系图


    - 自定义图表


     代码信息


    - 通过Understand的信息浏览器,你可以看到相关代码实体所有详细信息。


    - 代码上下文信息显示


    - 自动化:通过Understand提供的相应命令,您可以快速的创建、分析项目和产生报


    告,同时可以将Understand与您的构建系统进行集成,自动完成所以代码的分析。


     报告


    Understand支持各种报告的生成,根据您项目的开发语言,基本可以生成如下几类报告:


    - 交叉引用报告


    - 架构分析报告


    - 质量报告


    - 度量报告


    - 客户自定义报告




     支持的语言


    Understand支持:Ada, C/C++, C#, FORTRAN, Java, JOVIAL, Pascal, PL/M, VHDL, Cobol, Web 


    Languages (如PHP, HTML, CSS, Javascript和XML等)。


    即使您的项目同时包含多种语言,Understand也可以进行分析。


     支持的操作系统


    Understand支持:Microsoft Windows (32位、64位),Linux (32位、64位),Solaris Sparc,Solaris 


    X86,Mac OSX。


    引用自http://www.emenda.eu/index.php?


    option=com_content&view=article&id=44&Itemid=22&lang=zh
     
    ========


    强大的代码阅读工具Understand


    个附件_http://www.scitools.com/products/understand/


    Understand软件的功能主要定位于代码的阅读理解。界面貌似是用Qt开发的。


    具备如下特性:
    1、支持多语言:Ada, 
    C, C++, C#, Java, FORTRAN, Delphi, Jovial, and PL/M ,混合语言的project也支持
    2、多平台: 
    Windows/Linux/Solaris/HP-UX/IRIX/MAC OS 
    X
    3、代码语法高亮、代码折叠、交叉跳转、书签等基本阅读功能。
    4、可以对整个project的architecture、metrics进行分析并输出报表。
    5、可以对代码生成多种图(butterfly 
    graph、call graph、called by graph、control flow graph、UML class 
    graph等),在图上点击节点可以跳转到对应的源代码位置。
    6、提供Perl 
    API便于扩展。作图全部是用Perl插件实现的,直接读取分析好的数据库作图。
    7、内置的目录和文件比较器。
    8、支持project的snapshot,并能和自家的TrackBack集成便于监视project的变化。


    小技巧(官网的FAQ里有):


    1、设置字体和颜色风格


    修改默认字体:Tools 
    -> Options -> Editor -> Default style
    修改颜色: Tools -> Options 
    -> Editor -> 
    Styles


    2、生成UML类图、调用树图


    默认安装的插件不支持这两种图,需要从官网下载插件。
    _http://www.scitools.com/perl_scripts/uperl/uml_class.upl
    _http://www.scitools.com/perl_scripts/uperl/invocation.upl
    放到sti/conf/scripts/local目录下。
    然后重新运行,执行 
    project-> project graphical views -> 
    xxxx可以生成这两种图。


    3、更改图的字体


    直接修改对应的脚本文件(\Program 
    Files\STI\conf\scripts目录下),在do_load( 
    )函数的对应位置加入如下的设置:


    $graph->default("fontname","Consolas","node");
    $graph->default("fontsize","10","node");
    $graph->default("fontname","Consolas","edge");
    $graph->default("fontsize","10","edge");


    注意:有的脚本中的作图变量名不是 
    $graph 而是 $g。


    另外一款代码可视化理解工具: http://www.sgvsarc.com/prod_crystalrevs_screenshots.htm


    ========


    Source Insight 生成函数调用关系图

      做数据结构作业,其中有要交一个算法的函数调用关系图。这个课堂上也没讲过。懒人自然不会用


    word或者mspaint一点一点去画了。而visio这种东西,用着也挺麻烦。


      于是上网百度了一下,查到一个叫Source Insight的软件,这个可以根据代码自动生成函数调用关


    系图。这里把最基本的使用方法介绍一下。在使用之前要先在Options-Preference里设置一下语言)


      1、程序主界面(不会排版,图片传上来竟然这么大)


    2、导入源码文件


    File-Load File,选择你要用的文件


    3、生成调用关系图


    打开文件后,将光标置于对应函数上,在Relation Window中就会显示对应的函数关系调用图。


    ========


    Source Insight函数调用关系显示设置



    当我们需要设置source Insight的项目代码中函数调用关系时,可通过如下的设置来实现:
    1、显示函数调用关系窗口
      Source Insight工具栏中“View”—>“Relation Window”,选中“Relation Window”则在右下角显


    示一个函数调用关系窗口,如图:


    上图表示ServiceMain函数调用了其他函数,例如调用了ServiceDelete函数等。
    2、设置显示风格
    如果希望显示ServiceMain被谁调用了,这可通过如下步骤设置:
    点击下图红色框住的图标


    将弹出如下窗口后,修改下图红色框中的内容为Referenced by Functions,系统默认的是Calls。设置


    好后,然后点击ok按钮保存设置。


    点击下图红色框住的刷新按钮


    则显示如下图内容:


    该图表示ServiceMain函数被main函数在15048和15056行调用了。
    设置完毕。
    ========

    给Source Insight做个外挂之一--发现Source Insight



        一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功


    能或者作弊的工具,其中很多也是以外挂的形式提供的。外挂和插件的区别在于插件通常依赖于程序的


    支持,如果程序不支持插件机制,那么就无法为其开发插件,而外挂则不然,它不依赖于程序本身的功


    能,通常是一个单独运行的程序,“挂”其它程序的方法就是跨进程代码注入。如果这个世界的所有软


    件都是开放源代码的,而且没有那么多的License限制,黑客们可以自由修改代码发布新功能,那么就不


    会出现外挂这东西。给别的程序做外挂是一件很麻烦的事情,并不是所有的程序都能够“容忍”从外部


    注入的代码,特别是一些程序存在内部缺陷,按照正常的Windows运行机制注入的功能通常不能达到预期


    的效果,甚至是造成该程序不能使用,所以如果不是实在没有别的办法的话,没有人会主动使用做外挂


    的方式一个程序扩展功能。
        
        尽管不愿意,但还是有一些程序只能通过外挂来扩展它的功能,本文提到的这个“不得不挂”的程


    序就是大名鼎鼎的源代码浏览工具:Source Insight。Source Insight是一款....[此处省略介绍性文字


    字符数(不计空格)1028,非中文单词126,中文字符或朝鲜语单词819]。我使用VC很长时间,也许是被


    VC的“Tabbar”插件惯坏了,所以当我使用不能通过文件标签切换文件的编辑器的时候就感觉非常不适


    应,很不幸,“Source Insight”就是这样的。使用了“Source Insight”一段时间之后,我开始寻求


    为其添加一个文件标签栏的方法,“Source Insight”功能强大,可以通过自定义命令扩展它的功能,


    甚至支持一种类似于C语言语法的宏语言,但是经过一段时间的研究之后,我得结论是只能通过外挂对“


    Source Insight”的界面进行扩展,添加一个用于文件切换的标签栏。
        前面已经提到,不是所有的程序都能够“容忍”外部注入的代码,我之所以觉得“Source Insight


    ”可以挂一下,是因为“Source Insight”使用的是标准的Windows MDI(多文档界面)窗口,窗口之前


    的消息流向简单且遵循Windows标准机制。于是一个月以后,“Source Insight”的文件标签外挂:


    TabSiPlus就诞生了,在研究“Source Insight”和编写“TabSiPlus”期间积累了一些经验,留在自己


    的脑子中只会慢慢遗忘,现在把它们整理成文字和大家一起共享。
        首先介绍一下“TabSiPlus”,它的主要功能就是给“Source Insight”添加一个文件切换标签栏,


    这个切换标签栏对于使用“Source Insight”编写代码的人有很大的帮助,先看一些它都给“Source 


    Insight”带来了哪些变化:


    代码窗口下面多了一个文件标签栏,菜单也变样了,还加上了几个图标,其实菜单的底色和文字颜色都


    是可以改变的,文件标签栏的颜色也是可以改变的,看看:


    除此之外,还添加了C/C++文件翻转的功能,这个可是VA的常用功能,相信大家都不陌生,这个C/C++文


    件翻转功能继承了“Tabbar for Visual C++”插件的多目录、多扩展名搜索功能:


        从现在开始,我就通过一系列文章介绍“TabSiPlus”是怎样一步一步的做出来的,也包括


    对“Source Insight”的研究过程,本篇主要介绍如何找到“Source Insight”。这是一个很重要的问


    题,如果不能从系统中找到正在运行的“Source Insight”,那么外挂就无从挂起了。查找系统中运行


    的“Source Insight”程序有很多种方法,可以遍历系统中的所有进程,然后看看有没有insight3.exe


    ,并得到这个进程的句柄;也可以通过窗口枚举,找到有“Source Insight”标志的主窗口,并获得这


    个主窗口的句柄。当然还有其他的方法,这里就不一一介绍了,“TabSiPlus”采用窗口枚举的方法,因


    为“Source Insight”的主窗口的类名是固定的且标题栏文字很有规律,在任何情况下都有“Source 


    Insight”字样,便于匹配,其实主要的原因是窗口枚举方法简单。
        使用Spy++工具研究“Source Insight”的主窗口,发现其窗口的类名是“si_Frame”,这是一个好


    兆头,如果一个窗口的类名是类似于“Afx:400000:0:10011:10:0”就麻烦了,这是MFC主框架窗口类的


    典型名字,里面的那些数字是诸如进程地址,窗口图标句柄,鼠标光标句柄格式化成的一个字符串,它


    是可变的,在某个系统上是一个结果,在另一个系统上可能是另一个结果。再来看看“Source Insight


    ”主窗口的标题文字,发现无论什么情况都包含一个“Source Insight”子串,这对于我们确定这个窗


    口是否是“Source Insight”主窗口可以起到一个辅助判断的作用。枚举窗口使用EnumWindows() API,


    这个API使用一个回调函数,以下是回调函数的原型:
    BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
    下面是“TabSiPlus”中EnumWindowsProc()回调函数的实现:
    BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
    {
     BOOL bSuccess = TRUE;
     if(hwnd != NULL && IsSourceInsightFrameWnd(hwnd))
     {
      if(lParam)
      {
       HWND *pHwnd = (HWND *)lParam;
       *pHwnd = hwnd;
       bSuccess = FALSE;//已经找到一个Source Insight窗口,退出枚举
      }
     }
     return bSuccess;
    }
    这个函数利用lParam参数将窗口句柄传递出来,它一次只处理一个“Source Insight”窗口,如果系统


    中有多个“Source Insight”运行,就会有多个“Source Insight”主窗口,这由外部机制驱动枚举函


    数进行多次枚举,保证对所有的“Source Insight”进行处理。你可能已经看出来这样存在重复发现的


    问题了,是的,存在这样的问题,不过“TabSiPlus”采用一个巧妙的方法解决了这个问


    题。“TabSiPlus”在Hook一个“Source Insight”窗口之后就在窗口标题栏添加一个“ with 


    TabSiPlus”的标志,这样就可以区分窗口是否已经处理过了。下面就是判断一个窗口是否是“Source 


    Insight”窗口的IsSourceInsightFrameWnd()函数:
    LPCTSTR lpszSourceInsight = _T("Source Insight");
    LPCTSTR lpszSiFrameWndClass = _T("si_Frame");
    LPCTSTR lpszTextMark = _T(" with TabSiPlus");
    BOOL IsSourceInsightFrameWnd(HWND hWnd)
    {
     TCHAR szClassName[128],szTitle[256];
     
     int nRtn = GetClassName(hWnd,szClassName,128);
     if(nRtn == 0)
      return FALSE;
     nRtn = GetWindowText(hWnd,szTitle,256);
     if(nRtn == 0)
      return FALSE;
     //类名是si_Frame,并且窗口标题又含有Source  Insight,可以基本判定是一个Source Insignt窗口
     if((lstrcmp(lpszSiFrameWndClass,szClassName) == 0) && (StrStr(szTitle,lpszSourceInsight) 


    != NULL))
     {
      if(StrStr(szTitle,lpszTextMark) != NULL)//有这个mark说明已经Hook过了,不要再骚扰source 


    insignt窗口了
       return FALSE;
      return TRUE;
     }
     return FALSE;
    }
        下面是找到一个“Source  Insight”窗口的调度函数,每次调用一次调度函数可以查询到一个没有


    被Hook过的“Source  Insight”:
    HWND FindSourceInsightFrameWindow()
    {
     HWND hSiFrmWnd = NULL;
     
     BOOL bRtn = ::EnumWindows(EnumWindowsProc,(LPARAM)&hSiFrmWnd);
     if(!bRtn && hSiFrmWnd != NULL)
      return hSiFrmWnd;
     else
      return NULL;
    }
        最后是查找“Source  Insight”窗口并将指定的动态连接库挂到“Source  Insight”进程中的函


    数:
    //一次试图查找并Hook一个Source Insighe窗口
    BOOL FindAndHookSourceInsightWindow(LPCTSTR lpszHookDll)
    {
     BOOL bSuccess = FALSE;
     if(lpszHookDll)
     {
      HWND hSiFrmWnd = FindSourceInsightFrameWindow();
      if(hSiFrmWnd != NULL)
      {
       bSuccess = HookSourceInsightWindow(hSiFrmWnd,lpszHookDll);
      }
     }
     return bSuccess;
    }
    这个函数中调用了一个重要的函数:HookSourceInsightWindow(),这个函数负责将我们的代码注入


    到“Source  Insight”进程中,这涉及到代码远程注入的很多细节,关于代码注入方法将在下一篇:《


    给Source Insight做个外挂系列之二--将本地代码注入到Source Insight进程》中介绍,本篇到此结


    束。
    Source Insignt文件标签外挂:TabSiPlus的下载地址:
    点击下载
    ========

    sourceinsight的使用记录



    在windows下开发linux程序,使用vc2005有点大材小用,用linux下的vim吧,又有很多的记忆负担。


    那就用sourceinsight吧。


    文件目录树怎么切换磁盘?
    直接在文件搜索栏里面输入D: 就会切换到D:盘。


    0) 跳转的函数定义处
       ctrl + = 
       跳转到函数原型: ???


    1){}符号的自动缩进。换行时,默认是 在上一行左端向右缩进一个tab,但我想让对齐,怎么办?
             新开一个PROJECT后,点Options->Document Options,弹出对话框后先在左上角选好要用的


    Document Type,主要就是设C Source File和C++ Source File,选好后点右边中间的Auto Indent调整


    缩进。单选里一定要点Smart,右边有两个复选框Indent Open Brace和Indent Close Brace,具体效果


    是如何的可以看SIS的HELP。
        勾选Auto Indent和SMART的效果: 在C程序里, 如果遇到行末没有分号的语句,如IF, WHILE, SWITCH


    等, 写到该行末按回车,则新行自动相对上一行缩进四列.
        勾掉indent Open Brace和Indent Close Brace的效果: 继上一段,在相对缩进行里, 如果输入"{"


    或"}", 则自动和上一行列对齐。
    参考:http://blog.csdn.net/yjzl1911/archive/2010/06/13/5669037.aspx


    2)跳转到最近编辑过的地方 有点像 vc2005的 ctrl+<- 和 ctrl+->.
        si的跳转快捷键是:alt+,alt+. 分别是 alt+逗号和 alt+句号!


    3) 跳到指定行 ctrl+g, 这和vc2005一致


    4) F3向上搜索,F4向下搜索
        shift+F4: 快速搜索. 不用调出搜索界面,双击关键字选择关键字即可搜索.


    5) 快速注释/取消注释代码段
        好像没有内置这样的功能,使用 #if 0 #endif 吧


    6) 如何开一个函数 的左大括号 { 跳转到函数结束的 右大括号位置 }
       vc2005 我记得是 直接 输入 ctrl+}就会在两个{}之间来回跳转
    到块的下面                           : Ctrl+Shift+]
    到块的上面                           : Ctrl+Shift+[


         另外,si双击{会选中函数的全部内容
     
    7)检查引用                             : Ctrl+/
    这个功能非常有用哦,就像vc2005里面的查找所有引用


     8)列操作
      虽然开篇时就说过,SI的列操作功能比较弱,但不等于没有。先按下Alt键,接着就可用鼠标进行列


    选择,然后就可以删除指定的列 
      
      9)
    Shift+F8
    高亮显示指定标识,快速浏览标识的使用情况。 


    10)
      项目-项目设置-选中在数据库中存储局部函数符号。 不管那么多,选中即可。
      选项-参数设置-符号查找-项目符号路径-添加项目到路径。可以把之前建立好的linux内核头文件项目


    加到这里的符号路径, 这样你写代码是,比如 str 会自动提示 strcmp strcpy等等东西, 而且可以直


    接看到函数原型,功能一点不次于vc2005!


    11) 让source insight 显示文件的全路径
       写代码时,实时的知道当前编辑的文件是硬盘上的哪个文件时非常有必要的,不然万一编辑错了怎么办


    ?特别是在有同名文件的时候. 这点vc2005就非常好,在标签上右键就可以打开文件位置.
       si也可以满足你.  只是不能打开文件位置.
       选项-参数设置-显示, 取消选中 "用省略号修剪长路径名"(该选项出现在倒数第二行).


    12) 如何更高效的使用查找引用, 切换到下一个引用,上一个引用?
       当使用查找引用功能时,会弹出一个查找结果页面,页面的左侧有个小按钮,可以调到引用的实际位


    置。但是每次看完一个要回来才能看下一个很不方便,其实,在工具栏上有两个小按钮,可以跳到下一


    个引用和上一个引用。并且 Alt+F9  可以直接跳转到下一个。
        另外,查找引用的时候,还是用“简单字符串”模式查找比较靠谱吧,速度慢点,我用另外一个默


    认总是找到漏掉几个,不爽!


     
    13)
      关于删掉半个汉字的问题
      网上流传一个 *      猪 哥  作 品可以解决该问题,方法如下:


     a)记事本编辑另存为:SuperBackspace.em 即可。
     b) 由于我的si是绿色的,没有Base项目, 只有自己在 D:/我的文档/Source Insight/Projects/Base 


    路径下新建一个 Base项目。
     c)将SuperBackspace.em 加入到当前base项目中来。
         方法如下:
         c.1)选项-文档选项-解析-语言-SI Macro Language, 然后选择上面 文档类型-Source Insight 


    宏文件, 文件过滤器里面是 *.EM 
         c.2)然后找到 superasespace.em添加进来。 确保在右侧的文件列表里可以看到
     d)重启SI
     e)添加键映射,选项-键关联-在左侧的列表里找到 “宏:SuperBackspace”, 点击分配新建,按键


    盘的 Backspace键,击键出出现 Backspace。ok
     f)试试删除半个汉字是否ok了?
         
    /**
     *       ╭︿︿︿╮
     *       {/ . ./}
     *       (  (oo)  )
     *        ︶︶︶︶
     *      猪 哥  作 品
     *
     * 2006 丁兆杰 Ding Zhaojie
     * zhaojie.ding@gmail.com
     *
     * SuperBackspace Version 0.1beta
     *
     * 代替SourceInsight原有的Backspace功能(希望如此)
     * 增加了对双字节汉字的支持,在删除汉字的时候也能同时删除汉字的高字节而缓解半个汉字问题
     * 能够对光标在汉字中间的情况进行自动修正
     *
     * 安装:
     * ① 复制入SourceInsight安装目录;
     * ② Project→Open Project,打开Base项目;
     * ③ 将复制过去的SuperBackspace.em添加入Base项目;
     * ④ 重启SourceInsight;
     * ⑤ Options→Key Assignments,将Marco: SuperBackspace绑定到BackSpace键;
     * ⑥ Enjoy!!
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
    macro SuperBackspace()
    {
        hwnd = GetCurrentWnd();
        hbuf = GetCurrentBuf();
        if (hbuf == 0)
            stop;   // empty buffer
        // get current cursor postion
        ipos = GetWndSelIchFirst(hwnd);
        // get current line number
        ln = GetBufLnCur(hbuf);
        if ((GetBufSelText(hbuf) != "") || (GetWndSelLnFirst(hwnd) != GetWndSelLnLast(hwnd))) {
            // sth. was selected, del selection
            SetBufSelText(hbuf, " ");  // stupid & buggy sourceinsight :(
            // del the " "
            SuperBackspace(1);
            stop;
        }
        // copy current line
        text = GetBufLine(hbuf, ln);
        // get string length
        len = strlen(text);
        // if the cursor is at the start of line, combine with prev line
        if (ipos == 0 || len == 0) {
            if (ln <= 0)
                stop;   // top of file
            ln = ln - 1;    // do not use "ln--" for compatibility with older versions
            prevline = GetBufLine(hbuf, ln);
            prevlen = strlen(prevline);
            // combine two lines
            text = cat(prevline, text);
            // del two lines
            DelBufLine(hbuf, ln);
            DelBufLine(hbuf, ln);
            // insert the combined one
            InsBufLine(hbuf, ln, text);
            // set the cursor position
            SetBufIns(hbuf, ln, prevlen);
            stop;
        }
        num = 1; // del one char
        if (ipos >= 1) {
            // process Chinese character
            i = ipos;
            count = 0;
            while (AsciiFromChar(text[i - 1]) >= 160) {
                i = i - 1;
                count = count + 1;
                if (i == 0)
                    break;
            }
            if (count > 0) {
                // I think it might be a two-byte character
                num = 2;
                // This idiot does not support mod and bitwise operators
                if ((count / 2 * 2 != count) && (ipos < len))
                    ipos = ipos + 1;    // adjust cursor position
            }
        }
        // keeping safe
        if (ipos - num < 0)
            num = ipos;
        // del char(s)
        text = cat(strmid(text, 0, ipos - num), strmid(text, ipos, len));
        DelBufLine(hbuf, ln);
        InsBufLine(hbuf, ln, text);
        SetBufIns(hbuf, ln, ipos - num);
        stop;
    }
     
    xx)


      本来以为自己对source Insight的使用还算熟练的,结果发现它还支持这么多先进的以前没玩过,


    或者以为它实现不了的功能。非常棒!!
      关于sourceInsight3.5的缩进问题和库函数补全问题。
      由于vc自动缩进很智能,而且编写库函数能自动提示补全函数名并提示参数组成,很方便。
      今天研究得出SourceInsight3.5也可以实现这两个效果。
      1. Preferences->Symbols Lookups:Add Project to Path... 选择"D:/Program Files/Microsoft 


    Visual Studio6.0/VC98/Include后它默认取名为CStandard.PR,点加入所有头文件确定后就可以看到


    Project symbol path:下面的文本框里面多了一个字符串"D:/Program Files/Microsoft Visual 


    Studio6.0/VC98/Include/CStandard.PR",这样点确定后,以后所有的工程都可以自动补全库函数和看


    到库函数的原型了。 
      2. Alt+T打开Document Options对话框,如果你是c++代码,那么选择c++类型,file filter下面输


    入框下面的两个复选框第一个Use options from Default type不要选中,一定不要选,要不indent调不


    对的,Editing Options下面的复选框选上:Allow auto-complete自动补齐功能,少打几个字而且补齐


    的可以避免不必要的输入错误,Expand tabs把tabs变换成空格,不同编辑器打开都统一了不会乱,


    Enter Key->new line回车建立新行,show line numbers显示行号看到代码很方便的,show right 


    margin编写代码时候可以提醒自己不要一行不要太长及时换行,symbol window符号索引打开很好很强大


    ,快速定位一个函数或者变量。 


    更多快捷键信息参考:
    source insight快捷键及使用技巧
    http://blog.csdn.net/hbd1986/archive/2010/01/27/5260727.aspx
    ========

    source insight设置Project Symbol Path



    今天使用source insight查看代码,不小心删除了Base项目,导致c标准库符号、mfc中的符号不能被识


    别。于是自己新建了一个Base项目,发现不能被别的项目所公用。最后在SI的帮助文档里面找到了解决


    方法。
    image
    这里的path跟Path路径的概念几乎一样,都是在当前项目中找不到符号的时候最后查找的地方。
    ========

    代码阅读分析工具Understand 2.0试用



    Understand 2.0是一款源代码阅读分析软件,功能强大。试用过一段时间后,感觉相当不错,确实可以


    大大提高代码阅读效率。由于Understand功能十分强大,本文不可能详尽地介绍它的所有功能,所以只


    列举本人认为比较重要或有特色的功能,以做抛砖引玉之举。
    Understand 2.0可以从http://www.scitools.com/下载到,安装后可以试用15天。
    使用Understand阅读代码前,要先创建一个Project,然后把所有的源代码文件加入到这个Project里。


    这里我创建了一个ATLSTL的Project,然后把Microsoft Visual Studio 2008带的ATL、STL、MFC的源代


    码加入其中。需要说明的是,Understand支持很多种源代码,包括C#,而不光是C++代码。
    这是整个用户界面的概览,可以看到和Visual Studio的风格很相似。所有了子窗口都可以任意停靠或折


    迭。
    Understand界面概览
     
     下面逐个介绍Understand的特性。
    一,强大的自动绘图能力。
    Understand可以生成许多种有用的图形,如类关系图、函数调用关系图、头文件包括关系等。下面是


    CFile的类图。
    CFile类图
     
    当然,如果愿意,你可以生成CObject派生的整个MFC的类图


    (https://p-blog.csdn.net/images/p_blog_csdn_net/qwang24/EntryImages/20090411/ButterflyGraph


    -CObject.png)。这种图以前只能在MSDN里可以见到,现在可以在瞬间自动生成,很酷!
     
    还可以显示函数的调用关系:
     
    函数调用关系图
     
    以及头文件的包括关系:
    头文件包括图
     
    二,出色的增量搜索功能
    增量搜索也许不是新概念,在Visual Studio里早就有。但是Understand里的增量搜索具有动态代码加亮


    的功能。也就是说,可以把选中的标识符的所有实例,都以醒目的颜色显示出来,对于阅读代码时,非


    常有帮助。如下图,对函数参数pFileTime执行增量搜索,则它出现的过地方都会被标示出来。这样,这


    个参数是如何被使用的,一目了然。
     
    增量搜索
     
     
    三,丰富的标识符信息
    Understand的代码信息数据库十分完善,所有的标识符可以分类显示。每一类标识符又具有不同的信息


    。如关于函数的信息,可以显示定义它的文件名,返回值类型,参数信息,调用函数,被调用函数,引


    用这个函数的信息,代码量等。其中,引用信息里会有引用类型,如申明、调用、定义等,非常实用。
    函数信息
     
    而对于变量信息,而以显示变量在哪里定义的,是设置变量的值还是使用变量的值等。
    变量信息
     
    四,方便的搜索功能
    很多窗口都有方便的搜索功能,便于快速定位需要的信息。如项目浏览器里,可以输入文件名,快速找


    到相关的文件。
    文件名搜索
     
    Understand还有很多其他强大的功能,如报表功能,代码编辑,代码变化跟踪等。
    当然,和其他所有的代码分析工具软件一样,Understand也有一些不足之处,如有时候会解析错误,不


    支持COM代码的Attribute扩展,对机器配置要求较高,对正则表达式的支持不完善等。
     
    备注:
    解决不能正确解析COM的Attribute扩展问题: 搜索正则表达式: /]/s*$ , 替换为 /]; (使用


    Visual Studio的在多个文件中替换功能,不能直接使用Unserstand替换。)
    解决不能正确解析__interface关键字问题: 在project选项里增加宏定义,把__interface定义为


    struct。
    ========
    展开全文
  • 搭建Hadoop源代码阅读环境

    千次阅读 2016-01-08 14:05:42
    总体上说,目前存在两种Hadoop源代码阅读环境搭建方法,分别是构建Maven工程和构建Java工程。两种方法各有利弊:前者可通过网络自动下载依赖的第三方库,但源代码会被分散到多个工程中进而带来阅读上的不便;后者可...

    本节将介绍如何创建一个Hadoop源代码工程以方便阅读源代码。总体上说,目前存在两种Hadoop源代码阅读环境搭建方法,分别是构建Maven工程和构建Java工程。两种方法各有利弊:前者可通过网络自动下载依赖的第三方库,但源代码会被分散到多个工程中进而带来阅读上的不便;后者可将所有源代码组织在一个工程中,但需要自己添加依赖的第三方库,大家可根据自己的喜好选择一种方法。本节将依次介绍这两种方法。

    (1)构建Maven工程

    通过Maven工程搭建Hadoop源代码阅读环境的步骤如下:

    步骤1 解压缩Hadoop源代码。

    下载到的Hadoop源代码压缩包解压到工作目录下,比如hadoop-2.0-src.tar.gz(注意,为了方便,此处直接使用版本号2.0,实际下载到的源代码版本号并不是这样的,可能是2.2.0,这样压缩包的名字实际为hadoop-2.2.0-src.tar.gz)。

    步骤2 导入Maven工程。

    在Eclipse中,依次选择“File”→“Import”→“Maven”→“Existing Maven Project”,在弹出的对话框中的“Root Directory”后面,选择Java源代码所在的目录。

    单击“Next”按钮,在弹出的对话框中选择“Resolve All Later”,并单击“Finish”按钮完成Maven项目导入。之后,Eclipse会自动通过网络从Maven库中下载依赖的第三方库(JAR包等)。注意,你所使用的电脑必须能够联网。

    将Hadoop 2.0源代码导入Maven项目后,会生成50个左右的工程,这些都是通过Maven构建出来的,每个工程是一个代码模块,且彼此相对独立,可以单独编译。你可以在某个工程下的“src/main/java”目录下查看对应的源代码。

    (2)构建Java工程

    通过Java工程搭建Hadoop源代码阅读环境的步骤如下:

    步骤1 解压缩Hadoop源代码。

    同“构建Maven工程”中的步骤1类似。

    步骤2 新建Java工程。

    打开Eclipse,进入Eclipse可视化界面后,如图1-1所示,依次单击“File”→“New”→ “Java Project”,并在弹出的对话框中去掉“Use default location”前的勾号,然后选择Hadoop安装目录的位置,默认情况下,工程名称与Hadoop安装目录名称相同,用户可自行修改。单击“完成”按钮,Hadoop源代码工程创建完毕。

     

    回到Eclipse主界面后,打开新建的Hadoop工程,可看到整个工程按图1-2所示组织代码:按目录组织源代码,且每个目录下以JAR包为单位显示各个Java文件。

     

    除了使用源代码压缩包导入Eclipse工程的方法外,读者可也尝试直接从Hadoop SVN上导入Hadoop源代码。

    需要注意的是,通过以上方法导入Hadoop 2.0源代码后,很多类或者方法找不到对应的JAR包,为了解决这个问题,你需要将第三方JAR包导入工程中,如图1-3所示,方法如下:解压存放JAR包的压缩包,然后右击Project名称,在弹出的快捷菜单中选择“Properties”命令,将会弹出一个界面,然后在该界面中依次选择“Java Build Path”→ “Libraries”→“Add External JARs...”,将解压目录中share/hadoop目录下各个子目录中lib文件夹下的JAR包导入工程。

    前面提到CDH版本将源代码和JAR包放到了一起,因此,如果使用CDH版本,则直接按照上述方法将源代码导入Eclipse工程即可。

    细心的读者在阅读源代码过程中仍会发现部分类或者函数无法找到,这是因为Hadoop 2.0使用了Protocol Buffers定义了RPC协议,而这些Protocol Buffers文件在Maven编译源代码时才会生成对应的Java类,因此若其他类在源代码中引用这些类则暂时无法找到,解决方法是先编译Hadoop 2.0源代码再倒入Eclipse工程,具体方法如下(注意,进行以下步骤之前,需先完成1.6.1节中的准备工作)。

     

    首先,使用以下命令安装Elicpse插件hadoop-maven-plugins:

    $ cd ${HADOOP_HOME}/hadoop-maven-plugins

    $ mvn install

    然后生成Eclipse工程文件:

    $ cd ${HADOOP_HOME}

    $ mvn eclipse:eclipse -DskipTests

    最后在Eclipse中按照以下流程导入源代码:“File” → “Import”→ “Existing Projects into Workspace”。

    展开全文
  • yolov5代码阅读笔记

    万次阅读 多人点赞 2020-06-23 15:31:53
    文章目录Yolov5 代码笔记1. train(hyp)函数重点部分1.1 载入图片路径1.2 创建模型1.3 确定训练和测试图片的尺寸1.4 根据参数的形式(是否为weight,是否为bias等)定义模型的不同部分,并为不同部分设置不同的学习率...
  • iwconfig源代码阅读

    千次阅读 2011-11-10 11:38:49
    1、iwconfig源代码阅读 首先下载iwconfig.c代码,源代码包为\wireless_tools.29目录 先看执行 iwconfig eth0 的命令的执行过程: 调用main函数,因为是两个参数: if(argc == 2) print_info(skfd, argv[1],...
  • 公众号首发:... 目录 目录 说明 ... 四:逐行阅读 ... Go项目的一些阅读技巧 ... 技巧1:忽视真正入口之前的代码 技巧2:接口的实现一般就在接口定义的下方 技巧3:见到了类似...
  • 如何阅读项目源代码

    万次阅读 多人点赞 2016-12-04 00:30:25
    如何阅读项目源代码(github)Github工程项目过大?难以阅读怎么办,本文就根据自己阅读代码的经验,总结一下如何阅读代码的经验。借鉴博客看他人的源码分析是最简单也是最直接的项目经验获取方式,一篇好的源码...
  • 如何阅读代码

    千次阅读 2014-04-28 18:50:55
    2.要有选择地阅读代码, 同时, 还要有自己的目标. 您是想学习新的模式|编码风格|还是满足某些需求的方法. 3.要注意并重视代码中特殊的非功能性需求, 这些需求也许会导致特殊的实现风格. 4.在现有的代码上工作时...
  • 代码阅读笔记-MDnet

    千次阅读 热门讨论 2016-11-12 18:33:58
    前段时间读完MDnet的论文,又阅读了一下代码,由于调用函数太多,为了方便记忆,随手写了个记录代码结构及功能的笔记: MDnet--master 源代码目录 --dataset (存放数据集的文件夹) --matconvnet (这个不用说了,...
  • centernet代码阅读笔记

    千次阅读 2019-07-17 13:57:54
    代码: centernet 原理请看扔掉anchor!真正的CenterNet——Objects as Points论文解读 尊重原创,请读原文 1. 网络结构 1.1 主干网络 1.2 输出部分 net.reg,??? Sequential( (0): Conv2d(64, 25...
  • GitHub代码阅读神器,你值有拥有!

    千次阅读 2018-12-20 13:54:31
    (题图:from github) ...由于是在线Web应用,阅读代码时极不方便,再加上网络原因(你懂的),一个Page一个Page的去翻看代码也是很熬人,任何一个看过github上代码的朋友相信深有体会。想深入了解代码的朋友,一...
  • 如何阅读一份源代码

    千次阅读 多人点赞 2021-04-09 11:31:18
    希望能给大家阅读代码有一个整体上的指导。 阅读代码的能力算是程序员的一种底层基础能力之一,这个能力之所以重要,原因在于: 不可避免的需要阅读或者接手他人的项目。比如调研一个开源项目,比如接手一个其他...
  • DANet论文及代码阅读笔记

    万次阅读 多人点赞 2019-07-15 12:15:12
    代码:https://github.com/junfu1115/DANet/ 存在的问题 为提高像素级识别特征表示的辨别能力:①利用多尺度上下文融合,结合不同的膨胀卷积和池化操作,来聚合多尺度上下文。②使用分解...
  • [ C/C++ ] 程序学习--如何阅读别人的代码 ++++++++++++ 第一章: 导论 ++++++++++++ 1.要养成一个习惯, 经常花时间阅读别人编写的高品质代码. 2.要有选择地阅读代码, 同时, 还要有自己的目标. 您是想...
  • zookeeper客户端C lib代码阅读

    千次阅读 2012-06-07 14:14:05
    zookeeper客户端C lib代码阅读 添加评论2011年10月10日 ahxgw 如果想用C/C++进行zookeeper应用开发或者想学习一下linux系统/网络编程,那么阅读一下zookeeper客户端C lib的代码都是很有帮助的 zookeeper提供了...
  • 搭建Linux内核代码阅读环境

    千次阅读 2011-10-09 10:02:46
    前言:在Win7下安装了VMware+Ubuntu,现在想阅读Linux...首先想到的是使用Source Insight,它是一款非常优秀的代码阅读工具。但Source Insight是运行在Window下的。除了将Linux内核代码copy到window下外,还有其他方法
  • 阅读Android framework源代码方式

    千次阅读 2017-11-02 09:34:30
    阅读代码的方式有很多,这里只讲其中的两种方式。一.AndroidXRef(强烈推荐) 这种方式速度快,操作简单,效率高。 打开网址:http://androidxref.com/ 这是一个快速搜索源代码的引擎。界面如下: 上面有...
  • 【以下转载】 不知道大家在平时看代码写代码都使用什么工具? 在以前在 windows 下我一般使用 VS2008, 其实我觉得 VS 做的还是很好的, 它在调试... 之前在阅读代码的时候,发现一款很好的代码阅读工具,是 Scitool
  • MySQL源代码阅读调试 - 1. 环境搭建

    万次阅读 2016-05-18 21:59:52
    最近需要阅读下MySQL源代码,所以写这系列博客记录下。 搭调试环境真是比较蛋疼,公司基本Java开发,这里回到C++。。。 用了两天晚上,尝试了VS2013,Eclipse CDT,CodeBlock还有GDB。 GDB比较好搭建,将带Boost...
  • Source Insight源代码阅读工具

    千次阅读 2010-01-04 16:42:00
    今天看Linux源代码的过程中浏览帖子,发现别人推荐了一个源代码阅读工具Source Insight,这几天看源代码最痛苦的就是函数的调用。这里定义的函数在其他的文件中调用,不断变化的变量也非常痛苦,使用了一下软件,...
  • tensorflow seq2seq模型 代码阅读分析

    千次阅读 2017-08-11 15:08:17
    如果刚开始入门该模型请阅读tf官方说明:Sequence-to-Sequence Models模型应用于机器翻译的示例代码:github如果还没有看懂tf的translate示例代码,请先理解透彻translate项目代码之后再阅读本文。开始开始阅读源码...
  • Linux内核源码在windows下的快速阅读查找,内核代码导入Source Insight3.5 。
  • 【论文+代码阅读】PointPillars

    万次阅读 热门讨论 2019-08-24 10:16:05
    后续就是漫长的训练,不过在训练的inference阶段,我出现了一个小bug:也就是一个需要bool型但是代码却是一个byte的错,需要做如下的修改,如果没有报错就不用修改了。 在 second.pytorch\second\pytorch\models ...
  • ROS源代码阅读(1):找切入点

    千次阅读 2017-09-17 15:49:40
    ROS机器人操作系统是一个机器人软件平台,它能为...阅读ROS操作系统源代码,将代码下载下来后,需要找到一个切入点,层层深入阅读。 在ROS官网教程中,第一段示例应用代码如下,本文以此为依据,找到解析ROS的切入点。
  • MAC下阅读Android源代码

    千次阅读 2016-08-18 17:52:16
    之前在Linux环境下开发的时候,一直用source insight查看源代码,觉得挺方便的,但是在mac Retina环境...一、使用eclipse阅读Android源码: 这种方式不需要编译源代码,能够快捷方便导入。 1.将eclipse工具根目录下的
  • 一、为什么要阅读代码?很多作家成名之前都阅读过大量的优秀文学作品,经过长期的阅读和写作积累,慢慢的才有可能写出一些好的、甚至是优秀的文学作品。 而程序员与此类似,很多程序员也需要阅读大量的优秀程序或...
  • 代码阅读】YOLOv3 Loss构建详解

    万次阅读 多人点赞 2019-05-29 18:19:42
    aaaYolov3网络框架Loss构建功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 400,565
精华内容 160,226
关键字:

代码阅读网