1

我仍然是 C++ 的菜鸟,我目前正在尝试创建一个程序,该程序手动将 256 色位图加载到数组并打印出控制台中每个像素的值。在大多数情况下,这些价值观似乎是不正确的。

我的其他输出(例如信息标题内的值)都很好biSizeImage,除了 5 字节。这不应该是 25 字节吗?
出于测试目的,我拍摄了两张 5 x 5 的图像。其中一个是全黑的,另一个是全白的。
我希望黑色的输出类似于:

0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

但我得到:

0 0 0 0 0
0 0 0 0 0
3452816845 3452816845 3452816845 3452816845 3452816845
3452816845 3452816845 3452816845 3452816845 3452816845
3452816845 3452816845 3452816845 3452816845 3452816845

有了全白的图片,我的结果就更加奇怪了:

4294967295 255 4294967295 255 4294967295
255 4294967295 255 4294967295 255
3452816845 3452816845 3452816845 3452816845 3452816845
3452816845 3452816845 3452816845 3452816845 3452816845
3452816845 3452816845 3452816845 3452816845 3452816845

下面你可以看到我当前的代码:

#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>  

using namespace std;

#pragma pack(push,1)
typedef struct tagBITMAPFILEHEADER {  
unsigned short      bfType;                 // Specifies the type of file. This member must be BM. (0x4D42)
unsigned int        bfSize;                 // Specifies the size of the file, in bytes.
short               bfReserved1;            // Reserved; must be set to zero.
short               bfReserved2;            // Reserved; must be set to zero.
unsigned int        bfOffBits;              // Specifies the byte offset from the BITMAPFILEHEADER structure to the actual bitmap data in the file.
} BITMAPFILEHEADER;


typedef struct tagBITMAPINFOHEADER {  
unsigned int        biSize;                 // Specifies the number of bytes required by the BITMAPINFOHEADER structure.
int                 biWidth;                // Specifies the width of the bitmap, in pixels.
int                 biHeight;               // Specifies the height of the bitmap, in pixels.
unsigned short      biPlanes;               // Specifies the number of planes for the target device. This member must be set to 1.
unsigned short      biBitCount;             // Specifies the number of bits per pixel. This value must be 1,4, 8, or 24.
unsigned int        biCompression;          // Specifies the type of compression for a compressed bitmap. Itcan be one of the following values: BI_RGB, BI_RLE8, BI_RLE4   
unsigned int        biSizeImage;            // Specifies the size, in bytes, of the image. It is valid to set this member to zero if the bitmap is in the BI_RGB format.
int                 biXPelsPerMeter;        // Specifies the horizontal resolution, in pixels per meter, of the target device for the bitmap.
int                 biYPelsPerMeter;        // Specifies the vertical resolution, in pixels per meter, of the target device for the bitmap.
unsigned int        biClrUsed;              // Specifies the number of color indexes in the color table actually used by the bitmap.
unsigned int        biClrImportant;         // Specifies the number of color indexes that are considered important for displaying the bitmap. If this value is zero, all colors are important.
} BITMAPINFOHEADER;  

typedef struct tagRGBPIXEL{
unsigned char b;
unsigned char g;
unsigned char r;
} rgbPIXEL;
#pragma pack(pop)

typedef struct tagBITMAP{
BITMAPFILEHEADER FILEHEADER;
BITMAPINFOHEADER INFOHEADER;
rgbPIXEL*    IMAGEDATA;
}BITMAP;

