【算法】bfs与dfs算法解决FloodFill(洪流)问题(C++)

news2024/7/4 5:56:20

文章目录

  • 1. 什么是FloodFill问题
  • 2. 用什么方法解决FloodFill问题
  • 3. 具体例题
    • 773.图像渲染
    • 200.岛屿数量
    • 695.岛屿的最大面积
    • 130.被围绕的区域

1. 什么是FloodFill问题

一般floodfill问题可以描述为:给定一个二维矩阵,其中每个元素代表一个像素点,并给定一个起始点、目标颜色和填充颜色。问题要求将以起始点为中心,与其相邻且具有相同颜色的所有区域都填充为目标颜色。

在这里插入图片描述


2. 用什么方法解决FloodFill问题

我们通常使用下面两种方法解决FloodFill算法问题:

DFS(深搜) 算法通常使用递归实现,在处理当前像素点的相邻像素点时,递归调用 DFS 函数,不断深入直到无法找到相邻像素为止。

BFS (宽搜)算法通常使用队列实现,将起始像素点加入队列中,并不断扩展队列中的像素点,直到队列为空为止。

下面直接结合例题来理解两种解题方法。


3. 具体例题

第一道题 “图像渲染” 是下面例题中作为基座的一道题,讲解会尽量详细,后面的题

773.图像渲染

在这里插入图片描述

题意分析

即对于一个矩阵,我们随机选取一个元素,将该元素与对于其有相同值的上下左右 的元素(对上下左右的元素继续找上下左右) 均上色为color。本质上就是找区域内的相邻元素。

解法

DFS(深度优先搜索)

“搜索一个位置的上下左右坐标,并对其上下左右坐标继续搜索上下左右坐标” 的过程可以理解为将一个主问题分解为多个子问题的过程,适用递归解决。

我们知道:深度优先搜索即一条路走到死,而该递归过程也是深搜的思想。

在这里插入图片描述

思路

  1. 先记录题目给我们的当前位置的颜色prevColor,如果当前位置=color,则直接返回
  2. 以当前位置开始,直接进行递归操作
    • 对于递归函数:先将当前位置上色,随后遍历上下左右四个方向的元素,每遍历到一个元素
    • 对该元素进行进行dfs操作,即可完成对 相连像素点的上色

代码

class Solution {
private:
    int dx[4] = {0, 0, -1, 1}; // 用于计算上下左右位置的下标
    int dy[4] = {-1, 1, 0, 0};
    int m, n, _color, prevColor; // 定义成全局变量方便调用(也可以传参)

    // dfs: 查看 坐标(x, y) 上下左右四个元素,并更改颜色
    void dfs(vector<vector<int>>& image, int x, int y) {
        image[x][y] = _color; // 当前位置上色
        for(int k = 0; k < 4; ++k) {
            int nx = x + dx[k]; // 计算一个方向的坐标
            int ny = y + dy[k];
            // 确保不越界 以及 值相同
            if(nx >= 0 && nx < m && ny >= 0 && ny < n && image[nx][ny] == prevColor)
                dfs(image, nx, ny); // 递归找连结的需要上色的像素
        }
    }

public:
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        prevColor = image[sr][sc]; // 记录最开始 / 当前元素颜色
        if(prevColor == color) return image; // 如果当前像素已经是所需颜色,则直接返回矩阵

        m = image.size(), n = image[0].size();
        _color = color;

        dfs(image, sr, sc);

        return image;
    }
};

BFS(宽度优先搜索)

宽度优先搜索利用队列和循环,每次将坐标入队,随后将其上下左右四个坐标入队,通过每次取队列元素,持续该过程,直到队列为空在这里插入图片描述

思路

  1. 先记录当前位置的颜色prevColor,如果当前位置=color,则直接返回
  2. 将当前位置坐标入队,每次提取当前元素的坐标,并将其上色
  3. 将上下左右四个方向的元素(符合条件的)入队,重复此过程

