计算几何——扫描线 学习笔记

这篇具有很好参考价值的文章主要介绍了计算几何——扫描线 学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

计算几何——扫描线 学习笔记

你会发现我的笔记的顺序和很多扫描线的讲解是反着来的。

其实是和我老师给的课件完全是逆序(谁帮我算一下逆序对啊喵)。

前言

一开始以为扫描线就是用来求二维几何图像的信息的。

但是其实这个并不准确。个人认为,扫描线其实是一个思想,就像动态规划一样。

具体的,其思想为,用一根(无形的)的线,去扫描一个空间。

在扫描的过程中记录下信息,然后加以处理、应用。如图:

当然你可以暂时忽略这个图片的内容。

引入——会议室问题

问题描述:一个饭店要接待 \(n\) 个顾客,每个顾客会在时间 \([l_i,r_i]\) 内就餐。求饭店里同时存在的最多的顾客数量。

非常基础的一道题了。我们举一个例子:

假设我们有 \(4\) 个顾客,分别记为 \(ABCD\),我们画出这 \(4\) 个顾客到访的时间段。

\[\begin{array}{lcl} A&:&\kern{4em}|\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}|\\[0.5em] B&:&\kern{2em}|\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}|\\[0.5em] C&:&\kern{11em}|\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}|\\[0.5em] D&:&\kern{1em}|\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}-\kern{-0.5em}|\\[0.5em] \hline\\[-0.5em] &&0\kern{0.5em}1\kern{0.5em}2\kern{0.5em}2\kern{0.5em}3\kern{0.5em}3\kern{0.5em}3\kern{0.5em}2\kern{0.5em}1\kern{0.5em}1\kern{0.5em}0\kern{0.5em}1\kern{0.5em}1\kern{0.5em}1 \end{array} \]

考虑人是怎么思考这一个问题的。

我们一般会从左到右(当然从右到左也是可以的)看完这个时间线,然后直接找到一个时间点,存在最多的线段。显然,这个图里最多有 \(3\) 个顾客。

考虑计算机是怎么实现的。好吧。一样。

考虑一根线从左到右的扫描时间轴,我们发现,当这个线遇到一个线段的左端点(称为入点)的时候,顾客数就会加一,当遇到一个线段的右端点(称为出点)的时候,顾客数就会减一。

于是,我们可以在数轴上标记一个值,记为 \(p_i\),表示如果扫描线经过这个点,会增加多少。

显然,对于一个顾客 \([l,r]\)\(p_l=p_l+1,p_r=p_r-1\)

但是这个算法会有一个问题,就是如果时间轴过于长?

考虑离散化。把数轴压缩。因为我们发现实际上对于这个答案,仅有存在的最多的人数是有用的,区间长度实际上没有用。

于是我们就得到了一个 \(\mathcal O(n)\) 的算法,由于他过于简单,就不写了。

开始——二维数点

先看一道例题:P1972 [SDOI2009] HH的项链。

题目描述:给定长度为 \(n\) 的序列,多组询问,每次询问一个区间 \([l_i,r_i]\),求这个区间内的不同的数的个数。

显然有莫队和分块的做法。这里讲二维数点的算法。

我们发现,当区间的右端点固定的时候,对于一个数字,其最后一次出现可以作用的范围更大,因此我们贪心的考虑:

\(F_i\) 表示为,当前状态下,\(i\) 这个数是否为最后一次出现,即是否计入贡献,记 \(S_i\) 为其前缀和。

固定右端点,考虑每个数最后一次出现的位置所存在的贡献,那么可以知道,区间 \([l,r]\) 内不同的数的个数为 \(\sum_{i=l}^rF_i=S_r-S_{l-1}\)

然后考虑右端点不固定的情况,我们发现这个右端点向右扩展,是非常容易的。

于是考虑将所有区间离线下来,然后按照右端点排序,从头开始,一个一个扩展右端点。

