diff --git a/docs/apis/analysis.md b/docs/apis/analysis.md new file mode 100644 index 0000000000000000000000000000000000000000..aef04aa0ac6b42de6b52dd958f600b7088ce4260 --- /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 0000000000000000000000000000000000000000..2bc61a79a5593c68e8419b24400d71976dfa5fad --- /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 8554e3d858ad7101d125d066ad1df19095eb2525..e27da2f3caa129f57f7e47432f6827fed4e95a4e 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 1b8cea3503a07b4301240b5b0834f5a648db75e8..d35fd044e3cbd0066ca8ce3d0188ab8493088d85 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 be1d58f8205fb72ce35a301ca6f370d1eeac13f0..869331c843cf58c6dd6b686586d14bda976edb4c 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 9b0f2e708e3116c56e2eaf7914d633757dfd3fd6..1c66de4fada53d4818e829e65e93a754edb23e53 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 46990b01a7d4c5bd0fba80be248f4632a8ecfe68..31c76aa02988ef5cab24bcfd2fa21a3064fe2ed7 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 c482930ca18a39a2e684c17d470b931dfc6e5823..c3b597fca4d658edef977cda2a1fef9627875a68 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 edcee85e8f42edbda41a7feb7557f6f3c5524d34..57924649c89f783f1b6adf67f3b56065ef3d1400 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=[