2012-01-12 39 views
17

Tôi đang thực hiện dự án tìm kiếm thông qua cơ sở dữ liệu hình ảnh và khi tôi tìm thấy kết quả cho một số truy vấn - 5 hình ảnh cơ sở dữ liệu, tôi muốn hiển thị kết quả trực quan. Tôi không giữ tất cả các hình ảnh trong bộ nhớ, vì vậy tôi đã tải hình ảnh đầu tiên để hiển thị nó.OpenCV - đóng cửa sổ hiển thị hình ảnh

Tôi có một cái gì đó đơn giản trong tâm trí, trong giả:

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

Dưới đây là một đoạn mã của tôi trong C++ sử dụng OpenCV cho mục đích này:

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

Mảng images sử dụng ở đây không như như vậy tồn tại trong mã của tôi, nhưng vì lợi ích của câu hỏi, nó chứa Tên tệp của các hình ảnh liên quan đến điểm chạy chương trình hiện tại nếu thành viên name của nó. Tôi lưu trữ tên hình ảnh một chút khác nhau trong dự án của tôi.

Mã ở trên gần như hoạt động: Tôi có thể lặp qua 4/5 hình ảnh OK, nhưng khi hình ảnh cuối cùng được hiển thị và nhấn một phím, hình ảnh sẽ chuyển sang màu xám và tôi không thể đóng cửa sổ hình ảnh. ứng dụng của tôi.

Ý tưởng đầu tiên của tôi là do tối ưu hóa thời gian biên dịch, cvReleaseImage giải phóng hình ảnh trước khi cvDestroyWindow kết thúc và bằng cách nào đó làm cho nó bị đóng băng. Nhưng, tôi đã thử thêm một số thời gian chờ đợi (do đó nhận xét ra sleep(1) dòng mã của tôi) và nó đã không giúp đỡ.

Tôi đang gọi chức năng hiển thị này từ ứng dụng bảng điều khiển của mình và khi hình ảnh bị đóng băng, điều khiển sẽ trả về ứng dụng của tôi và tôi có thể tiếp tục sử dụng nó (nhưng cửa sổ hình ảnh vẫn bị đóng băng trong nền).

Bạn có thể cho tôi bất kỳ đề xuất nào về cách sửa lỗi này không?

EDIT

Tôi đã nói chuyện với một số người đối phó với computer vision và OpenCV một cách thường xuyên kể từ khi đặt ra câu hỏi, và vẫn không có ý tưởng.

Tôi cũng đã tìm thấy một số stackoverflow question tương tự, nhưng vẫn không có câu trả lời được chấp nhận. Googleing chỉ cung cấp các câu hỏi tương tự như vậy, nhưng không có câu trả lời.

Bất kỳ ý tưởng nào về những gì cần thử (ngay cả khi chúng không phải là giải pháp hoàn chỉnh) đều được đánh giá rất cao.

+0

bạn có thể cho chúng tôi biết gì về mảng hình ảnh? bạn có thể gửi mã tạo và gán của nó không? BTW bạn gắn thẻ này là C++ vậy tại sao bạn sử dụng kiểu C cũ của opencv? – Boaz

+0

Kiểu C của OpenCv là vì tôi phải sử dụng thư viện C sử dụng OpenCV với mã của tôi và một số dữ liệu trong cấu trúc dữ liệu OpenCV kiểu C. Si này thực sự là một đoạn mã lớn hơn nhiều, và tôi truy cập vào tên của hình ảnh của tôi rất khác nhau. Vì lợi ích của câu hỏi, mảng ảnh chứa filepaths cho ảnh trong thành viên .name (tôi sẽ cập nhật câu hỏi) – penelope

+0

Bạn đã thử 'cvDestroyAllWindows()' chưa? Nếu vậy, và nó không hoạt động sau đó có một lỗi lạ hoặc trong mã của bạn một nơi nào đó (có thể không phải ở trên) hoặc trong OpenCv. Nếu nó hoạt động có một lỗi đơn giản ở đâu đó. – Florian

Trả lời

15

Đối với mục đích thử nghiệm, ứng dụng dưới đây thực hiện chính xác những gì bạn nêu trong câu hỏi: nó tải 7 hình ảnh thông qua các lệnh dòng, từng cái một, và tạo ra một cửa sổ mới cho mỗi hình ảnh sẽ được hiển thị.

Nó hoạt động hoàn hảo với OpenCV 2.3.1 trên 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; 
} 
-2

Hãy thử sử dụng

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

tại sao? Điều đó sẽ giúp tôi như thế nào? Và, nếu tôi không phát hành hình ảnh trước đó trước khi tải một hình ảnh mới, điều đó sẽ không khiến cho tất cả hình ảnh thực sự nằm trong bộ nhớ? – penelope

2

Không cần để tiêu diệt các cửa sổ trên mỗi khung hình, bạn có thể chỉ cần gọi cvShowImage() với tên cùng một cửa sổ và nó sẽ thay thế hình ảnh hiện tại.

Bạn chỉ cần gọi phá hủy cửa sổ khi tắt máy. Bạn có thể sử dụng cvCreateWindow() để tạo cửa sổ lúc khởi động nhưng nó sẽ được tạo tự động trong lệnh showWindow() đầu tiên.

+0

