diff --git a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/README.md b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/README.md index 7aba491c9d..1b1a28767c 100644 --- a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/README.md +++ b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/README.md @@ -1,6 +1,6 @@ -# Run PaddlePaddle model by OpenCV +# Run PaddlePaddle model using OpenCV -This tutorial shows how to run PaddlePaddle model by opencv. +These two demonstrations show how to inference PaddlePaddle model using OpenCV. ## Environment Setup @@ -10,16 +10,69 @@ pip install paddlehub pip install paddle2onnx ``` -## Run PaddlePaddle model demo +## 1. Run PaddlePaddle ResNet50 using OpenCV -Run the example code as below, +### Run PaddlePaddle model demo + +Run the code sample as follows: ```shell python paddle_resnet50.py ``` -there are 3 part of this execution +There are three parts to the process: -- 1. Export PaddlePaddle ResNet50 model to onnx format; -- 2. Use `cv2.dnn.readNetFromONNX` load model file; -- 3. Preprocess image file and do inference. +1. Export PaddlePaddle ResNet50 model to onnx format. +2. Use `cv2.dnn.readNetFromONNX` to load the model file. +3. Preprocess image file and do the inference. + +## 2. Run PaddleSeg Portrait Segmentation using OpenCV + +### Convert to ONNX Model + +#### 1. Get Paddle Inference model + +For more details, please refer to [PaddleSeg](https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.1/contrib/HumanSeg/README.md). + +```shell +wget https://x2paddle.bj.bcebos.com/inference/models/humanseg_hrnet18_small_v1.zip +unzip humanseg_hrnet18_small_v1.zip +``` + +Notes: + +* The exported model must have a fixed input shape, as dynamic is not supported at this moment. + +#### 2. Convert to ONNX model using paddle2onnx + +To convert the model, use the following command: + +``` +paddle2onnx --model_dir humanseg_hrnet18_small_v1 \ + --model_filename model.pdmodel \ + --params_filename model.pdiparams \ + --opset_version 11 \ + --save_file humanseg_hrnet18_tiny.onnx +``` + +The converted model can be found in the current directory by the name `humanseg_hrnet18_tiny.onnx` . + +### Run PaddleSeg Portrait Segmentation demo + +Run the code sample as follows: + +```shell +python paddle_humanseg.py +``` + +There are three parts to the process: + +1. Use `cv2.dnn.readNetFromONNX` to load the model file. +2. Preprocess image file and do inference. +3. Postprocess image file and visualize. + +The resulting file can be found at `data/result_test_human.jpg` . + +### Portrait segmentation visualization + + diff --git a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/data/result_test_human.jpg b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/data/result_test_human.jpg new file mode 100644 index 0000000000..652b03f3b4 Binary files /dev/null and b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/data/result_test_human.jpg differ diff --git a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_humanseg.py b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_humanseg.py new file mode 100644 index 0000000000..e2ef62eade --- /dev/null +++ b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_humanseg.py @@ -0,0 +1,112 @@ +import os +import paddlehub.vision.transforms as T +import numpy as np +import cv2 as cv + + +def get_color_map_list(num_classes): + """ + Returns the color map for visualizing the segmentation mask, + which can support arbitrary number of classes. + + Args: + num_classes (int): Number of classes. + + Returns: + (list). The color map. + """ + + num_classes += 1 + color_map = num_classes * [0, 0, 0] + for i in range(0, num_classes): + j = 0 + lab = i + while lab: + color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) + color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) + color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) + j += 1 + lab >>= 3 + color_map = color_map[3:] + return color_map + + +def visualize(image, result, save_dir=None, weight=0.6): + """ + Convert predict result to color image, and save added image. + + Args: + image (str): The path of origin image. + result (np.ndarray): The predict result of image. + save_dir (str): The directory for saving visual image. Default: None. + weight (float): The image weight of visual image, and the result weight is (1 - weight). Default: 0.6 + + Returns: + vis_result (np.ndarray): If `save_dir` is None, return the visualized result. + """ + + color_map = get_color_map_list(256) + color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)] + color_map = np.array(color_map).astype("uint8") + # Use OpenCV LUT for color mapping + c1 = cv.LUT(result, color_map[:, 0]) + c2 = cv.LUT(result, color_map[:, 1]) + c3 = cv.LUT(result, color_map[:, 2]) + pseudo_img = np.dstack((c1, c2, c3)) + + im = cv.imread(image) + vis_result = cv.addWeighted(im, weight, pseudo_img, 1 - weight, 0) + + if save_dir is not None: + if not os.path.exists(save_dir): + os.makedirs(save_dir) + image_name = os.path.split(image)[-1] + out_path = os.path.join(save_dir, image_name) + cv.imwrite(out_path, vis_result) + else: + return vis_result + + +def preprocess(image_path): + ''' preprocess input image file to np.ndarray + + Args: + image_path(str): Path of input image file + + Returns: + ProcessedImage(numpy.ndarray): A numpy.ndarray + variable which shape is (1, 3, 192, 192) + ''' + transforms = T.Compose([ + T.Resize((192, 192)), + T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + ], + to_rgb=True) + return np.expand_dims(transforms(image_path), axis=0) + + +if __name__ == '__main__': + img_path = "../../../../data/messi5.jpg" + # load PPSeg Model use cv.dnn + net = cv.dnn.readNetFromONNX('humanseg_hrnet18_tiny.onnx') + # read and preprocess image file + im = preprocess(img_path) + # inference + net.setInput(im) + result = net.forward(['save_infer_model/scale_0.tmp_1']) + # post process + image = cv.imread(img_path) + r, c, _ = image.shape + result = np.argmax(result[0], axis=1).astype(np.uint8) + result = cv.resize(result[0, :, :], + dsize=(c, r), + interpolation=cv.INTER_NEAREST) + + print("grid_image.shape is: ", result.shape) + folder_path = "data" + if not os.path.exists(folder_path): + os.makedirs(folder_path) + file_path = os.path.join(folder_path, '%s.jpg' % "result_test_human") + result_color = visualize(img_path, result) + cv.imwrite(file_path, result_color) + print('%s saved' % file_path) diff --git a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_resnet50.py b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_resnet50.py index b95ce917e6..90c0d26e15 100644 --- a/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_resnet50.py +++ b/samples/dnn/dnn_model_runner/dnn_conversion/paddlepaddle/paddle_resnet50.py @@ -16,15 +16,15 @@ def preprocess(image_path): variable which shape is (1, 3, 224, 224) ''' transforms = T.Compose([ - T.Resize((256, 256)), - T.CenterCrop(224), - T.Normalize(mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225])], - to_rgb=True) + T.Resize((256, 256)), + T.CenterCrop(224), + T.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225])], + to_rgb=True) return np.expand_dims(transforms(image_path), axis=0) -def export_onnx_mobilenetv2(save_path): +def export_onnx_resnet50(save_path): ''' export PaddlePaddle model to ONNX format Args: @@ -35,7 +35,7 @@ def export_onnx_mobilenetv2(save_path): ''' model = hub.Module(name="resnet50_vd_imagenet_ssld") input_spec = paddle.static.InputSpec( - [1, 3, 224, 224], "float32", "image") + [1, 3, 224, 224], "float32", "image") paddle.onnx.export(model, save_path, input_spec=[input_spec], opset_version=10) @@ -45,9 +45,9 @@ if __name__ == '__main__': save_path = './resnet50' image_file = './data/cat.jpg' labels = open('./data/labels.txt').read().strip().split('\n') - model = export_onnx_mobilenetv2(save_path) + model = export_onnx_resnet50(save_path) - # load mobilenetv2 use cv.dnn + # load resnet50 use cv.dnn net = cv.dnn.readNetFromONNX(save_path + '.onnx') # read and preprocess image file im = preprocess(image_file) diff --git a/samples/dnn/dnn_model_runner/dnn_conversion/requirements.txt b/samples/dnn/dnn_model_runner/dnn_conversion/requirements.txt index eb217e27df..6887c2ab2c 100644 --- a/samples/dnn/dnn_model_runner/dnn_conversion/requirements.txt +++ b/samples/dnn/dnn_model_runner/dnn_conversion/requirements.txt @@ -12,3 +12,4 @@ paddlepaddle>=2.0.0 paddlepaddle-gpu>=2.0.0 paddlehub>=2.1.0 paddle2onnx>=0.5.1 +paddleseg>=2.0.0 \ No newline at end of file