准备工作

  • 安装 Scoop
  • 安装 Visual Studio 2022
  • 安装 Ninja v1.11.1
  • 安装 Python v3.10 和 Numpy
  • 安装 wget 和 7zip
  • 安装 Cuda SDK v11.7
  • 一键下载 OpenCV 4.6.0 源码并编译
  • 部署
  • 测试 OpenCV

安装 Scoop

打开 PowerShell 后执行:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-WebRequest get.scoop.sh | Invoke-Expression

安装参考: https://scoop.sh

安装 Visual Studio 2022

  1. 从 https://visualstudio.microsoft.com 下载你喜欢的版本,推荐免费的 Community 版本
  2. 安装 使用C++的桌面开发 工作负荷

    使用所选工具(包括 MSVC、CLang、CMake 或 MSBuild)生成适用于 Windows 的现代 C++ 应用

安装 Ninja v1.11.1

scoop install ninja@1.11.1

安装 Python v3.10 和 Numpy

scoop bucket add versions
scoop install python310
pip install numpy

安装 wget 和 7zip

scoop install wget 7zip

安装 Cuda SDK v11.7

安装流程: https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html#installing-cuda-development-tools

一键下载 opencv 4.6.0 源码并编译

download_and_build_opencv.bat

@ECHO OFF
ECHO -- Downloading OpenCV Source Code --
wget https://github.com/opencv/opencv/archive/refs/tags/4.6.0.zip -O opencv-4.6.0.zip
IF %ERRORLEVEL% NEQ 0 GOTO :error
7z x -y opencv-4.6.0.zip
IF %ERRORLEVEL% NEQ 0 GOTO :error
wget https://github.com/opencv/opencv_contrib/archive/refs/tags/4.6.0.zip -O opencv_contrib-4.6.0.zip
IF %ERRORLEVEL% NEQ 0 GOTO :error
7z x -y opencv_contrib-4.6.0.zip
IF %ERRORLEVEL% NEQ 0 GOTO :error
SET "USERHOME=%USERPROFILE:\=/%"
SET OPENCV_SRC_DIR=%cd%/opencv-4.6.0
SET OPENCV_CONTRIB_SRC_DIR=%cd%/opencv_contrib-4.6.0
SET OPENCV_BUILD_DIR=%cd%/opencv-build
SET SCOOP_ROOT=%USERHOME%/scoop
SET PYTHON_PATH=%SCOOP_ROOT%/apps/python310/current
SET PYTHON_LIBRARY=%PYTHON_PATH%/libs/python310.lib
SET PYTHON_VERSION=3.10
SET CUDA_SDK_DIR="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.7"
@REM CUDA_ARCH=8.6 for GeForce RTX 3080 Ti
@REM Choice value for your GPU with: https://developer.nvidia.com/cuda-gpus
SET CUDA_ARCH=8.6
ECHO -- Starting OpenCV Configuration --
call "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"
@REM If you want to use the Visual Studio 2019 MSVC:
@REM call "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat" -vcvars_ver=14.29
cmake ^
-S%OPENCV_SRC_DIR% ^
-B%OPENCV_BUILD_DIR% ^
-DCMAKE_INSTALL_PREFIX=%OPENCV_BUILD_DIR%/install ^
-DOPENCV_EXTRA_MODULES_PATH=%OPENCV_CONTRIB_SRC_DIR%/modules ^
-DPYTHON3_EXECUTABLE=%PYTHON_PATH%/python.exe ^
-DPYTHON3_INCLUDE_DIR=%PYTHON_PATH%/include ^
-DPYTHON3_LIBRARY=%PYTHON_LIBRARY% ^
-DPYTHON3_NUMPY_INCLUDE_DIRS=%PYTHON_PATH%/Lib/site-packages/numpy/core/include ^
-DPYTHON3_PACKAGES_PATH=%PYTHON_PATH%/Lib/site-packages ^
-DPYTHON_INCLUDE_DIRS=%PYTHON_PATH%/include ^
-DPYTHON_LIBRARIES=%PYTHON_LIBRARY% ^
-DCUDA_TOOLKIT_ROOT_DIR=%CUDA_SDK_DIR% ^
-DENABLE_FAST_MATH=ON ^
-DCUDA_FAST_MATH=ON ^
-DWITH_CUDA=ON ^
-DWITH_CUDNN=ON ^
-DOPENCV_DNN_CUDA=ON ^
-DCUDA_ARCH_BIN=%CUDA_ARCH% ^
-DCUDA_ARCH_PTX=%CUDA_ARCH% ^
-DWITH_OPENGL=ON ^
-DWITH_GSTREAMER=ON ^
-DWITH_FREETYPE=ON ^
-DBUILD_opencv_python3=ON ^
-DCMAKE_CONFIGURATIONS_TYPES=Release ^
-DCMAKE_BUILD_TYPE=Release ^
-DOPENCV_PYTHON3_VERSION=%PYTHON_VERSION% ^
-DBUILD_TESTS=OFF ^
-DBUILD_PERF_TESTS=OFF ^
-DBUILD_JAVA=OFF ^
-DBUILD_opencv_objc_bindings_generator=OFF ^
-DBUILD_opencv_js=OFF ^
-GNinja ^
-DBUILD_opencv_world=ON
IF %ERRORLEVEL% NEQ 0 GOTO :error
ECHO -- OpenCV Configuration has finished, press any key to proceeding to build phase... --
PAUSE > NUL
cmake --build %OPENCV_BUILD_DIR% --target install
IF %ERRORLEVEL% NEQ 0 GOTO :error
ECHO -- OpenCV Build has finished, press any key to install... --
PAUSE > NUL
pip uninstall opencv-python opencv-contrib-python
IF %ERRORLEVEL% NEQ 0 GOTO :error
COPY %OPENCV_BUILD_DIR%/lib/python3/cv2.cp310-win_amd64.pyd %PYTHON_PATH%/Lib/site-packages/cv2.cp310-win_amd64.pyd /Y
IF %ERRORLEVEL% NEQ 0 GOTO :error
COPY %OPENCV_BUILD_DIR%/install/x64/vc17/bin/*.dll %PYTHON_PATH%/Scripts /Y
IF %ERRORLEVEL% NEQ 0 GOTO :error
pip install opencv-contrib-python
IF %ERRORLEVEL% NEQ 0 GOTO :error
ECHO -- all finished, press any key to exit... --
PAUSE > NUL
GOTO :EOF

:error
ECHO -- Error has occurred!!! press any key to exit... --
PAUSE > NUL

点我下载:download_and_build_opencv.bat

部署

  1. 卸载 opencv-python opencv-contrib-python
  2. 拷贝 %OPENCV_BUILD_DIR%/lib/python3/cv2.cp310-win_amd64.pyd 到 %PYTHON_PATH%/Lib/site-packages 目录
  3. 拷贝 %OPENCV_BUILD_DIR%/install/x64/vc17/bin/*.dll 到 Windows 的 %PATH% 目录 或者 %PYTHON_PATH%/Scripts 目录
  4. 重新安装 opencv-contrib-python
pip uninstall opencv-python opencv-contrib-python
COPY %OPENCV_BUILD_DIR%/lib/python3/cv2.cp310-win_amd64.pyd %PYTHON_PATH%/Lib/site-packages/cv2.cp310-win_amd64.pyd /Y
COPY %OPENCV_BUILD_DIR%/install/x64/vc17/bin/*.dll %PYTHON_PATH%/Scripts /Y
pip install opencv-contrib-python

测试 OpenCV

python -c "import cv2; print(cv2.__version__); print(cv2.cuda.getCudaEnabledDeviceCount())"

更换原因

Hexo 作为博客引擎长期以来使用还是非常方便的。

但由于其本身是基于nodejs和npm,其中使用到的各种npm库的嵌套依赖关系十分杂乱。有可能因為某个子包出BUG而导致引用了该包的所有库均出现各种问题。

已经受够了长期以来明明没有更新文章,但本地却需要保持各种npm包为较新的版本所花费的额外精力。

所以干脆直接更换为基本没有三方依赖的 Hugo,并自己重写了自己使用的主题。

如果有Hugo用户恰好喜欢这款主题,欢迎使用: Fika theme for Hugo

Hexo文章 转 Hugo文章 的脚本

hexo_to_hugo.py

# pip install yaml
# usage: python hexo_to_hugo.py
import os
import re
import yaml

# fill default date with this GMT zone
GMT = '+08:00' # for SG time zone

HEADER_FLAG = '---\n'

def file_lines(fname):
    f = open(fname, "r", encoding='utf-8')
    ret = f.readlines()
    f.close()
    return ret


def parser_head_lines(lines: list[str]) -> list[str]:
    header_lines = list[str]([])
    in_header = False
    for i, line in enumerate(lines):
        if line == HEADER_FLAG:
            if not in_header:
                in_header = True
                continue
            else:  # header end flag founded
                break
        if in_header:
            header_lines.append(line)
    return header_lines


def revise_header_flag(lines):
    revised = False
    if len(lines) == 0:
        return lines, revised
    if lines[0] == HEADER_FLAG:
        return lines, revised
    lines.insert(0, HEADER_FLAG)
    revised = True
    return lines, revised


def revise_header_content(header_lines: list[str]):
    revised = False
    for i, line in enumerate(header_lines):
        # date line
        rdate = re.search(r'date:\s+(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})', line, re.M)
        if rdate:  # date line for need revise to TZ format
            header_lines[i] = f'date: {rdate.group(1)}-{rdate.group(2)}-{rdate.group(3)}T{rdate.group(4)}:{rdate.group(5)}:{rdate.group(6)}{GMT}\n'
            revised = True

    # revise for tags and categories to type list
    header_revised = False
    header = yaml.safe_load(''.join(header_lines))
    if 'categories' in header: # make sure categories are type list
        categories = header['categories']
        if not isinstance(categories, list):
            if categories == None:
                header['categories'] = []
            else:
                header['categories'] = [categories]
            header_revised = True

    if 'tags' in header: # make sure tags are type list
        tags = header['tags']
        if not isinstance(tags, list):
            if tags == None:
                header['tags'] = []
            else:
                header['tags'] = [tags]
            header_revised = True

    if header_revised:
        header_lines = yaml.dump(header, default_flow_style=False, allow_unicode=True).splitlines(True)
        revised = True
    return header_lines, revised


def save_file(fname, lines):
    f = open(fname, 'w', encoding='utf-8', newline='\n')
    f.writelines(lines)
    f.close()


def main():
    HEADER_FLAG = '---\n'
    print('--- starting revise for posts... ---')
    POST_PATH = os.path.join('content', 'posts')
    posts_count = 0
    header_flag_revised_count = 0
    header_revised_count = 0
    files = os.listdir(POST_PATH)
    for fname in files:
        fullpath = os.path.join(POST_PATH, fname)
        if not os.path.isfile(fullpath):
            continue
        lines = file_lines(fullpath)
        lines, header_flag_revised = revise_header_flag(lines)
        if header_flag_revised:
            print(f'revised header flag: {fname}')
            header_flag_revised_count += 1
        header_lines = parser_head_lines(lines)
        body_lines = lines[len(header_lines)+2:]  # skip two header flag lines
        header_lines, header_revised = revise_header_content(header_lines)
        if header_revised:
            print(f'revised date: {fname}')
            header_revised_count += 1
        if header_flag_revised or header_revised:
            print(f'save revised file: {fname}')
            header_lines.insert(0, HEADER_FLAG)
            header_lines.append(HEADER_FLAG)
            save_file(fullpath, header_lines + body_lines)
        posts_count += 1
    print(f'--- revise over. ---\nposts: {posts_count}\nheader flag revised: {header_flag_revised_count}\nheader revised: {header_revised_count}')


if __name__ == "__main__":
    main()

使用方法: 进入 Hugo 站点根目录运行

python hexo_to_hugo.py

脚本下载: hexo_to_hugo.py

内容部分占满页面的剩余高度

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dock.Full</title>
  <style type="text/css">
    html,
    body,
    #full {
      color: #EFEFEF;
      background-color: #423F3E;
      margin: 0;
      padding: 0;
      height: 100%;
    }

    #full {
      background-color: #171010;
      display: flex;
      flex-direction: column;
    }

    #someid {
      background-color: #362222;
      flex-grow: 1;
    }
  </style>
</head>

<body>
  <div id="full">
    <div id="header">Dock.Top</div>
    <div id="someid">Dock.Full</div>
  </div>
</body>

</html>

线上预览

登录框居中显示

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style type="text/css">
    html,
    body,
    #parent {
      color: #EFEFEF;
      background-color: #423F3E;
      margin: 0;
      padding: 0;
      height: 100%;
    }

    #parent {
      background-color: #171010;
      /* 使用Flex布局 */
      display: flex;
      /* 主轴位于中间 */
      justify-content: center;
      /* 交叉轴位于中间 */
      align-items: center;
    }

    #someid {
      background-color: #362222;
      width: 200px;
      height: 200px;
    }
  </style>
