目標
在本教程中,您將學習如何
- 轉換 TensorFlow (TF) 分割模型
- 使用 OpenCV 執行轉換後的 TensorFlow 模型
- 獲取 TensorFlow 和 OpenCV DNN 模型的評估結果
我們將透過 DeepLab 架構的示例來探討上述要點。
簡介
透過 OpenCV API 轉換 TensorFlow 分類和分割模型所涉及的關鍵概念幾乎相同,除了圖最佳化階段。將 TensorFlow 模型轉換為 cv.dnn.Net 的初始步驟是獲取凍結的 TF 模型圖。凍結圖定義了模型圖結構與所需變數的保留值的組合,例如權重。通常,凍結圖儲存在 protobuf (.pb) 檔案中。要使用 cv.dnn.readNetFromTensorflow 讀取生成的分割模型 .pb 檔案,需要使用 TF 圖轉換工具修改該圖。
實踐
在本部分中,我們將介紹以下幾點
- 建立 TF 分類模型轉換管道並提供推理
- 評估和測試 TF 分類模型
如果您只想執行評估或測試模型管道,“模型轉換管道”教程部分可以跳過。
模型轉換管道
本小節中的程式碼位於 dnn_model_runner 模組中,可以使用以下行執行
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_deeplab
TensorFlow 分割模型可以在 TensorFlow 研究模型部分中找到,其中包含基於已釋出的研究論文的模型實現。我們將從以下連結檢索包含預訓練 TF DeepLabV3 的存檔
http://download.tensorflow.org/models/deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz
完整的凍結圖獲取管道在 deeplab_retrievement.py 中描述
def get_deeplab_frozen_graph()
models_url = 'http://download.tensorflow.org/models/'
mobilenetv2_voctrainval = 'deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz'
model_link = models_url + mobilenetv2_voctrainval
try:
urllib.request.urlretrieve(model_link, mobilenetv2_voctrainval)
except Exception
print("TF DeepLabV3 未檢索:{}".format(model_link))
return
tf_model_tar = tarfile.open(mobilenetv2_voctrainval)
for model_tar_elem in tf_model_tar.getmembers()
if TF_FROZEN_GRAPH_NAME in os.path.basename(model_tar_elem.name)
tf_model_tar.extract(model_tar_elem, FROZEN_GRAPH_PATH)
tf_model_tar.close()
執行此指令碼後
python -m dnn_model_runner.dnn_conversion.tf.segmentation.deeplab_retrievement
我們將在 deeplab/deeplabv3_mnv2_pascal_trainval 中獲得 frozen_inference_graph.pb。
在使用 OpenCV 載入網路之前,需要最佳化提取的 frozen_inference_graph.pb。為了最佳化圖,我們使用帶有預設引數的 TF TransformGraph
DEFAULT_OPT_GRAPH_NAME = "optimized_frozen_inference_graph.pb"
DEFAULT_INPUTS = "sub_7"
DEFAULT_OUTPUTS = "ResizeBilinear_3"
DEFAULT_TRANSFORMS = "remove_nodes(op=Identity)" \
" merge_duplicate_nodes" \
" strip_unused_nodes" \
" fold_constants(ignore_errors=true)" \
" fold_batch_norms" \
" fold_old_batch_norms"
def optimize_tf_graph(
in_graph,
out_graph=DEFAULT_OPT_GRAPH_NAME,
inputs=DEFAULT_INPUTS,
outputs=DEFAULT_OUTPUTS,
transforms=DEFAULT_TRANSFORMS,
is_manual=True,
was_optimized=True
):
tf_opt_graph = TransformGraph(
tf_graph,
inputs,
outputs,
transforms
)
要執行圖最佳化過程,執行以下行
python -m dnn_model_runner.dnn_conversion.tf.segmentation.tf_graph_optimizer --in_graph deeplab/deeplabv3_mnv2_pascal_trainval/frozen_inference_graph.pb
結果,deeplab/deeplabv3_mnv2_pascal_trainval 目錄將包含 optimized_frozen_inference_graph.pb。
獲得模型圖後,讓我們檢查以下列出的步驟
- 讀取 TF
frozen_inference_graph.pb 圖
- 使用 OpenCV API 讀取最佳化的 TF 凍結圖
- 準備輸入資料
- 提供推理
- 從預測中獲取彩色掩碼
- 視覺化結果
deeplab_graph = read_deeplab_frozen_graph(deeplab_frozen_graph_path)
opencv_net = cv2.dnn.readNetFromTensorflow(opt_deeplab_frozen_graph_path)
print("OpenCV 模型已成功讀取。模型層:\n", opencv_net.getLayerNames())
original_img_shape, tf_input_blob, opencv_input_img = get_processed_imgs("test_data/sem_segm/2007_000033.jpg")
opencv_prediction = get_opencv_dnn_prediction(opencv_net, opencv_input_img)
tf_prediction = get_tf_dnn_prediction(deeplab_graph, tf_input_blob)
pascal_voc_classes, pascal_voc_colors = read_colors_info("test_data/sem_segm/pascal-classes.txt")
opencv_colored_mask = get_colored_mask(original_img_shape, opencv_prediction, pascal_voc_colors)
tf_colored_mask = get_tf_colored_mask(original_img_shape, tf_prediction, pascal_voc_colors)
color_legend = get_legend(pascal_voc_classes, pascal_voc_colors)
cv2.imshow('TensorFlow 彩色掩碼', tf_colored_mask)
cv2.imshow('OpenCV DNN 彩色掩碼', opencv_colored_mask)
cv2.imshow('顏色圖例', color_legend)
為了提供模型推理,我們將使用來自 PASCAL VOC 驗證資料集的下圖

