C++ 如何将 BMP 像素值读入数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5751749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:48:31  来源:igfitidea点击:

How can I read BMP pixel values into an array?

c++windowsimagebmp

提问by Carlos Daniel Gadea Omelchenko

I'm writing code in C++ (on Windows) and I'm trying to extract the pixel values of a grayscale bmp. I don't care about keeping any of the metadata, and just want to store the pixel values in a char array. I haven't been able to find a standard or "typical" way of doing this manually, so I'm wondering if there's perhaps a simple library that people use to load bitmaps into memory.

我正在用 C++(在 Windows 上)编写代码,我正在尝试提取灰度 bmp 的像素值。我不关心保留任何元数据,只想将像素值存储在一个字符数组中。我一直无法找到手动执行此操作的标准或“典型”方式,所以我想知道是否有一个简单的库供人们用来将位图加载到内存中。

Thanks in advance!

提前致谢!

采纳答案by Mark Ransom

Read the entire file into memory. There will be a small header at the front, and the rest of it will be the pixel values.

将整个文件读入内存。前面会有一个小标题,其余部分是像素值。

The first part will be a BITMAPFILEHEADERstructure. The only part you care about is the bfOffBits, which gives the number of bytes from the start of the file to the pixel values.

第一部分将是一个BITMAPFILEHEADER结构。您唯一关心的部分是 bfOffBits,它给出了从文件开头到像素值的字节数。

The next part after the BITMAPFILEHEADERwill be a BITMAPINFOHEADER. This will be useful to determine the format of the pixels.

之后的下一部分BITMAPFILEHEADER将是BITMAPINFOHEADER. 这将有助于确定像素的格式。

This will be followed by a palette, if the bit depth requires one.

如果位深度需要一个调色板,这将跟随一个调色板。

There are a couple of gotchas with the pixel values. First is that the order is (blue,green,red), just opposite of the way everybody else does it. Second is that the rows go from bottom to top of the image, again backwards from everybody else. Finally, the number of bytes in a row will always be padded up to the next multiple of 4.

像素值有几个问题。首先是顺序是(蓝色,绿色,红色),与其他人的做法相反。其次是行从图像的底部到顶部,再次从其他人向后。最后,一行中的字节数将始终填充到下一个 4 的倍数。

I almost forgot to mention, it is possible for a JPEG or PNG file to be encoded as a BMP, but this is not common. Have a look at the biCompression field of the BITMAPINFOHEADER, if it's anything but BI_RGB you'll need a little more help.

我差点忘了提,JPEG 或 PNG 文件有可能被编码为 BMP,但这并不常见。看看 的 biCompression 字段BITMAPINFOHEADER,如果它不是 BI_RGB,您将需要更多帮助。

回答by Yola

and ready to go code, tested with g++ (not Windows, but may help someone):

并准备好使用 g++ 测试的代码(不是 Windows,但可能会帮助某人):

#pragma pack(1)

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

#include "bmp.h"

vector<char> buffer;
PBITMAPFILEHEADER file_header;
PBITMAPINFOHEADER info_header;

void fill() {
    std::ifstream file("data.bmp");

    if (file) {
        file.seekg(0,std::ios::end);
        streampos length = file.tellg();
        file.seekg(0,std::ios::beg);

        buffer.resize(length);
        file.read(&buffer[0],length);

        file_header = (PBITMAPFILEHEADER)(&buffer[0]);
        info_header = (PBITMAPINFOHEADER)(&buffer[0] + sizeof(BITMAPFILEHEADER));
    }
}

int main() {
    fill();

    cout << buffer[0] << buffer[1] << endl;
    cout << file_header->bfSize << endl;
    cout << info_header->biWidth << " " << info_header->biHeight << endl;

    return 0;
}

In bmp.h i have defined structures:

在 bmp.hi 中定义了结构:

#pragma once

typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;

typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

回答by Yoshimitsu

