IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Histogramme d'une image BMP - Où mon code cloche-t-il?


Sujet :

C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 10
    Points : 8
    Points
    8
    Par défaut Histogramme d'une image BMP - Où mon code cloche-t-il?
    Bonsoir à toutes et à tous,
    Je suis débutant (une semaine et demie intensive, en autodidacte) en programmation C++ (mon premier langage POO). Cependant, grâce au codes sources trouvés un peu partout sur la toile, en y passant des journées, et en passant au crible chaque ligne, j’ai réussi à apprendre la syntaxe, les bases de la POO et, je crois, la notion de pointeur (que je comprend comme une adresse mémoire).

    Après avoir réussi à charger / sauvegarder une image BMP, je me retrouve face à un mur lorsque j’essaie de mettre en place l’histogramme d’un BMP 24 bpp non compressé chargé au préalable.
    Voici le code que j’ai étudié :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // Constitue l'histogramme de l'image suivant le canal R=4,G=3,B=2,GRAY=1 ou RGB=0 :
    bool ClassBMP::Histogram(int Canal)
    {
    	if((!m_Bitmap) || (m_Header.m_BitCount!=24)) return false;
     
    	unsigned int Value;
     
    	if(Hist) delete Hist;
    	unsigned int * Hist = new unsigned int[255];
    	for(int i=0; i<256; i++) Hist[i]=0;
     
        unsigned char * BmpIndex = &m_Data[m_Header.m_OffBits];
     
    	for(int i=0; i<m_Header.m_Height; i++)
    	{
            for(int j=0; j<m_Header.m_Width; j++)
            {
                switch(Canal)
                {
                    case GRAY:  Value = (BmpIndex[0]+BmpIndex[1]+BmpIndex[2])/3;break;
                    case BLUE:  Value = BmpIndex[0];break;
                    case GREEN:  Value = BmpIndex[1];break;
                    case RED:   Value = BmpIndex[2];break;
                }
            }
            Hist[Value]+=1;
            BmpIndex+=3;
    	}
     
        return true;
    }
    A savoir que m_Data est le flux de données du BMP lu au moyen de la méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // Charge le flux de donnée m_Data à partir du fichier FileName :
    bool ClassBMP::LoadData(char * FileName)
    {
        unsigned long FileSize;                             // Taille du flux.
     
        std::ifstream IntStream;                            // Crée l'interface de gestion de flux Input (cf. http://www.cplusplus.com/reference/iostream/ifstream/).
        IntStream.open(FileName, std::ios::binary);         // Ouverture du fichier.
        if(IntStream==NULL) return false;                   // Si rien n'a été ouvert, on alerte.
     
        // Obtention de la taille du fichier :
        IntStream.seekg(0, std::ios_base::end);             // On déplace le curseur en fin de flux.
        FileSize = IntStream.tellg();                       // Position du curseur situé en fin de flux par rapport au début (0).
        IntStream.seekg(0, std::ios_base::beg);             // On se replace au début.
     
        if(m_Data) delete m_Data;                           // Supprime un eventuel précédant flux.
     
        // Changement de m_Data :
        m_Data = new unsigned char[FileSize];
    	if(m_Data==NULL)                                    // Si on a rien chargé.
    	{
    		IntStream.close();
    		return false;
    	}
     
    	IntStream.read((char*)m_Data, FileSize);            // Renseignement de m_Data.
    	IntStream.close();                                  // On oublie pas rompre le pont entre l'interface de flux et le fichier.
     
    	return true;
    }
    Je souhaiterais commencer par afficher ligne par ligne dans la console :
    Intensité du canal (de 0 à 255) | Nombre de pixel de cette intensité (obtenu par le script précédant).

    J'écris donc ceci dans le main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include "bmp.h"
    #include <iostream>
    using namespace std;
     
    int main()
    {
        ClassBMP Bmp1;
        Bmp1.Load("../Ressources/1pxRVB.bmp"); // Image d'un pixel de couleur composée pour tester, enregistré en 24bpp sans compression évidement.
     
        Bmp1.Histogram(RED);// RED est une constante globale (valant 4).
        for(int i=0; i<=255; i++) cout << "Niveau de rouge : " << i << ", " << Bmp1.Hist[i] << endl;
    }
    Et il me ressort 256 lignes de nombres "unsigned int" sans aucune signification apparente, indépendants de l'image choisie. J'imagine donc que c'est une erreur sur l'utilisation des pointeurs, mais cela fait trois jours que j'essaie en vain de régler le problème...

    Pourriez-vous m'aider?
    Je vous remercie d'avance et vous souhaite une bonne journée!

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Quelques remarques en vrac :
    1/ Le code source est mis 'initié'. Si tu débutes, c'est peut être un peu ambitieux de commencer par ça non ?
    2/ Les allocations de tableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Hist = new unsigned int[255];
    //ou
    m_Data = new unsigned char[FileSize];
    doivent se libérer comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    delete []Hist;
    delete []m_Data;
    3/MAIS SURTOUT : utilises des std::vector. En C++, il n'y a presque jamais de bonne raison d'utiliser un tableau à la 'C' (new []) à la place d'un std::vector.
    4/Si ce code compile :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	if(Hist) delete Hist;
    	unsigned int * Hist = new unsigned int[255];
    Alors Hist doit être défini ailleurs (probablement membre de ClassBMP) et est masqué par la déclaration de la variable locale : en gros tu ne remplis pas le bon tableau.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 10
    Points : 8
    Points
    8
    Par défaut
    1/ Oui effectivement, c'est un peu dure, mais je pense pouvoir m'en sortir en y mettant le temps et glanant exemples, documentations et conseils sur la toile... Mon but est de pouvoir utiliser toute la Mathématique que je connais (je suis capésien) en application à l'image numérique.
    C'est à dessein personnel, si je code n'est pas fantastique, ce n'est pas bien grave, aussi j'en arrive aux points 2/ et 3/ :

    2/ Sur la doc de l'operateur delete[] je lis :
    in C++, delete is an operator with a very specific behavior: An expression with the delete operator, first calls the appropriate destructors for each element in its array (if needed), and then call function operator delete[] to release the storage.
    N'est-ce donc pas plus large d'appeler delete plutot que delete[]? A moins que delete []Array ne signifie pas delete[] Array... ?!

    3/ En ce qui concerne les vecteurs, je lis aussi, sur le lien que tu m'as fourni :
    Compared to arrays, they provide almost the same performance for these tasks (accessing individual elements by their position index, ...)
    Quel avantage ai-je à les utiliser?


    4/ En effet, le code compile et j'ai oublié de signaler que mon attribut Hist était bien défini comme ceci :
    unsigned int * Hist; // Tableau histogramme : VALEUR | NB DE PIXEL.
    ...

    YES! Ça marche! Il me suffisait d'enlever cette nouvelle déclaration (partie en gras) :
    signed int * Hist = new unsigned int[255];
    Je te remercie pour ce coup de main! Si tu veux bien prendre le temps d'éclaircir les points 2/ et 3/ ce serait sympa!

    Merci encore!

    EDIT : je mets un bémol, il semble que les valeurs soient fausses (dans le cas général).
    C'est bon, c'est résolu. Simple problème de parcours de l'image. Encore merci!

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Killerboy Voir le message
    je suis capésien
    Ca fait mal ?
    Citation Envoyé par Killerboy Voir le message
    2/ Sur la doc de l'operateur delete[] je lis : N'est-ce donc pas plus large d'appeler delete plutot que delete[]? A moins que delete []Array ne signifie pas delete[] Array... ?!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Type *p_obj = new Type; // 1
    delete p_obj; // 2
    Type *p_tableau = new Type[5]; // 3
    delete []p_tableau; // 4
    Type *p_tableau_error = new Type[5]; // 5
    delete p_tableau_error; // 6
    1/ Allocation d'une zone mémoire suffisante pour stocker 1 objet Type + appel du constructeur 1 fois de Type
    2/ Appel du destructeur 1 fois de Type et libération de la mémoire allouée en 1
    3/ Allocation d'une zone mémoire suffisante pour stocker 5 objets Type + 5 appel du constructeur de Type
    4/ Appel du destructeur de Type sur les 5 objets et libération de la mémoire allouée en 3
    4/ Allocation d'une zone mémoire suffisante pour stocker 5 objets Type + 5 appel du constructeur de Type
    5/ Je n'ai pas la norme sous les yeux, mais il me semble que c'est un comportement indéterminé : tu ne peux prédire ce qu'il va se passer. Maintenant, l'implémentation que l'on trouve dans des compilateurs comme Visual C++ Express ou MinGW : appel du destructeur sur 1 objet et libération de la mémoire. Donc les quatre objets suivants ne sont pas 'détruits' au sens où leur destructeur n'est pas appelé. Ce qui se traduit chez toi par jamais de delete des membres (Hist par exemple).
    Donc règle d'or :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    new -> delete
    new [] -> delete []
    Il faut comprendre que même s'il porte le même nom, new/delete et new[]/delete[] correspondent à deux comportements différents et ne peuvent être mixés les uns avec les autres.
    Accessoirement delete[] p et delete []p, c'est la même chose.
    Citation Envoyé par Killerboy Voir le message
    3/ En ce qui concerne les vecteurs, je lis aussi, sur le lien que tu m'as fourni :
    Quel avantage ai-je à les utiliser?
    Réponse de jésuite : quel inconvénient as-tu à les utiliser ? Ou encore quel avantage tires-tu de l'utilisation des tableaux dynamiques à la C (new []) ?
    Réponse plus motivée : la gestion dynamique de la mémoire est assez complexe et l'expérience montre qu'elle concentre une grande majorité de bug. A titre d'information, tu peux lire l'article Pointeurs intelligents qui présente quelques problématiques. Pour remédier à ces différents problèmes (entre autres), la STL a été construite : elle permet de définir des types génériques couramment (pour ne pas dire systématiquement) utilisés dans les dev. C++ : vector, list, string, map, etc. ainsi que de nombreux algorithmes (tri, recherche, etc.).
    Et std::vector sert à t'abstraire des problèmes de gestion de mémoire dans l'utilisation de tableau. Tu n'as plus à faire d'allocation et de libération, tu n'as plus à te préoccuper comment est gérer la mémoire quand tu ajoutes ou supprimes un élément, etc... Donc, je redis : donnes-moi une bonne raison de ne pas utiliser std::vector ? D'autant que 1/C'est encore plus simple à mettre en œuvre que des allocations dynamiques, 2/les développeurs C++ connaissent la STL et le code sera compréhensible par quelqu'un arrivant derrière toi et souhaitant le reprendre.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 10
    Points : 8
    Points
    8
    Par défaut
    Oui, être capésien fait mal : au dos! Comme tout bon concours qui se respecte!

    Merci d'avoir éclairci les deux points suivants. Je vais effectivement transformer mon code au moyen des vecteurs (et rajouter les bons delete[] où il le faudra éventuellement).

    Pour répondre au jésuite : le principal avantage est que je n'ai pas à reprendre mon code. Maintenant, l'apprenti a compris que c'est un plus non négligeable. Donc ça le motive pour se documenter et réécrire les choses.

    J'avais lu des posts où l'on conseillait d'utiliser delete[] et les vecteurs, mais la documentation (et la traduction que j'en ai faite) m'a induit en erreur.

    Merci de ton aide!
    Au plaisir!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [PHP 5.3] Contrôler le type MIME d'une image + contrôle de mon code
    Par beegees dans le forum Langage
    Réponses: 3
    Dernier message: 31/05/2011, 21h03
  2. Histogramme d'une image BMP en niveaux de gris?
    Par bahiatoon dans le forum C++Builder
    Réponses: 13
    Dernier message: 19/11/2008, 16h36
  3. [C#] Comment convertir une image bmp en jpg !!!
    Par vandeyy dans le forum Windows Forms
    Réponses: 5
    Dernier message: 13/07/2004, 20h37
  4. inserer une image BMP dans un fichier rtf
    Par Alice9 dans le forum MFC
    Réponses: 17
    Dernier message: 06/07/2004, 10h31
  5. [BPW] Impression d'une image BMP
    Par Alcatîz dans le forum Turbo Pascal
    Réponses: 13
    Dernier message: 21/08/2003, 14h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo