使用 OpenCV 构建稳定的多面镜片墙效果(镜面反射 + Delaunay 分块)

发布于:2025-05-23 ⋅ 阅读:(21) ⋅ 点赞:(0)

✨ 效果概览

我们将实现一种视觉效果,模拟由许多小镜面拼接而成的“镜子墙”。每个镜面是一个三角形区域,其内容做镜像反射(如水平翻转),在视频中形成奇特的万花筒、哈哈镜、空间折叠感。

使用 OpenCV 实现“随机镜面墙”——多镜片密铺的哈哈镜效果-CSDN博客https://blog.csdn.net/weixin_43607107/article/details/148126936?spm=1001.2014.3001.5501

为了让视频连贯播放而无明显闪烁,还需解决随机性带来的帧间结构跳变问题。

1. 背景知识:图像分块与仿射变换

我们利用以下两种技术实现该效果:

🟩 1.1 Delaunay 三角剖分

  • 对图像中随机生成的点进行 Delaunay 三角剖分(scipy.spatial.Delaunay

  • 可将整个图像划分为若干不重叠的三角区域,适合对局部进行变换

🟦 1.2 OpenCV 仿射变换

  • 对每个三角形区域进行水平镜像变换,使用 cv2.warpAffine() 函数

  • 控制点的选择与重心、外接圆等几何信息结合使用,可衍生更多镜面效果


2. 初始实现的问题:每帧结构不同导致闪烁

以下是初始实现的逻辑:

# 在 do() 方法中每帧都重新生成点 -> Delaunay -> 镜面反射

这样每帧都会产生不同的三角剖分结构,导致在连续视频中“镜子拼贴”的结构跳动不定,出现明显闪烁(flickering)现象,观感较差。


3. 改进思路:结构一次生成,复用即可

我们将随机点生成和三角剖分的过程移到 __init__initialize() 方法中,只在第一次调用时执行一次,之后的每一帧复用同样的三角结构。

✅ 优势

  • 消除闪烁

  • 性能更高,避免频繁三角剖分

  • 保持视觉一致性,为后续加入动态渐变打基础


4. 完整实现代码(固定结构版)

import cv2
import numpy as np
from scipy.spatial import Delaunay

class FrameObject:
    def __init__(self):
        self.initialized = False
        self.triangles = None

    def initialize(self, w, h):
        # 初始化结构一次
        num_points = np.random.randint(50, 101)
        points = np.random.randint(0, [w, h], size=(num_points, 2))
        corners = np.array([[0,0], [w-1,0], [w-1,h-1], [0,h-1]])
        points = np.vstack((points, corners))

        tri = Delaunay(points)
        self.triangles = points[tri.simplices]
        self.initialized = True

    def do(self, frame, device):
        h, w = frame.shape[:2]

        if not self.initialized:
            self.initialize(w, h)

        result = np.zeros_like(frame)

        for tri_pts in self.triangles:
            mask = np.zeros((h, w), dtype=np.uint8)
            cv2.fillConvexPoly(mask, tri_pts, 255)

            centroid = np.mean(tri_pts, axis=0)

            x, y, bw, bh = cv2.boundingRect(tri_pts)
            roi = frame[y:y+bh, x:x+bw]
            mask_roi = mask[y:y+bh, x:x+bw]

            cx, cy = centroid - np.array([x, y])

            M = np.array([
                [-1, 0, 2*cx],
                [0, 1, 0]
            ], dtype=np.float32)

            warped = cv2.warpAffine(roi, M, (bw, bh), flags=cv2.INTER_LINEAR)

            mask_bin = (mask_roi > 0)
            for c in range(frame.shape[2]):
                result[y:y+bh, x:x+bw, c][mask_bin] = warped[:, :, c][mask_bin]

        return result
5. 效果展示(截图/动画)


6. 扩展思路

你可以将该基础框架扩展为更多样的视觉效果:

  • 👓 镜面方式可选:水平、垂直、中心扭曲

  • 🎛 添加控制面板,改变分块数量或动态渐变

  • 🌀 加入水波或镜面变形(结合重心旋转/扰动)

  • 🎮 使用鼠标或动作检测交互式驱动镜面变换


7. 小结

在处理视频特效时,结构的一致性往往比效果的复杂性更重要。通过将镜面剖分结构从逐帧生成改为初始化一次,既提升了性能,又增强了稳定性,是视频处理中的一个经典优化思路。


如果你也在做计算机视觉、视频滤镜或 AI 视觉交互应用,这种“稳定拼接 + 局部变换”的方法可以广泛应用于艺术特效、增强现实等场景。

欢迎讨论与交流 👇!如果你感兴趣我还可以继续写更多镜面视觉扩展方案!


网站公告

今日签到

点亮在社区的每一天
去签到