同时记录下每一个问题的答案。然后考虑这个过程需要怎么维护。

首先,我们需要快速的知道 \(S_x\) 的值,还需要快速的修改任意一个 \(F\) 的值,这就是树状数组!

然后(用扫描线)去扫右端点,就没啥难度了。自己看代码:

#include <bits/stdc++.h>

using namespace std;

#define range(x) x.begin(), x.end()

struct query {
    int id;
    int l, r;
    query() = default;
    query(int id, int l, int r): id(id), l(l), r(r) {}
    friend bool operator <(const query &a, const query &b) { return a.r < b.r; }
};

#define lowbit(x) ((x) & -(x))

constexpr int N = 1e6 + 10;

int n, m;

int s[N];

void add(int x, int v) {
    for (; x <= n; x += lowbit(x)) s[x] += v;
} int sum(int x) {
    int r = 0;
    for (; x; x -= lowbit(x)) r += s[x];
    return r;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n; vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    cin >> m; vector<query> q(m);
    for (int i = 0; i < m; ++i) cin >> q[i].l >> q[i].r, q[i].id = i;
    sort(range(q)); int now = 0;
    vector<int> pos(N), e(m);
    for (query &_ : q) {
        int l = _.l, r = _.r;
        for (int i = now + 1; i <= r; ++i) {
            if (pos[a[i]]) add(pos[a[i]], -1);
            add(i, 1), pos[a[i]] = i;
        } now = r; e[_.id] = sum(r) - sum(l - 1);
    } for (int i : e) cout << i << '\n';
    return 0;
}

入门——二维数点

没错,这个就是真真正正的「二维数点」了。

例题:P2163 [SHOI2007] 园丁的烦恼。

题目描述:给定平面内 \(n\) 个点 \((x_i,y_2)\),询问一个矩形,求这个矩形内有多少个点。

这道题其实一眼离线、二维查分加离散化(当然这道题数据范围比较小,不需要离散化)。

\(S_{a,b}\) 表示 \(\sum_{i=0}^a\sum_{j=0}^bQ_{i,j}\),其中 \(Q_{i,j}\) 表示 \((i,j)\) 是否有点。

注意到询问 \([x_1,y_1,x_2,y_2]\) 的答案即为 \(S_{x_2,y_2}-S_{x_2,y_1-1}-S_{x_1-1,y_2}+S_{x_1-1,y_1-1}\)

然后考虑离线怎么处理。

可以将这些分别来看,然后再乘上系数(\(\pm1\))加到对应的询问里。

因此转化为怎么求所有存在的点 \(S_{a,b}\) 了。

延伸上一题的思路:

按照 \(x\) 左边排序,然后用树状数组维护 \(y\) 轴上是否有点,以及快速前缀求和。

然后扫描,并更新即可。也不难,代码:

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'

using ll = long long;

#define range(x) x.begin(), x.end()

struct point {
    int x, y;
    point() = default;
    point(int x, int y): x(x), y(y) {}
    friend bool operator <(const point &a, const point &b) { return a.x < b.x; }
};

struct query {
    int x, y, v, id;
    query() = default;
    query(int x, int y, int v, int id): x(x), y(y), v(v), id(id) {}
    friend bool operator <(const point &a, const point &b) { return a.x < b.x; }
};

constexpr int N = 1e7 + 10;

int s[N];

#define lowbit(x) ((x) & -(x))

void add(int x, int v) {
    for (; x < N; x += lowbit(x)) s[x] += v;
}

