怎么评估一个对象的边界很复杂?代码与指标
基于尾矿库的形状特征和研究目标,我为您推荐以下形状指数方案来突出尾矿库的形状复杂性:
推荐的核心形状指数
1. 形状指数 (Shape Index)
公式: SI = P / (2 * √(π * A))
- P: 周长
- A: 面积
- π: 圆周率
优势: 直接反映边界相对于圆形的偏离程度,值越大越复杂
2. 分形维数 (Fractal Dimension)
公式: FD = 2 * log(P/4) / log(A)
- 衡量边界在不同尺度上的自相似性
- 值范围: 1.0(简单) ~ 2.0(极其复杂)
3. 紧密度 (Compactness)
公式: C = 4πA / P²
- 与形状指数互补,值越小形状越复杂
计算教程 - Python实现
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure, morphology
import pandas as pddef calculate_tailing_pond_complexity(segmentation_png_path):"""计算尾矿库形状复杂度"""# 1. 读取语义分割结果img = cv2.imread(segmentation_png_path, cv2.IMREAD_GRAYSCALE)# 2. 二值化处理 (假设尾矿库为前景,背景为0)_, binary_mask = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)# 3. 形态学操作去除噪声binary_mask = morphology.opening(binary_mask, morphology.disk(2))# 4. 寻找轮廓contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)results = []for i, contour in enumerate(contours):# 跳过太小的区域area = cv2.contourArea(contour)if area < 100: # 面积阈值,可根据实际情况调整continue# 计算周长perimeter = cv2.arcLength(contour, True)# 计算形状指数shape_index = perimeter / (2 * np.sqrt(np.pi * area))# 计算分形维数if perimeter > 4 and area > 0:fractal_dimension = 2 * np.log(perimeter / 4) / np.log(area)else:fractal_dimension = 1.0# 计算紧密度compactness = (4 * np.pi * area) / (perimeter ** 2) if perimeter > 0 else 0# 计算矩形度_, _, w, h = cv2.boundingRect(contour)rect_area = w * hrectangularity = area / rect_area if rect_area > 0 else 0# 计算椭圆相似度if len(contour) >= 5:ellipse = cv2.fitEllipse(contour)ellipse_center, ellipse_axes, ellipse_angle = ellipseellipse_area = np.pi * ellipse_axes[0] * ellipse_axes[1] / 4ellipticity = area / ellipse_area if ellipse_area > 0 else 0else:ellipticity = 0results.append({'object_id': i,'area': area,'perimeter': perimeter,'shape_index': shape_index,'fractal_dimension': fractal_dimension,'compactness': compactness,'rectangularity': rectangularity,'ellipticity': ellipticity,'complexity_score': shape_index * fractal_dimension * (1 - compactness)})return results, binary_mask, contoursdef visualize_results(img_path, results, contours):"""可视化结果"""img = cv2.imread(img_path)img_contour = img.copy()# 绘制轮廓cv2.drawContours(img_contour, contours, -1, (0, 255, 0), 2)# 创建结果图fig, axes = plt.subplots(2, 2, figsize=(15, 12))# 原始图像axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))axes[0, 0].set_title('Original Image')axes[0, 0].axis('off')# 带轮廓的图像axes[0, 1].imshow(cv2.cvtColor(img_contour, cv2.COLOR_BGR2RGB))axes[0, 1].set_title('Detected Tailing Ponds')axes[0, 1].axis('off')# 形状指数分布si_values = [r['shape_index'] for r in results]axes[1, 0].hist(si_values, bins=20, alpha=0.7, color='blue')axes[1, 0].set_xlabel('Shape Index')axes[1, 0].set_ylabel('Frequency')axes[1, 0].set_title('Shape Index Distribution')# 复杂度评分complexity_scores = [r['complexity_score'] for r in results]axes[1, 1].scatter(si_values, complexity_scores, alpha=0.6)axes[1, 1].set_xlabel('Shape Index')axes[1, 1].set_ylabel('Complexity Score')axes[1, 1].set_title('Shape Complexity Analysis')plt.tight_layout()plt.show()return fig# 使用示例
if __name__ == "__main__":# 替换为你的PNG文件路径png_path = "tailing_pond_segmentation.png"# 计算形状复杂度results, binary_mask, contours = calculate_tailing_pond_complexity(png_path)# 转换为DataFrame便于分析df = pd.DataFrame(results)# 打印统计结果print("尾矿库形状复杂度统计:")print(f"检测到尾矿库数量: {len(df)}")print(f"形状指数范围: {df['shape_index'].min():.3f} - {df['shape_index'].max():.3f}")print(f"平均形状指数: {df['shape_index'].mean():.3f}")print(f"分形维数范围: {df['fractal_dimension'].min():.3f} - {df['fractal_dimension'].max():.3f}")# 可视化结果fig = visualize_results(png_path.replace('_segmentation', ''), results, contours)# 保存结果到CSVdf.to_csv('tailing_pond_complexity_analysis.csv', index=False)
快速评估函数
def quick_complexity_assessment(shape_index):"""快速评估形状复杂度等级"""if shape_index < 1.2:return "简单", "类圆形,边界平滑"elif shape_index < 1.8:return "中等", "有一定不规则性"elif shape_index < 2.5:return "复杂", "明显不规则边界"else:return "极其复杂", "高度不规则,边界蜿蜒"# 对每个尾矿库进行评估
for idx, result in enumerate(results):level, description = quick_complexity_assessment(result['shape_index'])print(f"尾矿库 {idx}: SI={result['shape_index']:.3f} - {level} - {description}")
关键解读指南
形状指数范围 | 复杂度等级 | 尾矿库特征 |
---|---|---|
1.0-1.2 | 非常简单 | 接近圆形,可能是新建或规整尾矿库 |
1.2-1.5 | 简单 | 略有凹凸,边界相对规整 |
1.5-2.0 | 中等复杂 | 明显不规则,可能有多个凸起 |
2.0-3.0 | 复杂 | 高度不规则,边界蜿蜒 |
>3.0 | 极其复杂 | 极度不规则,可能包含多个子区域 |
建议
- 重点关注形状指数(SI) - 最直观反映边界复杂度
- 结合分形维数(FD) - 验证复杂度的尺度不变性
- 使用复杂度综合评分 - 多指标加权评估
- 对比不同时期 - 分析尾矿库形状随时间的变化
这个方案特别适合尾矿库这种通常具有复杂边界的人工地物,能够有效区分不同建设阶段和使用状态的尾矿库形状特征。
当然可以!我为您优化代码,实现批量处理文件夹中的所有切片文件。
批量处理优化代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure, morphology
import pandas as pd
import os
from pathlib import Path
import json
from tqdm import tqdmclass TailingPondComplexityAnalyzer:def __init__(self, input_folder, output_folder=None):"""初始化分析器Args:input_folder: 输入PNG切片文件夹路径output_folder: 输出结果文件夹路径"""self.input_folder = Path(input_folder)self.output_folder = Path(output_folder) if output_folder else self.input_folder / "results"self.output_folder.mkdir(exist_ok=True)# 支持的图像格式self.supported_formats = {'.png', '.jpg', '.jpeg', '.tif', '.tiff'}def find_image_files(self):"""查找文件夹中的所有图像文件"""image_files = []for format in self.supported_formats:image_files.extend(self.input_folder.glob(f"*{format}"))image_files.extend(self.input_folder.glob(f"*{format.upper()}"))print(f"找到 {len(image_files)} 个图像文件")return sorted(image_files)def calculate_single_image_complexity(self, image_path, min_area=100):"""计算单张图像的形状复杂度Args:image_path: 图像文件路径min_area: 最小面积阈值,过滤小区域Returns:results: 形状分析结果列表binary_mask: 二值掩码contours: 检测到的轮廓"""try:# 读取图像img = cv2.imread(str(image_path), cv2.IMREAD_GRAYSCALE)if img is None:print(f"无法读取图像: {image_path}")return [], None, []# 二值化处理_, binary_mask = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)# 形态学操作去除噪声binary_mask = morphology.opening(binary_mask, morphology.disk(2))# 寻找轮廓contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)results = []for i, contour in enumerate(contours):area = cv2.contourArea(contour)if area < min_area:continue# 计算周长perimeter = cv2.arcLength(contour, True)# 计算形状指数shape_index = perimeter / (2 * np.sqrt(np.pi * area))# 计算分形维数if perimeter > 4 and area > 0:fractal_dimension = 2 * np.log(perimeter / 4) / np.log(area)# 限制分形维数在合理范围内fractal_dimension = np.clip(fractal_dimension, 1.0, 2.0)else:fractal_dimension = 1.0# 计算紧密度compactness = (4 * np.pi * area) / (perimeter ** 2) if perimeter > 0 else 0# 计算矩形度_, _, w, h = cv2.boundingRect(contour)rect_area = w * hrectangularity = area / rect_area if rect_area > 0 else 0# 计算椭圆相似度if len(contour) >= 5:try:ellipse = cv2.fitEllipse(contour)ellipse_axes = ellipse[1]ellipse_area = np.pi * ellipse_axes[0] * ellipse_axes[1] / 4ellipticity = area / ellipse_area if ellipse_area > 0 else 0except:ellipticity = 0else:ellipticity = 0# 计算综合复杂度评分complexity_score = shape_index * fractal_dimension * (1 - compactness)# 复杂度等级评估complexity_level, complexity_desc = self.assess_complexity_level(shape_index)results.append({'image_name': image_path.name,'object_id': i,'area': area,'perimeter': perimeter,'shape_index': shape_index,'fractal_dimension': fractal_dimension,'compactness': compactness,'rectangularity': rectangularity,'ellipticity': ellipticity,'complexity_score': complexity_score,'complexity_level': complexity_level,'complexity_description': complexity_desc})return results, binary_mask, contoursexcept Exception as e:print(f"处理图像 {image_path} 时出错: {e}")return [], None, []def assess_complexity_level(self, shape_index):"""评估形状复杂度等级"""if shape_index < 1.2:return "非常简单", "类圆形,边界平滑"elif shape_index < 1.5:return "简单", "略有凹凸,边界相对规整"elif shape_index < 2.0:return "中等复杂", "明显不规则,有多个凸起"elif shape_index < 3.0:return "复杂", "高度不规则,边界蜿蜒"else:return "极其复杂", "极度不规则,可能包含多个子区域"def visualize_single_result(self, image_path, results, contours, save_fig=False):"""可视化单张图像的分析结果"""try:img = cv2.imread(str(image_path))if img is None:return Noneimg_contour = img.copy()cv2.drawContours(img_contour, contours, -1, (0, 255, 0), 2)fig, axes = plt.subplots(2, 2, figsize=(15, 12))fig.suptitle(f'尾矿库形状复杂度分析 - {image_path.name}', fontsize=16)# 原始图像axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))axes[0, 0].set_title('原始图像')axes[0,