Conversion d'un bitmap en niveau de gris - Cocoa

Illuvatar

Membre actif
28 Décembre 2004
148
5
37
Hello,

Dans le cadre d'un de mes projets je suis amené à réaliser une fonction de détection de contour d'une forme simple mais avant de proceder à cette opération je souhaiterais prétraiter mon image en entrée afin de la standardiser : Je voudrais obtenir une représentation bitmap de l'image en 256 niveaux de gris que je pourrais stocker par la suite dans une matrice pour la suite de mes opérations.
J'ai donc écrit le code suivant ( inspiré par un article de projetomega.org ) ( sachant que mon image de départ est un NSImage nommé motif ) :

Bloc de code:
//Déclaration des variables d'image/representation d'image

    NSBitmapImageRep *MotifBitmap = [NSBitmapImageRep
                    imageRepWithData:[motif TIFFRepresentation]];

    int w = [MotifBitmap pixelsWide];
    int h = [MotifBitmap pixelsHigh];
	
	NSImage *MotifGris = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];

    NSBitmapImageRep *MotifGrisRep = [[[NSBitmapImageRep alloc] 
                    initWithBitmapDataPlanes:NULL
                    pixelsWide:w 
                    pixelsHigh:h 
                    bitsPerSample:8 
                    samplesPerPixel:1
                    hasAlpha:NO
                    isPlanar:NO
                    colorSpaceName:NSCalibratedWhiteColorSpace
                    bytesPerRow:NULL 
                    bitsPerPixel:NULL] autorelease];
		
//Parcours de l'image pixel par pixel			
					
unsigned char *srcData = [MotifBitmap  bitmapData];
unsigned char *destData = [MotifGrisRep bitmapData];

unsigned char *p1, *p2;
int n = [MotifBitmap bitsPerPixel] / 8;


int x,y;
for ( y = 0; y < h; y++ ) {
        for ( x = 0; x < w; x++ ) {
        p1 = srcData + n * (y * w + x);       
		p2 = destData + y * w + x;
        *p2 = (unsigned char)rint((*p1+*(p1+1)+*(p1+2))/3);

        }
}

//Affichage du r&#233;sultat dans un NSImageView nomm&#233; apercu

[MotifGris addRepresentation:MotifGrisRep];
[apercu setImage:MotifGris];

Je pensais que ce code marcherait pour n'importe quelle image en true color ( m&#234;me avec une couche alpha ) mais apr&#232;s avoir test&#233; mon programme mes r&#233;sultats sont assez d&#233;cevants :
Pour quelques images, j'obtiens parfaitement une version en niveaux de gris mais pour beaucoup d'autres j'obtiens juste une image incoh&#233;rente...

Si quelqun pouvait m'aider &#224; avancer ce serait hyper-sympa ( j'ai cherch&#233; dans le forum pour voir si qqun avait deja eu le m&#234;me type de probl&#232;me mais je n'ai rien trouv&#233; )...

Merci d'avance.
 
Si ça marche pour certaines images, c'est que ton algo n'est pas complètement faux. :rateau:
Quelle est la différence entre les images pour lequel il marche et celles pour lequel il ne marche par ?
Est-ce que tu es sur de ton parcours de tableau, il n'y a pas un décalage quelque part qui fait que ton image devient incohérente. Tu recalcules p2 avec 3 valeurs consécutives de p1, mais est-ce que tu es sur que ton image est toujours en 24 bits ? Non puisque tu as introduit la variable n. :siffle:
 
Thx pour ton soutien
J'ai un peu regardé mais je n'ai pas réussi à trouvé de critère pertinent pour distinguer les images qui marchent de celles qui ne marchent pas...
En tout cas ca marche pour un psd RGB 8bits/chan enregistré sous photoshop sans profil de couleur...
Je vais déjà poursuivre mon programme ( detection de contour ) avec des images test de ce type mais bon j'aurais bien aimé connaitre le fin mot de l'histoire...
 
mais est-ce que tu es sur que ton image est toujours en 24 bits ? Non puisque tu as introduit la variable n

Je pensais avoir bien verifié ( j'avais fait un NSLog en sortie au début pour controler n et je crois n'être tombé que sur du 24/32bits) mais je suis de plus en plus persuadé que c'est un truc comme ca qui fait planter le tout... À la limite si n=1 on est sur d'être en 256 couleurs ou en 256 gris ( dites moi si je me trompe ) et là je devrais calculer la teinte grise d'une manière différente...
 
Je pensais avoir bien verifié ( j'avais fait un NSLog en sortie au début pour controler n et je crois n'être tombé que sur du 24/32bits) mais je suis de plus en plus persuadé que c'est un truc comme ca qui fait planter le tout... À la limite si n=1 on est sur d'être en 256 couleurs ou en 256 gris ( dites moi si je me trompe ) et là je devrais calculer la teinte grise d'une manière différente...
n indique si ta couleur de départ p1 est codée sur 1, 2 ou 3 octets. Le but est de coder la couleur d'arrivée p2 sur un octet à partir du ou des octets de la couleurs de départ p1, donc ta ligne qui calcule *p2 doit tenir compte de la profondeur de la couleur de départ. Or dans le code que tu as donné, ce n'est valable que pour une couleur de départ codée sur 3 octets.
 
En fait j'avais fait l'hypoth&#232;se que l'image que j'aurais &#224; traiter serait n&#233;cessairement en RGB donc :
Toutes mes images de tests ( y compris celles qui ne marchent pas ) ont un profil RGB.

