【问题标题】:How can I read BMP pixel values into an array?如何将 BMP 像素值读入数组?
【发布时间】:2011-08-10 17:45:28
【问题描述】:

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

提前致谢!

【问题讨论】:

    标签: c++ windows image bmp


    【解决方案1】:

    准备好代码,用 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;
    }
    

    在 bmp.h 我定义了结构:

    #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;
    

    【讨论】:

    • #include "bmp.h"?它不是标准的包含文件。
    • 我还认为 OP 想要一个像素颜色数组,而不是所有文件字节的数组。
    【解决方案2】:

    将整个文件读入内存。前面会有一个小表头,剩下的就是像素值了。

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

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

    如果位深需要,后面会跟着一个调色板。

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

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

    【讨论】:

      【解决方案3】:

      如果在 Visual Studios 中编码,在声明您的 tagBITMAPFILEHEADER 和 tagBITMAPINFOHEADER 结构(显示在 Yola 的回复中)之前,请务必包含“#pragma pack(2)”。否则结构体将被填充到下一个 4 字节边界,而不是下一个 2 字节边界,并且数据将是垃圾。

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

      【讨论】:

        【解决方案4】:

        扩展 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;
        }
        

        【讨论】:

          【解决方案5】:

          您可以尝试MagicWand ImageMagic 库的 API。

          【讨论】:

            【解决方案6】:

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

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

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

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

            【讨论】:

              【解决方案7】:

              您有 2 个不错的选择:

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

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

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

              当访问 Windows BMP 像素时,记住行总是 DWORD 对齐的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-03-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-05-20
                • 1970-01-01
                • 2012-03-06
                • 1970-01-01
                相关资源
                最近更新 更多