manout's blog

Something about me

Next 主题添加站内搜索功能

  • 安装 hexo-generator-searchdb, 站点根目录执行以下命令
1
npm install hexo-generator-searchdb  --save
  • 编辑 站点配置文件, 新增以下内容到任意位置
1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000
  • 编辑 主题配置文件, 启用本地搜索功能
1
2
3
# Local search
local_search:
enable: true

可能出现的问题

如果出现点击搜索一直停留在加载状态,可能是文件编码问题,可以在本地测试时访问根目录下 search.xml 文件

1
localhost:4000/search.xml

查看报错信息

处理图像三个基本步骤,把图像读进来,对图像做些处理,把图像存回去,现在我还困在第一步(手动划掉)

前期准备

读取 png 格式的图像需要使用 libpng.

Mac OSX 上使用如下指令安装 libpng.

1
brew install libpng

png.h 中声明了 libpng 中公有的函数原型和需要的宏。在所有需要使用 libpng 的模块中都需要包含 png.h 头文件

1
#include "png.h"

初始化环境

基本的 PNG 图像读取程序通常开始于图像文件已经打开(二进制模式)并且其文件流指针已经传递给我们的 libpng 初始化例程 readpng_init. 函数原型如下

1
int readpng_init(FILE *infile, long *pWidth, long *pHeight);

返回值为 0 表示读取成功,其他非 0 值表示错误码。

检查文件特征码

readpng_init 我们首先要读取输入文件的前 8 个字节,保证符合 PNG 的特征码,这样我们才可以进行之后的操作,一般的文件读取中通常会建立 512 字节的缓冲区,但是 libpng 有自己的缓冲机制,并不需要多于 8 字节的缓冲。

1
2
3
4
uint8_t sig[8];
fread(sig, 1, 8, infile);
if (! png_check_sig(sig, 8))
return -1; /* bad signature */

建立 PNG 结构

当文件的特征码通过检查后,我们需要使用 png.h 头文件中的结构保存基本的 png 图像信息。

1
2
3
4
5
6
7
8
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_SRTING, NULL, NULL, NULL);
if (! png_ptr)
return 4; /* out of memory */
png_infop info_ptr = png_create_info_struct(png_ptr);
if (! info_ptr){
png_destory_read_struct(&png_ptr, nullptr, nullptr);
return 4; /* out of memory */
}

这里使用两个结构保存 PNG 图像的信息, png_structppng_infop
png_structp 结构指向 libpng 内部的结构体,在任意给定的时刻跟踪其指向的 png 图像的当前状态。
png_infop 结构体用来表示在用户要求每一次图像变换之后图像的状态。当需要保存每次图像处理前和处理后的状态时需要大量的使用该结构,比如图像编辑器

异常处理

libpng 使用 setjmplongjmp 实现出现异常时的跳转,使用 png_ptr->jmpbuf 保存当前堆栈和寄存器的信息

1
2
3
4
5
if (setjmp(png->jmpbuf))
{
png_destory_read_struct(&png_ptr, &info_ptr, nullptr);
return 2; /* error when save stack */
}

读取图像数据

接下来是真正读取数据的部分代码

1
2
3
png_init_io(png_ptr, infile);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info);

png_init_io 接口将文件流指针存储在 png_structp 结构中以备后续使用。
png_set_sig_byteslibpng 表示这里已经检查过 png 的特征码所以这里不应再文件流中检查特征码。
png_read_info 读取 png 图像文件的元信息,如 IHDR, PLTE 等头部,详见文档, 接下来读取图像中的宽和高

1
2
3
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &colortype, nullptr, nullptr, nullptr);
*pWidth = widht;
*pHeight = height;

后来发现我用 opencv 就方便太多了,(๑•̀ㅂ•́)و✧
这坑就留着了

当前的任务是在一些处理图像原始数据的接口添加 stride 参数
目前需要处理的接口如下

  • from_pixels
  • from_pixels_resize
  • to_pixels
  • to_pixels_resize

这些接口是将输入图像根据其 type 参数从一个颜色空间转换到另一个颜色空间,同时做些 resize 的处理。

stride 参数的含义

