OpenCV实现基于交叉双边滤波的红外可见光融合算法

news2025/1/14 2:23:17

1 算法原理

CBF是*Cross Bilateral Filter(交叉双边滤波)*的缩写,论文《IMAGE FUSION BASED ON PIXEL SIGNIFICANCE USING CROSS BILATERAL FILTER》。
论文中,作者使用交叉双边滤波算法对原始图像 A A A B B B 进行处理得到细节(detail)层,通过细节层得到权重系数,最后经过权重系数对原始图像 A A A B B B进行融合,得到最终的输出图像 F F F
在这里插入图片描述

2 算法实现

2.1 交叉双边滤波算法

双边波算法是局部的,非线性、非迭代的方法,使用高斯滤波器和颜色滤波器来对图像进行处理, 兼具平滑和保持边界的功能。这在我之前的专栏中有提到过,我在这就不在细说双边滤波,接下来说一下文中提到的交叉双边滤波。
交叉双边滤波算法考虑了 A A A图像的灰度层的相似性和几何层的闭合性来对 B B B 图像进行过滤,或者使用 B B B图像来对 A A A 图像进行过滤,输入为 A A A, B B B 两幅输入图像,假设使用 A A A 图像对 B B B图像进行过滤,论文中 σ s = 1.8 , σ r = 25 \sigma_s=1.8,\sigma_r=25 σs=1.8,σr=25 ,CBFlv滤波窗口大小为 11 ∗ 11 11*11 1111 那么:

B C B F ( p ) = 1 W ∑ q ∈ S ( e − ∣ p − q ∣ 2 2 σ s 2 ∗ e − ∣ A ( p ) − A ( q ) ∣ 2 2 σ r 2 ∗ B ( q ) ) B_{CBF}(p)=\frac{1}{W}\sum_{q\in S}(e^{-\frac{|p-q|^2}{2\sigma_s^2}}*e^{-\frac{|A(p)-A(q)|^2}{2\sigma_r^2}}*B(q)) BCBF(p)=W1qS(e2σs2pq2e2σr2A(p)A(q)2B(q))
W = ∑ q ∈ S e − ∣ p − q ∣ 2 2 σ s 2 ∗ e − ∣ A ( p ) − A ( q ) ∣ 2 2 σ r 2 W=\sum_{q\in S}e^{-\frac{|p-q|^2}{2\sigma_s^2}}*e^{-\frac{|A(p)-A(q)|^2}{2\sigma_r^2}} W=qSe2σs2pq2e2σr2A(p)A(q)2

同理,对于 A C B F ( p ) A_{CBF}(p) ACBF(p) 使用的是 B B B图像来对 A A A 图像进行过滤,由 $B_{CBF}、 A_{CBF} $以及原始图像 A A A B B B ,在原始图像的基础上减去交叉双边滤波得到的图像就是细节层图像,
A D = A − A C B F , B D = B − B C B F A_D=A-A_{CBF},B_D=B-B_{CBF} AD=AACBF,BD=BBCBF
文中作者给出交叉双边滤波有用的解释是:在多聚焦图像中, A A A 图像未聚焦的区域在图像 B B B 中是聚焦的区域,对图像 B B B 进行交叉双边滤波会使得对图像 B B B 的聚焦区域平滑的比图像 B B B 的非聚焦区域更强,原因是:图像 A A A 中的非聚焦区域由相近的颜色会使得过滤器更像是高斯过滤器。

最上面两幅为原始图像,中间两幅为CBF滤波后的图像,最下面两幅就是由原始图像-CBF图像得到的图像

2.2基于像素的融合策略

接下来的一步是使用细节图像计算融合权重。
选定窗口大小为 w ∗ w w∗w ww(论文中使用 5 ∗ 5 5*5 55),来方便计算权重,这个窗口划过的区域看做矩阵 $C_h{i,j}C_h{i,j} $的无偏估计:

c o v a r i a n c e ( X ) = E [ ( X − E [ X ] ) ( X − E ( X ) ) T ] covariance(X)=E[(X-E[X])(X-E(X))^T] covariance(X)=E[(XE[X])(XE(X))T]

