NSImage et Tiger...

MalaMala Membre, Modérateur
15:26 modifié dans API AppKit #1
Hello, tout le monde. Bon après une longue abscence, je me remets un peu dans mes projets d'astronomie. :fouf):

Je viens de migrer sous Tiger et horreur mes softs de traitement et d'acquistion ne marchent plus sous 10.4!!! B) Par exemple, toutes les images que je gère dans SLDriver ( http://www.poisson-lune.com/pages/astro/pages_anglais/SLDriver.html ) sont complètement décalées sous Tiger...
SLDriver_bug_Tiger.jpg
La même image chargées sous 10.3.9 s'affiche parfaitement. Les informations semblent bien traitées mais c'est l'affichage (transfert de ma matrice de travail vers une NSImage) qui semble merder.

J'ai bien regardé du côté de la création de mon NSBitmapImageRep...

    // Create the RVB representation
    ImageRep = [[NSBitmapImageRep alloc]
                initWithBitmapDataPlanes:NULL
                pixelsWide:ImageCPLUSPLUS[ImgIndex]->RetournerLargeur()
                pixelsHigh:ImageCPLUSPLUS[ImgIndex]->RetournerHauteur()
                bitsPerSample:8
                samplesPerPixel:3
                hasAlpha:NO
                isPlanar:NO
                colorSpaceName:NSCalibratedRGBColorSpace
                bytesPerRow:3*ImageCPLUSPLUS[ImgIndex]->RetournerLargeur()
                bitsPerPixel:3*8];

Mais je vois pas trop ce qui cloche. j'alloue bien une matrice avec 3x8bits par pixel en non planair et sans couche de transparence.

Si quelqu'un a une piste, je suis preneur. o:)

