21

I am doing on a project for searching through an image database, and when I find the results to some query - 5 database images, I would like to display the results visually. I do not keep all the images in memory, so I have do load the image first in order to display it.

I had something simple in mind, in pseudocode:

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

Here's a snippet of my code in C++ using OpenCV for this purpose:

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 array used here does not as such exist in my code, but for the sake of the question, it contains the File Names of the images relative to the current program running point if its name member. I store the image names a bit differently in my project.

The code above almost works: I can iterate through 4/5 images OK, but when last image is displayed and a key is pressed, the image goes gray and I can not close the image window withouth crashing the rest of my application.

My first idea was that becouse of compile-time optimizations, cvReleaseImage releases the image before cvDestroyWindow is finished, and that somehow makes it freeze. But, I've tried adding some waiting time (hence the commented out sleep(1) line of my code) and it didn't help.

I am calling this display functionality from my console application, and when the image freezes, the control returns back to my application and I can keep using it (but the image window is still frozen in the background).

Can you give me any suggestions on how to fix this?

EDIT

I have talked to some people dealing with computer vision and OpenCV on a regular basis since asking the question, and still no ideas.

I have also found a similar stackoverflow question, but there is still no accepted answer. Googleing just gives similar questions as a result, but no answers.

Any ideas on what to try (even if they are not the complete solution) are very much appreciated.

Community
  • 1
  • 1
penelope
  • 8,251
  • 8
  • 45
  • 87
  • what can you tell us about the images array? can you post its creation and assignment code? BTW you tagged this as c++ so why are you using the old C style of opencv? – Boaz Jan 13 '12 at 07:11
  • C style of OpenCv is becouse I had to use an C library which uses OpenCV with my code, and some of the data is in C-style OpenCV data structures. This si actually a snippet of much bigger code, and I access the names of my images much differently. For the sake of the question, images array contains filepaths to the picture in the .name member (I'll update the question) – penelope Jan 13 '12 at 08:04
  • Did you try `cvDestroyAllWindows()`? If so, and it doesn't work then there is a strange bug either in your code somewhere (maybe not in the above) or in OpenCv. If it works there's a simple bug somewhere. – znkr Jan 25 '12 at 19:45
  • can you provide sample code, sample images, and sample image names to reproduce the observed behavior? – moooeeeep Jan 27 '12 at 09:34

9 Answers9

22

For testing purposes, the application below does exactly what you stated in the question: it loads 7 images through the command line, one by one, and creates a new window for each image to be display.

It works flawlessly with OpenCV 2.3.1 on 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;
}
karlphillip
  • 92,053
  • 36
  • 243
  • 426
5

cvDestroyWindow() usually only starts pretty complicated procedure of window destruction. This procedure requires some interaction (event exchange) between windowing system and your application. Until this procedure finishes, the window cannot be completely destroyed. That is the reason why you see partially destroyed window while your application performs something not related to GUI.

Event exchange may be performed in system-dependent manner. In Windows this means (directly or indirectly) calling GetMessage or MsgWaitFor* functions and processing the result. For Unixes this means (directly or indirectly) calling XNextEvent and processing the result.

OpenCV allows to do this event exchange in system-independent way. There are two documented methods to do this. First one is cvWaitKey() (just call cvWaitKey(1) after you close the last image). Second one is to call cvStartWindowThread() at the start of your program to allow OpenCV updating its windows automatically.

Only one of these methods worked properly on my Linux box with libcv2.1: cvStartWindowThread().


Update (code snippet with 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;
}
Evgeny Kluev
  • 24,287
  • 7
  • 55
  • 98
  • can you please show a code snippet of how you used cvStartWindowThread()? I'm facing the same problem and the first solution you proposed (putting cvWaitKey(1) after window destruction) hasn't worked. I continue to have the empty window open with no image displayed in it for all the rest of the time my program keeps running!Thks in advance! – Matteo Mar 03 '12 at 09:23
  • @Matteo, I've added the code snippet. (Tested it once more - still works correctly). `cvWaitKey(1)` didn't work for me either. – Evgeny Kluev Mar 03 '12 at 09:25
  • Thks a lot, it looks really simple!Just another question: if I use a function to display an image do you suggest creating the thread in global or local scope? I might need to call this function many times in the main. – Matteo Mar 03 '12 at 09:34
  • @Matteo, I think, it doesn't matter, where you create the thread. If you create it in the 'display' function, just make sure it is created only once, on the first function call. – Evgeny Kluev Mar 03 '12 at 09:44
  • It's all clear and neat. Thks a lot for your prompt reply and help!Bye ;D – Matteo Mar 03 '12 at 09:47
3

There is no need to destroy the window on each frame, you can simply call cvShowImage() with the same window name and it will replace the current image.

You only need to call destroy window at shutdown. You can use cvCreateWindow() to create the window at startup but it will be created automatically on the first showWindow() call.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • Hey, thnx for the answer, but I actually have a different name for each window. The problem is not with displaying the windows, it's that `cvDestroyWindow` does not close the windows - more precisely, the last window stays open. – penelope Jan 13 '12 at 06:58
1

In your code, I've seen no calls of cvNamedWindow() to create any of the windows you use to show images on (and that you destroy). You should probably put one of those calls into the loop, before you cvShowImage() (as karlphillip has shown in his answer).

If you create the named windows before the loop: Have you made sure that none of the images have duplicate names? To make sure you don't assign an image to a destroyed window, and to make sure you don't destroy a window that has already been destroyed?

Would omitting all calls of cvDestroyWindow() and using a single call of cvDestroyAllWindows() instead help to avoid your problem?

Community
  • 1
  • 1
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
0

If you want to close all OpenCV image display windows use: destroyAllWindows();

Samer
  • 1,923
  • 3
  • 34
  • 54
0

What I was looking for is a solution to check wether a key has been pressed or if the window has been closed. That way python doesn't stall:

"""Check if window closed or key pressed"""
import cv2 as cv

img = cv.imread('image.jpg')
cv.imshow("MyWindow", img)

while cv.getWindowProperty("MyWindow", cv.WND_PROP_VISIBLE) > 0:
    if cv.waitKey(1000) > 0:
        break
Alex
  • 36
  • 5
0

Did you test that your 5th image is correctly loaded? What about this?

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

   // display it
}

The problem you encounter sounds like a null pointer to cvShowImage();

Sam
  • 19,708
  • 4
  • 59
  • 82
0

I love openCV but it is not the only way to display the search result from your test query. You could write a html file from your c++-code in the root of your image folder and open it in a browser.

If you are running a webserver you could write a simple file list and publish it with a simple php-script or similar.

The resulting html-code would be something like:

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

On advantage of this approach is that it can run on a headless server.

For closing the image display window look at the documentation for opencv2.3: waitKey (especially notes about events), destroyWindow and this code example based on the image.cpp sample 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;
}
mfrellum
  • 169
  • 3
-1

Try using

cvDestroyWindow("Match");
// sleep(1);
cvReleaseImage(&img); // outside the for loop
sjngm
  • 12,423
  • 14
  • 84
  • 114
SB26
  • 225
  • 1
  • 6
  • 17
  • why? How would that help me? And, if I don't release the previous image before loading a new one, wouldn't that cause all the images to actually stay in the memory? – penelope Jan 13 '12 at 07:07