TensorFlow Faster R-CNN ckpt模型转pb模型

   tensorflow版本的faster r-cnn模型保存有两种类型:ckpt模型和pb模型,ckpt模型适合训练阶段,pb模型适合正式生产环境。

1、输入和输出tensor的确定

输入和输出tensor的确定是ckpt模型转pb模型的关键,因为faster r-cnn模型比较大,尝试过使用tensorboard将模型结果可视化出来,但是还是很难找到输出tensor,后来只好通过faster r-cnn测试脚本demo.py来逐句的看源代码了。最终确定了输入和输出tensor。

    # 定义输出的张量名称
    input_image_tensor = sess.graph.get_tensor_by_name("Placeholder:0")
    tensor_info = sess.graph.get_tensor_by_name("Placeholder_1:0")

    biasadd = sess.graph.get_tensor_by_name("vgg_16_3/cls_score/BiasAdd:0")
    score = sess.graph.get_tensor_by_name("vgg_16_3/cls_prob:0")
    bbox = sess.graph.get_tensor_by_name("add:0")
    rois = sess.graph.get_tensor_by_name("vgg_16_1/rois/concat:0")

下面简单介绍一下过程。
调用顺序tools/demo.py->lib/model/test.py->lib/net/network.py
下面我们逐个看这几个脚本
1.1、tools/demo.py
程序开始加载模型、获取图片,然后调用demo()函数开始做识别(我针对自己的应用场景对源代码做了些修改,大体没变)

def demo(sess, net, image_name,out_file):
    """Detect object classes in an image using pre-computed object proposals."""
    im = cv2.imdecode(np.fromfile(image_name,dtype=np.uint8),1)

    # Detect all object classes and regress object bounds
    timer = Timer()
    timer.tic()
    scores, boxes = im_detect(sess, net, im)
    timer.toc()
    print('Detection took {:.3f}s for {:d} object proposals'.format(timer.total_time, boxes.shape[0]))

    # Visualize detections for each class
    CONF_THRESH = 0.8
    NMS_THRESH = 0.3

    im = im[:, :, (2, 1, 0)]
    for cls_ind, cls in enumerate(CLASSES[1:]):
        cls_ind += 1 # because we skipped background
        cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]
        cls_scores = scores[:, cls_ind]
        dets = np.hstack((cls_boxes,cls_scores[:, np.newaxis])).astype(np.float32)
        keep = nms(dets, NMS_THRESH)
        dets = dets[keep, :]
        vis_detections(im,cls, dets,out_file, thresh=CONF_THRESH)
    cv2.imencode('.jpg',im)[1].tofile(out_file)

可以看到这个函数里关键部分就是下面这句,调用了lib/model/test.py里的im_detect()方法,返回的是分数和检测框。

scores, boxes = im_detect(sess, net, im)

1.2、lib/model/test.py

def im_detect(sess, net, im):
  blobs, im_scales = _get_blobs(im)
  assert len(im_scales) == 1, "Only single-image batch implemented"

  im_blob = blobs['data']
  blobs['im_info'] = np.array([im_blob.shape[1], im_blob.shape[2], im_scales[0]], dtype=np.float32)

  _, scores, bbox_pred, rois = net.test_image(sess, blobs['data'], blobs['im_info'])

  boxes = rois[:, 1:5] / im_scales[0]
  scores = np.reshape(scores, [scores.shape[0], -1])
  bbox_pred = np.reshape(bbox_pred, [bbox_pred.shape[0], -1])

  if cfg.TEST.BBOX_REG:
    # Apply bounding-box regression deltas
    box_deltas = bbox_pred
    pred_boxes = bbox_transform_inv(boxes, box_deltas)
    pred_boxes = _clip_boxes(pred_boxes, im.shape)
  else:
    # Simply repeat the boxes, once for each class
    pred_boxes = np.tile(boxes, (1, scores.shape[1]))

  return scores, pred_boxes

im_detect中关键的是下面这一句,通过调用lib/network.py的test_image()方法得到检测得分、检测边框和边框修改正值。

  _, scores, bbox_pred, rois = net.test_image(sess, blobs['data'], blobs['im_info'])

