Adinosaur commited on
Commit
1c980b1
·
verified ·
1 Parent(s): e877058

Upload folder using huggingface_hub

Browse files
Files changed (49) hide show
  1. utils/cost/Embb.txt +5 -0
  2. utils/cost/Embs.txt +5 -0
  3. utils/cost/Lisa.txt +5 -0
  4. utils/cost/MMRt.txt +5 -0
  5. utils/cost/MMRv.txt +5 -0
  6. utils/cost/cost_text.py +87 -0
  7. utils/cost/count_q.py +24 -0
  8. utils/cost/p_cost.py +53 -0
  9. utils/cost/token_results.csv +0 -0
  10. utils/csv/4D-BA.py +61 -0
  11. utils/csv/4D-BO.py +55 -0
  12. utils/json/Lisa.py +168 -0
  13. utils/json/Lisa_jtj.py +60 -0
  14. utils/json/MMR.py +128 -0
  15. utils/json/MedQA_jtj.py +126 -0
  16. utils/json/RS_jtj.py +87 -0
  17. utils/json/RS_merge.py +51 -0
  18. utils/json/correct.py +50 -0
  19. utils/json/display_j.py +35 -0
  20. utils/json/emb_ai_jtp.py +215 -0
  21. utils/json/emb_jtj.py +140 -0
  22. utils/json/ems_jtj.py +209 -0
  23. utils/json/jsonl.py +30 -0
  24. utils/json/mask.py +91 -0
  25. utils/json/merge_json.py +57 -0
  26. utils/oss/oss_batch_upload.py +75 -0
  27. utils/oss/oss_upload.py +63 -0
  28. utils/oss/testis.py +4 -0
  29. utils/parquet/ChemQA_ptj.py +159 -0
  30. utils/parquet/MathVerse_ptj.py +209 -0
  31. utils/parquet/MathVision_ptj.py +178 -0
  32. utils/parquet/MathVista_ptj.py +199 -0
  33. utils/parquet/merge_jp.py +90 -0
  34. utils/parquet/pa_to_p.py +214 -0
  35. utils/parquet/pathQA_ptj.py +136 -0
  36. utils/upload/batch_download.py +58 -0
  37. utils/upload/batch_search.py +46 -0
  38. utils/upload/batch_upload.py +45 -0
  39. utils/upload/compare.py +75 -0
  40. utils/upload/download.py +10 -0
  41. utils/upload/jsonl_otest.py +32 -0
  42. utils/upload/jsonl_split.py +44 -0
  43. utils/upload/load_ll.py +151 -0
  44. utils/upload/load_vl.py +198 -0
  45. utils/upload/request_create.py +143 -0
  46. utils/upload_test/batch_create.py +16 -0
  47. utils/upload_test/batch_download.py +12 -0
  48. utils/upload_test/batch_search.py +10 -0
  49. utils/upload_test/finderror.jsonl +94 -0
utils/cost/Embb.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ 图片文件夹路径: /mnt/data/users/zys/proj/vlm_reasoning/dataset/data/EmbSpatial_bench
2
+ 总Token数量: 2121770
3
+ 计算规则:
4
+ - 高质量模式:基础 85 Tokens + 每区块 170 Tokens
5
+ - 低质量模式:固定 85 Tokens
utils/cost/Embs.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ 图片文件夹路径: /mnt/data/users/zys/proj/vlm_reasoning/dataset/data/EmbSpatial_sft
2
+ 总Token数量: 10827725
3
+ 计算规则:
4
+ - 高质量模式:基础 85 Tokens + 每区块 170 Tokens
5
+ - 低质量模式:固定 85 Tokens
utils/cost/Lisa.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ 图片文件夹路径: /mnt/data/users/zys/proj/vlm_reasoning/dataset/data/Lisa
2
+ 总Token数量: 944350
3
+ 计算规则:
4
+ - 高质量模式:基础 85 Tokens + 每区块 170 Tokens
5
+ - 低质量模式:固定 85 Tokens
utils/cost/MMRt.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ 图片文件夹路径: /mnt/data/users/zys/proj/vlm_reasoning/dataset/data/MMR/train2017
2
+ 总Token数量: 50084125
3
+ 计算规则:
4
+ - 高质量模式:基础 85 Tokens + 每区块 170 Tokens
5
+ - 低质量模式:固定 85 Tokens
utils/cost/MMRv.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ 图片文件夹路径: /mnt/data/users/zys/proj/vlm_reasoning/dataset/data/MMR/val2017
2
+ 总Token数量: 2101370
3
+ 计算规则:
4
+ - 高质量模式:基础 85 Tokens + 每区块 170 Tokens
5
+ - 低质量模式:固定 85 Tokens
utils/cost/cost_text.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import tiktoken
3
+ from tqdm import tqdm
4
+ from multiprocessing import Pool
5
+ import pandas as pd
6
+
7
+ # 全局编码器初始化(每个子进程独立初始化)
8
+ def init_process():
9
+ global encoder
10
+ encoder = tiktoken.get_encoding("cl100k_base")
11
+
12
+ def calculate_tokens(obj):
13
+ """计算单个对象的token数量(子进程内部调用)"""
14
+ global encoder
15
+ total_text = []
16
+
17
+ try:
18
+ messages = obj.get("body", {}).get("messages", [])
19
+ for msg in messages:
20
+ # 系统提示
21
+ if msg.get("role") == "system":
22
+ content = msg.get("content", "")
23
+ if content: # 跳过空内容
24
+ total_text.append(content)
25
+
26
+ # 用户消息
27
+ elif msg.get("role") == "user":
28
+ content = msg.get("content", [])
29
+ if isinstance(content, list):
30
+ for item in content:
31
+ if isinstance(item, dict) and item.get("type") == "text":
32
+ text = item.get("text", "")
33
+ if text:
34
+ total_text.append(text)
35
+ elif isinstance(content, dict) and content.get("type") == "text":
36
+ text = content.get("text", "")
37
+ if text:
38
+ total_text.append(text)
39
+
40
+ # 合并文本并计算Token
41
+ return len(encoder.encode("\n".join(total_text)))
42
+
43
+ except Exception as e:
44
+ print(f"处理错误: {e} | 数据: {obj.get('custom_id')}")
45
+ return 0
46
+
47
+ def process_line(line):
48
+ """处理单行数据"""
49
+ try:
50
+ data = json.loads(line)
51
+ return {
52
+ "custom_id": data.get("custom_id"),
53
+ "tokens": calculate_tokens(data)
54
+ }
55
+ except json.JSONDecodeError:
56
+ print(f"无效JSON: {line[:100]}...") # 打印前100字符辅助定位
57
+ return None
58
+ except Exception as e:
59
+ print(f"全局错误: {e}")
60
+ return None
61
+
62
+ if __name__ == "__main__":
63
+ # 读取数据
64
+ with open("/mnt/data/users/zys/proj/vlm_reasoning/request/vqa_batch_requests.jsonl", "r") as f:
65
+ lines = f.readlines()
66
+
67
+ # 并行处理
68
+ with Pool(processes=8, initializer=init_process) as pool:
69
+ results = []
70
+ with tqdm(total=len(lines), desc="处理进度") as pbar:
71
+ for result in pool.imap(process_line, lines):
72
+ if result is not None: # 过滤失败记录
73
+ results.append(result)
74
+ pbar.update()
75
+
76
+ # 保存结果
77
+ df = pd.DataFrame(results)
78
+ df.to_csv("token_results.csv", index=False)
79
+
80
+ # 统计输出
81
+ total_tokens = df["tokens"].sum()
82
+ avg_tokens = df["tokens"].mean()
83
+ print(f"统计报告:\n"
84
+ f"- 总Token数: {total_tokens:,}\n"
85
+ f"- 平均每条: {avg_tokens:.1f}\n"
86
+ f"- 最大单条: {df['tokens'].max()}\n"
87
+ f"- 有效数据: {len(df)}/{len(lines)}")
utils/cost/count_q.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ def count_questions(json_file):
4
+ with open(json_file, 'r', encoding='utf-8') as f:
5
+ data = json.load(f)
6
+
7
+ total = 0
8
+ for obj in data:
9
+ # 获取question列表,若不存在则返回空列表
10
+ questions = obj.get('question', [])
11
+ # 确保questions是列表类型
12
+ if not isinstance(questions, list):
13
+ questions = [questions]
14
+ total += len(questions)
15
+ return total
16
+
17
+ if __name__ == "__main__":
18
+ import sys
19
+ if len(sys.argv) != 2:
20
+ print("Usage: python count_questions.py <json_file>")
21
+ sys.exit(1)
22
+ file_path = sys.argv[1]
23
+ count = count_questions(file_path)
24
+ print(f"Total number of questions: {count}")
utils/cost/p_cost.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from PIL import Image
3
+
4
+ def calculate_image_tokens(image_path, detail="high"):
5
+ if detail == "low":
6
+ return 85 # 低质量模式固定 85 Tokens
7
+
8
+ # 高质量模式(基础 85 Tokens + 分块计算)
9
+ with Image.open(image_path) as img:
10
+ width, height = img.size
11
+ short_side = min(width, height)
12
+
13
+ # 判断是否需要缩放
14
+ if short_side < 768:
15
+ # 不缩放,直接使用原图尺寸
16
+ new_width, new_height = width, height
17
+ else:
18
+ # 缩放短边到 768px
19
+ scale = 768 / short_side
20
+ new_width = int(width * scale)
21
+ new_height = int(height * scale)
22
+
23
+ # 计算分块数量(向上取整)
24
+ tiles_width = (new_width + 511) // 512
25
+ tiles_height = (new_height + 511) // 512
26
+ total_tiles = tiles_width * tiles_height
27
+
28
+ # 总 Tokens = 基础 85 + 分块数 × 170
29
+ return 85 + (total_tiles * 170)
30
+
31
+ def calculate_folder_tokens(folder_path, detail="high"):
32
+ total = 0
33
+ for filename in os.listdir(folder_path):
34
+ if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp')):
35
+ path = os.path.join(folder_path, filename)
36
+ total += calculate_image_tokens(path, detail)
37
+ return total
38
+
39
+ # 使用示例
40
+ folder_path = "/mnt/data/users/zys/proj/vlm_reasoning/dataset/data/EmbSpatial_sft"
41
+ output_file = "Embs.txt"
42
+
43
+ total_tokens = calculate_folder_tokens(folder_path, detail="high")
44
+
45
+ # 保存结果到文件
46
+ with open(output_file, "w", encoding="utf-8") as f:
47
+ f.write(f"图片文件夹路径: {folder_path}\n")
48
+ f.write(f"总Token数量: {total_tokens}\n")
49
+ f.write("计算规则:\n")
50
+ f.write("- 高质量模式:基础 85 Tokens + 每区块 170 Tokens\n")
51
+ f.write("- 低质量模式:固定 85 Tokens\n")
52
+
53
+ print(f"结果已保存至 {output_file}")
utils/cost/token_results.csv ADDED
The diff for this file is too large to render. See raw diff
 
