Pytanie OpenCV - zamknięcie okna wyświetlania obrazu


Robię projekt do przeszukiwania bazy obrazów, a kiedy znajdę wyniki w jakimś zapytaniu - 5 obrazów bazy danych, chciałbym wyświetlić wyniki wizualnie. Nie przechowuję wszystkich obrazów w pamięci, więc najpierw załadowałem obraz, aby go wyświetlić.

Miałem coś prostego na myśli, w pseudokod:

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

Oto fragment mojego kodu C++ za pomocą OpenCV w tym celu:

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

The images tablica używana tutaj nie występuje w moim kodzie, ale ze względu na to pytanie zawiera nazwy plików obrazów odnoszących się do bieżącego punktu uruchamiania programu, jeśli jest name członek. Przechowuję nazwy obrazów nieco inaczej w moim projekcie.

Powyższy kod prawie działa: Potrafię iterować po 4/5 obrazach OK, ale kiedy wyświetla się ostatni obraz i naciśnięty jest klawisz, obraz robi się szary i nie mogę zamknąć okna obrazka, powodując awarię reszty aplikacji.

Mój pierwszy pomysł polegał na tym, że ze względu na optymalizację kompilacji cvReleaseImage zwalnia obraz wcześniej cvDestroyWindow jest skończony, a to w jakiś sposób sprawia, że ​​zamarza. Ale próbowałem dodać trochę czasu oczekiwania (stąd skomentowane sleep(1) linia mojego kodu) i to nie pomogło.

Wywołuję tę funkcję wyświetlania z mojej aplikacji konsolowej, a gdy obraz się zawiesza, sterowanie wraca do mojej aplikacji i mogę nadal z niej korzystać (ale okno obrazu jest nadal zamrożone w tle).

Czy możesz dać mi jakieś sugestie, jak to naprawić?

EDYTOWAĆ

Rozmawiałem z kilkoma osobami zajmującymi się wizją komputerową i OpenCV regularnie, od zadawania pytań i wciąż bez pomysłów.

Znalazłem również podobne pytanie stackoverflow, ale wciąż nie ma akceptowanej odpowiedzi. Googleing po prostu podaje podobne pytania, ale bez odpowiedzi.

Wszelkie pomysły na to, co wypróbować (nawet jeśli nie są kompletnym rozwiązaniem) są bardzo doceniane.


18
2018-01-12 21:53


pochodzenie


co możesz nam powiedzieć o tablicy obrazów? czy możesz opublikować swój kod tworzenia i przypisania? BTW oznaczyłeś to jako c ++, więc dlaczego używasz starego stylu C opencv? - Boaz
Styl OpenCv był spowodowany tym, że musiałem użyć biblioteki C, która używa OpenCV z moim kodem, a niektóre dane są w strukturach danych OpenCV w stylu C. Jest to w rzeczywistości fragment znacznie większego kodu, a ja uzyskuję dostęp do nazw moich obrazów w bardzo różny sposób. Ze względu na to pytanie, tablica images zawiera ścieżki do pliku w nazwie .name (zaktualizuję pytanie) - penelope
Próbowałeś cvDestroyAllWindows()? Jeśli tak, to nie działa, to jest gdzieś dziwny błąd w twoim kodzie (może nie w powyższym) lub w OpenCv. Jeśli działa, jest gdzieś prosty błąd. - Florian
czy możesz podać przykładowy kod, przykładowe obrazy i przykładowe nazwy obrazów, aby odtworzyć zaobserwowane zachowanie? - moooeeeep


Odpowiedzi:


Do celów testowych poniższa aplikacja robi dokładnie to, co napisałeś w pytaniu: ładuje 7 obrazów za pomocą wiersza poleceń, jeden po drugim, i tworzy nowe okno dla każdego wyświetlanego obrazu.

Działa bezbłędnie z OpenCV 2.3.1 na 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;
}

18
2018-01-26 12:21





cvDestroyWindow() zazwyczaj tylko się zaczyna dość skomplikowana procedura niszczenia okna. Ta procedura wymaga interakcji (wymiany zdarzeń) między systemem okienkowym a aplikacją. Do czasu zakończenia tej procedury okno nie może zostać całkowicie zniszczone. To jest powód, dla którego widzisz częściowo zniszczone okno, podczas gdy twoja aplikacja wykonuje coś niezwiązanego z GUI.

Wymiana zdarzeń może odbywać się w sposób zależny od systemu. W systemie Windows oznacza to (bezpośrednio lub pośrednio) wywoływanie GetMessage lub MsgWaitFor* funkcje i przetwarzanie wyniku. W przypadku Uniksa oznacza to (bezpośrednio lub pośrednio) wywoływanie XNextEvent i przetwarzanie wyniku.

