2012-02-15 70 views
7

我怎么能在所有的像素[H * W]用C或C++在Windows [更好,没有任何第三方库]读取24位BMP图像的颜色值。我得到Dev-C++
一个工作代码将非常感激,因为我从来没有工作过图像阅读&谷歌搜索后[如果你可以谷歌比我更好,PLZ提供一个链接]。读取的像素值

+4

<以 “bmp文件格式” 谷歌搜索1秒:http://en.wikipedia.org/wiki/BMP_file_format – 2012-02-15 15:24:57

回答

35

看看你可以试试这个:

unsigned char* readBMP(char* filename) 
{ 
    int i; 
    FILE* f = fopen(filename, "rb"); 
    unsigned char info[54]; 
    fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header 

    // extract image height and width from header 
    int width = *(int*)&info[18]; 
    int height = *(int*)&info[22]; 

    int size = 3 * width * height; 
    unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel 
    fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once 
    fclose(f); 

    for(i = 0; i < size; i += 3) 
    { 
      unsigned char tmp = data[i]; 
      data[i] = data[i+2]; 
      data[i+2] = tmp; 
    } 

    return data; 
} 

现在data应包含(R,G,B)的像素值。像素(i,j)的颜色存储在data[3 * (i * width + j)],data[3 * (i * width + j) + 1]data[3 * (i * width + j) + 2]

在最后部分,每一个第一和第三像素之间的交换做是因为窗口存储的颜色值作为(B,G,R)三元组,而不是(R,G,B)。

+4

如果您正在阅读24位彩色BMP,您还需要注意行填充。由于某些原因,BMP预计所有行将以4的字节倍数对齐。您可以使用以下公式计算图像宽度的填充: int row_padded =(width * 3 + 3)&(〜3)' 然后, fread()'一行'row_padded'字节,但只使用宽度元素。其余部分被丢弃... – dominikschnitzer 2012-09-05 13:05:34

+2

请注意,上面的函数在图像的宽度和高度的赋值方面有一些不足:1.它假设小端。它不适用于大端平台2.它假定sizeof(int)是4.如果不是,它将不起作用。 – 2015-01-22 07:42:24

13

填充修复后readBMP功能的代码:

unsigned char* ReadBMP(char* filename) 
{ 
    int i; 
    FILE* f = fopen(filename, "rb"); 

    if(f == NULL) 
     throw "Argument Exception"; 

    unsigned char info[54]; 
    fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header 

    // extract image height and width from header 
    int width = *(int*)&info[18]; 
    int height = *(int*)&info[22]; 

    cout << endl; 
    cout << " Name: " << filename << endl; 
    cout << " Width: " << width << endl; 
    cout << "Height: " << height << endl; 

    int row_padded = (width*3 + 3) & (~3); 
    unsigned char* data = new unsigned char[row_padded]; 
    unsigned char tmp; 

    for(int i = 0; i < height; i++) 
    { 
     fread(data, sizeof(unsigned char), row_padded, f); 
     for(int j = 0; j < width*3; j += 3) 
     { 
      // Convert (B, G, R) to (R, G, B) 
      tmp = data[j]; 
      data[j] = data[j+2]; 
      data[j+2] = tmp; 

      cout << "R: "<< (int)data[j] << " G: " << (int)data[j+1]<< " B: " << (int)data[j+2]<< endl; 
     } 
    } 

    fclose(f); 
    return data; 
} 
+1

你永远不会删除数据 - >内存泄漏... – 2014-05-26 18:31:06

+2

@arc_lupus他返回数据。当他/她完成这个操作时,调用者需要'删除'数据。 – rubenvb 2014-06-26 08:00:53

+0

可能是错的,但我相信这段代码有一个错字。所需的内存量为3 * width * height,而不是row_padded。 row_padded仅用于读取文件。 – 2015-06-08 10:04:13

2

我已经创建了一个位图类,对于具有每像素24位的BMP文件的工作。如果bmp不兼容,你应该得到一个相关的错误。

它依循沿着几乎完全与Wikipedia article。 (一个问题是,它不适用于像素数组偏移量大于255的文件。这是在代码中注明的,应该很容易修复。)

我一直在使用它与bmp由mspaint创建的文件。

这里是从的顶部索引的示例用法

example.cpp

#include "bmp.h" 

