在 C 中处理 BMP 文件中的填充
Posted
技术标签:
【中文标题】在 C 中处理 BMP 文件中的填充【英文标题】:Dealing with padding in a BMP file in C 【发布时间】:2016-07-15 16:17:05 【问题描述】:我正在尝试用 C 语言编辑 BMP 文件。我的代码适用于没有填充的 BMP 文件,但我无法处理填充。
我读过一些关于 BMP 文件的其他问题,但大多数都使用其他语言,如 C# 和 Java,所以我觉得它们不是很有用。
这是像素阵列的样子,但要大得多:
char bmpArray[MAX] = B,G,R,B,G,R,B,G,R,0,0,0,
B,G,R,B,G,R,B,G,R,0,0,0,
B,G,R,B,G,R,B,G,R,0,0,0
零用于填充字节以使每行可被 4 整除,这取决于图像的像素宽度。我想要做的是将这些填充字节保留在相同位置,并且只处理 B、G、R 字节。如果我对填充值应用编辑,生成的图像将会失真。
我做了一个函数,根据宽度生成填充字节的数量。
它使用了这个公式4 - ((width * 3) % 4)
,当我用不同宽度的图像测试它时,它就可以工作了。
我成功提取了 BMP 文件的 B、G、R 数据并将其放入数组中,所以我只会发布我遇到问题的部分代码。
int c = 0;
for (int a = 0; a < height; a++)
for (int b = 0; b < width*3; b++)
if (bmpArray[a*(width*3)+b] < 127)
bmpArray[a*(width*3)+b] = 0;
else
bmpArray[a*(width*3)+b] = 255;
c++;
for (int pad = 0; pad < padding; pad++)
bmpArray[c++] = 0x00;
我要做的是“绘制”输出 BMP 文件的每一行,然后在到达行尾时立即停止,即宽度 * 3,然后在去之前绘制填充字节下一行像素。
或者,有没有一种方法可以使用单个 for 循环识别填充像素,然后使用 if 语句不修改填充像素?例如:
for (int a = 0; a < bmpArraySize; a++)
paddingBytes = ??? //for example for the first row
// paddingBytes are i + width*3 + 1
// and i + width*3 + 2 and i + width*3 + 3 if padding = 3
if (a = paddingBytes)
bmpArray[a] = 0x00;
else if (bmpArray[a] < 127)
bmpArray[a] = 0;
else
bmpArray[a] = 255;
【问题讨论】:
imgArray有什么用? 我解决了这个问题。我从另一个堆栈溢出问题中复制了那部分代码,忘记更改数组名称。 在遍历列时,您应该增加c
。
我在代码中添加了这个,对吗?
不...上移一行
【参考方案1】:
问题出在这部分:
int c = 0;
for (int a = 0; a < height; a++)
for (int b = 0; b < width*3; b++)
if (bmpArray[a*(width*3)+b] < 127)
bmpArray[a*(width*3)+b] = 0;
else
bmpArray[a*(width*3)+b] = 255;
for (int pad = 0; pad < padding; pad++)
bmpArray[c++] = 0x00; /* ONLY HERE is 'c' updated! */
在每一行的末尾,您填写从c
开始的填充,它从0
开始,因此会覆盖第一行的前几个字节。然后,每个 next 行都会被复制,但您会继续从头开始覆盖(c
最初指向的位置)。
应在每一行添加填充。在循环中,您调整了a
和b
,但您忘记调整填充。
我建议更直接的代码(未经测试!):
for (int a = 0; a < height; a++)
for (int b = 0; b < width*3; b++)
if (bmpArray[a*(width*3 + padding)+b] < 127)
bmpArray[a*(width*3 + padding)+b] = 0;
else
bmpArray[a*(width*3 + padding)+b] = 255;
for (int pad = 0; pad < padding; pad++)
bmpArray[a*(width*3 + padding) + 3*width + pad] = 0x00;
有什么方法可以识别填充像素..
是的——在我上面的循环中调整了填充,它会自动跳过填充本身。最后,您可以安全地删除显式的“将填充设置为 0”循环。
【讨论】:
太棒了!您认为使用第二种方法是否可行(1 个循环)? @user6005857:是的,但我建议不要这样做。您可以在height * (width*3 + padding)
上执行一次循环,但是需要识别像素和填充的内部表达式变得相当复杂。【参考方案2】:
类似:
...
int originalLineSize = width * 3;
int workLineSize = originalLineSize + 4 - originalLineSize % 4;
for (int a = 0; a < bmpArraySize; ++a)
if ((a % workLineSize) >= originalLineSize)
bmpArray[a] = 0x00;
else if (bmpArray[a] < 127)
bmpArray[a] = 0;
...
【讨论】:
【参考方案3】:“填充字节”是扫描线像素之后的字节。您对填充不像扫描线大小和像素大小那么感兴趣:
iScanlineSize = ((width * bitsperpixel) + 31) / 32 * 4;
iBytesperPixel = bitsperpixel / 8;
现在您可以遍历扫描线并按如下方式处理像素和像素部分(颜色):
for (int a = 0; a < height; a++)
for (int b = 0; b < width; b++)
for (int c = 0; c < iBytesperPixel; c++)
pixelPart= bmpArray[a*iScanlineSize + b*iBytesperPixel + c];
]
【讨论】:
以上是关于在 C 中处理 BMP 文件中的填充的主要内容,如果未能解决你的问题,请参考以下文章
C 中的 .BMP 文件 - DIB 标头返回图像大小 0,即使 BMP 标头返回文件大小
谁能解释一下 RowSize 和 PixelArraySize 在 BMP 文件格式中的含义?