openISP学习11-BNF — Bilateral Noise Filtering(双边滤波降噪)

文章目录

1.算法原理

双边滤波(Bilateral Filter)结合了空间域高斯滤波和值域相似性加权,在平滑噪声的同时保留边缘。

经典双边滤波权重:

复制代码
w(i,j) = G_spatial(||p-q||) × G_range(|I(p)-I(q)|)

上面双边滤波权重公式,它定义了如何计算邻域内像素对中心像素的"影响力(权重)"。

说白了,它要表达了一个核心思想:一个像素要想对中心像素产生影响,它必须同时满足"离得近"和"长得像"这两个条件。我们从下面2个方面再理解下:

1. 符号的含义

  • w(i,j)) :最终计算出的综合权重。它决定了邻域内的像素 q 对中心像素 p 的影响程度。

  • p和q : p 代表图像中的中心像素(正在被处理的像素), q 代表中心像素周围的邻域像素。

  • ∣∣p−q∣∣ :代表像素 p 和 q 之间的空间距离(即它们在图像上的物理坐标距离,比如横纵坐标的差值)。

  • ∣I( p)−I(q)∣ :代表像素 p 和 q 之间的像素值差异( I 代表强度或颜色,比如灰度值从 0 到 255 的差异)。

2.为什么要相乘

上面已经从空间上和值上分别给**"离得近"和"长得像"的元素给与最大的权重,那么他们相乘,意味着一票否决制,有点"与"**运算的味道.

  • 如果两个像素离得很近,但颜色差异很大(比如跨越了物体边缘), G_range的值会非常小,导致最终权重 w 趋近于 0。
  • 如果两个像素颜色很像,但离得非常远, G_spatial的值会非常小,最终权重 w 同样趋近于 0。
  • 只有当两个像素既在空间上相邻,又在颜色上相似时,它们的乘积才会产生显著的权重。
算法值域权重量化

openISP 默认值:差值范围 rthres=[128,32,8],权重rw=[0,8,16,32](差异越大权重越小)

在算法中计算的是5x5邻域内像素值的权重,所以最后得到也是一个5x5的矩阵. 下面就是判断中心像素域5x5邻域像素插值,根据插值范围,分别赋予不同的rw权重.

复制代码
if   rdiff >= rthres[0]:         rw = rw[0]   (差异很大,权重最小)
elif rdiff ∈ [rthres[1], rthres[0]): rw = rw[1]
elif rdiff ∈ [rthres[2], rthres[1]): rw = rw[2]
elif rdiff < rthres[2]:          rw = rw[3]   (差异很小,权重最大)
空间权重(dw 矩阵)

符合高斯分布,中间像素的权重自然最大

复制代码
 8  12  32  12   8
12  64 128  64  12
32 128 1024 128  32    (高斯分布近似)
12  64 128  64  12
 8  12  32  12   8

高斯函数:

2.算法核心代码

实现细节
  • 仅处理 Y(亮度)通道,5×5 窗口
  • 逐像素双重循环,逐邻居计算值域差,计算量大
  • padding 2 像素(reflect 模式)
关键参数
参数 说明
dw 5×5 空间距离权重矩阵
rw 4 级值域权重 rw0, rw1, rw2, rw3
rthres 值域阈值 rthres0, rthres1, rthres2
clip 输出最大值
最终像素输出

output = Σ(pixel × dw × rw) / Σ(dw × rw)