int main() { 
    // load the file. The constructor now does most of the work 
    BitMap example_bmp("examplefile.bmp"); 

    // get the vector <R,G,B> for the pixel at (1,1) 
    std::vector<unsigned int> example_vector = example_bmp.getPixel(1,1); 
} 

example_vector现在包含RGB(按该顺序)在坐标(1,1)的像素的值形象,下去。索引从0开始。请参阅Wikipedia示例。

这里是头文件:

#ifndef BMP_H 
#define BMP_H 

#include <iostream> 
#include <vector> 
#include <fstream> 
class BitMap { 

    private: 
     unsigned char m_bmpFileHeader[14]; 
     unsigned int m_pixelArrayOffset; 
     unsigned char m_bmpInfoHeader[40]; 

     int m_height; 
     int m_width; 
     int m_bitsPerPixel; 

     int m_rowSize; 
     int m_pixelArraySize; 

     unsigned char* m_pixelData; 

     char * m_copyname; 
     const char * m_filename; 
    public: 
     BitMap(const char * filename); 
     ~BitMap(); 

     std::vector<unsigned int> getPixel(int i,int j); 

     void makeCopy(char * filename); 
     void writePixel(int i,int j, int R, int G, int B); 

     void swapPixel(int i, int j, int i2, int j2); 

     void dispPixelData(); 

     int width() {return m_width;} 
     int height() {return m_height;} 

     int vd(int i, int j); 
     int hd(int i, int j); 

     bool isSorted(); 
}; 

BitMap::BitMap(const char * filename) { 

    using namespace std; 

    m_filename = filename; 

    ifstream inf(filename); 
    if(!inf) { 
     cerr<<"Unable to open file: "<<filename<<"\n"; 
    } 



    //unsigned char m_bmpFileHeader[14]; 
    unsigned char a; 
    for(int i =0;i<14;i++) { 
     inf>>hex>>a; 
     m_bmpFileHeader[i] = a; 
    } 
    if(m_bmpFileHeader[0]!='B' || m_bmpFileHeader[1]!='M') { 
     cerr<<"Your info header might be different!\nIt should start with 'BM'.\n"; 
    } 

    /* 
     THE FOLLOWING LINE ONLY WORKS IF THE OFFSET IS 1 BYTE!!!!! (it can be 4 bytes max) 
     That should be fixed now. 
     old line was 
     m_pixelArrayOffset = m_bmpFileHeader[10]; 
    */ 
    unsigned int * array_offset_ptr = (unsigned int *)(m_bmpFileHeader + 10); 
    m_pixelArrayOffset = *array_offset_ptr; 


    if(m_bmpFileHeader[11] != 0 || m_bmpFileHeader[12] !=0 || m_bmpFileHeader[13] !=0) { 
     std::cerr<< "You probably need to fix something. bmp.h("<<__LINE__<<")\n"; 
    } 



    //unsigned char m_bmpInfoHeader[40]; 
    for(int i=0;i<40;i++) { 
     inf>>hex>>a; 
     m_bmpInfoHeader[i]=a; 
    } 

    int * width_ptr = (int*)(m_bmpInfoHeader+4); 
    int * height_ptr = (int*)(m_bmpInfoHeader+8); 

    m_width = *width_ptr; 
    m_height = *height_ptr; 

    printf("W: %i, H: %i", m_width, m_height); 

    m_bitsPerPixel = m_bmpInfoHeader[14]; 
    if(m_bitsPerPixel!=24) { 
     cerr<<"This program is for 24bpp files. Your bmp is not that\n"; 
    } 
    int compressionMethod = m_bmpInfoHeader[16]; 
    if(compressionMethod!=0) { 
     cerr<<"There's some compression stuff going on that we might not be able to deal with.\n"; 
     cerr<<"Comment out offending lines to continue anyways. bpm.h line: "<<__LINE__<<"\n"; 
    } 


    m_rowSize = int(floor((m_bitsPerPixel*m_width + 31.)/32)) *4; 
    m_pixelArraySize = m_rowSize* abs(m_height); 

    m_pixelData = new unsigned char [m_pixelArraySize]; 

    inf.seekg(m_pixelArrayOffset,ios::beg); 
    for(int i=0;i<m_pixelArraySize;i++) { 
     inf>>hex>>a; 
     m_pixelData[i]=a; 
    } 



} 

