像素格式位字节序
Pixel format bit endianness
我目前正在实现一个从 BMP 文件读取和解码图像的程序。但我真的对像素格式感到困惑,尤其是位字节序。
我目前的理解是指像素格式,例如RGBA32,我们用人的方式说,就是大端,所以第一个字节是R,第二个G,第三个B,第四个A:
RGBA32
11111111 22222222 33333333 44444444
R G B A
并且当向这种格式附加big/little字节序时,例如RGBA32BE/RGBA32LE,这将改变字节顺序因此,如:
RGBA32BE (unchanged)
11111111 22222222 33333333 44444444
R G B A
RGBA32LE (reversed)
11111111 22222222 33333333 44444444
A B G R
这对我来说看起来很自然,只要我们假设每个组件的单独值正是我们读取的字节值即可。
然而,当组件大小小于 1 字节或 8 位时,事情开始让我感到困惑。说 RGB555BE,我想下面是它如何表示为字节数组:
RGB555BE
1'22222'33 333'44444
A R G B
0'00001'00 000'00000
我们读 R 分量是 10000=16
还是 00001=1
?
我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序相关?
RGB555LE格式用字节数组表示的方式是什么?
RGB555LE_v1
333'44444 1'22222'33
G" B A R G'
000'00000 0'00001'00
or RGB555LE_v2 ?
44444'333 33'22222'1
B G R A
00000'000 00'10000'0
我认为字节顺序对 rgb555
并不重要,这就是为什么您的 link 将 rgb555
、rgb555le
和 rgb555be
捆绑在一起的原因。就此而言,您使用 rgba
的示例也不对字节序敏感,因为它的组件都是 <= 8 位。
至于 rgb555
是如何用 2 个字节(好吧,15 位)表示的,您可以在 FFmpeg 存储库中搜索 rgb555
并查看 encoders/decoders 如何处理这种像素格式。这是我找到的 rpzaenc.c
Line 138:
static uint16_t rgb24_to_rgb555(uint8_t *rgb24)
{
uint16_t rgb555 = 0;
uint32_t r, g, b;
r = rgb24[0] >> 3;
g = rgb24[1] >> 3;
b = rgb24[2] >> 3;
rgb555 |= (r << 10);
rgb555 |= (g << 5);
rgb555 |= (b << 0);
return rgb555;
}
编码器采用 rgb555
,一个 16 位无符号整数,并使用 put_bits()
效用函数将这 15 位推入其比特流,如 Line 683[=25= 所示]
put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color));
这里是link到put_bits.h
我们可能会使用 FFmpeg 来测试不同的像素格式。
结论是:
- 没有位字节序,只有字节字节序。
- 当基本元素为 2 个字节 (
uint16
) 时,字节顺序是相关的。
Little endian:uint16
元素的字节顺序是:[LSB, MSB].
位字节序:uint16
元素的字节顺序是:[MSB, LSB].
正在执行 FFmpeg CLI 命令进行测试:
从rgb24
开始,例如:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo blue.raw
使用像 HxD 这样的十六进制查看器来检查原始文件内容。
red.raw FF 00 00 FF 00 00
...
blue.raw 00 FF 00 00 FF 00
...
green.raw 00 00 FF 00 00 FF
...
rgba
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo blue.raw
r: FF 00 00 FF
g: 00 FF 00 FF
b: 00 00 FF FF
FFmpeg 约定字节顺序。
我们不能说命名适用小端还是大端。
使用 uint16
组件(而不是 uint8
组件)时,字节序是相关的。
rgba
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo blue.raw
r: FF 00 00 FF
g: 00 FF 00 FF
b: 00 00 FF FF
A
然后 R
然后 G
然后 B
然后 A
...
我们可以将 rgba
称为 RGBA32BE
,但这太令人困惑了...
rgb555le
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo blue.raw
r: 00 7C 00 7C
g: E0 03 E0 03
b: 1F 00 1F 00
我们还可以检查低位(用于位排序):
将颜色设置为 8,以便在移动值后为 1。
ffmpeg -y -lavfi color=0x000008:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo red.raw
ffmpeg -y -lavfi color=0x000800:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo green.raw
ffmpeg -y -lavfi color=0x080000:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo blue.raw
r: 01 00 01 00
2 位字节: 00000001 00000000
g: 20 00 20 00
2 位字节:00100000 00000000
b: 00 04 00 04
2 位字节: 00000000 00000100
rgb555be 像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo blue.raw
r: 7C 00 7C 00
g: 03 E0 03 E0
b: 00 1F 00 1F
检查低位(用于位排序):
ffmpeg -y -lavfi color=0x000008:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo red.raw
ffmpeg -y -lavfi color=0x000800:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo green.raw
ffmpeg -y -lavfi color=0x080000:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo blue.raw
r: 00 01 00 01
g: 00 20 00 20
b: 04 00 04 00
在大端格式中,只交换字节。
(对于每个 uint16 元素(两个字节),交换第一个和第二个字节的顺序)。
问题答案:
- 我们读R分量是
10000=16
还是00001=1 ?
答案:
对于 rgb555le:00000001 00000000
对于 rgb555be:00000000 00000001
- 我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序有关?
答案:
在像素格式的上下文中,没有“位字节序”,只有“字节字节序”。
位字节顺序与串行通信更相关,从软件角度来看,我们看不到每个字节中的位顺序。
- RGB555LE格式用字节数组表示的方式有哪些?
答案:
RGB555LE(如RGB555LE_v1):
byte0 byte1
gggbbbbb 0rrrrrgg
(最左边的位是每个字节中的高位)。
我们最好将数据视为一个 uint16
元素:
b: 0000000000011111
(纯蓝色)
g: 0000001111100000
(纯绿色)
r: 0111110000000000
(纯红色)
我们可以把它看成uint16
:0rrrrrgggggbbbbb
我目前正在实现一个从 BMP 文件读取和解码图像的程序。但我真的对像素格式感到困惑,尤其是位字节序。
我目前的理解是指像素格式,例如RGBA32,我们用人的方式说,就是大端,所以第一个字节是R,第二个G,第三个B,第四个A:
RGBA32
11111111 22222222 33333333 44444444
R G B A
并且当向这种格式附加big/little字节序时,例如RGBA32BE/RGBA32LE,这将改变字节顺序因此,如:
RGBA32BE (unchanged)
11111111 22222222 33333333 44444444
R G B A
RGBA32LE (reversed)
11111111 22222222 33333333 44444444
A B G R
这对我来说看起来很自然,只要我们假设每个组件的单独值正是我们读取的字节值即可。
然而,当组件大小小于 1 字节或 8 位时,事情开始让我感到困惑。说 RGB555BE,我想下面是它如何表示为字节数组:
RGB555BE
1'22222'33 333'44444
A R G B
0'00001'00 000'00000
我们读 R 分量是
10000=16
还是00001=1
?我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序相关?
RGB555LE格式用字节数组表示的方式是什么?
RGB555LE_v1 333'44444 1'22222'33 G" B A R G' 000'00000 0'00001'00 or RGB555LE_v2 ? 44444'333 33'22222'1 B G R A 00000'000 00'10000'0
我认为字节顺序对 rgb555
并不重要,这就是为什么您的 link 将 rgb555
、rgb555le
和 rgb555be
捆绑在一起的原因。就此而言,您使用 rgba
的示例也不对字节序敏感,因为它的组件都是 <= 8 位。
至于 rgb555
是如何用 2 个字节(好吧,15 位)表示的,您可以在 FFmpeg 存储库中搜索 rgb555
并查看 encoders/decoders 如何处理这种像素格式。这是我找到的 rpzaenc.c
Line 138:
static uint16_t rgb24_to_rgb555(uint8_t *rgb24)
{
uint16_t rgb555 = 0;
uint32_t r, g, b;
r = rgb24[0] >> 3;
g = rgb24[1] >> 3;
b = rgb24[2] >> 3;
rgb555 |= (r << 10);
rgb555 |= (g << 5);
rgb555 |= (b << 0);
return rgb555;
}
编码器采用 rgb555
,一个 16 位无符号整数,并使用 put_bits()
效用函数将这 15 位推入其比特流,如 Line 683[=25= 所示]
put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color));
这里是link到put_bits.h
我们可能会使用 FFmpeg 来测试不同的像素格式。
结论是:
- 没有位字节序,只有字节字节序。
- 当基本元素为 2 个字节 (
uint16
) 时,字节顺序是相关的。
Little endian:uint16
元素的字节顺序是:[LSB, MSB].
位字节序:uint16
元素的字节顺序是:[MSB, LSB].
正在执行 FFmpeg CLI 命令进行测试:
从rgb24
开始,例如:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb24 -f rawvideo blue.raw
使用像 HxD 这样的十六进制查看器来检查原始文件内容。
red.raw FF 00 00 FF 00 00
...
blue.raw 00 FF 00 00 FF 00
...
green.raw 00 00 FF 00 00 FF
...
rgba
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo blue.raw
r: FF 00 00 FF
g: 00 FF 00 FF
b: 00 00 FF FF
FFmpeg 约定字节顺序。
我们不能说命名适用小端还是大端。
使用 uint16
组件(而不是 uint8
组件)时,字节序是相关的。
rgba
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgba -f rawvideo blue.raw
r: FF 00 00 FF
g: 00 FF 00 FF
b: 00 00 FF FF
A
然后 R
然后 G
然后 B
然后 A
...
我们可以将 rgba
称为 RGBA32BE
,但这太令人困惑了...
rgb555le
像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo blue.raw
r: 00 7C 00 7C
g: E0 03 E0 03
b: 1F 00 1F 00
我们还可以检查低位(用于位排序):
将颜色设置为 8,以便在移动值后为 1。
ffmpeg -y -lavfi color=0x000008:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo red.raw
ffmpeg -y -lavfi color=0x000800:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo green.raw
ffmpeg -y -lavfi color=0x080000:size=8x8:rate=1:duration=1 -pix_fmt rgb555le -f rawvideo blue.raw
r: 01 00 01 00
2 位字节: 00000001 00000000
g: 20 00 20 00
2 位字节:00100000 00000000
b: 00 04 00 04
2 位字节: 00000000 00000100
rgb555be 像素格式:
ffmpeg -y -lavfi color=red:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo red.raw
ffmpeg -y -lavfi color=0x00FF00:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo green.raw
ffmpeg -y -lavfi color=blue:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo blue.raw
r: 7C 00 7C 00
g: 03 E0 03 E0
b: 00 1F 00 1F
检查低位(用于位排序):
ffmpeg -y -lavfi color=0x000008:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo red.raw
ffmpeg -y -lavfi color=0x000800:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo green.raw
ffmpeg -y -lavfi color=0x080000:size=8x8:rate=1:duration=1 -pix_fmt rgb555be -f rawvideo blue.raw
r: 00 01 00 01
g: 00 20 00 20
b: 04 00 04 00
在大端格式中,只交换字节。
(对于每个 uint16 元素(两个字节),交换第一个和第二个字节的顺序)。
问题答案:
- 我们读R分量是
10000=16
还是00001=1 ?
答案:
对于 rgb555le:00000001 00000000
对于 rgb555be:00000000 00000001
- 我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序有关?
答案:
在像素格式的上下文中,没有“位字节序”,只有“字节字节序”。
位字节顺序与串行通信更相关,从软件角度来看,我们看不到每个字节中的位顺序。
- RGB555LE格式用字节数组表示的方式有哪些?
答案:
RGB555LE(如RGB555LE_v1):
byte0 byte1
gggbbbbb 0rrrrrgg
(最左边的位是每个字节中的高位)。
我们最好将数据视为一个 uint16
元素:
b: 0000000000011111
(纯蓝色)
g: 0000001111100000
(纯绿色)
r: 0111110000000000
(纯红色)
我们可以把它看成uint16
:0rrrrrgggggbbbbb