OpenCV – 关闭图像显示窗口

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

在伪代码中,我有一些简单的想法:

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

以下是使用OpenCV实现此目的的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图像确定,但是当显示最后一个图像并按下一个键时,图像变为灰色,我无法关闭图像窗口而不会崩溃我的应用程序的其余部分。

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

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

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

编辑

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

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

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

出于测试目的, 下面应用程序完全按照您在问题中的说明进行操作 :它通过命令行逐个加载7个图像,并为每个要显示的图像创建一个新窗口。

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

 #include  #include  #define NUM_IMGS 7 int main(int argc, char* argv[]) { if (argc < 8) { printf("Usage: %s       \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; } 

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  #include  #include  #include  #define NUM_IMGS 2 int main(int argc, char* argv[]) { if (argc < 2) { printf("Usage: %s \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; } 

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

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

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

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

省略cvDestroyWindow()所有调用并使用cvDestroyWindow()的单个调用来帮助避免你的问题?

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

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

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

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

尝试使用

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

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

如果您正在运行Web服务器,则可以编写一个简单的文件列表,并使用简单的php脚本或类似文件发布它。

生成的html代码将类似于:

               

这种方法的优点是它可以在无头服务器上运行。

要关闭图像显示窗口,请查看opencv2.3的文档: waitKey (特别是有关事件的注释), destroyWindow以及基于openCV2.3中image.cpp示例的此代码示例:

 #include "cv.h" // include standard OpenCV headers, same as before #include "highgui.h" #include  #include  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 iplimg = cvLoadImage(imagename); // Ptr 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; }