</head>

<body>
  <div id="parent">
    <div id="someid">Dialog Content</div>
  </div>
</body>

</html>

线上预览

目前在 Windows 安装软件特别喜欢使用 Scoop 进行管理

使用 Scoop 安装 PowerShell Core 最新版

scoop install pwsh

发现在 VSCode 的 Terminal Shells 列表里面依然看不到新安装的 PowerShell Core

VSCode Terminal Shells

但我又不想使用默认的 PowerShell 或者 cmd 当我的 Shell 这时就可以这样配置:

  1. 在 VSCode 里打开 Command Palette(Ctrl+Shift+P) 选择 Preferences: Open Settings (JSON) 打开用户配置文件 settings.json
  2. 添加如下配置
    {
        "terminal.integrated.shell.windows": "C:/Users/${env:USERNAME}/scoop/apps/pwsh/current/pwsh.exe",
        "terminal.integrated.automationShell.windows": "C:/Users/${env:USERNAME}/scoop/apps/pwsh/current/pwsh.exe"
    }
    
    注:如果用户自定义了 Scoop 的路径($env:SCOOP) 则配置为
    {
        "terminal.integrated.shell.windows": "${env:SCOOP}/apps/pwsh/current/pwsh.exe",
        "terminal.integrated.automationShell.windows": "${env:SCOOP}/apps/pwsh/current/pwsh.exe"
    }
    
  3. 重新运行 VSCode