BitMap::~BitMap() { 
    delete[] m_pixelData; 
} 

void BitMap::dispPixelData() { 
    for(int i=0;i<m_pixelArraySize;i++) { 
     std::cout<<(unsigned int)m_pixelData[i]<<" "; 
    } 
    std::cout<<"\n"; 
} 

// output is in rgb order. 
std::vector<unsigned int> BitMap::getPixel(int x, int y) { 
    if(x<m_width && y<m_height) { 
     std::vector<unsigned int> v; 
     v.push_back(0); 
     v.push_back(0); 
     v.push_back(0); 

     y = m_height -1- y; //to flip things 
     //std::cout<<"y: "<<y<<" x: "<<x<<"\n"; 
     v[0] = (unsigned int) (m_pixelData[ m_rowSize*y+3*x+2 ]); //red 
     v[1] = (unsigned int) (m_pixelData[ m_rowSize*y+3*x+1 ]); //greed 
     v[2] = (unsigned int) (m_pixelData[ m_rowSize*y+3*x+0 ]); //blue 


     return v; 
    } 
    else {std::cerr<<"BAD INDEX\n";std::cerr<<"X: "<<x<<" Y: "<<y<<"\n";} 
} 

void BitMap::makeCopy(char * filename) { 
    std::ofstream copyfile(filename); 
    std::ifstream infile(m_filename); 
    m_copyname = filename; 

    unsigned char c; 
    while(infile) { 
     infile>>c; 
     copyfile<<c; 
    } 
} 

// changes the file 
void BitMap::writePixel(int x,int y, int R, int G, int B) { 
    std::fstream file(m_filename); 
    y = m_height -1- y; // to flip things. 
    int blueOffset = m_pixelArrayOffset+m_rowSize*y+3*x+0; 

    // writes to the file 
    file.seekg(blueOffset,std::ios::beg); 
    file<< (unsigned char)B; 
    file.seekg(blueOffset+1,std::ios::beg); 
    file<< (unsigned char)G; 
    file.seekg(blueOffset+2,std::ios::beg); 
    file<< (unsigned char)R; 

    // edits data in pixelData array 
    m_pixelData[m_rowSize*y+3*x+2] = (unsigned char)R; 
    m_pixelData[m_rowSize*y+3*x+1] = (unsigned char)G; 
    m_pixelData[m_rowSize*y+3*x+0] = (unsigned char)B; 
} 

// changes the file 
void BitMap::swapPixel(int i, int j, int i2, int j2) { 
    std::vector<unsigned int> p1 = (*this).getPixel(i,j); 

    std::vector<unsigned int> p2 = (*this).getPixel(i2,j2); 

    (*this).writePixel(i,j,p2[0],p2[1],p2[2]); 
    (*this).writePixel(i2,j2,p1[0],p1[1],p1[2]); 

} 
#endif 
+0

我喜欢你的方法 - 但这种方法不起作用。它没有得到正确的高度和宽度。 – 2016-06-03 12:54:19

+0

@robben_ford_fan_boy什么是正确的值,你会得到什么。我记得使用这个有点广泛,虽然它可能是这个版本有一个错误 – Liam 2016-06-03 13:00:08

+0

我认为实际是1300,同时它是拉高20和高度和宽度 – 2016-06-03 15:24:50

4

下面是答案的工作C++版本:

#include <fstream> 
#include <iostream> 
#include <string> 
#include <array> 
#include <vector> 
#include <iterator> 

std::vector<char> readBMP(const std::string &file) 
{ 
    static constexpr size_t HEADER_SIZE = 54; 

    std::ifstream bmp(file, std::ios::binary); 

    std::array<char, HEADER_SIZE> header; 
    bmp.read(header.data(), header.size()); 

    auto fileSize = *reinterpret_cast<uint32_t *>(&header[2]); 
    auto dataOffset = *reinterpret_cast<uint32_t *>(&header[10]); 
    auto width = *reinterpret_cast<uint32_t *>(&header[18]); 
    auto height = *reinterpret_cast<uint32_t *>(&header[22]); 
    auto depth = *reinterpret_cast<uint16_t *>(&header[28]); 

    std::cout << "fileSize: " << fileSize << std::endl; 
    std::cout << "dataOffset: " << dataOffset << std::endl; 
    std::cout << "width: " << width << std::endl; 
    std::cout << "height: " << height << std::endl; 
    std::cout << "depth: " << depth << "-bit" << std::endl; 

    std::vector<char> img(dataOffset - HEADER_SIZE); 
    bmp.read(img.data(), img.size()); 

    auto dataSize = ((width * 3 + 3) & (~3)) * height; 
    img.resize(dataSize); 
    bmp.read(img.data(), img.size()); 

    char temp = 0; 

    for (auto i = dataSize - 4; i >= 0; i -= 3) 
    { 
     temp = img[i]; 
     img[i] = img[i+2]; 
     img[i+2] = temp; 

     std::cout << "R: " << int(img[i] & 0xff) << " G: " << int(img[i+1] & 0xff) << " B: " << int(img[i+2] & 0xff) << std::endl; 
    } 

    return img; 
} 
2