int sum(int x) {
    int r = 0;
    for (; x; x -= lowbit(x)) r += s[x];
    return r;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
    int n, m; cin >> n >> m; vector<point> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i].x >> a[i].y, ++a[i].x, ++a[i].y;
    sort(range(a)); vector<query> q(m << 2);
    for (int i = 0; i < m; ++i) {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        ++x1, ++y1, ++x2, ++y2;
        q[i] = query(x2, y2, 1, i);
        q[i + m] = query(x1 - 1, y2, -1, i);
        q[i + 2 * m] = query(x2, y1 - 1, -1, i);
        q[i + 3 * 2] = query(x1 - 1, y1 - 1, 1, i);
    } sort(range(q)); vector<int> ans(m);
    int cur = 0; for (int i = m; i < (m << 2); ++i) {
        int x = q[i].x, y = q[i].y, v = q[i].v;
        for (; cur < n && a[cur].x <= x; ++cur) add(a[cur].y, 1);
        ans[q[i].id] += v * sum(y);
    } for (int i : ans) cout << i << endl;
    return 0;
}

基础——亚特兰蒂斯(Atlantis)问题

最经典的扫描线喵:P5490 【模板】扫描线。

题目描述:给出平面内 \(n\) 个矩形的左下以及右上坐标,求出所有矩形构成的图形的面积。

现在假设一根线从下往上扫:

我们可以把整个矩形分成 \(5\)个颜色不同的小矩形。

每个矩形的面积该如何求呢?我们按照 \(y\) 坐标从下往上遍历每个矩形,高也就是相邻的矩形的 \(y\) 坐标的差值,矩形的长度是若干条线段的交集,并且在不断发生变化。

我们使用线段树维护矩形的长度:对于每个矩形,记下面的边为「入边」,上面的边为「出边」。

按照 \(y\) 坐标从下往上遍历每个矩形,入边先被扫描到,将入边加入到线段树,出边后被扫描到,将出边从线段树中删除。对于每一条入边和出边,可以标记为 \(\pm1\),代表加入和删除。

线段树维护的东西都是点,但是我们需要维护的是区间,那么我们可以把区间下放到点上,也就是每一个叶子节点维护的是一个线段。

维护一个 \(\mathit{cov}\) 为当前区间被几个矩形覆盖,以及一个 \(\mathit{len}\) 表示当前区间被覆盖的区间长度。

我们扫描到一条线,将该条线段加入到线段树维护,修改对应区间的 \(\mathit{cov}\)

向上更新表示当前区间被覆盖的区间长度,当 \(\mathit{cov}\)\(0\),则代表整个区间被覆盖;\(\mathit{cov}\)\(0\),则统计子区间被覆盖的长度

注意到维护的线段,一定是成对出现的,因此不需要标记下传,最后一定会被减回去。

我们根据代码来解释一些细节问题:

  • 下文代码表示的是在标准平面直角坐标系,\(x\) 轴水平向右、\(y\) 轴数值向上,扫描线从下往上扫的。
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

#define range(x) x.begin(), x.end()
  • 头文件等。
struct line {
    int y, x1, x2, v;
    line() = default;
    line(int y, int x1, int x2, int v): y(y), x1(x1), x2(x2), v(v) {}
    friend bool operator <(const line &a, const line &b) { return a.y < b.y; }
};
  • 定义了线段,包括其纵坐标,横坐标上延伸的起始和终止,以及权值。
