web-dev-qa-db-fra.com

lire la valeur de pixel dans le fichier bmp

Comment puis-je lire la valeur de couleur des images BMP à 24 bits à tous les pixels [h * w] en C ou C++ sous Windows [mieux encore, sans aucune bibliothèque tierce]]. J'ai eu Dev-C++
Un code fonctionnel sera vraiment apprécié car je n’ai jamais travaillé sur la lecture d’image et suis venu à SO après Google (si vous pouvez google mieux que moi, fournissez un lien].

9
Sourav

Vous pouvez essayer celui-ci:

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

Maintenant, data devrait contenir les valeurs (R, V, B) des pixels. La couleur du pixel (i, j) est stockée dans data[3 * (i * width + j)], data[3 * (i * width + j) + 1] et data[3 * (i * width + j) + 2].

Dans la dernière partie, l’échange entre le premier et le troisième pixel est effectué, car Windows stocke les valeurs de couleur sous forme de triples (B, G, R), et non de (R, V, B).

39
0605002

Code de la fonction readBMP après le bourrage:

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;
}
16

Voici une version fonctionnelle de la réponse en 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;
}
5
didil

Je ne peux pas commenter la réponse de premier niveau car je n'ai pas encore assez de représentants stackoverflow, mais je voulais simplement signaler un bogue très critique lié à cette implémentation. 

Certaines bitmaps peuvent être écrites avec une hauteur négative. Ainsi, lorsque vous essayez d’allouer votre mémoire tampon de données d’image, votre code se bloque avec std::bad_alloc. Les images bitmap avec une hauteur négative signifient que les données d'image sont stockées de haut en bas au lieu de la base traditionnelle. Par conséquent, une version légèrement meilleure de la réponse de niveau supérieur est (toujours sans inclure la portabilité pour les systèmes avec une taille et des octets différents):

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;
}
4
Owen Morgan

J'ai créé une classe BitMap qui fonctionne pour les fichiers bmp de 24 bits par pixel. Si le bmp n'est pas compatible, vous devriez obtenir une erreur pertinente.

Il suit presque exactement l'article de Wikipedia . (Le seul problème est que cela ne fonctionne pas avec les fichiers dont le décalage de matrice de pixels est supérieur à 255. Cela est noté dans le code et doit pouvoir être facilement résolu.)

Je l'utilise avec les fichiers bmp créés par mspaint. 

Voici un exemple d'utilisation

exemple.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 contient maintenant les valeurs rgb (dans cet ordre) du pixel à la coordonnée (1,1) indexée du haut de l'image, en descendant. Les indices commencent à 0. Voir les exemples Wikipedia.

Voici le fichier d'en-tête:

#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
3
Liam

Vous devez d'abord lire l'en-tête bitmap. Une fois arrivé au décalage de données que vous trouverez dans les en-têtes de bitmap et lisez les pixels ligne par ligne, veillez au remplissage au format de fichier bmp.

jetez un oeil sur msdn http://msdn.Microsoft.com/en-us/library/aa452883.aspx

http://msdn.Microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx

2
AlexTheo

Une solution Python simple et portable sur OS est fournie dans Comment lire la valeur RVB d'un pixel donné en Python . S'adapte à plusieurs formats d'image, prend en charge le remplissage, etc.

1
Chris Johnson