1.3、lib/net/network.py

  # only useful during testing mode
  def test_image(self, sess, image, im_info):

    feed_dict = {self._image: image,
                 self._im_info: im_info}

    cls_score, cls_prob, bbox_pred, rois = sess.run([self._predictions["cls_score"],
                                                     self._predictions['cls_prob'],
                                                     self._predictions['bbox_pred'],
                                                     self._predictions['rois']],
                                                    feed_dict=feed_dict)

    return cls_score, cls_prob, bbox_pred, rois

现在我们要看的就是sess.run这句话,这句是tensorflow开始run模型,输入的参数就是我们想要的,根据run()的调用方式,前面的列表是输出的tensor,后面的feed_dict字典是喂给图的数据和输入的tensor,这里我们可以提取打印一下这几个参数。

  def test_image(self, sess, image, im_info):

    feed_dict = {self._image: image,
                 self._im_info: im_info}

    print("输入tensor")
    print(self._image)
    print(self._im_info)
    print("输出tensor")
    print(self._predictions["cls_score"])
    print(self._predictions["cls_prob"])
    print(self._predictions["bbox_pred"])
    print(self._predictions["rois"])
    cls_score, cls_prob, bbox_pred, rois = sess.run([self._predictions["cls_score"],
                                                     self._predictions['cls_prob'],
                                                     self._predictions['bbox_pred'],
                                                     self._predictions['rois']],
                                                    feed_dict=feed_dict)

    return cls_score, cls_prob, bbox_pred, rois

然后我们来执行一下demo.py做一次目标识别。可以看到下面打印出了tensor

image

至此我们得到了需要的输入和输出的tensor名称。

2、ckpt模型转pb模型

关于如何将ckpt转pb模型,网上已经有很多介绍了, TensoFlow为我们提供了convert_variables_to_constants()方法,该方法可以固化模型结构,将计算图中的变量取值以常量的形式保存,但是这里面有个问题是,faster r-cnn模型在训练阶段输入的tensor有三个,在预测阶段只需要两个,原因是训练阶段需要输入label作为ground truth。测试发现如果使用通用的方式读取ckpt模型然后固化为pb模型需要有3个输入tensor,无法调用,后来在这里找到解决方法https://github.com/endernewton/tf-faster-rcnn/issues/340,他是用demo.py里加载的模型来进行固化的,固化后发现可以正常调用,因此faster r-cnn的固化方法是借用demo.py加载模型的方式,再固化。

if __name__ == '__main__':
    cfg.TEST.HAS_RPN = True  # Use RPN for proposals
    #args = parse_args()

    f_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

    # model path
    # demonet = args.demo_net
    # dataset = args.dataset
    # input_dir = args.input
    # output_dir = args.output
    demonet = "vgg16"
    dataset = "pascal_voc"
    # input_dir = sys.argv[1]
    # output_dir = sys.argv[2]
    input_dir = r"D:\result\ship_tf\input_data"
    output_dir = r"D:\result\ship_tf"
    tfmodel = os.path.join(f_path,'output', demonet, DATASETS[dataset][0], 'default_ship',
                              NETS[demonet][0])

    if not os.path.isfile(tfmodel + '.meta'):
        raise IOError(('{:s} not found.\nDid you download the proper networks from '
                       'our server and place them properly?').format(tfmodel + '.meta'))

    # set config
    tfconfig = tf.ConfigProto(allow_soft_placement=True)
    print(tfconfig)
    tfconfig.gpu_options.allow_growth=True

    # init session
    sess = tf.Session(config=tfconfig)
    # load network
    if demonet == 'vgg16':
        net = vgg16()
    elif demonet == 'res101':
        net = resnetv1(num_layers=101)
    else:
        raise NotImplementedError
    net.create_architecture("TEST", len(CLASSES),
                          tag='default', anchor_scales=[8, 16, 32])
    saver = tf.train.Saver()
    saver.restore(sess, tfmodel)

    #ckpt to pb
    graph = tf.get_default_graph()
    input_graph_def = graph.as_graph_def()
    output_graph = r"D:\result\ship_tf\ship_model_from_demo3.pb"
    output_node_names = "vgg_16_3/cls_prob,add,vgg_16_1/rois/concat,vgg_16_3/cls_score/BiasAdd"
    output_graph_def = graph_util.convert_variables_to_constants(sess, input_graph_def, output_node_names.split(","))
    with tf.gfile.GFile(output_graph, "wb") as f:
        f.write(output_graph_def.SerializeToString())

