2012-01-12 33 views
17

Sto facendo su un progetto per la ricerca attraverso un database di immagini, e quando trovo i risultati per alcune query - 5 immagini di database, vorrei visualizzare i risultati visivamente. Non conservo tutte le immagini in memoria, quindi devo caricare prima l'immagine per visualizzarla.OpenCV - chiusura della finestra di visualizzazione dell'immagine

ho avuto qualcosa di semplice in mente, in pseudocodice:

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

Ecco un frammento del mio codice in C++ usando OpenCV per questo scopo:

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

L'array images usata qui non lo fa come tale esiste nel mio codice, ma per il gusto della domanda, contiene i nomi file delle immagini relative al punto corrente del programma corrente se è membro name. Memorizzo i nomi delle immagini in modo leggermente diverso nel mio progetto.

Il codice di cui sopra quasi lavori: posso scorrere 4/5 immagini OK, ma quando viene visualizzata l'ultima immagine e si preme un tasto, l'immagine diventa grigio e non riesco a chiudere la finestra dell'immagine tecnico senza schiantarsi il resto della mia domanda.

La mia prima idea è stata che, a causa delle ottimizzazioni in fase di compilazione, cvReleaseImage rilascia l'immagine prima del cvDestroyWindow e che in qualche modo la blocchi. Ma, ho provato ad aggiungere un po 'di attesa (da qui la riga del mio codice commentata sleep(1)) e non ha aiutato.

Sto chiamando questa funzionalità di visualizzazione dalla mia applicazione console e quando l'immagine si blocca, il controllo ritorna alla mia applicazione e posso continuare ad usarlo (ma la finestra dell'immagine è ancora congelata sullo sfondo).

Potete darmi qualche suggerimento su come risolvere questo problema?

EDIT

Ho parlato con alcune persone che si occupano di computer vision e OpenCV su base regolare dal porre la domanda, ed ancora senza idee.

Ho anche trovato un simile stackoverflow question, ma non esiste ancora una risposta accettata. Googleing dà solo domande simili come risultato, ma nessuna risposta.

Tutte le idee su cosa provare (anche se non sono la soluzione completa) sono molto apprezzate.

+0

cosa ci puoi dire sull'array di immagini? puoi pubblicare il suo codice di creazione e assegnazione? BTW hai taggato questo come C++ quindi perché stai usando il vecchio stile C di opencv? – Boaz

+0

Lo stile C di OpenCv è dovuto alla necessità di utilizzare una libreria C che utilizza OpenCV con il mio codice e alcuni dati sono in strutture di dati OpenCV in stile C. Questo in realtà è uno snippet di codice molto più grande, e accedo molto diversamente ai nomi delle mie immagini. Per il gusto della domanda, l'array di immagini contiene percorsi di file per l'immagine nel membro .name (aggiornerò la domanda) – penelope

+0

Hai provato 'cvDestroyAllWindows()'? Se è così, e non funziona, c'è uno strano bug nel tuo codice da qualche parte (forse non nel precedente) o in OpenCv. Se funziona, c'è un piccolo bug da qualche parte. – Florian

risposta

15

A scopo di verifica, l'applicazione di seguito non esattamente quello che ha dichiarato nella domanda: carica 7 immagini attraverso il comando linea, uno per uno, e crea una nuova finestra per ogni immagine da visualizzare.

Funziona perfettamente con OpenCV 2.3.1 su 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; 
} 
-2

Provare a utilizzare

cvDestroyWindow("Match"); 
// sleep(1); 
cvReleaseImage(&img); // outside the for loop 
+0

perché? Come mi sarebbe d'aiuto? E, se non rilascio l'immagine precedente prima di caricarne una nuova, non farebbe in modo che tutte le immagini rimangano effettivamente nella memoria? – penelope

2

Non c'è bisogno di distruggere la finestra su ogni fotogramma, si può semplicemente chiamare cvShowImage() con lo stesso nome della finestra e andrà a sostituire l'immagine corrente.

È sufficiente chiamare la finestra di distruzione all'arresto. È possibile utilizzare cvCreateWindow() per creare la finestra all'avvio, ma verrà creata automaticamente alla prima chiamata showWindow().

+0

Hey, thnx per la risposta, ma in realtà ho un nome diverso per ogni finestra. Il problema non è con la visualizzazione delle finestre, è che 'cvDestroyWindow' non chiude le finestre - più precisamente, l'ultima finestra rimane aperta. – penelope

5

cvDestroyWindow() di solito inizia solo procedura piuttosto complicata di distruzione delle finestre. Questa procedura richiede alcune interazioni (scambio di eventi) tra il sistema di finestre e l'applicazione. Finché questa procedura non termina, la finestra non può essere completamente distrutta.Questo è il motivo per cui vedi una finestra parzialmente distrutta mentre l'applicazione esegue qualcosa che non è correlato alla GUI.

Lo scambio di eventi può essere eseguito in modo dipendente dal sistema. In Windows significa (direttamente o indirettamente) chiamare le funzioni GetMessage o MsgWaitFor* ed elaborare il risultato. Per Unix questo significa (direttamente o indirettamente) chiamare XNextEvent ed elaborare il risultato.

