web-dev-qa-db-fra.com

OpenCV - fermeture de la fenêtre d'affichage d'image

Je fais un projet de recherche dans une base de données d'images, et lorsque je trouve les résultats dans des images de base de données query-5, j'aimerais afficher les résultats de manière visuelle. Je ne garde pas toutes les images en mémoire, je dois donc d'abord charger l'image pour l'afficher.

Je pensais à quelque chose de simple, en pseudocode:

for image 1..5
    load images
    display image in a window
    wait for any keypress
    close the window

Voici un extrait de mon code dans C++ en utilisant OpenCV à cette fin:

IplImage *img;

for (int i=0; i < 5; ++i){
    img = cvLoadImage(images[i].name.c_str(),1);
    cvShowImage(("Match" + images[i].name).c_str(), img);
    cvWaitKey(0);
    cvDestroyWindow(("Match" + images[i].name).c_str());
    // sleep(1);
    cvReleaseImage(&img);
}

Le tableau images utilisé ici n’existe pas en tant que tel dans mon code mais, dans l’intérêt de la question, il contient les noms de fichier des images relatives au point d’exécution du programme en cours si son membre name. Je stocke les noms d'images un peu différemment dans mon projet.

Le code ci-dessus presque _ fonctionne: je peux parcourir 4/5 images, mais, lorsque la dernière image est affichée et qu'une touche est enfoncée, l'image devient grise et je ne peux pas fermer la fenêtre d'image sans que le reste ne s'écrase de ma candidature.

Ma première idée était que, grâce aux optimisations à la compilation, cvReleaseImage libère l'image avant que cvDestroyWindow ne soit terminée et que, d'une manière ou d'une autre, elle se fige. Mais, j'ai essayé d'ajouter un peu de temps d'attente (d'où la ligne commentée sleep(1) de mon code) et cela n'a pas aidé.

J'appelle cette fonctionnalité d'affichage à partir de mon application console et lorsque l'image se fige, le contrôle revient à mon application et je peux continuer à l'utiliser (mais la fenêtre d'image est toujours figée en arrière-plan).

Pouvez-vous me donner des suggestions sur la façon de résoudre ce problème?

MODIFIER

Depuis que j'ai posé la question, j'ai parlé régulièrement à des personnes travaillant avec la vision par ordinateur et OpenCV, mais toujours sans idées.

J'ai également trouvé une question similaire - stackoverflow , mais il n'y a toujours pas de réponse acceptée. Le résultat est simplement que Google donne des questions similaires, mais pas de réponses.

Toutes les idées sur ce qu'il faut essayer (même si elles ne sont pas la solution complète) sont très appréciées. 

18
penelope

À des fins de test, l'application ci-dessous fait exactement ce que vous avez dit dans la question : elle charge 7 images par la ligne de commande, une par une, et crée une nouvelle fenêtre pour chaque image à afficher.

Cela fonctionne parfaitement avec OpenCV 2.3.1 sur Linux.

#include <cv.h>
#include <highgui.h>

#define NUM_IMGS 7

int main(int argc, char* argv[])
{
    if (argc < 8)
    {
        printf("Usage: %s <img1> <img2> <img3> <img4> <img5> <img6> <img7>\n", argv[0]);
        return -1;
    }

    // Array to store pointers for the images
    IplImage* images[NUM_IMGS] = { 0 };

    for (int i = 0; i < NUM_IMGS; i++)
    {
        // load image
        images[i] = cvLoadImage(argv[i+1], CV_LOAD_IMAGE_UNCHANGED);
        if (!images[i])
        {
            printf("!!! failed to load: %s\n", argv[i+1]);
            continue;
        }

        // display image in a window
        cvNamedWindow(argv[i+1], CV_WINDOW_AUTOSIZE); // creating a new window each time
        cvShowImage(argv[i+1], images[i]);

        // wait for keypress
        cvWaitKey(0);

        // close the window
        cvDestroyWindow(argv[i+1]);
        cvReleaseImage(&images[i]);
    }

    return 0;
}
19
karlphillip

cvDestroyWindow() habituellement ne commence que une procédure compliquée de destruction de la fenêtre. Cette procédure nécessite une interaction (échange d’événements) entre le système de fenêtrage et votre application. Tant que cette procédure n'est pas terminée, la fenêtre ne peut pas être complètement détruite. C'est la raison pour laquelle vous voyez une fenêtre partiellement détruite pendant que votre application exécute quelque chose qui n'est pas lié à l'interface graphique.

L'échange d'événements peut être effectué de manière dépendante du système. Sous Windows, cela signifie (directement ou indirectement) appeler les fonctions GetMessage ou MsgWaitFor* et traiter le résultat. Pour Unix, cela signifie (directement ou indirectement) appeler XNextEvent et traiter le résultat.

OpenCV permet d’échanger cet événement de manière indépendante du système. Il existe deux méthodes documentées pour le faire. Le premier est cvWaitKey() (appelez simplement cvWaitKey(1) après avoir fermé la dernière image). La deuxième consiste à appeler cvStartWindowThread() au début de votre programme pour permettre à OpenCV de mettre à jour automatiquement ses fenêtres.

Une seule de ces méthodes fonctionnait correctement sous Linux avec libcv2.1: cvStartWindowThread().


Mise à jour (extrait de code avec cvStartWindowThread ())

//gcc -std=c99 main.c -lcv -lcxcore -lhighgui
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <stdio.h>
#include <unistd.h>

#define NUM_IMGS 2

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        printf("Usage: %s <img1>\n", argv[0]);
        return -1;
    }

    cvStartWindowThread();

    // Array to store pointers for the images
    IplImage* images[NUM_IMGS] = { 0 };

    for (int i = 0; i < NUM_IMGS; i++)
    {
        // load image
        images[i] = cvLoadImage(argv[i+1], CV_LOAD_IMAGE_UNCHANGED);
        if (!images[i])
        {
            printf("!!! failed to load: %s\n", argv[i+1]);
            continue;
        }

        // display image in a window
        cvNamedWindow(argv[i+1], CV_WINDOW_AUTOSIZE); // creating a new window each time
        cvShowImage(argv[i+1], images[i]);

        // wait for keypress
        cvWaitKey(0);

        // close the window
        cvDestroyWindow(argv[i+1]);
        cvReleaseImage(&images[i]);
    }

    //    cvWaitKey(1);
    sleep(10);
    return 0;
}
5
Evgeny Kluev

