import os
import numpy as np
import skimage.io
import glob
"定义图像路径和保存数据的路径"
mask_dir = '/data/study_Irrigation/Ca_256_val/labels' # mask_dir路径中共有435个256*256尺寸的影像
save_dir = '/data/study_Irrigation/Ca_256_val'
classes_num = 2
"初始化每个类的数目,我这里图像中只有2类"
maskset_hist = np.zeros(classes_num)
"返回目标目录中包含所有后缀名为 .tif 的文件的列表"
label_paths = glob.glob(os.path.join(mask_dir, '*.tif'))
# glob模块用法参考:https://zhuanlan.zhihu.com/p/71861602, https://rgb-24bit.github.io/blog/2018/glob.html
# glob.glob('*.org') # 返回包含所有后缀名为 .org 的文件的列表
# glob.iglob('*/') # 返回匹配所有目录的迭代器
print(len(label_paths))
"读取所有图像文件并且统计所有图像中的不同类别的数量"
for label_path in label_paths:
label = skimage.io.imread(label_path, as_gray=True)
# 判断label.shape 是否是我之前裁剪好的尺寸(256, 256),不是的话要打印出这个图像的ID
if label.shape != (256, 256):
print('label.shape', label.shape)
print('label_id', label_path.split("//")[-1])
unique_values, unique_counts = np.unique(label, return_counts=True)
# np.unique(arr, return_index, return_inverse, return_counts)
# return_counts:如果为 true,返回去重数组中的元素在原数组中的出现次数,默认为False
# 参考:https://blog.csdn.net/weixin_44211968/article/details/123772201
# 检查每一个label文件是否正确:是否符合设定的类别数; 如果不符合,则跳过统计这个label,并记录其id。
max_value, min_value = np.max(unique_values), np.min(unique_values)
if max_value > (self.classes_num - 1) or min_value < 0:
print('Labels can take value between 0 and number of classes.')
print('Some problem with labels. Please check. label_set:', unique_values)
print('Label Image ID: ' + label_path.split("//")[-1])
continue
else:
maskset_hist += unique_counts
# 计算每个类别数在总数的占比
class_ratio = maskset_hist / np.sum(maskset_hist)
# 平滑类别权重
class_weights = 1 / (np.log(self.normVal + class_ratio))
# classWeights = 1 / (class_ratio + 0.01) # 直接置倒数
class_weights = np.power(class_weights, self.label_weight_scale_factor) # 根据标签权重系数缩放
# 必须将imgset_mean数据类型转换为str。写入txt文件的必须是str,不能是数字。
# 否则报错ufunc 'add' did not contain a loop with signature matching types (dtype('float32'), dtype
# 直接读取出txt中的内容,其数据类型为str,因而不可以直接用来计算,需要将其数据类型改为数字.
with open(os.path.join(self.save_dir, 'maskset_hist_class_ratio.txt'), 'w') as f:
f.writelines(line.astype('str') + '\n' for line in maskset_hist)
f.writelines(line.astype('str') + '\n' for line in class_ratio)
with open(os.path.join(self.save_dir, 'class_weights.txt'), 'w') as f:
f.writelines(line.astype('str') + '\n' for line in class_weights)
print(maskset_hist) # [20447653.0 11992667.0]
print(np.sum(maskset_hist)) # 32440320
print(class_ratio) # [0.6303160079801926 0.36968399201980745]
出现问题:
mask_dir路径中共有435个256256尺寸的影像,这些影像包含的像素数目总数是 435256256=28508160。
然而np.sum(maskset_hist)的结果为 32440320,足足比 mask_dir路径 中的像素总数多出来 3932160个,相当于多出来60个256256尺寸的影像。
问题出在 maskset_hist += unique_counts ,
因为 有的结果是unique_count [63076 2460], 有的结果是 unique_counts [65536]. 我们的出发点是统计图像中包含的2个类别值的每个类别的数量,我们期望所有的图像中的类别数量都是2类,得到的nique_counts 数组shape为(2,),然而有的图像中的类别数量只有1类,这样得到的 unique_counts 数组shape为(1,). shape为(2,)的数组和 shape为(1,)的数组 相加时候, 就会启动数组增广,因而实际上这里多加了一个数。例如:[63076 2460]和[65536]相加,实际上是 [63076 2460]和[65536 65536]相加.
我暂时没有找到方法来避免不同shape数组相加时候自动出现numpy数据增广的机制。因而我改变上述代码中的统计图像中不同类别数量的方法。
下面只改变核心代码:
# 核心代码:
# "初始化每个类的数目"
background_num = 0
Irrigated_num = 0
for label_path in label_paths:
label = skimage.io.imread(label_path, as_gray=True)
# 判断label.shape 是否是我之前裁剪好的尺寸(256, 256),不是的话要打印出这个图像的ID
if label.shape != (256, 256):
print('label.shape', label.shape)
print('label_id', label_path.split("//")[-1])
unique_values = np.unique(mask, return_counts=False)
# np.unique(arr, return_index, return_inverse, return_counts)
# return_counts:如果为 true,返回去重数组中的元素在原数组中的出现次数,默认为False
# 参考:https://blog.csdn.net/weixin_44211968/article/details/123772201
# 检查每一个label文件是否正确:是否符合设定的类别数; 如果不符合,则跳过统计这个label,并记录其id。
max_value, min_value = np.max(unique_values), np.min(unique_values)
if max_value > (self.classes_num - 1) or min_value < 0:
print('Labels can take value between 0 and number of classes.')
print('Some problem with labels. Please check. label_set:', unique_values)
print('Label Image ID: ' + label_path.split("//")[-1])
continue
else:
background_num += np.sum(mask == 0)
Irrigated_num += np.sum(mask == 1)
maskset_hist = [background_num, Irrigated_num]
class_ratio = maskset_hist / sum(maskset_hist) # 计算每个类别数在总数的占比
完整代码如下:
import os
import numpy as np
import skimage.io
import glob
"定义图像路径和保存数据的路径"
mask_dir = '/data/study_Irrigation/Ca_256_val/labels' # mask_dir路径中共有435个256*256尺寸的影像
save_dir = '/data/study_Irrigation/Ca_256_val'
classes_num = 2
"初始化每个类的数目,我这里图像中只有2类"
maskset_hist = np.zeros(classes_num)
"返回目标目录中包含所有后缀名为 .tif 的文件的列表"
label_paths = glob.glob(os.path.join(mask_dir, '*.tif'))
# glob模块用法参考:https://zhuanlan.zhihu.com/p/71861602, https://rgb-24bit.github.io/blog/2018/glob.html
# glob.glob('*.org') # 返回包含所有后缀名为 .org 的文件的列表
# glob.iglob('*/') # 返回匹配所有目录的迭代器
print(len(label_paths))
"读取所有图像文件并且统计所有图像中的不同类别的数量"
for label_path in label_paths:
label = skimage.io.imread(label_path, as_gray=True)
# 判断label.shape 是否是我之前裁剪好的尺寸(256, 256),不是的话要打印出这个图像的ID
if label.shape != (256, 256):
print('label.shape', label.shape)
print('label_id', label_path.split("//")[-1])
unique_values = np.unique(label, return_counts=False)
# np.unique(arr, return_index, return_inverse, return_counts)
# return_counts:如果为 true,返回去重数组中的元素在原数组中的出现次数,默认为False
# 参考:https://blog.csdn.net/weixin_44211968/article/details/123772201
# 检查每一个label文件是否正确:是否符合设定的类别数; 如果不符合,则跳过统计这个label,并记录其id。
max_value, min_value = np.max(unique_values), np.min(unique_values)
if max_value > (self.classes_num - 1) or min_value < 0:
print('Labels can take value between 0 and number of classes.')
print('Some problem with labels. Please check. label_set:', unique_values)
print('Label Image ID: ' + label_path.split("//")[-1])
continue
else:
background_num += np.sum(mask == 0)
Irrigated_num += np.sum(mask == 1)
maskset_hist = [background_num, Irrigated_num]
# 计算每个类别数在总数的占比
class_ratio = maskset_hist / sum(maskset_hist)
# 平滑类别权重
class_weights = 1 / (np.log(self.normVal + class_ratio))
# classWeights = 1 / (class_ratio + 0.01) # 直接置倒数
class_weights = np.power(class_weights, self.label_weight_scale_factor) # 根据标签权重系数缩放
# 必须将imgset_mean数据类型转换为str。写入txt文件的必须是str,不能是数字。
# 否则报错ufunc 'add' did not contain a loop with signature matching types (dtype('float32'), dtype
# 直接读取出txt中的内容,其数据类型为str,因而不可以直接用来计算,需要将其数据类型改为数字.
with open(os.path.join(self.save_dir, 'maskset_hist_class_ratio.txt'), 'w') as f:
f.writelines(line.astype('str') + '\n' for line in maskset_hist)
f.writelines(line.astype('str') + '\n' for line in class_ratio)
with open(os.path.join(self.save_dir, 'class_weights.txt'), 'w') as f:
f.writelines(line.astype('str') + '\n' for line in class_weights)
print(maskset_hist) # [20447653 8060507]
print(np.sum(maskset_hist)) # 28508160
print(class_ratio) # [0.7172561470119433 0.28274385298805677]