class segment {

private:

struct emm {
    int l, r;
    int cnt, len;
};

vector<emm> a;
  • 个人习惯,使用 class + struct 定义线段树,标记左端点 \(l\) 及右端点 \(r\)(表示的是原坐标,即未经离散化的,而且注意此处的端点也与普通线段树有区别,是左闭右开的区间,而非闭区间)。
void push_up(int k) {
    if (a[k].cnt) a[k].len = a[k].r - a[k].l;
    else a[k].len = a[k * 2].len + a[k * 2 + 1].len;
}
  • 标记上传,不能全用 else 里的语句的原因是,没有标记下传,只能根据这个区间当前有没有被覆盖来考虑。而如果其两个子区间都被完全覆盖,而其未被标记为完全覆盖,会调用子节点的信息,加起来也是其区间总长度。
void build(vector<int> &p, int k, int l, int r) {
    a[k].l = p[l], a[k].r = p[r];
    a[k].len = a[k].cnt = 0;
    if (r - l == 1) return;
    int mid = l + r >> 1;
    build(p, k * 2, l, mid);
    build(p, k * 2 + 1, mid, r);
}
  • 建树,传入的 \(l\)\(r\) 并不是区间的端点,而是区间端点的离散化的 \(\mathit{rank}\),需要调用 \(p_i\) 来寻找原坐标,用来标记给线段树维护的区间。而终止条件 r - l == 1 是因为线段树的叶子结点维护的是区间,而非单一的节点,需要两个端点。不需要在这里标记上次的原因是还没有任何有意义的值赋给它。
void modify(int k, int p, int q, int v) {
    int l = a[k].l, r = a[k].r;
    if (l >= p && r <= q) return void((a[k].cnt += v, push_up(k)));
    if (a[k * 2].r > p) modify(k * 2, p, q, v);
    if (a[k * 2 + 1].l < q) modify(k * 2 + 1, p, q, v);
    push_up(k);
}
  • 修改操作,与普通线段树唯一的区别是,修改一个区间仅仅是标记它被完全覆盖,然后标记上传,也就是重新计算这个区间的 \(\mathit{len}\) 值。
public:

segment(vector<int> &p, int n) { a.resize(n << 3); build(p, 1, 1, n); }
void change(int l, int r, int v) { modify(1, l, r, v); }
int xht() { return a[1].len; }

};
  • 没什么意义,只是简化下面调用的代码。
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
    int n; cin >> n;
    vector<line> a(n * 2);
    vector<int> p(n * 2);
    p.push_back(-1);
    for (int i = 0; i < n; ++i) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        p[i] = x1, p[i + n] = x2;
        a[i] = line(y1, x1, x2, 1);
        a[i + n] = line(y2, x1, x2, -1);
    }
    sort(range(a)), sort(range(p));
    int tot = unique(range(p)) - p.begin();
  • 输入和离散化,注意 \(p\) 要加一个极小值(根据题目不同,要比最小坐标值还要小,这道题左边都是非负,因此取 \(-1\) 是可以的,有的还要取更小的值)。
    segment seg(p, tot - 1); ll ans = 0;
    for (int i = 0; i < 2 * n - 1; ++i) {
        seg.change(a[i].x1, a[i].x2, a[i].v);
        ans += 1ll * seg.xht() * (a[i + 1].y - a[i].y);
    } cout << ans << endl;
    return 0;
}
  • 建树和询问。具体的看上面对于小矩阵面积计算的解释。

进阶——矩形周长(Picture)问题

例题:P1856 [IOI1998] [USACO5.5] 矩形周长Picture。

题目描述:给出平面内 \(n\) 个矩形的左下以及右上坐标,求出所有矩形构成的图形的周长。

首先计算横线,不难发现,在扫描线向上平移的时候,增加或者减少的长度就是周长的共线,因为我们的线段是一条一条加的,所以就保证了,加上去,一定是加了一个表面;减去,也一定是减去了一个表面。

因此对答案的贡献就是上一次获得的长度与这一次的长度的差的绝对值。再解释一下绝对值,因为周长一定是正的,而每次长度变化量就是新增的表面。

所以我们需要加个绝对值。而竖线的长度有两种方法:

  1. 从左到右再扫描一遍。
  2. 扫描横线的过程中,同步记录没有重合的矩形个数,再乘上二就是新增的纵边数,在乘上这个矩形的高度就是新增的竖线的长度了。

作者太菜,只会第一个。

  • 然后我们根据代码再详解一下:
#include <bits/stdc++.h>

using namespace std;

#define range(x) x.begin(), x.end()

using ll = long long;

struct line {
    int y, x1, x2, v;
    line() = default;
    line(int y, int x1, int x2, int v): y(y), x1(x1), x2(x2), v(v) {}
    friend bool operator <(const line &a, const line &b) { return a.y < b.y; }
};

