Przeglądaj źródła

fix draw_pr_curve in docs

FlyingQianMM 5 lat temu
rodzic
commit
0e9ae27343

+ 59 - 0
docs/anaconda_install.md

@@ -0,0 +1,59 @@
+# Anaconda安装使用
+Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。使用Anaconda可以通过创建多个独立的Python环境,避免用户的Python环境安装太多不同版本依赖导致冲突。
+
+## Windows安装Anaconda
+### 第一步 下载
+在Anaconda官网[(https://www.anaconda.com/products/individual)](https://www.anaconda.com/products/individual)选择下载Windows Python3.7 64-Bit版本
+
+### 第二步 安装
+运行下载的安装包(以.exe为后辍),根据引导完成安装, 用户可自行修改安装目录(如下图)
+![](./images/anaconda_windows.png)
+
+### 第三步 使用
+- 点击Windows系统左下角的Windows图标,打开:所有程序->Anaconda3/2(64-bit)->Anaconda Prompt  
+- 在命令行中执行下述命令
+```cmd
+# 创建名为my_paddlex的环境,指定Python版本为3.7
+conda create -n my_paddlex python=3.7
+# 进入my_paddlex环境
+conda activate my_paddlex
+# 安装git
+conda install git
+# 安装pycocotools
+pip install cython
+pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
+# 安装paddlepaddle-gpu
+pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
+# 安装paddlex
+pip install paddlex -i https://mirror.baidu.com/pypi/simple
+```  
+按如上方式配置后,即可在环境中使用PaddleX了,命令行输入`python`回车后,`import paddlex`试试吧,之后再次使用都可以通过打开'所有程序->Anaconda3/2(64-bit)->Anaconda Prompt',再执行`conda activate my_paddlex`进入环境后,即可再次使用paddlex
+
+## Linux/Mac安装
+
+### 第一步 下载
+在Anaconda官网[(https://www.anaconda.com/products/individual)](https://www.anaconda.com/products/individual)选择下载对应系统 Python3.7版本下载(Mac下载Command Line Installer版本即可)
+
+### 第二步 安装
+打开终端,在终端安装Anaconda
+```
+# ~/Downloads/Anaconda3-2019.07-Linux-x86_64.sh即下载的文件
+bash ~/Downloads/Anaconda3-2019.07-Linux-x86_64.sh
+```
+安装过程中一直回车即可,如提示设置安装路径,可根据需求修改,一般默认即可。
+
+### 第三步 使用
+```
+# 创建名为my_paddlex的环境,指定Python版本为3.7
+conda create -n my_paddlex python=3.7
+# 进入paddlex环境
+conda activate my_paddlex
+# 安装pycocotools
+pip install cython
+pip install pycocotools
+# 安装paddlepaddle-gpu
+pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
+# 安装paddlex
+pip install paddlex -i https://mirror.baidu.com/pypi/simple
+```
+按如上方式配置后,即可在环境中使用PaddleX了,终端输入`python`回车后,`import paddlex`试试吧,之后再次使用只需再打开终端,再执行`conda activate my_paddlex`进入环境后,即可使用paddlex

BIN
docs/apis/images/insect_bbox_pr_curve(iou-0.5).png


+ 54 - 5
docs/apis/visualize.md

@@ -3,7 +3,7 @@ PaddleX提供了一系列模型预测和结果分析的可视化函数。
 
 
 ## 目标检测/实例分割预测结果可视化
 ## 目标检测/实例分割预测结果可视化
 ```
 ```
-paddlex.det.visualize(image, result, threshold=0.5, save_dir=None)
+paddlex.det.visualize(image, result, threshold=0.5, save_dir='./')
 ```
 ```
 将目标检测/实例分割模型预测得到的Box框和Mask在原图上进行可视化
 将目标检测/实例分割模型预测得到的Box框和Mask在原图上进行可视化
 
 
@@ -11,7 +11,7 @@ paddlex.det.visualize(image, result, threshold=0.5, save_dir=None)
 > * **image** (str): 原图文件路径。  
 > * **image** (str): 原图文件路径。  
 > * **result** (str): 模型预测结果。
 > * **result** (str): 模型预测结果。
 > * **threshold**(float): score阈值,将Box置信度低于该阈值的框过滤不进行可视化。默认0.5
 > * **threshold**(float): score阈值,将Box置信度低于该阈值的框过滤不进行可视化。默认0.5
-> * **save_dir**(str): 可视化结果保存路径。若为None,则表示不保存,该函数将可视化的结果以np.ndarray的形式返回;若设为目录路径,则将可视化结果保存至该目录下
+> * **save_dir**(str): 可视化结果保存路径。若为None,则表示不保存,该函数将可视化的结果以np.ndarray的形式返回;若设为目录路径,则将可视化结果保存至该目录下。默认值为'./'。
 
 
 ### 使用示例
 ### 使用示例
 > 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/xiaoduxiong_epoch_12.tar.gz)和[测试图片](https://bj.bcebos.com/paddlex/datasets/xiaoduxiong.jpeg)
 > 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/xiaoduxiong_epoch_12.tar.gz)和[测试图片](https://bj.bcebos.com/paddlex/datasets/xiaoduxiong.jpeg)
@@ -23,17 +23,66 @@ pdx.det.visualize('xiaoduxiong.jpeg', result, save_dir='./')
 # 预测结果保存在./visualize_xiaoduxiong.jpeg
 # 预测结果保存在./visualize_xiaoduxiong.jpeg
 ```
 ```
 
 
+## 目标检测/实例分割准确率-召回率可视化
+```
+paddlex.det.draw_pr_curve(eval_details_file=None, gt=None, pred_bbox=None, pred_mask=None, iou_thresh=0.5, save_dir='./')
+```
+将目标检测/实例分割模型评估结果中各个类别的准确率和召回率的对应关系进行可视化,同时可视化召回率和置信度阈值的对应关系。
+
+### 参数
+> * **eval_details_file** (str): 模型评估结果的保存路径,包含真值信息和预测结果。默认值为None。
+> * **gt** (list): 数据集的真值信息。默认值为None。
+> * **pred_bbox** (list): 模型在数据集上的预测框。默认值为None。
+> * **pred_mask** (list): 模型在数据集上的预测mask。默认值为None。
+> * **iou_thresh** (float): 判断预测框或预测mask为真阳时的IoU阈值。默认值为0.5。
+> * **save_dir** (str): 可视化结果保存路径。默认值为'./'。
+
+**注意:**`eval_details_file`的优先级更高,只要`eval_details_file`不为None,就会从`eval_details_file`提取真值信息和预测结果做分析。当`eval_details_file`为None时,则用`gt`、`pred_mask`、`pred_mask`做分析。
+
+### 使用示例
+点击下载如下示例中的[模型]()和[数据集](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz)
+
+> 方式一:分析训练过程中保存的模型文件夹中的评估结果文件`eval_details.json`,例如[模型]()中的`eval_details.json`。
+```
+import paddlex as pdx
+eval_details_file = 'insect_epoch_/eval_details.json'
+pdx.det.draw_pr_curve(eval_details_file, save_dir='./insect')
+```
+> 方式二:分析模型评估函数返回的评估结果。
+
+```
+import os
+# 选择使用0号卡
+os.environ['CUDA_VISIBLE_DEVICES'] = '0'
+
+from paddlex.det import transforms
+import paddlex as pdx
+
+model = pdx.load_model('xiaoduxiong_epoch_12')
+eval_dataset = pdx.datasets.CocoDetection(
+    data_dir='xiaoduxiong_ins_det/JPEGImages',
+    ann_file='xiaoduxiong_ins_det/val.json',
+    transforms=model.eval_transforms)
+metrics, evaluate_details = model.evaluate(eval_dataset, batch_size=8, return_details=True)
+gt = evaluate_details['gt']
+bbox = evaluate_details['bbox']
+pdx.det.draw_pr_curve(gt=gt, pred_bbox=bbox, save_dir='./insect')
+```
+
+预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:
+![](./images/insect_bbox_pr_curve(iou-0.5).png)
+
 ## 语义分割预测结果可视化
 ## 语义分割预测结果可视化
 ```
 ```
-paddlex.seg.visualize(image, result, weight=0.6, save_dir=None)
+paddlex.seg.visualize(image, result, weight=0.6, save_dir='./')
 ```
 ```
 将语义分割模型预测得到的Mask在原图上进行可视化
 将语义分割模型预测得到的Mask在原图上进行可视化
 
 
 ### 参数
 ### 参数
 > * **image** (str): 原图文件路径。  
 > * **image** (str): 原图文件路径。  
 > * **result** (str): 模型预测结果。
 > * **result** (str): 模型预测结果。
-> * **weight**(float): mask可视化结果与原图权重因子,weight表示原图的权重。默认0.6
-> * **save_dir**(str): 可视化结果保存路径。若为None,则表示不保存,该函数将可视化的结果以np.ndarray的形式返回;若设为目录路径,则将可视化结果保存至该目录下
+> * **weight**(float): mask可视化结果与原图权重因子,weight表示原图的权重。默认0.6
+> * **save_dir**(str): 可视化结果保存路径。若为None,则表示不保存,该函数将可视化的结果以np.ndarray的形式返回;若设为目录路径,则将可视化结果保存至该目录下。默认值为'./'。
 
 
 ### 使用示例
 ### 使用示例
 > 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/cityscape_deeplab.tar.gz)和[测试图片](https://bj.bcebos.com/paddlex/datasets/city.png)
 > 点击下载如下示例中的[模型](https://bj.bcebos.com/paddlex/models/cityscape_deeplab.tar.gz)和[测试图片](https://bj.bcebos.com/paddlex/datasets/city.png)

BIN
docs/images/anaconda_windows.png


BIN
docs/images/visualized_maskrcnn.jpeg


+ 3 - 1
docs/install.md

@@ -1,6 +1,8 @@
 # 安装
 # 安装
 
 
-> 以下安装过程默认用户已安装好**paddlepaddle-gpu或paddlepaddle(版本大于或等于1.7.1)**,paddlepaddle安装方式参照[飞桨官网](https://www.paddlepaddle.org.cn/install/quick)
+以下安装过程默认用户已安装好**paddlepaddle-gpu或paddlepaddle(版本大于或等于1.7.1)**,paddlepaddle安装方式参照[飞桨官网](https://www.paddlepaddle.org.cn/install/quick)
+
+> 推荐使用Anaconda Python环境,Anaconda下安装PaddleX参考文档[Anaconda安装使用](./anaconda_install.md)
 
 
 ## Github代码安装
 ## Github代码安装
 github代码会跟随开发进度不断更新
 github代码会跟随开发进度不断更新

+ 150 - 5
paddlex/cv/models/utils/visualize.py

@@ -14,14 +14,19 @@
 
 
 import os
 import os
 import cv2
 import cv2
+import colorsys
 import numpy as np
 import numpy as np
 import matplotlib as mpl
 import matplotlib as mpl
+import matplotlib.pyplot as plt
 import matplotlib.figure as mplfigure
 import matplotlib.figure as mplfigure
 import matplotlib.colors as mplc
 import matplotlib.colors as mplc
 from matplotlib.backends.backend_agg import FigureCanvasAgg
 from matplotlib.backends.backend_agg import FigureCanvasAgg
 
 
+import paddlex.utils.logging as logging
+from .detection_eval import fixed_linspace, backup_linspace, loadRes
 
 
-def visualize_detection(image, result, threshold=0.5, save_dir=None):
+
+def visualize_detection(image, result, threshold=0.5, save_dir='./'):
     """
     """
         Visualize bbox and mask results
         Visualize bbox and mask results
     """
     """
@@ -34,11 +39,12 @@ def visualize_detection(image, result, threshold=0.5, save_dir=None):
             os.makedirs(save_dir)
             os.makedirs(save_dir)
         out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name))
         out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name))
         cv2.imwrite(out_path, image)
         cv2.imwrite(out_path, image)
+        logging.info('The visualized result is saved as {}'.format(out_path))
     else:
     else:
         return image
         return image
 
 
 
 
-def visualize_segmentation(image, result, weight=0.6, save_dir=None):
+def visualize_segmentation(image, result, weight=0.6, save_dir='./'):
     """
     """
     Convert segment result to color image, and save added image.
     Convert segment result to color image, and save added image.
     Args:
     Args:
@@ -65,6 +71,7 @@ def visualize_segmentation(image, result, weight=0.6, save_dir=None):
         image_name = os.path.split(image)[-1]
         image_name = os.path.split(image)[-1]
         out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name))
         out_path = os.path.join(save_dir, 'visualize_{}'.format(image_name))
         cv2.imwrite(out_path, vis_result)
         cv2.imwrite(out_path, vis_result)
+        logging.info('The visualized result is saved as {}'.format(out_path))
     else:
     else:
         return vis_result
         return vis_result
 
 
@@ -122,6 +129,18 @@ def clip_bbox(bbox):
 
 
 def draw_bbox_mask(image, results, threshold=0.5):
 def draw_bbox_mask(image, results, threshold=0.5):
     # refer to  https://github.com/facebookresearch/detectron2/blob/master/detectron2/utils/visualizer.py
     # refer to  https://github.com/facebookresearch/detectron2/blob/master/detectron2/utils/visualizer.py
+    def _change_color_brightness(color, brightness_factor):
+        assert brightness_factor >= -1.0 and brightness_factor <= 1.0
+        color = mplc.to_rgb(color)
+        polygon_color = colorsys.rgb_to_hls(*mplc.to_rgb(color))
+        modified_lightness = polygon_color[1] + (
+            brightness_factor * polygon_color[1])
+        modified_lightness = 0.0 if modified_lightness < 0.0 else modified_lightness
+        modified_lightness = 1.0 if modified_lightness > 1.0 else modified_lightness
+        modified_color = colorsys.hls_to_rgb(
+            polygon_color[0], modified_lightness, polygon_color[2])
+        return modified_color
+
     _SMALL_OBJECT_AREA_THRESH = 1000
     _SMALL_OBJECT_AREA_THRESH = 1000
     # setup figure
     # setup figure
     width, height = image.shape[1], image.shape[0]
     width, height = image.shape[1], image.shape[0]
@@ -176,7 +195,7 @@ def draw_bbox_mask(image, results, threshold=0.5):
                 fill=False,
                 fill=False,
                 edgecolor=color,
                 edgecolor=color,
                 linewidth=linewidth * scale,
                 linewidth=linewidth * scale,
-                alpha=0.5,
+                alpha=0.8,
                 linestyle="-",
                 linestyle="-",
             ))
             ))
 
 
