2012-01-12 2 views
17

Я занимаюсь проектом для поиска через базу данных изображений, и когда я нахожу результаты для некоторых запросов - 5 изображений базы данных, я хотел бы визуально отображать результаты. Я не сохраняю все изображения в памяти, поэтому сначала загружаю изображение, чтобы отобразить его.OpenCV - закрытие окна отображения изображения

У меня было что-то простое в виду, в псевдокоде:

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

Вот фрагмент моего кода в C++ используя OpenCV для этой цели:

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

images массив используется здесь делает не так такие существуют в моем коде, но, ради вопроса, он содержит Имена Файлов изображений относительно текущей текущей рабочей точки программы, если ее член name. Я храню имена изображений немного по-другому в своем проекте.

Код почти Работы: Я могу выполнять итерацию через 4/5 изображений ОК, но когда отображается последнее изображение и нажата клавиша, изображение становится серым, и я не могу закрыть окно изображения, моей заявки.

Моя первая идея заключалась в том, что из-за оптимизации времени компиляции cvReleaseImage выпускает изображение до cvDestroyWindow, и это как-то заставляет его замораживать. Но я попытался добавить некоторое время ожидания (отсюда прокомментировал строку sleep(1) моего кода), и это не помогло.

Я вызываю эту функцию отображения из своего консольного приложения, и когда изображение зависает, элемент управления возвращается обратно в мое приложение, и я могу продолжать использовать его (но окно изображения все еще заморожено в фоновом режиме).

Можете ли вы дать мне какие-либо предложения о том, как исправить это?

EDIT

Я разговаривал с некоторыми людьми, занимающихся компьютерным зрением и OpenCV на регулярной основе с задавая вопрос, и до сих пор нет идей.

Я также нашел аналогичный stackoverflow question, но до сих пор нет принятого ответа. В результате Google просто дает похожие вопросы, но ответов нет.

Любые идеи о том, что попробовать (даже если они не являются полным решением), очень ценятся.

+0

Что вы можете рассказать нам о массиве изображений? можете ли вы опубликовать свой код создания и назначения? Кстати, вы отметили это как C++, так почему вы используете старый стиль C opencv? – Boaz

+0

Стиль C OpenCv - это потому, что мне пришлось использовать библиотеку C, которая использует OpenCV с моим кодом, а некоторые данные находятся в структурах данных OpenCV в стиле C. Это si фактически фрагмент гораздо большего кода, и я обращаюсь к именам своих изображений по-разному. Ради вопроса, массив изображений содержит пути к картинке в элементе .name (я обновлю вопрос) – penelope

+0

Вы пытались 'cvDestroyAllWindows()'? Если это так, и это не сработает, то есть странная ошибка в вашем коде где-нибудь (может быть, не в выше) или в OpenCv. Если он работает, там где-то есть простая ошибка. – Florian

ответ

15

Для целей тестирования, то приложение ниже делает именно то, что вы заявили в вопросе: он загружает 7 изображений с помощью команды line, один за другим и создает новое окно для отображения каждого изображения.

Он работает безупречно с OpenCV 2.3.1 на 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

Попробуйте использовать

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

почему? Как это мне поможет? И, если я не выпущу предыдущее изображение перед загрузкой нового, не приведет ли это к тому, что все изображения фактически останутся в памяти? – penelope

2

Там нет необходимости, чтобы уничтожить окно на каждом кадре, вы можете просто вызвать cvShowImage() с тем же именем окна и заменит текущее изображение.

Вам нужно только вызвать окно уничтожения при выключении. Вы можете использовать cvCreateWindow() для создания окна при запуске, но он будет создан автоматически при первом вызове showWindow().

+0

Эй, thnx для ответа, но у меня на самом деле есть другое имя для каждого окна. Проблема заключается не в отображении окон, а в том, что 'cvDestroyWindow' не закрывает окна - точнее, последнее окно остается открытым. – penelope

5

cvDestroyWindow() обычно только начинается довольно сложная процедура разрушения окна. Эта процедура требует некоторого взаимодействия (обмена событиями) между системой окон и вашим приложением. Пока эта процедура не завершится, окно не может быть полностью уничтожено.Именно по этой причине вы видите частично разрушенное окно, в то время как ваше приложение выполняет что-то, не связанное с графическим интерфейсом.

Обмен событиями может осуществляться в зависимости от системы. В Windows это означает (прямо или косвенно) вызов функций GetMessage или MsgWaitFor* и обработку результата. Для Unixs это означает (прямо или косвенно) вызов XNextEvent и обработку результата.

OpenCV позволяет осуществлять обмен событиями независимо от системы. Для этого есть два документальных метода. Первый - cvWaitKey() (просто позвоните cvWaitKey(1) после того, как вы закроете последнее изображение). Во-вторых, нужно позвонить cvStartWindowThread() в начале вашей программы, чтобы OpenCV автоматически обновлял свои окна.

Только один из этих методов корректно работал на моем ящике Linux с libcv2.1: cvStartWindowThread().


Update (фрагмент кода с 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

вы можете показать фрагмент кода, как вы использовали cvStartWindowThread()? Я столкнулся с той же проблемой, и первое предложенное вами решение (установка cvWaitKey (1) после разрушения окна) не сработала. Я продолжаю открывать пустое окно без изображения, отображаемого на нем, в течение оставшейся части времени моя программа продолжает работать! Thks заранее! – Matteo

+0

@Matteo, я добавил фрагмент кода. (Протестировано еще раз - все еще работает правильно). 'cvWaitKey (1)' тоже не помог мне. –

+0

Thx много, это выглядит очень просто! Еще один вопрос: если я использую функцию для отображения изображения, вы предлагаете создать поток в глобальной или локальной области? Возможно, мне придется многократно вызывать эту функцию в основном. – Matteo

0

Вы проверили, что ваше пятое изображение правильно загружено? Как насчет этого?

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

    // display it 
} 

Проблема, с которой вы сталкиваетесь, звучит как нулевой указатель на cvShowImage();

1

В вашем коде я не видел звонков cvNamedWindow(), чтобы создать любое из окон, которые вы используете для показа изображений (и которые вы уничтожаете). Вероятно, вы должны поместить один из этих вызовов в цикл, прежде чем вы получите cvShowImage() (как показывает karlphillip в his answer).

Если вы создали именованные окна перед циклом: вы удостоверились, что ни одно из изображений не имеет повторяющихся имен? Чтобы убедиться, что вы не назначаете изображение поврежденному окну и убедитесь, что вы не уничтожаете окно, которое уже было уничтожено?

Опуская все звонки cvDestroyWindow() и используя один звонок cvDestroyAllWindows(), вместо этого поможет избежать вашей проблемы?

-1

Я люблю openCV, но это не единственный способ отобразить результат поиска из вашего тестового запроса. Вы можете написать html-файл из вашего C++-кода в корневой папке вашего изображения и открыть его в браузере.

Если вы используете веб-сервер, вы можете написать простой список файлов и опубликовать его с помощью простого php-скрипта или аналогичного.

Полученный HTML-код будет что-то вроде:

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

На пользу этого подхода заключается в том, что она может работать на обезглавленный сервере.

Для закрытия окна вида отображения изображения в документации для opencv2.3: waitKey (особенно заметок о событиях), destroyWindow и этот код, например на основе образца image.cpp в 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

Если вы хотите закрыть все окна отображения изображений OpenCV использовать: destroyAllWindows();

Смежные вопросы