我如何读取BMP像素值到一个数组

How can I read BMP pixel values into an array?

本文关键字:数组 一个 BMP 何读取 读取 像素      更新时间:2023-10-16

我正在用c++(在Windows上)编写代码,我正在尝试提取灰度bmp的像素值。我不关心是否保留任何元数据,只想将像素值存储在字符数组中。我还没能找到一个标准的或"典型"的方式来手动做到这一点,所以我想知道是否有一个简单的库,人们用它来加载位图到内存。

提前感谢!

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

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

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

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

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

像素值有几个陷阱。首先,它的顺序是(蓝、绿、红),和其他人的顺序正好相反。其次,这些行从图像的底部到顶部,与其他所有行相反。最后,一行中的字节数将一直填充到4的下一个倍数。

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

如果在Visual studio中编码,在声明tagBITMAPFILEHEADER和tagBITMAPINFOHEADER结构体(如Yola的响应所示)之前,请确保包含"#pragma pack(2)"。否则,结构体将被填充到下一个4字节边界,而不是下一个2字节边界,并且数据将成为垃圾。

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

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

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

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

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

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

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

你有两个很好的选择:

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

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

选项2如果你是在Windows上是更好的,因为大概LoadImage()为你检查无效的文件格式,可以加载更多的BMP文件。

访问Windows BMP像素时,请记住行始终与dword对齐。

相关文章: