TimothyQiu's Blog

keep it simple stupid

字符动画的制作方法

2 Comments

字符画版本的 Bad Apple 的流行应该是好多年以前的事情了吧……虽然当时也想过自己做一个,但一直觉得图片转字符画是一个很神秘的过程(据说 mplayer 可以直接把视频按字符画输出)。前两天做完 ssoaag 发现,貌似可以用这个东西的原理输出字符动画,于是就有了下面这个视频 = =

【似乎是教程】如何制作字符动画

看到最后的效果你会发现最终产出的字符动画最右一列字符是略有问题的,在视频里也注明了,是那个 GetColor 函数里有个失误 = = 会导致某些情况下取到下一行的颜色,需要 continue 掉,并且把最后除以的 w * h 变成真正有效的像素数。

我们仍未记住那年所学到的线性代数的知识

No Comments

不少知识都是毕业了才知道为什么要学,继而有了学习的动力。想标题的时候发现连这门学科叫什么都反应不过来了……先后想起「离散数学」「高等数学」等等不忍回首的课程,最后终于憋出来正确的「线性代数」四个字。以下便是今回复习这个看了又忘忘了又看了不知道多少遍的东西的笔记。

前情提要,简称前提

左右手坐标系:手指沿 X 轴方向伸展,卷向 Y 轴正方向,大拇指的方向是 Z 轴正方向。

向量 Vector

向量是表示大小和长度的几何对象,有一个起点和一个终点。向量有一种重要的分类方法,下面会反复提到:

至于向量相加、相减、与标量相乘、取长度、标准化,这些都是很显而易见的操作,此处省略一万字。

阅读剩余部分...

SSOAAG

2 Comments

好吧,标题的意思其实是 Some Sort Of Ascii Art Generator……

把这两天的无聊成果放 GitHub 上了,按标题的意思可以理解为某种字符画生成器。

一句话解释原理

在一个 HTML 文档里,把一系列「■」字符作为像素点,并用 <span> 元素为每一个字符指定颜色。再把行距、字间距变小,就形成了一个画布。把图片中每一个像素的颜色赋值给对应的「■」字符就可以产出该图片的马赛克版本(类似于放大 N 倍后的效果)。而根据这个像素点的颜色值可以计算出灰度,再根据灰度的深浅选择相应的字符代替「■」字符(比如灰度小于 64 选 @、小于 128 选 :、其余选 .),就可以产出该图片的字符画版本了。

好吧,这不是一句话,这是一段话 Orz...

Bitmap

嗯,挑了个很矬的名字命名这个类。Bitmap 类主要是为了读取位图文件中的颜色数据而生的。在 Load 时解析位图文件内容,无论原来是什么格式,内部都用 0xAARRGGBB 的形式保存……………………(这是设想)

现实:色深和压缩方式好多……于是目前只把最简单的未压缩版本的 1位色(黑白)/ 4位色 / 8位色 / 16位色 / 24 位色 / 32 位色位图读取实现了。

于是 DIB Header 是 BITMAPINFOHEADER 版本及以后的未压缩位图应该都可以读取了,理论上 4 色位图也支持读取,但我真心没有找到可以产出一个 4 色位图的办法,汗~

小试了一下 FFmpeg

6 Comments

今天逛 Nico 的时候忽然起了搬运的念头,于是把原视频下载下来,开始进行传说中的「战渣浪」运动。在地图上到处逛搜集到如下信息:

发现不是很麻烦,懒得再去找某个版本的 MediaCoder,就直接上 FFmpeg 了……

查看文件信息

为了确定码率,首先用这样的方法查看文件信息:

ffmpeg -i <输入文件>

我从 Nico 下载视频的时候不是混杂时段,下载到的是高清 MP4 文件,于是 FFmpeg 就会输出类似这样的信息:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'filename.mp4':
  Metadata:
  Duration: 00:02:59.77, start: 0.000000, bitrate: 430 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 854x480, 315 kb/s, 30 fps, 30 tbr, 30 tbn, 60 tbc
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, s16, 111 kb/s

可以看出一共有 1 个输入文件,其中包含 1 条视频流和 1 条音频流。各自的码率已经可以看得很清楚了,这里该文件的平均码率没有超过 500Kbps,于是安心地进行下一步。