OpenCV pozwala na wymianę tego zdarzenia w sposób niezależny od systemu. Istnieją dwie udokumentowane metody, aby to zrobić. Pierwszy to cvWaitKey()(Zadzwoń cvWaitKey(1) po zamknięciu ostatniego zdjęcia). Drugim jest zadzwonić cvStartWindowThread() na początku programu, aby umożliwić OpenCV automatyczne aktualizowanie jego okien.

Tylko jedna z tych metod działała poprawnie na moim komputerze z Linuksem z libcv2.1: cvStartWindowThread().


Uaktualnij (fragment kodu za pomocą 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
2018-01-26 11:15



czy możesz pokazać fragment kodu, w jaki sposób użyłeś cvStartWindowThread ()? Mam taki sam problem i pierwsze rozwiązanie, które zaproponowałeś (umieszczenie cvWaitKey (1) po zniszczeniu okna) nie zadziałało. Wciąż pozostawiam puste okno bez wyświetlania w nim obrazu przez resztę czasu, w którym mój program ciągle działa! Thks z góry! - Matteo
@Matteo, dodałem fragment kodu. (Przetestowałem to jeszcze raz - nadal działa poprawnie). cvWaitKey(1) nie działa też dla mnie. - Evgeny Kluev
Bardzo dużo, to wygląda naprawdę prosto! Kolejne pytanie: jeśli używam funkcji do wyświetlania obrazu, czy sugerujesz tworzenie wątku w zasięgu globalnym lub lokalnym? Być może będę musiał wywołać tę funkcję wiele razy w głównej części. - Matteo
@Matteo, myślę, że nie ma znaczenia, gdzie tworzysz wątek. Jeśli utworzysz go w funkcji "display", po prostu upewnij się, że jest tworzony tylko raz, przy pierwszym wywołaniu funkcji. - Evgeny Kluev
Wszystko jest jasne i zadbane. Dużo za szybką odpowiedź i pomoc! Do widzenia; D - Matteo


Nie ma potrzeby niszczenia okna na każdej klatce, możesz po prostu wywołać cvShowImage () z tą samą nazwą okna i zastąpi bieżący obraz.

Musisz tylko wywołać okno zniszczenia przy wyłączaniu. Możesz użyć cvCreateWindow (), aby utworzyć okno przy uruchomieniu, ale zostanie ono utworzone automatycznie podczas pierwszego wywołania showWindow ().


2
2018-01-12 22:15



Cześć, za odpowiedź, ale tak naprawdę mam inną nazwę dla każdego okna. Problem nie polega na wyświetlaniu okien, to jest to cvDestroyWindow nie zamyka okien - dokładniej, ostatnie okno pozostaje otwarte. - penelope


W twoim kodzie nie widziałem żadnych połączeń cvNamedWindow() aby utworzyć dowolne z okien, których używasz do wyświetlania obrazów (i które niszczysz). Najpewniej powinieneś umieścić w pętli jedno z tych połączeń cvShowImage() (jak pokazał karlphillip w jego odpowiedź).

Jeśli utworzysz nazwane okna przed pętlą: czy upewniłeś się, że żadne z obrazów nie mają duplikatów? Aby upewnić się, że nie przypisujesz obrazu do zniszczonego okna i nie niszczysz okna, które zostało już zniszczone?

Pominie wszystkie połączenia cvDestroyWindow() i za pomocą jednego połączenia z cvDestroyAllWindows() zamiast tego pomóc w uniknięciu problemu?


1
2018-01-27 09:31





Czy sprawdziłeś, czy twoje 5. zdjęcie zostało poprawnie załadowane? A co z tym?

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

   // display it
}

Napotkany problem brzmi jak zerowy wskaźnik do cvShowImage ();


0
2018-01-26 19:55





Jeśli chcesz zamknąć wszystkie okna wyświetlania obrazu OpenCV posługiwać się: destroyAllWindows();


0
2018-02-14 23:51





Spróbuj użyć

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

-1
2018-01-12 22:13



czemu? Jak by mi to pomogło? A jeśli nie zwolnię poprzedniego obrazu przed załadowaniem nowego, czy nie spowoduje to, że wszystkie obrazy rzeczywiście zostaną w pamięci? - penelope


Uwielbiam openCV, ale nie jest to jedyny sposób, aby wyświetlić wynik wyszukiwania z zapytania testowego. Możesz napisać plik html z twojego kodu c ++ w katalogu głównym swojego folderu z obrazami i otworzyć go w przeglądarce.

Jeśli używasz serwera WWW, możesz napisać prostą listę plików i opublikować ją za pomocą prostego skryptu php lub podobnego.

Powstały kod html mógłby wyglądać następująco:

<!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>

Zaletą tego podejścia jest to, że może działać na serwerze bezgłowym.

Aby zamknąć okno wyświetlania obrazu spójrz na dokumentację dla opencv2.3: waitKey (szczególnie uwagi na temat wydarzeń), destroyWindow i ten przykład kodu na podstawie próbki image.cpp w 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;
}

-1
2018-01-31 23:39