这个参数表示每行的数据长度(单位字节,包含无效数据), 与像素类型无关。
上面这些接口的参数中都会有 widthheight 表示输入图像的每行每列的像素个数,由于图像以像素矩阵,行优先的格式排布在内存中,所以每当一行像素结束时应当紧接下一行像素。
但是有时对数据的排布会有特殊的要求,比如数据对齐等,这是为了迎合数据对齐要求就会在一行像素末尾添加若干无效的字节。此时每行像素之间便不是紧密排布,stride 参数就是表示一行实际所占的字节数

如果 (通常一个像素占四个字节, 取决于图像深度和颜色空间类型),那么说明图像中没有无效字节直接处理即可,转换时也无需考虑 stride 参数。如果不等(), 那么这是使用一些接口时就要注意图像中包含无效字节的问题。

一些想法

原先这些接口都没有 stride 参数,底层 from_*2* 这些接口都将输入图像视为不包含无效数据,在接口中增加该参数需要改变底层数据处理的实现。

参考 libyuv 的实现,其中是将每行像素的处理单独作为一个接口实现,这样将解耦之后就可以很灵活的加入 stride 参数。这样的实现扩展性很好,无需其他的性能上的开销,目前项目中还没有单独处理一行的接口,手动实现的话怕是要花不少时间。
但是在工程中尽量少自己造轮子,尽可能多的复用当前的接口((¦3」∠)),实现最快而且 bug 最少。实际上看到需求就可以得出一个很直观需要消耗额外内存的方法,就是将其原始图像的有效数据拷贝到新的空间使得其每行像素紧密排列,再复用原始的接口即可,(~ ̄(OO) ̄)ブ。

to_pixels 接口就是 from_pixels 接口的逆操作,生成图像之后再在每行末尾添加无效字节好了。

  • 搭建测试工程,验证代码正确性。

目前使用 hexo 时常提示某行存在 non-printable character, 不可打印字符肉眼也看不到,只能将其提示的整行删除重新输入,google 下发现这是 vscode 的bug, 已经有人提出了 issue

这个 bug 尚未修复,目前比较可行的解决办法是在用户首选项中添加如下一行

1
"editor.renderControlCharacters": true

这行配置指示编辑器渲染控制字符,这样就可以看到 non-printable character, 将其删除即可.

或者安装一个 Remove backspace control character 插件,在用户首选项中设置以下内容

1
2
"editor.formatOnSave": true,
"editor.formatOnType": true

ARMv8 推出了新的 A64 指令集,A64 中也包含 SIMD 向量运算指令,执行在 AArch64 模式下。
ARM 的高级 SIMD 架构通常又称为 Neon 技术。

寄存器

32 个 SIMD&FP 寄存器,V0 - V31, 每个寄存器可以以如下格式引用

  • Q0 - Q31, 128 bits 寄存器
  • D0 - D31, 64 bits 寄存器
  • S0 - S31z, 32 bits 寄存器
  • H0 - H31, 16 bits 寄存器
  • B0 - B31, 8 bits 寄存器

组织结构如下

aarch64_register

向量格式

SIMD&FP 寄存器可以加载,读取并处理以下三种格式的数据

  • 在寄存器低位,单个的标量元素
  • 64 bits 长度的向量,包含半字,字,双字长度的元素
  • 128 bits 长度的向量,包含半字,字,双字长度的元素

元素大小

B H S D
8 bits 16 bits 32 bits 64 bits
  • 对于 128 bits 寄存器
    Vn.{2D, 4S, 8H, 16B}
    aarch64_128bits_vector
  • 对于 64 bits 寄存器
    Vn.{1D, 2S, 4H, 8B}
    aarch64_64bits_vector

卷积神经网络的一般结构如下

  • Input Layer
    保存着输入图像的原始数据
  • Convolution Layer
    每个神经元都会使用各自的权重与其相连的区域做点乘,然后作为该神经元的输出。通常输出通道的个数与卷积核的数量相同
  • ReLU Layer
    这一层会对每一个像素使用 函数,最简单的函数形式为 。更一般的形式为

    即当 为 0 的情况
  • Pool Layer
    池化层会对其输入进行下采样,降低输入的维度,防止过拟合
  • Fully Connection Layer
    会计算出对应于每个类的概率,计算结果通常为一维向量。长度为类的个数
0%