OpenCV consente di effettuare questo scambio di eventi in modo indipendente dal sistema. Ci sono due metodi documentati per farlo. Il primo è cvWaitKey() (basta chiamare cvWaitKey(1) dopo aver chiuso l'ultima immagine). Il secondo è chiamare cvStartWindowThread() all'inizio del programma per consentire ad OpenCV di aggiornare automaticamente le sue finestre.

Solo uno di questi metodi ha funzionato correttamente sulla mia macchina Linux con libcv2.1: cvStartWindowThread().


Update (frammento di codice con 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; 
} 
+0

puoi mostrare un frammento di codice di come hai usato cvStartWindowThread()? Sto affrontando lo stesso problema e la prima soluzione che hai proposto (mettendo cvWaitKey (1) dopo la distruzione della finestra) non ha funzionato. Continuo ad avere la finestra vuota aperta senza immagini visualizzate per tutto il resto del tempo in cui il mio programma continua a funzionare! In anticipo! – Matteo

+0

@Matteo, ho aggiunto lo snippet di codice. (Testato ancora una volta - funziona ancora correttamente). 'cvWaitKey (1)' non ha funzionato neanche per me. –

+0

Molto, sembra davvero semplice! Solo un'altra domanda: se utilizzo una funzione per visualizzare un'immagine, suggerisci di creare il thread in ambito globale o locale? Potrei aver bisogno di chiamare questa funzione molte volte nel principale. – Matteo

0

Hai provato che la quinta immagine è stata caricata correttamente? Che dire di questo?

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

    // display it 
} 

Il problema riscontrato si presenta come un puntatore nullo a cvShowImage();

1

Nel codice, non ho visto nessuna chiamata di cvNamedWindow() per creare una qualsiasi delle finestre utilizzate per mostrare le immagini (e che si distrugge). Probabilmente dovresti inserire una di quelle chiamate nel loop, prima di te cvShowImage() (come ha mostrato Karlphillip in his answer).

Se si crea la finestra con nome prima del ciclo: si è verificato che nessuna delle immagini abbia nomi duplicati? Per essere sicuro di non assegnare un'immagine a una finestra distrutta e assicurarti di non distruggere una finestra che è già stata distrutta?

Omettere tutte le chiamate di cvDestroyWindow() e utilizzare una singola chiamata di cvDestroyAllWindows() invece aiuta a evitare il problema?

-1

Amo openCV ma non è l'unico modo per visualizzare i risultati della ricerca dalla query di prova. Puoi scrivere un file html dal tuo codice C++ nella cartella radice della tua cartella di immagini e aprirlo in un browser.

Se si sta eseguendo un server Web, è possibile scrivere un semplice elenco di file e pubblicarlo con un semplice script php o simile.

Il codice HTML risultante sarebbe qualcosa di simile:

<!DOCTYPE html> 
<html> 
    <head> 
    <style>img {display:block}</style> 
    <meta http-equiv="refresh" content="5"> 
    </head> 
    <body> 
    <img src="subfolderA/img1.png" /> 
    <img src="subfolderB/img2.png" /> 
    <img src="subfolderC/img3.png" /> 
    <img src="subfolderD/img4.png" /> 
    <img src="subfolderE/img5.png" /> 
    </body> 
</html> 

Su vantaggio di questo approccio è che può essere eseguito su un server senza testa.

Per la chiusura della finestra di visualizzazione delle immagini sguardo alla documentazione per opencv2.3: waitKey (nota in particolare sugli eventi), destroyWindow e questo codice di esempio sulla base del campione image.cpp in openCV2.3:

#include "cv.h" // include standard OpenCV headers, same as before 
#include "highgui.h" 
#include <stdio.h> 
#include <iostream> 

using namespace cv; // all the new API is put into "cv" namespace. Export its content 
using namespace std; 

void help(){ 
    cout << 
    "\nThis program shows how to use cv::Mat and IplImages converting back and forth.\n" 
    "Call:\n" 
    "./image img1.png img2.png img3.png img4.png img5.png\n" << endl; 
} 

int main(int argc, char** argv){ 
    help(); 
    namedWindow("Peephole", CV_WINDOW_AUTOSIZE); 
    int i=0; 
    while ((argc-1) > i){ 
    i++; 
    const char* imagename = argv[i]; 
    Ptr<IplImage> iplimg = cvLoadImage(imagename); // Ptr<T> is safe ref-conting pointer    class 
    if(iplimg.empty()){ 
     fprintf(stderr, "Can not load image %s\n", imagename); 
     return -1; 
    } 
    Mat img(iplimg); // cv::Mat replaces the CvMat and IplImage, but it's easy to convert 
    // between the old and the new data structures (by default, only the header 

    // is converted, while the data is shared) 

    if(!img.data) // check if the image has been loaded properly 
     return -1; 
    // it's easy to pass the new matrices to the functions that only work with IplImage or CvMat: 
    // step 1) - convert the headers, data will not be copied 
    // this is counterpart for cvNamedWindow 
    imshow("Peephole", img); 
    waitKey(); 
    } 
    destroyAllWindows(); 
    while (1) { 
    waitKey(10); 
    } 
    // all the memory will automatically be released by Vector<>, Mat and Ptr<> destructors. 
    return 0; 
} 
0

Se si desidera chiudere tutte le finestre di visualizzazione delle immagini OpenCV utilizzare: destroyAllWindows();

Problemi correlati