将 MP4 封装为 FLV

因为下载下来的是 MP4 格式文件,需要将文件(容器)换为 FLV 才能够不被新浪二压:

ffmpeg -i <输入文件> -vcodec copy -acodec copy -f flv <输出文件>

FFmpeg 的 -vcodec-acodec 选项指定了输出文件的编码器,而填入 copy 则表示直接复制,不编码。

-f 选项是用来强制指定输出格式的,虽然 FFmpeg 自己会根据输出文件的扩展名猜,但还是写一下更保险。

其它未尽事宜

于是收工上传,不久发现直接成功了 = = 呃……我记得很久很久以前我用 MediaCoder 还失败了两次呢,囧。

这次很幸运的是下载到的文件码率没有超标,如果超标的话,据说这样可以指定码率:

ffmpeg -i input.mp4 -vcodec h264 -acodec aac -b:v <视频码率> -b:a <音频码率> output.mp4

至于传说中的 h264 2-pass 压制法(用来在更好的视频质量下控制码率),据说是这样的:

ffmpeg -i input.mp4 -pass 1 -vcodec h264 -an -b:v <视频码率> -f rawvideo -y NUL
ffmpeg -i input.mp4 -pass 2 -vcodec h264 -acodec aac -b:v <视频码率> -b:a <音频码率> output.mp4

这里的第一 Pass 因为目的只是取得一个包含视频信息的 log 文件,所以用 -an 禁用音频、用 NUL(或者 /dev/null)防止视频文件生成。

至于其它用法就留待以后要用上的时候再去研究了(懒……

以上。

mbstowcs 与 wcstombs

No Comments

还记得大明湖畔的 mbstowcswcstombs 吗?个人觉得这哥俩的存在感真的是比 setjmplongjmp 还要低啊。

size_t mbstowcs (wchar_t *dest, char const *src, size_t max);
size_t wcstombs (char *dest, wchar_t const *src, size_t max);

解释一下:「mbs」对应「Multibyte String」而「wcs」对应「Wide-character String」。于是顾名思义,这两位的功能就是把多字节字符串和宽字符字符串互相转换。

如果把 Wide-character 看作是 Unicode 的 code point,那么 Multibyte 就是对该 code point 的具体编码。至于这两个函数是如何得知 Multibyte 究竟使用什么编码,答案是他们根据当前 locale 中所指定的字符编码决定。下面的代码是在 Windows 上将 Big5 编码的字符串转换为 GBK 编码:

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE (128)

int main()
{
    char const *source = "Hello 世界!"; // 将文件保存为 Big5 编码

    wchar_t wc_out[BUFFER_SIZE];
    char    mb_out[BUFFER_SIZE];

    printf("%s\n", source);     // 输出不正常

    setlocale(LC_CTYPE, "chinese-traditional"); // 认为输入的 MBS是 Big5 编码
    mbstowcs(wc_out, source, BUFFER_SIZE);

    setlocale(LC_CTYPE, "chinese-simplified");  // 设置输出的 MBS 为 GBK 编码
    wcstombs(mb_out, wc_out, BUFFER_SIZE);

    printf("%s\n", mb_out);     // 正常输出

    return 0;
}

也可以试试日文 Shift-JIS 编码和 GBK 的转换:将文件存为 Shift-JIS 编码,然后把第一个 setlocale 的目标修改为 "japanese"(反正「世界」在简繁日里写法都一样)。甚至还可以跑到 Linux 上在「zh_CN.gbk」和 「zh_CN.utf-8」互转。

于是第一次知道这对函数时,我的第一想法是「哇,原来标准库里也有这样的函数啊!那我岂不是可以用很 portable 的方法来转换编码了?」无奈正常人都会说:你太甜了,这两个函数完全不可靠,还是用 Windows API 吧~还是用 libiconv 吧~

为什么呢?因为这两个函数所能进行的转换取决于 locale 的支持。例如 Windows 的中文 locale 就只能设置为 GBK 和 Big5 编码两种,Unix 的可用 locale 也和系统本身有关(?)。所以,想要通用还是老老实实用 libiconv 吧少年们~

参考:Code Pages Supported by Windows