FEATURED · 精选文章

基于小波纹理特征的无人机离岸流识别方法

发布时间 / 2026/8/30 2:58:57
来源 / 创域科博编辑部
栏目 / 资讯中心
基于小波纹理特征的无人机离岸流识别方法 1. 背景与核心概念1.1 离岸流监测为什么这么难每年夏季海滨浴场最危险的自然灾害并不是台风也不是大浪而是一种看起来很平静的水流——离岸流Rip Current。离岸流是沿海地区由于波浪破碎后水体堆积在特定地形条件下形成的一股狭窄而强劲的向外海流动的水流。它的宽度通常只有几米到几十米但流速可达每秒 1 到 2 米超过了多数人的游泳速度。更棘手的是离岸流在影像上往往表现为“碎波带中的一道缺口”或“浑浊水带”普通游客很难分辨甚至专业救生员也需要借助特定经验才能快速判断。传统离岸流监测手段包括救生员目视观察经验依赖强存在盲区和疲劳问题海岸固定摄像头覆盖范围有限夜间和恶劣天气效果差浮标与海流计只能获取单点数据无法覆盖整个海滩数值模拟依赖地形和水文参数计算复杂。这引出一个非常实际的问题能不能用更高效的手段从空中持续、自动地识别离岸流指示物1.2 无人机与遥感图像处理的结合无人机UAVUnmanned Aerial Vehicle近年来在环境监测领域发展非常快。相比卫星遥感无人机可以在低空获取高分辨率影像空间分辨率可以达到厘米级。相比地面摄像头无人机又具备灵活机动的优势可以沿特定航线对海滩区域进行周期性巡检。当无人机飞到海滩上空时它的可见光相机可以捕捉到以下与离岸流相关的指示物碎波带中的暗色水带泥沙或浑浊水体向外海延伸的通道两侧波浪破碎明显而中间波浪不破碎的区域沙坝地形在水下的纹理差异。这些指示物的共同特征是它们不依赖单一像素的颜色值而是在一定空间范围内表现出特定的“纹理”模式。由于无人机影像分辨率高、海浪噪声大、光照变化明显直接做像素级分类很容易出现误检因此需要引入更稳健的特征描述方法。1.3 小波变换与纹理特征的作用纹理特征是图像处理中描述“局部空间结构”的重要工具。与颜色特征不同纹理特征关注的是像素之间的空间关系、频率分布和方向性。小波变换Wavelet Transform是一种时频分析工具它能把图像分解为不同的频率子带。低频分量对应图像的平滑部分高频分量对应边缘、纹理和噪声。相比傅里叶变换小波变换的优势在于它既保留频率信息又保留空间位置信息非常适合分析海洋影像中这种“局部有规律、全局有变化”的纹理模式。将小波变换和纹理特征结合技术路线是这样的对无人机航拍影像做预处理对图像做二维离散小波变换2D DWT得到近似系数和细节系数基于小波系数计算能量、熵、标准差、对比度等纹理特征以滑动窗口方式生成纹理特征图使用阈值或机器学习分类器识别离岸流指示物区域。这套方案的核心价值在于它不依赖单帧图像的绝对颜色值而是关注频域上的结构规律因此对光照变化、水色差异有更强的鲁棒性。2. 总体技术方案与流程2.1 技术路线总览整个离岸流指示物识别系统可以拆分为以下六个环节环节输入输出核心技术数据采集海滩区域无人机航拍影像航线规划、云台控制图像预处理原始影像纠正后的灰度图畸变校正、去噪、增强小波分解灰度图多尺度小波系数二维离散小波变换特征提取小波系数纹理特征向量能量、熵、标准差等特征分析与识别特征向量离岸流指示物区域阈值筛选 / 分类器结果可视化识别区域标注影像热力图、轮廓绘制2.2 为什么选用小波特征而不是纯深度学习方法看到这里可能有读者会问为什么不直接用深度学习目标检测模型比如 YOLO 或者 Faster R-CNN这个问题很实际。深度学习当然可以做离岸流识别但它有三个现实瓶颈标注数据难获取离岸流是动态现象专家标注成本极高很难积累上千张高质量标注图可解释性不足救生员和海洋管理人员需要知道“为什么判定这里是离岸流”纯黑盒模型的信任度有限部署成本高GPU 边缘设备价格不低而小波特征提取只需要 CPU 就能跑实时处理。小波特征方案属于传统机器学习与信号处理的融合它依赖的数学原理清晰、计算成本低、可解释性强。在实际工程中我们可以用小波纹理特征做第一级筛选再用少量深度模型做确认这样既兼顾性能又控制成本。2.3 核心创新点本文方案的创新点主要体现在三个方面多尺度分析小波变换将图像分解在不同分辨率层能同时捕捉离岸流大尺度水带结构和小尺度波浪纹理组合特征不仅使用灰度统计量还结合小波系数在不同方向的细节分量使特征对方向性纹理更敏感工程可行性整个特征提取流程可以封装成 Python 工具库在普通办公电脑上即可处理单张航拍影像。3. 环境准备与依赖安装3.1 开发环境说明本文的实验环境以常见配置为例具体版本请根据你的项目实际情况调整。核心目标是把处理流程跑通而不是锁定在某个特定版本上。操作系统Windows 10/11、Ubuntu 20.04 或 macOS本文代码为跨平台实现编程语言Python 3.8 及以上主要依赖库NumPy、OpenCV、PyWavelets、scikit-image、scikit-learn、Matplotlib开发工具PyCharm、VS Code 或 Jupyter Notebook 均可。版本注意PyWavelets的 API 在 1.x 和 1.4 之后有些细节差异建议使用较新版本本文代码使用pywt.dwt2和pywt.wavedec2这两个函数在近几个大版本中保持稳定。3.2 安装依赖建议先创建虚拟环境再安装依赖python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate然后安装依赖pip install numpy opencv-python PyWavelets scikit-image scikit-learn matplotlib如果下载速度慢可以指定国内镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy opencv-python PyWavelets scikit-image scikit-learn matplotlib安装完成后可以通过以下命令验证环境import numpy as np import cv2 import pywt import skimage import sklearn print(NumPy:, np.__version__) print(OpenCV:, cv2.__version__) print(PyWavelets:, pywt.__version__) print(scikit-image:, skimage.__version__) print(scikit-learn:, sklearn.__version__)4. 小波变换与纹理特征原理拆解4.1 二维离散小波变换一维小波变换处理的是信号序列而图像是二维信号因此需要使用二维离散小波变换2D DWT。它通过行和列两个方向的滤波和降采样将图像分解为四个子带cA近似系数LL对应低频信息反映图像的整体结构cH水平细节LH反映水平方向的高频变化cV垂直细节HL反映垂直方向的高频变化cD对角细节HH反映对角方向的高频变化。可以理解为近似系数是原图的“缩略版”而三个细节系数分别抓住了不同方向上的纹理和边缘。使用 PyWavelets 的dwt2函数做一层分解代码如下import pywt import numpy as np # 假设 img 是已经读取的灰度图 cA, (cH, cV, cD) pywt.dwt2(img, waveletdb2, modesymmetric) print(cA shape:, cA.shape) print(cH shape:, cH.shape) print(cV shape:, cV.shape) print(cD shape:, cD.shape)每个子带的尺寸大约是原图的一半。如果原图是 1024×1024那么每个子带就是 512×512。4.2 为什么多尺度纹理特征能反映离岸流指示物离岸流指示物在空中的视觉表现有几个特点水面浑浊带与周围海水之间有明显边界但这种边界不是直线而是不规则曲线离岸流通道内的波浪纹理比两侧更弱碎波带出现断裂通道内水体与两侧水体在小波高频子带的能量上有差异。小波变换的多尺度特性正好匹配这些特点。第一层分解能捕捉细小波浪纹理第二层、第三层分解能逐渐体现水带的结构形态。通过多层分解后我们可以得到类似“图像金字塔”的特征集合每一层描述不同尺度的纹理强度。4.3 常用纹理特征指标基于小波系数我们可以计算以下特征特征名称计算公式含义能量sum(系数^2) / N反映纹理的强弱程度熵-sum(p * log(p))反映纹理的混乱程度标准差std(系数)反映纹理的对比强度均值mean(系数)反映系数整体水平对比度sum((i-j)^2 * p(i,j))反映局部差异其中能量和熵是最常用的两个指标。能量高说明纹理规则、过渡稳定熵高说明纹理杂乱、随机性强。离岸流通道内部的水体相对平滑高频系数能量会低于两侧波浪破碎区域而通道边界处熵会明显升高。5. 完整实战从无人机图像到纹理特征提取这一节我们用 Python 完整实现一个离岸流指示物纹理特征提取流程。为方便演示代码以单张无人机影像为输入使用滑动窗口遍历整张图像对每个窗口计算小波纹理特征。5.1 项目结构建议按照下面的目录组织代码便于扩展rip_current_monitor/ ├── config.py # 配置文件 ├── preprocess.py # 图像预处理模块 ├── wavelet_features.py# 小波纹理特征提取模块 ├── visualize.py # 可视化模块 ├── main.py # 主流程 └── data/ └── demo_beach.jpg # 示例无人机影像5.2 图像预处理模块无人机原始航拍影像往往包含海陆交接、沙滩高亮区域等干扰信息。预处理阶段我们只做两个关键操作转灰度并归一化。这样可以降低后续计算量同时保持纹理结构信息。# 文件路径preprocess.py import cv2 import numpy as np def load_and_preprocess(image_path, target_sizeNone): 读取无人机影像并转为灰度图。 :param image_path: 图像路径 :param target_size: 可选重新缩放的尺寸 (width, height) :return: 归一化后的灰度图float32范围 [0, 1] # 读取原图 img cv2.imread(image_path) if img is None: raise FileNotFoundError(f无法读取图像: {image_path}) # 转灰度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 可选缩放 if target_size is not None: gray cv2.resize(gray, target_size, interpolationcv2.INTER_AREA) # 归一化到 [0, 1] gray gray.astype(np.float32) / 255.0 return gray为什么做归一化因为不同无人机、不同光照条件下影像的绝对灰度值差异很大。归一化之后后续小波系数的尺度是一致的特征值才有可比性。5.3 小波纹理特征提取模块这是整个流程的核心模块。我们定义一个函数对输入图像做二维离散小波分解并从每个子带提取统计特征。# 文件路径wavelet_features.py import numpy as np import pywt def extract_wavelet_features(block, waveletdb2, level2): 对图像块做多层小波分解提取纹理特征。 :param block: 输入图像块二维数组 :param wavelet: 小波基名称常用 db2, db4, sym3 :param level: 分解层数 :return: 特征字典 features {} # 多层二维小波分解 coeffs pywt.wavedec2(block, waveletwavelet, levellevel, modesymmetric) # coeffs[0] 是最低层近似系数 # 从高到低依次为 cH1, cV1, cD1, cH2, cV2, cD2, ... for i in range(1, level 1): cH, cV, cD coeffs[i] for name, coeff in [(cH, cH), (cV, cV), (cD, cD)]: coeff_flat coeff.flatten() prefix flevel{i}_{name} features[f{prefix}_energy] np.sum(coeff_flat ** 2) / len(coeff_flat) features[f{prefix}_std] np.std(coeff_flat) features[f{prefix}_mean] np.mean(coeff_flat) # 计算熵 hist, _ np.histogram(coeff_flat, bins32, range(-1, 1)) hist hist / hist.sum() non_zero hist[hist 0] features[f{prefix}_entropy] -np.sum(non_zero * np.log2(non_zero)) return features这段代码的关键点pywt.wavedec2返回一个列表第 0 个元素是近似系数后续元素是按层组织的三个细节系数对每个细节系数我们计算能量、标准差、均值、熵四个统计量2 层分解会产生 6 组特征每层 3 个方向 × 4 个统计量共 24 维特征。5.4 滑动窗口特征图生成单张航拍影像通常是几千像素宽。直接对全图计算特征没有意义因为离岸流是一个局部现象。我们用一个固定大小的窗口例如 64×64 或 128×128在图像上滑动对每个窗口提取特征形成特征图。# 文件路径wavelet_features.py追加 def compute_feature_map(image, window_size64, stride32, waveletdb2, level2): 在整张图像上滑动窗口提取小波纹理特征。 :param image: 二维灰度图 :param window_size: 窗口大小 :param stride: 滑动步长 :param wavelet: 小波基 :param level: 分解层数 :return: (feature_maps, row_list, col_list) h, w image.shape rows range(0, h - window_size 1, stride) cols range(0, w - window_size 1, stride) feature_maps {} first True for r in rows: for c in cols: block image[r:r window_size, c:c window_size] feats extract_wavelet_features(block, wavelet, level) if first: for key in feats.keys(): feature_maps[key] [] first False for key, value in feats.items(): feature_maps[key].append(value) n_rows len(rows) n_cols len(cols) # 把特征列表转换为二维数组特征图 for key in feature_maps: feature_maps[key] np.array(feature_maps[key]).reshape(n_rows, n_cols) return feature_maps, rows, cols5.5 可视化模块为了直观验证特征是否有效我们把某个特征图显示成热力图叠加在原图上。# 文件路径visualize.py import matplotlib.pyplot as plt import numpy as np import cv2 def show_feature_map(original, feature_map, rows, cols, titleFeature Map): 显示原始图像和提取的特征图。 fig, axes plt.subplots(1, 2, figsize(12, 5)) axes[0].imshow(original, cmapgray) axes[0].set_title(Original Image) axes[0].axis(off) # 将特征图缩放到原始图像尺度 feature_map_resized cv2.resize( feature_map.astype(np.float32), (original.shape[1], original.shape[0]), interpolationcv2.INTER_LINEAR ) im axes[1].imshow(feature_map_resized, cmapjet) axes[1].set_title(title) axes[1].axis(off) plt.colorbar(im, axaxes[1], fraction0.046, pad0.04) plt.tight_layout() plt.show()5.6 主流程调用# 文件路径main.py import cv2 from preprocess import load_and_preprocess from wavelet_features import compute_feature_map from visualize import show_feature_map def main(): image_path data/demo_beach.jpg img load_and_preprocess(image_path, target_size(1024, 1024)) feature_maps, rows, cols compute_feature_map( img, window_size64, stride32, waveletdb2, level2 ) print(特征图维度行数列数:, len(rows), len(cols)) print(提取的特征名称:) for key in feature_maps.keys(): print( -, key) # 显示第一个方向的能量特征图 key level1_cH_energy if key in feature_maps: show_feature_map(img, feature_maps[key], rows, cols, titlekey) if __name__ __main__: main()运行python main.py预期输出类似特征图维度行数列数: 30 30 提取的特征名称: - level1_cH_energy - level1_cH_std - level1_cH_mean - level1_cH_entropy ...这里的特征维度是 30×30因为 1024 像素图像、64 窗口、32 步长会产生(1024-64)/32 1 30个位置。6. 基于纹理特征做离岸流指示物识别提取特征之后下一步就是“怎么用这些特征找到离岸流指示物”。这里提供两种思路一种是传统阈值法适合快速原型验证另一种是机器学习分类法适合更复杂的场景。6.1 阈值筛选法离岸流通道内的高频能量通常低于周围碎波区我们可以直接对能量特征图做阈值分割# 文件路径detect.py import cv2 import numpy as np def detect_by_threshold(feature_map, threshold_ratio0.6): 基于能量特征图的阈值筛选。 threshold_ratio: 低于最大能量的多少比例时视为疑似离岸流区域。 max_val feature_map.max() threshold max_val * threshold_ratio mask (feature_map threshold).astype(np.uint8) return mask def postprocess_mask(mask, min_area20): 对二值掩码做形态学后处理。 # 去除小连通区域 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(mask, connectivity8) for i in range(1, num_labels): if stats[i, cv2.CC_STAT_AREA] min_area: mask[labels i] 0 # 形态学闭运算 kernel np.ones((5, 5), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) return mask需要注意阈值法只能作为快速筛选手段。实际海况复杂单一阈值很容易产生误检必须结合多特征综合判断。6.2 机器学习分类法更可靠的做法是把提取到的特征送入一个轻量级分类器例如随机森林或逻辑回归。这一步骤需要带标签的训练数据。假设我们已经有了一批带标注的海滩影像块标注为“离岸流”和“非离岸流”训练代码如下# 文件路径train_classifier.py import numpy as np from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from wavelet_features import extract_wavelet_features import cv2 def load_training_data(image_paths, labels, window_size64): 从带标签的图像块中提取特征。 image_paths: 图像块路径列表 labels: 对应的标签1 表示离岸流0 表示非离岸流 X_list [] y_list [] for path, label in zip(image_paths, labels): img cv2.imread(path, cv2.IMREAD_GRAYSCALE) if img is None: continue img img.astype(np.float32) / 255.0 # 图像块必须大于等于 window_size h, w img.shape if h window_size or w window_size: continue block img[:window_size, :window_size] feats extract_wavelet_features(block, waveletdb2, level2) X_list.append(list(feats.values())) y_list.append(label) X np.array(X_list) y np.array(y_list) return X, y def train_random_forest(X, y): X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) clf RandomForestClassifier( n_estimators200, max_depth10, random_state42, n_jobs-1 ) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(classification_report(y_test, y_pred, target_names[非离岸流, 离岸流])) return clf在实际项目中训练样本的获取需要与海洋管理部门合作利用历史巡查影像和人工标注建立数据集。这是整个系统落地过程中最花时间但也最关键的环节。7. 常见问题与排查思路在编写和运行小波纹理特征提取代码时最容易遇到以下几类问题。7.1 PyWavelets 子带尺寸不一致导致报错错误现象当图像块尺寸不是 2 的倍数时不同层的小波系数形状不统一后续计算熵或标准差时出现对齐错误。可能原因wavedec2在分解时如果图像尺寸是奇数最低层与次低层的形状可能相差 1。解决方案在提取特征前确保输入窗口尺寸是2^level的整数倍。例如 level2 时窗口尺寸选用 64×64 或 128×128。# 在 extract_wavelet_features 前强制检查 assert block.shape[0] % (2 ** level) 0 assert block.shape[1] % (2 ** level) 07.2 特征图中出现大量 NaN 或 0错误现象计算熵时np.histogram的某些 bin 没有数据导致 log 计算 0。可能原因灰度归一化后高频小波系数大量集中在 0 附近直方图分布极不均匀。解决方案在计算熵之前过滤 0 值概率项或者增加直方图 bin 数量来提高统计稳定性。non_zero hist[hist 0] entropy -np.sum(non_zero * np.log2(non_zero))7.3 特征图分辨率太低难以叠加到原图错误现象使用 64 窗口、32 步长时1024×1024 图像只有 30×30 的特征图可视化时边缘模糊。可能原因滑动步长过大。解决方案减小步长到 8 或 16特征图分辨率会显著提升但计算时间也会成倍增加。实际生产中可以用重叠滑窗配合多线程优化。7.4 小波基选择的影响小波基特点适用场景Haar最简单计算快但不够平滑快速原型验证Daubechies (db2/db4)正交、紧支撑常用于图像纹理本文默认推荐Symlets (sym3/sym5)近似对称相位畸变小更注重形状保持时小波基没有绝对的好坏建议在验证集上对比特征分布差异后再做选择。8. 最佳实践与工程建议8.1 数据采集阶段无人机巡检航线需要提前规划尽量保持同一高度、同一角度减少透视变形对特征的干扰不同时间段早晨、中午、傍晚光照差异大建议同一区域分多个时段采集增加特征鲁棒性飞行作业必须遵守当地航空法规获得合法授权避免在禁飞区飞行每次飞行记录拍摄参数高度、焦距、云台角度、天气、风速这些元数据对后期特征标准化非常重要。8.2 特征提取阶段图像预处理时先做畸变校正和几何配准再做灰度化和归一化不要只使用单一特征将水平、垂直、对角三个方向的小波特征全部保留因为它们分别捕捉不同纹理方向对特征图做平滑例如高斯滤波可以减少海浪运动造成的局部噪声如果图像中有明显的陆地、沙滩区域建议先使用海岸线检测或 ROI 掩码去掉非海面区域因为沙滩纹理特征与水体差异很大会对分类造成干扰。8.3 模型部署阶段小波变换是计算密集型操作但对 CPU 友好。在边缘设备上可以先用 OpenCV 的dnn模块做分割再用小波特征做验证如果实时性要求高可以考虑用 GPU 加速小波卷积或者预先将小波滤波器固化为卷积核建议维护一个特征基线的版本管理当无人机相机参数变化时所有历史特征需要重新校准。9. 总结与学习路线本文围绕“UAV-Based Environmental Monitoring of Rip-Current Indicators Using Wavelet-Derived Texture Features”这个课题完整拆解了从无人机影像到小波纹理特征提取再到离岸流指示物识别的技术流程。核心内容包括离岸流监测的实际困难与无人机影像的优势二维离散小波变换的基本原理与子带含义能量、熵、标准差等纹理特征的计算方式完整的 Python 代码实现预处理、小波分解、滑动窗口特征提取、可视化阈值筛选与随机森林两种识别思路常见报错与工程化建议。下一步如果你想继续深入可以优先学习这几个方向小波包变换相比离散小波变换小波包能对高频部分进一步分解可能提取到更细致的纹理信息方向性特征结合 Gabor 滤波器与小波特征形成多通道特征集时序分析离岸流是一个动态过程可以获取同一区域不同时刻的影像序列做时序上的变化检测边缘部署把特征提取流程打包成服务部署在无人机地面站或云服务器上实现端到端的自动化巡检。最后提醒一句离岸流识别是关系到公众安全的应用场景任何模型在正式投入使用前都必须经过大量现场验证并且与专业救生人员共同评估。技术只是辅助工具真正的判断和决策永远需要人的参与。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