utils/csv/4D-BA.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from pathlib import Path
3
+
4
+ def process_json(input_path, output_path, file_stem):
5
+ # 读取原始JSON文件
6
+ with open(input_path, 'r', encoding='utf-8') as f:
7
+ original_data = json.load(f)
8
+
9
+ processed = []
10
+
11
+ # 处理每个条目
12
+ for index, (key, item) in enumerate(original_data.items()):
13
+ # 生成媒体路径
14
+ video_id = key.split('_')[0]
15
+ media_path = "./" + (Path("data") / file_stem / video_id).as_posix()
16
+
17
+ # 处理选项
18
+ options = []
19
+ for opt_id in ['A', 'B', 'C', 'D']:
20
+ if text := item.get(f'({opt_id})', ''):
21
+ options.append({"id": opt_id, "text": text.strip()})
22
+
23
+ # 处理答案
24
+ try:
25
+ answer_num = int(item['Answer index'])
26
+ answer_ids = [options[answer_num]['id']] if 0 <= answer_num < len(options) else []
27
+ except (ValueError, IndexError, KeyError):
28
+ answer_ids = []
29
+
30
+ # 构建数据结构
31
+ processed.append({
32
+ "index": index,
33
+ "media_type": "Video",
34
+ "media_paths": media_path,
35
+ "description": item.get("Category", ""),
36
+ "task_type": "Vision-Question-Answer",
37
+ "question": [item.get("Question", "")],
38
+ "question_type": "multi-choice",
39
+ "annotations": {},
40
+ "options": options,
41
+ "answer": answer_ids,
42
+ "source": "4D-Bench",
43
+ "domain": "Embodied_ai"
44
+ })
45
+
46
+ # 保存结果
47
+ with open(output_path, 'w', encoding='utf-8') as f:
48
+ json.dump(processed, f, indent=2, ensure_ascii=False)
49
+
50
+ if __name__ == "__main__":
51
+ # 静态参数配置
52
+ input_path = "/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/emb_ai/4d/4D_Object_Question_Answering/data/4d_qa.json" # 默认输入文件
53
+ output_path = "/mnt/data/users/zys/proj/vlm_reasoning/dataset/4D_Object_Question_Answering.json" # 默认输出文件
54
+ file_stem = "4D_Object_Question_Answering" # 专用数据集标识
55
+
56
+ # 执行处理流程
57
+ process_json(
58
+ input_path=input_path,
59
+ output_path=output_path,
60
+ file_stem=file_stem
61
+ )
utils/csv/4D-BO.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import json
3
+ from pathlib import Path
4
+
5
+ def csv_to_json(csv_path, json_path, file_stem):
6
+ # 初始化JSON数据结构
7
+ json_data = []
8
+
9
+ with open(csv_path, 'r', encoding='utf-8') as csv_file:
10
+ csv_reader = csv.DictReader(csv_file)
11
+
12
+ for index, row in enumerate(csv_reader):
13
+ # 构建media_path
14
+ folder_name = row['folder_name']
15
+ media_path = "./" + (Path("data") / file_stem / folder_name).as_posix()
16
+
17
+ # 收集五个caption作为答案
18
+ answer = [
19
+ row['caption_1'],
20
+ row['caption_2'],
21
+ row['caption_3'],
22
+ row['caption_4'],
23
+ row['caption_5']
24
+ ]
25
+
26
+ # 构建JSON条目
27
+ entry = {
28
+ "index": index,
29
+ "media_type": "Video",
30
+ "media_paths": media_path,
31
+ "description": "",
32
+ "task_type": "Vision-Question-Answer",
33
+ "question": ["Please generate descriptive captions for this multi-view video."],
34
+ "question_type": "free-form",
35
+ "annotations": {},
36
+ "options": [],
37
+ "answer": answer,
38
+ "source": "4D-Bench",
39
+ "domain": "Embodied_ai"
40
+ }
41
+
42
+ json_data.append(entry)
43
+
44
+ # 写入JSON文件
45
+ with open(json_path, 'w', encoding='utf-8') as json_file:
46
+ json.dump(json_data, json_file, indent=2)
47
+
48
+ # 使用示例
49
+ if __name__ == "__main__":
50
+ # 用户需要修改以下参数
51
+ INPUT_CSV = "/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/emb_ai/4d/4D_Object_Captioning/data/human_annotations.csv" # 输入CSV文件路径
52
+ OUTPUT_JSON = "/mnt/data/users/zys/proj/vlm_reasoning/dataset/4D_Object_Captioning.json" # 输出JSON文件路径
53
+ FILE_STEM = "4D_Object_Captioning" # media_path中的file_stem部分
54
+
55
+ csv_to_json(INPUT_CSV, OUTPUT_JSON, FILE_STEM)
utils/json/Lisa.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import math
3
+ from pathlib import Path
4
+ import numpy as np
5
+ import cv2
6
+ from pycocotools import mask as mask_utils
7
+ from PIL import Image, ExifTags
8
+
9
+ def get_image_dimensions(image_path):
10
+ """更健壮的尺寸获取方法,包含多种异常处理"""
11
+ try:
12
+ # 优先使用PIL获取信息
13
+ with Image.open(image_path) as img:
14
+ width, height = img.size
15
+ orientation = 1
16
+
17
+ try:
18
+ exif = img._getexif() or {}
19
+ for tag, name in ExifTags.TAGS.items():
20
+ if name == 'Orientation':
21
+ orientation = exif.get(tag, 1)
22
+ break
23
+ except Exception as e:
24
+ print(f"EXIF读取警告 [{image_path.name}]: {str(e)}")
25
+
26
+ # 根据方向调整宽高
27
+ if orientation in [5, 6, 7, 8]:
28
+ return height, width # 返回交换后的尺寸 (width, height)
29
+ else:
30
+ return width, height
31
+
32
+ except Exception as pil_error:
33
+ print(f"PIL读取失败 [{image_path.name}], 尝试OpenCV: {str(pil_error)}")
34
+ try:
35
+ # PIL失败时使用OpenCV
36
+ img = cv2.imread(str(image_path))
37
+ if img is not None:
38
+ h, w = img.shape[:2]
39
+ return w, h
40
+ raise ValueError("OpenCV返回空图像")
41
+ except Exception as cv_error:
42
+ print(f"严重错误: 无法获取尺寸 [{image_path.name}]: {str(cv_error)}")
43
+ return (0, 0) # 返回无效尺寸,后续会报错但避免崩溃
44
+
45
+ def points_to_rle(points, img_dimensions):
46
+ """带安全坐标钳位的多边形转换"""
47
+ width, height = img_dimensions
48
+ mask = np.zeros((height, width), dtype=np.uint8)
49
+
50
+ polygon = []
51
+ for x, y in points:
52
+ # 先四舍五入再钳位(根据标注工具特性选择策略)
53
+ safe_x = min(max(0, int(round(x))), width - 1)
54
+ safe_y = min(max(0, int(round(y))), height - 1)
55
+
56
+ # 若需要更保守处理(如截断小数部分)
57
+ # safe_x = min(max(0, int(math.floor(x))), width - 1)
58
+ # safe_y = min(max(0, int(math.floor(y))), height - 1)
59
+
60
+ polygon.append((safe_x, safe_y))
61
+
62
+ # 验证多边形有效性
63
+ if len(polygon) < 3:
64
+ raise ValueError(f"无效多边形,点数不足3个")
65
+
66
+ # 生成掩码
67
+ cv2.fillPoly(mask, [np.array(polygon, dtype=np.int32)], color=1)
68
+ rle = mask_utils.encode(np.asfortranarray(mask))
69
+
70
+ return {
71
+ "size": [height, width],
72
+ "counts": rle['counts'].decode('utf-8')
73
+ }
74
+
75
+ def convert_medical_json(input_file, config=None):
76
+ """增强版转换函数"""
77
+ cfg = {
78
+ "task_type": "Image-Segmentation",
79
+ "source": "Lisa",
80
+ "domain": "General",
81
+ **(config or {})
82
+ }
83
+
84
+ try:
85
+ input_path = Path(input_file)
86
+ image_path = input_path.with_suffix('.jpg')
87
+
88
+ # 强制校验图片存在性
89
+ if not image_path.exists():
90
+ raise FileNotFoundError(f"关联图片不存在: {image_path.name}")
91
+
92
+ media_paths=(Path(".") / "data" / cfg['source'] / image_path.name).as_posix()
93
+ media_paths = f"./{media_paths}"
94
+ # 获取真实尺寸(已处理EXIF)
95
+ width, height = get_image_dimensions(image_path)
96
+ if width == 0 or height == 0:
97
+ raise ValueError("获取图片尺寸失败")
98
+
99
+ # 处理标注数据
100
+ with open(input_file, 'r', encoding='utf-8') as f:
101
+ raw_data = json.load(f)
102
+
103
+ annotations = []
104
+ for shape in raw_data.get('shapes', []):
105
+ if shape.get('label') != 'target':
106
+ continue
107
+
108
+ points = shape.get('points', [])
109
+ try:
110
+ rle = points_to_rle(points, (width, height))
111
+ annotations.append({
112
+ "bbox": [],
113
+ "segmentation": rle,
114
+ "category_name": ""
115
+ })
116
+ except ValueError as e:
117
+ print(f"标注跳过 [{input_path.name}]: {str(e)}")
118
+
119
+ return [{
120
+ "index": 0,
121
+ "media_type": "image",
122
+ "media_paths": media_paths,
123
+ "description": "",
124
+ "task_type": cfg['task_type'],
125
+ "question": raw_data.get('text', []),
126
+ "question_type": "detection-form",
127
+ "options": [],
128
+ "annotations": [annotations],
129
+ "answer": [],
130
+ "source": cfg['source'],
131
+ "domain": cfg['domain']
132
+ }]
133
+
134
+ except Exception as e:
135
+ print(f"转换失败 [{input_path.name}]: {str(e)}")
136
+ return None
137
+
138
+ def batch_convert(input_dir, output_file):
139
+ """批量处理增强版"""
140
+ input_dir = Path(input_dir)
141
+ all_data = []
142
+ success_count = 0
143
+ failed_files = []
144
+ index_counter = 0 # 新增全局索引计数器
145
+
146
+ for json_file in input_dir.glob('*.json'):
147
+ if result := convert_medical_json(json_file):
148
+ # 为每个条目分配递增索引
149
+ for item in result:
150
+ item["index"] = index_counter
151
+ index_counter += 1
152
+ all_data.extend(result)
153
+ success_count += len(result)
154
+ else:
155
+ failed_files.append(json_file.name)
156
+
157
+ with open(output_file, 'w', encoding='utf-8') as f:
158
+ json.dump(all_data, f, indent=2, ensure_ascii=False)
159
+
160
+ print(f"转换完成: 成功 {success_count} 个文件,失败 {len(failed_files)} 个")
161
+ if failed_files:
162
+ print("失败文件列表:\n" + "\n".join(failed_files))
163
+
164
+ if __name__ == "__main__":
165
+ batch_convert(
166
+ input_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/general/lisa/image/val",
167
+ output_file="/mnt/data/users/zys/proj/vlm_reasoning/utils/json/converted_dataset3.json"
168
+ )
utils/json/Lisa_jtj.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import argparse
4
+
5
+ def delete_json_files(folder_path):
6
+ """删除指定文件夹中的所有JSON文件"""
7
+ for filename in os.listdir(folder_path):
8
+ if filename.endswith('.json'):
9
+ file_path = os.path.join(folder_path, filename)
10
+ try:
11
+ os.remove(file_path)
12
+ print(f"已删除JSON文件: {file_path}")
13
+ except Exception as e:
14
+ print(f"删除 {file_path} 时出错: {str(e)}")
15
+
16
+ def merge_folders(input_path, output_dir):
17
+ """合并三个文件夹的内容到输出目录"""
18
+ # 确保输出目录存在
19
+ os.makedirs(output_dir, exist_ok=True)
20
+
21
+ # 定义要处理的子文件夹
22
+ subfolders = ['train', 'test', 'val']
23
+
24
+ # 检查输入目录结构有效性
25
+ for sub in subfolders:
26
+ if not os.path.exists(os.path.join(input_path, sub)):
27
+ raise ValueError(f"输入路径中缺少 {sub} 文件夹")
28
+
29
+ # 处理每个子文件夹
30
+ for sub in subfolders:
31
+ sub_path = os.path.join(input_path, sub)
32
+
33
+ # 删除JSON文件
34
+ delete_json_files(sub_path)
35
+
36
+ # 复制所有文件到目标目录
37
+ for filename in os.listdir(sub_path):
38
+ src = os.path.join(sub_path, filename)
39
+ dst = os.path.join(output_dir, filename)
40
+
41
+ # 处理重复文件名(保留后复制到的文件)
42
+ if os.path.exists(dst):
43
+ print(f"警告: {filename} 已存在,将被覆盖")
44
+
45
+ shutil.copy2(src, dst)
46
+ print(f"已复制: {src} -> {dst}")
47
+
48
+ if __name__ == "__main__":
49
+ parser = argparse.ArgumentParser(description="合并训练/测试/验证集并删除JSON文件")
50
+ parser.add_argument('-i','--input_path', required=True, help='包含train/test/val文件夹的输入路径')
51
+ parser.add_argument('-o','--output_dir', required=True, help='合并后的输出目录路径')
52
+
53
+ args = parser.parse_args()
54
+
55
+ try:
56
+ print(f"开始处理,输入路径: {args.input_path}")
57
+ merge_folders(args.input_path, args.output_dir)
58
+ print("\n操作完成!合并后的文件位于:", os.path.abspath(args.output_dir))
59
+ except Exception as e:
60
+ print(f"\n发生错误: {str(e)}")
utils/json/MMR.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from concurrent.futures import ThreadPoolExecutor
5
+
6
+ def convert_medical_json(input_file, output_file, config=None):
7
+ default_config = {
8
+ "task_type": "Visual_Question_Answering",
9
+ "source": "Embspatial",
10
+ "domain": "Embodied_ai"
11
+ }
12
+ cfg = {**default_config, **(config or {})}
13
+
14
+ try:
15
+ with open(input_file, 'r', encoding='utf-8') as f:
16
+ raw_data = json.load(f)
17
+
18
+ converted = []
19
+ for index, item in enumerate(raw_data if isinstance(raw_data, list) else [raw_data]):
20
+ # 重构媒体路径
21
+ img_path = item['file_name']
22
+ media_path = (Path("data") / cfg['source'] / img_path).as_posix()
23
+ media_path = f"./{media_path}"
24
+ # 处理annotations(原样保留answers结构)
25
+ annotations = []
26
+ for answer_group in item.get("answers", []): # 遍历每个答案组
27
+ group_annotations = []
28
+ for answer in answer_group: # 遍历组中的每个答案对象
29
+ annotation = {
30
+ "bbox": answer.get("bbox", []),
31
+ "segmentation": answer.get("segmentation", {}),
32
+ "category_name": answer.get("category_name", "")
33
+ }
34
+ group_annotations.append(annotation)
35
+ annotations.append(group_annotations)
36
+
37
+ converted.append({
38
+ "index": index,
39
+ "media_type": "image",
40
+ "media_paths": media_path,
41
+ "description": "",
42
+ "task_type": cfg['task_type'],
43
+ "question": item.get('questions', []), # 直接取列表值
44
+ "question_type": "detection-form",
45
+ "options": [],
46
+ "annotations": annotations, # 保持原列表套列表结构
47
+ "answer": item.get('raw_answers', []), # 直接取列表值
48
+ "source": cfg['source'],
49
+ "domain": cfg['domain']
50
+ })
51
+
52
+ with open(output_file, 'w', encoding='utf-8') as f:
53
+ json.dump(converted, f, indent=2, ensure_ascii=False)
54
+ return True
55
+
56
+ except Exception as e:
57
+ print(f"转换失败: {input_file} → {str(e)}")
58
+ return False
59
+
60
+ def process_single_file(input_path, output_dir, config):
61
+ """单个文件处理函数(扁平化输出)"""
62
+ try:
63
+ # 生成输出路径:输出目录 + 原文件名
64
+ output_file = output_dir / input_path.name
65
+
66
+ return convert_medical_json(
67
+ input_file=str(input_path),
68
+ output_file=str(output_file),
69
+ config=config
70
+ )
71
+ except Exception as e:
72
+ print(f"文件处理异常: {input_path} → {str(e)}")
73
+ return False
74
+
75
+ def batch_convert_json(input_dir, output_dir, config=None, max_workers=8):
76
+ """扁平化批量处理器"""
77
+ input_path = Path(input_dir)
78
+ output_path = Path(output_dir)
79
+
80
+ # 创建输出目录(只需创建一次)
81
+ output_path.mkdir(parents=True, exist_ok=True)
82
+
83
+ if not input_path.exists():
84
+ raise FileNotFoundError(f"输入目录不存在: {input_dir}")
85
+
86
+ success_count = 0
87
+ failure_count = 0
88
+
89
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
90
+ futures = []
91
+ # 仅遍历当前目录的JSON文件
92
+ for input_file in input_path.glob('*.json'): # 关键修改点
93
+ if input_file.is_file(): # 确保是文件
94
+ futures.append(
95
+ executor.submit(
96
+ process_single_file,
97
+ input_path=input_file,
98
+ output_dir=output_path,
99
+ config=config
100
+ )
101
+ )
102
+
103
+ # 统计处理结果
104
+ for future in futures:
105
+ if future.result():
106
+ success_count += 1
107
+ else:
108
+ failure_count += 1
109
+
110
+ print(f"\n处理完成: 成功 {success_count} 个文件,失败 {failure_count} 个文件")
111
+ print(f"输出目录: {output_path.resolve()}")
112
+
113
+ if __name__ == "__main__":
114
+ custom_config = {
115
+ "source": "MMR",
116
+ "task_type": "Multi-Format-Task",
117
+ "domain": "General"
118
+ }
119
+
120
+ try:
121
+ batch_convert_json(
122
+ input_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/general/mmr",
123
+ output_dir="/mnt/data/users/zys/proj/vlm_reasoning/dataset",
124
+ config=custom_config,
125
+ max_workers=os.cpu_count() * 2
126
+ )
127
+ except Exception as e:
128
+ print(f"批量处理异常终止: {str(e)}")
utils/json/MedQA_jtj.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from concurrent.futures import ThreadPoolExecutor
5
+
6
+ def convert_medical_json(input_file, output_file, config=None):
7
+ """医疗数据格式转换器(扁平化输出版本)"""
8
+ default_config = {
9
+ "task_type": "Visual_Question_Answering",
10
+ "source": "OmniMedVQA"
11
+ }
12
+ cfg = {**default_config, **(config or {})}
13
+
14
+ try:
15
+ with open(input_file, 'r', encoding='utf-8') as f:
16
+ raw_data = json.load(f)
17
+
18
+ converted = []
19
+ for index, item in enumerate(raw_data if isinstance(raw_data, list) else [raw_data]):
20
+ # 重构媒体路径
21
+ img_path = item['image_path'].split('Images/', 1)[-1]
22
+ media_path = f"./{cfg['source']}/{img_path}"
23
+
24
+ # 构建选项结构
25
+ options = []
26
+ for opt_id in ['A', 'B', 'C', 'D']:
27
+ if text := item.get(f'option_{opt_id}', ''):
28
+ options.append({"id": opt_id, "text": text.strip()})
29
+
30
+ # 匹配正确答案
31
+ gt_answer = str(item['gt_answer']).strip()
32
+ correct_ids = [opt['id'] for opt in options if opt['text'] == gt_answer]
33
+
34
+ converted.append({
35
+ "index": index,
36
+ "media_type": "image",
37
+ "media_paths": media_path,
38
+ "description": "",
39
+ "task_type": cfg['task_type'],
40
+ "question": [item['question']],
41
+ "question_type": "multi-choice",
42
+ "options": options,
43
+ "annotations": [],
44
+ "answer": correct_ids,
45
+ "source": cfg['source'],
46
+ "domain": "Biomedical"
47
+ })
48
+
49
+ with open(output_file, 'w', encoding='utf-8') as f:
50
+ json.dump(converted, f, indent=2, ensure_ascii=False)
51
+ return True
52
+
53
+ except Exception as e:
54
+ print(f"转换失败: {input_file} → {str(e)}")
55
+ return False
56
+
57
+ def process_single_file(input_path, output_dir, config):
58
+ """单个文件处理函数(扁平化输出)"""
59
+ try:
60
+ # 生成输出路径:输出目录 + 原文件名
61
+ output_file = output_dir / input_path.name
62
+
63
+ return convert_medical_json(
64
+ input_file=str(input_path),
65
+ output_file=str(output_file),
66
+ config=config
67
+ )
68
+ except Exception as e:
69
+ print(f"文件处理异常: {input_path} → {str(e)}")
70
+ return False
71
+
72
+ def batch_convert_json(input_dir, output_dir, config=None, max_workers=8):
73
+ """扁平化批量处理器"""
74
+ input_path = Path(input_dir)
75
+ output_path = Path(output_dir)
76
+
77
+ # 创建输出目录(只需创建一次)
78
+ output_path.mkdir(parents=True, exist_ok=True)
79
+
80
+ if not input_path.exists():
81
+ raise FileNotFoundError(f"输入目录不存在: {input_dir}")
82
+
83
+ success_count = 0
84
+ failure_count = 0
85
+
86
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
87
+ futures = []
88
+ # 遍历所有JSON文件(忽略目录结构)
89
+ for root, _, files in os.walk(input_dir):
90
+ for filename in files:
91
+ if filename.lower().endswith('.json'):
92
+ input_file = Path(root) / filename
93
+ futures.append(
94
+ executor.submit(
95
+ process_single_file,
96
+ input_path=input_file,
97
+ output_dir=output_path,
98
+ config=config
99
+ )
100
+ )
101
+
102
+ # 统计处理结果
103
+ for future in futures:
104
+ if future.result():
105
+ success_count += 1
106
+ else:
107
+ failure_count += 1
108
+
109
+ print(f"\n处理完成: 成功 {success_count} 个文件,失败 {failure_count} 个文件")
110
+ print(f"输出目录: {output_path.resolve()}")
111
+
112
+ if __name__ == "__main__":
113
+ custom_config = {
114
+ "source": "OmniMedVQA",
115
+ "task_type": "Visual_Question_Answering"
116
+ }
117
+
118
+ try:
119
+ batch_convert_json(
120
+ input_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/biomedical/medvqa/OmniMedVQA/QA_information",
121
+ output_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/biomedical/medvqa",
122
+ config=custom_config,
123
+ max_workers=min(os.cpu_count() * 2, 32)
124
+ )
125
+ except Exception as e:
126
+ print(f"批量处理异常终止: {str(e)}")
utils/json/RS_jtj.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from collections import defaultdict
3
+
4
+ def transform_json_structure(input_path, output_path):
5
+ """
6
+ 将RSVQA原始JSON格式转换为按图片分组的结构
7
+ :param input_path: 输入JSON文件路径
8
+ :param output_path: 输出JSON文件路径
9
+ """
10
+ # 读取原始数据
11
+ try:
12
+ with open(input_path, 'r', encoding='utf-8') as f:
13
+ original_data = json.load(f)
14
+ question_list = original_data.get('merged_data', [])
15
+ except Exception as e:
16
+ raise RuntimeError(f"读取输入文件失败: {str(e)}")
17
+
18
+ # 创建分组容器(自动处理键不存在的情况)
19
+ image_groups = defaultdict(list)
20
+
21
+ # 分组处理原始数据
22
+ for qa_pair in question_list:
23
+ try:
24
+ img_id = qa_pair['img_id']
25
+ # 过滤无效数据
26
+ if not isinstance(img_id, int) or img_id < 0:
27
+ continue
28
+
29
+ # 每组最多保留15个问题
30
+ if len(image_groups[img_id]) < 15:
31
+ image_groups[img_id].append({
32
+ 'question': qa_pair.get('question', ''),
33
+ 'answer': qa_pair.get('answer', '')
34
+ })
35
+ except KeyError as ke:
36
+ print(f"跳过缺少关键字段的数据: {str(ke)}")
37
+ continue
38
+
39
+ # 构建新数据结构
40
+ transformed_data = []
41
+ for index, (img_id, qa_pairs) in enumerate(image_groups.items()):
42
+ # 生成媒体文件路径
43
+ media_path = f"./data/RSVQA/{img_id}.png"
44
+
45
+ # 提取问题和答案列表
46
+ questions = [pair['question'] for pair in qa_pairs]
47
+ answers = [pair['answer'] for pair in qa_pairs]
48
+
49
+ # 构建输出格式
50
+ transformed_data.append({
51
+ "index": img_id,
52
+ "media_type": "image",
53
+ "media_paths": media_path,
54
+ "description": "",
55
+ "task_type": "Vision-Question-Answer",
56
+ "question": questions,
57
+ "question_type": "free-form",
58
+ "annotations": [],
59
+ "options": [],
60
+ "answer": answers,
61
+ "source": "RSVQA",
62
+ "domain": "Satellite-Remote-Sensing"
63
+ })
64
+
65
+ # 保存转换后的数据
66
+ try:
67
+ with open(output_path, 'w', encoding='utf-8') as f:
68
+ json.dump(transformed_data, f, indent=2, ensure_ascii=False)
69
+ except Exception as e:
70
+ raise RuntimeError(f"写入输出文件失败: {str(e)}")
71
+
72
+ if __name__ == "__main__":
73
+ import argparse
74
+
75
+ parser = argparse.ArgumentParser(description='RSVQA数据格式转换工具')
76
+ parser.add_argument('-i','--input', type=str, required=True, help='输入JSON文件路径')
77
+ parser.add_argument('-o','--output', type=str, default='transformed.json',
78
+ help='输出JSON文件路径 (默认: transformed.json)')
79
+
80
+ args = parser.parse_args()
81
+
82
+ try:
83
+ transform_json_structure(args.input, args.output)
84
+ print(f"转换成功!输出文件已保存至: {args.output}")
85
+ except Exception as e:
86
+ print(f"处理过程中发生错误: {str(e)}")
87
+ exit(1)
utils/json/RS_merge.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ 这个程序是需要先运行的一个程序它的作用是将哦answers和of questions合并为一个JSON文件便于去转换
3
+ 使用方法很简单看程序最后的使用示例,只需填入两个文件的地址然后给定输出地址就可以
4
+ '''
5
+
6
+ import json
7
+ from collections import defaultdict
8
+
9
+ def deep_merge(base_dict, merge_dict):
10
+ """递归合并字典,处理嵌套结构和冲突"""
11
+ for key in merge_dict:
12
+ if key in base_dict:
13
+ # 处理字典类型合并
14
+ if isinstance(base_dict[key], dict) and isinstance(merge_dict[key], dict):
15
+ deep_merge(base_dict[key], merge_dict[key])
16
+ # 处理数组类型合并(保留原数组)
17
+ elif isinstance(base_dict[key], list) and isinstance(merge_dict[key], list):
18
+ base_dict[key] = base_dict[key] + merge_dict[key]
19
+ # 处理其他类型冲突(保留原始值)
20
+ else:
21
+ pass # 保持base_dict原有值
22
+ else:
23
+ # 新增不存在字段
24
+ base_dict[key] = merge_dict[key]
25
+ return base_dict
26
+
27
+ def merge_json_files(answers_file, questions_file, output_file):
28
+ # 加载数据并建立索引
29
+ with open(answers_file) as f:
30
+ answers = {item['id']: item for item in json.load(f)['answers']}
31
+
32
+ with open(questions_file) as f:
33
+ questions = json.load(f)['questions']
34
+
35
+ # 智能合并处理
36
+ merged = []
37
+ for q in questions:
38
+ merged_q = q.copy()
39
+ # 处理answers_ids关联
40
+ for ans_id in q.get('answers_ids', []):
41
+ if ans_id in answers:
42
+ # 执行深度合并
43
+ merged_q = deep_merge(merged_q, answers[ans_id])
44
+ merged.append(merged_q)
45
+
46
+ # 保存结果
47
+ with open(output_file, 'w') as f:
48
+ json.dump({"merged_data": merged}, f, indent=2, ensure_ascii=False)
49
+
50
+ # 使用示例
51
+ merge_json_files("/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/Satellite/USGSanswers.json", "/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/Satellite/USGSquestions.json", '/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/Satellite/merged_output.json')
utils/json/correct.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ def replace_underscores_in_task_type(data):
5
+ """处理JSON数据,替换task_type中的下划线"""
6
+ for item in data:
7
+ if isinstance(item, dict) and 'task_type' in item:
8
+ original = item['task_type']
9
+ if isinstance(original, str):
10
+ item['task_type'] = original.replace('_', '-')
11
+ return data
12
+
13
+ def process_json_file(file_path):
14
+ """处理单个JSON文件"""
15
+ try:
16
+ with open(file_path, 'r+', encoding='utf-8') as f:
17
+ # 读取并验证数据格式
18
+ data = json.load(f)
19
+ if not isinstance(data, list):
20
+ print(f" 已跳过非数组文件:{file_path}")
21
+ return
22
+
23
+ # 处理数据并写回
24
+ modified_data = replace_underscores_in_task_type(data)
25
+ f.seek(0)
26
+ json.dump(modified_data, f, indent=4, ensure_ascii=False)
27
+ f.truncate()
28
+
29
+ print(f" 成功处理:{os.path.basename(file_path)}")
30
+
31
+ except Exception as e:
32
+ print(f" 处理失败 [{os.path.basename(file_path)}]:{str(e)}")
33
+
34
+ def process_directory(target_dir):
35
+ """处理目录下的JSON文件(不包含子目录)"""
36
+ for file in os.listdir(target_dir):
37
+ if file.lower().endswith('.json'):
38
+ file_path = os.path.join(target_dir, file)
39
+ if os.path.isfile(file_path):
40
+ process_json_file(file_path)
41
+
42
+ if __name__ == '__main__':
43
+ input_directory = "/mnt/data/users/zys/proj/vlm_reasoning/utils/json" # 修改为你的目录路径
44
+
45
+ if os.path.isdir(input_directory):
46
+ print(f" 正在处理目录:{input_directory}")
47
+ process_directory(input_directory)
48
+ print(" 处理完成")
49
+ else:
50
+ print(f" 目录不存在:{input_directory}")
utils/json/display_j.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import sys
3
+
4
+ def show_jsonl_object(file_path, index=0):
5
+ """
6
+ 从JSONL文件中提取并展示指定索引位置的对象
7
+ :param file_path: JSONL文件路径
8
+ :param index: 要提取的对象索引(从0开始)
9
+ """
10
+ try:
11
+ with open(file_path, 'r', encoding='utf-8') as f:
12
+ for i, line in enumerate(f):
13
+ if i == index:
14
+ try:
15
+ data = json.loads(line)
16
+ print(f"第 {index} 个对象的JSON内容:")
17
+ print(json.dumps(data, indent=4, ensure_ascii=False))
18
+ return
19
+ except json.JSONDecodeError:
20
+ print(f"错误:第 {index} 行不是有效的JSON格式")
21
+ return
22
+ print(f"警告:文件只有 {i+1} 行,无法读取第 {index} 行")
23
+
24
+ except FileNotFoundError:
25
+ print(f"错误:文件 {file_path} 不存在")
26
+
27
+ if __name__ == "__main__":
28
+ if len(sys.argv) < 2:
29
+ print("使用方法:python show_jsonl.py <文件路径> [行号]")
30
+ sys.exit(1)
31
+
32
+ file_path = sys.argv[1]
33
+ index = int(sys.argv[2]) if len(sys.argv) > 2 else 0
34
+
35
+ show_jsonl_object(file_path, index)
utils/json/emb_ai_jtp.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import base64
4
+ import sys
5
+ import time
6
+ import logging
7
+ from pathlib import Path
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from typing import Tuple, List
10
+
11
+ from PIL import Image
12
+ import io
13
+
14
+ # 配置日志格式
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ format="%(asctime)s - %(levelname)s - %(message)s",
18
+ stream=sys.stdout
19
+ )
20
+
21
+ def process_json_element(element: dict,
22
+ index: int,
23
+ output_dir: Path,
24
+ overwrite: bool,
25
+ output_format: str) -> Tuple[int, str]: # 新增格式参数
26
+ """处理单个JSON数组元素(支持格式选择)"""
27
+ try:
28
+ # 参数校验并统一转为小写
29
+ output_format = output_format.lower() # 确保后续判断统一使用小写
30
+ if output_format not in ['jpg', 'png']:
31
+ raise ValueError(f"不支持的格式: {output_format}")
32
+
33
+ # 动态生成路径参数(文件扩展名保持小写)
34
+ file_ext = output_format
35
+ # 映射PIL所需的格式名称(JPEG非JPG)
36
+ img_format = 'JPEG' if output_format == 'jpg' else output_format.upper()
37
+ save_args = {'quality': 95} if output_format == 'jpg' else {'compress_level': 6}
38
+
39
+ output_path = output_dir / f"{index}.{file_ext}"
40
+
41
+ # 跳过已存在文件
42
+ if not overwrite and output_path.exists():
43
+ return (index, "skipped")
44
+
45
+ # 数据校验
46
+ if not isinstance(element, dict):
47
+ raise ValueError("数组元素不是字典类型")
48
+ if "image" not in element:
49
+ raise KeyError("缺少'image'字段")
50
+
51
+ # 图像解码
52
+ image_bytes = base64.b64decode(element["image"])
53
+
54
+ with Image.open(io.BytesIO(image_bytes)) as img:
55
+ # 公共处理:CMYK转换
56
+ if img.mode == 'CMYK':
57
+ img = img.convert('RGB')
58
+
59
+ # 格式专用处理
60
+ if output_format == 'jpg':
61
+ # 处理需要转换的透明模式
62
+ if img.mode == 'RGBA':
63
+ background = Image.new('RGB', img.size, (255, 255, 255))
64
+ background.paste(img, mask=img.split()[-1])
65
+ img = background
66
+ elif img.mode in ['P', 'PA']: # 调色板模式处理
67
+ img = img.convert('RGBA')
68
+ background = Image.new('RGB', img.size, (255, 255, 255))
69
+ background.paste(img, mask=img.split()[-1])
70
+ img = background
71
+ elif img.mode == 'LA': # 灰度+透明度
72
+ img = img.convert('L')
73
+
74
+ # 最终模式校验
75
+ if img.mode not in ['RGB', 'L']:
76
+ img = img.convert('RGB')
77
+
78
+ # 保存图像(使用PIL兼容的格式名)
79
+ img.save(output_path, img_format, **save_args)
80
+ return (index, "success")
81
+
82
+ except Exception as e:
83
+ return (index, f"error: {str(e)}")
84
+
85
+ def process_single_json(json_path: Path,
86
+ output_root: Path,
87
+ threads: int = 4,
88
+ overwrite: bool = False,
89
+ output_format: str = 'jpg') -> Tuple[str, int, int]: # 新增格式参数
90
+ """处理单个JSON文件(支持并发)"""
91
+ start_time = time.time()
92
+ file_stem = json_path.stem
93
+ output_dir = output_root / file_stem
94
+ output_dir.mkdir(parents=True, exist_ok=True)
95
+
96
+ error_log = []
97
+ success_count = 0
98
+ skipped_count = 0
99
+
100
+ try:
101
+ with open(json_path, "r") as f:
102
+ json_data = json.load(f)
103
+
104
+ if not isinstance(json_data, list):
105
+ raise ValueError("JSON根元素不是数组类型")
106
+
107
+ with ThreadPoolExecutor(max_workers=threads) as executor:
108
+ futures = [
109
+ executor.submit(
110
+ process_json_element,
111
+ element,
112
+ idx,
113
+ output_dir,
114
+ overwrite,
115
+ output_format # 传递格式参数
116
+ )
117
+ for idx, element in enumerate(json_data)
118
+ ]
119
+
120
+ for future in as_completed(futures):
121
+ idx, status = future.result()
122
+ if status == "success":
123
+ success_count += 1
124
+ elif status == "skipped":
125
+ skipped_count += 1
126
+ elif status.startswith("error"):
127
+ error_log.append(f"元素{idx}错误: {status[6:]}")
128
+
129
+ process_time = time.time() - start_time
130
+ logging.info(
131
+ f"文件 {file_stem} 处理完成 | "
132
+ f"成功: {success_count} | "
133
+ f"跳过: {skipped_count} | "
134
+ f"错误: {len(error_log)} | "
135
+ f"耗时: {process_time:.2f}s"
136
+ )
137
+
138
+ if error_log:
139
+ (output_dir / "process_errors.log").write_text("\n".join(error_log))
140
+
141
+ return (json_path.name, success_count, len(error_log))
142
+
143
+ except Exception as e:
144
+ logging.error(f"文件处理失败: {str(e)}")
145
+ return (json_path.name, 0, 1)
146
+
147
+ def batch_process_jsons(input_dir: Path,
148
+ output_root: Path,
149
+ threads: int = 4,
150
+ overwrite: bool = False,
151
+ output_format: str = 'jpg'): # 新增格式参数
152
+ """批量处理JSON文件"""
153
+ input_path = Path(input_dir)
154
+ output_root = Path(output_root)
155
+
156
+ if not input_path.exists():
157
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
158
+
159
+ json_files = list(input_path.glob("*.json"))
160
+ if not json_files:
161
+ logging.warning("未找到JSON文件")
162
+ return
163
+
164
+ total_stats = {"success": 0, "errors": 0}
165
+
166
+ with ThreadPoolExecutor(max_workers=threads) as executor:
167
+ futures = {
168
+ executor.submit(
169
+ process_single_json,
170
+ json_file,
171
+ output_root,
172
+ threads,
173
+ overwrite,
174
+ output_format # 传递格式参数
175
+ ): json_file for json_file in json_files
176
+ }
177
+
178
+ for future in as_completed(futures):
179
+ try:
180
+ filename, success, errors = future.result()
181
+ total_stats["success"] += success
182
+ total_stats["errors"] += errors
183
+ except Exception as e:
184
+ total_stats["errors"] += 1
185
+ logging.error(f"处理异常: {str(e)}")
186
+
187
+ logging.info(f"\n{'='*40}")
188
+ logging.info(f"处理完成文件总数: {len(json_files)}")
189
+ logging.info(f"总成功图片数: {total_stats['success']}")
190
+ logging.info(f"总错误数: {total_stats['errors']}")
191
+
192
+ if __name__ == "__main__":
193
+ parser = argparse.ArgumentParser(description="处理JSON文件中的Base64图片(支持格式选择)")
194
+ parser.add_argument("-i", "--input", required=True, help="输入目录路径")
195
+ parser.add_argument("-o", "--output", required=True, help="输出目录路径")
196
+ parser.add_argument("--threads", type=int, default=4, help="并发线程数(默认4)")
197
+ parser.add_argument("--overwrite", action="store_true", help="覆盖已存在的文件")
198
+ parser.add_argument("--format", choices=['png', 'jpg'], default='jpg',
199
+ help="输出图片格式(png/jpg,默认jpg)")
200
+
201
+ args = parser.parse_args()
202
+
203
+ try:
204
+ start = time.time()
205
+ batch_process_jsons(
206
+ input_dir=args.input,
207
+ output_root=args.output,
208
+ threads=args.threads,
209
+ overwrite=args.overwrite,
210
+ output_format=args.format # 传递格式参数
211
+ )
212
+ logging.info(f"\n总耗时: {time.time()-start:.2f}秒")
213
+ except Exception as e:
214
+ logging.error(f"程序异常终止: {str(e)}")
215
+ sys.exit(1)
utils/json/emb_jtj.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from concurrent.futures import ThreadPoolExecutor
5
+
6
+ def convert_medical_json(input_file, output_file, config=None):
7
+ default_config = {
8
+ "task_type": "Visual_Question_Answering",
9
+ "source": "Embspatial",
10
+ "domain": "Embodied_ai"
11
+ }
12
+ cfg = {**default_config, **(config or {})}
13
+ input_path = Path(input_file)
14
+ file_stem = input_path.stem
15
+
16
+ try:
17
+ with open(input_file, 'r', encoding='utf-8') as f:
18
+ raw_data = json.load(f)
19
+
20
+ converted = []
21
+ for index, item in enumerate(raw_data if isinstance(raw_data, list) else [raw_data]):
22
+ # 重构媒体路径
23
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
24
+
25
+ # 构建选项结构
26
+ options = []
27
+ for idx, option_text in enumerate(item['answer_options']):
28
+ opt_id = chr(65 + idx) # 65对应ASCII码'A'
29
+ options.append({"id": opt_id, "text": option_text.strip()})
30
+
31
+ try:
32
+ answer_num = int(item['answer'])
33
+ answer_ids = [options[answer_num]['id']] if 0 <= answer_num < len(options) else []
34
+ except (ValueError, IndexError, KeyError):
35
+ answer_ids = []
36
+
37
+ annotations = []
38
+ for obj in item.get("objects", []): # 提取每个item中的object数组
39
+ annotation = {
40
+ "bbox": obj.get("bbox", []),
41
+ "segmentation": {
42
+ "size": [], # 无对应数据,置空
43
+ "counts": "" # 无对应数据,置空字符串
44
+ },
45
+ "category_name": obj.get("name", "")
46
+ }
47
+ annotations.append(annotation)
48
+
49
+ converted.append({
50
+ "index": index,
51
+ "media_type": "image",
52
+ "media_paths": media_path,
53
+ "description": str(item.get('relation', "")),
54
+ "task_type": cfg['task_type'],
55
+ "question": [item.get('question', "")],
56
+ "question_type": "multi-choice",
57
+ "options": options,
58
+ "annotations": annotations,
59
+ "answer": answer_ids,
60
+ "source": cfg['source'],
61
+ "domain": cfg['domain']
62
+ })
63
+
64
+ with open(output_file, 'w', encoding='utf-8') as f:
65
+ json.dump(converted, f, indent=2, ensure_ascii=False)
66
+ return True
67
+
68
+ except Exception as e:
69
+ print(f"转换失败: {input_file} → {str(e)}")
70
+ return False
71
+
72
+ def process_single_file(input_path, output_dir, config):
73
+ """单个文件处理函数(扁平化输出)"""
74
+ try:
75
+ # 生成输出路径:输出目录 + 原文件名
76
+ output_file = output_dir / input_path.name
77
+
78
+ return convert_medical_json(
79
+ input_file=str(input_path),
80
+ output_file=str(output_file),
81
+ config=config
82
+ )
83
+ except Exception as e:
84
+ print(f"文件处理异常: {input_path} → {str(e)}")
85
+ return False
86
+
87
+ def batch_convert_json(input_dir, output_dir, config=None, max_workers=8):
88
+ """扁平化批量处理器"""
89
+ input_path = Path(input_dir)
90
+ output_path = Path(output_dir)
91
+
92
+ # 创建输出目录(只需创建一次)
93
+ output_path.mkdir(parents=True, exist_ok=True)
94
+
95
+ if not input_path.exists():
96
+ raise FileNotFoundError(f"输入目录不存在: {input_dir}")
97
+
98
+ success_count = 0
99
+ failure_count = 0
100
+
101
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
102
+ futures = []
103
+ # 仅遍历当前目录的JSON文件
104
+ for input_file in input_path.glob('*.json'): # 关键修改点
105
+ if input_file.is_file(): # 确保是文件
106
+ futures.append(
107
+ executor.submit(
108
+ process_single_file,
109
+ input_path=input_file,
110
+ output_dir=output_path,
111
+ config=config
112
+ )
113
+ )
114
+
115
+ # 统计处理结果
116
+ for future in futures:
117
+ if future.result():
118
+ success_count += 1
119
+ else:
120
+ failure_count += 1
121
+
122
+ print(f"\n处理完成: 成功 {success_count} 个文件,失败 {failure_count} 个文件")
123
+ print(f"输出目录: {output_path.resolve()}")
124
+
125
+ if __name__ == "__main__":
126
+ custom_config = {
127
+ "source": "EmbSpatial",
128
+ "task_type": "Object_Detection",
129
+ "domain": "Embodied_ai"
130
+ }
131
+
132
+ try:
133
+ batch_convert_json(
134
+ input_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/emb_ai",
135
+ output_dir="/mnt/data/users/zys/proj/vlm_reasoning/dataset",
136
+ config=custom_config,
137
+ max_workers=min(os.cpu_count() * 2, 32)
138
+ )
139
+ except Exception as e:
140
+ print(f"批量处理异常终止: {str(e)}")
utils/json/ems_jtj.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import re
4
+ from pathlib import Path
5
+ from concurrent.futures import ThreadPoolExecutor
6
+
7
+ def convert_medical_json(input_file, output_file, config=None):
8
+ """医疗数据格式转换器(支持多格式选项解析)"""
9
+ default_config = {
10
+ "task_type": "Visual_Question_Answering",
11
+ "source": "Embspatial",
12
+ "domain": "Embodied_ai"
13
+ }
14
+ cfg = {**default_config, **(config or {})}
15
+ input_path = Path(input_file)
16
+ file_stem = input_path.stem
17
+
18
+ try:
19
+ with open(input_file, 'r', encoding='utf-8') as f:
20
+ raw_data = json.load(f)
21
+
22
+ converted = []
23
+ for index, item in enumerate(raw_data if isinstance(raw_data, list) else [raw_data]):
24
+ # 媒体路径生成
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
26
+
27
+ # 处理对象标注
28
+ annotations = []
29
+ objects_list = []
30
+ for obj in item.get("objects", []):
31
+ annotation = {
32
+ "bbox": obj.get("bbox", []),
33
+ "segmentation": {},
34
+ "category_name": obj.get("name", "")
35
+ }
36
+ objects_list.append(annotation)
37
+ annotations.append(objects_list)
38
+
39
+ # 问题解析增强逻辑
40
+ questions_list = item.get('questions', [])
41
+ question_for_eval = str(questions_list[0]) if questions_list else ""
42
+ options = []
43
+ question_text = ""
44
+ question_type = "free-form"
45
+
46
+ # 多格式选项解析
47
+ if "Options:" in question_for_eval:
48
+ question_type = "multi-choice"
49
+ q_parts = question_for_eval.split("Options:", 1)
50
+ question_part = q_parts[0].strip()
51
+ choices_part = q_parts[1].strip() if len(q_parts) > 1 else ""
52
+
53
+ # 清理问题文本
54
+ question_text = re.sub(r'\s+', ' ', question_part.replace("\n", " ")).strip()
55
+
56
+ # 自动生成选项ID的计数器
57
+ option_id_counter = 65 # ASCII 'A'
58
+
59
+ # 分层次解析选项
60
+ for line in re.split(r'[\n;]', choices_part):
61
+ line = line.strip()
62
+ if not line:
63
+ continue
64
+
65
+ # 处理分号分隔的选项(新增逻辑)
66
+ if re.match(r'^[^:\.]+$', line): # 没有冒号或点号的情况
67
+ for sub_opt in re.split(r';\s*', line):
68
+ sub_opt = sub_opt.strip()
69
+ if sub_opt:
70
+ options.append({
71
+ "id": chr(option_id_counter),
72
+ "text": re.sub(r'\s+', ' ', sub_opt)
73
+ })
74
+ option_id_counter += 1
75
+ else:
76
+ # 处理标准格式(A: 或 A.)
77
+ match = re.match(r'^([A-Za-z])[\.:]\s*(.+)$', line)
78
+ if match:
79
+ opt_id, opt_text = match.groups()
80
+ options.append({
81
+ "id": opt_id.upper(),
82
+ "text": re.sub(r'\s+', ' ', opt_text.strip())
83
+ })
84
+ else:
85
+ # 保底处理:自动生成ID
86
+ options.append({
87
+ "id": chr(option_id_counter),
88
+ "text": re.sub(r'\s+', ' ', line.strip())
89
+ })
90
+ option_id_counter += 1
91
+
92
+ else:
93
+ # 自由格式问题处理
94
+ question_text = re.sub(r'\s+', ' ', question_for_eval.replace("\n", " ")).strip()
95
+
96
+ # 智能答案匹配系统
97
+ def match_answer(raw_answer, options_list):
98
+ """四层答案匹配机制"""
99
+ raw_answer = str(raw_answer).strip()
100
+ if not raw_answer:
101
+ return ""
102
+
103
+ # 1. 直接ID匹配
104
+ id_map = {opt['id'].upper(): opt['id'] for opt in options_list}
105
+ if raw_answer.upper() in id_map:
106
+ return id_map[raw_answer.upper()]
107
+
108
+ # 2. 精确文本匹配
109
+ text_to_id = {opt['text'].lower(): opt['id'] for opt in options_list}
110
+ if raw_answer.lower() in text_to_id:
111
+ return text_to_id[raw_answer.lower()]
112
+
113
+ # 3. 包含匹配(去除标点)
114
+ clean_answer = re.sub(r'[^\w\s]', '', raw_answer).lower()
115
+ for opt in options_list:
116
+ clean_text = re.sub(r'[^\w\s]', '', opt['text']).lower()
117
+ if clean_answer in clean_text:
118
+ return opt['id']
119
+
120
+ # 4. 首字母匹配
121
+ if len(raw_answer) == 1 and raw_answer.isalpha():
122
+ return raw_answer.upper()
123
+
124
+ return raw_answer # 保底返回原始值
125
+
126
+ # 处理答案
127
+ raw_answer = item.get('answer', '')
128
+ processed_answer = match_answer(raw_answer, options) if question_type == "multi-choice" else str(raw_answer)
129
+ answer = [processed_answer.strip().upper() if question_type == "multi-choice" else processed_answer.strip()]
130
+
131
+ converted.append({
132
+ "index": index,
133
+ "media_type": "image",
134
+ "media_paths": media_path,
135
+ "description": str(item.get('relation', "")),
136
+ "task_type": cfg['task_type'],
137
+ "question": [question_text],
138
+ "question_type": question_type,
139
+ "options": options,
140
+ "annotations": annotations,
141
+ "answer": answer,
142
+ "source": cfg['source'],
143
+ "domain": cfg['domain']
144
+ })
145
+
146
+ with open(output_file, 'w', encoding='utf-8') as f:
147
+ json.dump(converted, f, indent=2, ensure_ascii=False)
148
+ return True
149
+
150
+ except Exception as e:
151
+ print(f"转换失败: {input_file} → {str(e)}")
152
+ return False
153
+
154
+ def process_single_file(input_path, output_dir, config):
155
+ """文件处理单元"""
156
+ try:
157
+ output_file = output_dir / input_path.name
158
+ return convert_medical_json(
159
+ input_file=str(input_path),
160
+ output_file=str(output_file),
161
+ config=config
162
+ )
163
+ except Exception as e:
164
+ print(f"文件处理异常: {input_path} → {str(e)}")
165
+ return False
166
+
167
+ def batch_convert_json(input_dir, output_dir, config=None, max_workers=8):
168
+ """并行批量处理器"""
169
+ input_path = Path(input_dir)
170
+ output_path = Path(output_dir)
171
+ output_path.mkdir(parents=True, exist_ok=True)
172
+
173
+ success_count = 0
174
+ failure_count = 0
175
+
176
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
177
+ futures = []
178
+ for input_file in input_path.glob('*.json'):
179
+ if input_file.is_file():
180
+ futures.append(executor.submit(
181
+ process_single_file,
182
+ input_path=input_file,
183
+ output_dir=output_path,
184
+ config=config
185
+ ))
186
+
187
+ for future in futures:
188
+ success_count += 1 if future.result() else 0
189
+ failure_count += 0 if future.result() else 1
190
+
191
+ print(f"\n处理完成: 成功 {success_count} 个,失败 {failure_count} 个")
192
+ print(f"输出目录: {output_path.resolve()}")
193
+
194
+ if __name__ == "__main__":
195
+ custom_config = {
196
+ "source": "EmbSpatial",
197
+ "task_type": "Object-Detection",
198
+ "domain": "Embodied_ai"
199
+ }
200
+
201
+ try:
202
+ batch_convert_json(
203
+ input_dir="/mnt/data/users/zys/proj/vlm_reasoning/unprocessed_data/emb_ai/EmbSpatial",
204
+ output_dir="/mnt/data/users/zys/proj/vlm_reasoning/dataset",
205
+ config=custom_config,
206
+ max_workers=os.cpu_count() * 2
207
+ )
208
+ except Exception as e:
209
+ print(f"批处理异常: {str(e)}")
utils/json/jsonl.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import json
3
+
4
+ def count_jsonl_objects(file_path):
5
+ count = 0
6
+ with open(file_path, 'r', encoding='utf-8') as file:
7
+ for line_number, line in enumerate(file, 1):
8
+ stripped_line = line.strip()
9
+ if not stripped_line:
10
+ continue # 跳过空行
11
+ try:
12
+ json.loads(stripped_line)
13
+ count += 1
14
+ except json.JSONDecodeError as e:
15
+ print(f"解析第 {line_number} 行时发现无效JSON: {e}")
16
+ return count
17
+
18
+ if __name__ == "__main__":
19
+ if len(sys.argv) != 2:
20
+ print("使用方法: python count_jsonl.py <文件路径>")
21
+ sys.exit(1)
22
+
23
+ file_path = sys.argv[1]
24
+ try:
25
+ total = count_jsonl_objects(file_path)
26
+ print(f"文件 {file_path} 中共包含 {total} 个有效的JSON对象")
27
+ except FileNotFoundError:
28
+ print(f"错误:文件 {file_path} 未找到")
29
+ except Exception as e:
30
+ print(f"发生未知错误: {e}")
utils/json/mask.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+ from matplotlib import pyplot as plt
4
+
5
+ def visualize_mask(points, size, save_path="mask.png"):
6
+ """
7
+ 输入:
8
+ points : 多边形坐标 [[x1,y1], [x2,y2], ...]
9
+ size : 图像尺寸 [height, width]
10
+ save_path : 输出图片保存路径(默认当前目录mask.png)
11
+
12
+ 输出:
13
+ 展示并保存包含多边形顶点(红色)和掩码区域(半透明绿)的图片
14
+ """
15
+ # 数值有效性检查
16
+ assert len(size) == 2, "size must be [height, width]"
17
+ height, width = size
18
+ assert height > 0 and width > 0, "invalid image size"
19
+
20
+ # 1. 创建彩色画布(RGBA格式,支持半透明叠加)
21
+ canvas = np.zeros((height, width, 4), dtype=np.uint8)
22
+
23
+ # 2. 预处理坐标点
24
+ polygon = []
25
+ for x, y in points:
26
+ # 将点限制在图像范围内(防止越界)
27
+ x = int(np.clip(round(x), 0, width-1))
28
+ y = int(np.clip(round(y), 0, height-1))
29
+ polygon.append([x, y])
30
+ polygon = np.array([polygon], dtype=np.int32) # OpenCV要求形状为[N,1,2]
31
+
32
+ # 3. 生成掩码区域(绿色半透明填充)
33
+ mask = np.zeros((height, width), dtype=np.uint8)
34
+ cv2.fillPoly(mask, polygon, color=1)
35
+ canvas[..., 1] = 80 * mask # 绿色通道
36
+ canvas[..., 3] = 200 * mask # 透明度通道(掩码区域半透明)
37
+
38
+ # 4. 绘制多边形顶点(红色圆点标记)
39
+ for idx, (x, y) in enumerate(polygon[0]):
40
+ color = (0, 0, 255, 255) # 纯红色(RGBA)
41
+ cv2.circle(canvas, (x, y), radius=5, color=color, thickness=-1)
42
+ cv2.putText(canvas, str(idx+1), (x+7, y+3),
43
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255,255), 1)
44
+
45
+ # 5. 绘制多边形边界线(蓝色)
46
+ cv2.polylines(canvas, polygon, isClosed=True,
47
+ color=(255, 0, 0, 255), thickness=2) # 蓝色边界
48
+
49
+ # 6. 保存和显示结果
50
+ cv2.imwrite(save_path, canvas)
51
+
52
+ # 使用matplotlib显示(确保透明天正确处理)
53
+ plt.figure(figsize=(10, 6))
54
+ plt.title(f"Mask Visualization (Saved to {save_path})")
55
+ plt.imshow(cv2.cvtColor(canvas, cv2.COLOR_BGRA2RGBA))
56
+ plt.axis('off')
57
+ plt.show()
58
+
59
+ # ------------ 使用示例 ------------
60
+ if __name__ == "__main__":
61
+ # 你的输入数据
62
+ points = [
63
+ [602.245, 290.396],
64
+ [585.264, 289.452],
65
+ [568.283, 301.716],
66
+ [557.905, 309.264],
67
+ [546.584, 324.358],
68
+ [543.754, 342.283],
69
+ [544.698, 365.867],
70
+ [552.245, 381.905],
71
+ [571.113, 398.886],
72
+ [583.377, 407.377],
73
+ [597.528, 409.264],
74
+ [607.905, 398.886],
75
+ [626.773, 387.566],
76
+ [636.208, 384.735],
77
+ [646.584, 381.905],
78
+ [660.735, 370.584],
79
+ [661.679, 335.679],
80
+ [654.132, 313.037],
81
+ [639.981, 302.660],
82
+ [618.283, 293.226]
83
+ ]
84
+ size = [960, 1280] # 高度=488,宽度=640
85
+
86
+ # 生成可视化图像
87
+ visualize_mask(
88
+ points=points,
89
+ size=size,
90
+ save_path="custom_mask_visualization.png"
91
+ )
utils/json/merge_json.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ from pathlib import Path
4
+
5
+ def merge_json_dataset(dataset_dir: str, output_name: str = "ChemQA") -> None:
6
+ """
7
+ 自动合并dataset_dir中所有JSON文件的数据部分
8
+
9
+ 参数:
10
+ dataset_dir : 存放.json文件的目录
11
+ output_name : 合并后的数据集名称(默认ChemQA)
12
+ """
13
+ dataset_path = Path(dataset_dir)
14
+ output_json = dataset_path / f"{output_name}.json"
15
+
16
+ # 获取所有需要合并的JSON文件(排除输出文件自身)
17
+ json_files = sorted(dataset_path.glob("*.json"))
18
+ json_files = [f for f in json_files if f != output_json]
19
+
20
+ if not json_files:
21
+ raise FileNotFoundError("未找到任何JSON文件")
22
+
23
+ merged_data = []
24
+ global_offset = 0 # 全局索引偏移量
25
+
26
+ for json_file in json_files:
27
+ # 读取JSON数据
28
+ with open(json_file, 'r', encoding='utf-8') as f:
29
+ part_data = json.load(f)
30
+
31
+ # 更新索引
32
+ for item in part_data:
33
+ item["index"] = global_offset + item["index"]
34
+
35
+ # 合并数据并更新偏移量
36
+ merged_data.extend(part_data)
37
+ global_offset += len(part_data)
38
+
39
+ # 保存合并结果
40
+ with open(output_json, 'w', encoding='utf-8') as f:
41
+ json.dump(merged_data, f, indent=2, ensure_ascii=False)
42
+
43
+ print(f"\n合并完成!共处理 {len(json_files)} 个JSON文件")
44
+ print(f"生成数据集: {output_name}.json")
45
+ print(f"- 总条目数: {len(merged_data)} 条")
46
+
47
+ if __name__ == "__main__":
48
+ parser = argparse.ArgumentParser(description="合并JSON格式的问答数据集")
49
+ parser.add_argument('-i',dest="dataset_dir", required=True, help="JSON文件所在的目录路径")
50
+ parser.add_argument('-o',"--output_name", default="ChemQA", help="输出数据集名称(默认为ChemQA)")
51
+
52
+ args = parser.parse_args()
53
+
54
+ merge_json_dataset(
55
+ dataset_dir=args.dataset_dir,
56
+ output_name=args.output_name
57
+ )
utils/oss/oss_batch_upload.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import alibabacloud_oss_v2 as oss
4
+
5
+ def main():
6
+ parser = argparse.ArgumentParser(description="批量上传JSONL文件到OSS")
7
+
8
+ # 必需参数
9
+ parser.add_argument('--region', required=True, help='OSS存储空间所在区域')
10
+ parser.add_argument('--bucket', required=True, help='目标存储空间名称')
11
+ parser.add_argument('--key', required=True, help='OSS目标文件夹路径(如:/data)')
12
+ parser.add_argument('--file_path', required=True, help='本地包含JSONL文件的文件夹路径')
13
+
14
+ # 可选参数
15
+ parser.add_argument('--endpoint', help='自定义访问端点')
16
+
17
+ args = parser.parse_args()
18
+
19
+ # 验证文件路径有效性
20
+ if not os.path.isdir(args.file_path):
21
+ raise ValueError(f"无效的目录路径: {args.file_path}")
22
+
23
+ # 收集所有JSONL文件
24
+ jsonl_files = []
25
+ for filename in os.listdir(args.file_path):
26
+ if filename.endswith('.jsonl'):
27
+ full_path = os.path.join(args.file_path, filename)
28
+ if os.path.isfile(full_path):
29
+ jsonl_files.append((full_path, filename))
30
+
31
+ if not jsonl_files:
32
+ print("未找到任何JSONL文件")
33
+ return
34
+
35
+ # 初始化OSS配置
36
+ credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
37
+ cfg = oss.config.load_default()
38
+ cfg.credentials_provider = credentials_provider
39
+ cfg.region = args.region
40
+ if args.endpoint:
41
+ cfg.endpoint = args.endpoint
42
+
43
+ # 创建OSS客户端
44
+ client = oss.Client(cfg)
45
+ uploader = client.uploader()
46
+
47
+ # 处理OSS路径格式
48
+ base_key = args.key.rstrip('/')
49
+
50
+ # 批量上传
51
+ for local_path, filename in jsonl_files:
52
+ oss_key = f"{base_key}/{filename}" if base_key else filename
53
+
54
+ try:
55
+ result = uploader.upload_file(
56
+ oss.PutObjectRequest(
57
+ bucket=args.bucket,
58
+ key=oss_key,
59
+ ),
60
+ filepath=local_path
61
+ )
62
+
63
+ # 输出上传结果
64
+ print(f" 成功上传 {filename}")
65
+ print(f" OSS路径: {oss_key}")
66
+ print(f" 状态码: {result.status_code}")
67
+ print(f" 请求ID: {result.request_id}")
68
+ print(f" ETag: {result.etag}\n")
69
+
70
+ except Exception as e:
71
+ print(f" 上传失败 {filename}")
72
+ print(f" 错误信息: {str(e)}\n")
73
+
74
+ if __name__ == "__main__":
75
+ main()
utils/oss/oss_upload.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import alibabacloud_oss_v2 as oss
3
+
4
+ # 创建一个命令行参数解析器,并描述脚本用途:上传文件示例
5
+ parser = argparse.ArgumentParser(description="upload file sample")
6
+
7
+ # 添加命令行参数 --region,表示存储空间所在的区域,必需参数
8
+ parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
9
+ # 添加命令行参数 --bucket,表示要上传文件到的存储空间名称,必需参数
10
+ parser.add_argument('--bucket', help='The name of the bucket.', required=True)
11
+ # 添加命令行参数 --endpoint,表示其他服务可用来访问OSS的域名,非必需参数
12
+ parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
13
+ # 添加命令行参数 --key,表示对象(文件)在OSS中的键名,必需参数
14
+ parser.add_argument('--key', help='The name of the object.', required=True)
15
+ # 添加命令行参数 --file_path,表示本地待上传文件的路径,必需参数,例如“/Users/yourLocalPath/yourFileName”
16
+ parser.add_argument('--file_path', help='The path of Upload file.', required=True)
17
+
18
+ def main():
19
+ # 解析命令行提供的参数,获取用户输入的值
20
+ args = parser.parse_args()
21
+
22
+ # 从环境变量中加载访问OSS所需的认证信息,用于身份验证
23
+ credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
24
+
25
+ # 使用SDK的默认配置创建配置对象,并设置认证提供者
26
+ cfg = oss.config.load_default()
27
+ cfg.credentials_provider = credentials_provider
28
+
29
+ # 设置配置对象的区域属性,根据用户提供的命令行参数
30
+ cfg.region = args.region
31
+
32
+ # 如果提供了自定义endpoint,则更新配置对象中的endpoint属性
33
+ if args.endpoint is not None:
34
+ cfg.endpoint = args.endpoint
35
+
36
+ # 使用上述配置初始化OSS客户端,准备与OSS交互
37
+ client = oss.Client(cfg)
38
+
39
+ # 创建一个用于上传文件的对象
40
+ uploader = client.uploader()
41
+
42
+ # 调用方法执行文件上传操作
43
+ result = uploader.upload_file(
44
+ oss.PutObjectRequest(
45
+ bucket=args.bucket, # 指定目标存储空间
46
+ key=args.key, # 指定文件在OSS中的名称
47
+ ),
48
+ filepath=args.file_path # 指定本地文件的位置
49
+ )
50
+
51
+ # 打印上传结果的相关信息,包括状态码、请求ID、内容MD5等
52
+ print(f'status code: {result.status_code},'
53
+ f' request id: {result.request_id},'
54
+ f' content md5: {result.headers.get("Content-MD5")},'
55
+ f' etag: {result.etag},'
56
+ f' hash crc64: {result.hash_crc64},'
57
+ f' version id: {result.version_id},'
58
+ f' server time: {result.headers.get("x-oss-server-time")},'
59
+ )
60
+
61
+ # 当此脚本被直接执行时,调用main函数开始处理逻辑
62
+ if __name__ == "__main__":
63
+ main() # 脚本入口点,控制程序流程从这里开始
utils/oss/testis.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import os
2
+
3
+ print("ACCESS_KEY_ID:", os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'))
4
+ print("ACCESS_KEY_SECRET:", os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'))
utils/parquet/ChemQA_ptj.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from ast import literal_eval
9
+ import time
10
+ from typing import Tuple
11
+ import pandas as pd
12
+
13
+ # 配置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s - %(levelname)s - %(message)s",
17
+ stream=sys.stdout
18
+ )
19
+
20
+ def process_row(args):
21
+ """处理单行数据(线程安全)"""
22
+ index, row, file_stem= args
23
+ try:
24
+ # 生成媒体路径
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
26
+
27
+ # 处理选项
28
+ choices = row.get('choices', [])
29
+ if isinstance(choices, str):
30
+ try:
31
+ choices = literal_eval(choices)
32
+ except:
33
+ choices = []
34
+
35
+ # 生成选项对象
36
+ options = [
37
+ {"id": chr(65 + i), "text": str(text)}
38
+ for i, text in enumerate(choices)
39
+ ]
40
+
41
+ # 处理答案
42
+ label = row.get('label', '')
43
+ answer = []
44
+ try:
45
+ label_idx = int(label)
46
+ if 0 <= label_idx < len(options):
47
+ answer = [options[label_idx]['id']]
48
+ except (ValueError, TypeError):
49
+ pass
50
+
51
+ return {
52
+ "index": index,
53
+ "media_type": "image",
54
+ "media_paths": media_path,
55
+ "description": row.get('description', ''),
56
+ "task_type": "Vision-Question-Answer",
57
+ "question": [row.get('question', '')],
58
+ "question_type": "multi-choice",
59
+ "options": options,
60
+ "annotations":[],
61
+ "answer": answer,
62
+ "source": "ChemQA",
63
+ "domain": "Chemistry"
64
+ }
65
+ except Exception as e:
66
+ logging.error(f"处理行 {index} 时出错: {str(e)}")
67
+ return None
68
+
69
+ def process_single_parquet(parquet_path: Path, output_root: Path) -> Tuple[int, int]:
70
+ """处理单个Parquet文件"""
71
+ start_time = time.time()
72
+ file_stem = parquet_path.stem
73
+ output_dir = output_root
74
+ output_json = output_dir / f"{file_stem}.json"
75
+
76
+ success_count = 0
77
+ error_count = 0
78
+ results = []
79
+
80
+ try:
81
+ df = pd.read_parquet(parquet_path)
82
+ total_rows = len(df)
83
+
84
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
85
+ logging.info(f"Output directory: {output_dir}")
86
+
87
+ # 创建线程池
88
+ with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
89
+ task_args = [(idx, row, file_stem) for idx, row in df.iterrows()]
90
+ futures = [executor.submit(process_row, args) for args in task_args]
91
+
92
+ for future in futures:
93
+ result = future.result()
94
+ if result:
95
+ results.append(result)
96
+ success_count += 1
97
+ else:
98
+ error_count += 1
99
+
100
+ # 写入JSON文件
101
+ with open(output_json, 'w', encoding='utf-8') as f:
102
+ json.dump(results, f, ensure_ascii=False, indent=2)
103
+
104
+ # 生成报告
105
+ process_time = time.time() - start_time
106
+ logging.info(
107
+ f"Processed: {success_count}/{total_rows} | "
108
+ f"Errors: {error_count} | "
109
+ f"Time: {process_time:.2f}s"
110
+ )
111
+
112
+ return success_count, error_count
113
+
114
+ except Exception as e:
115
+ logging.error(f"处理文件失败: {str(e)}")
116
+ return 0, total_rows
117
+
118
+ def batch_process_parquets(input_dir: Path, output_root: Path):
119
+ """批量处理目录下所有Parquet文件"""
120
+ input_path = Path(input_dir)
121
+ output_root = Path(output_root)
122
+
123
+ if not input_path.exists():
124
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
125
+
126
+ parquet_files = list(input_path.glob("*.parquet"))
127
+ if not parquet_files:
128
+ logging.warning("未找到Parquet文件")
129
+ return
130
+
131
+ total_stats = {'success': 0, 'errors': 0}
132
+
133
+ for parquet_file in parquet_files:
134
+ success, errors = process_single_parquet(parquet_file, output_root)
135
+ total_stats['success'] += success
136
+ total_stats['errors'] += errors
137
+
138
+ logging.info(f"\n{'='*40}\n批量处理完成")
139
+ logging.info(f"处理文件总数: {len(parquet_files)}")
140
+ logging.info(f"总成功条目: {total_stats['success']}")
141
+ logging.info(f"总失败条目: {total_stats['errors']}")
142
+
143
+ if __name__ == "__main__":
144
+ parser = argparse.ArgumentParser(description='批量处理Parquet文件转JSON')
145
+ parser.add_argument('-i', '--input', required=True, help='输入目录路径')
146
+ parser.add_argument('-o', '--output', required=True, help='输出根目录路径')
147
+
148
+ args = parser.parse_args()
149
+
150
+ try:
151
+ start_time = time.time()
152
+ batch_process_parquets(
153
+ input_dir=args.input,
154
+ output_root=args.output
155
+ )
156
+ logging.info(f"\n总耗时: {time.time()-start_time:.2f}s")
157
+ except Exception as e:
158
+ logging.error(f"程序异常终止: {str(e)}")
159
+ sys.exit(1)
utils/parquet/MathVerse_ptj.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from ast import literal_eval
9
+ import time
10
+ from typing import Tuple
11
+ import pandas as pd
12
+
13
+ # 配置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s - %(levelname)s - %(message)s",
17
+ stream=sys.stdout
18
+ )
19
+
20
+ def process_row(args):
21
+ """处理单行数据(线程安全)"""
22
+ index, row, file_stem = args
23
+ try:
24
+ # ================== 基础字段处理 ==================
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
26
+ raw_question_type = row.get("question_type", "").strip().lower()
27
+
28
+ # ================== 题目类型转换 ==================
29
+ # 根据数据集规范转换question_type格式
30
+ if raw_question_type == "multi-choice":
31
+ formatted_question_type = "multi-choice"
32
+ elif raw_question_type == "free-form":
33
+ formatted_question_type = "free-form"
34
+ else:
35
+ logging.warning(f"未知的题目类型: {raw_question_type}")
36
+ formatted_question_type = raw_question_type
37
+
38
+ # ================== 公共字段处理 ==================
39
+ description = str(row.get("problem_version", "")).strip()
40
+
41
+ # ================== 初始化关键字段 ==================
42
+ options = []
43
+ answer = []
44
+ question_text = ""
45
+
46
+ # ================== 分类型处理 ==================
47
+ if formatted_question_type == "multi-choice":
48
+ # 提取问题文本和选项
49
+ question_for_eval = str(row.get("question_for_eval", ""))
50
+
51
+ # 分割问题描述和选项部分
52
+ if "Choices:" in question_for_eval:
53
+ q_parts = question_for_eval.split("Choices:", 1)
54
+ question_part = q_parts[0].strip()
55
+ choices_part = q_parts[1].strip()
56
+ else:
57
+ question_part = question_for_eval.strip()
58
+ choices_part = ""
59
+
60
+ # 清理问题文本格式(合并换行和多余空格)
61
+ question_text = " ".join(question_part.replace("\n", " ").split())
62
+
63
+ # 解析选项
64
+ for line in choices_part.split('\n'):
65
+ line = line.strip()
66
+ if not line:
67
+ continue
68
+
69
+ # 支持 A: 或 A. 两种分隔符
70
+ if ':' in line:
71
+ id_text = line.split(':', 1)
72
+ elif '.' in line:
73
+ id_text = line.split('.', 1)
74
+ else:
75
+ continue # 跳过无法解析的行
76
+
77
+ if len(id_text) == 2:
78
+ option_id = id_text[0].strip().upper()
79
+ option_text = " ".join(id_text[1].replace("\n", " ").split())
80
+ options.append({"id": option_id, "text": option_text})
81
+
82
+ # 处理答案(直接使用原始答案字母)
83
+ raw_answer = str(row.get("answer", "")).strip().upper()
84
+ if raw_answer:
85
+ answer = [raw_answer]
86
+
87
+ elif formatted_question_type == "free-form":
88
+ # 提取问题文本
89
+ question_text = " ".join(str(row.get("query", "")).replace("\n", " ").split())
90
+
91
+ # 处理答案(统一转为字符串)
92
+ raw_answer = row.get("answer", "")
93
+ if pd.isna(raw_answer):
94
+ answer = [""]
95
+ else:
96
+ cleaned_answer = " ".join(str(raw_answer).strip().split())
97
+ answer = [cleaned_answer]
98
+
99
+ # ================== 构建结果对象 ==================
100
+ return {
101
+ "index": index,
102
+ "media_type": "image",
103
+ "media_paths": media_path,
104
+ "description": description,
105
+ "task_type": "Vision-Question-Answer",
106
+ "question": [question_text],
107
+ "question_type": formatted_question_type,
108
+ "options": options,
109
+ "annotations": [],
110
+ "answer": answer,
111
+ "source": "MathVerse",
112
+ "domain": "Math"
113
+ }
114
+
115
+ except Exception as e:
116
+ logging.error(f"处理行 {index} 时出错: {str(e)}")
117
+ return None
118
+
119
+ def process_single_parquet(parquet_path: Path, output_root: Path) -> Tuple[int, int]:
120
+ """处理单个Parquet文件"""
121
+ start_time = time.time()
122
+ file_stem = parquet_path.stem
123
+ output_dir = output_root
124
+ output_json = output_dir / f"{file_stem}.json"
125
+
126
+ success_count = 0
127
+ error_count = 0
128
+ results = []
129
+
130
+ try:
131
+ df = pd.read_parquet(parquet_path)
132
+ total_rows = len(df)
133
+
134
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
135
+ logging.info(f"Output directory: {output_dir}")
136
+
137
+ # 创建线程池
138
+ with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
139
+ task_args = [(idx, row, file_stem) for idx, row in df.iterrows()]
140
+ futures = [executor.submit(process_row, args) for args in task_args]
141
+
142
+ for future in futures:
143
+ result = future.result()
144
+ if result:
145
+ results.append(result)
146
+ success_count += 1
147
+ else:
148
+ error_count += 1
149
+
150
+ # 写入JSON文件
151
+ with open(output_json, 'w', encoding='utf-8') as f:
152
+ json.dump(results, f, ensure_ascii=False, indent=2)
153
+
154
+ # 生成报告
155
+ process_time = time.time() - start_time
156
+ logging.info(
157
+ f"Processed: {success_count}/{total_rows} | "
158
+ f"Errors: {error_count} | "
159
+ f"Time: {process_time:.2f}s"
160
+ )
161
+
162
+ return success_count, error_count
163
+
164
+ except Exception as e:
165
+ logging.error(f"处理文件失败: {str(e)}")
166
+ return 0, total_rows
167
+
168
+ def batch_process_parquets(input_dir: Path, output_root: Path):
169
+ """批量处理目录下所有Parquet文件"""
170
+ input_path = Path(input_dir)
171
+ output_root = Path(output_root)
172
+
173
+ if not input_path.exists():
174
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
175
+
176
+ parquet_files = list(input_path.glob("*.parquet"))
177
+ if not parquet_files:
178
+ logging.warning("未找到Parquet文件")
179
+ return
180
+
181
+ total_stats = {'success': 0, 'errors': 0}
182
+
183
+ for parquet_file in parquet_files:
184
+ success, errors = process_single_parquet(parquet_file, output_root)
185
+ total_stats['success'] += success
186
+ total_stats['errors'] += errors
187
+
188
+ logging.info(f"\n{'='*40}\n批量处理完成")
189
+ logging.info(f"处理文件总数: {len(parquet_files)}")
190
+ logging.info(f"总成功条目: {total_stats['success']}")
191
+ logging.info(f"总失败条目: {total_stats['errors']}")
192
+
193
+ if __name__ == "__main__":
194
+ parser = argparse.ArgumentParser(description='批量处理Parquet文件转JSON')
195
+ parser.add_argument('-i', '--input', required=True, help='输入目录路径')
196
+ parser.add_argument('-o', '--output', required=True, help='输出根目录路径')
197
+
198
+ args = parser.parse_args()
199
+
200
+ try:
201
+ start_time = time.time()
202
+ batch_process_parquets(
203
+ input_dir=args.input,
204
+ output_root=args.output
205
+ )
206
+ logging.info(f"\n总耗时: {time.time()-start_time:.2f}s")
207
+ except Exception as e:
208
+ logging.error(f"程序异常终止: {str(e)}")
209
+ sys.exit(1)
utils/parquet/MathVision_ptj.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from ast import literal_eval
9
+ import time
10
+ from typing import Tuple
11
+ import pandas as pd
12
+
13
+ # 配置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s - %(levelname)s - %(message)s",
17
+ stream=sys.stdout
18
+ )
19
+
20
+ def process_row(args):
21
+ """处理单行数据(线程安全)"""
22
+ index, row, file_stem = args
23
+ try:
24
+ # ================== 基础字段处理 ==================
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
26
+ description = row.get("subject", "") # 从subject字段获取描述
27
+
28
+ # ================== 动态确定问题类型 ==================
29
+ # 解析options字段(支持字符串格式的列表)
30
+ options_data = row.get("options", [])
31
+ if isinstance(options_data, str):
32
+ try:
33
+ options_data = literal_eval(options_data) # 尝试解析字符串为列表
34
+ except:
35
+ options_data = [] # 解析失败时视为空列表
36
+ elif not isinstance(options_data, list):
37
+ options_data = list(options_data) # 强制转换为列表(处理数组/Series)
38
+ formatted_question_type = "free-form" if len(options_data) == 0 else "multi-choice"
39
+
40
+ # ================== 不同类型处理 ==================
41
+ options = []
42
+ answer = []
43
+
44
+ if formatted_question_type == "multi-choice":
45
+ # 生成标准选项结构
46
+ options = [
47
+ {"id": chr(65 + i), "text": str(text).strip()}
48
+ for i, text in enumerate(options_data)
49
+ ]
50
+
51
+ # 匹配答案选项
52
+ answer_text = str(row.get("answer", "")).strip()
53
+ for option in options:
54
+ if option["text"] == answer_text:
55
+ answer = [option["id"]]
56
+ break
57
+
58
+ else: # free-form类型处理
59
+ raw_answer = row.get("answer", "")
60
+ # 处理空值和特殊格式
61
+ if pd.isna(raw_answer) or raw_answer in ["nan", "None"]:
62
+ answer = [""]
63
+ else:
64
+ # 统一转换为字符串并清理格式
65
+ cleaned_answer = " ".join(str(raw_answer).strip().split())
66
+ answer = [cleaned_answer]
67
+
68
+ # ================== 构建结果对象 ==================
69
+ return {
70
+ "index": index,
71
+ "media_type": "image",
72
+ "media_paths": media_path,
73
+ "description": description,
74
+ "task_type": "Vision-Question-Answer",
75
+ "question": [row.get('question', '')],
76
+ "question_type": formatted_question_type,
77
+ "options": options,
78
+ "annotations": [],
79
+ "answer": answer,
80
+ "source": "MathVision",
81
+ "domain": "Math"
82
+ }
83
+
84
+ except Exception as e:
85
+ logging.error(f"处理行 {index} 时出错: {str(e)}")
86
+ return None
87
+
88
+ def process_single_parquet(parquet_path: Path, output_root: Path) -> Tuple[int, int]:
89
+ """处理单个Parquet文件"""
90
+ start_time = time.time()
91
+ file_stem = parquet_path.stem
92
+ output_dir = output_root
93
+ output_json = output_dir / f"{file_stem}.json"
94
+
95
+ success_count = 0
96
+ error_count = 0
97
+ results = []
98
+
99
+ try:
100
+ df = pd.read_parquet(parquet_path)
101
+ total_rows = len(df)
102
+
103
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
104
+ logging.info(f"Output Directory: {output_dir.name}")
105
+
106
+ # 创建线程池
107
+ with ThreadPoolExecutor(max_workers = min(os.cpu_count() * 2, 32)) as executor:
108
+ task_args = [(idx, row, file_stem) for idx, row in df.iterrows()]
109
+ futures = [executor.submit(process_row, args) for args in task_args]
110
+
111
+ for future in futures:
112
+ result = future.result()
113
+ if result:
114
+ results.append(result)
115
+ success_count += 1
116
+ else:
117
+ error_count += 1
118
+
119
+ # 写入JSON文件
120
+ with open(output_json, 'w', encoding='utf-8') as f:
121
+ json.dump(results, f, ensure_ascii=False, indent=2)
122
+
123
+ # 生成报告
124
+ process_time = time.time() - start_time
125
+ logging.info(
126
+ f"Processed: {success_count}/{total_rows} | "
127
+ f"Errors: {error_count} | "
128
+ f"Time: {process_time:.2f}s"
129
+ )
130
+
131
+ return success_count, error_count
132
+
133
+ except Exception as e:
134
+ logging.error(f"处理文件失败: {str(e)}")
135
+ return 0, total_rows
136
+
137
+ def batch_process_parquets(input_dir: Path, output_root: Path):
138
+ """批量处理目录下所有Parquet文件"""
139
+ input_path = Path(input_dir)
140
+ output_root = Path(output_root)
141
+
142
+ if not input_path.exists():
143
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
144
+
145
+ parquet_files = list(input_path.glob("*.parquet"))
146
+ if not parquet_files:
147
+ logging.warning("未找到Parquet文件")
148
+ return
149
+
150
+ total_stats = {'success': 0, 'errors': 0}
151
+
152
+ for parquet_file in parquet_files:
153
+ success, errors = process_single_parquet(parquet_file, output_root)
154
+ total_stats['success'] += success
155
+ total_stats['errors'] += errors
156
+
157
+ logging.info(f"\n{'='*40}\n批量处理完成")
158
+ logging.info(f"处理文件总数: {len(parquet_files)}")
159
+ logging.info(f"总成功条目: {total_stats['success']}")
160
+ logging.info(f"总失败条目: {total_stats['errors']}")
161
+
162
+ if __name__ == "__main__":
163
+ parser = argparse.ArgumentParser(description='批量处理Parquet文件转JSON')
164
+ parser.add_argument('-i', '--input', required=True, help='输入目录路径')
165
+ parser.add_argument('-o', '--output', required=True, help='输出根目录路径')
166
+
167
+ args = parser.parse_args()
168
+
169
+ try:
170
+ start_time = time.time()
171
+ batch_process_parquets(
172
+ input_dir=args.input,
173
+ output_root=args.output
174
+ )
175
+ logging.info(f"\n总耗时: {time.time()-start_time:.2f}s")
176
+ except Exception as e:
177
+ logging.error(f"程序异常终止: {str(e)}")
178
+ sys.exit(1)
utils/parquet/MathVista_ptj.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from ast import literal_eval
9
+ import time
10
+ from typing import Tuple
11
+ import pandas as pd
12
+
13
+ # 配置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s - %(levelname)s - %(message)s",
17
+ stream=sys.stdout
18
+ )
19
+
20
+ def process_row(args):
21
+ """处理单行数据(线程安全)"""
22
+ index, row, file_stem = args
23
+ try:
24
+ # ================== 基础字段处理 ==================
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.jpg").as_posix()
26
+ question_type = row.get("question_type", "")
27
+
28
+ # ================== 题目类型转换 ==================
29
+ # 转换question_type格式
30
+ if question_type == "multi_choice":
31
+ formatted_question_type = "multi-choice"
32
+ elif question_type == "free_form":
33
+ formatted_question_type = "free-form"
34
+ else:
35
+ logging.warning(f"未知的题目类型: {question_type}")
36
+ formatted_question_type = question_type.replace("_", "-")
37
+
38
+ # ================== 解析query字段 ==================
39
+ query = row.get("query", "")
40
+ description = ""
41
+
42
+ # 仅提取Hint部分
43
+ if "Hint:" in query:
44
+ # 获取第一个出现的Question:位置之前的文本
45
+ hint_part = query.split("Question:")[0].replace("Hint:", "").strip()
46
+ # 清理格式(保留自然空格)
47
+ description = " ".join(hint_part.replace("\n", " ").split())
48
+
49
+ # ================== 不同类型处理 ==================
50
+ options = []
51
+ answer = []
52
+
53
+ if formatted_question_type == "multi-choice":
54
+ # 处理选项
55
+ choices = row.get("choices", [])
56
+ if isinstance(choices, str):
57
+ try:
58
+ choices = literal_eval(choices)
59
+ except:
60
+ choices = []
61
+
62
+ # 生成选项对象
63
+ options = [
64
+ {"id": chr(65 + i), "text": str(text).strip()}
65
+ for i, text in enumerate(choices)
66
+ ]
67
+
68
+ # 处理答案(基于label索引)
69
+ label = row.get("answer", "")
70
+ answer = []
71
+ if label is not None:
72
+ label_text = str(label).strip()
73
+ for option in options:
74
+ if option["text"] == label_text:
75
+ answer.append(option["id"])
76
+ break
77
+
78
+ elif formatted_question_type == "free-form":
79
+ # 自由格式处理
80
+ raw_answer = row.get("answer", "")
81
+ # 处理数字和空值
82
+ if pd.isna(raw_answer):
83
+ answer = [""]
84
+ else:
85
+ # 转换为字符串并清理格式
86
+ cleaned_answer = " ".join(str(raw_answer).strip().split())
87
+ answer = [cleaned_answer]
88
+
89
+ # ================== 构建结果对象 ==================
90
+ return {
91
+ "index": index,
92
+ "media_type": "image",
93
+ "media_paths": media_path,
94
+ "description": description,
95
+ "task_type": "Vision-Question-Answer",
96
+ "question": [row.get('question', '')],
97
+ "question_type": formatted_question_type,
98
+ "options": options,
99
+ "annotations": [],
100
+ "answer": answer,
101
+ "source": "MathVista",
102
+ "domain": "Math"
103
+ }
104
+
105
+ except Exception as e:
106
+ logging.error(f"处理行 {index} 时出错: {str(e)}\n原始数据: {row.to_dict()}")
107
+ return None
108
+
109
+ def process_single_parquet(parquet_path: Path, output_root: Path) -> Tuple[int, int]:
110
+ """处理单个Parquet文件"""
111
+ start_time = time.time()
112
+ file_stem = parquet_path.stem
113
+ output_dir = output_root
114
+ output_json = output_dir / f"{file_stem}.json"
115
+
116
+ success_count = 0
117
+ error_count = 0
118
+ results = []
119
+
120
+ try:
121
+ df = pd.read_parquet(parquet_path)
122
+ total_rows = len(df)
123
+
124
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
125
+ logging.info(f"Output directory: {output_dir}")
126
+
127
+ # 创建线程池
128
+ with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
129
+ task_args = [(idx, row, file_stem) for idx, row in df.iterrows()]
130
+ futures = [executor.submit(process_row, args) for args in task_args]
131
+
132
+ for future in futures:
133
+ result = future.result()
134
+ if result:
135
+ results.append(result)
136
+ success_count += 1
137
+ else:
138
+ error_count += 1
139
+
140
+ # 写入JSON文件
141
+ with open(output_json, 'w', encoding='utf-8') as f:
142
+ json.dump(results, f, ensure_ascii=False, indent=2)
143
+
144
+ # 生成报告
145
+ process_time = time.time() - start_time
146
+ logging.info(
147
+ f"Processed: {success_count}/{total_rows} | "
148
+ f"Errors: {error_count} | "
149
+ f"Time: {process_time:.2f}s"
150
+ )
151
+
152
+ return success_count, error_count
153
+
154
+ except Exception as e:
155
+ logging.error(f"处理文件失败: {str(e)}")
156
+ return 0, total_rows
157
+
158
+ def batch_process_parquets(input_dir: Path, output_root: Path):
159
+ """批量处理目录下所有Parquet文件"""
160
+ input_path = Path(input_dir)
161
+ output_root = Path(output_root)
162
+
163
+ if not input_path.exists():
164
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
165
+
166
+ parquet_files = list(input_path.glob("*.parquet"))
167
+ if not parquet_files:
168
+ logging.warning("未找到Parquet文件")
169
+ return
170
+
171
+ total_stats = {'success': 0, 'errors': 0}
172
+
173
+ for parquet_file in parquet_files:
174
+ success, errors = process_single_parquet(parquet_file, output_root)
175
+ total_stats['success'] += success
176
+ total_stats['errors'] += errors
177
+
178
+ logging.info(f"\n{'='*40}\n批量处理完成")
179
+ logging.info(f"处理文件总数: {len(parquet_files)}")
180
+ logging.info(f"总成功条目: {total_stats['success']}")
181
+ logging.info(f"总失败条目: {total_stats['errors']}")
182
+
183
+ if __name__ == "__main__":
184
+ parser = argparse.ArgumentParser(description='批量处理Parquet文件转JSON')
185
+ parser.add_argument('-i', '--input', required=True, help='输入目录路径')
186
+ parser.add_argument('-o', '--output', required=True, help='输出根目录路径')
187
+
188
+ args = parser.parse_args()
189
+
190
+ try:
191
+ start_time = time.time()
192
+ batch_process_parquets(
193
+ input_dir=args.input,
194
+ output_root=args.output
195
+ )
196
+ logging.info(f"\n总耗时: {time.time()-start_time:.2f}s")
197
+ except Exception as e:
198
+ logging.error(f"程序异常终止: {str(e)}")
199
+ sys.exit(1)
utils/parquet/merge_jp.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import shutil
4
+ from pathlib import Path
5
+ import pyarrow.parquet as pq
6
+
7
+ def merge_chemqa_dataset(dataset_dir: str, data_dir: str, output_name: str = "ChemQA") -> None:
8
+ """
9
+ 自动合并dataset_dir中所有Parquet文件对应的数据部分
10
+
11
+ 参数:
12
+ dataset_dir : 存放.parquet文件的目录
13
+ data_dir : 存放图片文件夹的根目录(JSON文件位于其父目录)
14
+ output_name : 合并后的数据集名称
15
+ """
16
+ # 转换为Path对象
17
+ dataset_path = Path(dataset_dir)
18
+ data_path = Path(data_dir)
19
+
20
+ # 获取所有需要合并的Parquet文件(按文件名排序)
21
+ parquet_files = sorted(dataset_path.glob("*.parquet"))
22
+ if not parquet_files:
23
+ raise FileNotFoundError("未找到任何Parquet文件")
24
+
25
+ # 创建目标图片目录
26
+ chemqa_img_dir = data_path / output_name
27
+ chemqa_img_dir.mkdir(exist_ok=True)
28
+
29
+ merged_data = []
30
+ global_offset = 0 # 全局索引偏移量
31
+
32
+ for pq_file in parquet_files:
33
+ part_name = pq_file.stem # 获取不带扩展名的文件名,如test-00000-of-00001
34
+ part_img_dir = data_path / part_name
35
+
36
+ # 验证图片文件夹存在
37
+ if not part_img_dir.exists():
38
+ raise FileNotFoundError(f"图片目录不存在: {part_img_dir}")
39
+
40
+ # 从Parquet获取行数
41
+ with pq.ParquetFile(pq_file) as pf:
42
+ num_rows = pf.metadata.num_rows
43
+
44
+ # 复制并重命名图片
45
+ print(f"合并 {part_name} 的 {num_rows} 张图片...")
46
+ for idx in range(num_rows):
47
+ src = part_img_dir / f"{idx}.jpg" # 假设图片格式为jpg
48
+ dst = chemqa_img_dir / f"{global_offset + idx}.jpg"
49
+ shutil.copy2(src, dst)
50
+
51
+ # 从data_dir的父目录加载对应JSON文件
52
+ json_file = data_path.parent / f"{part_name}.json"
53
+ if not json_file.exists():
54
+ raise FileNotFoundError(f"JSON文件不存在: {json_file}")
55
+
56
+ with open(json_file, 'r', encoding='utf-8') as f:
57
+ part_data = json.load(f)
58
+
59
+ # 更新索引和路径
60
+ for item in part_data:
61
+ original_idx = item["index"]
62
+ item["index"] = global_offset + original_idx
63
+ item["media_paths"] = f"./data/{output_name}/{global_offset + original_idx}.jpg"
64
+
65
+ merged_data.extend(part_data)
66
+ global_offset += num_rows
67
+
68
+ # 保存合并后的JSON到data_dir父目录
69
+ output_json = data_path.parent / f"{output_name}.json"
70
+ with open(output_json, 'w', encoding='utf-8') as f:
71
+ json.dump(merged_data, f, indent=2, ensure_ascii=False)
72
+
73
+ print(f"\n合并完成!共处理 {len(parquet_files)} 个部分")
74
+ print(f"生成数据集: {output_name}")
75
+ print(f"- 图片总数: {global_offset} 张(位于 {chemqa_img_dir})")
76
+ print(f"- JSON条目: {len(merged_data)} 条(位于 {output_json})")
77
+
78
+ if __name__ == "__main__":
79
+ parser = argparse.ArgumentParser(description="合并问答数据集")
80
+ parser.add_argument('-i', dest="dataset_dir", required=True, help="Parquet文件所在的目录路径")
81
+ parser.add_argument('-ip', dest="data_dir", required=True, help="图片文件夹根目录路径(JSON在其父目录)")
82
+ parser.add_argument('-o', "--output_name", default="ChemQA", help="输出数据集名称(默认为ChemQA)")
83
+
84
+ args = parser.parse_args()
85
+
86
+ merge_chemqa_dataset(
87
+ dataset_dir=args.dataset_dir,
88
+ data_dir=args.data_dir,
89
+ output_name=args.output_name
90
+ )
utils/parquet/pa_to_p.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import pandas as pd
3
+ from PIL import Image
4
+ from pathlib import Path
5
+ import io
6
+ import sys
7
+ import time
8
+ import logging
9
+ from concurrent.futures import ThreadPoolExecutor, as_completed
10
+ from typing import Tuple, List
11
+
12
+ # 配置日志格式
13
+ logging.basicConfig(
14
+ level=logging.INFO,
15
+ format="%(asctime)s - %(levelname)s - %(message)s",
16
+ stream=sys.stdout
17
+ )
18
+
19
+ def process_image_row(row_tuple: Tuple[int, pd.Series],
20
+ output_dir: Path,
21
+ overwrite: bool = False,
22
+ output_format: str = 'png') -> Tuple[int, str]: # 新增格式参数
23
+ """处理单行图像数据(线程安全)"""
24
+ idx, row = row_tuple
25
+
26
+ # 根据格式确定扩展名和保存参数
27
+ if output_format.lower() == 'jpg':
28
+ file_ext = 'jpg'
29
+ img_format = 'JPEG'
30
+ save_args = {'quality': 95} # JPEG质量参数
31
+ elif output_format.lower() == 'png':
32
+ file_ext = 'png'
33
+ img_format = 'PNG'
34
+ save_args = {'compress_level': 6} # PNG压缩级别
35
+ else:
36
+ raise ValueError(f"Unsupported format: {output_format}")
37
+
38
+ output_path = output_dir / f"{idx}.{file_ext}" # 动态扩展名
39
+
40
+ try:
41
+ # 跳过已存在文件
42
+ if not overwrite and output_path.exists():
43
+ return (idx, "skipped")
44
+
45
+ img_data = row['image']
46
+ # 统一处理不同存储格式
47
+ if isinstance(img_data, dict):
48
+ img_bytes = img_data['bytes']
49
+ elif isinstance(img_data, bytes):
50
+ img_bytes = img_data
51
+ else:
52
+ raise ValueError("Unknown image storage format")
53
+
54
+ with Image.open(io.BytesIO(img_bytes)) as img:
55
+ # 公共处理:CMYK模式必须转换(PNG和JPEG都不支持)
56
+ if img.mode == 'CMYK':
57
+ img = img.convert('RGB')
58
+
59
+ # 格式专用处理
60
+ if output_format == 'jpg':
61
+ # JPEG不支持的模式转换
62
+ if img.mode in ['P', 'PA']: # 调色板模式
63
+ img = img.convert('RGBA')
64
+ background = Image.new('RGB', img.size, (255, 255, 255))
65
+ background.paste(img, mask=img.split()[-1])
66
+ img = background
67
+ elif img.mode == 'LA': # 带透明度的灰度
68
+ img = img.convert('L') # 转换为纯灰度
69
+ elif img.mode in ['RGBA', 'RGBa']: # 带透明度的RGB
70
+ background = Image.new('RGB', img.size, (255, 255, 255))
71
+ background.paste(img, mask=img.split()[-1])
72
+ img = background
73
+ # 最终保险:强制转换非标准模式
74
+ if img.mode not in ['RGB', 'L']:
75
+ img = img.convert('RGB') # 确保所有JPEG都是RGB或灰度
76
+ else:
77
+ # PNG专用处理(仅CMYK需要转换,其他保留原始模式)
78
+ pass # 可以添加其他PNG优化逻辑(如调色板压缩)
79
+
80
+ # 保存图像(根据格式使用不同参数)
81
+ img.save(output_path, img_format, **save_args)
82
+ return (idx, "success")
83
+
84
+ except Exception as e:
85
+ return (idx, f"error: {str(e)}")
86
+
87
+ def process_single_parquet(parquet_path: Path,
88
+ output_root: Path,
89
+ threads: int = 4,
90
+ overwrite: bool = False,
91
+ output_format: str = 'png') -> Tuple[int, int]: # 新增格式参数
92
+ """处理单个Parquet文件(带并发)"""
93
+ start_time = time.time()
94
+ file_name = parquet_path.stem
95
+ output_dir = output_root / f"{file_name}"
96
+ output_dir.mkdir(parents=True, exist_ok=True)
97
+
98
+ error_log = []
99
+ success_count = 0
100
+ skipped_count = 0
101
+
102
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
103
+ logging.info(f"Output format: {output_format.upper()}")
104
+ logging.info(f"Output directory: {output_dir}")
105
+
106
+ try:
107
+ df = pd.read_parquet(parquet_path)
108
+ total_rows = len(df)
109
+
110
+ # 创建线程池
111
+ with ThreadPoolExecutor(max_workers=threads) as executor:
112
+ futures = [
113
+ executor.submit(
114
+ process_image_row,
115
+ (idx, row),
116
+ output_dir,
117
+ overwrite,
118
+ output_format # 传递格式参数
119
+ )
120
+ for idx, row in df.iterrows()
121
+ ]
122
+
123
+ # 收集处理结果
124
+ for future in as_completed(futures):
125
+ idx, status = future.result()
126
+ if status == "success":
127
+ success_count += 1
128
+ elif status.startswith("error"):
129
+ error_log.append(f"line {idx} error: {status[6:]}")
130
+ elif status == "skipped":
131
+ skipped_count += 1
132
+
133
+ # 生成处理报告
134
+ process_time = time.time() - start_time
135
+ report = (
136
+ f"Results: {success_count}/{total_rows} succeed | "
137
+ f"Skipped: {skipped_count} | "
138
+ f"Errors: {len(error_log)}\n"
139
+ f"Time: {process_time:.2f}s | "
140
+ f"Speed: {total_rows/process_time:.2f} rows/s"
141
+ )
142
+ logging.info(report)
143
+
144
+ # 保存错误日志
145
+ if error_log:
146
+ (output_dir / "process_errors.log").write_text("\n".join(error_log))
147
+
148
+ return success_count, len(error_log)
149
+
150
+ except Exception as e:
151
+ logging.error(f"File processing failed: {str(e)}")
152
+ return 0, 0
153
+
154
+ def batch_process_parquets(input_dir: Path,
155
+ output_root: Path,
156
+ threads: int = 4,
157
+ overwrite: bool = False,
158
+ output_format: str = 'png'): # 新增格式参数
159
+ """批量处理目录下所有Parquet文件"""
160
+ input_path = Path(input_dir)
161
+ output_root = Path(output_root)
162
+
163
+ if not input_path.exists():
164
+ raise FileNotFoundError(f"Input directory not found: {input_path}")
165
+
166
+ parquet_files = list(input_path.glob("*.parquet"))
167
+ if not parquet_files:
168
+ logging.warning("No Parquet files found")
169
+ return
170
+
171
+ total_stats = {'success':0, 'errors':0}
172
+
173
+ for parquet_file in parquet_files:
174
+ success, errors = process_single_parquet(
175
+ parquet_file,
176
+ output_root,
177
+ threads=threads,
178
+ overwrite=overwrite,
179
+ output_format=output_format # 传递格式参数
180
+ )
181
+ total_stats['success'] += success
182
+ total_stats['errors'] += errors
183
+
184
+ # 最终统计报告
185
+ logging.info(f"\n{'='*40}\nBatch processing completed")
186
+ logging.info(f"Total processed files: {len(parquet_files)}")
187
+ logging.info(f"Total successful images: {total_stats['success']}")
188
+ logging.info(f"Total errors: {total_stats['errors']}")
189
+
190
+ if __name__ == "__main__":
191
+ parser = argparse.ArgumentParser(description="Parallel processing of Parquet files to generate images")
192
+ parser.add_argument("-i", "--input", required=True, help="Input directory path")
193
+ parser.add_argument("-o", "--output", required=True, help="Output directory path")
194
+ parser.add_argument("--threads", type=int, default=4, help="Number of concurrent threads (default 4)")
195
+ parser.add_argument("--overwrite", action="store_true", help="Overwrite existing files")
196
+ # 新增格式选择参数
197
+ parser.add_argument("--format", choices=['png', 'jpg'], default='jpg',
198
+ help="Output image format (png/jpg, default jpg)")
199
+
200
+ args = parser.parse_args()
201
+
202
+ try:
203
+ start = time.time()
204
+ batch_process_parquets(
205
+ input_dir=args.input,
206
+ output_root=args.output,
207
+ threads=args.threads,
208
+ overwrite=args.overwrite,
209
+ output_format=args.format # 传递格式参数
210
+ )
211
+ logging.info(f"\nTotal processing time: {time.time()-start:.2f}s")
212
+ except Exception as e:
213
+ logging.error(f"Fatal error: {str(e)}")
214
+ sys.exit(1)
utils/parquet/pathQA_ptj.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from ast import literal_eval
9
+ import time
10
+ from typing import Tuple
11
+ import pandas as pd
12
+
13
+ # 配置日志
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format="%(asctime)s - %(levelname)s - %(message)s",
17
+ stream=sys.stdout
18
+ )
19
+
20
+ def process_row(args):
21
+ """处理单行数据(线程安全)"""
22
+ index, row, file_stem= args
23
+ try:
24
+ # 生成媒体路径
25
+ media_path = "./" + (Path("data") / file_stem / f"{index}.png").as_posix()
26
+
27
+
28
+ return {
29
+ "index": index,
30
+ "media_type": "image",
31
+ "media_paths": media_path,
32
+ "description": "",
33
+ "task_type": "Vision-Question-Answer",
34
+ "question": [row.get('question', '')],
35
+ "question_type": "free-form",
36
+ "annotations":[],
37
+ "options": [],
38
+ "answer": [row.get('answer', '')],
39
+ "source": "PathQA",
40
+ "domain": "Biomedical"
41
+ }
42
+ except Exception as e:
43
+ logging.error(f"处理行 {index} 时出错: {str(e)}")
44
+ return None
45
+
46
+ def process_single_parquet(parquet_path: Path, output_root: Path) -> Tuple[int, int]:
47
+ """处理单个Parquet文件"""
48
+ start_time = time.time()
49
+ file_stem = parquet_path.stem
50
+ output_dir = output_root
51
+ output_json = output_dir / f"{file_stem}.json"
52
+
53
+ success_count = 0
54
+ error_count = 0
55
+ results = []
56
+
57
+ try:
58
+ df = pd.read_parquet(parquet_path)
59
+ total_rows = len(df)
60
+
61
+ logging.info(f"\n{'='*40}\nProcessing: {parquet_path.name}")
62
+ logging.info(f"Output directory: {output_dir}")
63
+
64
+ # 创建线程池
65
+ with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as executor:
66
+ task_args = [(idx, row, file_stem) for idx, row in df.iterrows()]
67
+ futures = [executor.submit(process_row, args) for args in task_args]
68
+
69
+ for future in futures:
70
+ result = future.result()
71
+ if result:
72
+ results.append(result)
73
+ success_count += 1
74
+ else:
75
+ error_count += 1
76
+
77
+ # 写入JSON文件
78
+ with open(output_json, 'w', encoding='utf-8') as f:
79
+ json.dump(results, f, ensure_ascii=False, indent=2)
80
+
81
+ # 生成报告
82
+ process_time = time.time() - start_time
83
+ logging.info(
84
+ f"Processed: {success_count}/{total_rows} | "
85
+ f"Errors: {error_count} | "
86
+ f"Time: {process_time:.2f}s"
87
+ )
88
+
89
+ return success_count, error_count
90
+
91
+ except Exception as e:
92
+ logging.error(f"处理文件失败: {str(e)}")
93
+ return 0, total_rows
94
+
95
+ def batch_process_parquets(input_dir: Path, output_root: Path):
96
+ """批量处理目录下所有Parquet文件"""
97
+ input_path = Path(input_dir)
98
+ output_root = Path(output_root)
99
+
100
+ if not input_path.exists():
101
+ raise FileNotFoundError(f"输入目录不存在: {input_path}")
102
+
103
+ parquet_files = list(input_path.glob("*.parquet"))
104
+ if not parquet_files:
105
+ logging.warning("未找到Parquet文件")
106
+ return
107
+
108
+ total_stats = {'success': 0, 'errors': 0}
109
+
110
+ for parquet_file in parquet_files:
111
+ success, errors = process_single_parquet(parquet_file, output_root)
112
+ total_stats['success'] += success
113
+ total_stats['errors'] += errors
114
+
115
+ logging.info(f"\n{'='*40}\n批量处理完成")
116
+ logging.info(f"处理文件总数: {len(parquet_files)}")
117
+ logging.info(f"总成功条目: {total_stats['success']}")
118
+ logging.info(f"总失败条目: {total_stats['errors']}")
119
+
120
+ if __name__ == "__main__":
121
+ parser = argparse.ArgumentParser(description='批量处理Parquet文件转JSON')
122
+ parser.add_argument('-i', '--input', required=True, help='输入目录路径')
123
+ parser.add_argument('-o', '--output', required=True, help='输出根目录路径')
124
+
125
+ args = parser.parse_args()
126
+
127
+ try:
128
+ start_time = time.time()
129
+ batch_process_parquets(
130
+ input_dir=args.input,
131
+ output_root=args.output
132
+ )
133
+ logging.info(f"\n总耗时: {time.time()-start_time:.2f}s")
134
+ except Exception as e:
135
+ logging.error(f"程序异常终止: {str(e)}")
136
+ sys.exit(1)
utils/upload/batch_download.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+
5
+ def batch_download_files():
6
+ # 初始化客户端
7
+ client = OpenAI(
8
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
9
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
10
+ )
11
+
12
+ try:
13
+ # 获取用户输入
14
+ input_jsonl = input("请输入包含batch任务状态的jsonl文件路径:")
15
+ output_dir = input("请输入保存文件的目录路径:")
16
+
17
+ # 创建输出目录(如果不存在)
18
+ os.makedirs(output_dir, exist_ok=True)
19
+
20
+ # 读取并处理jsonl文件
21
+ with open(input_jsonl, 'r', encoding='utf-8') as f:
22
+ for line_num, line in enumerate(f, 1):
23
+ try:
24
+ entry = json.loads(line.strip())
25
+
26
+ # 检查状态和文件ID
27
+ if entry.get('status') == 'completed':
28
+ file_id = entry.get('output_file_id')
29
+ if not file_id:
30
+ print(f"第 {line_num} 行: 缺少output_file_id")
31
+ continue
32
+
33
+ # 下载文件内容
34
+ content = client.files.content(file_id=file_id)
35
+
36
+ # 构建保存路径
37
+ filename = f"{file_id}.jsonl"
38
+ save_path = os.path.join(output_dir, filename)
39
+
40
+ # 保存文件
41
+ content.write_to_file(save_path)
42
+ print(f"成功保存: {filename} -> {save_path}")
43
+
44
+ else:
45
+ print(f"第 {line_num} 行: 状态未完成(当前状态:{entry.get('status')})")
46
+
47
+ except json.JSONDecodeError:
48
+ print(f"第 {line_num} 行: JSON解析失败")
49
+ except Exception as e:
50
+ print(f"第 {line_num} 行: 发生错误 - {str(e)}")
51
+
52
+ except FileNotFoundError:
53
+ print("错误:输入文件不存在")
54
+ except Exception as e:
55
+ print(f"发生未预期的错误: {str(e)}")
56
+
57
+ if __name__ == "__main__":
58
+ batch_download_files()
utils/upload/batch_search.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+
5
+ # 初始化OpenAI客户端(实际对接阿里云百炼)
6
+ client = OpenAI(
7
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
8
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
9
+ )
10
+
11
+ def process_batches(input_file):
12
+ output_file = "batch_status_output.jsonl"
13
+
14
+ with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
15
+ for line in infile:
16
+ try:
17
+ # 解析JSONL行数据
18
+ entry = json.loads(line.strip())
19
+ batch_id = entry['id']
20
+
21
+ # 查询Batch详细信息
22
+ batch = client.batches.retrieve(batch_id)
23
+
24
+ # 构建结果记录
25
+ result = {
26
+ "status": batch.status,
27
+ "input_file_id": batch.input_file_id,
28
+ "output_file_id": batch.output_file_id
29
+ }
30
+
31
+ # 写入结果到新JSONL文件
32
+ outfile.write(json.dumps(result) + '\n')
33
+ print(f"Processed batch: {batch_id}")
34
+
35
+ except KeyError:
36
+ print(f"Invalid entry format: {line.strip()}")
37
+ except Exception as e:
38
+ print(f"Error processing batch {batch_id}: {str(e)}")
39
+
40
+ if __name__ == "__main__":
41
+ input_path = input("请输入包含Batch IDs的JSONL文件路径: ")
42
+ if os.path.exists(input_path):
43
+ process_batches(input_path)
44
+ print(f"处理完成,结果已保存到 batch_status_output.jsonl")
45
+ else:
46
+ print("错误:输入文件不存在")
utils/upload/batch_upload.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+
5
+ # 初始化OpenAI客户端
6
+ client = OpenAI(
7
+ api_key="sk-xxx",
8
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
9
+ )
10
+
11
+ # 获取用户输入的文件夹路径
12
+ folder_path = input("请输入包含批量文件的文件夹路径:")
13
+ if not os.path.isdir(folder_path):
14
+ print("错误:输入的路径不存在或不是文件夹")
15
+ exit()
16
+
17
+ # 获取文件夹下所有文件列表
18
+ file_list = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
19
+
20
+ # 创建并保存batch任务信息
21
+ with open("batch_results.jsonl", "w", encoding="utf-8") as jsonl_file:
22
+ for filename in file_list:
23
+ # 构建OSS文件标识符(假设OSS路径结构与本地一致)
24
+ input_file_id = f"oss:me-east-1:acm-mm/test/{filename}"
25
+
26
+ try:
27
+ # 创建batch任务
28
+ batch = client.batches.create(
29
+ input_file_id=input_file_id,
30
+ endpoint="/v1/chat/completions",
31
+ completion_window="1h"
32
+ )
33
+
34
+ # 提取需要的信息
35
+ batch_info = {
36
+ "id": batch.id,
37
+ "input_file_id": batch.input_file_id
38
+ }
39
+
40
+ # 写入JSONL文件
41
+ jsonl_file.write(json.dumps(batch_info, ensure_ascii=False) + "\n")
42
+ print(f"文件 {filename} 已成功创建任务,ID: {batch.id}")
43
+
44
+ except Exception as e:
45
+ print(f"为文件 {filename} 创建任务失败,错误信息: {str(e)}")
utils/upload/compare.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ def read_original_file(file_path):
4
+ original_data = {}
5
+ custom_id_order = []
6
+ with open(file_path, 'r', encoding='utf-8') as f:
7
+ for line in f:
8
+ entry = json.loads(line.strip())
9
+ custom_id = entry.get('custom_id')
10
+ if not custom_id:
11
+ continue
12
+
13
+ # 提取用户问题
14
+ body = entry.get('body', {})
15
+ messages = body.get('messages', [])
16
+ user_content = None
17
+ for msg in messages:
18
+ if msg.get('role') == 'user':
19
+ user_content = msg.get('content')
20
+ break
21
+
22
+ if user_content is not None:
23
+ original_data[custom_id] = user_content
24
+ custom_id_order.append(custom_id)
25
+
26
+ return custom_id_order, original_data
27
+
28
+ def read_output_file(file_path):
29
+ output_data = {}
30
+ with open(file_path, 'r', encoding='utf-8') as f:
31
+ for line in f:
32
+ entry = json.loads(line.strip())
33
+ custom_id = entry.get('custom_id')
34
+ if not custom_id:
35
+ continue
36
+
37
+ # 提取模型输出
38
+ response = entry.get('response', {})
39
+ body = response.get('body', {})
40
+ choices = body.get('choices', [])
41
+ model_output = ''
42
+ if choices:
43
+ message = choices[0].get('message', {})
44
+ model_output = message.get('content', '')
45
+
46
+ output_data[custom_id] = model_output
47
+
48
+ return output_data
49
+
50
+ def main():
51
+ # 获取用户输入路径
52
+ original_path = input("请输入原始请求文件路径:").strip()
53
+ output_path = input("请输入大模型输出文件路径:").strip()
54
+ save_path = input("请输入结果保存路径:").strip()
55
+
56
+ # 读取数据
57
+ custom_id_order, original_data = read_original_file(original_path)
58
+ output_data = read_output_file(output_path)
59
+
60
+ # 写入结果文件
61
+ with open(save_path, 'w', encoding='utf-8') as f:
62
+ for i, cid in enumerate(custom_id_order):
63
+ original_question = original_data.get(cid, '')
64
+ model_output = output_data.get(cid, '')
65
+
66
+ f.write(f"custom_id: {cid}\n")
67
+ f.write(f"原问题: {original_question}\n")
68
+ f.write(f"大模型输出: {model_output}\n")
69
+
70
+ # 组间空行(最后一组不空)
71
+ if i != len(custom_id_order) - 1:
72
+ f.write('\n')
73
+
74
+ if __name__ == "__main__":
75
+ main()
utils/upload/download.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+
4
+ client = OpenAI(
5
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
6
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
7
+ )
8
+ content = client.files.content(file_id="file-batch_output-xxx")
9
+ # 打印结果文件内容
10
+ content.write_to_file("result.jsonl")
utils/upload/jsonl_otest.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def analyze_jsonl_size(input_path):
2
+ over_count = 0
3
+ total_over_size = 0 # 总大小,单位字节
4
+ max_size = 0 # 最大对象大小,单位字节
5
+ line_counter = 0 # 行号跟踪(可选)
6
+
7
+ with open(input_path, 'r', encoding='utf-8') as file:
8
+ for line in file:
9
+ line_counter += 1
10
+ # 计算当前行的字节大小
11
+ line_bytes = line.encode('utf-8')
12
+ current_size = len(line_bytes)
13
+
14
+ # 检查是否超过1MB
15
+ if current_size > 1 * 1024 * 1024:
16
+ over_count += 1
17
+ total_over_size += current_size
18
+ if current_size > max_size:
19
+ max_size = current_size
20
+
21
+ # 输出统计结果
22
+ print(f"Number of objects exceeding 1MB: {over_count}")
23
+ if over_count > 0:
24
+ avg_size_mb = (total_over_size / over_count) / (1024 * 1024)
25
+ max_size_mb = max_size / (1024 * 1024)
26
+ print(f"Average size of oversized objects: {avg_size_mb:.2f} MB")
27
+ print(f"Largest object size: {max_size_mb:.2f} MB")
28
+ else:
29
+ print("No objects exceed the 1MB limit.")
30
+
31
+ # 示例调用
32
+ analyze_jsonl_size('/mnt/data/users/zys/proj/vlm_reasoning/request/vlm_batch_requests.jsonl')
utils/upload/jsonl_split.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ def split_jsonl_by_size(input_path, output_dir, target_size_mb):
4
+ target_size = target_size_mb * 1024 * 1024 # 转换为字节
5
+ os.makedirs(output_dir, exist_ok=True)
6
+
7
+ part_num = 1
8
+ current_size = 0
9
+ current_file = None
10
+ line_counter = 0 # 用于跟踪行号
11
+
12
+ with open(input_path, 'r', encoding='utf-8') as infile:
13
+ for line in infile:
14
+ line_counter += 1
15
+ line_bytes = line.encode('utf-8')
16
+ line_size = len(line_bytes)
17
+
18
+ # 新增:检查单行大小是否超过1MB
19
+ if line_size > 1 * 1024 * 1024:
20
+ raise ValueError(
21
+ f"JSON object at line {line_counter} exceeds 1MB limit "
22
+ f"(actual size: {line_size / 1024 / 1024:.2f}MB)"
23
+ )
24
+
25
+ # 原有分割逻辑
26
+ if current_file and (current_size + line_size > target_size):
27
+ current_file.close()
28
+ current_file = None
29
+ current_size = 0
30
+ part_num += 1
31
+
32
+ if not current_file:
33
+ output_path = os.path.join(output_dir, f'vlm_requests_{part_num}.jsonl')
34
+ current_file = open(output_path, 'wb')
35
+ current_size = 0
36
+
37
+ current_file.write(line_bytes)
38
+ current_size += line_size
39
+
40
+ if current_file:
41
+ current_file.close()
42
+
43
+ # 示例调用
44
+ split_jsonl_by_size('/mnt/data/users/zys/proj/vlm_reasoning/request/vlm_batch_requests.jsonl', '/mnt/data/users/zys/proj/vlm_reasoning/upload/vlm', 450)
utils/upload/load_ll.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import copy
4
+ import concurrent.futures
5
+ import traceback
6
+
7
+ system_prompt = """You are a professional textual question-answering analyst. Your expertise lies in transforming explicit textual queries, which may have originally been associated with images, into sophisticated, implicit instructions using only the provided text. Your goal is to reframe direct questions into nuanced directives that guide a user or another AI to the same answer through logical deduction, contextual understanding, or knowledge application based *solely on the textual information available*, rather than by a direct ask.\n\n**Important Context:** You must be aware that the `Original Question` you receive likely originated from a dataset where it was paired with an image. However, for your task, **you will NOT receive the image**. The explicit question might therefore contain implicit references to visual elements (e.g., \"the object on the left\", \"the color of the car\") that are underspecified in the text alone.\n\nYour Task:\nGiven an explicit textual question (and potentially its answer or relevant context), originating from an image-question pair but provided to you without the image, your task is to:\n1. Analyze the `Original Question` and any provided `Answer/Context` to understand the core informational intent, acknowledging potential reliance on missing visual details.\n2. Convert the explicit question into a compelling implicit instruction. This instruction should:\n * Focus on aspects of the query solvable using the **provided text** and general knowledge.\n * Leverage **all details** available in the `Answer/Context` field, as this might contain crucial information originally derived from the image.\n * Reformulate or abstract parts of the question that heavily relied on specific visual cues into more general or conceptual descriptions, if possible, using the analytical dimensions.\n * **Avoid** creating instructions that fundamentally require visual inspection of an image that is not present. If a question is purely visual and cannot be meaningfully reformulated textually (e.g., \"What specific shade of blue is the sky in the top right corner?\"), aim for the most reasonable textual abstraction or focus on other extractable information.\n3. Classify the ​​reframed instruction​​ into one of five analytical dimensions (Structural Property Enhancement, Spatial-Logical Relationship Modeling, Domain Knowledge Integration, Multimodal Reasoning Pathways, Semantic Context Reconstruction) based on the primary analytical approach used in the **textual reformulation**.\n\nMethodology: Reconstructing the Query Using Analytical Dimensions (Text-Only Adaptation)\nTo create the implicit instruction from the text-only input:\n1. Actively draw insights *only* from the provided `Original Question` and `Answer/Context`.\n2. Utilize one or more of the five analytical dimensions below to adjust the question, add textual constraints or domain knowledge, model relationships described textually, or reconstruct semantic meaning based on the available words.\n3. Assign a ​​classification​​ based on how the *textual* information was primarily restructured or analyzed.\n4. The implicit instruction should make the intended answer feel like a natural consequence of analyzing the **provided text** and applying relevant knowledge.\n\nAnalytical Dimensions & Examples (Applied to Textual Input, potentially abstracting original visual cues):\n\n1. ​​Structural Property Enhancement​​ - Add descriptive structural or quantitative attributes *mentioned or implied* in the text.\n 1.1 ​​Physical Properties​​ (Text-based)\n Original question (from image context, maybe): \"Describe the benzene ring shown.\" -> (Text input only): \"Describe a benzene ring structure.\"\n Implicit instruction: \"Describe the key structural features of an aromatic hydrocarbon molecule known for its planar regular hexagonal symmetry, focusing on its carbon-carbon bonding and associated hydrogen atoms based on standard chemical representation.\" (Focus shifts to general knowledge)\n 1.2 ​​Quantitative Features​​ (Text-based)\n Original question (from image context): \"Count the runways.\" -> (Text input only): \"Count the runways.\" (Potentially with context: \"Context: The image depicts a large international airport.\")\n Implicit instruction: \"Based on the context of a large international airport, enumerate the typical number of parallel, elongated structures designed for aircraft takeoff/landing that meet high-capacity standards (e.g., length > 3000m), assuming standard configurations if specifics aren't provided.\" (Uses context and general knowledge)\n\n2. ​​Spatial-Logical Relationship Modeling​​ - Model relationships *described or implied* textually.\n 2.1 ​​Hierarchical Structures​​ (Text-based)\n Original question (from image/text): \"What skin disease based on these microscopic findings?\" -> (Text input only): \"What skin disease?\" (Context: \"Hyperkeratosis in epidermis, lymphocyte infiltration in dermis.\")\n Implicit instruction: \"Given the pathological findings described as 'hyperkeratosis in the epidermal layer' and 'lymphocyte infiltration in the dermal layer', determine the most probable dermatological diagnosis by correlating these layer-specific abnormalities.\" (Uses provided textual context directly)\n 2.2 ​​Spatial Topology​​ (Conceptual/Mathematical)\n Original question: \"Number of common tangents for these two circles.\" -> (Text input only): \"Number of common tangents when two circles are tangent.\"\n Implicit instruction: \"In a conceptual geometric scenario where two circles are defined as being externally tangent, determine the total count of lines that can be drawn tangent to both circles simultaneously, based on the properties of this specific topological arrangement.\" (Focuses on the defined geometric condition)\n\n3. ​​Domain Knowledge Integration​​ - Infuse domain knowledge relevant to the *textual topic*.\n 3.1 ​​Domain-Specific Characteristics​​ (Text-based)\n Original question (from image): \"Identify the oil storage tanks.\" -> (Text input only): \"Identify the oil storage tanks.\" (Context: \"Area contains large, circular metal structures.\")\n Implicit instruction: \"Based on the description of 'large, circular metal structures' often found in industrial areas, and applying typical characteristics known from domains like remote sensing or industrial engineering (e.g., large diameter, specific roof types, association with pipelines), infer the likely function of these structures as potential storage units, possibly for petroleum products.\" (Connects text description to domain knowledge)\n 3.2 ​​Mathematical Constraints​​\n Original question: \"Prove the triangle angle sum theorem.\" (Text only)\n Implicit instruction: \"Utilizing the axioms and postulates of Euclidean geometry, particularly the properties of parallel lines and transversal intersections, construct a logical argument demonstrating that the sum of the interior angles of any planar triangle invariably equals 180 degrees.\" (Purely conceptual/textual)\n\n4. ​​Multimodal Reasoning Pathways​​ (Text-based: Combining textual info/logic)\n 4.1 ​​Exclusion Logic​​ (Text-based)\n Original question (from image/symptoms): \"Which vitamin deficiency?\" -> (Text input only): \"Which vitamin deficiency?\" (Context: \"Symptoms: follicular hyperkeratosis, nyctalopia. Patient gets ample sunlight.\")\n Implicit instruction: \"Considering the presented symptoms 'follicular hyperkeratosis' and 'nyctalopia', and given the contextual information ruling out insufficient light exposure (a common factor for Vitamin D issues), deduce the most likely fat-soluble vitamin deficiency responsible for this specific combination of clinical signs.\" (Uses text symptoms + exclusion context)\n 4.2 ​​Data Association​​ (Text-based)\n Original question: \"What is the molecular weight of sodium chloride?\" (Text only)\n Implicit instruction: \"Accessing standard atomic weight data, calculate the sum corresponding to the chemical formula NaCl, reflecting the one-to-one ionic ratio between sodium (Na, element 11) and chlorine (Cl, element 17).\" (Associates name/formula with data lookup and calculation rule)\n\n5. ​​Semantic Context Reconstruction​​ - Leverage functional descriptions or context *provided in text*.\n 5.1 ​​Functional Descriptions​​ (Text-based)\n Original question (from image): \"Name this apparatus.\" -> (Text input only): \"Name the apparatus.\" (Context: \"Used for collecting fractions during distillation.\")\n Implicit instruction: \"Identify the standard laboratory glassware term for a conical-shaped vessel specifically employed during atmospheric distillation processes to receive liquids condensing at different boiling points.\" (Uses functional context provided textually)\n 5.2 ​​Anomaly Detection​​ (Based on *described* features)\n Original question (from image): \"Is this illegal mining?\" -> (Text input only): \"Is this illegal mining?\" (Context: \"Vegetated area shows regular bare patches and new roads.\")\n Implicit instruction: \"Evaluate the provided description of a land area – 'geometrically regular bare areas appearing within a vegetation zone, accompanied by traces of transport roads' – against typical indicators of unauthorized resource extraction activities to determine if it signifies potential illegal mining.\" (Focuses on interpreting the textual description)\n\nInput Format You Will Receive:\n* ​​Original Question​​ (required, text - potentially referencing unseen visual context)\n* ​​Answer/Context​​ (optional, text - crucial for potentially bridging the visual gap)\n* *Note: Although the original data source included images, your input for this task consists solely of the text components.*\n\nOutput Format You Should Generate:\nParse the reframed ​​instruction​​ and its ​​classification​​ into JSON format. Follow this structure:\n\n{\n \"question\": \"[Implicit instruction focused on textual analysis, abstracting visual reliance]\",\n \"classification\": \"[One of: Structural Property Enhancement, Spatial-Logical Relationship Modeling, Domain Knowledge Integration, Multimodal Reasoning Pathways, Semantic Context Reconstruction]\"\n}\n\n​​Example Output (using the distillation apparatus example):​​\n{\n \"question\": \"Identify the standard laboratory glassware term for a conical-shaped vessel specifically employed during atmospheric distillation processes to receive liquids condensing at different boiling points.\",\n \"classification\": \"Semantic Context Reconstruction\"\n}"""
8
+
9
+ base_template = {
10
+ "custom_id": None,
11
+ "method": "POST",
12
+ "url": "/v1/chat/completions",
13
+ "body": {
14
+ "model": "qwen-plus-latest",
15
+ "messages": [
16
+ {"role": "system", "content": system_prompt},
17
+ {"role": "user", "content": None}
18
+ ],
19
+ "temperature": 0.5,
20
+ "response_format":{"type": "json_object"},
21
+ "max_tokens": 200
22
+ }
23
+ }
24
+
25
+ def process_single_file(json_file, dataset_dir):
26
+ print(f"\nProcessing JSON file: {json_file}")
27
+ json_path = os.path.join(dataset_dir, json_file)
28
+ file_requests = []
29
+
30
+ try:
31
+ with open(json_path, 'r', encoding='utf-8') as f:
32
+ data = json.load(f)
33
+ except (IOError, json.JSONDecodeError) as e:
34
+ print(f"Failed to load {json_path}: {str(e)}")
35
+ return file_requests
36
+
37
+ if not isinstance(data, list):
38
+ print(f"Invalid data format in {json_file}, expected list")
39
+ return file_requests
40
+
41
+ for item_idx, item in enumerate(data):
42
+ try:
43
+ if not isinstance(item, dict):
44
+ print(f"Invalid item format at index {item_idx} in {json_file}")
45
+ continue
46
+
47
+ questions = item.get('question', [])
48
+ answers = item.get('answer', [])
49
+ options = item.get('options', [])
50
+ description = item.get('description', '')
51
+
52
+ valid_description = ""
53
+ if (isinstance(description, str) and
54
+ len(description.split()) >= 3 and
55
+ len(description) > 0):
56
+ valid_description = description.strip() + " "
57
+
58
+ if not isinstance(questions, list) or len(questions) == 0:
59
+ print(f"Invalid questions in item {item_idx} of {json_file}")
60
+ continue
61
+
62
+ option_map = {}
63
+ if options and isinstance(options, list):
64
+ try:
65
+ option_map = {opt['id']: opt['text'] for opt in options
66
+ if 'id' in opt and 'text' in opt}
67
+ except KeyError as e:
68
+ print(f"Invalid option format in {json_file} item {item_idx}: {str(e)}")
69
+
70
+ for qa_idx in range(len(questions)):
71
+ try:
72
+ original_q = questions[qa_idx]
73
+ if not isinstance(original_q, str):
74
+ print(f"Invalid question format at index {qa_idx} in {json_file} item {item_idx}")
75
+ continue
76
+
77
+ q_text = valid_description + original_q
78
+
79
+ answer_text = ""
80
+ if qa_idx < len(answers):
81
+ original_answer = answers[qa_idx]
82
+ try:
83
+ if len(option_map) >= 1:
84
+ answer_text = option_map.get(original_answer[0], original_answer[0])
85
+ else:
86
+ answer_text = original_answer if isinstance(original_answer, str) else ""
87
+ except (TypeError, IndexError) as e:
88
+ print(f"Answer processing error: {str(e)}")
89
+
90
+ # 纯文本内容构造
91
+ text_content = f"question:{q_text}"
92
+ if answer_text:
93
+ text_content += f"\nanswer:{answer_text}"
94
+
95
+ request = copy.deepcopy(base_template)
96
+ request['custom_id'] = f"{json_file[:-5]}-{item_idx}-{qa_idx}"
97
+ request['body']['messages'][1]['content'] = text_content
98
+
99
+ file_requests.append(request)
100
+ except Exception as e:
101
+ print(f"Error processing QA pair {qa_idx} in {json_file} item {item_idx}: {str(e)}")
102
+ traceback.print_exc()
103
+
104
+ except Exception as e:
105
+ print(f"Error processing item {item_idx} in {json_file}: {str(e)}")
106
+ continue
107
+
108
+ return file_requests
109
+
110
+ def process_dataset(dataset_dir):
111
+ batch_requests = []
112
+
113
+ if not os.path.exists(dataset_dir):
114
+ print(f"Error: Dataset directory {dataset_dir} does not exist")
115
+ return batch_requests
116
+
117
+ json_files = [f for f in os.listdir(dataset_dir) if f.endswith('.json')]
118
+
119
+ # 保持并行处理以提升效率
120
+ with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
121
+ futures = {executor.submit(process_single_file, json_file, dataset_dir): json_file
122
+ for json_file in json_files}
123
+
124
+ for future in concurrent.futures.as_completed(futures):
125
+ json_file = futures[future]
126
+ try:
127
+ requests = future.result()
128
+ batch_requests.extend(requests)
129
+ except Exception as e:
130
+ print(f"Error processing file {json_file}: {str(e)}")
131
+ traceback.print_exc()
132
+
133
+ return batch_requests
134
+
135
+ if __name__ == "__main__":
136
+ dataset_directory = "/mnt/data/users/zys/proj/vlm_reasoning/load"
137
+ try:
138
+ batch_requests = process_dataset(dataset_directory)
139
+
140
+ with open("/mnt/data/users/zys/proj/vlm_reasoning/request/llm_batch_requests.jsonl", 'w') as f:
141
+ for req in batch_requests:
142
+ try:
143
+ f.write(json.dumps(req, ensure_ascii=False) + '\n')
144
+ except (TypeError, IOError) as e:
145
+ print(f"Failed to write request: {str(e)}")
146
+ continue
147
+
148
+ print(f"Successfully generated {len(batch_requests)} QA requests")
149
+ except Exception as e:
150
+ print(f"Critical error occurred: {str(e)}")
151
+ traceback.print_exc()
utils/upload/load_vl.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import base64
5
+ import copy
6
+ from io import BytesIO
7
+ from PIL import Image
8
+ import traceback
9
+ import concurrent.futures # 新增多线程支持
10
+
11
+ def encode_image_from_pil(pil_image, max_size=256, quality=85):
12
+ """
13
+ 将PIL图像按比例缩放到指定最大边长后,编码为Base64字符串
14
+
15
+ 参数:
16
+ pil_image (Image): PIL图像对象
17
+ max_size (int): 缩放后的最长边像素(默认256)
18
+ quality (int): JPEG压缩质量(1-95,默认85)
19
+
20
+ 返回:
21
+ str: Base64编码的字符串,失败时返回None
22
+ """
23
+ try:
24
+ # 1. 尺寸调整
25
+ width, height = pil_image.width, pil_image.height
26
+ if max(width, height) > max_size:
27
+ ratio = min(max_size / width, max_size / height)
28
+ new_size = (int(width * ratio), int(height * ratio))
29
+ resized_img = pil_image.resize(new_size)
30
+ else:
31
+ resized_img = pil_image.copy() # 小图不放大
32
+
33
+ # 2. 编码为Base64
34
+ buffered = BytesIO()
35
+ resized_img.save(buffered, format="JPEG", quality=quality)
36
+ return base64.b64encode(buffered.getvalue()).decode('utf-8')
37
+
38
+ except Exception as e:
39
+ print(f"Image processing failed: {str(e)}")
40
+ return None
41
+
42
+
43
+ system_prompt = """You are an expert Vision-Language Model assistant specializing in transforming explicit questions about images into sophisticated, implicit instructions. Your goal is to reframe direct queries into nuanced directives that guide a user or another AI to the same answer through observation and inference, rather than by a direct ask.\n\nYour Task:\nGiven an image and a corresponding explicit question-answer (QA) pair, your task is to:\n1. Convert the explicit question into a compelling implicit instruction that leverages the image content and guides the user to deduce the information sought by the original question.\n2. Classify the ​​reframed question​​ into one of five analytical dimensions (Spatial Relationships, Visual Attributes, Functional Context, Logical Reasoning, Semantic Connections) based on its core intent.\n\nMethodology: Reconstructing the Query Using Analytical Dimensions\nTo create the implicit instruction, you will:\n1. Actively draw insights from the provided image.\n2. Utilize one or more of the five analytical dimensions to adjust the original question, add contextual information, and reconstruct its intent into an implicit directive.\n3. Assign a ​​classification​​ to the reframed question based on which dimension(s) it primarily engages (e.g., questions about object positioning vs. symbolic meaning).\n4. The implicit instruction should make the answer feel like a natural consequence of observation or analysis tied to the classification.\n\n1. Spatial Relationships – Used to add spatial descriptive attributes\n1.1. Positional Arrangements\nOriginal Question Type (Location/Identification): What is the fountain in the middle of the photo like?\nImplicit Instruction (using positional arrangement description): Among the series of landscape structures in the square, please describe in detail the facility that is neither located at the very edge nor immediately adjacent to buildings, but is roughly in the geometric center area and jets water upwards.\n1.2. Directional Orientation\nOriginal Question Type (Location/Identification): Find the person in the picture who has their back to us.\nImplicit Instruction (using directional orientation description): Among the multiple individuals in the scene, please identify and describe the person whose main body part (especially the face) is oriented roughly opposite to the observer's line of sight.\n\n2. Visual Attributes – Used to add visual descriptive attributes\n2.1. Color\nOriginal Question Type (Location/Identification): How is the green apple in the basket? (Assuming there are red and green apples)\nImplicit Instruction (using color attribute description): In the container holding various fruits, please focus on the spherical fruits whose skin presents a hue similar to leaves or unripe bananas, and describe the condition of one of them.\n2.2. Shape\nOriginal Question Type (Location/Identification): Find the square cushion.\nImplicit Instruction (using shape attribute description): Among the multiple cushions on the sofa, please point out the fabric item used for comfortable leaning that has an outer contour with four roughly equal sides and internal angles close to right angles.\n2.3. Size\nOriginal Question Type (Location/Identification): What is the tallest book in that pile of books?\nImplicit Instruction (using size attribute description): Among the several books stacked together, please identify the printed material that significantly surpasses all other books in the vertical dimension, and describe its cover.\n2.4. Material Properties\nOriginal Question Type (Location/Identification): Which sculpture is made of stone?\nImplicit Instruction (using material attribute description): Among the multiple displayed artistic forms, please identify the work whose surface presents natural rock texture, feels cold to the touch, has a hard texture, and typically possesses a certain sense of weight.\n\n3. Functional Context – Used to add functional or behavioral descriptive attributes\n3.1. Item Purposes\nOriginal Question Type (Location/Identification): Find the knife used for cutting vegetables in the kitchen.\nImplicit Instruction (using item purpose description): Among the various utensils in the kitchen, please identify the hand-held metal tool that typically has a sharp single or double-edged blade and is designed for segmenting or slicing food ingredients.\n3.2. Human Actions\nOriginal Question Type (Location/Identification): Which waiter is wiping the table?\nImplicit Instruction (using human action description): Among the staff in the restaurant, please locate the employee who is currently holding a cloth or cleaning supplies and whose upper body and arms are repeatedly performing a wiping motion on a flat surface.\n3.3. Environmental Conditions\nOriginal Question Type (Location/Identification): Point out the rabbit in the snow. (Assuming a complex background where the environment helps)\nImplicit Instruction (using environmental condition description): On the vast ground covered with white ice crystals, carefully search for and point out the small mammal that contrasts with the surrounding snowy white environment, which it might be using for camouflage.\n\n4. Logical Reasoning – Used to add logical judgment attributes\n4.1. Quantity Comparisons\nOriginal Question Type (Location/Identification): Which team has the fewest people? (Assuming there are three teams)\nImplicit Instruction (using quantity comparison description for differentiation/location): Separately count the members of each clearly distinguishable group of people in the frame, and then indicate the group whose total number of members is at the lowest level in comparison.\n4.2. Conditional Evaluations\nOriginal Question Type (Location/Identification): If I were going out, which umbrella should I take? (Assuming one good umbrella and one broken one)\nImplicit Instruction (using conditional evaluation description): Examine all available rain gear in the image, assess their respective conditions and functionality, and select the umbrella that would provide reliable shelter in the event of wet weather and currently has no obvious damage.\n4.3. Causal Relationships\nOriginal Question Type (Location/Identification): Which child knocked over the milk? (Assuming spilled milk and several children nearby)\nImplicit Instruction (using causal relationship description): Observe the location where the milky white liquid was spilled and the position of its overturned container, and considering the positions of nearby children, their expressions, or any traces on their hands, infer the child most likely responsible for this accident.\n\n5. Semantic Connections – Used to add symbolic or emotional descriptive attributes\n5.1. Cultural Metaphors\nOriginal Question Type (Location/Identification): Find the traditional Chinese painting that features bamboo.\nImplicit Instruction (using cultural metaphor description): Among several traditional Eastern paintings, please identify the artwork whose main subject is a plant characterized by its hollow, segmented stems, often used in a specific culture as a symbol of gentlemanly qualities (such as integrity, modesty, and resilience).\n5.2. Emotional Expressions\nOriginal Question Type (Location/Identification): Which member of the audience looks the most disappointed?\nImplicit Instruction (using emotional expression description): Among the people watching the game or performance, find the individual whose facial expression (e.g., downturned mouth, dull eyes, furrowed brow) and body posture most clearly convey negative emotions, such as unmet expectations or dissatisfaction.\n5.3. Symbolic Meanings\nOriginal Question Type (Location/Identification): Identify the decoration on top of the wedding cake.\nImplicit Instruction (using symbolic meaning description): Observe the multi-tiered celebration cake and locate the small decorative object placed at its very top, which typically carries auspicious meanings (such as figures of the newlyweds, symbols of love, a shared future, etc.) and serves as a finishing touch for the overall ceremony.\n\nInput Format You Will Receive:\n​​Image​​ (required)\n​​Original Question​​ (required)\n​​Answer/Context​​ (optional): May include partial or no contextual constraints\n\nOutput Format You Should Generate:\nParse the reframed ​​question​​ and its ​​classification​​ into JSON format. Follow this structure:\n\n{\n \"question\": \"[Implicit instruction guiding observation/inference]\",\n \"classification\": \"[One of: Spatial Relationships, Visual Attributes, Functional Context, Logical Reasoning, Semantic Connections]\"\n}\n​​Example Output:​​\n{\n \"question\": \"Observe the multi-tiered celebration cake and locate the small decorative object placed at its very top, which typically carries auspicious meanings (such as figures of the newlyweds, symbols of love, a shared future, etc.) and serves as a finishing touch for the overall ceremony.\",\n \"classification\": \"Semantic Connections\"\n}"""
44
+
45
+ base_template = {
46
+ "custom_id": None,
47
+ "method": "POST",
48
+ "url": "/v1/chat/completions",
49
+ "body": {
50
+ "model": "qwen-vl-max-latest",
51
+ "messages": [
52
+ {"role": "system", "content": system_prompt},
53
+ {"role": "user", "content": None}
54
+ ],
55
+ "temperature": 0.1,
56
+ "response_format":{"type": "json_object"},
57
+ "max_tokens": 200
58
+ }
59
+ }
60
+
61
+ def process_single_file(json_file, dataset_dir): # 新增文件处理函数
62
+ print(f"\nProcessing JSON file: {json_file}") # 添加开始处理日志
63
+ json_path = os.path.join(dataset_dir, json_file)
64
+ file_requests = []
65
+
66
+ try:
67
+ with open(json_path, 'r', encoding='utf-8') as f:
68
+ data = json.load(f)
69
+ except (IOError, json.JSONDecodeError) as e:
70
+ print(f"Failed to load {json_path}: {str(e)}")
71
+ return file_requests
72
+
73
+ if not isinstance(data, list):
74
+ print(f"Invalid data format in {json_file}, expected list")
75
+ return file_requests
76
+
77
+ for item_idx, item in enumerate(data):
78
+ try:
79
+ if not isinstance(item, dict):
80
+ print(f"Invalid item format at index {item_idx} in {json_file}")
81
+ continue
82
+
83
+ img_path = os.path.join(dataset_dir, item['media_paths'])
84
+ img_path = os.path.normpath(img_path)
85
+ if not os.path.exists(img_path):
86
+ print(f"Image file not found: {img_path}")
87
+ continue
88
+
89
+ try:
90
+ image = Image.open(img_path)
91
+ base64_img = encode_image_from_pil(image)
92
+ if not base64_img:
93
+ continue
94
+ except (IOError, OSError) as e:
95
+ print(f"Failed to process image {img_path}: {str(e)}")
96
+ continue
97
+
98
+ questions = item.get('question', [])
99
+ answers = item.get('answer', [])
100
+ options = item.get('options', [])
101
+
102
+ if not isinstance(questions, list) or len(questions) == 0:
103
+ print(f"Invalid questions in item {item_idx} of {json_file}")
104
+ continue
105
+
106
+ option_map = {}
107
+ if options and isinstance(options, list):
108
+ try:
109
+ option_map = {opt['id']: opt['text'] for opt in options
110
+ if 'id' in opt and 'text' in opt}
111
+ except KeyError as e:
112
+ print(f"Invalid option format in {json_file} item {item_idx}: {str(e)}")
113
+
114
+ for qa_idx in range(len(questions)):
115
+ try:
116
+ q_text = questions[qa_idx]
117
+ if not isinstance(q_text, str):
118
+ print(f"Invalid question format at index {qa_idx} in {json_file} item {item_idx}")
119
+ continue
120
+
121
+ answer_text = ""
122
+ if qa_idx < len(answers):
123
+ original_answer = answers[qa_idx]
124
+ try:
125
+ if len(option_map) >= 1:
126
+ answer_text = option_map.get(original_answer[0], original_answer[0])
127
+ else:
128
+ answer_text = original_answer if isinstance(original_answer, str) else ""
129
+ answer_text= re.sub(r'\s*\[.*?\]', '', answer_text).strip()
130
+ except (TypeError, IndexError) as e:
131
+ print(f"Answer processing error: {str(e)}")
132
+
133
+ user_content = []
134
+ text_content = f"question: {q_text} answer: {answer_text}" if answer_text else f"question: {q_text}"
135
+
136
+ user_content.append({"type": "text", "text": text_content})
137
+ user_content.append({
138
+ "type": "image_url",
139
+ "image_url": {"url": f"data:image/jpeg;base64,{base64_img}"}
140
+ })
141
+
142
+ request = copy.deepcopy(base_template)
143
+ request['custom_id'] = f"{json_file[:-5]}-{item_idx}-{qa_idx}"
144
+ request['body']['messages'][1]['content'] = user_content
145
+
146
+ file_requests.append(request)
147
+ except Exception as e:
148
+ print(f"Error processing QA pair {qa_idx} in {json_file} item {item_idx}: {str(e)}")
149
+ traceback.print_exc()
150
+
151
+ except Exception as e:
152
+ print(f"Error processing item {item_idx} in {json_file}: {str(e)}")
153
+ continue
154
+
155
+ return file_requests
156
+
157
+ def process_dataset(dataset_dir):
158
+ batch_requests = []
159
+
160
+ if not os.path.exists(dataset_dir):
161
+ print(f"Error: Dataset directory {dataset_dir} does not exist")
162
+ return batch_requests
163
+
164
+ json_files = [f for f in os.listdir(dataset_dir) if f.endswith('.json')]
165
+
166
+ # 使用线程池并行处理文件
167
+ with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
168
+ futures = {executor.submit(process_single_file, json_file, dataset_dir): json_file
169
+ for json_file in json_files}
170
+
171
+ for future in concurrent.futures.as_completed(futures):
172
+ json_file = futures[future]
173
+ try:
174
+ requests = future.result()
175
+ batch_requests.extend(requests)
176
+ except Exception as e:
177
+ print(f"Error processing file {json_file}: {str(e)}")
178
+ traceback.print_exc()
179
+
180
+ return batch_requests
181
+
182
+ if __name__ == "__main__":
183
+ dataset_directory = "/mnt/data/users/zys/proj/vlm_reasoning/dataset"
184
+ try:
185
+ batch_requests = process_dataset(dataset_directory)
186
+
187
+ with open("/mnt/data/users/zys/proj/vlm_reasoning/request/vlm_batch_requests.jsonl", 'w') as f:
188
+ for req in batch_requests:
189
+ try:
190
+ f.write(json.dumps(req, ensure_ascii=False) + '\n')
191
+ except (TypeError, IOError) as e:
192
+ print(f"Failed to write request: {str(e)}")
193
+ continue
194
+
195
+ print(f"Successfully generated {len(batch_requests)} requests")
196
+ except Exception as e:
197
+ print(f"Critical error occurred: {str(e)}")
198
+ traceback.print_exc()
utils/upload/request_create.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import sys
3
+ import random
4
+ from collections import defaultdict
5
+
6
+ def collect_dataset_info(file_path):
7
+ """收集数据集信息,包括每个数据集的行号列表和首次出现顺序"""
8
+ dataset_lines = defaultdict(list)
9
+ order = []
10
+ seen = set()
11
+
12
+ with open(file_path, 'r') as f:
13
+ for line_num, line in enumerate(f, 1):
14
+ try:
15
+ data = json.loads(line.strip())
16
+ custom_id = data['custom_id']
17
+ dataset = custom_id.split('-')[0]
18
+
19
+ if dataset not in seen:
20
+ order.append(dataset)
21
+ seen.add(dataset)
22
+
23
+ dataset_lines[dataset].append(line_num)
24
+ except json.JSONDecodeError:
25
+ print(f"Error: Invalid JSON at line {line_num}", file=sys.stderr)
26
+ except KeyError:
27
+ print(f"Error: Missing 'custom_id' at line {line_num}", file=sys.stderr)
28
+ except IndexError:
29
+ print(f"Error: Invalid custom_id format at line {line_num}", file=sys.stderr)
30
+
31
+ return dataset_lines, order
32
+
33
+ def main():
34
+ if len(sys.argv) != 4:
35
+ print("Usage: python sample_datasets.py <input.jsonl> <output.jsonl> <N>")
36
+ sys.exit(1)
37
+
38
+ input_file = sys.argv[1]
39
+ output_file = sys.argv[2]
40
+ try:
41
+ N = int(sys.argv[3])
42
+ except ValueError:
43
+ print("Error: N must be an integer.")
44
+ sys.exit(1)
45
+
46
+ # 收集数据集信息
47
+ dataset_info, dataset_order = collect_dataset_info(input_file)
48
+ k = len(dataset_info)
49
+
50
+ if k == 0:
51
+ print("Error: No datasets found in the input file.")
52
+ sys.exit(1)
53
+
54
+ # 检查每个数据集是否有至少5个样本
55
+ for dataset, lines in dataset_info.items():
56
+ if len(lines) < 5:
57
+ print(f"Error: Dataset '{dataset}' has fewer than 5 samples.")
58
+ sys.exit(1)
59
+
60
+ total_samples = sum(len(lines) for lines in dataset_info.values())
61
+ min_samples = 5 * k
62
+
63
+ if N < min_samples or N > total_samples:
64
+ print(f"Error: N must be between {min_samples} and {total_samples}.")
65
+ sys.exit(1)
66
+
67
+ # 计算可用样本数和剩余需要分配的样本数
68
+ available = {dataset: len(lines) - 5 for dataset, lines in dataset_info.items()}
69
+ total_available = sum(available.values())
70
+ R = N - 5 * k
71
+
72
+ if R > total_available:
73
+ print(f"Error: Cannot allocate {R} samples from available {total_available}.")
74
+ sys.exit(1)
75
+
76
+ # 计算每个数据集分配的剩余样本数
77
+ allocations = []
78
+ sum_avail = total_available if total_available != 0 else 1 # 避免除以零
79
+
80
+ for dataset in dataset_order:
81
+ avail = available[dataset]
82
+ alloc_float = R * avail / sum_avail
83
+ allocations.append(alloc_float)
84
+
85
+ integer_part = [int(alloc) for alloc in allocations]
86
+ remainders = [alloc - int_part for alloc, int_part in zip(allocations, integer_part)]
87
+ remainder_total = R - sum(integer_part)
88
+
89
+ # 分配余数
90
+ remainder_indices = sorted(enumerate(remainders), key=lambda x: (-x[1], x[0]))
91
+ for i in range(remainder_total):
92
+ idx = remainder_indices[i][0]
93
+ integer_part[idx] += 1
94
+
95
+ # 计算每个数据集的最终采样数
96
+ sample_counts = {}
97
+ for i, dataset in enumerate(dataset_order):
98
+ alloc = integer_part[i]
99
+ if alloc > available[dataset]:
100
+ print(f"Error: Allocation for dataset '{dataset}' exceeds available samples.")
101
+ sys.exit(1)
102
+ sample_counts[dataset] = 5 + alloc
103
+
104
+ # 打印采样分布信息(新增部分)
105
+ print("\nSampling Distribution:")
106
+ total_sampled = 0
107
+ for dataset in dataset_order:
108
+ count = sample_counts[dataset]
109
+ total_sampled += count
110
+ print(f" - {dataset}: {count} samples")
111
+ print(f"Total samples: {total_sampled} (target: {N})")
112
+
113
+ # 验证总数正确性
114
+ if total_sampled != N:
115
+ print(f"Error: Total sampled count mismatch ({total_sampled} vs {N})")
116
+ sys.exit(1)
117
+
118
+ # 随机选择行号
119
+ selected_lines = []
120
+ for dataset in dataset_order:
121
+ lines = dataset_info[dataset]
122
+ count = sample_counts[dataset]
123
+ selected = random.sample(lines, count)
124
+ selected_lines.extend(selected)
125
+
126
+ selected_lines.sort()
127
+
128
+ # 写入输出文件
129
+ current_idx = 0
130
+ total_selected = len(selected_lines)
131
+
132
+ with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
133
+ for line_num, line in enumerate(infile, 1):
134
+ if current_idx >= total_selected:
135
+ break
136
+ if line_num == selected_lines[current_idx]:
137
+ outfile.write(line)
138
+ current_idx += 1
139
+
140
+ print(f"\nSuccessfully sampled {N} records to {output_file}.")
141
+
142
+ if __name__ == "__main__":
143
+ main()
utils/upload_test/batch_create.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+
4
+ client = OpenAI(
5
+ # 若没有配置环境变量,可用阿里云百炼API Key将下行替换为:api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
6
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
7
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 阿里云百炼服务的base_url
8
+ )
9
+
10
+ batch = client.batches.create(
11
+ input_file_id="oss:cn-beijing:acm-mm-reason/test/vlm_test100.jsonl", # 上传文件返回的id或OSS文件URL或OSS文件资源标识符
12
+ endpoint="/v1/chat/completions", # Embedding文本向量模型填写/v1/embeddings,测试模型batch-test-model填写/v1/chat/ds-test,其他模型填写/v1/chat/completions
13
+ completion_window="10h",
14
+ metadata={'ds_name':"vlm_test100",'ds_description':'finally test'}
15
+ )
16
+ print(batch)
utils/upload_test/batch_download.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+
4
+ client = OpenAI(
5
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
6
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
7
+ )
8
+ content = client.files.content(file_id="file-batch_output-kJ3eejYgBU2JI3m9D3nTs6Jp")
9
+ # 打印结果文件内容
10
+ print(content.text)
11
+ # 保存结果文件至本地
12
+ content.write_to_file("finderror.jsonl")
utils/upload_test/batch_search.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+
4
+ client = OpenAI(
5
+ # 若没有配置环境变量,可用阿里云百炼API Key将下行替换为:api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
6
+ api_key=os.getenv("DASHSCOPE_API_KEY"),
7
+ base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 阿里云百炼服务的base_url
8
+ )
9
+ batch = client.batches.retrieve("batch_d76f011f-e7bb-4686-bc6f-de7a202fa9fa") # 将batch_id替换为Batch任务的id
10
+ print(batch)
utils/upload_test/finderror.jsonl ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id":"f112028f-6663-9701-b2d8-6b43ed717e99","custom_id":"Lisa-242-3","response":{"status_code":500,"request_id":"f112028f-6663-9701-b2d8-6b43ed717e99","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
2
+ {"id":"8ffae164-6447-94f4-af7a-f161ad4248ad","custom_id":"Lisa-295-0","response":{"status_code":500,"request_id":"8ffae164-6447-94f4-af7a-f161ad4248ad","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
3
+ {"id":"c455f77b-4285-9d67-8eb4-1913df6c2ccb","custom_id":"Lisa-610-5","response":{"status_code":500,"request_id":"c455f77b-4285-9d67-8eb4-1913df6c2ccb","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
4
+ {"id":"9431003e-9a74-945f-a175-127355b27d76","custom_id":"Lisa-632-5","response":{"status_code":500,"request_id":"9431003e-9a74-945f-a175-127355b27d76","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
5
+ {"id":"850bb280-8d40-9712-81bc-e4b756c09ebe","custom_id":"Lisa-756-0","response":{"status_code":500,"request_id":"850bb280-8d40-9712-81bc-e4b756c09ebe","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
6
+ {"id":"0b06a5c5-311e-935b-8f6c-ecac974900d3","custom_id":"Lisa-1173-0","response":{"status_code":500,"request_id":"0b06a5c5-311e-935b-8f6c-ecac974900d3","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
7
+ {"id":"0a4a3c74-df68-983f-ad91-0e83ded9e082","custom_id":"EmbSpatial-1612-0","response":{"status_code":500,"request_id":"0a4a3c74-df68-983f-ad91-0e83ded9e082","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
8
+ {"id":"91be2da1-efda-9ffe-98f6-31c4e27a3153","custom_id":"EmbSpatial-7398-0","response":{"status_code":500,"request_id":"91be2da1-efda-9ffe-98f6-31c4e27a3153","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
9
+ {"id":"7075f06d-5123-9aed-9927-172df5b27a00","custom_id":"EmbSpatial-8495-0","response":{"status_code":500,"request_id":"7075f06d-5123-9aed-9927-172df5b27a00","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
10
+ {"id":"6c62ced8-1104-93be-abc3-d61cd230ce56","custom_id":"EmbSpatial-11682-0","response":{"status_code":500,"request_id":"6c62ced8-1104-93be-abc3-d61cd230ce56","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
11
+ {"id":"6b2bf3e4-6abf-9722-ae22-bb681a522290","custom_id":"EmbSpatial-12955-0","response":{"status_code":500,"request_id":"6b2bf3e4-6abf-9722-ae22-bb681a522290","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
12
+ {"id":"2fcd6b4c-cfab-9214-92e2-ffc94a0ddee7","custom_id":"EmbSpatial-15924-0","response":{"status_code":500,"request_id":"2fcd6b4c-cfab-9214-92e2-ffc94a0ddee7","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
13
+ {"id":"a8ea0e75-4ac4-97fe-a6d3-1c47ed87390c","custom_id":"EmbSpatial-17091-0","response":{"status_code":500,"request_id":"a8ea0e75-4ac4-97fe-a6d3-1c47ed87390c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
14
+ {"id":"2f91dc53-2ec0-9780-8855-dc4c63f2a16c","custom_id":"EmbSpatial-17249-0","response":{"status_code":500,"request_id":"2f91dc53-2ec0-9780-8855-dc4c63f2a16c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
15
+ {"id":"ffe4335a-138c-9a84-a40f-b91c9d58aa65","custom_id":"EmbSpatial-17382-0","response":{"status_code":500,"request_id":"ffe4335a-138c-9a84-a40f-b91c9d58aa65","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
16
+ {"id":"5c753db1-2557-9618-abe3-4e381baa21f4","custom_id":"EmbSpatial-20852-0","response":{"status_code":500,"request_id":"5c753db1-2557-9618-abe3-4e381baa21f4","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
17
+ {"id":"7fb1206e-fd3c-9abb-8bec-89d7d1ca4e3a","custom_id":"EmbSpatial-21270-0","response":{"status_code":500,"request_id":"7fb1206e-fd3c-9abb-8bec-89d7d1ca4e3a","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
18
+ {"id":"1ad37634-ef3d-95fa-823f-3781bc1bb762","custom_id":"EmbSpatial-21954-0","response":{"status_code":500,"request_id":"1ad37634-ef3d-95fa-823f-3781bc1bb762","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
19
+ {"id":"e05b95eb-59a3-9ec7-b059-1c1f9a9e4a96","custom_id":"EmbSpatial-22416-0","response":{"status_code":500,"request_id":"e05b95eb-59a3-9ec7-b059-1c1f9a9e4a96","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
20
+ {"id":"f6b9688f-4204-9fb4-8fd2-86386d78c9d1","custom_id":"EmbSpatial-24631-0","response":{"status_code":500,"request_id":"f6b9688f-4204-9fb4-8fd2-86386d78c9d1","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
21
+ {"id":"f9e7938a-05bf-9760-9f7f-61105c07ffc9","custom_id":"EmbSpatial-26898-0","response":{"status_code":500,"request_id":"f9e7938a-05bf-9760-9f7f-61105c07ffc9","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
22
+ {"id":"b8d7738e-771d-9e41-bb46-9eb99eda7a5c","custom_id":"EmbSpatial-28983-0","response":{"status_code":500,"request_id":"b8d7738e-771d-9e41-bb46-9eb99eda7a5c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
23
+ {"id":"0493d089-e505-9d15-b0bb-49339a3e9b91","custom_id":"MMR-134-0","response":{"status_code":500,"request_id":"0493d089-e505-9d15-b0bb-49339a3e9b91","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
24
+ {"id":"9008de99-fcdb-95b5-9049-5e8852f7be3d","custom_id":"MMR-171-3","response":{"status_code":500,"request_id":"9008de99-fcdb-95b5-9049-5e8852f7be3d","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
25
+ {"id":"7cb673b1-3351-919b-85d5-d52db8ceed7b","custom_id":"MMR-300-0","response":{"status_code":500,"request_id":"7cb673b1-3351-919b-85d5-d52db8ceed7b","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
26
+ {"id":"44af9316-adf4-9de8-9b45-c0b2b83afb89","custom_id":"MMR-305-0","response":{"status_code":500,"request_id":"44af9316-adf4-9de8-9b45-c0b2b83afb89","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
27
+ {"id":"cfa4c691-c810-904a-b4c4-659cd9b62b66","custom_id":"MMR-331-2","response":{"status_code":500,"request_id":"cfa4c691-c810-904a-b4c4-659cd9b62b66","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
28
+ {"id":"0481df84-9d4e-9fa7-b6ac-bbaec8effd99","custom_id":"MMR-725-2","response":{"status_code":500,"request_id":"0481df84-9d4e-9fa7-b6ac-bbaec8effd99","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
29
+ {"id":"e7a559af-9bda-902c-9ae9-6ac8f0613f80","custom_id":"MMR-2275-0","response":{"status_code":500,"request_id":"e7a559af-9bda-902c-9ae9-6ac8f0613f80","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
30
+ {"id":"0e155d01-d5ff-952c-acb3-9570c8d7e807","custom_id":"MMR-2468-1","response":{"status_code":500,"request_id":"0e155d01-d5ff-952c-acb3-9570c8d7e807","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
31
+ {"id":"b61ca940-e684-9b3a-8130-c69843bf77ac","custom_id":"MMR-2935-2","response":{"status_code":500,"request_id":"b61ca940-e684-9b3a-8130-c69843bf77ac","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
32
+ {"id":"8728de73-ddd0-9af0-b009-1d7aaad20139","custom_id":"MMR-3490-2","response":{"status_code":500,"request_id":"8728de73-ddd0-9af0-b009-1d7aaad20139","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
33
+ {"id":"364deec3-8f67-9cd1-91c8-2ab76d5be7d5","custom_id":"MMR-3919-1","response":{"status_code":500,"request_id":"364deec3-8f67-9cd1-91c8-2ab76d5be7d5","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
34
+ {"id":"ed04af32-3d22-93ca-8bbf-c629184d8451","custom_id":"MMR-4067-2","response":{"status_code":500,"request_id":"ed04af32-3d22-93ca-8bbf-c629184d8451","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
35
+ {"id":"c69e6583-90f1-9a47-ac7d-776130e36cf6","custom_id":"MMR-4297-2","response":{"status_code":500,"request_id":"c69e6583-90f1-9a47-ac7d-776130e36cf6","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
36
+ {"id":"1d16d552-70ae-916e-aa15-75936f932364","custom_id":"MMR-5152-2","response":{"status_code":500,"request_id":"1d16d552-70ae-916e-aa15-75936f932364","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
37
+ {"id":"69f9beb7-8769-9c45-904a-150e0a25d1ef","custom_id":"MMR-5319-0","response":{"status_code":500,"request_id":"69f9beb7-8769-9c45-904a-150e0a25d1ef","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
38
+ {"id":"ed27aa37-cb20-9659-9191-7103b4632c03","custom_id":"MMR-6139-3","response":{"status_code":500,"request_id":"ed27aa37-cb20-9659-9191-7103b4632c03","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
39
+ {"id":"759a6605-f409-9f2c-9b75-58f4e36a46e5","custom_id":"MMR-6793-1","response":{"status_code":500,"request_id":"759a6605-f409-9f2c-9b75-58f4e36a46e5","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
40
+ {"id":"0c0692be-2591-98f6-aa34-11fa21e1d7f1","custom_id":"MMR-9581-2","response":{"status_code":500,"request_id":"0c0692be-2591-98f6-aa34-11fa21e1d7f1","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
41
+ {"id":"7edf6a92-fa60-9195-844c-f7f9a4ab6432","custom_id":"MMR-10764-2","response":{"status_code":500,"request_id":"7edf6a92-fa60-9195-844c-f7f9a4ab6432","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
42
+ {"id":"266f796f-e2bb-9925-93d8-ddcbeecd2e14","custom_id":"MMR-10966-1","response":{"status_code":500,"request_id":"266f796f-e2bb-9925-93d8-ddcbeecd2e14","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
43
+ {"id":"5fcc4302-8845-974d-a377-e80a401d7821","custom_id":"MMR-11897-2","response":{"status_code":500,"request_id":"5fcc4302-8845-974d-a377-e80a401d7821","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
44
+ {"id":"df749e88-6d5e-98a2-b6af-b9f90bc99aad","custom_id":"MMR-12199-2","response":{"status_code":500,"request_id":"df749e88-6d5e-98a2-b6af-b9f90bc99aad","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
45
+ {"id":"19d8e5a9-0991-9d18-83d9-3e9e7b3436d0","custom_id":"MMR-12378-1","response":{"status_code":500,"request_id":"19d8e5a9-0991-9d18-83d9-3e9e7b3436d0","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
46
+ {"id":"41baf8eb-cc41-90c4-bff5-cdaeca7d9a9f","custom_id":"MMR-13621-2","response":{"status_code":500,"request_id":"41baf8eb-cc41-90c4-bff5-cdaeca7d9a9f","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
47
+ {"id":"b0326514-4dad-9852-aea2-bb62a49a6cba","custom_id":"MMR-14582-1","response":{"status_code":500,"request_id":"b0326514-4dad-9852-aea2-bb62a49a6cba","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
48
+ {"id":"dae759d3-8319-9d5c-bf76-a7f9637f8149","custom_id":"MMR-14908-1","response":{"status_code":500,"request_id":"dae759d3-8319-9d5c-bf76-a7f9637f8149","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
49
+ {"id":"1b4b87b0-19d9-9720-87ae-8890dca85514","custom_id":"MMR-15221-0","response":{"status_code":500,"request_id":"1b4b87b0-19d9-9720-87ae-8890dca85514","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
50
+ {"id":"8fbda12c-f543-909f-90cd-ab5a43c33402","custom_id":"MMR-16040-2","response":{"status_code":500,"request_id":"8fbda12c-f543-909f-90cd-ab5a43c33402","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
51
+ {"id":"de5b38ae-5205-9692-b40a-50bf0b9f2e56","custom_id":"MMR-18168-0","response":{"status_code":500,"request_id":"de5b38ae-5205-9692-b40a-50bf0b9f2e56","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
52
+ {"id":"a8dc2936-82d9-99d6-b837-8961e5abfeec","custom_id":"MMR-20306-1","response":{"status_code":500,"request_id":"a8dc2936-82d9-99d6-b837-8961e5abfeec","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
53
+ {"id":"0e58d5cf-fdb9-9d19-ac70-aeea67e126fa","custom_id":"MMR-21718-0","response":{"status_code":500,"request_id":"0e58d5cf-fdb9-9d19-ac70-aeea67e126fa","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
54
+ {"id":"b171708f-b8ab-904b-b5c4-1168a3b4d834","custom_id":"MMR-22581-1","response":{"status_code":500,"request_id":"b171708f-b8ab-904b-b5c4-1168a3b4d834","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
55
+ {"id":"99bca0c8-e7b6-93ee-a328-bd190b467738","custom_id":"MMR-23390-1","response":{"status_code":500,"request_id":"99bca0c8-e7b6-93ee-a328-bd190b467738","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
56
+ {"id":"69508cdd-7560-93f1-9bee-4bc8dbc0ef44","custom_id":"MMR-23603-2","response":{"status_code":500,"request_id":"69508cdd-7560-93f1-9bee-4bc8dbc0ef44","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
57
+ {"id":"f475ba96-b312-9452-bb12-166259ea959e","custom_id":"MMR-24700-2","response":{"status_code":500,"request_id":"f475ba96-b312-9452-bb12-166259ea959e","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
58
+ {"id":"afb8e374-850e-9b2a-a899-f49b18c67620","custom_id":"MMR-27112-0","response":{"status_code":500,"request_id":"afb8e374-850e-9b2a-a899-f49b18c67620","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
59
+ {"id":"b65215bd-6b0f-960b-8507-79cf0d6c3923","custom_id":"MMR-27435-2","response":{"status_code":500,"request_id":"b65215bd-6b0f-960b-8507-79cf0d6c3923","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
60
+ {"id":"cd9b192e-621c-952a-9686-63b310528e52","custom_id":"MMR-27983-4","response":{"status_code":500,"request_id":"cd9b192e-621c-952a-9686-63b310528e52","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
61
+ {"id":"cd26cd84-e0c3-973e-8425-a84682693406","custom_id":"MMR-29200-1","response":{"status_code":500,"request_id":"cd26cd84-e0c3-973e-8425-a84682693406","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
62
+ {"id":"c086beee-ac4a-9fc0-87b8-b3ffdf27f2d0","custom_id":"MMR-29279-1","response":{"status_code":500,"request_id":"c086beee-ac4a-9fc0-87b8-b3ffdf27f2d0","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
63
+ {"id":"55bc024f-de7a-949f-8080-2d2b3a9cd28d","custom_id":"MMR-30032-1","response":{"status_code":500,"request_id":"55bc024f-de7a-949f-8080-2d2b3a9cd28d","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
64
+ {"id":"9fe5df9b-5be5-9a55-a47a-b0fcfad916c6","custom_id":"MMR-32992-2","response":{"status_code":500,"request_id":"9fe5df9b-5be5-9a55-a47a-b0fcfad916c6","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
65
+ {"id":"310d5d06-8e2f-936c-addc-d2e7793a9ff3","custom_id":"MMR-33740-0","response":{"status_code":500,"request_id":"310d5d06-8e2f-936c-addc-d2e7793a9ff3","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
66
+ {"id":"2ef5cfbe-a8c3-90e9-b606-3ef40b006f86","custom_id":"MMR-33998-3","response":{"status_code":500,"request_id":"2ef5cfbe-a8c3-90e9-b606-3ef40b006f86","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
67
+ {"id":"43aba159-dc9a-96a3-8939-cc0147d7985c","custom_id":"MMR-34992-2","response":{"status_code":500,"request_id":"43aba159-dc9a-96a3-8939-cc0147d7985c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
68
+ {"id":"5b7c27ab-6322-93cc-8bea-34e0cdcec92e","custom_id":"MMR-37312-1","response":{"status_code":500,"request_id":"5b7c27ab-6322-93cc-8bea-34e0cdcec92e","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
69
+ {"id":"89eea748-43c8-9a28-aa7a-140dfd40b2d8","custom_id":"MMR-37405-0","response":{"status_code":500,"request_id":"89eea748-43c8-9a28-aa7a-140dfd40b2d8","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
70
+ {"id":"6f08f754-005d-9773-8d61-cd22a5be7535","custom_id":"MMR-37799-1","response":{"status_code":500,"request_id":"6f08f754-005d-9773-8d61-cd22a5be7535","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
71
+ {"id":"0e4cfcb7-ead6-9c03-8f1a-81546cbcff69","custom_id":"MMR-38631-1","response":{"status_code":500,"request_id":"0e4cfcb7-ead6-9c03-8f1a-81546cbcff69","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
72
+ {"id":"7350938b-9728-944b-9081-7fe40444b1ff","custom_id":"MMR-39225-0","response":{"status_code":500,"request_id":"7350938b-9728-944b-9081-7fe40444b1ff","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
73
+ {"id":"c2205a9a-6908-98a9-ad0b-ab2530fb3018","custom_id":"MMR-39575-1","response":{"status_code":500,"request_id":"c2205a9a-6908-98a9-ad0b-ab2530fb3018","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
74
+ {"id":"97f6fd35-9361-9395-a59c-b81899d5fe17","custom_id":"MMR-39635-0","response":{"status_code":500,"request_id":"97f6fd35-9361-9395-a59c-b81899d5fe17","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
75
+ {"id":"fb8709ec-c35f-97e0-96f3-93187c9bb24d","custom_id":"MMR-41248-2","response":{"status_code":500,"request_id":"fb8709ec-c35f-97e0-96f3-93187c9bb24d","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
76
+ {"id":"3e40b136-f3a5-92ce-9b1e-61a786324e7f","custom_id":"MMR-41383-1","response":{"status_code":500,"request_id":"3e40b136-f3a5-92ce-9b1e-61a786324e7f","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
77
+ {"id":"75fcfb45-cf9f-9208-b296-e3a66c7dfddc","custom_id":"MMR-41541-1","response":{"status_code":500,"request_id":"75fcfb45-cf9f-9208-b296-e3a66c7dfddc","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
78
+ {"id":"4fb406a4-6489-92a0-8fee-23e39ec6374a","custom_id":"MMR-42068-1","response":{"status_code":500,"request_id":"4fb406a4-6489-92a0-8fee-23e39ec6374a","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
79
+ {"id":"04b3516c-233c-9da0-aba4-76ef1c9730ec","custom_id":"MMR-43502-1","response":{"status_code":500,"request_id":"04b3516c-233c-9da0-aba4-76ef1c9730ec","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
80
+ {"id":"7e65fa6d-af65-9d65-b29c-eb14ed0d3c04","custom_id":"MMR-43504-2","response":{"status_code":500,"request_id":"7e65fa6d-af65-9d65-b29c-eb14ed0d3c04","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
81
+ {"id":"65c04f66-673c-95f7-ae40-55095b8a649d","custom_id":"MMR-43758-0","response":{"status_code":500,"request_id":"65c04f66-673c-95f7-ae40-55095b8a649d","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
82
+ {"id":"4c2353ce-8ffd-9050-bcd4-7227e1b9c88c","custom_id":"MMR-43992-3","response":{"status_code":500,"request_id":"4c2353ce-8ffd-9050-bcd4-7227e1b9c88c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
83
+ {"id":"bffd08ff-1d4b-9534-a060-f4e3b284f561","custom_id":"MMR-44141-0","response":{"status_code":500,"request_id":"bffd08ff-1d4b-9534-a060-f4e3b284f561","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
84
+ {"id":"e9e4361d-a1fe-9a02-b737-0c0d9be54a88","custom_id":"MMR-45554-1","response":{"status_code":500,"request_id":"e9e4361d-a1fe-9a02-b737-0c0d9be54a88","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
85
+ {"id":"5f5abc51-0bfb-9535-86e1-24e62ccf7b14","custom_id":"MMR-48256-0","response":{"status_code":500,"request_id":"5f5abc51-0bfb-9535-86e1-24e62ccf7b14","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
86
+ {"id":"aa572146-1c97-97bc-a6a5-b014ddb3944e","custom_id":"MMR-48592-1","response":{"status_code":500,"request_id":"aa572146-1c97-97bc-a6a5-b014ddb3944e","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
87
+ {"id":"987ea0a7-80e3-9480-b35f-b8a2274c3f0b","custom_id":"MMR-49126-2","response":{"status_code":500,"request_id":"987ea0a7-80e3-9480-b35f-b8a2274c3f0b","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
88
+ {"id":"ae4aed04-b35d-93de-9749-03072a4fc5fb","custom_id":"MMR-49269-0","response":{"status_code":500,"request_id":"ae4aed04-b35d-93de-9749-03072a4fc5fb","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
89
+ {"id":"f30dcb02-9cac-9aaf-b614-f2cbd4f36287","custom_id":"MMR-49653-1","response":{"status_code":500,"request_id":"f30dcb02-9cac-9aaf-b614-f2cbd4f36287","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
90
+ {"id":"481cf69b-81cf-921d-906d-36a443cdad2f","custom_id":"MMR-50614-1","response":{"status_code":500,"request_id":"481cf69b-81cf-921d-906d-36a443cdad2f","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
91
+ {"id":"d87f29c5-ae32-9b98-a498-e2c5ce218c81","custom_id":"MMR-52000-2","response":{"status_code":500,"request_id":"d87f29c5-ae32-9b98-a498-e2c5ce218c81","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
92
+ {"id":"9d2bec62-161a-9baf-9110-8ce8bc1aac2c","custom_id":"MMR-52537-0","response":{"status_code":500,"request_id":"9d2bec62-161a-9baf-9110-8ce8bc1aac2c","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
93
+ {"id":"56ccda42-f2ba-9415-afce-a149cf945fd3","custom_id":"MMR-54912-1","response":{"status_code":500,"request_id":"56ccda42-f2ba-9415-afce-a149cf945fd3","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}
94
+ {"id":"b693bc74-18f8-9858-bba3-3f5e153bacce","custom_id":"MMR-57095-2","response":{"status_code":500,"request_id":"b693bc74-18f8-9858-bba3-3f5e153bacce","body":{"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":"internal_error"}}},"error":{"code":"internal_error","param":null,"message":"An internal error has occured, please try again later or contact service support.","type":null}}