2012-01-12 609 views
17

我做的一个项目,通过图像数据库检索,当我发现的结果,一些查询 - 5数据库图像,我想直观地显示结果。我不会将所有图像保存在内存中,因此我必须先加载图像才能显示图像。使用OpenCV用于此目的的OpenCV - 关闭图像显示窗口

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

这里是我的代码片段在C++

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的图像OK迭代,但是当最后显示的图像和一键被按下时,图像会灰色的,我不能关闭图像窗口withouth的崩溃休息我的应用程序。

我的第一个想法是,由于编译时优化,cvReleaseImagecvDestroyWindow完成之前释放图像,并以某种方式使其冻结。但是,我尝试添加一些等待时间(因此我的代码中已注释掉sleep(1)行),并没有帮助。

我从我的控制台应用程序调用此显示功能,当图像冻结,控制返回到我的申请,我可以继续使用它(但在图像窗口仍然在后台冻结)。

你能给我如何解决这个问题有什么建议?

编辑

,因为问的问题我交谈过的一些人与计算机视觉和OpenCV定期处理,仍然没有想法。

我也发现了类似的stackoverflow question,但仍然没有公认的答案。 Googleing只是提供了类似的问题,但没有答案。

什么尝试(即使它们不是完整的解决方案)的任何想法非常赞赏。

+0

你可以告诉我们关于图像数组?你可以发布它的创建和分配代码吗?顺便说一句,你把它标记为C++,那么你为什么要使用旧的C风格的opencv? – Boaz 2012-01-13 07:11:30

+0

C风格的OpenCv是因为我不得不使用C库与我的代码使用OpenCV和一些数据是C风格的OpenCV数据结构。这实际上是一个更大代码的片段,我以不同的方式访问我的图像的名称。为了这个问题,images数组包含文件路径到.name成员中的图片(我将更新问题) – penelope 2012-01-13 08:04:17

+0

您是否尝试过'cvDestroyAllWindows()'?如果是这样,并且它不起作用,那么代码中的某处(可能不在上面)或OpenCv中有一个奇怪的错误。如果它有效,那么就有一个简单的错误。 – Florian 2012-01-25 19:45:36

回答

15

出于测试目的,下面应用程序正是你的问题说:它通过命令加载7张图片一行一行,并为每个要显示的图像创建一个新窗口。

它在Linux上与OpenCV 2.3.1完美搭配。

#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 2012-01-13 07:07:20

2

没有必要摧毁每一帧窗口试试,你可以简单地用相同的窗口名称叫cvShowImage(),它会替换当前的图像。

你只需要调用在关机时销毁窗口。您可以使用cvCreateWindow()在启动时创建窗口,但它会在第一个showWindow()调用时自动创建。

+0

嘿,thnx的答案,但我实际上有不同的名称为每个窗口。问题不在于显示窗口,而是'cvDestroyWindow'不关闭窗口 - 更确切地说,最后一个窗口保持打开状态。 – penelope 2012-01-13 06:58:17

5

cvDestroyWindow()通常只开始窗口破坏的相当复杂的程序。此过程需要窗口系统和您的应用程序之间进行一些交互(事件交换)。在此过程完成之前,窗口不能完全销毁。这就是为什么当你的应用程序执行一些与GUI无关的事情时,你会看到部分被摧毁的窗口。

事件交换可以以系统相关的方式执行。在Windows中,这意味着(直接或间接)调用GetMessageMsgWaitFor*函数并处理结果。对于Unix,这意味着(直接或间接)调用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; 
} 
+0

你可以请你展示你如何使用cvStartWindowThread()的代码片段吗?我面临着同样的问题,你提出的第一个解决方案(在窗口销毁后放置cvWaitKey(1))没有奏效。在我的程序继续运行的其余时间内,我仍然打开空的窗口,没有任何图像显示!提前提示! – Matteo 2012-03-03 09:23:09

+0

@Matteo,我添加了代码片段。 (再次测试 - 仍然正常工作)。 'cvWaitKey(1)'也不适合我。 – 2012-03-03 09:25:27

+0

很多,它看起来很简单!还有一个问题:如果我使用函数来显示图像,您是否建议在全局或本地范围内创建线程?我可能需要多次调用这个函数。 – Matteo 2012-03-03 09:34:19

0

您是否测试过您的第5张图片是否正确加载?那这个呢?

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

    // display it 
} 

你遇到的问题听起来像一个空指针cvShowImage();

1

在你的代码中,我没有看到cvNamedWindow()的任何调用来创建任何用于在(以及你销毁)上显示图像的窗口。您应该将其中一个呼叫置于循环中,然后才能使用cvShowImage()(如karlphillip在his answer中显示的那样)。

如果您在循环之前创建了命名窗口:您确定没有任何图像具有重复的名称吗?为了确保您不会将图像分配到已销毁的窗口,并确保您不销毁已经销毁的窗口?

会忽略所有cvDestroyWindow()的呼叫,并使用单个呼叫cvDestroyAllWindows()代替有助于避免您的问题?

-1

我喜欢openCV,但它不是从测试查询中显示搜索结果的唯一方法。您可以从您的图像文件夹的根目录中的C++代码编写一个html文件,并在浏览器中打开它。

如果你正在运行一个web服务器,你可以编写一个简单的文件列表,并用一个简单的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:

#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; 
} 
:基于在所述openCV2.3样品image.cpp waitKey(尤其注意到关于事件), destroyWindow和该代码示例
0

如果你想关闭所有的OpenCV图像显示窗口 使用:destroyAllWindows();