C h i , j = ∑ k = 1 w ( x k − x ˉ ) ( x k − x ˉ ) T w − 1 C_h^{i,j}=\frac{\sum_{k=1}^w(x_k-\bar{x})(x_k-\bar{x})^T}{w-1} Chi,j=w1k=1w(xkxˉ)(xkxˉ)T

得到 C h i , j C_h^{i,j} Chi,j 大小为 w ∗ w w*w ww,然后计算矩阵 C h i , j C_h^{i,j} Chi,j 的特征值,并且将这些特征值相加就得到了水平方向区域细节强度,并记为 H d e t a i l S t r e n g t h HdetailStrength HdetailStrength
H d e t a i l S t r e n g t h ( i , j ) = ∑ k = 1 w e i g e n k HdetailStrength(i,j)=\sum_{k=1}^weigen_k HdetailStrength(i,j)=k=1weigenk

同理将 X X X的每一列看做观察量,将 X X X的行看做是变量,计算协方差矩阵 C v i , j C_v^{i,j} Cvi,j,并计算垂直方向区域细节强度 V d e t a i l S t r e n g t h VdetailStrength VdetailStrength :

V d e t a i l S t r e n g t h ( i , j ) = ∑ k = 1 w e i g e n k VdetailStrength(i,j)=\sum_{k=1}^weigen_k VdetailStrength(i,j)=k=1weigenk$

最终的权重为,水平和垂直方向的累加和:
w t ( i , j ) = H d e t a i l S t r e n g t ( i , j ) + V d e t a i l S t r e n g t ( i , j ) wt(i,j)=HdetailStrengt(i,j)+VdetailStrengt(i,j) wt(i,j)=HdetailStrengt(i,j)+VdetailStrengt(i,j)

得到 A A A B B B 图像的权重系数后,就可以按照权重系数就行融合:

F ( i , j ) = A ( i , j ) w t a ( i , j ) + B ( i , j ) w t b ( i , j ) w t a ( i , j ) + w t b ( i , j ) F(i,j)=\frac{A(i,j)wt_a(i,j)+B(i,j)wt_b(i,j)}{wt_a(i,j)+wt_b(i,j)} F(i,j)=wta(i,j)+wtb(i,j)A(i,j)wta(i,j)+B(i,j)wtb(i,j)

3 opencv实现代码

import numpy as np
import cv2
import argparse
import math

cov_wsize = 5
sigmas = 1.8
sigmar = 25
ksize = 11

def gaussian_kernel_2d_opencv(kernel_size = 11,sigma = 1.8):
    kx = cv2.getGaussianKernel(kernel_size,sigma)
    ky = cv2.getGaussianKernel(kernel_size,sigma)
    return np.multiply(kx,np.transpose(ky)) 

def bilateralFilterEx(img_r, img_v):
    #edge solved
    win_size = ksize//2
    img_r_copy = None
    img_v_copy = None
    img_r_copy = cv2.copyTo(img_r, None)
    img_v_copy = cv2.copyTo(img_v, None)
    img_r_cbf = np.ones_like(img_r, dtype=np.float32)
    img_v_cbf = np.ones_like(img_r, dtype=np.float32)
    img_r_copy = np.pad(img_r_copy, (win_size, win_size), 'reflect')
    img_v_copy = np.pad(img_v_copy, (win_size, win_size), 'reflect')
    gk = gaussian_kernel_2d_opencv()
    for i in range(win_size, win_size+img_r.shape[0]):
        for j in range(win_size, win_size+img_r.shape[1]):
            sumr1 = 0.
            sumr2 = 0.
            sumv1 = 0.
            sumv2 = 0.
            img_r_cdis = img_r_copy[i-win_size:i+win_size+1, j-win_size:j+win_size+1] *1.0- img_r_copy[i,j]*1.0
            img_v_cdis = img_v_copy[i-win_size:i+win_size+1, j-win_size:j+win_size+1] *1.0- img_v_copy[i,j]*1.0
            sumr1 = np.sum(np.exp(-img_v_cdis*img_v_cdis) *gk/ (2*sigmar*sigmar) )
            sumv1 = np.sum(np.exp(-img_r_cdis*img_r_cdis) *gk/ (2*sigmar*sigmar) )
            sumr2 = np.sum(np.exp(-img_v_cdis*img_v_cdis) *gk*img_r_copy[i-win_size:i+win_size+1, j-win_size:j+win_size+1] *1.0/ (2*sigmar*sigmar) )
            sumv2 = np.sum(np.exp(-img_r_cdis*img_r_cdis) *gk*img_v_copy[i-win_size:i+win_size+1, j-win_size:j+win_size+1] *1.0/ (2*sigmar*sigmar) )
            img_r_cbf[i-win_size,j-win_size] = sumr2 / sumr1
            img_v_cbf[i-win_size,j-win_size] = sumv2 / sumv1
    return (img_r*1. - img_r_cbf, img_v*1. - img_v_cbf)

