读取bmp文件中的像素值
如何在Windows上以C或C ++的所有像素[h * w]读取24位BMP图像的颜色值[更好,没有任何第三方库]。 我得到了Dev-C ++
一个工作代码将非常受欢迎,因为我从未参与过图像阅读,并且在谷歌搜索后来到了SO [如果你能比我更好,请提供链接]。
你可以尝试这个:
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)。
填充修复后的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; }
这是一个有效的C ++版本的答案:
#include #include #include #include #include #include std::vector readBMP(const std::string &file) { static constexpr size_t HEADER_SIZE = 54; std::ifstream bmp(file, std::ios::binary); std::array header; bmp.read(header.data(), header.size()); auto fileSize = *reinterpret_cast(&header[2]); auto dataOffset = *reinterpret_cast (&header[10]); auto width = *reinterpret_cast (&header[18]); auto height = *reinterpret_cast (&header[22]); auto depth = *reinterpret_cast(&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 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; }
我无法对顶级答案发表评论,因为我还没有足够的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; }
您必须首先读取位图标头。 在获得位图标题中的数据偏移量并逐行读取像素后,请注意bmp文件格式的填充。
看看msdn http://msdn.microsoft.com/en-us/library/aa452883.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx
我创建了一个BitMap类,适用于每像素24位的bmp文件。 如果bmp不兼容,则应该收到相关错误。
它几乎与维基百科文章完全一致。 (一个问题是它不适用于像素数组偏移大于255的文件。这在代码中注明,应该很容易修复。)
我一直在使用mspaint创建的bmp文件。
这是一个示例用法
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 for the pixel at (1,1) std::vector example_vector = example_bmp.getPixel(1,1); }
example_vector现在包含从图像顶部索引的坐标(1,1)处的像素的rgb(按此顺序)值,向下。 指数从0开始。请参阅维基百科示例。
这是头文件:
#ifndef BMP_H #define BMP_H #include #include #include 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 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: "<>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>hex>>a; m_pixelData[i]=a; } } BitMap::~BitMap() { delete[] m_pixelData; } void BitMap::dispPixelData() { for(int i=0;i BitMap::getPixel(int x, int y) { if(x v; v.push_back(0); v.push_back(0); v.push_back(0); y = m_height -1- y; //to flip things //std::cout<<"y: "<>c; copyfile< p1 = (*this).getPixel(i,j); std::vector 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
如何在Python中读取给定像素的RGB值,提供了一个简单的,可移植的便携式Python解决方案? 。 适应多种图像格式,负责填充等。
我通过didil尝试了上面的代码,我在下面引用作为参考(对不起,我没有足够的声誉来发表评论)。
代码编译好,但在for循环迭代期间崩溃。 我认为这与’我’是uint32_t而不是int有关 。 当’i’到达零时,for循环仍然有效,’i’减3,变为负值。 由于’i’是uint32_t,它的值变为正值并且大于0.因此,当’i’指向超出img存储的边界时,for循环永远不会完成并导致执行崩溃。
#include #include #include #include #include #include std::vector readBMP(const std::string &file) { static constexpr size_t HEADER_SIZE = 54; std::ifstream bmp(file, std::ios::binary); std::array header; bmp.read(header.data(), header.size()); auto fileSize = *reinterpret_cast(&header[2]); auto dataOffset = *reinterpret_cast (&header[10]); auto width = *reinterpret_cast (&header[18]); auto height = *reinterpret_cast (&header[22]); auto depth = *reinterpret_cast(&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 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; }