194 lines
6.7 KiB
Python
Raw Permalink Normal View History

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)