def CBF_WEIGHTS(img_r_d, img_v_d):
    win_size = cov_wsize // 2
    img_r_weights = np.ones_like(img_r_d, dtype=np.float32)
    img_v_weights= np.ones_like(img_v_d, dtype=np.float32)
    img_r_d_pad = np.pad(img_r_d, (win_size, win_size), 'reflect')
    img_v_d_pad = np.pad(img_v_d, (win_size, win_size), 'reflect')
    for i in range(win_size, win_size+img_r_d.shape[0]):
        for j in range(win_size, win_size+img_r_d.shape[1]):
            npt_r = img_r_d_pad[i-win_size:i+win_size+1, j-win_size:j+win_size+1]
            npt_v = img_v_d_pad[i-win_size:i+win_size+1, j-win_size:j+win_size+1]
            npt_r_V = npt_r - np.mean(npt_r, axis=0)
            npt_r_V = npt_r_V*npt_r_V.transpose()
            npt_r_H = npt_r.transpose() - np.mean(npt_r, axis=1)
            npt_r_H = npt_r_H*npt_r_H.transpose()
            npt_v_V = npt_v - np.mean(npt_v, axis=0)
            npt_v_V = npt_v_V*npt_v_V.transpose()
            npt_v_H = npt_v.transpose() - np.mean(npt_v, axis=1)
            npt_v_H = npt_v_H*npt_v_H.transpose()
            img_r_weights[i-win_size,j-win_size] = np.trace(npt_r_H) + np.trace(npt_r_V) 
            img_v_weights[i-win_size,j-win_size] = np.trace(npt_v_H) + np.trace(npt_v_V) 
    return img_r_weights, img_v_weights

def CBF_GRAY(img_r, img_v):
    img_r_d, img_v_d = bilateralFilterEx(img_r, img_v)
    img_r_weights, img_v_weights = CBF_WEIGHTS(img_r_d, img_v_d)
    img_fused =(img_r*1. * img_r_weights + img_v*1.*img_v_weights) /(img_r_weights+img_v_weights)
    img_fused = cv2.convertScaleAbs(img_fused)
    return img_fused

def CBF_RGB(img_r, img_v):
    img_r_gray = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY)
    img_v_gray = cv2.cvtColor(img_v, cv2.COLOR_BGR2GRAY)
    return CBF_GRAY(img_r_gray, img_v_gray)

def CBF(_rpath, _vpath):
    img_r = cv2.imread(_rpath)
    img_v = cv2.imread(_vpath)
    if not isinstance(img_r, np.ndarray) :
        print('img_r is null')
        return
    if not isinstance(img_v, np.ndarray) :
        print('img_v is null')
        return
    if img_r.shape[0] != img_v.shape[0]  or img_r.shape[1] != img_v.shape[1]:
        print('size is not equal')
        return
    fused_img = None
    if len(img_r.shape)  < 3 or img_r.shape[2] ==1:
        if len(img_v.shape)  < 3 or img_v.shape[-1] ==1:
            fused_img = CBF_GRAY(img_r, img_v)
        else:
            img_v_gray = cv2.cvtColor(img_v, cv2.COLOR_BGR2GRAY)
            fused_img = CBF_GRAY(img_r, img_v)
    else:
        if len(img_v.shape)  < 3 or img_v.shape[-1] ==1:
            img_r_gray = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY)
            fused_img = CBF_GRAY(img_r_gray, img_v)
        else:
            fused_img = CBF_RGB(img_r, img_v)
    cv2.imshow('fused image', fused_img)

    cv2.imwrite("fused_image_2.jpg", fused_img)
    cv2.waitKey(0)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-r', type=str, default='ir2.png' ,help='input IR image path', required=False)
    parser.add_argument('-v', type=str, default= 'vr2.png',help='input Visible image path', required=False)
    args = parser.parse_args()
    CBF(args.r, args.v)

