像素格式位字节顺序

发布于 2025-01-11 04:33:42 字数 1364 浏览 0 评论 0 原文

我目前正在实现一个程序来读取和解码 BMP 文件中的图像。但我对像素格式,尤其是位字节顺序感到非常困惑。

我目前的理解是,当提到像素格式时,例如RGBA32,我们以人类的方式说它,即使用大端,因此第一个字节是R,第二个G,第三个B,第四个字节答:

RGBA32
11111111 22222222 33333333 44444444
R        G        B        A

当将大/小尾数附加到此格式时,例如 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
  1. 我们将 R 组件读取为 10000=16或<代码>00001=1

  2. 我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序有关?

  3. RGB555LE格式在字节数组中的表示方式是什么?

    <前><代码>RGB555LE_v1 333'44444 1'22222'33 G”酒吧G' 000'00000 0'00001'00 或 RGB555LE_v2 ? 44444'333 33'22222'1 BGRA 00000'000 00'10000'0

I am currently implementing a program to read and decode image from a BMP file. But I am really confused about the pixel format and especially about bit endianness.

My current understanding is when referring to a pixel format, e.g. RGBA32, we are saying it in our human way, which is using big endian, thus the first byte is R, second G, third B, fourth A:

RGBA32
11111111 22222222 33333333 44444444
R        G        B        A

And when appending big/little endian to this format, such as RGBA32BE/RGBA32LE, this will change the byte order accordingly, as:

RGBA32BE (unchanged)
11111111 22222222 33333333 44444444
R        G        B        A

RGBA32LE (reversed)
11111111 22222222 33333333 44444444
A        B        G        R

This all looks naturally to me, as long as we are assuming the individual value of each component is exactly the value of byte we read.

However things start to confuse me when component size is less than 1 byte or 8 bits. Say RGB555BE, I suppose below is how it is represented as byte array:

RGB555BE
1'22222'33 333'44444
A   R      G     B
0'00001'00 000'00000
  1. Do we read the R component as 10000=16 or 00001=1 ?

  2. I'm even more confused about if the way we read the component (bit endianness) is related to byte endianness?

  3. Which way is RGB555LE format represented in byte array?

    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
    

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

揽月 2025-01-18 04:33:42

我们可以使用 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然后< code>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 元素(两个字节),第一个和第二个字节的顺序交换)。


问题答案:

  1. 我们将 R 组件读作 10000=16 还是 00001=1 ?

答案:
对于 rgb555le:00000001 00000000
对于 rgb555be: 00000000 00000001


  1. 我更困惑的是我们读取组件的方式(位字节序)是否与字节字节序有关?

答案:
在像素格式的上下文中,不存在“位字节顺序”,只有“字节字节顺序”。

位字节序与串行通信更相关,从软件角度来看,我们看不到每个字节中位的顺序。


  1. RGB555LE格式在字节数组中的表示方式是什么?

答案:

RGB555LE(如 RGB555LE_v1):

byte0 byte1
gggbbbbb 0rrrrrgg

(最左边的位是每个字节的高位)。

我们最好将数据视为一个 uint16 元素:

b: 0000000000011111(纯蓝色)
g: 0000001111100000(纯绿色)
r:0111110000000000(纯红色)

我们可以将其视为uint160rrrrggggggbbbbbb

We may use FFmpeg for testing the different pixel formats.

The conclusion are:

  • There is no bit endianness, only bytes endianness.
  • The endianness is relevant when the base elements is 2 bytes (uint16).
    Little endian: bytes order of uint16 element is: [LSB, MSB].
    Bit endian: bytes order of uint16 element is: [MSB, LSB].

Executing FFmpeg CLI commands for testing:

Start with rgb24 for example:

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

Use some hex viewer like HxD for inspecting the raw file content.

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 pixel format:

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

The FFmpeg convention the order of the bytes.
We can't say if the naming applies little or big endian.
The endianness is relevant when using uint16 components (not uint8 components).


rgba pixel format:

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 then R then G then B then A...

We may refer rgba as RGBA32BE, but it's too confusing...


rgb555le pixel format:

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

We may also check the lower bit (for bits ordering):
Set the color to 8 so after shifting the value be 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 bytes in bits: 00000001 00000000
g: 20 00 20 00 2 bytes in bits: 00100000 00000000
b: 00 04 00 04 2 bytes in bits: 00000000 00000100


rgb555be pixel format:

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

Checking the lower bit (for bits ordering):

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

In big endian format, only the bytes are swapped.
(For every uint16 element (two bytes), order of the first and second byte is swapped).


Answers to questions:

  1. Do we read the R component as 10000=16 or 00001=1 ?

Answer:
For rgb555le: 00000001 00000000
For rgb555be: 00000000 00000001


  1. I'm even more confused about if the way we read the component (bit endianness) is related to byte endianness?

Answer:
In context of pixel format, there is no "bit endianness" there is only "byte endianness".

Bits endianness is more relevant to serial communication, from software perspective, we don't see the order of bits in each byte.


  1. Which way is RGB555LE format represented in byte array?

Answer:

RGB555LE (as RGB555LE_v1):

byte0 byte1
gggbbbbb 0rrrrrgg

(most left bit is the upper bit in each byte).

We better look at the data as one uint16 element:

b: 0000000000011111 (pure blue)
g: 0000001111100000 (pure green)
r: 0111110000000000 (pure red)

We may look at it as uint16 as: 0rrrrrgggggbbbbb

始终不够 2025-01-18 04:33:42

我认为字节序对 rgb555 并不重要,这就是为什么您的链接会聚集 rgb555rgb555lergb555be一起。就此而言,您的 rgba 示例也不是字节序敏感的,因为它的组件都是 <= 8 位。

至于rgb555如何用2个字节(好吧,15位)表示,您可以在FFmpeg存储库中搜索rgb555并查看编码器/解码器如何处理这种像素格式。这是我发现的 rpzaenc.c 第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 位推送到其比特流,如 第 683 行

put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color));

这是put_bits.h

I don't think endianness matters to rgb555, and that's why your link bunches rgb555, rgb555le, and rgb555be together. For that matter, your example with rgba also isn't endian sensitive as its components are all <= 8 bits.

As for how rgb555 is represented in 2 bytes (well, 15 bits), you can search FFmpeg repo for rgb555 and see how encoders/decoders handle such pixel format. Here is one I found 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;
}

the encoder takes rgb555, a 16-bit unsigned int, an uses put_bits() utility function to push these 15 bits to its bitstream as shown on Line 683

put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color));

Here is the link to put_bits.h

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文