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-255),0表示不使用阈值 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()