代码

class Solution {
public:
    typedef pair<int, int> pii;
    int dx[4] = {0, 0, -1, 1}; // 查询当前像素的上下左右时,xy坐标的更改
    int dy[4] = {-1, 1, 0, 0};

    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        // 边界情况
        int tmp = image[sr][sc]; // 记录要更改的像素值
        if(tmp == color) return image;
        
        queue<pii> q; // 存储像素坐标
        q.push({sr, sc});
        while(!q.empty())
        {
            auto [a, b] = q.front(); // 提取当前像素坐标
            q.pop();
            image[a][b] = color; // 上色(更改像素值)
            for(int i = 0; i < 4; ++i)
            {
                int x = a + dx[i];
                int y = b + dy[i];
                // 如果未越界则入队
                if(x >= 0 && x < image.size() && y >= 0 && y < image[0].size() && image[x][y] == tmp)
                    q.push({x, y});
            }
        }

        return image;
    }
};

200.岛屿数量

在这里插入图片描述
题意分析

矩阵由’1’, ‘0’ 两个元素组成,每一块相连的1(横着竖着相连)组成的区域为一个岛屿,我们需要找到矩阵中岛屿的数量。

解法

DFS(深度优先搜索)

思路

  1. 首先我们定义visit数组,用于记录矩阵中每一位元素是否已经被遍历过,如果为true,则不去执行该位的操作。
  2. 遍历整个矩阵,对于矩阵的每个(符合要求的)元素都执行dfs算法
  3. 每次循环中dfs彻底结束,则统计出了一片岛屿,++ret(最终结果)
  4. 关于此题的dfs:
    • 用于将矩阵中坐标为(x, y)的元素及其上下左右的元素标记

代码

class Solution {
private:
    int ret = 0; // 定义为全局变量,方便dfs改动和调用
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    int m, n;
    vector<vector<bool>> visit; // 存储网格是否被检索过的信息

private:
    // 通过递归,将当前位置的岛屿统计出来
    // dfs: 将grid[x][y] 位置所在的岛屿统计出来
    void dfs(vector<vector<char>>& grid, int x, int y) {
        // 将当前位置检索设置为true
        visit[x][y] = true;
        for(int i = 0; i < 4; ++i)
        {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if((nx >= 0 && nx < m) && (ny >= 0 && ny < n) && !visit[nx][ny] && grid[nx][ny] == '1')
            {
                dfs(grid, nx, ny);
            }
        }
    }

public:
    int numIslands(vector<vector<char>>& grid) {
        m = grid.size(), n = grid[0].size();
        visit.resize(m, vector<bool>(n, false)); // 初始化visit数组
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(!visit[i][j] && grid[i][j] == '1') // 该位置并未检索过且值为1
                {
                    ++ret;
                    dfs(grid, i, j);
                }
            }
        }
        return ret;
    }
};

BFS(宽度优先搜索)

思路

  1. 这里宽搜和深搜的代码非常相似,只需要将bfs代码内部将递归改为使用队列即可,具体代码中注解,不再多余解释

代码

class Solution {
public:
    int ret = 0;
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    int m, n;
    vector<vector<bool>> visit; // 存储网格是否被检索过的信息

    void bfs(vector<vector<char>>& grid, int x, int y) {
        queue<pair<int, int>> q; // 存储位置坐标
        
        q.push({x, y});
        while(!q.empty())
        {
            auto [a, b] = q.front(); // 取出队头元素
            q.pop();
            for(int k = 0; k < 4; ++k) // 遍历该位置的上下左右四个位置
            {
                int nx = a + dx[k];
                int ny = b + dy[k];
                if((nx >= 0 && nx < m) && (ny >= 0 && ny < n) && !visit[nx][ny] && grid[nx][ny] == '1')
                {
                    q.push({nx, ny});
                    visit[nx][ny] = true;
                }
            }
        }
    }

