ai_project_v1/uv_module/draw_numbers_and_boundary.py

174 lines
5.8 KiB
Python
Raw Permalink 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.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