From 640c9ff9404a0756e2f2e6dfc020406fa0303466 Mon Sep 17 00:00:00 2001 From: FlyingQianMM <245467267@qq.com> Date: Tue, 1 Sep 2020 12:08:11 +0000 Subject: [PATCH] add analysis.md, add jpeg and bmp image format in decode_image for seg_transform --- docs/apis/analysis.md | 48 +++++++ .../multi-channel_remote_sensing/analysis.md | 121 ++++++++++++++++++ .../multi-channel_remote_sensing/README.md | 2 +- paddlex/__init__.py | 4 +- paddlex/cv/datasets/analysis.py | 3 + paddlex/cv/datasets/imagenet.py | 2 +- paddlex/cv/transforms/ops.py | 2 +- paddlex/cv/transforms/seg_transforms.py | 4 +- setup.py | 2 +- 9 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 docs/apis/analysis.md create mode 100644 docs/examples/multi-channel_remote_sensing/analysis.md diff --git a/docs/apis/analysis.md b/docs/apis/analysis.md new file mode 100644 index 0000000..aef04aa --- /dev/null +++ b/docs/apis/analysis.md @@ -0,0 +1,48 @@ +# 数据集分析 + +## paddlex.datasets.analysis.Seg +```python +paddlex.datasets.analysis.Seg(data_dir, file_list, label_list) +``` + +构建统计分析语义分类数据集的分析器。 + +> **参数** +> > * **data_dir** (str): 数据集所在的目录路径。 +> > * **file_list** (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对`data_dir`的相对路径)。 +> > * **label_list** (str): 描述数据集包含的类别信息文件路径。 + +### analysis +```python +analysis(self) +``` + +Seg分析器的分析接口,完成以下信息的分析统计: + +> * 图像数量 +> * 图像最大和最小的尺寸 +> * 图像通道数量 +> * 图像各通道的最小值和最大值 +> * 图像各通道的像素值分布 +> * 图像各通道归一化后的均值和方差 +> * 标注图中各类别的数量及比重 + +[代码示例](https://github.com/PaddlePaddle/PaddleX/examples/multi-channel_remote_sensing/tools/analysis.py) + +[统计信息示例](../../examples/multi-channel_remote_sensing/analysis.html#id2) + +### cal_clipped_mean_std +```python +cal_clipped_mean_std(self, clip_min_value, clip_max_value, data_info_file) +``` + +Seg分析器用于计算图像截断后的均值和方差的接口。 + +> **参数** +> > * **clip_min_value** (list): 截断的下限,小于min_val的数值均设为min_val。 +> > * **clip_max_value** (list): 截断的上限,大于max_val的数值均设为max_val。 +> > * **data_info_file** (str): 在analysis()接口中保存的分析结果文件(名为`train_information.pkl`)的路径。 + +[代码示例](https://github.com/PaddlePaddle/PaddleX/examples/multi-channel_remote_sensing/tools/cal_clipped_mean_std.py) + +[计算结果示例](../../examples/multi-channel_remote_sensing/analysis.html#id4) diff --git a/docs/examples/multi-channel_remote_sensing/analysis.md b/docs/examples/multi-channel_remote_sensing/analysis.md new file mode 100644 index 0000000..2bc61a7 --- /dev/null +++ b/docs/examples/multi-channel_remote_sensing/analysis.md @@ -0,0 +1,121 @@ +# 数据分析 + +遥感影像往往由许多波段组成,不同波段数据分布可能大相径庭,例如可见光波段和热红外波段分布十分不同。为了更深入了解数据的分布来优化模型训练效果,需要对数据进行分析。 + +## 统计分析 +执行以下脚本,对训练集进行统计分析,屏幕会输出分析结果,同时结果也会保存至文件`train_information.pkl`中: + +``` +python tools/analysis.py +``` + +数据统计分析内容如下: + +* 图像数量 + +例如统计出训练集中有64张图片: +``` +64 samples in file dataset/remote_sensing_seg/train.txt +``` +* 图像最大和最小的尺寸 + +例如统计出训练集中最大的高宽和最小的高宽分别是(1000, 1000)和(1000, 1000): +``` +Minimal image height: 1000 Minimal image width: 1000. +Maximal image height: 1000 Maximal image width: 1000. +``` +* 图像通道数量 + +例如统计出图像的通道数量为10: + +``` +Image channel is 10. +``` +* 图像各通道的最小值和最大值 + +最小值和最大值分别以列表的形式输出,按照通道从小到大排列。例如: + +``` +Minimal image value: [7.172e+03 6.561e+03 5.777e+03 5.103e+03 4.291e+03 1.000e+00 1.000e+00 4.232e+03 6.934e+03 7.199e+03] +Maximal image value: [65535. 65535. 65535. 65535. 65535. 65535. 65535. 56534. 65535. 63215.] + +``` +* 图像各通道的像素值分布 + +针对各个通道,统计出各像素值的数量,并以柱状图的形式呈现在以'distribute.png'结尾的图片中。**需要注意的是,为便于观察,纵坐标为对数坐标**。用户可以查看这些图片来选择是否需要对分布在头部和尾部的像素值进行截断。 + +``` +Image pixel distribution of each channel is saved with 'distribute.png' in the dataset/remote_sensing_seg +``` + +* 图像各通道归一化后的均值和方差 + +各通道归一化系数为各通道最大值与最小值之差,均值和方差以列别形式输出,按照通道从小到大排列。例如: + +``` +Image mean value: [0.23417574 0.22283101 0.2119595 0.2119887 0.27910388 0.21294892 0.17294037 0.10158925 0.43623915 0.41019192] +Image standard deviation: [0.06831269 0.07243951 0.07284761 0.07875261 0.08120818 0.0609302 0.05110716 0.00696064 0.03849307 0.03205579] +``` + +* 标注图中各类别的数量及比重 + +统计各类别的像素数量和在数据集全部像素的占比,以(类别值,该类别的数量,该类别的占比)的格式输出。例如: + +``` +Label pixel information is shown in a format of (label_id, the number of label_id, the ratio of label_id): +(0, 13302870, 0.20785734374999995) +(1, 4577005, 0.07151570312500002) +(2, 3955012, 0.0617970625) +(3, 2814243, 0.04397254687499999) +(4, 39350870, 0.6148573437500001) + +``` + +## 2 确定像素值截断范围 + +遥感影像数据分布范围广,往往存在一些异常值,这会影响算法对实际数据分布的拟合效果。为更好地对数据进行归一化,可以抑制遥感影像中少量的异常值。根据`图像各通道的像素值分布`来确定像素值的截断范围,并在后续图像预处理过程中对超出范围的像素值通过截断进行校正,从而去除异常值带来的干扰。**注意:该步骤是否执行根据数据集实际分布来决定。** + +例如各通道的像素值分布可视化效果如下: + +![](../../../examples/multi-channel_remote_sensing/docs/images/image_pixel_distribution.png) +**需要注意的是,为便于观察,纵坐标为对数坐标。** + + +对于上述分布,我们选取的截断范围是(按照通道从小到大排列): + +``` +截断范围最小值: clip_min_value = [7172, 6561, 5777, 5103, 4291, 4000, 4000, 4232, 6934, 7199] +截断范围最大值: clip_max_value = [50000, 50000, 50000, 50000, 50000, 40000, 30000, 18000, 40000, 36000] +``` + +## 3 确定像素值截断范围 + +为避免数据截断范围选取不当带来的影响,应该统计异常值像素占比,确保受影响的像素比例不要过高。接着对截断后的数据计算归一化后的均值和方差,**用于后续模型训练时的图像预处理参数设置**。 + +执行以下脚本: +``` +python tools/cal_clipped_mean_std.py +``` + +截断像素占比统计结果如下: + +``` +Channel 0, the ratio of pixels to be clipped = 0.00054778125 +Channel 1, the ratio of pixels to be clipped = 0.0011129375 +Channel 2, the ratio of pixels to be clipped = 0.000843703125 +Channel 3, the ratio of pixels to be clipped = 0.00127125 +Channel 4, the ratio of pixels to be clipped = 0.001330140625 +Channel 5, the ratio of pixels to be clipped = 8.1375e-05 +Channel 6, the ratio of pixels to be clipped = 0.0007348125 +Channel 7, the ratio of pixels to be clipped = 6.5625e-07 +Channel 8, the ratio of pixels to be clipped = 0.000185921875 +Channel 9, the ratio of pixels to be clipped = 0.000139671875 +``` +可看出,被截断像素占比均不超过0.2%。 + +裁剪后数据的归一化系数如下: +``` +Image mean value: [0.15163569 0.15142828 0.15574491 0.1716084 0.2799778 0.27652043 0.28195933 0.07853807 0.56333154 0.5477584 ] +Image standard deviation: [0.09301891 0.09818967 0.09831126 0.1057784 0.10842132 0.11062996 0.12791838 0.02637859 0.0675052 0.06168227] +(normalized by (clip_max_value - clip_min_value), arranged in 0-10 channel order) +``` diff --git a/examples/multi-channel_remote_sensing/README.md b/examples/multi-channel_remote_sensing/README.md index 8554e3d..e27da2f 100644 --- a/examples/multi-channel_remote_sensing/README.md +++ b/examples/multi-channel_remote_sensing/README.md @@ -41,7 +41,7 @@ cd PaddleX/examples/channel_remote_sensing/ 遥感影像的格式多种多样,不同传感器产生的数据格式也可能不同。PaddleX现已兼容以下4种格式图片读取: - `tif` -- `png` +- `png`, `jpeg`, `bmp` - `img` - `npy` diff --git a/paddlex/__init__.py b/paddlex/__init__.py index 1b8cea3..d35fd04 100644 --- a/paddlex/__init__.py +++ b/paddlex/__init__.py @@ -45,8 +45,8 @@ except: ) import paddlehub as hub -if hub.version.hub_version < '1.6.2': - raise Exception("[ERROR] paddlehub >= 1.6.2 is required") +if hub.version.hub_version < '1.8.2': + raise Exception("[ERROR] paddlehub >= 1.8.2 is required") env_info = get_environ_info() load_model = cv.models.load_model diff --git a/paddlex/cv/datasets/analysis.py b/paddlex/cv/datasets/analysis.py index be1d58f..869331c 100644 --- a/paddlex/cv/datasets/analysis.py +++ b/paddlex/cv/datasets/analysis.py @@ -265,6 +265,9 @@ class Seg: def cal_clipped_mean_std(self, clip_min_value, clip_max_value, data_info_file): + if not osp.exists(data_info_file): + raise Exception("Dataset information file {} does not exist.". + format(data_info_file)) with open(data_info_file, 'rb') as f: im_info = pickle.load(f) channel_num = im_info['channel_num'] diff --git a/paddlex/cv/datasets/imagenet.py b/paddlex/cv/datasets/imagenet.py index 9b0f2e7..1c66de4 100644 --- a/paddlex/cv/datasets/imagenet.py +++ b/paddlex/cv/datasets/imagenet.py @@ -67,7 +67,7 @@ class ImageNet(Dataset): with open(file_list, encoding=get_encoding(file_list)) as f: for line in f: items = line.strip().split() - if len(items): + if len(items) > 2: raise Exception( "A space is defined as the separator, but it exists in image or label name {}." .format(line)) diff --git a/paddlex/cv/transforms/ops.py b/paddlex/cv/transforms/ops.py index 46990b0..31c76aa 100644 --- a/paddlex/cv/transforms/ops.py +++ b/paddlex/cv/transforms/ops.py @@ -18,7 +18,7 @@ import numpy as np from PIL import Image, ImageEnhance -def normalize(im, mean, std, min_value, max_value): +def normalize(im, mean, std, min_value=[0, 0, 0], max_value=[255, 255, 255]): # Rescaling (min-max normalization) range_value = [max_value[i] - min_value[i] for i in range(len(max_value))] im = (im - min_value) / range_value diff --git a/paddlex/cv/transforms/seg_transforms.py b/paddlex/cv/transforms/seg_transforms.py index c482930..c3b597f 100644 --- a/paddlex/cv/transforms/seg_transforms.py +++ b/paddlex/cv/transforms/seg_transforms.py @@ -82,8 +82,8 @@ class Compose(SegTransform): raise Exception('Can not open', img_path) im_data = dataset.ReadAsArray() return im_data.transpose((1, 2, 0)) - elif img_format == 'png': - return np.asarray(Image.open(img_path)) + elif img_format in ['jpeg', 'bmp', 'png']: + return cv2.imread(img_path) elif ext == '.npy': return np.load(img_path) else: diff --git a/setup.py b/setup.py index edcee85..5792464 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setuptools.setup( setup_requires=['cython', 'numpy'], install_requires=[ "pycocotools;platform_system!='Windows'", 'pyyaml', 'colorama', 'tqdm', - 'paddleslim==1.0.1', 'visualdl>=2.0.0b', 'paddlehub>=1.6.2', + 'paddleslim==1.1.1', 'visualdl>=2.0.0', 'paddlehub>=1.8.2', 'shapely>=1.7.0', "opencv-python" ], classifiers=[ -- GitLab