class segment {

private:

struct emm {
    int l, r;
    int cov, len;
};

vector<emm> a;

void push_up(int k) {
    if (a[k].cov) a[k].len = a[k].r - a[k].l;
    else a[k].len = a[k * 2].len + a[k * 2 + 1].len;
}

void build(vector<int> &p, int k, int l, int r) {
    a[k].l = p[l], a[k].r = p[r];
    a[k].cov = a[k].len = 0;
    if (r - l == 1) return;
    int mid = r + l >> 1;
    build(p, k * 2, l, mid);
    build(p, k * 2 + 1, mid, r);
}

void modify(int k, int p, int q, int v) {
    int l = a[k].l, r = a[k].r;
    if (l >= p && r <= q) return void((a[k].cov += v, push_up(k)));
    if (a[k * 2].r > p) modify(k * 2, p, q, v);
    if (a[k * 2 + 1].l < q) modify(k * 2 + 1, p, q, v);
    push_up(k);
}

public:

segment(vector<int> &p, int n) { a.resize(n << 3); build(p, 1, 1, n); }
int xht() { return a[1].len; }
void change(int l, int r, int v) { modify(1, l, r, v); }

};

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
    int n, lt; cin >> n;
  • 从这下面开始才和上一个有本质不同,也就是建了两个扫描线,其中标号 \(1\) 的是向上扫描的,标号 \(2\) 的是向右扫描的。
    vector<line> a1(n * 2), a2(n * 2);
    vector<int> p1(n * 2); vector<int> p2(n * 2);
    p1.push_back(-1e5); p2.push_back(-1e5);
    for (int i = 0; i < n; ++i) {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        p1[i] = x1, p1[i + n] = x2;
        p2[i] = y1, p2[i + n] = y2;
        a1[i] = line(y1, x1, x2, 1);
        a1[i + n] = line(y2, x1, x2, -1);
        a2[i] = line(x1, y1, y2, 1);
        a2[i + n] = line(x2, y1, y2, -1);
    }
    sort(range(a1)), sort(range(p1));
    sort(range(a2)), sort(range(p2));
    int tot1 = unique(range(p1)) - p1.begin();
    int tot2 = unique(range(p2)) - p2.begin();
    segment seg1(p1, tot1 - 1);
    segment seg2(p2, tot2 - 1);
    ll res = 0; int lt1 = 0, lt2 = 0;
    for (int i = 0; i < 2 * n; ++i) {
        seg1.change(a1[i].x1, a1[i].x2, a1[i].v);
        seg2.change(a2[i].x1, a2[i].x2, a2[i].v);
        int rt1 = seg1.xht(); res += abs(rt1 - lt1);
        int rt2 = seg2.xht(); res += abs(rt2 - lt2);
        lt1 = rt1, lt2 = rt2;
    } cout << res << endl;
    return 0;
}

是不是很简单

练习题

题单:https://www.luogu.com.cn/training/479926。

Reference

[1] https://oi-wiki.org/geometry/scanning/

[2] https://blog.csdn.net/qq_30320171/article/details/129787418

[3] https://www.luogu.com.cn/article/iilru8ad

[4] https://www.luogu.com.cn/article/9cuyuf44

[5] https://www.youtube.com/watch?v=YnIxejYW7cE文章来源地址https://www.toymoban.com/news/detail-838668.html

