【问题标题】:OpenCV - closing the image display windowOpenCV - 关闭图像显示窗口
【发布时间】:2012-02-09 04:53:23
【问题描述】:

我正在做一个搜索图像数据库的项目,当我找到一些查询的结果 - 5 个数据库图像时,我想直观地显示结果。我不会将所有图像都保存在内存中,所以我会先加载图像以显示它。

我的想法很简单,用伪代码写成:

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

这是我在C++ 中的代码的sn-p,为此使用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 个图像 OK,但是当显示最后一个图像并按下一个键时,图像变灰,我无法关闭图像窗口不会使我的应用程序的其余部分崩溃。

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

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

你能给我一些关于如何解决这个问题的建议吗?

编辑

自从提出这个问题后,我定期与一些处理计算机视觉和 OpenCV 的人交谈,但仍然没有任何想法。

我也找到了类似的stackoverflow question,但仍然没有接受的答案。谷歌搜索结果只是给出了类似的问题,但没有答案。

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

【问题讨论】:

  • 您能告诉我们有关图像数组的哪些信息?你能发布它的创建和分配代码吗?顺便说一句,您将其标记为 c++,那么您为什么使用旧的 C 风格的 opencv?
  • OpenCv 的 C 风格是因为我必须使用一个 C 库,该库在我的代码中使用 OpenCV,并且一些数据采用 C 风格的 OpenCV 数据结构。这个 si 实际上是一个更大的代码的 sn-p,我访问我的图像名称的方式大不相同。为了这个问题, images 数组包含 .name 成员中图片的文件路径(我会更新问题)
  • 你试过cvDestroyAllWindows()吗?如果是这样,并且它不起作用,那么您的代码中的某个地方(可能不在上面)或 OpenCv 中都有一个奇怪的错误。如果它有效,那么某处就有一个简单的错误。
  • 您能否提供示例代码、示例图像和示例图像名称来重现观察到的行为?

标签: c++ c opencv


【解决方案1】:

出于测试目的,下面的应用程序完全按照您在问题中的说明进行操作:它通过命令行一张一张地加载 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() 通常只启动相当复杂的窗口销毁过程。此过程需要窗口系统和您的应用程序之间的一些交互(事件交换)。在此过程完成之前,无法完全销毁该窗口。这就是您在应用程序执行与 GUI 无关的操作时看到部分损坏的窗口的原因。

    事件交换可以以系统相关的方式执行。在 Windows 中,这意味着(直接或间接)调用 GetMessageMsgWaitFor* 函数并处理结果。对于 Unix,这意味着(直接或间接)调用 XNextEvent 并处理结果。

    OpenCV 允许以独立于系统的方式进行此事件交换。有两种记录方法可以做到这一点。第一个是cvWaitKey()(关闭最后一张图片后只需拨打cvWaitKey(1))。第二个是在程序开始时调用cvStartWindowThread() 以允许OpenCV 自动更新其窗口。

    这些方法中只有一种在我的带有 libcv2.1 的 Linux 机器上正常工作:cvStartWindowThread()


    更新(带有 cvStartWindowThread() 的代码 sn-p)

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

    【讨论】:

    • 能否请您展示一下您如何使用 cvStartWindowThread() 的代码 sn-p?我面临同样的问题,您提出的第一个解决方案(在窗口破坏后放置 cvWaitKey(1) )没有奏效。在我的程序继续运行的所有剩余时间内,我继续打开空窗口,其中没有显示任何图像!提前谢谢!
    • @Matteo,我添加了代码 sn-p。 (再次测试它 - 仍然可以正常工作)。 cvWaitKey(1) 也不适合我。
    • 想了很多,看起来真的很简单!只是另一个问题:如果我使用函数来显示图像,您建议在全局或本地范围内创建线程吗?我可能需要在 main 中多次调用这个函数。
    • @Matteo,我认为,在哪里创建线程并不重要。如果您在“显示”函数中创建它,只需确保它仅在第一次函数调用时创建一次。
    • 一切都清晰整洁。非常感谢您的及时回复和帮助!再见;D
    【解决方案3】:

    不需要在每一帧都销毁窗口,只需调用 cvShowImage() 以相同的窗口名称,它将替换当前图像。

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

    【讨论】:

    • 嘿,谢谢回答,但实际上每个窗口我都有不同的名称。问题不在于显示窗口,而是 cvDestroyWindow 没有关闭窗口 - 更准确地说,最后一个窗口保持打开状态。
    【解决方案4】:

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

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

    省略cvDestroyWindow() 的所有调用并使用cvDestroyAllWindows() 的单个调用有助于避免您的问题吗?

    【讨论】:

      【解决方案5】:

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

      for(...)
      {
         if(!img)
             break;
      
         // display it
      }
      

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

      【讨论】:

        【解决方案6】:

        我喜欢 openCV,但它不是显示测试查询搜索结果的唯一方法。您可以在图像文件夹的根目录中从您的 c++ 代码编写一个 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 以及此代码示例基于 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;
        }
        

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

            我一直在寻找一种解决方案来检查是否按下了键或窗口是否已关闭。这样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
            

            【讨论】:

              【解决方案9】:

              尝试使用

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

              【讨论】:

              • 为什么?这对我有什么帮助?而且,如果我在加载新图像之前不释放之前的图像,那不会导致所有图像实际留在内存中吗?
              猜你喜欢
              • 1970-01-01
              • 2021-03-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-08-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多