可以看到我们 VSCode 已经使用了 PowerShell Core 作为默认 Terminal Shells 了

VSCode Default Terminal Shell

参考文献:

Quick Start · lukesampson/scoop Wiki

最近用 Github 比较频繁,无意间点开 GitHub Pages 的代码库时发现一个大大的 Dependabot alerts 顶在上面

点开后发现最大的一个警告就是这个:

顺着这个 dependencies 一路追查发现是 hexo-prism-plugin 这个插件使用的 由于该插件太久没更新,而且发现 hexo.io 最新版本已经集成了 prism 的代码高亮功能 所以更新 Hexo 版本并启用自带的代码高亮功能应该就可以解决了

解决方法

  1. 卸载 hexo-prism-plugin 插件
  2. 更新所有相关 dependencies 到最新版本
  3. 配置 _config.yml
prismjs:
  enable: true
  preprocess: true
  line_number: true
  tab_replace: '    '
  1. 重新生成

引用文章: Syntax Highlighting | Hexo

突然心血来潮想把自己的笔记用阿里云CDN加速一下,但域名就需要备案。 而我本身的域名不想因为备案就迁回到国内的域名服务商(这里有一段在国内购买域名的黑历史) 所以干脆重新购买了一个新的域名 www.kaij.cn 用来备案