void main (){
string          sImageLocation      = "C:/BMP.bmp";
BITMAP          bmpImage;
FILE*           fbmpImage           = NULL;
unsigned int    ImagePixelAmount    = 0;
bool            bImageLoaded        = false;

do{

//Open the image file
fopen_s(&fbmpImage, sImageLocation.c_str(), "rb");

//Check whether the image could be loaded successfully or not
if(fbmpImage == NULL){
    cout << "Loading the image file failed!" << " " << endl;
    }
if(fbmpImage != NULL){
    cout << "Loaded the image file successfully!" << endl;
    bImageLoaded = true;
    }


cout << endl;
cout << endl;
cout << endl;

//Read the BITMAP FILE HEADER
fseek(fbmpImage, 0, SEEK_SET);
fread(&bmpImage.FILEHEADER, sizeof(BITMAPFILEHEADER), 1, fbmpImage);

//Check if the image is a valid bitmap file
if(bmpImage.FILEHEADER.bfType != 19778){
bImageLoaded = false;
cout << "Image is not a valid Bitmap file!" << endl;
fclose(fbmpImage);
}
}while(bImageLoaded = false);

//Show the information of the BITMAP FILE HEADER in the consol
cout << "Bitmap File Header Data Values:"                               << endl;
cout                                                                    << endl;
cout << "Image Type:  " << bmpImage.FILEHEADER.bfType                   << endl;
cout << "Image Size:  " << bmpImage.FILEHEADER.bfSize       << " Bytes" << endl;
cout << "Reserved:    " << bmpImage.FILEHEADER.bfReserved1              << endl;
cout << "Reserved:    " << bmpImage.FILEHEADER.bfReserved2              << endl;
cout << "Byte Offset: " << bmpImage.FILEHEADER.bfOffBits                << endl;

cout << endl;
cout << endl;
cout << endl;


//Read the BITMAP INFO HEADER
fread(&bmpImage.INFOHEADER, sizeof(BITMAPINFOHEADER), 1, fbmpImage);

cout << "Bitmap Info Header Data Values: " << endl;
cout << endl;
cout << "Size of Bitmap Info Header: "  << bmpImage.INFOHEADER.biSize       << " Bytes"              << endl;
cout << "Width of Bitmap:             " << bmpImage.INFOHEADER.biWidth      << " Pixel"              << endl;
cout << "Height of Bitmap:            " << bmpImage.INFOHEADER.biHeight     << " Pixel"              << endl;
cout << "Size of Image Data:          " << bmpImage.INFOHEADER.biHeight     << " Bytes"              << endl;
cout << "Bit Count:                   " << bmpImage.INFOHEADER.biBitCount   << " Bits Per Pixel"     << endl;
cout << "Amount of color indexes:     " << bmpImage.INFOHEADER.biClrUsed                             << endl;
cout << "Compression:                 " << bmpImage.INFOHEADER.biCompression                         << endl;

cout << endl;
cout << endl;
cout << endl;

ImagePixelAmount = bmpImage.INFOHEADER.biHeight * bmpImage.INFOHEADER.biWidth;

//Create space in memory and read in pixel data from the image
bmpImage.IMAGEDATA = (rgbPIXEL*)malloc(sizeof(rgbPIXEL) * ImagePixelAmount);
cout << "Memory for image created" << endl;
fseek(fbmpImage, bmpImage.FILEHEADER.bfOffBits, SEEK_SET);
fread(bmpImage.IMAGEDATA, ImagePixelAmount * sizeof(rgbPIXEL), 1, fbmpImage);

fclose(fbmpImage);


//Show the pixel data in the consol in numbers instead of ASCII-symbols
for(int y =0; y < bmpImage.INFOHEADER.biHeight; y++){

    for(int x = 0; x < bmpImage.INFOHEADER.biWidth; x++){
    cout << "Red: " <<(unsigned int) bmpImage.IMAGEDATA[y * bmpImage.INFOHEADER.biWidth + x].r << " ";
    cout << "Green: " <<(unsigned int) bmpImage.IMAGEDATA[y * bmpImage.INFOHEADER.biWidth + x].g << " ";
    cout << "Blue: " <<(unsigned int) bmpImage.IMAGEDATA[y * bmpImage.INFOHEADER.biWidth + x].b << " ";
    }

cout << endl;
}

cout << endl;
cout << endl;
cout << endl;

cout << "Press any key to quit the programm" << endl;
_getch();
return;
}

更新:
根据 Hans Passant 的说法,我将代码更改为加载 24 位位图。使用红色位图,我会假设每个像素的打印值是red = 255, green = 0 and blue = 0. 但是,我收到的每个像素的实际值是red = 237, green = 28 and blue = 36. 对于任何其他图像,我都有同样的麻烦。

4

1 回答 1

2

您确定您正在阅读纯红色 (255,0,0 RGB) BMP 吗?我问是因为我创建了一个并且您的程序输出以下内容:

红色:255 绿色:0 蓝色:0 红色:255 绿色:0 蓝色:0

红色:0 绿色:0 蓝色:0 红色:0 绿色:255 蓝色:0

仍然不正确,但更接近您的预期。但是为什么第二行是错误的?

表示位图像素的位被打包成行。每行的大小通过填充四舍五入为 4 字节(32 位 DWORD)的倍数。-来源

以下是 Paint 生成的 BMP 的摘录:

0000 ff00 00ff 0000 0000 ff00 00ff 0000

这是:

00: 绿色 (0)

00: 蓝色 (0)

ff: 红色 (255)

00: 绿色 (0)

00: 蓝色 (0)

ff: 红色 (255)

00: 填充

00: 填充

00: 绿色 (0)

00: 蓝色 (0)

ff: 红色 (255)

00: 绿色 (0)

00: 蓝色 (0)

ff: 红色 (255)

00: 填充

00: 填充

请记住,填充量等于(width * channels) % 4,在本例中为(2 * 3) % 4 = 2,因此我们的两个填充添加到每行像素的末尾。

我没有时间完全检查你的代码,但我不相信你正在考虑这个填充。请注意,32 位 (GBRA) BMP 并不像(width * 4) % 4往常一样具有此填充0

这是一个应该可以工作的独立程序。我从一个旧项目中删除了 BMP 加载的相关部分。你可以看到我在哪里解释了 24 位 BMP 文件中的填充。

另外,维基百科文章中的另一个例子

编辑

忘了说这是一个 2x2 红色 BMP 图像。上面摘录的是像素数组,完整的文件如下:

424d 4600 0000 0000 0000 3600 0000 2800
0000 0200 0000 0200 0000 0100 1800 0000
0000 1000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 ff00 00ff 0000 0000
ff00 00ff 0000 
于 2013-10-28T21:09:30.233 回答