到了这里,关于计算几何——扫描线 学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 扫描线问题

    观前提醒 :「文章仅供学习和参考,如有问题请在评论区提出」 目录 引入 矩形面积并(Atlantis 问题) P5490 【模板】扫描线 - 洛谷 Robot - Virtual Judge 4350. 覆盖的面积 - AcWing 矩形并集的周长 262. 海报 - AcWing 二维数点 P2163 园丁的烦恼 - 洛谷 Pair Sum and Perfect Square - Virtual Judge 参考

    2024年02月14日
    浏览(41)
  • 【单调队列】 单调队列的“扫描线”理解

       “如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理 比你强,而且比你影响时间更长。 某种意义上,数学思维是生活中的思考的延伸。   算法学习笔记(66): 单调队列。引用 Pecco 的算法笔记。   在这里给出一种扫描线的理解。   我们以滑动

    2024年02月12日
    浏览(45)
  • 计算机安全学习笔记(V):UDP和网络扫描

    UDP是最简单的传输协议。多个程序(服务)可以在主机上侦听,因此操作系统需要知道将流量发送到哪个程序。 在传输协议中,每个程序都与源和目标处的端口相关联,该端口显示为程序的套接字。UDP 在 IP 内携带有效负载数据,附加功能最少。UDP的特点包括消息不被确认,

    2024年01月21日
    浏览(48)
  • 《计算机视觉中的多视图几何》笔记(2)

    本章主要介绍本书必要的几何知识与符号。 简要介绍了平面几何,本书将以代数和几何混合的方式来讲解。 行向量与列向量 本书默认所有向量的都是列向量,比如 x x x ,那么 x T x^T x T 就是行向量。对于一个行向量 ( x , y ) (x,y) ( x , y ) ,我们就有 x = ( x , y ) T x=(x,y)^T x = ( x

    2024年02月09日
    浏览(44)
  • Python学习4:计算几何形状的表面积与体积

    输入一个表示几何形状名称的字符串,再在一行内输入这种图形的数据,根据表示名称的字符串选择合适的公式计算几何形状的(表)面积和体积,若为二维图形,只计算面积,若为三维图形,计算其表面积与体积,结果严格保留2位小数。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬

    2024年02月08日
    浏览(47)
  • 【UnityShader入门精要学习笔记】第四章(4)矩阵的几何意义

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 (该系列笔记中大多数都会复习前文的知识,特别是前文知识非

    2024年02月01日
    浏览(45)
  • 线性代数学习笔记4-1:线性方程组的数学和几何意义、零空间/解空间/核

    求解方程 A x ⃗ = v ⃗ mathbf Avec x=vec v A x = v 首先说明系数矩阵的 行数和列数的意义 : 对于系数矩阵 A mathbf A A ,其行数代表方程个数,列数代表未知量个数 对于系数矩阵 A mathbf A A ,矩阵对应线性变换 矩阵 行数 代表变换后的基向量、 x ⃗ vec x x 和 v ⃗ vec v v 等向量的

    2024年02月02日
    浏览(49)
  • 《Android学习笔记》Android12蓝牙扫描不到设备的权限问题

    Android12 关于蓝牙这部分新增了 BLUETOOTH_SCAN 、 BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 权限,这些权限都属于敏感权限,都需要在代码中动态申请。移除了Android11 及以下版本中必须申请的位置权限[ FINE_LOCATION ] 和 [ COARES_LOCATION ]。 1、在Manifest.xml清单文件中添加对应的权限。 其中 An

    2024年02月15日
    浏览(51)
  • 【计算机视觉|人脸建模】学习从4D扫描中获取的面部形状和表情的模型

    本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题: Learning a model of facial shape and expression from 4D scans 链接:Learning a model of facial shape and expression from 4D scans | ACM Transactions on Graphics Permission to make digital or hard copies of part or all of this work for personal or classroom use is

    2024年02月07日
    浏览(60)
  • 深度学习·理论篇(2023版)·第002篇深度学习和计算机视觉中的基础数学知识01:线性变换的定义+基于角度的线性变换案例(坐标变换)+点积和投影+矩阵乘法的几何意义+图形化精讲

    💕 恭喜本博客浏览量达到两百万,CSDN内容合伙人,CSDN人工智能领域实力新星~ 🧡 本文章为2021版本迭代更新版本,在结合有效知识的基础上对文章进行合理的增加,使得整个文章时刻顺应时代需要 🧡 本专栏将通过系统的深度学习实例,从可解释性的角度对深度学习的原理

    2023年04月08日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包