2011-12-14 18 views
5

Tôi nghĩ hiển thị OpenCV2 Mat trên MFC View rất đơn giản nhưng không phải. This is only relevant material I found on google. Xin lỗi vì sự thiếu hiểu biết của tôi nhưng tôi không thể tìm thấy bất kỳ tài liệu nào khác cho thấy cách sử dụng SetDIBitsToDevice với mảng dữ liệu "mảng" một chiều trở về. Cụ thể hơn, tôi cần biết cách chỉ định BITMAPINFO cho hàm này. Tôi có quay trở lại OpenCV kiểu C cũ để làm việc với MFC không?Cách hiển thị OpenCV Mat trên MFC Xem

UPDATE:

tôi thấy an example of SetDIBitsToDevice mà thực sự là thay cũ OpenCV C-phong cách. Nhưng thật đơn giản để chuyển đổi nó cho OpenCV2. Có những điều tôi cần phải đề cập đến để làm cho nó làm việc:

  1. phương pháp

    BPP không hoạt động cũng như chiều sâu Mat của trả về 0. Tôi chỉ thay đổi như thế này:

    static int Bpp(cv::Mat img) { return 8 * img.channels(); } 
    
  2. Mat không có nguồn gốc hội viên. Nhưng chỉ cần đặt 0 là tốt cho đối số xuất xứ của phương thức FillBitmapInfo.

Ngoài ra, mã sau hoạt động rất tốt. Hy vọng điều này cũng giúp các nhà phát triển khác.

void COpenCVTestView::OnDraw(CDC* pDC) 
{ 
COpenCVTestDoc* pDoc = GetDocument(); 
ASSERT_VALID(pDoc); 
if (!pDoc) 
    return; 
if(pDoc->m_cvImage.empty()) return; 
// TODO: add draw code for native data here 
int height=pDoc->m_cvImage.rows; 
int width=pDoc->m_cvImage.cols; 
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024]; 
BITMAPINFO* bmi = (BITMAPINFO*)buffer; 
FillBitmapInfo(bmi,width,height,Bpp(pDoc->m_cvImage),0); 
SetDIBitsToDevice(pDC->GetSafeHdc(), 0, 0, width, 
    height, 0, 0, 0, height, pDoc->m_cvImage.data, bmi, 
    DIB_RGB_COLORS); 
} 
void COpenCVTestView::FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin) 
{ 
assert(bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32)); 

BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); 

memset(bmih, 0, sizeof(*bmih)); 
bmih->biSize = sizeof(BITMAPINFOHEADER); 
bmih->biWidth = width; 
bmih->biHeight = origin ? abs(height) : -abs(height); 
bmih->biPlanes = 1; 
bmih->biBitCount = (unsigned short)bpp; 
bmih->biCompression = BI_RGB; 

if (bpp == 8) 
{ 
    RGBQUAD* palette = bmi->bmiColors; 

      for (int i = 0; i < 256; i++) 
    { 
     palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; 
     palette[i].rgbReserved = 0; 
    } 
} 
} 
+2

Và những gì xảy ra với các liên kết mà bạn cung cấp? – Sam

+0

@vasile Mặc dù tài liệu trên SetDIBitsToDevice, tôi không thể tìm ra cách sử dụng SetDIBitsToDevice với mảng dữ liệu một chiều "thành viên" trả về. –

+0

@Paul Tôi đã xem ví dụ của bạn làm nền tảng cho một số câu hỏi/câu trả lời khác. Tuy nhiên, tôi tự hỏi, làm thế nào để bạn tránh rò rỉ bộ nhớ từ việc sử dụng 'memset' trong hàm' FillBitmapInfo'? hoặc bằng cách nào đó được tự động giải phóng? Bạn có thể xây dựng trong lĩnh vực này không. – adn

Trả lời

1

Từ MSDN:

lpvBits [trong] Một con trỏ đến dữ liệu màu được lưu trữ như một mảng các byte. Để biết thêm thông tin, hãy xem phần Nhận xét sau.

Đây là con trỏ bạn phải bắt đầu với dữ liệu được trả về từ dữ liệu Mat ::.

+0

ở đây là tất cả những gì bạn cần. http://msdn.microsoft.com/en-us/library/dd162974(v=vs.85).aspx Chỉ cần đọc nó cho chính mình – Sam

+0

BITMAPINFO là một cấu trúc với tên trường rõ ràng nói những gì chúng chứa: BITMAPINFOHEADER-> chiều rộng, chiều cao, vv Bạn đã bao giờ đọc trang đó chưa? Vui lòng đọc thêm tài liệu Mat. – Sam

+0

Ok, tôi đã sử dụng nó một thời gian trước đây, nhưng nhìn vào sửa đổi ở trên, nó không đơn giản như đã nhớ. Lấy làm tiếc. – Sam

2

Đây là một cách khác để có thể hiển thị dữ liệu OpenCV trong MFC mà tôi sử dụng và hoạt động tuyệt vời:

IplImage* image// <-- this contains the image you want to display 
CvvImage tempdefault; 
RECT myrect; // <-- specifiy where on the screen you want it to be displayed 
myrect.top = 0; 
myrect.bottom = _pictureh; 
myrect.left = _picturex; 
myrect.right = _picturew+_picturex; 
tempdefault.Create(_pictureh,_picturew,32); 
tempdefault.CopyOf(image); 
tempdefault.DrawToHDC(pDC->GetSafeHdc(),&myrect); 
+1

không chắc chắn nếu tôi đang sử dụng cùng một phiên bản OpenCV, nhưng để chuyển đổi từ một mat sang IplImage, tôi chỉ cần làm: 'CvMat mat = cvMat (chiều cao, chiều rộng, CV_8UC1, dữ liệu); IplImage * img = cvCreateImage (cvSize (chiều cao, chiều rộng), IPL_DEPTH_8U, 1); cvGetImage (& mat, img); ' – Smash

+0

nope Tôi không biết nếu nó nhanh hơn, nó chỉ dễ dàng hơn nhiều;) – Smash

1

CvvImage không có sẵn trong các phiên bản mới của OpenCV. Sử dụng đoạn mã sau bạn có thể chuyển đổi Mat để CImage và sau đó hiển thị CImage ở khắp mọi nơi bạn muốn:

int Mat2CImage(Mat *mat, CImage &img){ 
    if(!mat || mat->empty()) 
    return -1; 
    int nBPP = mat->channels()*8; 
    img.Create(mat->cols, mat->rows, nBPP); 
    if(nBPP == 8) 
    { 
    static RGBQUAD pRGB[256]; 
    for (int i = 0; i < 256; i++) 
     pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i; 
    img.SetColorTable(0, 256, pRGB); 
    } 
    uchar* psrc = mat->data; 
    uchar* pdst = (uchar*) img.GetBits(); 
    int imgPitch = img.GetPitch(); 
    for(int y = 0; y < mat->rows; y++) 
    { 
    memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!) 
    psrc += mat->step; 
    pdst += imgPitch; 
    } 

    return 0; 
} 
Các vấn đề liên quan