Kotlin Bytedeco OpenCV 图像图像51.2 光流背景消除

发布于:2025-02-12 ⋅ 阅读:(11) ⋅ 点赞:(0)

Kotlin Bytedeco OpenCV 图像图像51.2 光流背景消除

方法 适用场景 优点 计算量
MOG2 复杂背景 可检测阴影 中等
KNN 动态背景 适用于波动场景 较高
Running Average 静态背景 计算简单
Frame Differencing 低光照变化场景 计算快
Optical Flow 复杂运动场景 精度高

如果你的场景是 一般的视频监控,推荐使用 MOG2 或 KNN。
如果你的场景是 背景变化缓慢,推荐 Running Average。

1 添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xu</groupId>
    <artifactId>KotlinOpenCV</artifactId>
    <version>1.0</version>

    <properties>
        <kotlin.version>2.0.0</kotlin.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <kotlin.code.style>official</kotlin.code.style>
        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
    </properties>

    <repositories>
        <repository>
            <id>mavenCentral</id>
            <url>https://repo1.maven.org/maven2/</url>
        </repository>
    </repositories>

    <dependencies>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.29</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.27.0</version>
        </dependency>

        <dependency>
            <groupId>org.tukaani</groupId>
            <artifactId>xz</artifactId>
            <version>1.10</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-core</artifactId>
            <version>1.9.0-RC</version>
        </dependency>

        <!--        <dependency>-->
        <!--            <groupId>org.opencv</groupId>-->
        <!--            <artifactId>opencv</artifactId>-->
        <!--            <version>4100</version>-->
        <!--            <scope>system</scope>-->
        <!--            <systemPath>${project.basedir}/lib/opencv/opencv-4100.jar</systemPath>-->
        <!--        </dependency>-->

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv-platform</artifactId>
            <version>4.10.0-1.5.11</version>
        </dependency>

        <!--        <dependency>-->
        <!--            <groupId>org.bytedeco</groupId>-->
        <!--            <artifactId>ffmpeg-platform</artifactId>-->
        <!--            <version>6.1.1-1.5.10</version>-->
        <!--        </dependency>-->

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test-junit5</artifactId>
            <version>2.0.0</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>2.0.0</version>
        </dependency>

    </dependencies>

    <build>
        <sourceDirectory>src/main/kotlin</sourceDirectory>
        <testSourceDirectory>src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>2.0.0</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <configuration>
                    <mainClass>MainKt</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2 测试代码

package com.xu.com.xu.video

import org.bytedeco.javacpp.Loader
import org.bytedeco.opencv.global.opencv_core
import org.bytedeco.opencv.global.opencv_highgui
import org.bytedeco.opencv.global.opencv_imgproc
import org.bytedeco.opencv.global.opencv_video
import org.bytedeco.opencv.opencv_core.Mat
import org.bytedeco.opencv.opencv_core.MatVector
import org.bytedeco.opencv.opencv_core.Size
import org.bytedeco.opencv.opencv_videoio.VideoCapture

object BSM {

    init {
        Loader.load(opencv_core::class.java)
    }

    @JvmStatic
    fun main(args: Array<String>) {
        flow()
    }