Réponses

  • Eddy58Eddy58 Membre
    novembre 2005 modifié #2
    Ton NSBitmapImageRep est peut-être correct, le problème se situe peut-être aprés. Comment l'exploites-tu ensuite pour l'afficher ? ???
    Est-ce que tu as enregistré ta représentation dans un fichier pour la regarder ailleurs et vérifier que le problème vient bien d'elle ?
    [tt]
    [[ImageRep TIFFRepresentation] writeToFile:@MonFichier atomically:YES];
    [/tt]
  • MalaMala Membre, Modérateur
    15:26 modifié #3
    Et bien écoute, j'ai un peu décortiqué le truc pous faire un exemple simple car mon source d'origine fait appel à  des routines C++ qui n'apportent pas grand chose à  la compréhension du problème.

    Voici donc un petit projet sous Xcode 2.1 avec son source et qui illustre bien le phénomène...
    http://www.poisson-lune.com/downloads/tmp/test.zip

    C'est juste une petite routine qui prend en entrée une image (pour les tests: jpeg, 24 bits, sans transparence) puis la réduit à  50% de sa taille d'origine histoire de faire une manip sur la matrice d'entrée.

    Voici le code de la routine

    - (NSImage*)halfImage:(NSImage*)img
    {
        // Taille d'origine
        NSSize dim = On recupère la matrice de l'image d'origine    NSBitmapImageRep *BmpImageRep = [NSBitmapImageRep imageRepWithData:Enfin, on vient remplir la matrice d'arrivée            int pos = width2* Y + X;            destData[pos].r = valR;            destData[pos].g = valG;            destData[pos].b = valB;        }    }            return destImage;}



    Pour mon projet de test, dans l'interface on a 2 boutons. Le premier charge une image d'un iMac G5 en 864x540 en la réduisant à  50%. Le deuxième bouton charge une image d'un amas globulaire M13 en 3360x2245 et fait de même.

    Sous Panthère, aucun problème. Sous tiger, pour l'iMac c'est Ok mais pour M13 badaboum...  ???

    Je commence à  me demander s'il n'y aurait pas un bug  lié à  la taille des images sous Tiger.  :crackboom:-
  • AliGatorAliGator Membre, Modérateur
    15:26 modifié #4
    Attends tu fais tout ça juste pour réduire la hauteur de ton image par 2 ??!?

    Et pourquoi ne pas utiliser :[tt][sourceImage drawInRect: NSMakeRect(0, 0, resizeWidth, resizeHeight) fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height) operation: NSCompositeSourceOver fraction: 1.0];[/tt] Hein ?

    Il parait que tu peux même utiliser [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolation{None, Low, High}] pour choisir le type d'interpolation utilisée lors de la réduction ;)

    Parce que là  en plus ta méthode elle risque de faire des trucs bizarres sur les bords de ton image... et puis il y a des trucs tout faits, pourquoi s'embêter ?

    Allez, un exemple trouvé à  chaud sur google :ici
  • MalaMala Membre, Modérateur
    15:26 modifié #5

    Attends tu fais tout ça juste pour réduire la hauteur de ton image par 2 ??!?

    Et pourquoi ne pas utiliser :[sourceImage drawInRect: NSMakeRect(0, 0, resizeWidth, resizeHeight) fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height) operation: NSCompositeSourceOver fraction: 1.0]; Hein ?

    Merci AliGator mais c'est juste à  titre d'exemple pour illustrer le problème. ;)


    et puis il y a des trucs tout faits, pourquoi s'embêter ?

    OS X ne gère que du RVB. J'utilise notamment des images encodées en LRVB 40bits dans mes programmes.

    De plus, j'ai constaté de nombreux trucs très casse bonbon en faisant confiance aux routines Obj-C pour la manipulation d'images. Du genre une image en RVB 48bits est directement transformée en RVB 24 bits sans te demander ton avis dans certaines routines (je pense par exemple à  un simple setImage dans une NSImageView).
  • MalaMala Membre, Modérateur
    15:26 modifié #6
    Les choses se précisent. Sous Tiger une image en 2048x2048 passe sans problème mais une image en 2050x2050 ça merdouille.

    Il me semble avoir lu un truc sur Core Image où la taille max des images était de 2048 pixels de côté. Si c'est lié, bonjour la régression par rapport à  Panthère.  :-\\
  • MalaMala Membre, Modérateur
    15:26 modifié #8
    Bingo!!! 

    Merci pour la piste c'est exactement ça. Donc, depuis Tiger lorsqu'on initialise le bytesPerRow de sa NSBitmapImageRep on a absolument pas la garantie du résultat...  ???
    Avec mon image de 2050 pixels de côté, je me retrouve avec 2069 pixels par ligne. :'( En dessous de 2048, le problème ne semble pas se poser.
    Apparement, le fait de ne plus avoir systématiquement les lignes d'octets alignés au niveau mémoire permet d'accélérer l'affichage.

    A côté de ça, j'ai découvert une autre "régression" sous Tiger. Avant sous Panthère il était possible de récupérer les infos d'entête d'un fichier TIF (largeur de l'image, hauteur, nb bits par pixel, etc) avec un simple "initWithContentsOfFile" qui ne s'encombrait pas à  charger tout le fichier. Cela allait donc très vite. Maintenant, sous Tiger mon même programme met plusieurs minutes pour récupérer ces infos (pour une centaine d'images TIF 48 bits de 47 Mo).

    D'un côté on optimise et de l'autre on poubellise ce qui est bien. Merci Appoule...  :o
  • Eddy58Eddy58 Membre
    15:26 modifié #9
    Je ne sais pas où tu en es là -dessus, mais en faisant un essai avec une image de 4096 pixels de large, ça passe bien. Même si cela implique un traitement supplémentaire, tu peux toujours caler tes images pour qu'elles fassent 4096 pixels de large (en ajoutant un fond uni sur la droite de l'image) avant de leur appliquer ton traitement, puis une fois celui-ci terminé tu fais le nécessaire pour récupèrer l'image à  sa taille d'origine. :)
  • MalaMala Membre, Modérateur
    novembre 2005 modifié #10
    Et bien écoute, je me suis contenté de modifier ma boucle en tenant compte du "bytesPerRow".

    Par contre, il va falloir que je reporte cette modifs dans mes applis.

    Mais je ne comprends tout de même pas ce qui ne va pas à  la base. Quand je lis:

    This is a change to the semantics of -initWithBitmapDataPlanes:...
    It is now possible for the bitmap to not have packed bytesPerRow in order to speed up drawing (something that was always possible, but just didn't happen). Your code assumes that the bitmap data is packed and so as you fill up the bitmap, you start getting skewed rows. Either pass in an explicit bytesPerRow value instead of zero to - initWithBitmapDataPlanes:... to explicitly indicate packed values or change your loop to be something like:

        unsigned char* row = destbuffer;
        int i, j;
        for (i = 0; i < height; i++, row += [imageRep bytesPerRow]) {
            unsigned char* p = row;
            for (j = 0; j < width; j++) {
            *p++ = c;
            *p++ = c;
            *p++ = c;
            c++;
            }
        }

    Note that in Panther compiled apps, we should still alloc packed bytesPerRow for compatibility. I assume you recompiled on Tiger.

    Andrew Platzer
    Application Frameworks
    Apple Computer, Inc.


    Moi cela me laisse supposer que si on met le "bytesPerRow" à  0, on a pas la garantie que les lignes de pixels seront alignées en mémoire. Et si on l'initialise par contre on est tranquile. Hors dans les faits je constate que ce n'est en fait pas le cas sur de grosses images puisque dans mon exemple je l'initialisais bien.  >:(

    Donc au final, pour moi ça m'a tout l'air d'un bug ou alors il y a erreur d'interprétation. En attendant, toujours est-il qu'au final, j'initialise le bytesPerRow mais que je le récupère tout de même dans mes boucles afin d'être sûr d'avoir la bonne largeur en octets de mon image.

    Ce qui me donne comme modif (indiquée en italique gras) sur mon exemple du départ:

            ../..
            // Pointeur sur la matrice de l'image source
            rgb_8 *srcData  = (rgb_8 *)[BmpImageRep bitmapData];
           
            // Pointeur sur la matrice de destination
            rgb_8 *destData = (rgb_8 *)[ImageRep bitmapData];
           
            int srcBytesPerRow = [BmpImageRep bytesPerRow];
           
            // On boucle sur la matrice de pixels
            // On va parcourir les pixels 4 par 4 afin d'en
            // faire la moyenne pour creer une matrice 2 fois
            // plus petite à  l'arrivée.
            int x,y;
            for(x=0; x<width1-1; x+=2)
            {
                int X = x/2;
               
                for(y=0; y<height1-1; y+=2)
                {
                    int Y = y/2;
                   
                    // Position du pixel courant ainsi que celle de ces trois voisins
                    int pos1 = srcBytesPerRow* (y  )/3 + (x  );
                    int pos2 = srcBytesPerRow* (y  )/3 + (x+1);
                    int pos3 = srcBytesPerRow* (y+1)/3 + (x  );
                    int pos4 = srcBytesPerRow* (y+1)/3 + (x+1);
                   
                   
                    // Moyenne des 4 pixels rouges
                    int valR = ( (int)srcData[pos1].r +
                                 (int)srcData[pos2].r +
                                 (int)srcData[pos3].r +
                                 (int)srcData[pos4].r )/4;
                   
                    // Moyenne des 4 pixels verts
                    int valG = ( (int)srcData[pos1].g +
                                 (int)srcData[pos2].g +
                                 (int)srcData[pos3].g +
                                 (int)srcData[pos4].g )/4;
                   
                    // Moyenne des 4 pixels bleus
                    int valB = ( (int)srcData[pos1].b +
                                 (int)srcData[pos2].b +
                                 (int)srcData[pos3].b +
                                 (int)srcData[pos4].b )/4;
                   
                    // Enfin, on vient remplir la matrice d'arrivée
                    int pos = [ImageRep bytesPerRow]* Y/3 + X;
                    destData[pos].r = valR;
                    destData[pos].g = valG;
                    destData[pos].b = valB;
                }
            }
    ../..
Connectez-vous ou Inscrivez-vous pour répondre.