目標分割結果是

對於 PASCAL VOC 顏色解碼及其與預測掩碼的對映,我們還需要 pascal-classes.txt 檔案,其中包含 PASCAL VOC 類的完整列表和相應的顏色。
讓我們以預訓練的 TF DeepLabV3 MobileNetV2 為例,更深入地瞭解每個步驟
- 讀取 TF
frozen_inference_graph.pb 圖
model_graph = tf.Graph()
with tf.io.gfile.GFile(frozen_graph_path, 'rb') as graph_file
tf_model_graph = GraphDef()
tf_model_graph.ParseFromString(graph_file.read())
with model_graph.as_default()
tf.import_graph_def(tf_model_graph, name='')
- 使用 OpenCV API 讀取最佳化的 TF 凍結圖
opencv_net = cv2.dnn.readNetFromTensorflow(opt_deeplab_frozen_graph_path)
- 使用 cv2.dnn.blobFromImage 函式準備輸入資料
input_img = cv2.imread(img_path, cv2.IMREAD_COLOR)
input_img = input_img.astype(np.float32)
tf_preproc_img = cv2.resize(input_img, (513, 513))
tf_preproc_img = cv2.cvtColor(tf_preproc_img, cv2.COLOR_BGR2RGB)
mean = np.array([1.0, 1.0, 1.0]) * 127.5
scale = 1 / 127.5
input_blob = cv2.dnn.blobFromImage(
image=input_img,
scalefactor=scale,
size=(513, 513),
mean=mean,
swapRB=True,
crop=False
)
請注意 cv2.dnn.blobFromImage 函式中的預處理順序。首先,減去平均值,然後將畫素值乘以定義的比例。因此,為了重現 TF 影像預處理管道,我們將 mean 乘以 127.5。另一個重點是 TF DeepLab 的影像預處理。要將影像傳遞到 TF 模型中,我們只需要構建一個合適的形狀,其餘的影像預處理在 feature_extractor.py 中描述,並將自動呼叫。
opencv_net.setInput(preproc_img)
out = opencv_net.forward()
print("OpenCV DNN 分割預測:\n")
print("* 形狀:", out.shape)
out_predictions = np.argmax(out[0], axis=0)
在上述程式碼執行後,我們將獲得以下輸出
OpenCV DNN 分割預測
* shape: (1, 21, 513, 513)
21 個預測通道中的每一個,其中 21 代表 PASCAL VOC 類的數量,都包含機率,指示畫素對應於 PASCAL VOC 類的可能性。
preproc_img = np.expand_dims(preproc_img, 0)
tf_session = Session(graph=model_graph)
input_tensor_name = "ImageTensor:0",
output_tensor_name = "SemanticPredictions:0"
out = tf_session.run(
output_tensor_name,
feed_dict={input_tensor_name: [preproc_img]}
)
print("TF 分割模型預測:\n")
print("* 形狀:", out.shape)
TF 推理結果如下
TF 分割模型預測
* shape: (1, 513, 513)
TensorFlow 預測包含相應 PASCAL VOC 類的索引。
mask_height = segm_mask.shape[0]
mask_width = segm_mask.shape[1]
img_height = original_img_shape[0]
img_width = original_img_shape[1]
processed_mask = np.stack([colors[color_id] for color_id in segm_mask.flatten()])
processed_mask = processed_mask.reshape(mask_height, mask_width, 3)
processed_mask = cv2.resize(processed_mask, (img_width, img_height), interpolation=cv2.INTER_NEAREST).astype(
np.uint8)
processed_mask = cv2.cvtColor(processed_mask, cv2.COLOR_BGR2RGB)
在此步驟中,我們將分割掩碼的機率與預測類的相應顏色進行對映。讓我們看一下結果


