精华内容
下载资源
问答
  • OpenPose笔记——windows 10下,自编译openpose代码(vs下能跑了,pythonAPI也能使了) 原文出处:https://blog.csdn.net/xuelanlingying/article/details/102793110 再次感谢原文博主!! 但是还有一个不足点,就是...

    OpenPose笔记——windows 10下,自编译openpose代码(vs下能跑了,pythonAPI也能使了)

    原文出处:https://blog.csdn.net/xuelanlingying/article/details/102793110
    再次感谢原文博主!!
    1、但是还有一个不足点,就是在最后执行python代码时,一定要在安装了opencv库的python环境下运行,因为源代码导入了opencv库。
    2、安装的Cuda版本最好是cuda10,我用cuda8没有成功。步骤基本差不多。有几点说明,如果没有pyhind不能生成python api。但是C++的可用,如若不需要python接口,忽略即可。在VS中编译时,选择Release模式,而不是Debug。
    3、再附上安装的视频教程:一、https://www.bilibili.com/video/BV1Ma4y14734?from=search&seid=12437501417723638799
    二、https://www.bilibili.com/video/BV1BT4y13773?from=search&seid=12437501417723638799

    简直太可怕了,遇到N多的问题,我觉有必要写下来记录一下
    我自己编译了四五天
    编译了10几次
    夭寿哦!
    缺好多好多东西!
    给大家讲一下具体步骤

    一 .准备工作
    准备工作当然是各种环境:

    1. 至少VS2015 以上的版本
    2. CMake Gui,注意,千万不要下载3.16版的!3.16版本没有win64的选项,编译出来是win32版本的。。。然后x64和x86冲突简直哭死个人。。。。我个人用cmake 3.13.2版本莫问题
    3. OpenPose 源码 下载好后解压好待用。本人是2019年10月23日下载的源码
    4. 安装 CUDA 官网下载,按提示来就行,注意添加环境变量:添加 [yourPath]\cuda 和[yourPath]\cuda\bin 到环境变量
    5. 安装 CuDNN ,解压后拷贝:
      [yourPath]\cuda\bin\cudnn64_5.dll —> (拷贝至)
      [yourPath]\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\bin
      [yourPath]\cuda\include\cudnn.h —> (拷贝至)
      [yourPath]\NVIDIA GPU Computing Toolkit\CUDA\v8.0\include
      [yourPath]\cuda\lib\x64\cudnn.lib —>(拷贝至)
      [yourPath]\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\x64
      二. 各种依赖安装
    6. 运行…/openpose-master/models/中的getModels.bat 下载所需的模型
    7. 运行…/openpose-master/3rdparty/windows中的getCaffe.bat,getCaffe3rdparty.bat和getOpenCV.bat 下载各种依赖
    8. 下载pyhind11,放在openpose-master\3rdparty文件夹下覆盖即可。 pyhind11百度云,提取码e0gv

    三、编译源代码
    最重要的来了!

    1. 打开CMake GUI,第一个红框是openpose源码的文件夹,第二个红框里填的是生成路径,自己创建的
      在这里插入图片描述
    2. 点击【Configure】按钮,在弹出的对话框根据自己的版本选择合适的vs版本,一定要选择带win64的!对于同一个源码文件夹,这个对话框只弹一次。。。。。
      在这里插入图片描述
    3. 等出现 “Configuring done”后,差不多是这样的,再次点击【Configure】按钮直到红色全部消失。configure时如果出现错误,按照下面的错误提示自行解决。(我遇到的就是缺少pyhind11)
      在这里插入图片描述
    4. 勾选复选框
      在这里插入图片描述
      在这里插入图片描述
    5. 点击【Generate】按钮,出现“Generating done”就好了
    6. 然后下载 Caffe.rar, 解压后放在 openposeC-master\3rdparty\caffe\bin。因为之前通过 getcaffe.bat下载的caffe少boost_thread-vc140-mt-1_61.dll等dll文件,会导致无法运行,这几个dll根本下不到啊啊啊,所以我就把我自己弄好的dll打包好分享出来。 caffe百度云,提取码:0n32
      在这里插入图片描述
    7. 打开vs2015,打开项目,sln文件在刚刚的build文件夹里,选择Examples下的OpenPoseDeme,右键“设置为启动项目”,然后build整个解决方案在这里插入图片描述

    8. 在这里插入图片描述
      不知道别人怎么样,反正我跳过了一个。。。。
      在这里插入图片描述
      四、 跑起来啊嘿嘿嘿~
    9. 上面的步骤完了之后,C++的例子已经可以运行了,所有C++的例子都在这里,需要用哪个例子,就把它右键设置为启动项目,然后运行就可以了。(里面有几个例子是要用到摄像头的)
      在这里插入图片描述
      在这里插入图片描述
    10. 运行python
      正常步骤下来,这个时候在openpose-master\build\python\openpose\Release文件夹下会出现pyopenpose.cp37-win_amd64.pyd 这个库文件
      在这里插入图片描述
      这证明我们已经安装了python API
      然后通过cmd,cd到 openpose-masterV2\build\examples\tutorial_api_python文件夹下,执行 python 01_body_from_image.py,就可以得到结果
      在这里插入图片描述
      PS:如果出现了 找不到 DLL 文件的错误,会提示“BUILD_PYTHON”的错误,这个错误是python文件里写的。。。可以不用管,主要原因还是之前依赖没安好,在确定所有的依赖都放到了对应的文件夹的情况下,可以再次执行 点击【configure】按钮,点击【generate】按钮,vs内 【重新生成解决方案】,这样重新编译一遍就好了。
      真的太难了。。。我弄了整整5天。。。。各种缺库。。。我还试过自己编译boost_1_61,只能编译出来静态链接库lib,编译不出来动态链接库dll。。。最核心的caffe包(包含各种boost vc140 dll)还是我从一位大哥那要了一个已经编译好的openpose文件里抠出来的,哪哪都下不到,真的是心力交瘁

    已阵亡。。。。

    展开全文
  • tensorflow——openpose代码和原理分析

    万次阅读 多人点赞 2018-07-22 09:59:21
    openpose已经做了两个月了,精度和速度都还没有提上去,但是还是要总结一下。 人体姿态估计(pose estimation)的目标  实时地对图片中每个人的姿态进行精准的估计。总结起来,就是实时的多人姿态精准估计 ...

     做openpose已经做了两个月了,精度和速度都还没有提上去,但是还是要总结一下。

    人体姿态估计(pose estimation)的目标

       实时地对图片中每个人的姿态进行精准的估计。总结起来,就是实时的多人姿态精准估计

    现状

        实时的算法精度不高,精度高的算法慢的要死。

        openpose——比较快,比较准

         cpn——慢,准(pass,在实际场景中,慢等于没用)

         poseNet——快,不准(pass,不准更没啥用了)

     openpose的原理

        输入一张图片,经过一个backbone(比如vgg,resnet,mobilenet),在经过6个stage,每个stage有2个branch,一个用来检测heatmap,一个用来检测vectmap。有了heatmap和vectmap就可以知道图片中所有的关键点,但是不知道这些关键点都是哪个人的,PAFs把这些点对应到每个人身上。end

    openpose的训练

       数据集:coco,MPII,AIC,PoseTrack

        硬件要求:至少4  个 gpu以上(GPU不充裕的,可以直接用原作者公布的caffe模型,我训练了俩月精度都没赶上他们)

    openpose的代码(tensorflow版本)

        这俩月学到了啥,就是代码能力吧。之前就是只会训练MNIST,我就以为自己会深度学习了。源码比这个难100倍,不过,道理是相同的。我听过那么多道理,依然写不好代码,想哭......

    (1)下载数据集

    1.coco数据集的标注是什么形式

    2.coco数据集API使用 

    3.json文件的读和写      

    (2)数据处理

    1.关键点从COCO的格式转为openpose的格式

    2. 生成heatmap

    3.生成vectmap

    (3)多线程数据增强

    1.多线程

    2.数据增强

    (4)dataflow数据读入

    1.入队和出队

    (5)网络定义

    1.网络基础函数的定义

    2.网络定义

    3.loss函数

    (6)多GPU训练

    1.多GPU训练的流程

    (7)模型保存,恢复以及可视化

    1.模型和日志的保存

    2.训练的可视化

    3.模型的恢复

    4.模型挂载在服务器上训练

    (8)模型转freeze pb

    1.模型的发布

    (9)数据后处理

    (10)模型测试+opencv显示

     

    整体流程就这么多。。。。

    写完流程我就不想写细节了。。。

    随后再补

     

     

     

     

    展开全文
  • Openpose训练代码

    2020-06-16 16:14:14
    openpose训练代码(一): http://blog.csdn.net/u011956147/article/details/79292026 openpose训练代码(二):http://blog.csdn.net/u011956147/article/details/79292734 openspoe本身是很繁杂的,包含了人体...

    openpose训练代码(一): http://blog.csdn.net/u011956147/article/details/79292026
    openpose训练代码(二):http://blog.csdn.net/u011956147/article/details/79292734

    openspoe本身是很繁杂的,包含了人体姿态估计、手势估计、脸部关键点提取,还有3Dpose,是在caffe上再做的一层封装,但是如果我们实际要去用的话,很多其实都是不需要的,比如openpose里面的多线程,GUI等等,我们只需要关注一些核心的东西就好了。
    在这里,我们只关心openpose中的人体关键点估计,其实在上一篇博客中,我们可以大致了解到,Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields就是CVPR6016的CPM加上PAF,inference是很直观的,就是提取关键点,算PAF积分,再把关键点放到每个group(就是确定是不是同一个人)完成多人的姿态估计。

    训练代码,其实主要就是看数据准备和数据读取,主要包括几个文件:
    数据读取文件:

    cpm_data_layer.cpp
    cpm_data_transformer.cpp

        1
        2

    数据准备文件:

    genCOCOMask.m
    genJSON.m
    genLMDB.py
    getANNO.m

        1
        2
        3
        4

    cpm_data_layer和cpm_data_transformer都是在caffe中实现的,要理清楚这两个文件,我们需要先看一下数据准备是怎么做的,这里,也只是关注LMDB文件是怎么生成的,因为其他的都比较简单(其实生成LMDB也蛮简单的,但是作者这部分写的有点乱,需要静心好好梳理)可以自行查阅。
    在genLMDB.py中,把事先处理好的数据都写入LMDB中,其中有一个函数writeLMDB,这个函数就是逐行,逐页面(这里的页面可以理解长channel,因为在读取的时候都是利用指针移动)来写入的:

    def writeLMDB(datasets, lmdb_path, validation):
        env = lmdb.open(lmdb_path, map_size=int(1e12))  # 需要先建立一个空文件夹用来放LMDB文件,大概需要140G
        txn = env.begin(write=True)
        data = []
        numSample = 0

        for d in range(len(datasets)):
            if(datasets[d] == "MPI"):
                print datasets[d]
                with open('MPI.json') as data_file:
                    data_this = json.load(data_file)
                    data_this = data_this['root']
                    data = data + data_this
                numSample = len(data)
                #print data
                print numSample
            elif(datasets[d] == "COCO"):   # 读json文件
                print datasets[d]
                with open('dataset/COCO/json/COCO.json') as data_file:
                    data_this = json.load(data_file)
                    data_this = data_this['root']
                    data = data + data_this
                numSample = len(data)
                #print data
                print numSample

        random_order = np.random.permutation(numSample).tolist()

        isValidationArray = [data[i]['isValidation'] for i in range(numSample)];
        if(validation == 1):
            totalWriteCount = isValidationArray.count(0.0);
        else:
            totalWriteCount = len(data)
        print totalWriteCount;
        writeCount = 0

        for count in range(numSample):# numSample
            #idx = random_order[count]
                    idx = 3
            if (data[idx]['isValidation'] != 0 and validation == 1):
                print '%d/%d skipped' % (count,idx)
                continue

            if "MPI" in data[idx]['dataset']:
                path_header = 'dataset/MPI/images/'
            elif "COCO" in data[idx]['dataset']:
                path_header = '/proj/Sunjiarui/fcm_pose_train/training/dataset/COCO/images/'

            print os.path.join(path_header, data[idx]['img_paths'])
            img = cv2.imread(os.path.join(path_header, data[idx]['img_paths']))
            #print data[idx]['img_paths']
            img_idx = data[idx]['img_paths'][-16:-3];
            #print img_idx
            # 做mask_all 和mask_miss 这里是因为有一些人比较小,没有标注,但是又存在,所以才有这一步
            if "COCO_val" in data[idx]['dataset']:  
                mask_all = cv2.imread(path_header+'mask2014/val2014_mask_all_'+img_idx+'png', 0)
                mask_miss = cv2.imread(path_header+'mask2014/val2014_mask_miss_'+img_idx+'png', 0)
                #print path_header+'mask2014/val2014_mask_miss_'+img_idx+'png'
            elif "COCO" in data[idx]['dataset']:
                mask_all = cv2.imread(path_header+'mask2014/train2014_mask_all_'+img_idx+'png', 0)
                mask_miss = cv2.imread(path_header+'mask2014/train2014_mask_miss_'+img_idx+'png', 0)
                #print path_header+'mask2014/train2014_mask_miss_'+img_idx+'png'
            elif "MPI" in data[idx]['dataset']:
                img_idx = data[idx]['img_paths'][-13:-3];
                #print img_idx
                mask_miss = cv2.imread('dataset/MPI/masks/mask_'+img_idx+'jpg', 0)
                #mask_all = mask_miss

            height = img.shape[0]
            width = img.shape[1]
            if(width < 64):
                img = cv2.copyMakeBorder(img,0,0,0,64-width,cv2.BORDER_CONSTANT,value=(128,128,128))
                print 'saving padded image!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
                cv2.imwrite('padded_img.jpg', img)
                width = 64
                # no modify on width, because we want to keep information
            meta_data = np.zeros(shape=(height,width,1), dtype=np.uint8)
            #print type(img), img.shape
            #print type(meta_data), meta_data.shape
            clidx = 0 # current line index
            # dataset name (string)
            for i in range(len(data[idx]['dataset'])):
                meta_data[clidx][i] = ord(data[idx]['dataset'][i])
                            print 'type()=', type(ord(data[idx]['dataset'][i]))

            # 开始准备mata信息
            clidx = clidx + 1
            # image height, image width
            height_binary = float2bytes(data[idx]['img_height'])
            for i in range(len(height_binary)):
                meta_data[clidx][i] = ord(height_binary[i])
            width_binary = float2bytes(data[idx]['img_width'])
                    print 'type(width_binary)=',type(width_binary)
            for i in range(len(width_binary)):
                meta_data[clidx][4+i] = ord(width_binary[i])
            clidx = clidx + 1
            # (a) isValidation(uint8), numOtherPeople (uint8), people_index (uint8), annolist_index (float), writeCount(float), totalWriteCount(float)
            meta_data[clidx][0] = data[idx]['isValidation']
            meta_data[clidx][1] = data[idx]['numOtherPeople']
            meta_data[clidx][2] = data[idx]['people_index']
                    print 'type() =', type(data[idx]['isValidation'])
                    print 'data numOther = ',data[idx]['numOtherPeople']

            annolist_index_binary = float2bytes(data[idx]['annolist_index'])
            for i in range(len(annolist_index_binary)): # 3,4,5,6
                meta_data[clidx][3+i] = ord(annolist_index_binary[i])
            count_binary = float2bytes(float(writeCount)) # note it's writecount instead of count!
            for i in range(len(count_binary)):
                meta_data[clidx][7+i] = ord(count_binary[i])
            totalWriteCount_binary = float2bytes(float(totalWriteCount))
            for i in range(len(totalWriteCount_binary)):
                meta_data[clidx][11+i] = ord(totalWriteCount_binary[i])
            nop = int(data[idx]['numOtherPeople'])
            clidx = clidx + 1
            # (b) objpos_x (float), objpos_y (float)
            objpos_binary = float2bytes(data[idx]['objpos'])
            for i in range(len(objpos_binary)):
                meta_data[clidx][i] = ord(objpos_binary[i])
            clidx = clidx + 1
            # (c) scale_provided (float)
            scale_provided_binary = float2bytes(data[idx]['scale_provided'])
            for i in range(len(scale_provided_binary)):
                meta_data[clidx][i] = ord(scale_provided_binary[i])
            clidx = clidx + 1
            # (d) joint_self (3*16) (float) (3 line)
            joints = np.asarray(data[idx]['joint_self']).T.tolist() # transpose to 3*16
            for i in range(len(joints)):
                row_binary = float2bytes(joints[i])
                for j in range(len(row_binary)):
                    meta_data[clidx][j] = ord(row_binary[j])
                clidx = clidx + 1
            # (e) check nop, prepare arrays
                    print 'nop=',nop
            if(nop!=0):
                if(nop==1):
                    joint_other = [data[idx]['joint_others']]
                    objpos_other = [data[idx]['objpos_other']]
                    scale_provided_other = [data[idx]['scale_provided_other']]
                                    print 'joint_other=',joint_other
                else:
                    joint_other = data[idx]['joint_others']
                    objpos_other = data[idx]['objpos_other']
                    scale_provided_other = data[idx]['scale_provided_other']
                                    print 'joint_others2 =', joint_other
                # (f) objpos_other_x (float), objpos_other_y (float) (nop lines)
                for i in range(nop):
                    objpos_binary = float2bytes(objpos_other[i])
                    for j in range(len(objpos_binary)):
                        meta_data[clidx][j] = ord(objpos_binary[j])
                    clidx = clidx + 1
                # (g) scale_provided_other (nop floats in 1 line)
                scale_provided_other_binary = float2bytes(scale_provided_other)
                for j in range(len(scale_provided_other_binary)):
                    meta_data[clidx][j] = ord(scale_provided_other_binary[j])
                clidx = clidx + 1
                # (h) joint_others (3*16) (float) (nop*3 lines)
                for n in range(nop):
                    joints = np.asarray(joint_other[n]).T.tolist() # transpose to 3*16
                                    print 'joints=',joints
                                    print 'joint_other[n]=', joint_other[n]
                    for i in range(len(joints)):
                        row_binary = float2bytes(joints[i])
                        for j in range(len(row_binary)):
                            meta_data[clidx][j] = ord(row_binary[j])
                        clidx = clidx + 1

            # print meta_data[0:12,0:48]
            # total 7+4*nop lines
            # lmdb排列的顺序一定要记清楚,这个在读取数据的时候很重要,在C++代码中相关联的就是指针的偏移量
            if "COCO" in data[idx]['dataset']:
                img4ch = np.concatenate((img, meta_data, mask_miss[...,None], mask_all[...,None]), axis=2)
                #img4ch = np.concatenate((img, meta_data, mask_miss[...,None]), axis=2)
            elif "MPI" in data[idx]['dataset']:
                img4ch = np.concatenate((img, meta_data, mask_miss[...,None]), axis=2)

            img4ch = np.transpose(img4ch, (2, 0, 1))
            print img4ch.shape

            datum = caffe.io.array_to_datum(img4ch, label=0)
            key = '%07d' % writeCount
            txn.put(key, datum.SerializeToString())
            if(writeCount % 1000 == 0):
                txn.commit()
                txn = env.begin(write=True)
            print '%d/%d/%d/%d' % (count,writeCount,idx,numSample)
            writeCount = writeCount + 1

        txn.commit()
        env.close()

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
        86
        87
        88
        89
        90
        91
        92
        93
        94
        95
        96
        97
        98
        99
        100
        101
        102
        103
        104
        105
        106
        107
        108
        109
        110
        111
        112
        113
        114
        115
        116
        117
        118
        119
        120
        121
        122
        123
        124
        125
        126
        127
        128
        129
        130
        131
        132
        133
        134
        135
        136
        137
        138
        139
        140
        141
        142
        143
        144
        145
        146
        147
        148
        149
        150
        151
        152
        153
        154
        155
        156
        157
        158
        159
        160
        161
        162
        163
        164
        165
        166
        167
        168
        169
        170
        171
        172
        173
        174
        175
        176
        177
        178
        179
        180
        181
        182
        183
        184
        185
        186
        187
        188
        189

    在上述Python代码过后,就会生成训练所需要的LMDB文件,在实际的使用过程中,需要重新写caffe的data_layer,关于caffe的data_layer ,可以参考我之前的一篇博客: http://mp.blog.csdn.net/mdeditor/77987504

    下面是cpm_data_layer和cpm_data_transformer,其实cpm_data_layer主要就是layer的建立,主要的数据转化都是在cpm_data_transformer中完成的。
    先看cpm_data_layer的setup函数(代码有些细微地方我可能改过):

    template <typename Dtype>
    void CPMDataLayer<Dtype>::DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
          const vector<Blob<Dtype>*>& top) {
      cpm_data_transformer_.reset(
         new CPMDataTransformer<Dtype>(cpm_transform_param_, this->phase_));
      cpm_data_transformer_->InitRand();


      // Read a data point, and use it to initialize the top blob.
      Datum& datum = *(reader_.full().peek());
      LOG(INFO) << datum.height() << " " << datum.width() << " " << datum.channels();

      bool force_color = this->layer_param_.data_param().force_encoded_color();
      if ((force_color && DecodeDatum(&datum, true)) ||
          DecodeDatumNative(&datum)) {
        LOG(INFO) << "Decoding Datum";
      }

      // image
      const int crop_size = this->layer_param_.cpm_transform_param().crop_size();
      const int batch_size = this->layer_param_.data_param().batch_size();
      if (crop_size > 0) {
        // top[0]->Reshape(batch_size, datum.channels(), crop_size, crop_size);
        // for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
        //   this->prefetch_[i].data_.Reshape(batch_size, datum.channels(), crop_size, crop_size);
        // }
        // //this->transformed_data_.Reshape(1, 4, crop_size, crop_size);
        // this->transformed_data_.Reshape(1, 6, crop_size, crop_size);
      }
      else {
        const int height = this->phase_ != TRAIN ? datum.height() :
          this->layer_param_.cpm_transform_param().crop_size_y();
        const int width = this->phase_ != TRAIN ? datum.width() :
          this->layer_param_.cpm_transform_param().crop_size_x();
        LOG(INFO) << "PREFETCH_COUNT is " << this->PREFETCH_COUNT;  // asynchronously if to GPU memory
        top[0]->Reshape(batch_size, datum.channels(), height, width);
        for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
          this->prefetch_[i].data_.Reshape(batch_size, datum.channels(), height, width);   // 10,6,368,368
        }
        //this->transformed_data_.Reshape(1, 4, height, width);
        this->transformed_data_.Reshape(1, datum.channels(), height, width);  // 1,6,368,368
      }
      LOG(INFO) << "output data size: " << top[0]->num() << ","              
          << top[0]->channels() << "," << top[0]->height() << ","
          << top[0]->width();   // 10,6,368,368

      // label
      if (this->output_labels_) {
        const int stride = this->layer_param_.cpm_transform_param().stride();  // 8,重要
        const int height = this->phase_ != TRAIN ? datum.height() :
          this->layer_param_.cpm_transform_param().crop_size_y();
        const int width = this->phase_ != TRAIN ? datum.width() :
          this->layer_param_.cpm_transform_param().crop_size_x();

        int num_parts = this->layer_param_.cpm_transform_param().num_parts();  // 56
        top[1]->Reshape(batch_size, 2*(num_parts+1), height/stride, width/stride);
        for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
          this->prefetch_[i].label_.Reshape(batch_size, 2*(num_parts+1), height/stride, width/stride);  // 10,114,46,46
        }
        this->transformed_label_.Reshape(1, 2*(num_parts+1), height/stride, width/stride);  // 1,114,46,46
      }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62

    在这个函数中,主要是就一些超参数的读取,和数据输出格式的规定。
    关键的是load_batch 函数,我截取了一部分:

        // Apply data transformations (mirror, scale, crop...)
        timer.Start();
        const int offset_data = batch->data_.offset(item_id);
        const int offset_label = batch->label_.offset(item_id);
        this->transformed_data_.set_cpu_data(top_data + offset_data);
        this->transformed_label_.set_cpu_data(top_label + offset_label);
        if (datum.encoded()) {
          this->cpm_data_transformer_->Transform(cv_img, &(this->transformed_data_));
        } else {
          this->cpm_data_transformer_->Transform_nv(datum,
            &(this->transformed_data_),
            &(this->transformed_label_), cnt);
          ++cnt;
        }
        // if (this->output_labels_) {
        //   top_label[item_id] = datum.label();
        // }
        trans_time += timer.MicroSeconds();

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18

    这里调用Transform和Transform_nv 就进入了cpm_data_transformer文件。
    下一篇写cpm_data_transformer。
    https://blog.csdn.net/u011956147/article/details/79292026

    展开全文
  • openpose训练代码(一): http://blog.csdn.net/u011956147/article/details/79292026   openpose训练代码(二):...在上一篇openpose训练代码(一) 中讲到cpm_data_transformer,其实这个文件...

    openpose训练代码(一): http://blog.csdn.net/u011956147/article/details/79292026  
    openpose训练代码(二):http://blog.csdn.net/u011956147/article/details/79292734

    在上一篇openpose训练代码(一) 中讲到cpm_data_transformer,其实这个文件才是包含数据处理核心代码的文件,在上一篇博客提高Transform_nv函数,我们先来看看Transform_nv函数:

    template<typename Dtype> void CPMDataTransformer<Dtype>::Transform_nv(const Datum& datum, Blob<Dtype>* transformed_data, Blob<Dtype>* transformed_label, int cnt) {
      //std::cout << "Function 2 is used"; std::cout.flush();
      const int datum_channels = datum.channels();
      //const int datum_height = datum.height();
      //const int datum_width = datum.width();

      const int im_channels = transformed_data->channels();
      //const int im_height = transformed_data->height();
      //const int im_width = transformed_data->width();
      const int im_num = transformed_data->num();

      //const int lb_channels = transformed_label->channels();
      //const int lb_height = transformed_label->height();
      //const int lb_width = transformed_label->width();
      const int lb_num = transformed_label->num();

      //LOG(INFO) << "image shape: " << transformed_data->num() << " " << transformed_data->channels() << " " 
      //                             << transformed_data->height() << " " << transformed_data->width();
      //LOG(INFO) << "label shape: " << transformed_label->num() << " " << transformed_label->channels() << " " 
      //                             << transformed_label->height() << " " << transformed_label->width();

      CHECK_EQ(datum_channels, 6);
      CHECK_EQ(im_channels, 6);

      ///CHECK_EQ(im_channels, 4);
      //CHECK_EQ(datum_channels, 4);

      CHECK_EQ(im_num, lb_num);
      //CHECK_LE(im_height, datum_height);
      //CHECK_LE(im_width, datum_width);
      CHECK_GE(im_num, 1);

      //const int crop_size = param_.crop_size();

      // if (crop_size) {
      //   CHECK_EQ(crop_size, im_height);
      //   CHECK_EQ(crop_size, im_width);
      // } else {
      //   CHECK_EQ(datum_height, im_height);
      //   CHECK_EQ(datum_width, im_width);
      // }

      Dtype* transformed_data_pointer = transformed_data->mutable_cpu_data();
      Dtype* transformed_label_pointer = transformed_label->mutable_cpu_data();
      CPUTimer timer;
      timer.Start();
      Transform_nv(datum, transformed_data_pointer, transformed_label_pointer, cnt); //call function 1
      VLOG(2) << "Transform_nv: " << timer.MicroSeconds() / 1000.0  << " ms";
    }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849

    这个函数主要就是得到lmdb的一些参数,比如datum_channels,im_channels 等,转而调用Transform_nv函数

    template<typename Dtype> void CPMDataTransformer<Dtype>::Transform_nv(const Datum& datum, Dtype* transformed_data, Dtype* transformed_label, int cnt) {
      ...
    }123

    data是lmdb的首地址,datum_channels,datum_height ,datum_width 分别是之前python代码确定的每页的尺寸,mask_miss 和mask_all全1的矩阵,为后续所用做准备。

      const string& data = datum.data();
      const int datum_channels = datum.channels();
      const int datum_height = datum.height();
      const int datum_width = datum.width();
      // To do: make this a parameter in caffe.proto
      //const int mode = 5; //related to datum.channels();
      const int mode = 5;

      //const int crop_size = param_.crop_size();
      //const Dtype scale = param_.scale();
      //const bool do_mirror = param_.mirror() && Rand(2);
      //const bool has_mean_file = param_.has_mean_file();
      const bool has_uint8 = data.size() > 0;
      //const bool has_mean_values = mean_values_.size() > 0;
      int crop_x = param_.crop_size_x();
      int crop_y = param_.crop_size_y();

      CHECK_GT(datum_channels, 0);
      //CHECK_GE(datum_height, crop_size);
      //CHECK_GE(datum_width, crop_size);
      CPUTimer timer1;
      timer1.Start();
      //before any transformation, get the image from datum
      Mat img = Mat::zeros(datum_height, datum_width, CV_8UC3);
      Mat mask_all, mask_miss;
      if(mode >= 5){
        mask_miss = Mat::ones(datum_height, datum_width, CV_8UC1);
      }
      if(mode == 6){
        mask_all = Mat::zeros(datum_height, datum_width, CV_8UC1);
      }12345678910111213141516171819202122232425262728293031

    读取原始图片数据保存在rbg中,以及读取mask_miss 和 mask_all,如下: 
    offset = img.rows * img.cols,为指针偏移量,和python文件一一对应。

      int offset = img.rows * img.cols;
      int dindex;
      Dtype d_element;
      for (int i = 0; i < img.rows; ++i) {
        for (int j = 0; j < img.cols; ++j) {
          Vec3b& rgb = img.at<Vec3b>(i, j);
          for(int c = 0; c < 3; c++){
            dindex = c*offset + i*img.cols + j;
            if (has_uint8)
              d_element = static_cast<Dtype>(static_cast<uint8_t>(data[dindex]));
            else
              d_element = datum.float_data(dindex);
            rgb[c] = d_element;
          }

          if(mode >= 5){
            dindex = 4*offset + i*img.cols + j;
            if (has_uint8)
              d_element = static_cast<Dtype>(static_cast<uint8_t>(data[dindex]));
            else
              d_element = datum.float_data(dindex);
            if (round(d_element/255)!=1 && round(d_element/255)!=0){
              cout << d_element << " " << round(d_element/255) << endl;
            }
            mask_miss.at<uchar>(i, j) = d_element; //round(d_element/255);
          }

          if(mode == 6){
            dindex = 5*offset + i*img.cols + j;
            if (has_uint8)
              d_element = static_cast<Dtype>(static_cast<uint8_t>(data[dindex]));
            else
              d_element = datum.float_data(dindex);
            mask_all.at<uchar>(i, j) = d_element;
          }
        }
      }
      VLOG(2) << "  rgb[:] = datum: " << timer1.MicroSeconds()/1000.0 << " ms";
      timer1.Start();123456789101112131415161718192021222324252627282930313233343536373839

    接下来开始读meta文件,就是存储的关键点和尺寸信息,其中关键的是ReadMetaData函数,这个函数就是完全按照python写入格式来读的,所以,一定要理清楚python代码的逻辑,不然,这里很容易混乱,同时,这里有一个小的技巧就是转换了关键点的顺序,TransformMetaJoints函数实现这一功能,其实就是为了和MPII数据集对应,我的理解是方便transfer 权重,代码如下:

      //color, contract
      if(param_.do_clahe())
        clahe(img, clahe_tileSize, clahe_clipLimit);
      if(param_.gray() == 1){
        cv::cvtColor(img, img, CV_BGR2GRAY);
        cv::cvtColor(img, img, CV_GRAY2BGR);
      }
      VLOG(2) << "  color: " << timer1.MicroSeconds()/1000.0 << " ms";
      timer1.Start();

      int offset3 = 3 * offset;
      int offset1 = datum_width;
      int stride = param_.stride();
      ReadMetaData(meta, data, offset3, offset1);
      if(param_.transform_body_joint()) // we expect to transform body joints, and not to transform hand joints
        TransformMetaJoints(meta);

      VLOG(2) << "  ReadMeta+MetaJoints: " << timer1.MicroSeconds()/1000.0 << " ms";123456789101112131415161718

    读取到原始数据后,接下来做的就是数据增广,原始代码主要做了如下几种数据增广:scale、rotate、crop、flip;具体实现如下,没做一个都是叠加在原来的基础上,这里在做数据增广的时候,用到了原图scale的信息:

      //Start transforming
      Mat img_aug = Mat::zeros(crop_y, crop_x, CV_8UC3);
      Mat mask_miss_aug, mask_all_aug ;
      //Mat mask_miss_aug = Mat::zeros(crop_y, crop_x, CV_8UC1);
      //Mat mask_all_aug = Mat::zeros(crop_y, crop_x, CV_8UC1);
      Mat img_temp, img_temp2, img_temp3; //size determined by scale
      VLOG(2) << "   input size (" << img.cols << ", " << img.rows << ")"; 
      // We only do random transform as augmentation when training.
      if (phase_ == TRAIN) {
        as.scale = augmentation_scale(img, img_temp, mask_miss, mask_all, meta, mode);
        //LOG(INFO) << meta.joint_self.joints.size();
        //LOG(INFO) << meta.joint_self.joints[0];
        as.degree = augmentation_rotate(img_temp, img_temp2, mask_miss, mask_all, meta, mode);
        //LOG(INFO) << meta.joint_self.joints.size();
        //LOG(INFO) << meta.joint_self.joints[0];
        if(0 && param_.visualize()) 
          visualize(img_temp2, meta, as);
        as.crop = augmentation_croppad(img_temp2, img_temp3, mask_miss, mask_miss_aug, mask_all, mask_all_aug, meta, mode);
        //LOG(INFO) << meta.joint_self.joints.size();
        //LOG(INFO) << meta.joint_self.joints[0];
        if(0 && param_.visualize()) 
          visualize(img_temp3, meta, as);
        as.flip = augmentation_flip(img_temp3, img_aug, mask_miss_aug, mask_all_aug, meta, mode);
        //LOG(INFO) << meta.joint_self.joints.size();
        //LOG(INFO) << meta.joint_self.joints[0];
        if(param_.visualize()) 
          visualize(img_aug, meta, as);

        // imshow("img_aug", img_aug);
        // Mat label_map = mask_miss_aug;
        // applyColorMap(label_map, label_map, COLORMAP_JET);
        // addWeighted(label_map, 0.5, img_aug, 0.5, 0.0, label_map);
        // imshow("mask_miss_aug", label_map);

        if (mode > 4){
          resize(mask_miss_aug, mask_miss_aug, Size(), 1.0/stride, 1.0/stride, INTER_CUBIC);
        }
        if (mode > 5){
          resize(mask_all_aug, mask_all_aug, Size(), 1.0/stride, 1.0/stride, INTER_CUBIC);
        }
      }
      else {
        img_aug = img.clone();
        as.scale = 1;
        as.crop = Size();
        as.flip = 0;
        as.degree = 0;
      }
      VLOG(2) << "  Aug: " << timer1.MicroSeconds()/1000.0 << " ms";
      timer1.Start();1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

    数据增广过后就是归一化,和准备label文件,有一点不同的地方就是负责背景关键点的那一个label使用的是mask_miss信息,同时,把输入归一化到  [-0.5, 0.5] 具体如下:

      for (int i = 0; i < img_aug.rows; ++i) {
        for (int j = 0; j < img_aug.cols; ++j) {
          Vec3b& rgb = img_aug.at<Vec3b>(i, j);
          transformed_data[0*offset + i*img_aug.cols + j] = (rgb[0] - 128)/256.0;
          transformed_data[1*offset + i*img_aug.cols + j] = (rgb[1] - 128)/256.0;
          transformed_data[2*offset + i*img_aug.cols + j] = (rgb[2] - 128)/256.0;
        }
      }

      // label size is image size/ stride
      if (mode > 4){
        for (int g_y = 0; g_y < grid_y; g_y++){
          for (int g_x = 0; g_x < grid_x; g_x++){
            for (int i = 0; i < np; i++){
              float weight = float(mask_miss_aug.at<uchar>(g_y, g_x)) /255; //mask_miss_aug.at<uchar>(i, j); 
              if (meta.joint_self.isVisible[i] != 3){
                transformed_label[i*channelOffset + g_y*grid_x + g_x] = weight;
              }
            }  
            // background channel
            if(mode == 5){
              transformed_label[np*channelOffset + g_y*grid_x + g_x] = float(mask_miss_aug.at<uchar>(g_y, g_x)) /255;
            }
            if(mode > 5){
              transformed_label[np*channelOffset + g_y*grid_x + g_x] = 1;
              transformed_label[(2*np+1)*channelOffset + g_y*grid_x + g_x] = float(mask_all_aug.at<uchar>(g_y, g_x)) /255;
            }
          }
        }
      }  123456789101112131415161718192021222324252627282930

    做完上面的工作,把图片数据准备好,背景关键点准备好,就剩下其它关键点和PAF的label了,主要是在generateLabelMap函数中完成。

      //putGaussianMaps(transformed_data + 3*offset, meta.objpos, 1, img_aug.cols, img_aug.rows, param_.sigma_center());
      //LOG(INFO) << "image transformation done!";
      generateLabelMap(transformed_label, img_aug, meta);

      VLOG(2) << "  putGauss+genLabel: " << timer1.MicroSeconds()/1000.0 << " ms";
      //starts to visualize everything (transformed_data in 4 ch, label) fed into conv1
      //if(param_.visualize()){
        //dumpEverything(transformed_data, transformed_label, meta);
      //}123456789

    具体的,我们来看一下generateLabelMap函数,大概的说来,主要就是做两件事,其一是在每个关键点部位放置高斯响应,其二就是在有连接的关键点之间放vector,更具体的细节,可以去查阅源代码,这里不再做更为详细的说明:

    template<typename Dtype>
    void CPMDataTransformer<Dtype>::generateLabelMap(Dtype* transformed_label, Mat& img_aug, MetaData meta) {
      int rezX = img_aug.cols;
      int rezY = img_aug.rows;
      int stride = param_.stride();
      int grid_x = rezX / stride;
      int grid_y = rezY / stride;
      int channelOffset = grid_y * grid_x;
      int mode = 5; // TO DO: make this as a parameter

      for (int g_y = 0; g_y < grid_y; g_y++){
        for (int g_x = 0; g_x < grid_x; g_x++){
          for (int i = np+1; i < 2*(np+1); i++){
            if (mode == 6 && i == (2*np + 1))
              continue;
            transformed_label[i*channelOffset + g_y*grid_x + g_x] = 0;
          }
        }
      }

      if (np == 56){
        for (int i = 0; i < 18; i++){
          Point2f center = meta.joint_self.joints[i];
          if(meta.joint_self.isVisible[i] <= 1){
            putGaussianMaps(transformed_label + (i+np+39)*channelOffset, center, param_.stride(), 
                            grid_x, grid_y, param_.sigma()); //self
          }
          for(int j = 0; j < meta.numOtherPeople; j++){ //for every other person
            Point2f center = meta.joint_others[j].joints[i];
            if(meta.joint_others[j].isVisible[i] <= 1){
              putGaussianMaps(transformed_label + (i+np+39)*channelOffset, center, param_.stride(), 
                              grid_x, grid_y, param_.sigma());
            }
          }
        }

        int mid_1[19] = {2, 9,  10, 2,  12, 13, 2, 3, 4, 3,  2, 6, 7, 6,  2, 1,  1,  15, 16};
        int mid_2[19] = {9, 10, 11, 12, 13, 14, 3, 4, 5, 17, 6, 7, 8, 18, 1, 15, 16, 17, 18};
        int thre = 1;

        for(int i=0;i<19;i++){
          Mat count = Mat::zeros(grid_y, grid_x, CV_8UC1);
          Joints jo = meta.joint_self;
          if(jo.isVisible[mid_1[i]-1]<=1 && jo.isVisible[mid_2[i]-1]<=1){
            //putVecPeaks
            putVecMaps(transformed_label + (np+ 1+ 2*i)*channelOffset, transformed_label + (np+ 2+ 2*i)*channelOffset, 
                      count, jo.joints[mid_1[i]-1], jo.joints[mid_2[i]-1], param_.stride(), grid_x, grid_y, param_.sigma(), thre); //self
          }

          for(int j = 0; j < meta.numOtherPeople; j++){ //for every other person
            Joints jo2 = meta.joint_others[j];
            if(jo2.isVisible[mid_1[i]-1]<=1 && jo2.isVisible[mid_2[i]-1]<=1){
              //putVecPeaks
              putVecMaps(transformed_label + (np+ 1+ 2*i)*channelOffset, transformed_label + (np+ 2+ 2*i)*channelOffset, 
                      count, jo2.joints[mid_1[i]-1], jo2.joints[mid_2[i]-1], param_.stride(), grid_x, grid_y, param_.sigma(), thre); //self
            }
          }
        }

        //put background channel
        for (int g_y = 0; g_y < grid_y; g_y++){
          for (int g_x = 0; g_x < grid_x; g_x++){
            float maximum = 0;
            //second background channel
            for (int i = np+39; i < np+57; i++){
              maximum = (maximum > transformed_label[i*channelOffset + g_y*grid_x + g_x]) ? maximum : transformed_label[i*channelOffset + g_y*grid_x + g_x];
            }
            transformed_label[(2*np+1)*channelOffset + g_y*grid_x + g_x] = max(1.0-maximum, 0.0);
          }
        }
        //LOG(INFO) << "background put";
      }

      else if (np == 43){
        for (int i = 0; i < 15; i++){
          Point2f center = meta.joint_self.joints[i];
          if(meta.joint_self.isVisible[i] <= 1){
            putGaussianMaps(transformed_label + (i+np+29)*channelOffset, center, param_.stride(), 
                            grid_x, grid_y, param_.sigma()); //self
          }
          for(int j = 0; j < meta.numOtherPeople; j++){ //for every other person
            Point2f center = meta.joint_others[j].joints[i];
            if(meta.joint_others[j].isVisible[i] <= 1){
              putGaussianMaps(transformed_label + (i+np+29)*channelOffset, center, param_.stride(), 
                              grid_x, grid_y, param_.sigma());
            }
          }
        }

        int mid_1[14] = {0, 1, 2, 3, 1, 5, 6, 1, 14, 8, 9,  14, 11, 12};
        int mid_2[14] = {1, 2, 3, 4, 5, 6, 7, 14, 8, 9, 10, 11, 12, 13};
        int thre = 1;

        for(int i=0;i<14;i++){
          Mat count = Mat::zeros(grid_y, grid_x, CV_8UC1);
          Joints jo = meta.joint_self;
          if(jo.isVisible[mid_1[i]]<=1 && jo.isVisible[mid_2[i]]<=1){
            //putVecPeaks
            putVecMaps(transformed_label + (np+ 1+ 2*i)*channelOffset, transformed_label + (np+ 2+ 2*i)*channelOffset, 
                      count, jo.joints[mid_1[i]], jo.joints[mid_2[i]], param_.stride(), grid_x, grid_y, param_.sigma(), thre); //self
          }

          for(int j = 0; j < meta.numOtherPeople; j++){ //for every other person
            Joints jo2 = meta.joint_others[j];
            if(jo2.isVisible[mid_1[i]]<=1 && jo2.isVisible[mid_2[i]]<=1){
              //putVecPeaks
              putVecMaps(transformed_label + (np+ 1+ 2*i)*channelOffset, transformed_label + (np+ 2+ 2*i)*channelOffset, 
                      count, jo2.joints[mid_1[i]], jo2.joints[mid_2[i]], param_.stride(), grid_x, grid_y, param_.sigma(), thre); //self
            }
          }
        }

        //put background channel
        for (int g_y = 0; g_y < grid_y; g_y++){
          for (int g_x = 0; g_x < grid_x; g_x++){
            float maximum = 0;
            //second background channel
            for (int i = np+29; i < np+44; i++){
              maximum = (maximum > transformed_label[i*channelOffset + g_y*grid_x + g_x]) ? maximum : transformed_label[i*channelOffset + g_y*grid_x + g_x];
            }
            transformed_label[(2*np+1)*channelOffset + g_y*grid_x + g_x] = max(1.0-maximum, 0.0);
          }
        }
        //LOG(INFO) << "background put";
      }

      //visualize
      if(1 && param_.visualize()){
        Mat label_map;
        for(int i = 0; i < 2*(np+1); i++){      
          label_map = Mat::zeros(grid_y, grid_x, CV_8UC1);
          //int MPI_index = MPI_to_ours[i];
          //Point2f center = meta.joint_self.joints[MPI_index];
          for (int g_y = 0; g_y < grid_y; g_y++){
            //printf("\n");
            for (int g_x = 0; g_x < grid_x; g_x++){
              label_map.at<uchar>(g_y,g_x) = (int)(transformed_label[i*channelOffset + g_y*grid_x + g_x]*255);
              //printf("%f ", transformed_label_entry[g_y*grid_x + g_x]*255);
            }
          }
          resize(label_map, label_map, Size(), stride, stride, INTER_LINEAR);
          applyColorMap(label_map, label_map, COLORMAP_JET);
          addWeighted(label_map, 0.5, img_aug, 0.5, 0.0, label_map);

          //center = center * (1.0/(float)param_.stride());
          //circle(label_map, center, 3, CV_RGB(255,0,255), -1);
          char imagename [100];
          sprintf(imagename, "augment_%04d_label_part_%02d.jpg", meta.write_number, i);
          //LOG(INFO) << "filename is " << imagename;
          imwrite(imagename, label_map);
        }

      }
    }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154

    原文链接:http://blog.csdn.net/u011956147/article/details/79292734
    --------------------- 
    作者:FelixFuu 
    来源:CSDN 
    原文:https://blog.csdn.net/u011956147/article/details/79292734 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • openpose训练代码(一)

    万次阅读 热门讨论 2018-02-08 17:30:32
    openpose训练代码(一): http://blog.csdn.net/u011956147/article/details/79292026 openpose训练代码(二):http://blog.csdn.net/u011956147/article/details/79292734 openspoe本身是很繁杂的,包含了...
  • openpose pytorch代码分析

    2018-05-04 00:03:00
    github: https://github.com/tensorboy/pytorch_Realtime_Multi-Person_Pose_Estimation 1 # -*- coding: utf-8 -* 2 import os 3 import re 4 import sys 5 import cv2 ... 7 imp...
  • Openpose的简单使用

    2019-01-08 13:34:47
    可以直接使用的openpose代码,操作简单,已经将模型下载好并放进工程了,可以直接运行。
  • OpenPose安装编译

    2020-04-10 18:09:33
    OpenPose安装编译说明获取github中最新的OpenPose代码编译caffe安装GPU驱动程序获取训练原本集编译OpenPose验证安装成功生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表...
  • OpenPose

    2020-06-12 17:58:16
    4月23日,卡内基梅隆大学感知计算实验室将其打造的OpenPose——一套可以读懂人类肢体语言的库放在了GitHub上,并于6月和7月相继开源了核心的面部和手部识别源代码。源码网址:...
  • OpenPose Demo

    2020-09-04 10:41:29
    OpenPose Demo 忘记OpenPose代码,只编译库并使用演示./build/examples/openpose/openpose.bin
  • openpose1.7.0

    2020-12-01 11:26:26
    openpose1.7.0源码、openpose1.7.0_gpu及openpose1.7.0_cpu编译后代码(官网源码搬运分享,可自行编译VS使用)
  • 这是基于openpose的肢体识别项目中用于编译的源代码,上传到这边方便以后取用,注意,这个是openpose v1.3
  • 在自己C++代码中使用openpose的简单示例,有助于灵活的使用openpose而不是简单的运行官方demo
  • tensorflow——openpose代码和原理分析 https://blog.csdn.net/eereere/article/details/81151756 Tensorflow——用openpose进行人体骨骼检测 https://blog.csdn.net/eereere/article/details/80176007 ...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 226
精华内容 90
关键字:

openpose代码