可见光图像
红外图像
融合结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目实战--网页五子棋(用户模块)(1)

接下来我将使用Java语言&#xff0c;和Spring框架&#xff0c;实现一个简单的网页五子棋。 主要功能包括用户登录注册&#xff0c;人机对战&#xff0c;在线匹配对局&#xff0c;房间邀请对局&#xff0c;积分排行版等。 这篇文件讲解用户模块的后端代码 1. 用户表与实体类 …

机器学习之随机森林算法实现和特征重要性排名可视化

随机森林算法实现和特征重要性排名可视化 目录 随机森林算法实现和特征重要性排名可视化1 随机森林算法1.1 概念1.2 主要特点1.3 优缺点1.4 步骤1.5 函数及参数1.5.1 函数导入1.5.2 参数 1.6 特征重要性排名 2 实际代码测试 1 随机森林算法 1.1 概念 是一种基于树模型的集成学…

MySQL存储引擎、索引、索引失效

MySQL Docker 安装 MySQL8.0&#xff0c;安装见docker-compose.yaml 操作类型 SQL 程序语言有四种类型&#xff0c;对数据库的基本操作都属于这四种类&#xff0c;分为 DDL、DML、DQL、DCL DDL(Dara Definition Language 数据定义语言)&#xff0c;是负责数据结构定义与数据…

WPF基础(1.1):ComboBox的使用

本篇文章介绍ComboBox的基本使用。 本篇文章的例子实现的功能&#xff1a;后端获取前端复选框中的选项之后&#xff0c;点击“确定”按钮&#xff0c;弹出一个MessageBox&#xff0c;显示用户选择的选项。 文章目录 1. 效果展示2. 代码逻辑2.1 前端代码2.2 后端代码 1. 效果展…

前端炫酷动画--文字(二)

目录 一、弧形边框选项卡 二、零宽字符 三、目录滚动时自动高亮 四、高亮关键字 五、文字描边 六、按钮边框的旋转动画 七、视频文字特效 八、立体文字特效让文字立起来 九、文字连续光影特效 十、重复渐变的边框 十一、磨砂玻璃效果 十二、FLIP动画 一、弧形边框…

android 官网刷机和线刷

nexus、pixel可使用google官网线上刷机的方法。网址&#xff1a;https://flash.android.com/ 本文使用google线上刷机&#xff0c;将Android14 刷为Android12 以下是失败的线刷经历。 准备工作 下载升级包。https://developers.google.com/android/images?hlzh-cn 注意&…

25/1/12 嵌入式笔记 学习esp32

了解了一下位选线和段选线的知识&#xff1a; 位选线&#xff1a; 作用&#xff1a;用于选择数码管的某一位&#xff0c;例如4位数码管的第1位&#xff0c;第2位&#xff09; 通过控制位选线的电平&#xff08;高低电平&#xff09;&#xff0c;决定当前哪一位数码管处于激活状…

探秘block原理

01 概述 在iOS开发中&#xff0c;block大家用的都很熟悉了&#xff0c;是iOS开发中闭包的一种实现方式&#xff0c;可以对一段代码逻辑进行封装&#xff0c;使其可以像数据一样被传递、存储、调用&#xff0c;并且可以保存相关的上下文状态。 很多block原理性的文章都比较老&am…

【Docker】入门教程

目录 一、Docker的安装 二、Docker的命令 Docker命令实验 1.下载镜像 2.启动容器 3.修改页面 4.保存镜像 5.分享社区 三、Docker存储 1.目录挂载 2.卷映射 四、Docker网络 1.容器间相互访问 2.Redis主从同步集群 3.启动MySQL 五、Docker Compose 1.命令式安装 …

Bootstrap 前端 UI 框架

Bootstrap官网&#xff1a;Bootstrap中文网 铂特优选 Bootstrap 下载 点击进入中文文档 点击下载 生产文件是开发响应式网页应用&#xff0c;源码是底层逻辑代码&#xff0c;因为是要制作响应式网页&#xff0c;所以下载开发文件 引入 css 文件&#xff0c; bootstrap.css 和 …

Docker与微服务实战2-基础篇

