FunnyMirrors是LearnOpenCV组织下的一个有趣计算机视觉项目,它利用OpenCV库实现了多种实时哈哈镜效果。哈哈镜(Funny Mirror)是一种能够扭曲图像产生滑稽效果的镜面,在游乐园和娱乐场所常见。该项目通过数字图像处理技术,在普通摄像头视频流中模拟了这种效果。
项目GitHub仓库:https://github.com/spmallick/learnopencv/tree/master/FunnyMirrors
哈哈镜效果本质上是一种非线性图像变形技术,其数学基础可以表示为:
I d s t ( x , y ) = I s r c ( f ( x , y ) , g ( x , y ) ) I_{dst}(x,y) = I_{src}(f(x,y), g(x,y)) Idst(x,y)=Isrc(f(x,y),g(x,y))
其中:
凸透镜效果可以通过径向变形实现:
r ′ = ( x − x c ) 2 + ( y − y c ) 2 r' = \sqrt{(x - x_c)^2 + (y - y_c)^2} r′=(x−xc)2+(y−yc)2
r = { r ′ ( 1 − k ⋅ r ′ 2 ) if r ′ ≤ R r ′ otherwise r = \begin{cases} r'(1 - k \cdot r'^2) & \text{if } r' \leq R \\ r' & \text{otherwise} \end{cases} r={r′(1−k⋅r′2)r′if r′≤Rotherwise
其中:
凹透镜效果与凸透镜类似,但变形方向相反:
r = r ′ ( 1 + k ⋅ r ′ 2 ) r = r'(1 + k \cdot r'^2) r=r′(1+k⋅r′2)
波浪效果可以通过正弦波变形实现:
x ′ = x + A ⋅ sin ( 2 π y λ + ϕ ) x' = x + A \cdot \sin(\frac{2\pi y}{\lambda} + \phi) x′=x+A⋅sin(λ2πy+ϕ)
y ′ = y + A ⋅ sin ( 2 π x λ + ϕ ) y' = y + A \cdot \sin(\frac{2\pi x}{\lambda} + \phi) y′=y+A⋅sin(λ2πx+ϕ)
其中:
项目主要包含以下关键文件:
funnyMirrors.py
:主程序文件,实现各种哈哈镜效果utils.py
:包含辅助函数和工具类import cv2
cap = cv2.VideoCapture(0) # 打开默认摄像头
while True:
ret, frame = cap.read() # 读取帧
if not ret:
break
def create_bulge_effect_map(shape, center, radius, strength):
map_x = np.zeros(shape, np.float32)
map_y = np.zeros(shape, np.float32)
for y in range(shape[0]):
for x in range(shape[1]):
dx = x - center[0]
dy = y - center[1]
distance = np.sqrt(dx*dx + dy*dy)
if distance < radius:
factor = 1 - (distance / radius)**2
factor = factor * strength
new_x = dx * factor + center[0]
new_y = dy * factor + center[1]
map_x[y,x] = new_x
map_y[y,x] = new_y
else:
map_x[y,x] = x
map_y[y,x] = y
return map_x, map_y
def apply_effect(frame, map_x, map_y):
return cv2.remap(frame, map_x, map_y, cv2.INTER_LINEAR)
pip install opencv-python numpy
git clone https://github.com/spmallick/learnopencv.git
cd learnopencv/FunnyMirrors
python funnyMirrors.py
程序运行时可以通过键盘控制:
1-6
键切换不同效果+/-
调整变形强度ESC
退出程序错误现象:
[ WARN:0] global /tmp/opencv/modules/videoio/src/cap_v4l.cpp (889) open VIDEOIO(V4L2:/dev/video0): can't open camera by index
解决方案:
cap = cv2.VideoCapture(1) # 尝试其他索引
问题现象:帧率过低,效果不流畅
优化方案:
ret, frame = cap.read()
frame = cv2.resize(frame, (640, 480)) # 降低分辨率
cv2.remap(frame, map_x, map_y, cv2.INTER_LINEAR) # 改为INTER_NEAREST
调整方法:
可以扩展项目添加更多变形效果,例如:
漩涡效果:
def create_swirl_effect_map(shape, center, radius, strength):
map_x = np.zeros(shape, np.float32)
map_y = np.zeros(shape, np.float32)
for y in range(shape[0]):
for x in range(shape[1]):
dx = x - center[0]
dy = y - center[1]
distance = np.sqrt(dx*dx + dy*dy)
if distance < radius:
angle = strength * (radius - distance) / radius
new_x = center[0] + dx * np.cos(angle) - dy * np.sin(angle)
new_y = center[1] + dx * np.sin(angle) + dy * np.cos(angle)
map_x[y,x] = new_x
map_y[y,x] = new_y
else:
map_x[y,x] = x
map_y[y,x] = y
return map_x, map_y
使用NumPy向量化运算替代循环:
def create_bulge_effect_map_optimized(shape, center, radius, strength):
x = np.arange(shape[1])
y = np.arange(shape[0])
x, y = np.meshgrid(x, y)
dx = x - center[0]
dy = y - center[1]
distance = np.sqrt(dx**2 + dy**2)
factor = np.where(distance < radius, 1 - (distance / radius)**2, 0)
factor = factor * strength
new_x = np.where(distance < radius, dx * factor + center[0], x)
new_y = np.where(distance < radius, dy * factor + center[1], y)
return new_x.astype(np.float32), new_y.astype(np.float32)
图像变形理论:
实时图像处理:
remap
函数的优化实现计算机视觉中的几何变换:
非线性图像变形:
FunnyMirrors项目展示了如何利用OpenCV实现实时图像变形效果。通过该项目,我们可以学习到:
remap
函数的高级用法该项目代码简洁但功能强大,是学习计算机视觉和图像处理的优秀实践案例。读者可以基于此项目进行扩展,开发出更多有趣的图像变形应用。