@@ -187,7 +206,7 @@ def draw_bbox_mask(image, results, threshold=0.5):
             res = cv2.findContours(
             res = cv2.findContours(
                 mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
                 mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
             hierarchy = res[-1]
             hierarchy = res[-1]
-            alpha = 0.75
+            alpha = 0.5
             if hierarchy is not None:
             if hierarchy is not None:
                 has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0
                 has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0
                 res = res[-2]
                 res = res[-2]
@@ -221,7 +240,7 @@ def draw_bbox_mask(image, results, threshold=0.5):
         text = "{} {:.2f}".format(cname, score)
         text = "{} {:.2f}".format(cname, score)
         color = np.maximum(list(mplc.to_rgb(color)), 0.2)
         color = np.maximum(list(mplc.to_rgb(color)), 0.2)
         color[np.argmax(color)] = max(0.8, np.max(color))
         color[np.argmax(color)] = max(0.8, np.max(color))
-
+        color = _change_color_brightness(color, brightness_factor=0.7)
         ax.text(
         ax.text(
             text_pos[0],
             text_pos[0],
             text_pos[1],
             text_pos[1],
@@ -258,3 +277,129 @@ def draw_bbox_mask(image, results, threshold=0.5):
     visualized_image = visualized_image.astype("uint8")
     visualized_image = visualized_image.astype("uint8")
 
 
     return visualized_image
     return visualized_image
+
+
+def draw_pr_curve(eval_details_file=None,
+                  gt=None,
+                  pred_bbox=None,
+                  pred_mask=None,
+                  iou_thresh=0.5,
+                  save_dir='./'):
+    if eval_details_file is not None:
+        import json
+        with open(eval_details_file, 'r') as f:
+            eval_details = json.load(f)
+            pred_bbox = eval_details['bbox']
+            if 'mask' in eval_details:
+                pred_mask = eval_details['mask']
+            gt = eval_details['gt']
+    if gt is None or pred_bbox is None:
+        raise Exception(
+            "gt/pred_bbox/pred_mask is None now, please set right eval_details_file or gt/pred_bbox/pred_mask."
+        )
+    if pred_bbox is not None and len(pred_bbox) == 0:
+        raise Exception("There is no predicted bbox.")
+    if pred_mask is not None and len(pred_mask) == 0:
+        raise Exception("There is no predicted mask.")
+    from pycocotools.coco import COCO
+    from pycocotools.cocoeval import COCOeval
+    coco = COCO()
+    coco.dataset = gt
+    coco.createIndex()
+
+    def _summarize(coco_gt, ap=1, iouThr=None, areaRng='all', maxDets=100):
+        p = coco_gt.params
+        aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng]
+        mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets]
+        if ap == 1:
+            # dimension of precision: [TxRxKxAxM]
+            s = coco_gt.eval['precision']
+            # IoU
+            if iouThr is not None:
+                t = np.where(iouThr == p.iouThrs)[0]
+                s = s[t]
+            s = s[:, :, :, aind, mind]
+        else:
+            # dimension of recall: [TxKxAxM]
+            s = coco_gt.eval['recall']
+            if iouThr is not None:
+                t = np.where(iouThr == p.iouThrs)[0]
+                s = s[t]
+            s = s[:, :, aind, mind]
+        if len(s[s > -1]) == 0:
+            mean_s = -1
+        else:
+            mean_s = np.mean(s[s > -1])
+        return mean_s
+
+    def cal_pr(coco_gt, coco_dt, iou_thresh, save_dir, style='bbox'):
+        from pycocotools.cocoeval import COCOeval
+        coco_dt = loadRes(coco_gt, coco_dt)
+        np.linspace = fixed_linspace
+        coco_eval = COCOeval(coco_gt, coco_dt, style)
+        coco_eval.params.iouThrs = np.linspace(
+            iou_thresh, iou_thresh, 1, endpoint=True)
+        np.linspace = backup_linspace
+        coco_eval.evaluate()
+        coco_eval.accumulate()
+        stats = _summarize(coco_eval, iouThr=iou_thresh)
+        catIds = coco_gt.getCatIds()
+        if len(catIds) != coco_eval.eval['precision'].shape[2]:
+            raise Exception(
+                "The category number must be same as the third dimension of precisions."
+            )
+        x = np.arange(0.0, 1.01, 0.01)
+        color_map = get_color_map_list(256)[1:256]
+
+        plt.subplot(1, 2, 1)
+        plt.title(style + " precision-recall IoU={}".format(iou_thresh))
+        plt.xlabel("recall")
+        plt.ylabel("precision")
+        plt.xlim(0, 1.01)
+        plt.ylim(0, 1.01)
+        plt.grid(linestyle='--', linewidth=1)
+        plt.plot([0, 1], [0, 1], 'r--', linewidth=1)
+        my_x_ticks = np.arange(0, 1.01, 0.1)
+        my_y_ticks = np.arange(0, 1.01, 0.1)
+        plt.xticks(my_x_ticks, fontsize=5)
+        plt.yticks(my_y_ticks, fontsize=5)
+        for idx, catId in enumerate(catIds):
+            pr_array = coco_eval.eval['precision'][0, :, idx, 0, 2]
+            precision = pr_array[pr_array > -1]
+            ap = np.mean(precision) if precision.size else float('nan')
+            nm = coco_gt.loadCats(catId)[0]['name'] + ' AP={:0.2f}'.format(
+                float(ap * 100))
+            color = tuple(color_map[idx])
+            color = [float(c) / 255 for c in color]
+            color.append(0.75)
+            plt.plot(x, pr_array, color=color, label=nm, linewidth=1)
+        plt.legend(loc="lower left", fontsize=5)
+
+        plt.subplot(1, 2, 2)
+        plt.title(style + " score-recall IoU={}".format(iou_thresh))
+        plt.xlabel('recall')
+        plt.ylabel('score')
+        plt.xlim(0, 1.01)
+        plt.ylim(0, 1.01)
+        plt.grid(linestyle='--', linewidth=1)
+        plt.xticks(my_x_ticks, fontsize=5)
+        plt.yticks(my_y_ticks, fontsize=5)
+        for idx, catId in enumerate(catIds):
+            nm = coco_gt.loadCats(catId)[0]['name']
+            sr_array = coco_eval.eval['scores'][0, :, idx, 0, 2]
+            color = tuple(color_map[idx])
+            color = [float(c) / 255 for c in color]
+            color.append(0.75)
+            plt.plot(x, sr_array, color=color, label=nm, linewidth=1)
+        plt.legend(loc="lower left", fontsize=5)
+        plt.savefig(
+            os.path.join(save_dir, "./{}_pr_curve(iou-{}).png".format(
+                style, iou_thresh)),
+            dpi=800)
+        plt.close()
+
+    if not os.path.exists(save_dir):
+        os.makedirs(save_dir)
+    cal_pr(coco, pred_bbox, iou_thresh, save_dir, style='bbox')
+    if pred_mask is not None:
+        cal_pr(coco, pred_mask, iou_thresh, save_dir, style='segm')