,因为我没有,我不能在顶级回答评论足够的stackoverflow代表尚未,但我只想指出一个非常关键的错误与该实施。

某些位图可以用负高度书写,因此当您尝试分配图像数据缓冲区时,代码将与std::bad_alloc一起崩溃。具有负高度的位图意味着图像数据从顶部到底部而不是从传统的底部到顶部存储。因此,稍微更好的版本顶层答案的是(仍然不包括便携性,以便与不同的端序和字节的大小的系统):

unsigned char* readBMP(char* filename) 
{ 
    int i; 
    FILE* f = fopen(filename, "rb"); 
    unsigned char info[54]; 
    fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header 

    // extract image height and width from header 
    int width = *(int*)&info[18]; 
    int height = *(int*)&info[22]; 
    int heightSign =1; 
    if(height<0){ 
     heightSign = -1; 
    } 

    int size = 3 * width * abs(height); 
    unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel 
    fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once 
    fclose(f); 

    if(heightSign == 1){ 
     for(i = 0; i < size; i += 3) 
     { 
      //code to flip the image data here.... 
     } 
    } 
    return data; 
} 
-2

我尝试了上述代码由didil和我下文引用作为参考(对不起,我没有足够的声誉发表评论)。

代码编译成功,但在for循环迭代期间崩溃。我认为这与'我'是uint32_t有关,而不是int。当'i'达到零时,for循环仍然有效,'i'递减3,变成负值。由于'我'是一个uint32_t,它的值变成一个正值,并且大于0.因此,当'i'指向超出img存储边界时,for循环永远不会结束并导致执行崩溃。

#include <fstream> 
#include <iostream> 
#include <string> 
#include <array> 
#include <vector> 
#include <iterator> 

std::vector<char> readBMP(const std::string &file) 
{ 
    static constexpr size_t HEADER_SIZE = 54; 

    std::ifstream bmp(file, std::ios::binary); 

    std::array<char, HEADER_SIZE> header; 
    bmp.read(header.data(), header.size()); 

    auto fileSize = *reinterpret_cast<uint32_t *>(&header[2]); 
    auto dataOffset = *reinterpret_cast<uint32_t *>(&header[10]); 
    auto width = *reinterpret_cast<uint32_t *>(&header[18]); 
    auto height = *reinterpret_cast<uint32_t *>(&header[22]); 
    auto depth = *reinterpret_cast<uint16_t *>(&header[28]); 

    std::cout << "fileSize: " << fileSize << std::endl; 
    std::cout << "dataOffset: " << dataOffset << std::endl; 
    std::cout << "width: " << width << std::endl; 
    std::cout << "height: " << height << std::endl; 
    std::cout << "depth: " << depth << "-bit" << std::endl; 

    std::vector<char> img(dataOffset - HEADER_SIZE); 
    bmp.read(img.data(), img.size()); 

    auto dataSize = ((width * 3 + 3) & (~3)) * height; 
    img.resize(dataSize); 
    bmp.read(img.data(), img.size()); 

    char temp = 0; 

    for (auto i = dataSize - 4; i >= 0; i -= 3) 
    { 
     temp = img[i]; 
     img[i] = img[i+2]; 
     img[i+2] = temp; 

     std::cout << "R: " << int(img[i] & 0xff) << " G: " << int(img[i+1] & 0xff) << " B: " << int(img[i+2] & 0xff) << std::endl; 
    } 

    return img; 
} 
+0

这是如何回答这个问题的? – varro 2017-10-19 20:23:36