    fun flow() {
        // 打开视频流
        val capture = VideoCapture("lib/video/video_003.avi")
        if (!capture.isOpened) {
            return
        }

        val prev = Mat()  // 存储上一帧灰度图像
        val gray = Mat()      // 当前帧灰度图像
        val flow = Mat()      // 存储光流结果

        val org = Mat()       // 存储当前原始帧
        val mask = Mat()      // 用于存储前景掩码

        while (true) {
            // 读取当前帧
            capture.read(org)
            if (org.empty()) break
            // 转换为灰度图
            opencv_imgproc.cvtColor(org, gray, opencv_imgproc.COLOR_BGR2GRAY)
            if (!prev.empty()) {
                // 计算光流
                opencv_video.calcOpticalFlowFarneback(
                    prev, gray, flow,
                    0.5, // `pyrScale` 适当降低(0.4-0.6),减少背景干扰
                    3,   // `levels` 适当增加(2-4),提高检测范围
                    1,   // `windowSize` 增大(3→5),增强检测
                    20,  // `iterations` 增加迭代次数,提高准确度
                    5,   // `polyN`(5-7),适当提高减少噪声
                    1.2, // `polySigma` 增大(1.2 → 1.5),平滑光流
                    0    // `flags` 默认为 0
                )
                // 计算光流的幅度
                val channels = MatVector()
                opencv_core.split(flow, channels) // 拆分 X / Y 分量
                val mag = Mat()
                opencv_core.magnitude(channels[0], channels[1], mag)
                // 归一化幅度(norm 的类型为 CV_32F)
                val norm = Mat()
                opencv_core.normalize(mag, norm, 0.0, 255.0, opencv_core.NORM_MINMAX, -1, Mat())
                // 将 norm 转换为 8 位图像(CV_8U)
                val calc = Mat()
                norm.convertTo(calc, opencv_core.CV_8U)
                // 进行二值化处理,提取运动区域(mask 的类型为 CV_8UC1)
                //opencv_imgproc.threshold(calc, mask, 10.0, 255.0, opencv_imgproc.THRESH_BINARY)
                opencv_imgproc.adaptiveThreshold(
                    calc, mask, 255.0,
                    opencv_imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
                    opencv_imgproc.THRESH_BINARY,
                    10, 2.0
                )
                // 形态学处理去噪声
                val kernel = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, Size(1, 1))
                opencv_imgproc.morphologyEx(mask, mask, opencv_imgproc.MORPH_OPEN, kernel)
                opencv_imgproc.morphologyEx(mask, mask, opencv_imgproc.MORPH_CLOSE, kernel)
                // 确保类型一致
                val comm = Mat()
                opencv_imgproc.cvtColor(mask, comm, opencv_imgproc.COLOR_GRAY2BGR)
                // 确保大小一致
                if (comm.size() != org.size()) {
                    opencv_imgproc.resize(comm, comm, org.size())
                }
                // 进行拼接显示
                val show = Mat()
                opencv_core.hconcat(org, comm, show)
                opencv_highgui.imshow("Optical Flow BSM", show)
            }
            // 复制当前帧用于下一次计算
            gray.copyTo(prev)
            // 检查用户按键
            if (opencv_highgui.waitKey(30) == 'q'.code) break
        }
        // 释放资源
        capture.release()
        opencv_highgui.destroyAllWindows()
    }

    fun gmm() {
        // 打开视频流(可使用摄像头或者视频文件路径)
        val capture = VideoCapture(0)
        capture.open("lib/video/video_003.avi")
        // 创建背景减除器
        val bg = opencv_video.createBackgroundSubtractorMOG2().apply {
            history = 500         // 历史帧数
            varThreshold = 16.0   // 方差阈值
            detectShadows = true  // 检测阴影
        }
        // 创建Mat对象用于存储视频帧和结果
        val org = Mat()
        val dst = Mat()

        while (true) {
            // 读取当前帧
            capture.read(org)
            if (org.empty()) break
            // 应用背景减除器
            bg.apply(org, dst)
            // 可选:对前景蒙版进行一些后处理(如形态学操作)
            val kernel = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, Size(1, 1))
            opencv_imgproc.morphologyEx(dst, dst, opencv_imgproc.MORPH_CLOSE, kernel)
            // 确保类型一致
            val mat = Mat()
            opencv_imgproc.cvtColor(dst, mat, opencv_imgproc.COLOR_GRAY2BGR)
            // 确保大小一致
            if (dst.size() != org.size()) {
                opencv_imgproc.resize(mat, mat, org.size())
            }
            // 进行拼接
            val show = Mat()
            opencv_core.hconcat(org, mat, show)
            opencv_highgui.imshow("MOG2 BSM", show)
            // 检查用户按键
            if (opencv_highgui.waitKey(30) == 'q'.code) break
        }
        // 释放资源
        capture.release()
        opencv_highgui.destroyAllWindows()
    }

    fun knn() {
        // 打开视频流(可使用摄像头或者视频文件路径)
        val capture = VideoCapture("lib/video/video_003.avi")
        if (!capture.isOpened) {
            return
        }
        // 创建 KNN 背景减除器
        val bg = opencv_video.createBackgroundSubtractorKNN().apply {
            history = 500         // 历史帧数
            dist2Threshold = 400.0 // 距离阈值,控制前景检测的敏感度
            detectShadows = true  // 是否检测阴影
        }
        // 创建 Mat 对象用于存储视频帧和结果
        val org = Mat()
        val dst = Mat()
        while (true) {
            // 读取当前帧
            capture.read(org)
            if (org.empty()) break
            // 应用背景减除器
            bg.apply(org, dst)
            // 可选:对前景蒙版进行一些后处理(如形态学操作)
            val kernel = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, Size(3, 3))
            opencv_imgproc.morphologyEx(dst, dst, opencv_imgproc.MORPH_CLOSE, kernel)
            // 确保类型一致
            val mat = Mat()
            opencv_imgproc.cvtColor(dst, mat, opencv_imgproc.COLOR_GRAY2BGR)
            // 确保大小一致
            if (dst.size() != org.size()) {
                opencv_imgproc.resize(mat, mat, org.size())
            }
            // 进行拼接
            val show = Mat()
            opencv_core.hconcat(org, mat, show)
            // 显示结果
            opencv_highgui.imshow("KNN BSM", show)
            // 检查用户按键
            if (opencv_highgui.waitKey(30) == 'q'.code) break
        }
        // 释放资源
        capture.release()
        opencv_highgui.destroyAllWindows()
    }

}

3 测试结果

在这里插入图片描述