    int numIslands(vector<vector<char>>& grid) {
        m = grid.size(), n = grid[0].size();
        visit.resize(m, vector<bool>(n, false));

        // 遍历矩阵
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(!visit[i][j] && grid[i][j] == '1')
                {
                    ++ret;
                    bfs(grid, i, j);
                }
            }
        }

        return ret;
    }
};

695.岛屿的最大面积

在这里插入图片描述

题意分析

该题和上一题(岛屿数量)在解法和思路上可以说非常相似,只是前者要求岛屿数量,而后者要求岛屿最大面积,我们只需定义一个变量,每次统计一个岛屿后比较并更新该变量即可。

解法

DFS(深度优先搜索)

思路

重复:该题和上一题(岛屿数量)在解法和思路上可以说非常相似,只是前者要求岛屿数量,而后者要求岛屿最大面积,我们只需定义一个变量,每次统计一个岛屿后比较并更新该变量即可。

所以重点在于ret何时统计:

  1. 对于深搜,我们每次执行当前递归函数彻底结束后,执行ret = max(count, ret);即更新最大面积。
  2. 因为递归函数结束后,此时count记录的就是新岛屿的面积,则在dfs后更新最大面积。

代码

class Solution {
private:
    vector<vector<bool>> visit;
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    int ret = 0, count = 0; // 统计岛屿面积
    int m, n;

public:
    void dfs(vector<vector<int>>& grid, int x, int y) {
        visit[x][y] = true; // 将当前位置检索设置为true
        for(int k = 0; k < 4; ++k)
        {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if((nx >= 0 && nx < m) && (ny >= 0 && ny < n) && !visit[nx][ny] && grid[nx][ny] == 1) // 判断是否合法
            {
                ++count;
                dfs(grid, nx, ny);
            }
        }   
    }

    int maxAreaOfIsland(vector<vector<int>>& grid) {
        m = grid.size(), n = grid[0].size();
        visit.resize(m, vector<bool>(n, false));

        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                count = 1; // 当前位置开始(从1开始)
                if(!visit[i][j] && grid[i][j] == 1)
                {
                    dfs(grid, i, j);
                    ret = max(count, ret); // 找到最大面积
                }
            }
        }

        return ret;
    }
};

BFS(宽度优先搜索)

思路

ret何时统计:

  1. 对于宽搜,我们每次执行一次bfs函数后,进行ret = max(ret, count),
  2. 因为执行dfs后,count的值就是新岛屿的面积,所以我们在其后判断更新最大面积。

代码

class Solution {
private:
    typedef pair<int, int> pii;
    vector<vector<bool>> visit;
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    int ret = 0, count = 0; // 统计岛屿面积
    int m, n;

public:
    void bfs(vector<vector<int>>& grid, int x, int y) {
        visit[x][y] = true; // 将当前位置检索设置为true
        queue<pii> q; // 队列存储坐标
        q.push({x, y}); // 将起始坐标添加到队列中
        while(!q.empty()) // 广度优先搜索
        {
            auto [x, y] = q.front();
            q.pop();
            for(int k = 0; k < 4; ++k)
            {
                int nx = x + dx[k];
                int ny = y + dy[k];
                if((nx >= 0 && nx < m) && (ny >= 0 && ny < n) && !visit[nx][ny] && grid[nx][ny] == 1) // 判断是否合法
                {
                    ++count;
                    visit[nx][ny] = true;
                    q.push({nx, ny}); // 将新的岛屿坐标添加到队列中
                }
            }
        }   
    }

    int maxAreaOfIsland(vector<vector<int>>& grid) {
        m = grid.size(), n = grid[0].size();
        visit.resize(m, vector<bool>(n, false));

        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                count = 1; // 当前位置开始(从1开始)
                if(!visit[i][j] && grid[i][j] == 1)
                {
                    bfs(grid, i, j);
                    ret = max(ret, count);
                }
            }   
        }

        return ret;
    }
};

