194 lines
6.7 KiB
Python
194 lines
6.7 KiB
Python
|
|
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)
|