Files
HSI/edge_detect_method/edge_detect.py

842 lines
34 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 numpy as np
import pandas as pd
import spectral
import cv2
import os
from scipy.io import savemat
from scipy import ndimage
from scipy.ndimage import gaussian_filter, laplace
from typing import Optional, Dict, Any, Tuple, List, Union
from dataclasses import dataclass, field
import warnings
warnings.filterwarnings('ignore')
@dataclass
class EdgeDetectionConfig:
"""边缘检测配置类"""
# 输入文件配置
input_path: Optional[str] = None
band_index: int = 0 # 指定的波段索引从0开始
# 输出配置
output_path: Optional[str] = None
output_dir: Optional[str] = None
# 边缘检测方法配置
method: str = 'canny'
kernel_size: int = 3 # 卷积核大小(必须是奇数)
# 批量处理配置
batch_methods: List[str] = field(default_factory=lambda: ['canny'])
batch_bands: List[int] = field(default_factory=lambda: [0])
# Sobel和Scharr参数
sobel_dx: int = 1 # x方向导数阶数
sobel_dy: int = 0 # y方向导数阶数
sobel_ksize: int = -1 # Sobel核大小-1表示3x3
# Laplacian参数
laplacian_ksize: int = 1 # Laplacian核大小
laplacian_scale: float = 1.0 # Laplacian尺度参数
laplacian_delta: float = 0.0 # Laplacian delta参数
# LoG参数
log_sigma: float = 1.0 # 高斯标准差
log_threshold: float = 0.0 # LoG阈值
# 梯度算法阈值参数
gradient_threshold: float = 0.0 # 梯度阈值0-2550表示不使用阈值
gradient_use_otsu: bool = False # 是否对梯度图使用Otsu自动阈值
log_mode: str = 'reflect' # 边界模式:'reflect', 'constant', 'nearest', 'mirror', 'wrap'
# Canny参数
canny_min_threshold: int = 100
canny_max_threshold: int = 200
canny_aperture_size: int = 3 # Sobel算子孔径大小
canny_l2gradient: bool = False # 是否使用L2范数计算梯度
# 通用参数
normalize_output: bool = False # 是否将输出归一化到0-255
use_blur: bool = False # 是否预先进行高斯模糊
blur_kernel_size: int = 3 # 模糊核大小(必须是奇数)
blur_sigma: float = 1.0 # 模糊标准差
def __post_init__(self):
"""参数校验和默认值设置"""
# 校验必需的文件路径
if not self.input_path:
raise ValueError("必须指定输入文件路径(input_path)")
if not os.path.exists(self.input_path):
raise FileNotFoundError(f"输入文件不存在: {self.input_path}")
# 校验输出路径
if not self.output_path and not self.output_dir:
raise ValueError("必须指定输出路径(output_path)或输出目录(output_dir)")
# 校验边缘检测方法
supported_methods = self._get_supported_methods()
if self.method not in supported_methods:
raise ValueError(f"不支持的边缘检测方法: {self.method}。支持的方法: {list(supported_methods.keys())}")
# 校验批量处理参数
if len(self.batch_methods) != len(self.batch_bands):
# 如果长度不等,使用广播方式:单个波段对应多个方法,或单个方法对应多个波段
if len(self.batch_methods) == 1 and len(self.batch_bands) > 1:
# 一个方法应用到多个波段
self.batch_methods = self.batch_methods * len(self.batch_bands)
elif len(self.batch_bands) == 1 and len(self.batch_methods) > 1:
# 一个波段应用多个方法
self.batch_bands = self.batch_bands * len(self.batch_methods)
else:
raise ValueError("batch_methods和batch_bands长度不匹配请确保其中一个长度为1或两者长度相等")
for method in self.batch_methods:
if method not in supported_methods:
raise ValueError(f"批量处理中包含不支持的方法: {method}")
# 校验参数
self._validate_parameters()
def _validate_parameters(self):
"""参数校验"""
# 校验核大小
if self.kernel_size % 2 == 0 or self.kernel_size < 1:
raise ValueError("kernel_size必须是正奇数")
# 校验Sobel参数
if self.sobel_dx < 0 or self.sobel_dy < 0:
raise ValueError("sobel_dx和sobel_dy必须是非负整数")
if self.sobel_dx + self.sobel_dy != 1:
raise ValueError(f"sobel_dx({self.sobel_dx}) + sobel_dy({self.sobel_dy}) 必须等于1。有效组合: dx=1,dy=0 或 dx=0,dy=1")
if self.sobel_ksize != -1 and (self.sobel_ksize % 2 == 0 or self.sobel_ksize < 1):
raise ValueError("sobel_ksize必须是正奇数或-1")
# 校验Laplacian参数
if self.laplacian_ksize < 1 or self.laplacian_ksize > 31:
raise ValueError("laplacian_ksize必须在1-31之间")
if self.laplacian_scale <= 0:
raise ValueError("laplacian_scale必须大于0")
# 校验LoG参数
if self.log_sigma <= 0:
raise ValueError("log_sigma必须大于0")
if self.log_mode not in ['reflect', 'constant', 'nearest', 'mirror', 'wrap']:
raise ValueError("log_mode必须是'reflect', 'constant', 'nearest', 'mirror', 'wrap'之一")
# 校验Canny参数
if self.canny_min_threshold < 0 or self.canny_max_threshold < 0:
raise ValueError("Canny阈值必须是非负数")
if self.canny_min_threshold >= self.canny_max_threshold:
raise ValueError("canny_min_threshold必须小于canny_max_threshold")
if self.canny_aperture_size not in [3, 5, 7]:
raise ValueError("canny_aperture_size必须是3、5或7")
# 校验梯度阈值参数
if self.gradient_threshold < 0 or self.gradient_threshold > 255:
raise ValueError("gradient_threshold必须在0-255范围内")
# 校验通用参数
if self.blur_kernel_size % 2 == 0 or self.blur_kernel_size < 1:
raise ValueError("blur_kernel_size必须是正奇数")
if self.blur_sigma <= 0:
raise ValueError("blur_sigma必须大于0")
def _get_supported_methods(self) -> Dict[str, str]:
"""获取支持的边缘检测方法列表"""
methods = {
'sobel': 'Sobel算子',
'scharr': 'Scharr算子',
'laplacian': 'Laplacian算子',
'log': 'Laplacian of Gaussian (LoG)',
'canny': 'Canny算子'
}
return methods
class EdgeDetector:
"""边缘检测处理系统"""
def __init__(self, config: EdgeDetectionConfig):
self.config = config
# 初始化数据存储
self.data = None
self.selected_band = None
self.original_shape = None
def load_data(self) -> None:
"""
加载数据文件
"""
print(f"正在加载数据文件: {self.config.input_path}")
file_ext = os.path.splitext(self.config.input_path)[1].lower()
if file_ext in ['.dat', '.img', '.hdr']:
self._load_hyperspectral_data(self.config.input_path)
else:
raise ValueError(f"不支持的文件格式: {file_ext}")
# 提取指定波段
self._extract_band(self.config.band_index)
print("数据加载完成")
def _load_hyperspectral_data(self, file_path):
"""加载高光谱图像数据"""
base_path = os.path.splitext(file_path)[0]
hdr_file = base_path + '.hdr'
# 读取ENVI格式图像
img = spectral.open_image(hdr_file)
self.data = img.load()
self.original_shape = self.data.shape
# 转换为二维数组(像素×波段)
self.data = self.data.reshape(-1, self.data.shape[2])
def _extract_band(self, band_index):
"""提取指定波段"""
if band_index >= self.data.shape[1]:
raise ValueError(f"波段索引超出范围: {band_index}, 总波段数: {self.data.shape[1]}")
self.selected_band = self.data[:, band_index].reshape(self.original_shape[0], self.original_shape[1])
def apply_edge_detection(self) -> Tuple[np.ndarray, Dict[str, Any]]:
"""
应用边缘检测方法
Returns:
Tuple[np.ndarray, Dict[str, Any]]: (边缘检测结果, 附加信息)
"""
print(f"正在应用边缘检测方法: {self.config.method}")
if self.selected_band is None:
raise ValueError("请先加载数据")
# 归一化图像到0-255范围大多数OpenCV函数需要
image_normalized = self._normalize_image(self.selected_band)
# 应用不同的边缘检测方法
if self.config.method == 'sobel':
result, info = self._sobel_detection(image_normalized)
elif self.config.method == 'scharr':
result, info = self._scharr_detection(image_normalized)
elif self.config.method == 'laplacian':
result, info = self._laplacian_detection(image_normalized)
elif self.config.method == 'log':
result, info = self._log_detection(self.selected_band) # LoG使用原始数据
elif self.config.method == 'canny':
result, info = self._canny_detection(image_normalized)
else:
raise ValueError(f"不支持的边缘检测方法: {self.config.method}")
# 归一化输出
if self.config.normalize_output:
result = self._normalize_output(result)
return result.astype(np.uint8), info
def _normalize_image(self, image):
"""将图像归一化到0-255范围"""
image_min, image_max = np.min(image), np.max(image)
if image_max > image_min:
return ((image - image_min) / (image_max - image_min) * 255).astype(np.uint8)
else:
return image.astype(np.uint8)
def _normalize_output(self, image):
"""将输出归一化到0-255范围"""
image_min, image_max = np.min(image), np.max(image)
if image_max > image_min:
return ((image - image_min) / (image_max - image_min) * 255).astype(np.uint8)
else:
return (image * 255).astype(np.uint8)
def _apply_gradient_threshold(self, gradient_image, method_name):
"""对梯度图像应用阈值处理"""
if self.config.gradient_threshold > 0:
# 使用固定阈值
_, thresholded = cv2.threshold(gradient_image, self.config.gradient_threshold, 255, cv2.THRESH_BINARY)
print(f"{method_name} 使用固定阈值: {self.config.gradient_threshold}")
return thresholded
elif self.config.gradient_use_otsu:
# 使用Otsu自动阈值
_, thresholded = cv2.threshold(gradient_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"{method_name} 使用Otsu自动阈值")
return thresholded
else:
# 不使用阈值,直接返回梯度图像
return gradient_image
def _sobel_detection(self, image):
"""Sobel边缘检测"""
# 预处理:高斯模糊
if self.config.use_blur:
image = cv2.GaussianBlur(image, (self.config.blur_kernel_size, self.config.blur_kernel_size), self.config.blur_sigma)
# 校验Sobel参数OpenCV要求dx+dy == 1
if self.config.sobel_dx + self.config.sobel_dy != 1:
raise ValueError(f"Sobel参数错误: dx({self.config.sobel_dx}) + dy({self.config.sobel_dy}) 必须等于1。有效组合: dx=1,dy=0 或 dx=0,dy=1")
# 计算梯度
grad = cv2.Sobel(image, cv2.CV_64F, self.config.sobel_dx, self.config.sobel_dy, ksize=self.config.sobel_ksize)
# 计算梯度幅值(如果是混合梯度)
if self.config.sobel_dx == 1 and self.config.sobel_dy == 1:
grad_magnitude = cv2.convertScaleAbs(grad)
else:
# 对于单方向梯度,直接使用绝对值
grad_magnitude = cv2.convertScaleAbs(grad)
# 应用阈值处理(可选)
grad_magnitude = self._apply_gradient_threshold(grad_magnitude, 'Sobel')
info = {
'grad': grad,
'method': 'Sobel',
'parameters': {
'dx': self.config.sobel_dx,
'dy': self.config.sobel_dy,
'ksize': self.config.sobel_ksize,
'use_blur': self.config.use_blur,
'blur_kernel_size': self.config.blur_kernel_size if self.config.use_blur else None,
'blur_sigma': self.config.blur_sigma if self.config.use_blur else None,
'gradient_threshold': self.config.gradient_threshold,
'gradient_use_otsu': self.config.gradient_use_otsu
}
}
return grad_magnitude, info
def _scharr_detection(self, image):
"""Scharr边缘检测"""
# 预处理:高斯模糊
if self.config.use_blur:
image = cv2.GaussianBlur(image, (self.config.blur_kernel_size, self.config.blur_kernel_size), self.config.blur_sigma)
# Scharr算子是特殊的3x3 Sobel算子
grad_x = cv2.Scharr(image, cv2.CV_64F, 1, 0)
grad_y = cv2.Scharr(image, cv2.CV_64F, 0, 1)
# 计算梯度幅值
grad_magnitude = cv2.magnitude(grad_x, grad_y)
# 转换为uint8
grad_magnitude = cv2.convertScaleAbs(grad_magnitude)
# 应用阈值处理(可选)
grad_magnitude = self._apply_gradient_threshold(grad_magnitude, 'Scharr')
info = {
'grad_x': grad_x,
'grad_y': grad_y,
'method': 'Scharr',
'parameters': {
'use_blur': self.config.use_blur,
'blur_kernel_size': self.config.blur_kernel_size if self.config.use_blur else None,
'blur_sigma': self.config.blur_sigma if self.config.use_blur else None,
'gradient_threshold': self.config.gradient_threshold,
'gradient_use_otsu': self.config.gradient_use_otsu
}
}
return grad_magnitude, info
def _laplacian_detection(self, image):
"""Laplacian边缘检测"""
# 预处理:高斯模糊
if self.config.use_blur:
image = cv2.GaussianBlur(image, (self.config.blur_kernel_size, self.config.blur_kernel_size), self.config.blur_sigma)
# 应用Laplacian算子
laplacian = cv2.Laplacian(image, cv2.CV_64F, ksize=self.config.laplacian_ksize,
scale=self.config.laplacian_scale, delta=self.config.laplacian_delta)
# 取绝对值并转换为uint8
laplacian_abs = cv2.convertScaleAbs(laplacian)
# 应用阈值处理(可选)
laplacian_abs = self._apply_gradient_threshold(laplacian_abs, 'Laplacian')
info = {
'laplacian': laplacian,
'method': 'Laplacian',
'parameters': {
'ksize': self.config.laplacian_ksize,
'scale': self.config.laplacian_scale,
'delta': self.config.laplacian_delta,
'use_blur': self.config.use_blur,
'blur_kernel_size': self.config.blur_kernel_size if self.config.use_blur else None,
'blur_sigma': self.config.blur_sigma if self.config.use_blur else None,
'gradient_threshold': self.config.gradient_threshold,
'gradient_use_otsu': self.config.gradient_use_otsu
}
}
return laplacian_abs, info
def _log_detection(self, image):
"""Laplacian of Gaussian (LoG) 边缘检测"""
# 使用指定的边界模式进行高斯滤波
gaussian_filtered = gaussian_filter(image.astype(np.float64),
sigma=self.config.log_sigma,
mode=self.config.log_mode)
# 应用Laplacian算子
log_result = laplace(gaussian_filtered, mode=self.config.log_mode)
# 取绝对值
log_abs = np.abs(log_result)
# 应用阈值
if self.config.log_threshold > 0:
log_abs = (log_abs > self.config.log_threshold).astype(np.uint8) * 255
else:
# 自动阈值使用Otsu方法
# 先归一化到0-255范围然后应用Otsu
log_min, log_max = np.min(log_abs), np.max(log_abs)
if log_max > log_min:
log_normalized = ((log_abs - log_min) / (log_max - log_min) * 255).astype(np.uint8)
else:
log_normalized = log_abs.astype(np.uint8)
_, log_abs = cv2.threshold(log_normalized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
info = {
'gaussian_filtered': gaussian_filtered,
'log_result': log_result,
'method': 'LoG',
'parameters': {
'sigma': self.config.log_sigma,
'threshold': self.config.log_threshold,
'mode': self.config.log_mode
}
}
return log_abs.astype(np.uint8), info
def _canny_detection(self, image):
"""Canny边缘检测"""
# 预处理:高斯模糊
if self.config.use_blur:
image = cv2.GaussianBlur(image, (self.config.blur_kernel_size, self.config.blur_kernel_size), self.config.blur_sigma)
# 应用Canny算子
edges = cv2.Canny(image,
self.config.canny_min_threshold,
self.config.canny_max_threshold,
apertureSize=self.config.canny_aperture_size,
L2gradient=self.config.canny_l2gradient)
info = {
'method': 'Canny',
'parameters': {
'min_threshold': self.config.canny_min_threshold,
'max_threshold': self.config.canny_max_threshold,
'aperture_size': self.config.canny_aperture_size,
'L2gradient': self.config.canny_l2gradient,
'use_blur': self.config.use_blur,
'blur_kernel_size': self.config.blur_kernel_size if self.config.use_blur else None,
'blur_sigma': self.config.blur_sigma if self.config.use_blur else None
}
}
return edges, info
def save_results(self, edge_data, info, output_path, method):
"""
保存边缘检测结果
参数:
edge_data: 边缘检测结果
info: 附加信息
output_path: 输出路径
method: 使用的边缘检测方法
"""
output_base = os.path.splitext(output_path)[0]
# 保存.dat文件
self._save_dat_file(edge_data, output_path)
# 保存.hdr头文件
hdr_path = output_base + '.hdr'
self._save_hdr_file(hdr_path, edge_data.shape, method, info)
print(f"边缘检测结果已保存: {output_path}")
print(f"头文件已保存: {hdr_path}")
print(f"使用的检测方法: {method}")
def _save_dat_file(self, data, file_path):
"""保存.dat文件二进制格式"""
with open(file_path, 'wb') as f:
data.astype(np.uint8).tofile(f)
def _save_hdr_file(self, hdr_path, data_shape, method, info):
"""保存ENVI头文件"""
header_content = f"""ENVI
description = {{{method} Edge Detection Result}}
samples = {data_shape[1]}
lines = {data_shape[0]}
bands = 1
header offset = 0
file type = ENVI Standard
data type = 1
interleave = bsq
byte order = 0
band names = {{Edge_Detection_Result}}
classes = 2
class names = {{Background, Edges}}
class lookup = {{0,0,0, 255,255,255}}
"""
# 添加方法参数信息
if 'parameters' in info and info['parameters']:
params_str = ', '.join([f'{k}:{v}' for k, v in info['parameters'].items()])
header_content += f"edge_detection_parameters = {{{params_str}}}\n"
header_content += f"edge_detection_method = {method}\n"
with open(hdr_path, 'w', encoding='utf-8') as f:
f.write(header_content)
def batch_process(self, methods, bands, output_dir):
"""
批量处理多个边缘检测方法
参数:
methods: 边缘检测方法列表
bands: 波段索引列表
output_dir: 输出目录
"""
os.makedirs(output_dir, exist_ok=True)
# 显示数据信息
if self.data is not None:
print(f"\n=== 数据信息 ===")
print(f"图像尺寸: {self.original_shape[0]}x{self.original_shape[1]}")
print(f"波段数: {self.original_shape[2]}")
results = {}
for method, band_idx in zip(methods, bands):
print(f"\n正在处理: {method} (波段: {band_idx})")
try:
# 临时修改配置以适应当前方法和波段
original_method = self.config.method
original_band = self.config.band_index
self.config.method = method
self.config.band_index = band_idx
# 重新提取波段
self._extract_band(band_idx)
# 应用边缘检测
edge_data, info = self.apply_edge_detection()
# 恢复原始配置
self.config.method = original_method
self.config.band_index = original_band
# 保存结果
output_path = os.path.join(output_dir, f'edge_{method}_band{band_idx}.dat')
self.save_results(edge_data, info, output_path, method)
results[method] = {
'data': edge_data,
'info': info,
'band': band_idx
}
except Exception as e:
print(f"处理 {method} 时出错: {str(e)}")
return results
@staticmethod
def print_method_info():
"""
打印所有可用边缘检测方法的详细信息
"""
method_info = {
'sobel': {
'name': 'Sobel算子 (Sobel Operator)',
'description': '使用Sobel核计算图像梯度检测边缘',
'advantages': ['计算简单', '对噪声有一定抑制', '可以检测边缘方向'],
'limitations': ['对噪声敏感', '检测到的边缘较粗'],
'best_for': ['一般边缘检测', '需要方向信息的应用'],
'parameters': ['sobel_dx', 'sobel_dy', 'sobel_ksize', 'use_blur', 'blur_kernel_size', 'blur_sigma', 'gradient_threshold', 'gradient_use_otsu']
},
'scharr': {
'name': 'Scharr算子 (Scharr Operator)',
'description': '改进的Sobel算子具有更好的旋转不变性',
'advantages': ['比Sobel算子更精确', '更好的旋转不变性'],
'limitations': ['计算量稍大', '只适用于3x3核'],
'best_for': ['需要高精度边缘检测的应用'],
'parameters': ['use_blur', 'blur_kernel_size', 'blur_sigma', 'gradient_threshold', 'gradient_use_otsu']
},
'laplacian': {
'name': 'Laplacian算子 (Laplacian Operator)',
'description': '基于二阶导数的边缘检测方法,对灰度突变敏感',
'advantages': ['对灰度突变敏感', '计算简单'],
'limitations': ['对噪声非常敏感', '不能提供边缘方向信息'],
'best_for': ['检测灰度突变明显的边缘'],
'parameters': ['laplacian_ksize', 'laplacian_scale', 'laplacian_delta', 'use_blur', 'blur_kernel_size', 'blur_sigma', 'gradient_threshold', 'gradient_use_otsu']
},
'log': {
'name': 'Laplacian of Gaussian (LoG)',
'description': '先进行高斯平滑再应用Laplacian算子',
'advantages': ['减少噪声影响', '检测不同尺度边缘', '理论基础扎实'],
'limitations': ['计算复杂度高', '参数选择重要'],
'best_for': ['需要抑制噪声的边缘检测', '学术研究'],
'parameters': ['log_sigma', 'log_threshold', 'log_mode']
},
'canny': {
'name': 'Canny算子 (Canny Edge Detector)',
'description': '多阶段边缘检测算法,包括平滑、梯度计算、非最大抑制和双阈值处理',
'advantages': ['检测精度高', '抗噪声能力强', '单像素边缘'],
'limitations': ['计算复杂度较高', '参数调节复杂'],
'best_for': ['高质量边缘检测', '计算机视觉应用'],
'parameters': ['canny_min_threshold', 'canny_max_threshold', 'canny_aperture_size', 'canny_l2gradient', 'use_blur', 'blur_kernel_size', 'blur_sigma']
}
}
print("\n=== 边缘检测方法详细说明 ===\n")
for method, info in method_info.items():
print(f"{method} - {info['name']}")
print(f" 描述: {info['description']}")
print(f" 优势: {', '.join(info['advantages'])}")
print(f" 局限性: {', '.join(info['limitations'])}")
print(f" 适用场景: {', '.join(info['best_for'])}")
if 'parameters' in info:
print(f" 可调参数: {', '.join(info['parameters'])}")
print()
def main():
"""主函数:命令行接口"""
import argparse
parser = argparse.ArgumentParser(
description='高光谱图像边缘检测工具',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
1. 使用Canny算子检测边缘:
python edge_detect.py input.hdr -m canny -b 50 -o edge_result.dat
2. 批量应用多种边缘检测方法:
python edge_detect.py input.hdr -m sobel scharr laplacian log canny -B 25 25 25 25 25 -d results/
3. 使用自定义参数的Sobel检测:
python edge_detect.py input.hdr -m sobel -b 30 --sobel-dx 1 --sobel-dy 0 --use-blur --blur-kernel-size 5
支持的边缘检测方法:
sobel: Sobel算子
scharr: Scharr算子
laplacian: Laplacian算子
log: Laplacian of Gaussian (LoG)
canny: Canny算子
批量处理:
-m 指定多个方法,-B 指定对应波段索引
-d 指定输出目录
"""
)
parser.add_argument('input_path', help='输入高光谱图像文件路径 (.hdr)')
parser.add_argument('-m', '--methods', nargs='+', required=True,
choices=['sobel', 'scharr', 'laplacian', 'log', 'canny'],
help='边缘检测方法 (可指定多个)')
parser.add_argument('-b', '--band-index', type=int, default=0,
help='波段索引 (默认: 0)')
parser.add_argument('-B', '--batch-bands', nargs='+', type=int,
help='批量处理的波段索引列表 (与methods一一对应)')
parser.add_argument('-o', '--output', help='单个方法输出文件路径')
parser.add_argument('-d', '--output-dir', default='edge_detection_results',
help='批量处理输出目录 (默认: edge_detection_results)')
# Sobel和Scharr参数
parser.add_argument('--sobel-dx', type=int, default=1, choices=[0, 1],
help='Sobel x方向导数阶数必须与sobel-dy之和等于1 (默认: 1)')
parser.add_argument('--sobel-dy', type=int, default=0, choices=[0, 1],
help='Sobel y方向导数阶数必须与sobel-dx之和等于1 (默认: 0)')
parser.add_argument('--sobel-ksize', type=int, default=-1,
help='Sobel核大小-1表示3x3 (默认: -1)')
# Laplacian参数
parser.add_argument('--laplacian-ksize', type=int, default=1,
help='Laplacian核大小 (默认: 1)')
parser.add_argument('--laplacian-scale', type=float, default=1.0,
help='Laplacian尺度参数 (默认: 1.0)')
parser.add_argument('--laplacian-delta', type=float, default=0.0,
help='Laplacian delta参数 (默认: 0.0)')
# LoG参数
parser.add_argument('--log-sigma', type=float, default=1.0,
help='LoG高斯标准差 (默认: 1.0)')
parser.add_argument('--log-threshold', type=float, default=0.0,
help='LoG阈值0表示自动阈值 (默认: 0.0)')
parser.add_argument('--log-mode', default='reflect',
choices=['reflect', 'constant', 'nearest', 'mirror', 'wrap'],
help='LoG边界模式 (默认: reflect)')
# 梯度算法阈值参数
parser.add_argument('--gradient-threshold', type=float, default=0.0,
help='梯度阈值 (0-255)0表示不使用阈值 (默认: 0.0)')
parser.add_argument('--gradient-use-otsu', action='store_true',
help='对梯度图使用Otsu自动阈值')
# Canny参数
parser.add_argument('--canny-min-threshold', type=int, default=100,
help='Canny最小阈值 (默认: 100)')
parser.add_argument('--canny-max-threshold', type=int, default=200,
help='Canny最大阈值 (默认: 200)')
parser.add_argument('--canny-aperture-size', type=int, default=3, choices=[3, 5, 7],
help='Canny Sobel算子孔径大小 (默认: 3)')
parser.add_argument('--canny-l2gradient', action='store_true',
help='Canny使用L2范数计算梯度')
# 通用参数
parser.add_argument('--normalize-output', action='store_true',
help='将输出归一化到0-255范围')
parser.add_argument('--use-blur', action='store_true',
help='预先进行高斯模糊')
parser.add_argument('--blur-kernel-size', type=int, default=3,
help='模糊核大小,必须是奇数 (默认: 3)')
parser.add_argument('--blur-sigma', type=float, default=1.0,
help='模糊标准差 (默认: 1.0)')
# 其他选项
parser.add_argument('--show-methods', action='store_true',
help='显示所有可用方法的详细信息')
args = parser.parse_args()
# 显示方法信息
if args.show_methods:
EdgeDetector.print_method_info()
return 0
try:
print("=" * 60)
print("高光谱图像边缘检测工具")
print("=" * 60)
print(f"输入文件: {args.input_path}")
print(f"检测方法: {', '.join(args.methods)}")
print(f"波段索引: {args.band_index}")
print()
# 确定是单方法还是批量处理
if len(args.methods) == 1:
# 单方法处理
print("执行单方法边缘检测...")
config = EdgeDetectionConfig(
input_path=args.input_path,
band_index=args.band_index,
method=args.methods[0],
output_path=args.output,
# 参数设置
sobel_dx=args.sobel_dx,
sobel_dy=args.sobel_dy,
sobel_ksize=args.sobel_ksize,
laplacian_ksize=args.laplacian_ksize,
laplacian_scale=args.laplacian_scale,
laplacian_delta=args.laplacian_delta,
log_sigma=args.log_sigma,
log_threshold=args.log_threshold,
log_mode=args.log_mode,
gradient_threshold=args.gradient_threshold,
gradient_use_otsu=args.gradient_use_otsu,
canny_min_threshold=args.canny_min_threshold,
canny_max_threshold=args.canny_max_threshold,
canny_aperture_size=args.canny_aperture_size,
canny_l2gradient=args.canny_l2gradient,
normalize_output=args.normalize_output,
use_blur=args.use_blur,
blur_kernel_size=args.blur_kernel_size,
blur_sigma=args.blur_sigma
)
detector = EdgeDetector(config)
detector.load_data()
edge_data, info = detector.apply_edge_detection()
output_path = args.output or f"edge_{args.methods[0]}_band{args.band_index}.dat"
detector.save_results(edge_data, info, output_path, args.methods[0])
else:
# 批量处理
print("执行批量边缘检测...")
# 确定波段列表
if args.batch_bands:
if len(args.batch_bands) != len(args.methods):
raise ValueError(f"波段数量 ({len(args.batch_bands)}) 必须与方法数量 ({len(args.methods)}) 相等")
bands = args.batch_bands
else:
# 如果没有指定波段,使用默认波段
bands = [args.band_index] * len(args.methods)
# 创建基础配置
config = EdgeDetectionConfig(
input_path=args.input_path,
band_index=args.band_index, # 这个会被batch_process覆盖
method=args.methods[0], # 这个会被batch_process覆盖
output_dir=args.output_dir,
# 参数设置
sobel_dx=args.sobel_dx,
sobel_dy=args.sobel_dy,
sobel_ksize=args.sobel_ksize,
laplacian_ksize=args.laplacian_ksize,
laplacian_scale=args.laplacian_scale,
laplacian_delta=args.laplacian_delta,
log_sigma=args.log_sigma,
log_threshold=args.log_threshold,
log_mode=args.log_mode,
gradient_threshold=args.gradient_threshold,
gradient_use_otsu=args.gradient_use_otsu,
canny_min_threshold=args.canny_min_threshold,
canny_max_threshold=args.canny_max_threshold,
canny_aperture_size=args.canny_aperture_size,
canny_l2gradient=args.canny_l2gradient,
normalize_output=args.normalize_output,
use_blur=args.use_blur,
blur_kernel_size=args.blur_kernel_size,
blur_sigma=args.blur_sigma
)
detector = EdgeDetector(config)
detector.load_data()
results = detector.batch_process(args.methods, bands, args.output_dir)
print("\n" + "=" * 60)
print("边缘检测完成!")
print("=" * 60)
except Exception as e:
print(f"✗ 处理失败: {e}")
import traceback
traceback.print_exc()
return 1
return 0
if __name__ == "__main__":
main()