OpenCV - 关闭图像显示窗口

时间:2012-01-12 21:53:36

标签: c++ c 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数组并不存在于我的代码中,但为了问题,它包含相对于当前程序运行点的图像的文件名{{1}会员我在项目中存储的图像名称略有不同。

上面的代码几乎有效:我可以迭代4/5图像OK,但是当显示最后一张图像并按下一个键时,图像变灰,我无法关闭图像窗口在没有崩溃我的应用程序的其余部分。

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

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

您能否就如何解决此问题向我提出任何建议?

修改

自从提出问题以来,我已经定期与一些处理计算机视觉和OpenCV的人交谈,但仍然没有想法。

我也发现了类似的stackoverflow question,但仍然没有接受答案。谷歌只是给出了类似的问题,但没有答案。

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

9 个答案:

答案 0 :(得分:19)

出于测试目的,下面的应用程序完全按照您在问题中所述的内容:它通过命令行逐个加载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;
}

答案 1 :(得分: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;
}

答案 2 :(得分:2)

不需要破坏每个帧上的窗口,只需使用相同的窗口名称调用cvShowImage(),它就会替换当前图像。

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

答案 3 :(得分:1)

在你的代码中,我没有看到cvNamedWindow()的任何调用来创建用于显示图像的任何窗口(以及你要销毁的)。你应该在cvShowImage()之前将其中一个调用放入循环中(正如karlphillip在his answer中所示)。

如果在循环之前创建命名窗口:您是否确保没有任何图像具有重复的名称?要确保不将图像分配给已销毁的窗口,并确保不销毁已经销毁的窗口?

省略cvDestroyWindow()的所有来电并使用cvDestroyAllWindows()的单一来电有助于避免您的问题?

答案 4 :(得分:0)

您是否测试过您的第5张图片是否正确加载?那怎么样?

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

   // display it
}

您遇到的问题听起来像是指向cvShowImage()的空指针;

答案 5 :(得分:0)

如果要关闭所有OpenCV图像显示窗口 使用:=IF(ISNUMBER(A1),DATE(YEAR(A1),DAY(A1),MONTH(A1))+A1-INT(A1),DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)) + TIMEVALUE(RIGHT(A1,5)))

答案 6 :(得分:0)

我正在寻找的解决方案是检查是否已按下键或窗口是否已关闭。这样,python不会停止:

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

答案 7 :(得分:-1)

尝试使用

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

答案 8 :(得分:-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的文档: waitKey(特别是有关事件的注释),destroyWindow以及基于openCV2.3中image.cpp示例的代码示例:

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