ai-train_platform/util/yolo2pix.py

194 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import cv2
import re
import zipfile
import shutil
import numpy as np
# ---------------------- 工具函数 ----------------------
def coord_to_num(x, y, cols, cell_w, cell_h):
"""根据像素坐标反算格子编号从1开始"""
c = int(x // cell_w)
r = int(y // cell_h)
return r * cols + c + 1
def num_to_coord(num, cols, cell_w, cell_h):
"""网格编号转像素坐标"""
n = num - 1
r, c = divmod(n, cols)
x1, y1 = c * cell_w, r * cell_h
x2, y2 = x1 + cell_w, y1 + cell_h
return x1, y1, x2, y2
def convex_hull_poly(points):
"""计算点集凸包"""
pts = np.array(points)
hull = cv2.convexHull(pts)
return hull.reshape(-1, 2).tolist()
def draw_grid_on_image(image_path, grid_cells, cell_size=(108,102), save_path=None):
"""在图像上绘制网格编号和彩色框"""
image = cv2.imread(image_path)
if image is None:
print(f"❌ 无法读取图片: {image_path}")
return
h, w = image.shape[:2]
cell_w, cell_h = cell_size
cols = w // cell_w
overlay = image.copy()
for cname, nums in grid_cells.items():
color = (np.random.randint(64,255), np.random.randint(64,255), np.random.randint(64,255))
for num in nums:
x1, y1, x2, y2 = num_to_coord(num, cols, cell_w, cell_h)
cv2.rectangle(overlay, (x1,y1), (x2,y2), color, -1)
cv2.addWeighted(overlay, 0.4, image, 0.6, 0, image)
# 绘制网格线
for i in range(0, w, cell_w):
cv2.line(image, (i,0), (i,h), (100,100,100), 1)
for j in range(0, h, cell_h):
cv2.line(image, (0,j), (w,j), (100,100,100), 1)
if save_path:
cv2.imwrite(save_path, image)
return image
# ---------------------- YOLO-Seg → 网格编号 ----------------------
def yoloseg_to_grid_cells_fixed_v5(image_path, label_file, cell_size=(108,102), class_names=None):
import numpy as np
import cv2
img = cv2.imread(image_path)
if img is None:
raise ValueError(f"无法读取图片 {image_path}")
h, w = img.shape[:2]
cell_w, cell_h = cell_size
cols = max(1, w // cell_w)
class_cells = {}
result_lines = []
with open(label_file, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split()
if len(parts) < 5:
continue
cls_id = int(parts[0])
coords = [float(x) for x in parts[1:]]
if len(coords) % 2 != 0:
coords = coords[:-1]
if len(coords) < 6:
continue
poly = np.array(coords, dtype=np.float32).reshape(-1, 2)
poly[:, 0] *= w
poly[:, 1] *= h
x_min, y_min = poly.min(axis=0)
x_max, y_max = poly.max(axis=0)
col_start = int(x_min // cell_w)
col_end = int(x_max // cell_w)
row_start = int(y_min // cell_h)
row_end = int(y_max // cell_h)
# 直接生成网格编号
cells = [r*cols + c + 1
for r in range(row_start, row_end+1)
for c in range(col_start, col_end+1)]
if class_names and isinstance(class_names, list):
cname = class_names[cls_id] if cls_id < len(class_names) else str(cls_id)
else:
cname = str(cls_id)
if cname not in class_cells:
class_cells[cname] = set()
class_cells[cname].update(cells)
# 输出 _grid.txt
for cname, nums in class_cells.items():
sorted_nums = sorted(nums)
ids_str = '-'.join(map(str, sorted_nums)) + '-'
result_lines.append(f"{cname} {ids_str}")
return '\n'.join(result_lines), class_cells
# ---------------------- 主函数 ----------------------
def process_zip_yoloseg_with_draw(zip_path, output_dir, cell_size=(108,102), class_names=None, make_zip=True):
"""
处理 YOLO-Seg 数据集压缩包 → 网格编号 + 可视化
支持标签在不同子文件夹
"""
if not os.path.exists(zip_path):
raise FileNotFoundError(f"{zip_path} 不存在")
os.makedirs(output_dir, exist_ok=True)
# 解压
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(output_dir)
# 枚举所有图片
for root, _, files in os.walk(output_dir):
for file in files:
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp')):
image_path = os.path.join(root, file)
base_name = os.path.splitext(file)[0]
# 在所有子目录中寻找对应标签
label_file = None
for subroot, _, subfiles in os.walk(output_dir):
for sf in subfiles:
if sf == base_name + ".txt":
label_file = os.path.join(subroot, sf)
break
if label_file:
break
if not label_file:
print(f"⚠️ 找不到标签文件: {base_name}.txt")
continue
try:
output_str, class_cells = yoloseg_to_grid_cells_fixed_v5(
image_path, label_file, cell_size=cell_size, class_names=class_names
)
except Exception as e:
print(f"❌ 转换失败 {file}: {e}")
continue
# 写入 _grid.txt
out_txt_file = os.path.join(root, base_name + "_grid.txt")
with open(out_txt_file, 'w', encoding='utf-8') as f:
f.write(output_str)
# 绘制结果图
out_img_file = os.path.join(root, base_name + "_grid.jpg")
draw_grid_on_image(image_path, class_cells, cell_size=cell_size, save_path=out_img_file)
# 打包结果
if make_zip:
zip_out_path = os.path.splitext(output_dir.rstrip("/\\"))[0] + "_processed.zip"
shutil.make_archive(os.path.splitext(zip_out_path)[0], 'zip', output_dir)
print(f"✅ 处理完成,已生成压缩包: {zip_out_path}")
return zip_out_path
else:
print(f"✅ 处理完成,输出目录: {output_dir}")
return output_dir
# ---------------------- 示例调用 ----------------------
if __name__ == "__main__":
# zip_path = r"D:\work\develop\LF-where\1760968917215-1760968915394853977_all_zip.zip"
# zip_path = r"C:\Users\14867\Downloads\1761100847201-1761100834344385200_all_zip.zip"
zip_path = r"/test/predictions-20250711-111516-531.zip"
output_dir = "../test/out"
classes = ['裂缝','横向裂缝','纵向裂缝',"修补","坑洞"]
process_zip_yoloseg_with_draw(zip_path, output_dir, class_names=classes)