528 lines
22 KiB
Python
528 lines
22 KiB
Python
|
|
#
|
|||
|
|
# import json
|
|||
|
|
# import os
|
|||
|
|
# import time
|
|||
|
|
#
|
|||
|
|
# import cv2
|
|||
|
|
# import rasterio
|
|||
|
|
# import numpy as np
|
|||
|
|
# from pyproj import Transformer, CRS
|
|||
|
|
#
|
|||
|
|
#
|
|||
|
|
# def convert_to_wgs84(x, y, src_crs):
|
|||
|
|
# """将投影坐标转换为WGS84经纬度,增加错误处理"""
|
|||
|
|
# try:
|
|||
|
|
# if not (isinstance(x, (int, float)) and isinstance(y, (int, float))):
|
|||
|
|
# print(f"警告: 坐标值无效 (x={x}, y={y})")
|
|||
|
|
# return None, None
|
|||
|
|
#
|
|||
|
|
# transformer = Transformer.from_crs(src_crs, "EPSG:4326", always_xy=True)
|
|||
|
|
# lon, lat = transformer.transform(x, y)
|
|||
|
|
#
|
|||
|
|
# if not np.isfinite(lon) or not np.isfinite(lat):
|
|||
|
|
# print(f"警告: 坐标转换结果无效 (lon={lon}, lat={lat})")
|
|||
|
|
# return None, None
|
|||
|
|
#
|
|||
|
|
# return lon, lat
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"坐标转换异常: {str(e)}")
|
|||
|
|
# return None, None
|
|||
|
|
#
|
|||
|
|
#
|
|||
|
|
# def visualize_pil_segmentation_mask_opencv(mask_path, tif_path, output_path=None, colormap=cv2.COLORMAP_VIRIDIS, save=True):
|
|||
|
|
# """
|
|||
|
|
# 使用OpenCV实现掩码可视化+边界提取(避免Matplotlib后端问题)
|
|||
|
|
# 返回: (src_crs, json_result_path, vis_output_path, raw_json_path)
|
|||
|
|
# """
|
|||
|
|
# # 初始化返回值
|
|||
|
|
# src_crs = None
|
|||
|
|
# json_result_path = None
|
|||
|
|
# vis_output_path = None
|
|||
|
|
# raw_json_path = None
|
|||
|
|
#
|
|||
|
|
# try:
|
|||
|
|
# # 1. 读取并验证掩码文件
|
|||
|
|
# if not os.path.exists(mask_path):
|
|||
|
|
# raise FileNotFoundError(f"掩码文件不存在: {mask_path}")
|
|||
|
|
#
|
|||
|
|
# mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
|
|||
|
|
# if mask is None:
|
|||
|
|
# raise ValueError(f"无法读取掩码文件(可能已损坏): {mask_path}")
|
|||
|
|
#
|
|||
|
|
# # 2. 可视化处理
|
|||
|
|
# mask_vis = cv2.applyColorMap(mask, colormap)
|
|||
|
|
# contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
|
# cv2.drawContours(mask_vis, contours, -1, (0, 0, 255), 1)
|
|||
|
|
#
|
|||
|
|
# # 3. 设置输出路径
|
|||
|
|
# if output_path is None:
|
|||
|
|
# vis_output_path = os.path.splitext(mask_path)[0] + '_vis_cv2.png'
|
|||
|
|
# else:
|
|||
|
|
# vis_output_path = output_path
|
|||
|
|
#
|
|||
|
|
# # 4. 保存可视化结果
|
|||
|
|
# if save:
|
|||
|
|
# if not cv2.imwrite(vis_output_path, mask_vis):
|
|||
|
|
# raise IOError(f"无法保存可视化结果到: {vis_output_path}")
|
|||
|
|
# print(f"可视化结果已保存到: {vis_output_path}")
|
|||
|
|
#
|
|||
|
|
# # 5. 处理轮廓数据
|
|||
|
|
# instances = []
|
|||
|
|
# for i, contour in enumerate(contours):
|
|||
|
|
# if len(contour) < 1:
|
|||
|
|
# print(f"警告: 轮廓 {i} 为空,跳过")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# # 简化轮廓(可选,减少点数)
|
|||
|
|
# epsilon = 0.001 * cv2.arcLength(contour, True)
|
|||
|
|
# approx_contour = cv2.approxPolyDP(contour, epsilon, True)
|
|||
|
|
#
|
|||
|
|
# # 转换为列表格式(JSON 兼容)
|
|||
|
|
# contour_list = []
|
|||
|
|
# for point in approx_contour.squeeze():
|
|||
|
|
# if isinstance(point, np.ndarray) and point.size >= 2:
|
|||
|
|
# contour_list.append(point.tolist())
|
|||
|
|
# elif isinstance(point, (list, tuple)) and len(point) >= 2:
|
|||
|
|
# contour_list.append(list(map(int, point[:2]))) # 确保坐标是整数
|
|||
|
|
# else:
|
|||
|
|
# print(f"警告: 无效的轮廓点格式: {point}")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# if len(contour_list)> 3:
|
|||
|
|
# contour_list.append(contour_list[0]) #多加一个点,构成封闭平面
|
|||
|
|
# instances.append({
|
|||
|
|
# "instance_id": i + 1,
|
|||
|
|
# "contour": contour_list,
|
|||
|
|
# "area": int(cv2.contourArea(contour))
|
|||
|
|
# })
|
|||
|
|
#
|
|||
|
|
# if not instances:
|
|||
|
|
# print("警告: 未检测到任何有效轮廓")
|
|||
|
|
# return None, None, vis_output_path, None
|
|||
|
|
#
|
|||
|
|
# # 6. 保存原始JSON
|
|||
|
|
# raw_json_path = os.path.splitext(mask_path)[0] + '.json'
|
|||
|
|
# try:
|
|||
|
|
# with open(raw_json_path, 'w') as f:
|
|||
|
|
# json.dump(instances, f, indent=2)
|
|||
|
|
# print(f"基础轮廓数据已保存到: {raw_json_path}")
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"警告: 无法保存基础JSON文件 - {str(e)}")
|
|||
|
|
# raw_json_path = None
|
|||
|
|
# # start = time.perf_counter()
|
|||
|
|
# # count=0
|
|||
|
|
# # 7. 处理TIFF坐标转换(仅当提供有效tif_path时)
|
|||
|
|
# if tif_path and isinstance(tif_path, str) and tif_path.lower().endswith(('.tif', '.tiff')):
|
|||
|
|
# try:
|
|||
|
|
# with rasterio.open(tif_path) as src:
|
|||
|
|
# src_crs = src.crs
|
|||
|
|
# if not src_crs:
|
|||
|
|
# print("警告: TIFF文件缺少坐标系信息,跳过坐标转换")
|
|||
|
|
# json_result_path = raw_json_path
|
|||
|
|
# return src_crs, json_result_path, vis_output_path, raw_json_path
|
|||
|
|
#
|
|||
|
|
# transform = src.transform
|
|||
|
|
# band_data = src.read(1)
|
|||
|
|
# height, width = band_data.shape
|
|||
|
|
#
|
|||
|
|
# for instance in instances:
|
|||
|
|
# coord = []
|
|||
|
|
# for point in instance["contour"]:
|
|||
|
|
# try:
|
|||
|
|
# col, row = point[:2] # 提取坐标
|
|||
|
|
# if not (0 <= col < width and 0 <= row < height):
|
|||
|
|
# print(f"警告: 坐标({col}, {row})超出图像范围 ({width}x{height})")
|
|||
|
|
# coord.append(None)
|
|||
|
|
# continue
|
|||
|
|
# # count=count+1
|
|||
|
|
# # 像素坐标 → 投影坐标
|
|||
|
|
# x, y = transform * (col, row)
|
|||
|
|
# lon, lat = convert_to_wgs84(x, y, src_crs)
|
|||
|
|
#
|
|||
|
|
# # 获取像素值
|
|||
|
|
# z = float(band_data[int(row), int(col)])
|
|||
|
|
#
|
|||
|
|
# if lon is not None and lat is not None:
|
|||
|
|
# coord.append([lon, lat, z])
|
|||
|
|
# else:
|
|||
|
|
# coord.append([None, None, z])
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"警告: 处理轮廓点 {point} 时出错 - {str(e)}")
|
|||
|
|
# coord.append(None)
|
|||
|
|
#
|
|||
|
|
# instance["coord"] = [[c for c in coord if c is not None]] # 过滤无效坐标
|
|||
|
|
#
|
|||
|
|
# # 保存带坐标的结果
|
|||
|
|
# json_result_path = os.path.join(
|
|||
|
|
# os.path.dirname(raw_json_path),
|
|||
|
|
# "result_" + os.path.basename(raw_json_path)
|
|||
|
|
# )
|
|||
|
|
#
|
|||
|
|
# try:
|
|||
|
|
# with open(json_result_path, 'w') as f:
|
|||
|
|
# json.dump(instances, f, indent=2, default=lambda obj: float(obj) if isinstance(obj, np.generic) else str(obj))
|
|||
|
|
# print(f"完整结果(含坐标)已保存到: {json_result_path}")
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"错误: 无法保存结果JSON文件 - {str(e)}")
|
|||
|
|
# json_result_path = raw_json_path # 回退到原始JSON
|
|||
|
|
#
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"处理TIFF坐标时发生错误: {str(e)}")
|
|||
|
|
# json_result_path = raw_json_path # 回退到原始JSON
|
|||
|
|
# else:
|
|||
|
|
# json_result_path = raw_json_path # 非TIFF文件直接返回原始JSON
|
|||
|
|
# # end = time.perf_counter()
|
|||
|
|
# # print(f"总计 {count} 点,总计耗时: {end - start:.6f} 秒")
|
|||
|
|
#
|
|||
|
|
# return src_crs, json_result_path, vis_output_path, raw_json_path
|
|||
|
|
#
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"可视化处理过程中发生严重错误: {str(e)}")
|
|||
|
|
# return None, None, vis_output_path, None # 确保返回路径变量
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
import json
|
|||
|
|
import os
|
|||
|
|
import numpy as np
|
|||
|
|
import cv2
|
|||
|
|
import rasterio
|
|||
|
|
from pyproj import Transformer, CRS
|
|||
|
|
from geographiclib.geodesic import Geodesic # 用于精确计算球面多边形面积
|
|||
|
|
from pyproj import Geod
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
def calculate_polygon_area(coords):
|
|||
|
|
"""计算球面多边形面积(单位:平方米)"""
|
|||
|
|
if len(coords) < 3:
|
|||
|
|
return 0.0
|
|||
|
|
try:
|
|||
|
|
geod = Geod(ellps="WGS84") # WGS84 椭球体
|
|||
|
|
# 注意: pyproj.Geod 要求坐标顺序为 (lon, lat)
|
|||
|
|
lons, lats = zip(*[(lon, lat) for lon, lat in coords])
|
|||
|
|
area, _ = geod.polygon_area_perimeter(lons, lats)
|
|||
|
|
return abs(area) # 返回绝对值(平方米)
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"面积计算异常: {str(e)}")
|
|||
|
|
return 0.0
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
def convert_to_wgs84(x, y, src_crs):
|
|||
|
|
"""将投影坐标转换为WGS84经纬度,增加错误处理"""
|
|||
|
|
try:
|
|||
|
|
if not (isinstance(x, (int, float)) and isinstance(y, (int, float))):
|
|||
|
|
print(f"警告: 坐标值无效 (x={x}, y={y})")
|
|||
|
|
return None, None
|
|||
|
|
|
|||
|
|
transformer = Transformer.from_crs(src_crs, "EPSG:4326", always_xy=True)
|
|||
|
|
lon, lat = transformer.transform(x, y)
|
|||
|
|
|
|||
|
|
if not np.isfinite(lon) or not np.isfinite(lat):
|
|||
|
|
print(f"警告: 坐标转换结果无效 (lon={lon}, lat={lat})")
|
|||
|
|
return None, None
|
|||
|
|
|
|||
|
|
return lon, lat
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"坐标转换异常: {str(e)}")
|
|||
|
|
return None, None
|
|||
|
|
|
|||
|
|
#
|
|||
|
|
# def visualize_pil_segmentation_mask_opencv(mask_path, tif_path, output_path=None, colormap=cv2.COLORMAP_VIRIDIS, save=True):
|
|||
|
|
# """
|
|||
|
|
# 修改版:过滤面积 < 10㎡ 的轮廓,并更新可视化结果
|
|||
|
|
# """
|
|||
|
|
# src_crs = None
|
|||
|
|
# json_result_path = None
|
|||
|
|
# vis_output_path = None
|
|||
|
|
# raw_json_path = None
|
|||
|
|
#
|
|||
|
|
# try:
|
|||
|
|
# # 1. 读取掩码文件
|
|||
|
|
# if not os.path.exists(mask_path):
|
|||
|
|
# raise FileNotFoundError(f"掩码文件不存在: {mask_path}")
|
|||
|
|
#
|
|||
|
|
# mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
|
|||
|
|
# if mask is None:
|
|||
|
|
# raise ValueError(f"无法读取掩码文件(可能已损坏): {mask_path}")
|
|||
|
|
#
|
|||
|
|
# # 2. 初始化可视化图像(后续会更新)
|
|||
|
|
# mask_vis = cv2.applyColorMap(mask, colormap)
|
|||
|
|
# contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
|
# cv2.drawContours(mask_vis, contours, -1, (0, 0, 255), 1)
|
|||
|
|
#
|
|||
|
|
# # 3. 处理轮廓数据
|
|||
|
|
# valid_instances = []
|
|||
|
|
# filtered_contours = [] # 存储过滤后的轮廓(用于可视化)
|
|||
|
|
#
|
|||
|
|
# for i, contour in enumerate(contours):
|
|||
|
|
# if len(contour) < 3: # 至少需要3个点构成多边形
|
|||
|
|
# print(f"警告: 轮廓 {i} 点数不足,跳过")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# # 简化轮廓
|
|||
|
|
# epsilon = 0.001 * cv2.arcLength(contour, True)
|
|||
|
|
# approx_contour = cv2.approxPolyDP(contour, epsilon, True)
|
|||
|
|
#
|
|||
|
|
# # 转换为列表格式
|
|||
|
|
# contour_list = []
|
|||
|
|
# for point in approx_contour.squeeze():
|
|||
|
|
# if isinstance(point, np.ndarray) and point.size >= 2:
|
|||
|
|
# contour_list.append(point.tolist())
|
|||
|
|
# elif isinstance(point, (list, tuple)) and len(point) >= 2:
|
|||
|
|
# contour_list.append(list(map(int, point[:2])))
|
|||
|
|
# else:
|
|||
|
|
# print(f"警告: 无效的轮廓点格式: {point}")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# if len(contour_list) < 3:
|
|||
|
|
# print(f"警告: 轮廓 {i} 简化后点数不足,跳过")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# # 临时存储当前轮廓(后续可能被过滤)
|
|||
|
|
# temp_instance = {
|
|||
|
|
# "instance_id": i + 1,
|
|||
|
|
# "contour": contour_list,
|
|||
|
|
# "area_pixels": int(cv2.contourArea(contour)),
|
|||
|
|
# "coord": None # 后续填充
|
|||
|
|
# }
|
|||
|
|
#
|
|||
|
|
# # 4. 处理TIFF坐标转换(如果提供)
|
|||
|
|
# if tif_path and tif_path.lower().endswith(('.tif', '.tiff')):
|
|||
|
|
# try:
|
|||
|
|
# with rasterio.open(tif_path) as src:
|
|||
|
|
# src_crs = src.crs
|
|||
|
|
# if not src_crs:
|
|||
|
|
# print("警告: TIFF文件缺少坐标系信息,跳过坐标转换")
|
|||
|
|
# valid_instances.append(temp_instance)
|
|||
|
|
# filtered_contours.append(contour)
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# transform = src.transform
|
|||
|
|
# height, width = src.read(1).shape
|
|||
|
|
# coords = []
|
|||
|
|
#
|
|||
|
|
# # 修复后的代码
|
|||
|
|
# valid_coords = []
|
|||
|
|
# for point in contour_list:
|
|||
|
|
# col, row = point[:2]
|
|||
|
|
# # 检查坐标是否在图像范围内
|
|||
|
|
# if not (0 <= col < width and 0 <= row < height):
|
|||
|
|
# print(f"警告: 坐标({col}, {row})超出图像范围")
|
|||
|
|
# continue # 跳过这个点但不中断整个轮廓
|
|||
|
|
#
|
|||
|
|
# try:
|
|||
|
|
# # 转换为投影坐标
|
|||
|
|
# x, y = transform * (col, row)
|
|||
|
|
# # 转换为经纬度
|
|||
|
|
# lon, lat = convert_to_wgs84(x, y, src_crs)
|
|||
|
|
# if lon is not None and lat is not None:
|
|||
|
|
# valid_coords.append([lon, lat])
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"坐标处理错误: {str(e)}")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# # 过滤无效坐标
|
|||
|
|
# valid_coords = [c for c in coords if c is not None]
|
|||
|
|
# if len(valid_coords) < 3:
|
|||
|
|
# print(f"警告: 轮廓 {i} 有效坐标不足,跳过")
|
|||
|
|
# continue
|
|||
|
|
#
|
|||
|
|
# # 计算面积(平方米)
|
|||
|
|
# area_m2 = calculate_polygon_area(valid_coords)
|
|||
|
|
# temp_instance["coord"] = valid_coords
|
|||
|
|
# temp_instance["area_m2"] = area_m2
|
|||
|
|
#
|
|||
|
|
# # 过滤面积 < 10㎡ 的轮廓
|
|||
|
|
# if area_m2 >= 10:
|
|||
|
|
# valid_instances.append(temp_instance)
|
|||
|
|
# filtered_contours.append(contour) # 保留轮廓用于可视化
|
|||
|
|
# else:
|
|||
|
|
# print(f"过滤小面积轮廓: ID={i+1}, 面积={area_m2:.2f}㎡")
|
|||
|
|
#
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"处理TIFF坐标时发生错误: {str(e)}")
|
|||
|
|
# valid_instances.append(temp_instance) # 保留原始数据
|
|||
|
|
# filtered_contours.append(contour)
|
|||
|
|
# else:
|
|||
|
|
# # 非TIFF文件:直接保留轮廓
|
|||
|
|
# valid_instances.append(temp_instance)
|
|||
|
|
# filtered_contours.append(contour)
|
|||
|
|
#
|
|||
|
|
# # 5. 更新可视化图像(仅绘制保留的轮廓)
|
|||
|
|
# mask_vis_filtered = cv2.applyColorMap(mask, colormap)
|
|||
|
|
# if filtered_contours:
|
|||
|
|
# cv2.drawContours(mask_vis_filtered, filtered_contours, -1, (0, 0, 255), 1)
|
|||
|
|
#
|
|||
|
|
# # 6. 保存可视化结果
|
|||
|
|
# if output_path is None:
|
|||
|
|
# vis_output_path = os.path.splitext(mask_path)[0] + '_vis_cv2.png'
|
|||
|
|
# else:
|
|||
|
|
# vis_output_path = output_path
|
|||
|
|
#
|
|||
|
|
# if save:
|
|||
|
|
# if not cv2.imwrite(vis_output_path, mask_vis_filtered):
|
|||
|
|
# raise IOError(f"无法保存可视化结果到: {vis_output_path}")
|
|||
|
|
# print(f"可视化结果已保存到: {vis_output_path}")
|
|||
|
|
#
|
|||
|
|
# # 7. 保存JSON数据
|
|||
|
|
# raw_json_path = os.path.splitext(mask_path)[0] + '.json'
|
|||
|
|
# with open(raw_json_path, 'w') as f:
|
|||
|
|
# json.dump(valid_instances, f, indent=2)
|
|||
|
|
# print(f"基础轮廓数据已保存到: {raw_json_path}")
|
|||
|
|
#
|
|||
|
|
# # 8. 返回结果(src_crs 可能为None)
|
|||
|
|
# return src_crs, raw_json_path, vis_output_path, raw_json_path
|
|||
|
|
#
|
|||
|
|
# except Exception as e:
|
|||
|
|
# print(f"可视化处理过程中发生严重错误: {str(e)}")
|
|||
|
|
# return None, None, vis_output_path, None
|
|||
|
|
def visualize_pil_segmentation_mask_opencv(mask_path, tif_path, output_path=None, colormap=cv2.COLORMAP_VIRIDIS, save=True):
|
|||
|
|
"""
|
|||
|
|
修改版:过滤面积 < 10㎡ 的轮廓,并更新可视化结果
|
|||
|
|
"""
|
|||
|
|
src_crs = None
|
|||
|
|
json_result_path = None
|
|||
|
|
vis_output_path = None
|
|||
|
|
raw_json_path = None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 1. 读取掩码文件(同上)
|
|||
|
|
if not os.path.exists(mask_path):
|
|||
|
|
raise FileNotFoundError(f"掩码文件不存在: {mask_path}")
|
|||
|
|
|
|||
|
|
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
|
|||
|
|
if mask is None:
|
|||
|
|
raise ValueError(f"无法读取掩码文件(可能已损坏): {mask_path}")
|
|||
|
|
|
|||
|
|
# 2. 初始化可视化图像
|
|||
|
|
mask_vis = cv2.applyColorMap(mask, colormap)
|
|||
|
|
contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
|
cv2.drawContours(mask_vis, contours, -1, (0, 0, 255), 1)
|
|||
|
|
|
|||
|
|
# 3. 处理轮廓数据
|
|||
|
|
valid_instances = []
|
|||
|
|
filtered_contours = []
|
|||
|
|
|
|||
|
|
for i, contour in enumerate(contours):
|
|||
|
|
if len(contour) < 3:
|
|||
|
|
print(f"警告: 轮廓 {i} 点数不足,跳过")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 简化轮廓
|
|||
|
|
epsilon = 0.001 * cv2.arcLength(contour, True)
|
|||
|
|
approx_contour = cv2.approxPolyDP(contour, epsilon, True)
|
|||
|
|
|
|||
|
|
# 转换为列表格式并确保闭合
|
|||
|
|
contour_list = []
|
|||
|
|
for point in approx_contour.squeeze():
|
|||
|
|
if isinstance(point, np.ndarray) and point.size >= 2:
|
|||
|
|
contour_list.append(point.tolist())
|
|||
|
|
elif isinstance(point, (list, tuple)) and len(point) >= 2:
|
|||
|
|
contour_list.append(list(map(float, point[:2]))) # 使用float保持精度
|
|||
|
|
|
|||
|
|
if len(contour_list) < 3:
|
|||
|
|
print(f"警告: 轮廓 {i} 简化后点数不足,跳过")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 闭合多边形
|
|||
|
|
# if contour_list[0] != contour_list[-1]:
|
|||
|
|
# contour_list.append(contour_list[0])
|
|||
|
|
contour_list.append(contour_list[0])
|
|||
|
|
|
|||
|
|
temp_instance = {
|
|||
|
|
"instance_id": i + 1,
|
|||
|
|
"contour": contour_list,
|
|||
|
|
"area_pixels": int(cv2.contourArea(contour)),
|
|||
|
|
"coord": None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 4. 处理TIFF坐标转换
|
|||
|
|
if tif_path and tif_path.lower().endswith(('.tif', '.tiff')):
|
|||
|
|
try:
|
|||
|
|
with rasterio.open(tif_path) as src:
|
|||
|
|
src_crs = src.crs
|
|||
|
|
if not src_crs:
|
|||
|
|
print("警告: TIFF文件缺少坐标系信息")
|
|||
|
|
valid_instances.append(temp_instance)
|
|||
|
|
filtered_contours.append(contour)
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
transform = src.transform
|
|||
|
|
height, width = src.read(1).shape
|
|||
|
|
all_coords = []
|
|||
|
|
valid_coords = []
|
|||
|
|
|
|||
|
|
for point in contour_list[:-1]: # 不处理重复的闭合点
|
|||
|
|
col, row = point[:2]
|
|||
|
|
if not (0 <= col < width and 0 <= row < height):
|
|||
|
|
print(f"警告: 坐标({col}, {row})超出图像范围")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
x, y = transform * (col, row)
|
|||
|
|
lon, lat = convert_to_wgs84(x, y, src_crs)
|
|||
|
|
if lon is not None and lat is not None:
|
|||
|
|
all_coords.append([lon, lat])
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"坐标处理错误: {str(e)}")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 必须至少有3个有效坐标才能构成多边形
|
|||
|
|
if len(all_coords) >= 3:
|
|||
|
|
# 计算面积(平方米)
|
|||
|
|
area_m2 = calculate_polygon_area(all_coords)
|
|||
|
|
# # 闭合多边形
|
|||
|
|
all_coords.append(all_coords[0])
|
|||
|
|
# 适配geojson的三层格式
|
|||
|
|
temp_instance["coord"] = [all_coords]
|
|||
|
|
temp_instance["area_m2"] = area_m2
|
|||
|
|
|
|||
|
|
# 过滤面积 < 10㎡ 的轮廓
|
|||
|
|
if area_m2 >= 10:
|
|||
|
|
valid_instances.append(temp_instance)
|
|||
|
|
filtered_contours.append(contour)
|
|||
|
|
else:
|
|||
|
|
print(f"过滤小面积轮廓: ID={i+1}, 面积={area_m2:.2f}㎡")
|
|||
|
|
else:
|
|||
|
|
print(f"警告: 轮廓 {i} 有效坐标不足3个")
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"处理TIFF坐标时发生错误: {str(e)}")
|
|||
|
|
valid_instances.append(temp_instance)
|
|||
|
|
filtered_contours.append(contour)
|
|||
|
|
else:
|
|||
|
|
valid_instances.append(temp_instance)
|
|||
|
|
filtered_contours.append(contour)
|
|||
|
|
|
|||
|
|
# 5. 更新可视化图像
|
|||
|
|
mask_vis_filtered = cv2.applyColorMap(mask, colormap)
|
|||
|
|
if filtered_contours:
|
|||
|
|
cv2.drawContours(mask_vis_filtered, filtered_contours, -1, (0, 0, 255), 1)
|
|||
|
|
|
|||
|
|
# ...(剩余的保存和返回逻辑保持不变)
|
|||
|
|
|
|||
|
|
# 6. 保存可视化结果
|
|||
|
|
if output_path is None:
|
|||
|
|
vis_output_path = os.path.splitext(mask_path)[0] + '_vis_cv2.png'
|
|||
|
|
else:
|
|||
|
|
vis_output_path = output_path
|
|||
|
|
|
|||
|
|
if save:
|
|||
|
|
if not cv2.imwrite(vis_output_path, mask_vis_filtered):
|
|||
|
|
raise IOError(f"无法保存可视化结果到: {vis_output_path}")
|
|||
|
|
print(f"可视化结果已保存到: {vis_output_path}")
|
|||
|
|
|
|||
|
|
# 7. 保存JSON数据
|
|||
|
|
raw_json_path = os.path.splitext(mask_path)[0] + '.json'
|
|||
|
|
with open(raw_json_path, 'w') as f:
|
|||
|
|
json.dump(valid_instances, f, indent=2)
|
|||
|
|
print(f"基础轮廓数据已保存到: {raw_json_path}")
|
|||
|
|
|
|||
|
|
# 8. 返回结果(src_crs 可能为None)
|
|||
|
|
return src_crs, raw_json_path, vis_output_path, raw_json_path
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"可视化处理过程中发生严重错误: {str(e)}")
|
|||
|
|
return None, None, vis_output_path, None
|