直接在阿里云上面买域名可以不用等迁移的时间 顺便一说,使用阿里云的快速备案服务来备案真的太方便了

但备案的时候遇到了一点小问题,之前起的博客名称无法用于备案,收到了提示:

网站名称“ 李略帅 ”不合格,个人网站名称不能涉及到行业、企业等信息,且个人网站名称请勿涉及个人姓名、地名,请不要用纯数字或字母组成,不能包含特殊符号,不能使用成语;网站名称请使用3个以上汉字命名,请不能使用XXX个人空间、资讯、网站、网络、网址、爱好者、作品展示、工作室、平台、主页、热线、社团、导航这种的格式命名;网站名称中不能带有博客、论坛、在线、社区、交流等字样,若要带有此类信息,需提交前置审批文;具体命名要求请参考专区文章:https://help.aliyun.com/knowledge_detail/36948.html#title-lhm-b1g-ehx

按照这些要求来说 XXX的个人博客、XXX的个人主页 这类肯定是不行了。由于也禁止使用成语,所以想翻翻字典来找个合适的名字也不行。 想来想去似乎只有一句话类型的语言不容易触犯到这些规则。而经常有一些日系的电视电影起的名字我觉得就非常有创意。 刚好之前看过日本由百分之九十九的码农都会沉浸在她笑容里的公民老婆 Gakki(日文原名:新垣 結衣) 出演的两部剧:

  • 《逃避虽然可耻但有用》(日文:逃げるは恥だが役に立つ)
  • 《胜利即是正义》(日文:リーガルハイ)

