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

时间:2011-04-22 02:31:44

标签: c++ windows image bmp

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

提前致谢!

7 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:5)

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

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

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

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

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

我几乎忘记提及,JPEG或PNG文件可能被编码为BMP,但这并不常见。看看BITMAPINFOHEADER的biCompression字段,如果除了BI_RGB之外什么都不需要,你需要更多的帮助。

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

您可以尝试MagicWand ImageMagic库的API。

答案 4 :(得分:2)

肯定有图书馆(请参阅其他答案),但是在一瞬间,它坦率地说是一种脑死亡的简单文件格式,你可以很容易地解析自己。详情如下:

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/

答案 5 :(得分:2)

您有两个不错的选择:

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

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

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

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

答案 6 :(得分:2)

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

func doneJournalEntry(sender: UIButton) {
    journalEntryTextArea?.resignFirstResponder()
    var entry = journalEntryTextArea?.text

    if let entry = entry {
        let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
        let managedObjectContext = appDelegate.managedObjectContext!
        let request = NSFetchRequest(entityName: "Session")
        var error: NSError?

        // new entry
        if journalEntryText == "Record your thoughts" {
            let result = managedObjectContext.executeFetchRequest(request, error: &error)

            if let objects = result as? [Session] {
                if let lastTime = objects.last {
                    lastTime.journalEntry = entry
                }
            }
        } else {
            // existing entry
            let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
            request.sortDescriptors = [sortDescriptor]

            let result = managedObjectContext.executeFetchRequest(request, error: &error)
            if let objects = result as? [Session] {
                var location = journalEntryCoreDataLocation

                var object = objects[location!]
                object.journalEntry = entry
            }
        }

        if !managedObjectContext.save(&error) {
            println("save failed: \(error?.localizedDescription)")
        }
    }

   // in "edit" mode, self.navigationController is `nil` and this fails
   // in "record a new entry" mode, it's not nil and works fine
    self.navigationController?.popViewControllerAnimated(true)
}

func cancelEntryOrEditAndReturn(sender: UIButton) {
    self.journalEntryTextArea?.resignFirstResponder()

   // in "edit" mode, self.navigationController is `nil` and this fails
   // in "record a new entry" mode, it's not nil and works fine
    self.navigationController?.popViewControllerAnimated(true)
}