python代码实现将json中所有字段四舍五入保留n位小数

本文介绍了一种使用递归算法遍历并处理JSON中所有数值的方法,确保所有数值(包括字符串类型的数值)统一保留指定小数位数,适用于数据规范化处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

json中数据不规范,有时我们需要将数值(字符串类型的数值)统一保留多少位小数。

以下是我的代码,使用递归算法遍历json中所有的层。只要发现value值为浮点型或可转换成数值型的字符串则全部转换成浮点数;

具体代码如下:

class RoundJSON(object):

#data:需要处理的目标json,digit为保留多少位小数位默认保留4位

	def round_data(self,data={},digit=4):#将data中的所有字段只要是数值则四舍五入
	    if(type(data)==type({})):
	        keys=data.keys()
	        if (len(keys) > 0 and type(keys)!=type(None)):
	            for key in keys:
	                value=data.get(key)
	                isnum=self.is_number(value)
	                if(isnum==True):
	                    value = self.roundUp(value, digit)
	                    data[key] = value
	                elif(type(value)==type({}) or type(value)==type([])):
	                    self.round_data(value, digit)
	    elif(type(data)==type([])):
	        for i in range(len(data)):
	            if (type(data[i]) == type({})):
	                keys = data[i].keys()
	                if(len(keys)>0 and type(keys)!=type(None)):
	                    for key in keys:
	                        value = data[i].get(key)
	                        if (self.is_number(value) == True):
	                            value = self.roundUp(value, digit)
	                            data[i][key] = value
	                        elif(type(value)==type({}) or type(value)==type([])):
	                            self.round_data(value, digit)
	            elif(type(data[i]) == type([]) and len(data[i])>0):
	                for value in data[i]:
	                    if (self.is_number(value) == True):
	                        value_new = self.roundUp(value, digit)
	                        data[i]=[value_new if x==value else x for x in data[i]]
	                    elif (type(value) == type({}) or type(value) == type([])):
	                        self.round_data(value, digit)
	            else:
	                if (self.is_number(data[i]) == True):
	                    value_new = self.roundUp(data[i], digit)
	                    data=[value_new if x==data[i] else x for x in data]
	                elif (type(data[i]) == type({}) or type(data[i]) == type([])):
	                    self.round_data(data[i], digit)
	    return data
	def is_number(self,s):  #判断字符串s是否是数值
	    try:
	        float(s)
	        return True
	    except Exception as e:
	        pass
	    try:
	        unicodedata.numeric(s)
	        return True
	    except Exception as e:
	        pass
	    return False
	
	def roundUp(self, value, digit):
	    flag = self.is_number(value)
	    if (flag == True and 'e' not in str(value)): #排除科学计数法
	        result = str(value)
	        if (float(value) < 0):
	            result = result[1:]
	            if (result != ''):
	                indexDec = result.find('.')
	                if (indexDec > 0):
	                    decimal = result[indexDec + 1:]
	                    decimalCount = len(decimal)
	                    if (decimalCount > digit):
	                        xiaoshu = result[indexDec + digit + 1]  # 第digit+1位小数
	                        if (int(xiaoshu) > 4):
	                            result = str(float(value) * -1 + pow(10, digit * -1))
	                            # 存在进位的可能,小数点会移位
	                            indexDec = result.find('.')
	                            result = result[:indexDec + digit + 1]
	                        else:
	                            result = result[:indexDec + digit + 1]
	                    else:
	                        lens = digit - len(result[indexDec:]) + 1
	                        for i in range(lens):
	                            result += '0'
	            result = float(result) * -1
	            return result