1.学习一门新技术的理念 1.是什么 2.能干吗 3.去哪下载 4.怎么玩 5.永远的helloworld跑起来一次 AB法则 before 与 after 的对比 2.为什么会有Docker出现 3.docker理念 解决了运行环境和配置问题的软件容器&#xff0c;方便做持续集成并有助于整体发布的容器虚拟化…

蓝桥杯_B组_省赛_2022(用作博主自己学习)

题目链接算法11.九进制转十进制 - 蓝桥云课 进制转换 21.顺子日期 - 蓝桥云课 时间与日期 31.刷题统计 - 蓝桥云课 时间与日期 41.修剪灌木 - 蓝桥云课 思维 51.X 进制减法 - 蓝桥云课 贪心 61.统计子矩阵 - 蓝桥云课 二维前缀和 71.积木画 - 蓝桥云课 动态规划 82.扫雷 - 蓝桥…

CES 2025|美格智能高算力AI模组助力“通天晓”人形机器人震撼发布

当地时间1月7日&#xff0c;2025年国际消费电子展&#xff08;CES 2025&#xff09;在美国拉斯维加斯正式开幕。美格智能合作伙伴阿加犀联合高通在展会上面向全球重磅发布人形机器人原型机——通天晓&#xff08;Ultra Magnus&#xff09;。该人形机器人内置美格智能基于高通QC…

PyMysql 01|(包含超详细项目实战)连接数据库、增删改查、异常捕获

目录 一、数据库操作应用场景 二、安装PyMysql 三、事务的概念 四、数据库的准备 五、PyMysql连接数据库 1、建立连接方法 2、入门案例 六、PyMysql操作数据库 1、数据库查询 1️⃣查询操作流程 2️⃣cursor游标 ​3️⃣查询常用方法 4️⃣案例 5️⃣异常捕获 …

了解Node.js

Node.js是一个基于V8引擎的JavaScript运行时环境&#xff0c;它允许JavaScript代码在服务器端运行&#xff0c;从而实现后端开发。Node.js的出现&#xff0c;使得前端开发人员可以利用他们已经掌握的JavaScript技能&#xff0c;扩展技能树并成为全栈开发人员。本文将深入浅出地…

Unreal Engine 5 (UE5) Metahuman 的头部材质

在图中&#xff0c;你展示了 Unreal Engine 5 (UE5) Metahuman 的头部材质部分&#xff0c;列出了头部材质的多个元素。以下是对每个部分的解释&#xff1a; 材质解释 Element 0 - MI_HeadSynthesized_Baked 作用&#xff1a; 这是 Metahuman 的主要头部材质&#xff0c;控制整…

《自动驾驶与机器人中的SLAM技术》ch7:基于 ESKF 的松耦合 LIO 系统

目录 基于 ESKF 的松耦合 LIO 系统 1 坐标系说明 2 松耦合 LIO 系统的运动和观测方程 3 松耦合 LIO 系统的数据准备 3.1 CloudConvert 类 3.2 MessageSync 类 4 松耦合 LIO 系统的主要流程 4.1 IMU 静止初始化 4.2 ESKF 之 运动过程——使用 IMU 预测 4.3 使用 IMU 预测位姿进…

SQL从入门到实战-2

高级语句 窗口函数 排序窗口函数 例题二十九 select yr,party,votes, rank() over (PARTITION BY yr ORDER BY votes desc) as pson from ge where constituency S14000021 order by party,yr 偏移分析函数 例题三十 select name,date_format(whn,%Y-%m-%d) data, confi…

Spring Security单点登录

本文介绍了Spring Security单点登录的概念和基本原理。单点登录是指用户只需登录一次&#xff0c;即可在多个相互信任的系统中实现无缝访问和授权。通过Spring Security框架的支持&#xff0c;可以实现有效的用户管理和权限控制。最后&#xff0c;本文提供了实际应用案例&#…

LKT4304新一代算法移植加密芯片,守护物联网设备和云服务安全

凌科芯安作为一家在加密芯片领域深耕18年的企业&#xff0c;主推的LKT4304系列加密芯片集成了身份认证、算法下载、数据保护和完整性校验等多方面安全防护功能&#xff0c;可以为客户的产品提供一站式解决方案&#xff0c;并且在调试和使用过程提供全程技术支持&#xff0c;针对…