-n=1 : pas &#224; prendre en compte
-n=2 : impossible ?
-n=3 : codage 24 bits RGB donc aucun probl&#232;me ( &#224; priori... )
-n=4 codage 24 bits RGB + couche alpha donc je pensais laisser tomber cette couche et ne considerer que p1, p1+1 et p1+2 mais je crois &#224; la reflexion que ce n'est pas coh&#233;rent comme d&#233;cision. Je vais donc rajouter un *p2=(*p2) * (*(p1+3)) dans ce cas.
n>4 : &#192; priori pas &#224; prendre en compte.
Plutot des images avec un profil couleurs sp&#233;cifique non RGB ( je me trompe peut-&#234;tre ? ).

Maintenant peut-&#234;tre que je me plante totalement... Une image RGB peut-elle &#234;tre cod&#233;e sur plus de 3 ou 4 octets ?
 
Thx pour ta réponse Tatouille mais comme je suis plutot un débutant dans ce genre de programmation j'ai un peu de mal. J'ai téléchargé les fichiers sources de libart mais :
- Je n'ai pu télécharger que la partie lgpl de libart
- je ne sais pas trop ou chercher vu que j'ai un peu de mal à voir à quoi sert chaque fonction
Qqun saurait il ou je peux trouver un descriptif des fonctions libart en français ?
 
Je ne sais pas ce que ça vaut pour mac... mais j'ai un vague souvenir que sous windows, la largeur (en pixel) de ton image devait formcemment être un multiple de 32 bits (question d'optimisation déplacements mémoire et calculs).

Du coup, si ton image fait 10 pixels de large sur 5 de haut et que tu es en 24 bits (un octet par composante), on n'aura pas 10x24=240 bits de large, mais bien 256 ! (car 240 n'est pas un multiple de 32, le prochain multiple de 32 qui suit 240 est 256).

Ainsi, pour la lecture d'une ligne, (par exemple, la première), tu as les 240 premiers bits qui sont interessants, suivront 26 bits de bruit, avant de passer au premier bit de la seconde ligne. (difficile à expliquer sans un petit schéma :rateau:).

Bien entendu, les images 'truecolor' en 32 bits ne souffrent pas de ce soucis...
 
Thx bcp pour ta réponse.
Je crois que je vois ce que tu veux dire... Il faudra que je tente de creuser cette question...
Je te tiens au courant de mes conclusions.
 
Suite à ta réponse j'ai décidé de totalement remanier mon programme en utilisant les méthodes getPixel:atX:y: et setPixel:atX:y: . À présent tout marche parfaitement ce qui confirme selon moi le fait que le problème n'était pas du à la méthode de calcul du pixel moyen mais bien à l'organisation globale des données...
Bon quoi qu'il en soit ca marche. Je tacherais de me renseigner plus avant lorsque j'aurais plus le temps.
 
Juste un truc: pour transformer en niveaux de gris, tu fais (rouge + vert + bleu) / 3.
Hors, sache que l'oeil per&#231;oit mieux le vert, ensuite le rouge, puis le vert.

En vid&#233;o, on fait d'habitude la moyenne ainsi:
gris = 0,3 rouge + 0,59 vert + 0,11 bleu

Mais peut-&#234;tre que &#231;a n'a pas d'int&#233;r&#234;t dans ton application.

Ensuite, comme te l'&#233;cris GrandGibus, pour des raisons d'alignement, il y a des octets inutilis&#233;s &#224; la fin de chaque ligne. On appelle &#231;a les Row Bytes. Potasse la doc de NSBitmapRep pour plus d'infos.

P.S.: Le 48 bits/pixel est tr&#232;s courant pour les images issues de scanners.
 
J'ai fait quelques tests avec ta technique Céroce et c'est vrai que le rendu est un un peu meilleur pour les images bien colorées mais il a aussi tendance à uniformiser un peu l'intensité pour certaines images aussi j'ai l'impression...

En tout cas merci beaucoup pour toutes vos réponses.
 
salut je n'ai pas eu le temps de r&#233;pondre ces jours -ci je voulais que tu regardes
gnu libart et la doc de vincent ou les rowBytes sont expliqu&#233;s

il faut que la matrice soit " uniforme " en effet pour une transpose ...

http://developer.apple.com/document...tual/vImage/Chapter3/chapter_3_section_6.html

http://developer.apple.com/samplecode/ColorMatching/listing22.html
http://developer.apple.com/samplecode/CocoaVideoFrameToNSImage/listing5.html

le traitement d'image est un peu plus complexe par exemple
il faut reconnaitre un "profil g&#233;n&#233;rale" de l'image
et suivant ce profil tu n'appliqueras pas exactement le m^me traitement
la conversion en niveau de gris est un peu plus complexe qu'une moyenne
les images te paraitront fade tu peux chercher dans the Gimp API les traitements/algos
d'images sont bien expliqu&#233;s et document&#233;s

Bloc de code:
- (NSData *)representationUsingType:(NSBitmapImageFileType)storageType properties:(NSDictionary *)properties.

NSBitmapImageRep *bitmap = 
[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil 
pixelsWide:width 
pixelsHigh:height 
bitsPerSample:8 
samplesPerPixel:1              
hasAlpha:NO 
isPlanar:NO 
colorSpaceName:NSCalibratedWhiteColorSpace 
bytesPerRow:width 
bitsPerPixel:8
] ;