算法代码实现
python 复制代码
class BNF:
    'Bilateral Noise Filtering'

    def __init__(self, img, dw, rw, rthres, clip):
        self.img = img
        self.dw = dw
        self.rw = rw
        self.rthres = rthres
        self.clip = clip

    def padding(self):
        img_pad = np.pad(self.img, (2, 2), 'reflect')
        return img_pad

    def clipping(self):
        np.clip(self.img, 0, self.clip, out=self.img)
        return self.img

    def execute(self):
        img_pad = self.padding()
        img_pad = img_pad.astype(np.uint16)
        raw_h = self.img.shape[0]
        raw_w = self.img.shape[1]
        bnf_img = np.empty((raw_h, raw_w), np.uint16)
        rdiff = np.zeros((5,5), dtype='uint16')
        for y in range(img_pad.shape[0] - 4):
            for x in range(img_pad.shape[1] - 4):
                #print("[x,y]:["+str(x)+','+str(y)+']')
                for i in range(5):
                    for j in range(5):
                        rdiff[i,j] = abs(img_pad[y+i,x+j].astype(int) - img_pad[y+2, x+2].astype(int))
                        # rdiff[i,j] = abs(img_pad[y+i,x+j] - img_pad[y+2, x+2])
                        if rdiff[i,j] >= self.rthres[0]:
                            rdiff[i,j] = self.rw[0]
                        elif rdiff[i,j] < self.rthres[0] and rdiff[i,j] >= self.rthres[1]:
                            rdiff[i,j] = self.rw[1]
                        elif rdiff[i,j] < self.rthres[1] and rdiff[i,j] >= self.rthres[2]:
                            rdiff[i,j] = self.rw[2]
                        elif rdiff[i,j] < self.rthres[2]:
                            rdiff[i,j] = self.rw[3]
                weights = np.multiply(rdiff, self.dw)
                bnf_img[y,x] = np.sum(np.multiply(img_pad[y:y+5,x:x+5], weights[:,:])) / np.sum(weights)
        self.img = bnf_img
        return self.clipping()

3.测试代码

测试是用千问帮忙写的,代码生产力一个张30x30随机图,然后使用BNF算法后,能看到图像平滑了许多.

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
from bnf import *

# 假设 BNF 类已经存在于当前环境(即您提供的代码)
# from your_module import BNF 

# 1. 准备测试图像 (强烈建议裁剪或缩放,否则纯Python循环会运行极慢)
img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
if img is None:
    # 如果没有图片,生成一张带噪点的测试图
    img = np.random.randint(0, 256, (30, 30), dtype=np.uint8)
else:
    # 裁剪为 100x100 大小以加速测试
    img = img[0:100, 0:100] 

# 2. 配置双边滤波参数 (与算法原理一致)
dw = np.array([
    [8, 12, 32, 12, 8],
    [12, 64, 128, 64, 12],
    [32, 128, 1024, 128, 32],
    [12, 64, 128, 64, 12],
    [8, 12, 32, 12, 8]
], dtype=np.uint16)

rthres = [128, 32, 8]
rw = [0, 8, 16, 32]
clip_val = 255

# 3. 执行算法
bnf_filter = BNF(img, dw, rw, rthres, clip_val)
processed_img = bnf_filter.execute()

# 4. 左右对比显示
plt.figure(figsize=(12, 6))

# 左侧:处理前
plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray')
plt.title('Original Image')
plt.axis('off')

# 右侧:处理后
plt.subplot(1, 2, 2)
plt.imshow(processed_img, cmap='gray')
plt.title('BNF Processed Image')
plt.axis('off')

plt.tight_layout()
plt.show()
  • 测试效果
  • 测试效果2:
    说实话,虽然主要的边缘保留了,但是感觉这个算法小姑有点差强人意,图像中一些细节都抹去了.
相关推荐
昨日之日20067 小时前
【MiniMax H3】TaoMate-H3:3步LoRA让视频生成更快更高效,速度快10倍
人工智能·计算机视觉·音视频
桃西西呀8 小时前
从手写数字到ImageNet冠军:CNN架构20年,为什么越深越难训?
人工智能·计算机视觉·llm
AI的探索之旅6 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
美狐美颜SDK开放平台6 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
拉你进_教堂6 天前
小龙虾技能之【Parkinson‘s & Epileptic Behavior Recognition Skill | 帕金森癫痫行为识别技能】简介
人工智能·计算机视觉·多模态大模型·openclaw小龙虾技能
PascalXie6 天前
人形机器人的“眼睛“:多目视觉方案怎么选?
计算机视觉·机器人
普密斯科技6 天前
替代传统影像仪,摄像头磨切片测量新方案实测
计算机视觉·集成测试·测量
YOLO数据集集合6 天前
天空红外小目标检测数据集 | 红外小目标 天空目标检测 无人机检测 鸟类识别 低空安防9099期
人工智能·目标检测·计算机视觉·目标跟踪·无人机·无人机视角·直升机检测
江屿风6 天前
【认识深度学习】【深度学习分类与计算机视觉任务要点解析】流食般投喂
大数据·人工智能·深度学习·计算机视觉·目标跟踪·自然语言处理·语音识别
kaixin_啊啊6 天前
Micam 多容器太吃资源?用 Go2RTC + EasyNVR 给小米摄像头换一套轻量本地录像方案
图像处理·人工智能·摄像头