130.被围绕的区域

在这里插入图片描述

题意分析

  1. 题目要求我们找到所有被’X’围绕的’O’,并将其改为’X’
    • 被’X’围绕的’O’:
  2. 正难则反:我们直接找到被’X’围绕的’O’是有难度的,则可以逆转思维:
    • 找到所有不被’X’包围的’O’,将这些’O’改为’T’(改成任意字符)
    • 随后遍历矩阵,将所有’O’改为’X’,此时即完成了题目要求
    • 最后将’T’再改回X即可

在这里插入图片描述

解法

DFS(深度优先搜索)

思路

  1. 根据题意分析的思路讲解,首先遍历矩阵四边,对边缘元素执行dfs函数
    • dfs: 用于将当前位置联结的’O’改为’T’
  2. 当 循环+dfs 全部执行完毕,此时四边相连的’O’已经被改为’T’
  3. 遍历矩阵,将剩余的’O’改为’X’,将’T’改为’O’。

代码

class Solution {
public:
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    int m, n;

    // dfs将当前位置周围的'O'改为'T'
    void dfs(vector<vector<char>>& board, int x, int y) {
        board[x][y] = 'T';
        for(int k = 0; k < 4; ++k)
        {
            int a = x + dx[k];
            int b = y + dy[k];
            if((a >= 0 && a < m) && (b >= 0 && b < n) && board[a][b] == 'O') // 保证合法性
            {
                dfs(board, a, b);
            }
        }
    }

    void solve(vector<vector<char>>& board) {
        m = board.size(), n = board[0].size();

        // 正难则反(将未被'X'围绕的'O'更改,然后遍历矩阵找'O')
        // 1. 矩阵四边周围的'O' 改为 'T'
        for(int i = 0; i < m; ++i) // 上下边
        {
            if(board[i][0] == 'O') dfs(board, i, 0);
            if(board[i][n - 1] == 'O') dfs(board, i, n - 1);
        }
        for(int j = 0; j < n; ++j) // 左右边
        {
            if(board[0][j] == 'O') dfs(board, 0, j);
            if(board[m - 1][j] == 'O') dfs(board, m - 1, j);
        }

        // 2. 还原:将'O'改为'X' / 将'T'改为'O'
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(board[i][j] == 'O') board[i][j] = 'X';
                if(board[i][j] == 'T') board[i][j] = 'O';
            }
        }
    }
};

BFS(宽度优先搜索)

思路

和前面的题一致,我们用队列进行宽搜的实现:

  1. 首先创建visit数组,用于记录矩阵中的每位是否被遍历过
  2. 除了bfs函数思路不同,其余与dfs完全一致,这里主要在于bfs的写法:
    • 先将当前位置入队,并在visit中改为true
    • 只要队列不为空,持续获取上下左右方向的元素,符合要求的入队更改为true,持续此过程。

代码

class Solution {
public:
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
    vector<vector<bool>> visit;
    int m, n;

    // bfs将当前位置周围的'O'改为'T'
    void bfs(vector<vector<char>>& board, int x, int y) {
        queue<pair<int, int>> q;
        visit[x][y] = true;
        board[x][y] = 'T';
        q.push({x, y}); // 将起始位置入队
        while(!q.empty())
        {
            auto [x, y] = q.front();
            q.pop();
            for(int k = 0; k < 4; ++k)
            {
                int nx = x + dx[k];
                int ny = y + dy[k];
            if((nx >= 0 && nx < m) && (ny >= 0 && ny < n) && !visit[nx][ny] && board[nx][ny] == 'O')
                {
                    board[nx][ny] = 'T'; // 将'O'改为'T'
                    visit[nx][ny] = true; // 修改为单等号
                    q.push({nx, ny});
                }
            }
        }
    }

