0%

cv2 的参数输入格式

cv2 全部改成了用 numpy.ndarray 作为图像处理格式。只是操作图像的时候:

  1. 数据类型必须是 uint8。
  2. 形状必须是 (H, W, C) 或者 (H, W),后者为灰度图。
  3. 必须是连续的,必要时得用 np.ascontiguousarray() 转换一下(不然报错会很晦涩)。

如果使用训练模型用的张量转 numpy,一定要记得把批量维度去掉,并且转 channels-last 格式。否则 cv2 不能正常识别图像,并可能报错(报错信息晦涩难懂)。

此外,pillow 库的 Image 默认格式为 RGB,而 cv2 中图像的默认格式为 BGR,需要按需转换。

cv.imwrite()

cv2.imwrite() 是将 numpy 数组表示的图像写入到磁盘。

原文

https://developer.nvidia.com/blog/using-cuda-warp-level-primitives/

笔记

Warp 内规约

#define FULL_MASK 0xffffffff
for (int offset = 16; offset > 0; offset /= 2)
    val += __shfl_down_sync(FULL_MASK, val, offset);

先在 warp 内用原语规约,比在 block 级别用共享内存规约快很多。(虽然也有 __reduce_add_sync() 函数,但是截至 2024 年 9 月 5 日只支持 unsigned 和 int 类型,给浮点数做规约就要用 __shfl_xx_sync() 系列。)

CUDA 9 的相关 API

直接从原博客摘录:

原文链接

https://developer.nvidia.com/blog/cuda-pro-tip-optimized-filtering-warp-aggregated-atomics/

场景

src 中大于 0 的数字移动到 dst 中去,并返回新数组的元素数量。和 C++ 的 std::copy_if 相似。

int filter(int *dst, const int *src, int n) {
  int nres = 0;
  for (int i = 0; i < n; i++)
    if (src[i] > 0)
      dst[nres++] = src[i];
  // return the number of elements copied
  return nres;
}

如果用全局内存实现:

__global__ 
void filter_k(int *dst, int *nres, const int *src, int n) {
  int i = threadIdx.x + blockIdx.x * blockDim.x;
  if(i < n && src[i] > 0)
    dst[atomicAdd(nres, 1)] = src[i];
}

本机没有 NVIDIA GPU,只是无聊想要试试能不能在没有 GPU 的机器(宿主机没 GPU)上做编译工作

按照 官网链接,选择 Ubuntu 即可。cuda-repo-ubuntu2404-12-6-local_12.6.1-560.35.03-1_amd64.deb 这个文件占了 3.2G,安装之后就更大了。

安装完成之后,可以在 /usr/local/cuda-12.6/bin/ 中找到 nvcc。加到 PATH 之后,编译都能正常进行,但是因为没有 GPU,运行时 CUDA 相关的 API 都会报错,仅此而已。

这是我写的某个测试的脚本:

  1. B 需要等待 A 程序先运行起来之后才能启动。
  2. A 退出之后,应该中止 B。
  3. 代码能保证 B 不会在 A 之前中止。
#!/bin/bash

./build/test/A -n 5000 &
PROC_A=$!
sleep 3
python test/B.py &
PROC_B=$!
tail --pid=$PROC_A -f /dev/null
kill $PROC_B

tail --pid=$PROC_A -f /dev/null 这一句是等待特定进程结束的关键。参考 https://stackoverflow.com/questions/1058047/wait-for-a-process-to-finish

问题起因

我的某个程序会自己打印 \r\b 字符以在终端上起到提示效果,但是如果将内容重定向到文件,那么显示出来效果就不好。在很多阅读器中特殊字符不能被正确显示,在 VS Code 中 \r 会换行,而 \b 也不会真正起到删除的作用。

方法 1:cat 或者 less -r

假设现在文件 A.log 中包含了大量 \r\b,想要阅读它可以直接将其 cat 到终端,或者使用 less -r A.log 来阅读。less -r 是比较推荐的,因为还能用 / 查找、用 & 过滤

方法 2:col

如果想要真正保存一份和我们在终端看上去一样的文件,可以使用:

col -b < A.log > A1.log

想要搜索 bash 内置命令的帮助信息,但是发现找不到,比如 man ulimit 没有对应的页面,怎么办呢?

其实和 bash 内置命令相关的帮助信息就在 bash 的 man 手册当中,可以用 man bash 来查看。然后在弹出的 less 阅读器中,搜索 SHELL BUILTIN COMMANDS 就能找到这一栏了(用小写就能搜索)。

我的测试环境是 wsl,系统是空载的。理想的情况是两次程序的 pid 连续:

$ grep -i '^pid:' /proc/self/status
Pid:    3355
$ grep -i '^pid:' /proc/self/status
Pid:    3356

但实际上我发现我每次回车(不运行 grep)都会导致 Pid +7,运行 grep 时会 +8(合理,毕竟 grep 本身也是一个进程)。

我以前以为是 bash 在创建子进程的时候会多运行一些东西,所以每次创建的子进程 Pid 不连续。现在发现回车都会导致 Pid 增长,才发现是 PS1 变量的问题。我的 PS1 变量包含了一些代码,用来让 prompt 更加美观。

用以下方式修改 PS1

下载前使用 -F 选项来查看视频格式:

yt-dlp -F https://www.youtube.com/watch?v=lNPZV9Iqo3U

下载选定格式的视频(yt-dlp 给 YouTube 视频选的默认格式通常很模糊,但是因为同时包含音频和视频,所以就成了默认):

yt-dlp -N 4 -f 137+139 https://www.youtube.com/watch?v=lNPZV9Iqo3U

-N 4 表示分成 4 块下载,默认是 1 块。-f 表示格式,如果安装了 ffmpeg,可以选择多个格式,这些格式会被合并到一个视频中(audio only 和 video only 的格式的合并)。

std::shared_ptr<T> 的内存开销

std::shared_ptr<T>
    element_type*    _M_ptr;          // Contained pointer. sizeof(intptr_t) 字节
    __shared_count<_Lp>  _M_refcount; // Reference counter. sizeof(intptr_t) 字节
        _Sp_counted_base<_Lp>*  _M_pi;
std::_Sp_counted_base<__default_lock_policy>
    // vtable pointer               // sizeof(intptr_t) 字节
    _Atomic_word  _M_use_count;     // #shared                4 字节,实际上是 int 类型
    _Atomic_word  _M_weak_count;    // #weak + (#shared != 0) 4 字节

其中,记录 use count 是为了判断什么时候可以释放共享指针指向的对象;记录 weak count 是为了判断什么时候可以安全释放控制块本身。即便是共享指针指向对象已经被释放(use count 归零),也可能有弱指针会尝试转换成共享指针,因此应该保证这些弱指针能安全查询控制块。还有一点,如果用 std::make_shared 创建共享指针,use count 归零而 weak count 不归零时,共享对象只是被析构,其内存会等到 weak count 归零时一起被释放。可以参考 https://stackoverflow.com/a/49585948/

在 64 位环境下,一个 std::shared_ptr<T> 是 16 字节(8 + 8),其中控制块的真实大小是 16 字节(4 + 4 + 8)。(在 std::shared_ptr<T> 中存储的是指向控制块的指针、而不是控制块本身,这样才能保证使用相同的控制块。)

std::_Sp_counted_base<_Lp> 的大小

不过,std::_Sp_counted_base 还支持其他的上锁策略,从 /usr/include/c++/12/bits/shared_ptr_base.h 的以下代码可以看出一共有 4 种可选的上锁类型。