增加模块;增加主调用命令
This commit is contained in:
BIN
edge_detect_method/__pycache__/edge_detect.cpython-312.pyc
Normal file
BIN
edge_detect_method/__pycache__/edge_detect.cpython-312.pyc
Normal file
Binary file not shown.
841
edge_detect_method/edge_detect.py
Normal file
841
edge_detect_method/edge_detect.py
Normal file
@ -0,0 +1,841 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user