Hey, thnx cho câu trả lời, nhưng tôi thực sự có một tên khác nhau cho mỗi cửa sổ. Vấn đề không phải là hiển thị các cửa sổ, đó là 'cvDestroyWindow' không đóng cửa sổ - chính xác hơn, cửa sổ cuối cùng vẫn mở. – penelope

5

cvDestroyWindow() thường là chỉ bắt đầu quy trình phá hủy cửa sổ khá phức tạp. Quy trình này yêu cầu một số tương tác (trao đổi sự kiện) giữa hệ thống cửa sổ và ứng dụng của bạn. Cho đến khi thủ tục này kết thúc, cửa sổ không thể bị phá hủy hoàn toàn.Đó là lý do tại sao bạn thấy cửa sổ bị phá hủy một phần trong khi ứng dụng của bạn thực hiện một cái gì đó không liên quan đến GUI.

Trao đổi sự kiện có thể được thực hiện theo cách phụ thuộc vào hệ thống. Trong Windows, điều này có nghĩa là (trực tiếp hoặc gián tiếp) gọi các hàm GetMessage hoặc MsgWaitFor* và xử lý kết quả. Đối với Unix, điều này có nghĩa là (trực tiếp hoặc gián tiếp) gọi XNextEvent và xử lý kết quả.

OpenCV cho phép thực hiện trao đổi sự kiện này theo cách độc lập với hệ thống. Có hai phương pháp được làm tài liệu để thực hiện việc này. Đầu tiên là cvWaitKey() (chỉ cần gọi cvWaitKey(1) sau khi bạn đóng hình ảnh cuối cùng). Thứ hai là gọi cvStartWindowThread() khi bắt đầu chương trình của bạn để cho phép OpenCV tự động cập nhật cửa sổ của mình.

Chỉ một trong các phương pháp này hoạt động bình thường trên hộp Linux của tôi với libcv2.1: cvStartWindowThread().


Cập nhật (đoạn mã với 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

bạn có thể vui lòng hiển thị đoạn mã về cách bạn đã sử dụng cvStartWindowThread() không? Tôi đang phải đối mặt với cùng một vấn đề và giải pháp đầu tiên bạn đề xuất (đặt cvWaitKey (1) sau khi phá hủy cửa sổ) đã không làm việc. Tôi tiếp tục mở cửa sổ trống mà không hiển thị hình ảnh trong suốt thời gian còn lại mà chương trình của tôi vẫn tiếp tục chạy! – Matteo

+0

@Matteo, tôi đã thêm đoạn mã. (Thử nghiệm nó một lần nữa - vẫn hoạt động chính xác). 'cvWaitKey (1)' cũng không làm việc cho tôi. –

+0

Rất nhiều, nó trông rất đơn giản Chỉ cần một câu hỏi khác: nếu tôi sử dụng một chức năng để hiển thị một hình ảnh nào bạn đề nghị tạo ra các chủ đề trong phạm vi toàn cầu hoặc địa phương? Tôi có thể cần phải gọi hàm này nhiều lần trong chính. – Matteo

0

Bạn đã kiểm tra xem hình ảnh thứ 5 của bạn có được tải đúng không? Cái này thì sao?

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

    // display it 
} 

Sự cố bạn gặp phải giống như con trỏ rỗng tới cvShowImage();

1

Trong mã của bạn, tôi đã không thấy bất kỳ cuộc gọi nào của cvNamedWindow() để tạo bất kỳ cửa sổ nào bạn sử dụng để hiển thị hình ảnh (và bạn hủy). Có lẽ bạn nên đặt một trong những cuộc gọi đó vào vòng lặp, trước khi bạn cvShowImage() (như karlphillip đã hiển thị trong his answer).

Nếu bạn tạo cửa sổ được đặt tên trước vòng lặp: Bạn đã đảm bảo rằng không có hình ảnh nào có tên trùng lặp? Để chắc chắn rằng bạn không gán một hình ảnh cho một cửa sổ bị phá hủy, và để chắc chắn rằng bạn không phá hủy một cửa sổ đã bị phá hủy?

Sẽ bỏ qua tất cả các cuộc gọi cvDestroyWindow() và sử dụng một cuộc gọi duy nhất cvDestroyAllWindows() thay vì trợ giúp để tránh sự cố của bạn?

-1

Tôi thích openCV nhưng không phải là cách duy nhất để hiển thị kết quả tìm kiếm từ truy vấn thử nghiệm của bạn. Bạn có thể viết một tập tin html từ C++ - code của bạn trong thư mục gốc của thư mục hình ảnh của bạn và mở nó trong một trình duyệt.

Nếu bạn đang chạy máy chủ web, bạn có thể viết một danh sách tệp đơn giản và xuất bản nó bằng một tập lệnh php đơn giản hoặc tương tự.

Các kết quả html-code sẽ là một cái gì đó như:

<!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 lợi thế của phương pháp này là nó có thể chạy trên một máy chủ không đầu.

Đối với đóng cửa sổ nhìn hiển thị hình ảnh ở tài liệu cho opencv2.3: waitKey (đặc biệt lưu ý về các sự kiện), destroyWindow và mã này ví dụ dựa trên mẫu image.cpp trong 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; 
} 
0

Nếu bạn muốn đóng tất cả các cửa sổ hiển thị hình ảnh OpenCV sử dụng: destroyAllWindows();

Các vấn đề liên quan