colors = np.array(colors)
processed_mask = colors[segm_mask[0]]
img_height = original_img_shape[0]
img_width = original_img_shape[1]
processed_mask = cv2.resize(processed_mask, (img_width, img_height), interpolation=cv2.INTER_NEAREST).astype(
np.uint8)
processed_mask = cv2.cvtColor(processed_mask, cv2.COLOR_BGR2RGB)
結果是

結果,我們得到了兩個相等的分割掩碼。
模型評估
dnn/samples 中提出的 dnn_model_runner 模組允許在 PASCAL VOC 資料集上執行完整的評估管道,並測試 DeepLab MobileNet 模型的執行情況。
評估模式
以下行表示在評估模式下執行模組
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm
該模型將被讀入 OpenCV cv.dnn_Net 物件。TF 和 OpenCV 模型的評估結果(畫素精度、平均 IoU、推理時間)將被寫入日誌檔案。推理時間值也將以圖表形式顯示,以概括獲得的模型資訊。
必要的評估配置在 test_config.py 中定義
@dataclass
class TestSegmConfig
frame_size: int = 500
img_root_dir: str = "./VOC2012"
img_dir: str = os.path.join(img_root_dir, "JPEGImages/")
img_segm_gt_dir: str = os.path.join(img_root_dir, "SegmentationClass/")
segm_val_file: str = os.path.join(img_root_dir, "ImageSets/Segmentation/seg11valid.txt")
colour_file_cls: str = os.path.join(img_root_dir, "ImageSets/Segmentation/pascal-classes.txt")
這些值可以根據選擇的模型管道進行修改。
測試模式
以下行表示在測試模式下執行模組,該模式提供了模型推理的步驟
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm --test True --default_img_preprocess <True/False> --evaluate False
此處 default_img_preprocess 鍵定義您是否要使用一些特定值來引數化模型測試過程,或者使用預設值,例如 scale、mean 或 std。
測試配置在 test_config.py TestSegmModuleConfig 類中表示
@dataclass
class TestSegmModuleConfig
segm_test_data_dir: str = "test_data/sem_segm"
test_module_name: str = "segmentation"
test_module_path: str = "segmentation.py"
input_img: str = os.path.join(segm_test_data_dir, "2007_000033.jpg")
model: str = ""
frame_height: str = str(TestSegmConfig.frame_size)
frame_width: str = str(TestSegmConfig.frame_size)
scale: float = 1.0
mean: List[float] = field(default_factory=lambda: [0.0, 0.0, 0.0])
std: List[float] = field(default_factory=list)
crop: bool = False
rgb: bool = True
classes: str = os.path.join(segm_test_data_dir, "pascal-classes.txt")
預設影像預處理選項在 default_preprocess_config.py 中定義
tf_segm_input_blob = {
"scale": str(1 / 127.5),
"mean": ["127.5", "127.5", "127.5"],
"std": [],
"crop": "False",
"rgb": "True"
}
模型測試的基礎在 samples/dnn/segmentation.py 中表示。segmentation.py 可以獨立執行,並提供轉換後的模型 --input 和為 cv2.dnn.blobFromImage 填充的引數。
要從頭開始重現 “模型轉換管道” 中描述的帶有 dnn_model_runner 的 OpenCV 步驟,請執行以下行
python -m dnn_model_runner.dnn_conversion.tf.segmentation.py_to_py_segm --test True --default_img_preprocess True --evaluate False