3、pb模型测试

通过前两步我们确定了模型输入和输出tensor、得到了pb模型,下面就是直接调用pb模型进行目标识别了。(详细代码见demo_pb.py)
3.1、输入数据的准备
先看lib/model/test.py中的im_detect()函数中的下面几行。

  blobs, im_scales = _get_blobs(im)
  assert len(im_scales) == 1, "Only single-image batch implemented"

  im_blob = blobs['data']
  blobs['im_info'] = np.array([im_blob.shape[1], im_blob.shape[2], im_scales[0]], dtype=np.float32)

调用_get_blobs()方法,传入图片路径,得到blobs和im_scales,blobs字典中的data字段是存储重采样后的图片矩阵,然后将重采样后的图片宽和高、重采样比例三个参数存储成info字段。这个blobs[“info”]就是faster r-cnn模型输入的info信息。
3.2、模型预测
这一步主要是加载pb模型和定义输入输出tensor
模型加载

    p_path=""       #pb模型路径
    with tf.Graph().as_default():
        output_graph_def = tf.GraphDef()
        with open(pb_path, "rb") as f:
            output_graph_def.ParseFromString(f.read())
            tf.import_graph_def(output_graph_def, name="")

        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())

确定输入和输出tensor,(下面代码中的im_info是我自己测试的时候写死的一个info信息。)

    # 定义输出的张量名称
    input_image_tensor = sess.graph.get_tensor_by_name("Placeholder:0")
    tensor_info = sess.graph.get_tensor_by_name("Placeholder_1:0")

    biasadd = sess.graph.get_tensor_by_name("vgg_16_3/cls_score/BiasAdd:0")
    score = sess.graph.get_tensor_by_name("vgg_16_3/cls_prob:0")
    bbox = sess.graph.get_tensor_by_name("add:0")
    rois = sess.graph.get_tensor_by_name("vgg_16_1/rois/concat:0")

    im_info = np.array([600.0, 600.0, 2.34375], dtype=np.float32)
    _, scores, bbox_pred, rois = sess.run([biasadd, score, bbox, rois],
                                          feed_dict={input_image_tensor: image, tensor_info: im_info})

3.3、后处理
模型预测得到4个返回值,其中有用的是scores、bbox_pred、rois,分别是检测框分数,修正值,检测框。我们需要对初步得到的监测框做修正。主要代码为
lib/model/test.py中的im_detect()函数

  boxes = rois[:, 1:5] / im_scales[0]
  scores = np.reshape(scores, [scores.shape[0], -1])
  bbox_pred = np.reshape(bbox_pred, [bbox_pred.shape[0], -1])

  if cfg.TEST.BBOX_REG:
    # Apply bounding-box regression deltas
    box_deltas = bbox_pred
    pred_boxes = bbox_transform_inv(boxes, box_deltas)
    pred_boxes = _clip_boxes(pred_boxes, im.shape)
  else:
    # Simply repeat the boxes, once for each class
    pred_boxes = np.tile(boxes, (1, scores.shape[1]))

关键代码是bbox_transform_inv()和_clip_boxes,分别用来修正检测框和裁切超过图片边界的检测框。
通过以上分析我们发现可以直接调用源码里的预处理和后处理阶段的部分。

几个关键点

1、输入输出tensor的确定
2、读取ckpt模型需要使用demo.py

补充

其实做这个工作是为了把深度学习模型集成到我们的项目里,因此需要用C++来调用faster r-cnn模型。下一篇博客会介绍如何用C++来集成深度学习模型进行目标识别。

转自:Faster R-CNN ckpt模型转pb模型及其调用-python版本

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容