ai_project_v1/uv_module/draw_numbers_and_boundary.py

174 lines
5.8 KiB
Python
Raw Normal View History

import os.path
import numpy as np
import cv2
# 根据输出结果输出json问价还有边界
def segment_instance_and_contour(seg_map, class_names):
"""
参数
seg_map: HxW 的语义分割图像像素值表示类别编号
class_names: 类别编号到类别名的映射 {0:'Background', 1:'Farmland', 2:'Forest'}
返回
实例列表每个包含id类别名轮廓点
"""
instance_id = 1
results = []
for cls_id in np.unique(seg_map):
if cls_id == 0: # 忽略背景
continue
mask = (seg_map == cls_id).astype(np.uint8)
# 找连通域(不同地块)
num_labels, labels = cv2.connectedComponents(mask)
for i in range(1, num_labels): # 第0是背景
instance_mask = (labels == i).astype(np.uint8)
# 找轮廓
contours, _ = cv2.findContours(instance_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
contour_list = contour.squeeze().tolist()
if len(contour_list) < 3:
continue # 过滤掉太小的碎片
results.append({
'id': instance_id,
'class': class_names.get(cls_id, f"Class{cls_id}"),
'contour': contour_list
})
instance_id += 1
return results
def draw_instances_with_ids(seg_map, results, save_path="instance_visual.png"):
"""
在原始图或灰度图上绘制每个地块的轮廓及其编号
参数
seg_map: 原始图像或分割图HxW HxWx3
results: 上一步输出的地块信息列表
save_path: 保存路径
"""
if len(seg_map.shape) == 2: # 若为灰度图转为BGR
vis_img = cv2.cvtColor(seg_map, cv2.COLOR_GRAY2BGR)
else:
vis_img = seg_map.copy()
# h, w = vis_img.shape[:2]
for item in results:
contour = np.array(item['contour']).reshape(-1, 1, 2).astype(np.int32)
# 随机颜色(根据类别也可指定颜色)
color = tuple(np.random.randint(0, 255, size=3).tolist())
cv2.drawContours(vis_img, [contour], -1, color, 2)#线的宽度
# 计算重心位置以便放编号
M = cv2.moments(contour)
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
cv2.putText(vis_img, str(item['id']), (cx, cy),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2, cv2.LINE_AA)#序号的大小与线宽度
# # 替代 moments 重心方式,使用外接矩形中心
# x, y, w, h = cv2.boundingRect(contour)
# cx = x + w // 2
# cy = y + h // 2
# cv2.putText(vis_img, str(item['id']), (cx, cy),
# cv2.FONT_HERSHEY_SIMPLEX, 3, color, 2, cv2.LINE_AA)
# # 创建该块的 instance mask用于质心计算
# instance_mask = np.zeros((h, w), dtype=np.uint8)
# cv2.drawContours(instance_mask, [contour], -1, 1, -1)
#
# # 计算质心(内部像素点的几何中心)
# ys, xs = np.where(instance_mask > 0)
# if len(xs) > 0 and len(ys) > 0:
# cx = int(np.mean(xs))
# cy = int(np.mean(ys))
# cv2.putText(vis_img, str(item['id']), (cx, cy),
# cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2, cv2.LINE_AA)
cv2.imwrite(save_path, vis_img)
print(f"✅ 可视化图保存至: {save_path}")
def color_map_to_label(seg_img, palette):
"""
将彩色语义图映射为类别ID图
参数
seg_img: HxWx3 彩色图像例如读取的png
palette: 类别调色板 [[0,0,0], [139,69,19], ...]
返回
label_map: HxW 图像每个像素为类别IDint
"""
h, w, _ = seg_img.shape
label_map = np.zeros((h, w), dtype=np.uint8)
# 将调色板转换为字典
color2label = {tuple(color): idx for idx, color in enumerate(palette)}
# 对每个像素映射
for color, label in color2label.items():
mask = np.all(seg_img == color, axis=-1)
label_map[mask] = label
return label_map
# 定义类别调色板和类别名
PALETTE = [
[0, 0, 0], # background
[139, 69, 19], # barren
[0, 255, 0], # forest
[255, 255, 0], # farmland
[0, 0, 255], # water
[128, 128, 128], # road
[0, 255, 255] # building
]
CLASS_NAMES = {
0: 'background',
1: 'barren',
2: 'forest',
3: 'farmland',
4: 'water',
5: 'road',
6: 'building'
}
def draw_json_boundary(pic_url):
# 读取彩色分割图
seg_color = cv2.imread(pic_url) # 注意是 BGR 格式
# seg_color = cv2.imread(r"D:\project\UAV_model\prediction_results\unetformer_UAV_6000X4000\patch_0028.png") # 注意是 BGR 格式
# seg_color = cv2.imread(r"J:\uhr\pytools\numer_coordinate\patch_0047_rgb.png") # 注意是 BGR 格式
seg_color = cv2.cvtColor(seg_color, cv2.COLOR_BGR2RGB)
# 转为类别索引图
label_map = color_map_to_label(seg_color, PALETTE)
# 继续后续处理
results = segment_instance_and_contour(label_map, CLASS_NAMES)
dir_name=os.path.dirname(pic_url)
final_vis_png_name=os.path.basename(pic_url)+"final_vis.png"
final_vis_png_path=os.path.join(dir_name,final_vis_png_name) #输出大本地的只有边缘线条
instance_results_json_name=os.path.basename(pic_url)+"instance_results.json"
instance_results_json_path=os.path.join(dir_name,instance_results_json_name) # 输出到本地的分割的边缘的json文件
draw_instances_with_ids(label_map, results, save_path=final_vis_png_path)
# 保存为 JSON可选
import json
with open(instance_results_json_path, "w") as f:
json.dump(results, f, indent=2)
return final_vis_png_path,instance_results_json_path