文章目录
- 霍夫变换
- 基本原理
- 霍夫变换的步骤
- 使用 OpenCV 实现直线检测
- 示例:标准霍夫变换
- 示例:概率霍夫变换
- 参数解释
- 霍夫变换检测圆
- 基于GAN的样本生成
- GAN的基本原理
- 基于GAN的数据增广流程
- 实现代码示例
- 同态滤波(Homomorphic Filtering)
- 同态滤波的步骤
- 同态滤波的实现步骤
- 示例代码
- 偏振相机
- 光的偏振基础
- 常见的偏振状态
- 偏振相机的工作原理
- 关键技术
- 偏振信息的计算
- Stokes 参数
- 偏振特性
- 偏振相机的核心流程
- 偏振相机的硬件结构
- Bayer格式与偏振相机的结合
- 优点
- 挑战
- 金字塔 Retinex 算法(Pyramid Retinex Algorithm)
- Retinex 理论简介:
- Pyramid Retinex 算法原理:
- Pyramid Retinex 处理流程:
- Pyramid Retinex 算法步骤:
- Pyramid Retinex 算法代码示例(Python):
霍夫变换
霍夫变换(Hough Transform)是一种用于检测图像中具有特定形状的特征(如直线、圆等)的技术。它通过将图像空间中的点映射到参数空间,以便从噪声中更可靠地检测出全局形状。霍夫变换广泛应用于图像处理和计算机视觉中,特别是用于直线和圆的检测。
基本原理
在图像中,直线可以用笛卡尔坐标系下的方程表示为:
y
=
−
c
o
s
θ
/
s
i
n
θ
∗
x
+
r
/
s
i
n
θ
y = -cosθ/sinθ*x + r/sinθ
y=−cosθ/sinθ∗x+r/sinθ
然而,这时候两个参数不知道r 和θ,因此用极坐标表示为r=f(theta)更为合适:
ρ
=
x
cos
θ
+
y
sin
θ
\rho = x \cos \theta + y \sin \theta
ρ=xcosθ+ysinθ
其中:
- ρ \rho ρ 表示直线到坐标原点的距离。
- θ \theta θ 表示直线与 x x x 轴的夹角。
对于图像空间中的每个点
(
x
,
y
)
(x, y)
(x,y),可以在参数空间中绘制对应的
(
ρ
,
θ
)
(\rho, \theta)
(ρ,θ) 曲线。如果多条曲线在某一点相交,说明这些点共线。
图片来源:【霍夫Hough直线变换原理检测算法的个人简易理解(极简版,看不会说话的吴克的霍夫直线检测观后感)】https://www.bilibili.com/video/BV1k44y1G772?vd_source=b1f5728f3a9c87006fa48f39e09acbab
霍夫变换的步骤
- 边缘检测:在应用霍夫变换之前,通常会先使用 Canny 边缘检测来提取图像的边缘。
- 参数空间映射:将图像空间中的每个边缘点映射到参数空间。
- 寻找峰值:在参数空间中查找具有最多交点的区域,这些区域对应于图像中的直线。
使用 OpenCV 实现直线检测
OpenCV 提供了 cv2.HoughLines
和 cv2.HoughLinesP
函数来实现标准霍夫变换和概率霍夫变换。
示例:标准霍夫变换
import cv2
import numpy as np
# 读取图像并转换为灰度图
image = cv2.imread('example.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)
# 应用标准霍夫变换
lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
# 绘制检测到的直线
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow('Detected Lines', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
示例:概率霍夫变换
概率霍夫变换是标准霍夫变换的优化版,使用随机采样来提高效率,并返回直线段而不是整条直线。
# 应用概率霍夫变换
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
# 绘制检测到的直线段
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('Detected Line Segments', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数解释
-
rho
:距离分辨率,即霍夫空间的 ρ \rho ρ 单位。 -
theta
:角度分辨率,即霍夫空间的 θ \theta θ 单位。 -
threshold
:累加器阈值,只有累加值大于该值时才被认为是一条直线。 -
minLineLength
(用于HoughLinesP
):直线的最小长度。 -
maxLineGap
(用于HoughLinesP
):直线上点之间的最大允许间隙。
霍夫变换检测圆
除了直线,霍夫变换还可以用于检测圆。OpenCV 提供了 cv2.HoughCircles
函数。
# 使用霍夫变换检测圆
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1.2, minDist=30, param1=50, param2=30, minRadius=10, maxRadius=100)
# 绘制检测到的圆
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
cv2.circle(image, (x, y), r, (0, 255, 0), 4)
cv2.imshow('Detected Circles', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
基于GAN的样本生成
基于生成对抗网络(GAN,Generative Adversarial Networks)的数据增广是一种通过深度学习生成新样本的方法。这些新样本可用来扩展数据集,特别是在样本量有限的情况下提升模型性能。
GAN的基本原理
GAN由两个对抗的网络组成:
-
生成器(Generator):
- 输入随机噪声,输出伪造的样本(例如图像、音频等)。
- 目标是生成尽可能接近真实样本的伪样本。
-
判别器(Discriminator):
- 输入样本,判断是真实样本还是生成样本。
- 目标是正确区分真实样本和生成样本。
两者通过对抗性训练(生成器试图欺骗判别器,判别器试图更准确地区分)达到动态平衡。训练完成后,生成器可以生成逼真的新样本。
基于GAN的数据增广流程
-
准备数据:
- 收集现有的训练数据集,并对其进行预处理。
-
构建和训练GAN:
- 构建GAN网络,包括生成器和判别器。
- 生成器 (Generator) 是一个神经网络,输入一个随机噪声向量,输出一张与真实图像相似的“假图像”。生成器使用了三层全连接层(Linear)和两个激活函数:第一层将噪声向量扩展到更高的维度。中间层通过 ReLU 激活函数引入非线性。输出层通过 Tanh 将生成图像的像素值归一化到 [ − 1 , 1 ] [-1, 1] [−1,1],与数据预处理一致。
- 判别器 (Discriminator) 是一个神经网络,用于判断输入的图像是真实图像(来自数据集)还是假图像(由生成器生成)。判别器也是一个全连接网络:三层全连接层逐渐降低维度。激活函数使用 LeakyReLU,避免“神经元死亡”问题(梯度过小导致无学习效果)。最后一层通过 Sigmoid 输出一个概率值,表示输入图像为真实的概率,范围为 [ 0 , 1 ] [0, 1] [0,1]。
- 使用训练数据训练GAN,确保生成器能够生成逼真的样本。
-
生成新样本:
- 使用训练好的生成器生成新的样本。
- 新样本可以是多样化的、特定类别的,甚至是风格化的。
-
加入原始数据集:
- 将生成的样本加入原始数据集中。
- 对扩展后的数据集进行训练,提高模型性能。
实现代码示例
下面是一个简单的基于GAN的数据增广实现示例(以MNIST为例):
import torch # 导入PyTorch库,用于深度学习模型的构建与训练
import torch.nn as nn # 导入神经网络模块
import torch.optim as optim # 导入优化器模块
from torchvision import datasets, transforms # 导入用于处理数据的工具
from torchvision.utils import save_image # 导入用于保存生成图像的工具
# 定义生成器模型,生成假图像
class Generator(nn.Module):
def __init__(self, z_dim, img_dim):
super().__init__()
# 使用全连接层和激活函数构建生成器网络
self.net = nn.Sequential(
nn.Linear(z_dim, 128), # 输入维度为z_dim,输出为128
nn.ReLU(), # 使用ReLU激活函数
nn.Linear(128, 256), # 从128维到256维
nn.ReLU(), # 再次使用ReLU激活函数
nn.Linear(256, img_dim), # 输出维度为图像的展平大小
nn.Tanh() # 使用Tanh将输出值归一化到[-1, 1]范围
)
# 定义前向传播,接收输入噪声并生成图像
def forward(self, z):
return self.net(z) # 将输入z通过生成器网络
# 定义判别器模型,用于区分真假图像
class Discriminator(nn.Module):
def __init__(self, img_dim):
super().__init__()
# 使用全连接层和激活函数构建判别器网络
self.net = nn.Sequential(
nn.Linear(img_dim, 256), # 输入为图像展平后的维度,输出为256
nn.LeakyReLU(0.2), # 使用LeakyReLU激活函数,避免死亡神经元问题
nn.Linear(256, 128), # 从256维到128维
nn.LeakyReLU(0.2), # 再次使用LeakyReLU激活函数
nn.Linear(128, 1), # 最后一层输出1个值,表示真假的概率
nn.Sigmoid() # 使用Sigmoid激活函数,将输出映射到[0, 1]
)
# 定义前向传播,接收输入图像并输出真假概率
def forward(self, img):
return self.net(img)
# 参数设置
z_dim = 64 # 噪声向量的维度
img_dim = 28 * 28 # MNIST图像展平后的大小 (28x28像素)
lr = 0.0002 # 学习率
# 初始化生成器和判别器
generator = Generator(z_dim, img_dim) # 生成器
discriminator = Discriminator(img_dim) # 判别器
# 初始化优化器,用于更新生成器和判别器的参数
opt_gen = optim.Adam(generator.parameters(), lr=lr) # 生成器的Adam优化器
opt_disc = optim.Adam(discriminator.parameters(), lr=lr) # 判别器的Adam优化器
# 损失函数,使用二元交叉熵损失
criterion = nn.BCELoss()
# 数据加载和预处理
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5,), (0.5,)) # 将图像像素值归一化到[-1, 1]范围
])
data = datasets.MNIST(root="./data", transform=transform, download=True) # 下载MNIST数据集
loader = torch.utils.data.DataLoader(data, batch_size=64, shuffle=True) # 加载数据,批量大小为64
# 训练GAN模型
epochs = 10 # 训练的轮数
for epoch in range(epochs): # 遍历每一轮
for real, _ in loader: # 遍历每个批次的真实图像
real = real.view(-1, 28*28) # 将图像展平为一维
batch_size = real.size(0) # 获取当前批次的大小
# ---------------------
# 训练判别器
# ---------------------
z = torch.randn(batch_size, z_dim) # 生成随机噪声向量
fake = generator(z) # 用生成器生成假图像
disc_real = discriminator(real).view(-1) # 判别器对真实图像的输出
disc_fake = discriminator(fake.detach()).view(-1) # 判别器对假图像的输出(梯度不回传到生成器)
# 判别器的损失函数,真实图像的标签为1,假图像的标签为0
loss_disc = criterion(disc_real, torch.ones_like(disc_real)) + \
criterion(disc_fake, torch.zeros_like(disc_fake))
opt_disc.zero_grad() # 清空判别器的梯度
loss_disc.backward() # 反向传播计算梯度
opt_disc.step() # 更新判别器参数
# ---------------------
# 训练生成器
# ---------------------
output = discriminator(fake).view(-1) # 判别器对生成图像的输出
# 生成器的损失函数,生成器希望判别器认为其生成的图像都为真(标签为1)
loss_gen = criterion(output, torch.ones_like(output))
opt_gen.zero_grad() # 清空生成器的梯度
loss_gen.backward() # 反向传播计算梯度
opt_gen.step() # 更新生成器参数
# 打印当前轮的损失
print(f"Epoch [{epoch+1}/{epochs}] Loss D: {loss_disc:.4f}, Loss G: {loss_gen:.4f}")
# 保存生成的图像,便于可视化
save_image(fake.view(fake.size(0), 1, 28, 28), f"generated_{epoch+1}.png")
同态滤波(Homomorphic Filtering)
同态滤波是一种常用于图像处理中的技术,主要用于增强图像的对比度,尤其是用于抑制图像中的亮度不均匀性(例如由于光照变化或阴影)。同态滤波通过处理图像的亮度和反射分量来提高图像的整体质量。
同态滤波的核心思想是将图像分解为两部分:一部分表示图像的反射成分(物体表面的反射),另一部分表示光照成分。然后,利用频域滤波来增强或抑制这两个部分。
同态滤波的步骤
-
图像的对数变换:
同态滤波的第一步是对图像进行对数变换,以分离图像的亮度和反射成分。对数变换将图像的乘法性质转化为加法性质,从而使得图像的亮度成分与反射成分更容易分开。对图像进行对数变换后的表达式为:
I ′ ( x , y ) = log ( I ( x , y ) ) I'(x, y) = \log(I(x, y)) I′(x,y)=log(I(x,y))
其中,$ I(x, y) $ 是原始图像,$ I’(x, y) $ 是变换后的图像。 -
频域处理:
使用傅里叶变换将图像从空间域转换到频域,这样我们可以对图像的高频和低频部分进行分别处理。高频部分代表反射成分,低频部分代表光照成分。通过对频域中的图像进行滤波,我们可以增强或抑制某些频率成分。例如,可以对高频部分进行增强来提高细节,或对低频部分进行抑制来减少光照不均匀的影响。
-
反变换:
频域滤波完成后,使用反傅里叶变换将处理后的图像从频域转换回空间域。最后,使用指数变换将图像恢复到原始的动态范围。反变换后的图像表达式为:
I ( x , y ) = exp ( I ′ ( x , y ) ) I(x, y) = \exp(I'(x, y)) I(x,y)=exp(I′(x,y))
同态滤波的实现步骤
以下是一个简单的同态滤波实现流程:
- 读取图像。
- 对图像进行对数变换。
- 进行傅里叶变换。
- 应用频域滤波。
- 进行反傅里叶变换。
- 恢复图像的对数值并进行指数变换。
示例代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
def homomorphic_filter(image):
# 1. 对数变换
image_log = np.log1p(np.float32(image)) # 使用log1p避免log(0)
# 2. 傅里叶变换
f = np.fft.fft2(image_log)
fshift = np.fft.fftshift(f) # 将低频部分移到中心
# 3. 设计一个高通滤波器
rows, cols = image.shape
crow, ccol = rows // 2, cols // 2
mask = np.ones((rows, cols), np.float32)
r = 30 # 低频区域半径
center = [crow, ccol]
x, y = np.fft.fftfreq(cols), np.fft.fftfreq(rows)
X, Y = np.meshgrid(x, y)
d = np.sqrt((X - center[1]) ** 2 + (Y - center[0]) ** 2)
mask[d < r] = 0 # 低频部分置零
# 4. 频域滤波
fshift_filtered = fshift * mask
# 5. 反傅里叶变换
f_ishift = np.fft.ifftshift(fshift_filtered)
image_back = np.fft.ifft2(f_ishift)
# 6. 恢复图像并进行指数变换
image_back = np.exp(np.abs(image_back)) - 1
return np.uint8(image_back)
# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_COLOR)
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 应用同态滤波
filtered_image = homomorphic_filter(image_gray)
# 显示结果
plt.figure(figsize=(10, 5))
plt.subplot