Il n’est pas nécessaire de détruire la fenêtre sur chaque image, vous pouvez simplement appeler cvShowImage () avec le même nom de fenêtre et l’image actuelle sera remplacée. 

Il vous suffit d'appeler la fenêtre de destruction à l'arrêt. Vous pouvez utiliser cvCreateWindow () pour créer la fenêtre au démarrage, mais celle-ci sera automatiquement créée lors du premier appel à showWindow ().

2
Martin Beckett

Dans votre code, je n'ai vu aucun appel de cvNamedWindow() pour créer l'une des fenêtres que vous utilisez pour afficher des images (et que vous détruisez). Vous devriez probablement mettre l'un de ces appels dans la boucle, avant de cvShowImage() (comme l'a montré karlphillip dans sa réponse ).

Si vous créez les fenêtres nommées avant la boucle: Assurez-vous qu'aucune des images ne porte un nom en double? Pour vous assurer que vous n'attribuez pas d'image à une fenêtre détruite et pour ne pas détruire une fenêtre déjà détruite?

L'omission de tous les appels de cvDestroyWindow() et l'utilisation d'un seul appel de cvDestroyAllWindows() permettraient-elles d'éviter votre problème?

1
moooeeeep

Avez-vous testé que votre 5ème image est correctement chargée? Et ça?

for(...)
{
   if(!img)
       break;

   // display it
}

Le problème rencontré ressemble à un pointeur null sur cvShowImage ();

0
Sam

Si vous souhaitez fermer toutes les fenêtres d'affichage des images OpenCV, utilisez: destroyAllWindows();.

0
Samer