我怎麼能在所有的像素[H * W]用C或C++在Windows [更好,沒有任何第三方庫]讀取24位BMP圖像的顏色值。我得到Dev-C++
一個工作代碼將非常感激,因爲我從來沒有工作過圖像閱讀&谷歌搜索後[如果你可以谷歌比我更好,PLZ提供一個鏈接]。讀取的像素值
讀取的像素值
回答
看看你可以試試這個:
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)。
如果您正在閱讀24位彩色BMP,您還需要注意行填充。由於某些原因,BMP預計所有行將以4的字節倍數對齊。您可以使用以下公式計算圖像寬度的填充: int row_padded =(width * 3 + 3)&(〜3)' 然後, fread()'一行'row_padded'字節,但只使用寬度元素。其餘部分被丟棄... – dominikschnitzer 2012-09-05 13:05:34
請注意,上面的函數在圖像的寬度和高度的賦值方面有一些不足:1.它假設小端。它不適用於大端平臺2.它假定sizeof(int)是4.如果不是,它將不起作用。 – 2015-01-22 07:42:24
你必須先讀位圖頭。在獲得位圖標題中的數據偏移並逐行讀取像素後,請注意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
我使用C/C++沒有VC++,日Thnx的幫助:) – Sourav 2012-02-15 15:35:37
填充修復後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;
}
你永遠不會刪除數據 - >內存泄漏... – 2014-05-26 18:31:06
@arc_lupus他返回數據。當他/她完成這個操作時,調用者需要'刪除'數據。 – rubenvb 2014-06-26 08:00:53
可能是錯的,但我相信這段代碼有一個錯字。所需的內存量爲3 * width * height,而不是row_padded。 row_padded僅用於讀取文件。 – 2015-06-08 10:04:13
一個簡單的,OS-便攜式的Python溶液在How can I read the RGB value of a given pixel in Python?提供。適應多種圖像格式,照顧填充等。
我已經創建了一個位圖類,對於具有每像素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
我喜歡你的方法 - 但這種方法不起作用。它沒有得到正確的高度和寬度。 – 2016-06-03 12:54:19
@robben_ford_fan_boy什麼是正確的值,你會得到什麼。我記得使用這個有點廣泛,雖然它可能是這個版本有一個錯誤 – Liam 2016-06-03 13:00:08
我認爲實際是1300,同時它是拉高20和高度和寬度 – 2016-06-03 15:24:50
下面是答案的工作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;
}
,因爲我沒有,我不能在頂級回答評論足夠的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;
}
我嘗試了上述代碼由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;
}
這是如何回答這個問題的? – varro 2017-10-19 20:23:36
<以 「bmp文件格式」 谷歌搜索1秒:http://en.wikipedia.org/wiki/BMP_file_format – 2012-02-15 15:24:57