剧集给我的印象非常深刻。所以抱着试一试的心态填了一个,结果认证了我的猜想。像这样的一句话通常不会触犯到起名规则,可以顺利通过审核。

所以现在我的笔记多出了一个用于国内用户快速访问的通道 https://www.kaij.cn 国外用户依然可以使用之前的域名继续访问 https://www.leelib.com

毕竟出身一名程序员就不能少了使用 GitHub Pages 这样优秀的服务创建一个属于自己的小港湾,去记录一些生活中的点点滴滴。 (๑•̀ㅂ•́)و✧

当使用WSL的过程中访问WSL系统以外目录时候出现一些奇奇怪怪问题 通常就是WSL对于NTFS分区的目录权限问题。

使用 mount -l 查看,可能长这样

[root@wsl] mount -l
rootfs on / type lxfs (rw,noatime)
none on /dev type tmpfs (rw,noatime,mode=755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime,gid=5,mode=620)
none on /run type tmpfs (rw,nosuid,noexec,noatime,mode=755)
none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,noatime)
none on /run/shm type tmpfs (rw,nosuid,nodev,noatime)
none on /run/user type tmpfs (rw,nosuid,nodev,noexec,noatime,mode=755)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,relatime)
cgroup on /sys/fs/cgroup type tmpfs (rw,relatime,mode=755)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices)
C:\ on /mnt/c type drvfs (rw,noatime,uid=0,gid=0,case=off)
D:\ on /mnt/d type drvfs (rw,noatime,uid=0,gid=0,case=off)

可以看到 C:\ 和 D:\ 加载状况是这样的。这种情况下代表所有目录均以root权限进行访问。在使用ssh或git等工具的时候有可能会因为一些权限问题导致意外情况发生。

解决办法很简单,通过DrvFs让WSL可以支持NTFS分区增加文件metadata信息:

  1. 挂载配置
echo '[automount]
enabled = true
root = /mnt/
options = "metadata,umask=22,fmask=11"
mountFsTab = false' > /etc/wsl.conf
  1. 在PowerShell中重启WSL服务
Restart-Service LxssManager

之前的高亮插件支持的语言比较少,而且效果不是特别理想。特进行了一次修整,内容如下:

  • 由于hexo的更新导致之前的主题出现了一部分显示错误。目前已经修复
  • 启用 hexo-prism-plugin 插件
  • 重新过了一遍文章并重新优化 code-block 部分文章

Authentication failed (rejected by the remote node), please check the Erlang cookie

遇到这种情况原因就是本地的授权cookie文件不匹配导致的,结局方法如下:

  1. 删除 %UserProfile% 目录下的 .erlang.cookie 文件
  2. 使用 C:\Windows\System32\config\systemprofile 目录下的 .erlang.cookie 覆盖掉之前 %UserProfile% 目录的同名文件

在试试,现在应该已经可以正常访问了

开启NAT转发

firewall-cmd --permanent --zone=public --add-masquerade

本机到本机

firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=8080
firewall-cmd --permanent --zone=public --add-service=http

本机到其他机器

firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.0.2
firewall-cmd --permanent --zone=public --add-port=80/tcp

重新加载应用

firewall-cmd --reload

只允许指定IP连入指定端口

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.0.2" port protocol="tcp" port="80" accept'

只允许指定IP段连入指定端口范围

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.0.0/24" port protocol="tcp" port="80-8080" accept'

Linux 常用网络优化

# 发送 KeepAlive 消息的间隔 1200=2分钟
sysctl -w net.ipv4.tcp_keepalive_time=1200
# IP端口重用
sysctl -w net.ipv4.tcp_tw_reuse=1