Подтвердить что ты не робот

OpenCV - закрытие окна отображения изображения

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

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

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) моего кода), и это не помогло.

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

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

ИЗМЕНИТЬ

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

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

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

4b9b3361

Ответ 1

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

Он работает безупречно с 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() обычно запускает только довольно сложную процедуру разрушения окна. Эта процедура требует некоторого взаимодействия (обмена событиями) между системой окон и вашим приложением. Пока эта процедура не завершится, окно не может быть полностью уничтожено. Вот почему вы видите частично разрушенное окно, в то время как ваше приложение выполняет что-то, не связанное с графическим интерфейсом.

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

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

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


Обновление (фрагмент кода с помощью 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;
}

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

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

   // display it
}

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

Ответ 6

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

Ответ 7

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

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

Полученный 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 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 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;
}

Ответ 8

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

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