    void solve(vector<vector<char>>& board) {
        m = board.size(), n = board[0].size();
        visit.resize(m, vector<bool>(n, false)); // bfs算法中记录该位置是否被检查过

        // 正难则反(将未被'X'围绕的'O'更改,然后遍历矩阵找'O')
        // 1. 矩阵四边周围的'O' 改为 'T'
        for(int i = 0; i < m; ++i) // 上下边
        {
            if(board[i][0] == 'O') bfs(board, i, 0);
            if(board[i][n - 1] == 'O') bfs(board, i, n - 1);
        }
        for(int j = 0; j < n; ++j) // 左右边
        {
            if(board[0][j] == 'O') bfs(board, 0, j);
            if(board[m - 1][j] == 'O') bfs(board, m - 1, j);
        }

        // 2. 还原:将'O'改为'X' / 将'T'改为'O'
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(board[i][j] == 'O') board[i][j] = 'X';
                if(board[i][j] == 'T') board[i][j] = 'O';
            }
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python-flask 入门代码

python与pycharm安装 过程略&#xff0c;网上很多&#xff0c;记得为pycharm配置默认解释器 虚拟环境 pipenv # 全局安装虚拟环境 # 可加-U参数&#xff0c;明确全局安装&#xff0c;不加好像也可以? pip3 install pipenv #检查安装情况 pipenv --version # ---控制台输出…

跨域的解决方式(java后端)

文章目录 一、跨域介绍1、什么是跨域2、为什么会产生跨域3、禁止跨域的原因 二、简单请求和非简单请求1、简单请求1.1、什么时简单请求1.2、简单请求基础流程 2、非简单请求2.1、预检请求2.2、预检请求的回应2.3、浏览器的正常请求和回应 3、自定义跨域过滤器 三、解决方式1、C…

Java基础语法之抽象类和接口

抽象类 什么是抽象类 并不是所有的类都是用来描述对象的&#xff0c;这样的类就是抽象类 例如&#xff0c;矩形&#xff0c;三角形都是图形&#xff0c;但图形类无法去描述具体图形&#xff0c;所以它的draw方法无法具体实现&#xff0c;这个方法就可以没设计成抽象方法&…

003 Windows用户与组管理

Windows用户管理 一、用户账户 1、什么是用户账户 不同用户身份拥有不同的权限每个用户包含了一个名称和一个密码每个用户账户具有唯一的安全标识符查看系统中的用户 net user 安全标识符&#xff08;SID&#xff09; whoami /user 使用注册表查看 打开注册表命令regedi…

Sentinel使用详解

组件简介 Sentinel是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景&#xff0c;例如秒杀、消息削峰填谷、集群流量控…

Java集合-12

Map的接口特点 key重复时会进行替换 package com.edu.map;import java.util.HashMap; import java.util.Map; import java.util.Set;SuppressWarnings({"all"}) public class Demo01 {public static void main(String[] args) {Map map new HashMap<>();map.…

并查集<基于ranks 的优化,基于Path Spliting的优化>

需求分析 假设有n个村庄&#xff0c;有些村庄之间有连接的路&#xff0c;有些村庄之间并没有连接的路 请你设计一个数据结构&#xff0c;能够快速执行2个操作 ◼ 查询2个村庄之间是否有连接的路◼ 连接2个村庄 首先思考在现有的数据结构能否实现上面的功能&#xff0c;数组、…

JVM学习之运行时数据区

运行时数据区 概述 内存 内存是非常重要的系统资源&#xff0c;是硬盘和CPU的中间桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请&#xff0c;分配&#xff0c;管理的策略&#xff0c;保证了JVM高效稳定运行。不同的JVM对于…

HTTP 302错误:临时重定向

在Web开发中&#xff0c;HTTP状态码是用于表示Web服务器响应的各种状态。其中&#xff0c;HTTP 302错误表示临时重定向&#xff0c;这意味着请求的资源已被临时移动到其他位置&#xff0c;并且服务器已经提供了新的URL&#xff0c;以便客户端可以重新发送请求。 了解HTTP 302错…

[计网01] 物理层 详细解析笔记,特性

计算机网络的物理层是网络协议栈中的第一层&#xff0c;负责传输原始的比特流&#xff08;bitstream&#xff09;通过物理媒介进行通信。物理层主要关注传输介质、信号的编码和调制、数据传输速率以及数据传输的物理连接等方面。 相关特性 机械特性&#xff08;Mechanical Ch…

网络安全—学习溯源和日志分析

日志分析的步骤&#xff1a; 判断是否为攻击行为 不是&#xff1a;不用处理 是&#xff1a;判断攻击是否成功或者失败 攻击失败&#xff1a;判断IP地址是否为恶意地址&#xff0c;可以让防火墙过滤IP地址 攻击成功&#xff1a;做应急处置和溯源分析 应急处置&#xff1a;网络下…

[楚慧杯 2023] web

文章目录 eaaevalupload_shell eaaeval 打开题目&#xff0c;源码给了用户密码 登陆后啥也没有&#xff0c;扫一下发现源码泄露www.zip <?php class Flag{public $a;public $b;public function __construct(){$this->a admin;$this->b admin;}public function _…

Python计算圆的面积,几何学技法大解析!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python计算圆的面积&#xff0c;几何学技法大解析&#xff0c;全文3800字&#xff0c;阅读大约15分钟。 在本文中&#xff0c;将深入探讨如何使用 Python 计算圆的面积&…

用23种设计模式打造一个cocos creator的游戏框架----(十八)责任链模式

1、模式标准 模式名称&#xff1a;责任链模式 模式分类&#xff1a;行为型 模式意图&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有一个对象处…

『OPEN3D』1.5.1 动手实现点云暴力最近邻

本专栏地址: https://blog.csdn.net/qq_41366026/category_12186023.html?spm=1001.2014.3001.5482https://blog.csdn.net/qq_41366026/category_12186023.html?spm=1001.2014.3001.5482 1、暴力最近邻法 暴力最近邻法 (Brute-force Nearest Neighbour Search,BF 搜索) 是…

【数据结构】哈希表算法总结

知识概览&#xff08;哈希表&#xff09; 哈希表可以将一些值域较大的数映射到较小的空间内&#xff0c;通常用x mod 质数的方式进行映射。为什么用质数呢&#xff1f;这样的质数还要离2的整数幂尽量远。这可以从数学上证明&#xff0c;这样冲突最小。取余还是会出现冲突情况。…

ElasticSearch学习篇8_Lucene之数据存储(Stored Field、DocValue、BKD Tree)

前言 Lucene全文检索主要分为索引、搜索两个过程&#xff0c;对于索引过程就是将文档磁盘存储然后按照指定格式构建索引文件&#xff0c;其中涉及数据存储一些压缩、数据结构设计还是很巧妙的&#xff0c;下面主要记录学习过程中的StoredField、DocValue以及磁盘BKD Tree的一些…

【数据挖掘 | 相关性分析】Jaccard相似系数详解、关于集合的相关性(详细案例、附完详细代码实现和实操、学习资源)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

《opencv实用探索·二十》点追踪技术

前言&#xff1a; 在学习点追踪技术前需要先了解下光流发追踪目标&#xff0c;可以看上一章内容&#xff1a;光流法检测运动目标 如果以光流的方式追踪目标&#xff0c;基本上我们可以通过goodFeaturesToTrack函数计算一系列特征点&#xff0c;然后通过Lucas-Kanade算法进行一…

Java智慧工地源码,智慧工地管理平台的技术架构和工作原理

智慧工地管理平台是将互联网的理念和技术引入建筑工地&#xff0c;从施工现场源头抓起&#xff0c;最大程度的收集人员、安全、环境、材料等关键业务数据&#xff0c;依托物联网、互联网&#xff0c;建立云端大数据管理平台&#xff0c;形成“端云大数据”的业务体系和新的管理…