if coding in Visual Studios, before declaring your tagBITMAPFILEHEADER and tagBITMAPINFOHEADER structs (shown in Yola's response) be sure to include "#pragma pack(2)". Ohterwise the struct will be padded to the next 4 byte boundary, instead of the next 2 byte boundary, and the data will be garbage.

如果在 Visual Studios 中编码,在声明 tagBITMAPFILEHEADER 和 tagBITMAPINFOHEADER 结构(显示在 Yola 的响应中)之前,一定要包含“#pragma pack(2)”。Ohterwise struct 将被填充到下一个 4 字节边界,而不是下一个 2 字节边界,并且数据将是垃圾。

reference http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html

参考http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html

回答by philn

Expanding on what Yola wrote, this should be able to read in and output a file. It is not well tested, but seems to work. It uses format of the file it reads in when it outputs.

扩展 Yola 所写的内容,这应该能够读入和输出文件。它没有经过很好的测试,但似乎有效。它使用它在输出时读入的文件格式。

#include <iostream>
#include <unistd.h>
#include <fstream>

using std::cout;
using std::endl;
using std::ofstream;
using std::ifstream;

#pragma pack(1)
#pragma once

typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;

typedef struct tagBITMAPFILEHEADER {
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

unsigned char** reds;
unsigned char** greens;
unsigned char** blues;
int rows;
int cols;

void ColorTest() {
    // Makes Red Rectangle in top left corner. Rectangle stretches to right alot
    for (int i = rows / 10; i < 3 * rows / 10; i++)
        for (int j = cols / 10; j < 7 * cols / 10; j++)
            reds[i][j] = 0xff;

// Makes small green box in bottom right
    for (int i = 8 * rows / 10; i < rows; i++)
        for (int j = 8 * cols / 10; j < cols; j++)
            greens[i][j] = 0xff;

// Makes White box in the middle of the screeene    
    for (int i = rows * 4 / 10; i < rows * 6 / 10; i++)
        for (int j = cols * 4 / 10; j < cols * 6 / 10; j++) {
            greens[i][j] = 0xff;
            reds[i][j] = 0xff;
            blues[i][j] = 0xff;
        }

// Blue verticle rectangle bottom left
    for (int i = rows * 6 / 10; i < rows; i++)
        for (int j = cols * 0; j < cols * 1 / 10; j++)
            blues[i][j] = 0xff;
}

void RGB_Allocate(unsigned char**& dude) {
    dude = new unsigned char*[rows];
    for (int i = 0; i < rows; i++)
        dude[i] = new unsigned char[cols];
}

bool FillAndAllocate(char*& buffer, const char* Picture, int& rows, int& cols, int& BufferSize) { //Returns 1 if executed sucessfully, 0 if not sucessfull
    std::ifstream file(Picture);

    if (file) {
        file.seekg(0, std::ios::end);
        std::streampos length = file.tellg();
        file.seekg(0, std::ios::beg);

        buffer = new char[length];
        file.read(&buffer[0], length);

        PBITMAPFILEHEADER file_header;
        PBITMAPINFOHEADER info_header;

        file_header = (PBITMAPFILEHEADER) (&buffer[0]);
        info_header = (PBITMAPINFOHEADER) (&buffer[0] + sizeof(BITMAPFILEHEADER));
        rows = info_header->biHeight;
        cols = info_header->biWidth;
        BufferSize = file_header->bfSize;
        return 1;
    }
    else {
        cout << "File" << Picture << " don't Exist!" << endl;
        return 0;
    }
}

void GetPixlesFromBMP24(unsigned char** reds, unsigned char** greens, unsigned char** blues, int end, int rows, int cols, char* FileReadBuffer) { // end is BufferSize (total size of file)
    int count = 1;
int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
    for (int i = 0; i < rows; i++){
count += extra;
    for (int j = cols - 1; j >= 0; j--)
        for (int k = 0; k < 3; k++) {
                switch (k) {
                case 0:
                    reds[i][j] = FileReadBuffer[end - count++];
                    break;
                case 1:
                    greens[i][j] = FileReadBuffer[end - count++];
                    break;
                case 2:
                    blues[i][j] = FileReadBuffer[end - count++];
                    break;
                }
            }
            }
}

void WriteOutBmp24(char* FileBuffer, const char* NameOfFileToCreate, int BufferSize) {
    std::ofstream write(NameOfFileToCreate);
    if (!write) {
        cout << "Failed to write " << NameOfFileToCreate << endl;
        return;
    }
    int count = 1;
    int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
    for (int i = 0; i < rows; i++){
        count += extra;
        for (int j = cols - 1; j >= 0; j--)
            for (int k = 0; k < 3; k++) {
                switch (k) {
                case 0: //reds
                    FileBuffer[BufferSize - count] = reds[i][j];
                    break;
                case 1: //green
                    FileBuffer[BufferSize - count] = greens[i][j];
                    break;
                case 2: //blue
                    FileBuffer[BufferSize - count] = blues[i][j];
                    break;
                }
                count++;
            }
            }
    write.write(FileBuffer, BufferSize);
}


int main(int args, char** cat) {
char* FileBuffer; int BufferSize;

#define Picture "ReadInPicture.bmp"
if (!FillAndAllocate(FileBuffer, Picture, rows, cols, BufferSize)){cout << "File read error" << endl; return 0;}
cout << "Rows: " << rows << " Cols: " << cols << endl;

RGB_Allocate(reds);
RGB_Allocate(greens);
RGB_Allocate(blues);
GetPixlesFromBMP24( reds,  greens, blues,BufferSize, rows, cols, FileBuffer);
ColorTest();
#define WriteOutFile "OutputPicture.bmp"
WriteOutBmp24(FileBuffer,  WriteOutFile,BufferSize);
    return 1;
}

回答by b_yang

You have 2 good options:

您有 2 个不错的选择:

  1. Load and parse the BMP file yourself. BMP files start with a BITMAPFILEHADER, followed by a BITMAPINFOHEADER, followed by 0 or more RGBQUAD's (palette entry). Offset to pixel data is in BITMAPFILEHADER, but you should check BITMAPINFOHEADER to make sure the image format is what you expect/support.

  2. Call LoadImage() API with LR_CREATEDIBSECTION flag, it will return a handle to a DIB section. Next you call GetObject() passing in the returned handle and a pointer to a DIBSECTION structure. Then you read DIBSECTION structure for bitmap size, format, pointer to pixel data, etc.

  1. 自己加载和解析 BMP 文件。BMP 文件以 BITMAPFILEHADER 开头,然后是 BITMAPINFOHEADER,然后是 0 个或多个 RGBQUAD(调色板条目)。像素数据的偏移量在 BITMAPFILEHADER 中,但您应该检查 BITMAPINFOHEADER 以确保图像格式是您期望/支持的。

  2. 使用 LR_CREATEDIBSECTION 标志调用 LoadImage() API,它将返回一个 DIB 部分的句柄。接下来调用 GetObject(),传入返回的句柄和指向 DIBSECTION 结构的指针。然后读取位图大小、格式、像素数据指针等的 DIBSECTION 结构。

Option 2 is better if you're on Windows, because presumably LoadImage() checks for invalid file formats for you, and can load more than just BMP files.

如果您使用的是 Windows,则选项 2 会更好,因为大概 LoadImage() 会为您检查无效的文件格式,并且可以加载的不仅仅是 BMP 文件。

When accessing Windows BMP pixels, remember lines are always DWORD-aligned.

访问 Windows BMP 像素时,请记住行始终是 DWORD 对齐的。

回答by Albert Perrien

You could try MagicWandan API of the ImageMagiclibrary.

你可以试试MagicWand一个ImageMagic库的 API 。

回答by Ben Zotto

There are definitely libraries out there (see other answers), but in a jiffy, it's frankly a brain-dead simple file format that you can parse yourself quite easily. Details are here:

那里肯定有库(请参阅其他答案),但坦率地说,它是一种简单的文件格式,您可以很容易地解析自己。详细信息在这里:

http://www.fileformat.info/format/bmp/egff.htm

http://www.fileformat.info/format/bmp/egff.htm

(I've been out of Win32 for a few years, but the LoadImagefunction can get you an HBITMAP from a BMP file. I'm not certain how to turn that into a pixel array directly, but I'd imagine there's some contortion with a DC that would let you grab the values. http://support.microsoft.com/kb/158898

(我已经离开 Win32 好几年了,但是该LoadImage函数可以从 BMP 文件中获取 HBITMAP。我不确定如何直接将其转换为像素阵列,但我想这会有些扭曲一个可以让你获取值的DC。http://support.microsoft.com/kb/158898

More hints: http://alexkr.com/source-code/26/accessing-bitmap-pixels-in-gdi/)

更多提示:http: //alexkr.com/source-code/26/accessing-bitmap-pixels-in-gdi/