<think>好的,我现在需要帮用户解决如何用Python脚本将JSON标注文件换为YOLO格式,并且指定UTF-8编码的问题。首先,用户提到了参考之前的引用,里面有关于LabelMe和TT100K数据集换的例子,这可能涉及到不同的数据集结构,但用户的需求是通用的JSONYOLO。 用户的上一次问题关于YOLOv8的训练流程,现在向数据预处理部分,特别是标注格式换。这可能意味着用户已经准备好数据,但需要调整标注格式以适应YOLO的训练。我需要确保脚本能够处理常见的JSON结构,比如LabelMe的格式,或者用户特定的格式,但用户可能希望有一个通用的解决方案。 首先,我需要确定用户提供的JSON文件的结构。例如,LabelMe的JSON包含图像路径、尺寸和标注的坐标,而TT100K可能有不同的键名。用户可能希望脚本能够读取这些信息,并将边界框换为YOLO的归一化坐标(中心x, 中心y, 宽度, 高度)。 接下来,需要处理编码问题。用户明确要求使用UTF-8编码,这在Python中可以通过在打开文件时指定encoding='utf-8'来实现。需要确保读取和写入文件时都使用正确的编码,避免乱码。 然后,考虑目录结构。用户可能需要创建YOLO格式的标注目录,比如YOLO_labels,并按照图像文件名生成对应的txt文件。这需要处理文件路径的操作,使用os模块来创建目录和遍历文件。 另外,类别映射可能是一个关键点。用户提到TT100K的例子中将不同类别映射为数字标签,因此需要提供一个类别到索引的字典,比如{'person':0, 'bicycle':1},或者根据用户的具体需求调整。用户可能需要自定义这个映射,所以脚本需要灵活处理。 在遍历JSON文件时,需要检查每个文件是否以.json结尾,然后读取内容,提取图像尺寸和标注信息。对于每个标注对象,提取边界框坐标,并将其换为YOLO格式。换过程中,需要确保坐标归一化,除以图像的宽度和高度,得到0到1之间的值。 异常处理也很重要,比如文件不存在、JSON格式错误等,可能需要添加try-except块来捕获异常,并跳过有问题的文件,同时记录错误信息,方便用户调试。 最后,保存换后的txt文件时,要确保每行格式正确,使用空格分隔的类别索引和四个坐标值,并且使用UTF-8编码写入。同时,可能需要处理图像路径的问题,确保图像文件存在,并且路径正确。 测试脚本的部分,用户可能需要一个示例JSON文件来验证换是否正确,所以提供一个示例结构会有帮助。此外,提醒用户根据实际情况修改类别映射和路径设置,确保脚本适应不同的项目需求。 总结一下,我需要编写一个Python脚本,包含以下步骤: 1. 导入必要的库(os, json, PIL)。 2. 设置路径参数和类别映射。 3. 创建输出目录。 4. 遍历JSON文件,读取每个文件的内容。 5. 提取图像尺寸和标注信息。 6. 换边界框坐标到YOLO格式。 7. 写入txt文件,使用UTF-8编码。 8. 添加错误处理和日志输出。 还需要考虑用户可能遇到的常见问题,比如路径错误、编码问题、类别映射不正确等,并在回答中提供相应的注意事项和解决方案。</think>#### Python脚本实现JSONYOLO格式标注(UTF-8编码) 以下是分步实现方案,包含完整的代码示例和关键注释: ```python import os import json from PIL import Image def json_to_yolo(json_dir, image_dir, output_dir, class_map, encoding='utf-8'): """ 将LabelMe格式的JSON标注换为YOLO格式 :param json_dir: JSON标注文件目录 :param image_dir: 原始图片目录 :param output_dir: YOLO格式标注输出目录 :param class_map: 类别名称到索引的映射字典 :param encoding: 文件编码格式 """ # 创建输出目录 os.makedirs(output_dir, exist_ok=True) # 遍历所有JSON文件 for json_name in os.listdir(json_dir): if not json_name.endswith('.json'): continue json_path = os.path.join(json_dir, json_name) txt_name = os.path.splitext(json_name)[0] + '.txt' txt_path = os.path.join(output_dir, txt_name) try: # 读取JSON文件 with open(json_path, 'r', encoding=encoding) as f: data = json.load(f) # 获取图像尺寸 image_path = os.path.join(image_dir, data['imagePath']) with Image.open(image_path) as img: img_width, img_height = img.size # 换标注信息 yolo_lines = [] for shape in data['shapes']: # 验证类别是否在映射表中 class_name = shape['label'] if class_name not in class_map: print(f"警告:跳过未定义类别 {class_name}") continue # 提取边界框坐标 points = shape['points'] x_min = min(p[0] for p in points) x_max = max(p[0] for p in points) y_min = min(p[1] for p in points) y_max = max(p[1] for p in points) # 换为YOLO格式(归一化坐标) x_center = ((x_min + x_max) / 2) / img_width y_center = ((y_min + y_max) / 2) / img_height width = (x_max - x_min) / img_width height = (y_max - y_min) / img_height # 格式化为字符串 yolo_lines.append( f"{class_map[class_name]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}" ) # 写入YOLO标注文件 with open(txt_path, 'w', encoding=encoding) as f: f.write('\n'.join(yolo_lines)) print(f"成功换:{json_name} -> {txt_name}") except Exception as e: print(f"换失败:{json_name},错误信息:{str(e)}") # 示例用法 if __name__ == "__main__": CLASS_MAPPING = { 'person': 0, 'bicycle': 1, 'car': 2 } json_to_yolo( json_dir='path/to/json_annotations', image_dir='path/to/images', output_dir='path/to/yolo_labels', class_map=CLASS_MAPPING, encoding='utf-8' ) ``` #### 关键要素说明 1. **数据结构要求**: - JSON文件需包含图像路径`imagePath`和标注形状列表`shapes` - 每个形状应包含`label`字段和`points`坐标数组[^1] 2. **坐标换原理**: $$x_{\text{center}} = \frac{x_{\min} + x_{\max}}{2 \times \text{width}}$$ $$y_{\text{center}} = \frac{y_{\min} + y_{\max}}{2 \times \text{height}}$$ $$w = \frac{x_{\max} - x_{\min}}{\text{width}}, \quad h = \frac{y_{\max} - y_{\min}}{\text{height}}$$ 3. **编码规范**: - 使用`with open(..., encoding='utf-8')`确保读写一致性 - 保留6小数精度保证坐标精度[^2] #### 测试用例示例 假设存在`test_001.json`文件: ```json { "imagePath": "image_001.jpg", "shapes": [ { "label": "person", "points": [[120, 80], [280, 400]] } ] } ``` 换后生成`test_001.txt`: ``` 0 0.333333 0.400000 0.266667 0.533333 ``` #### 注意事项 1. **路径配置**:确保`image_dir`中的图片与JSON的`imagePath`字段匹配 2. **类别扩展**:修改`CLASS_MAPPING`字典可支持自定义类别 3. **异常处理**:脚本会自动跳过格式错误的JSON文件并打印错误信息 4. **数据验证**:建议换完成后使用YOLOv8的`model.val()`进行格式验证[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值