2015-04-26 15 views
8

Trong mã của tôi, tôi lấy khung từ máy ảnh với con trỏ tới đối tượng không được quản lý, thực hiện một số phép tính và sau đó tôi hiển thị nó trên một điều khiển bằng hình ảnh.
Trước khi tôi đi xa hơn trong ứng dụng này với tất cả các chi tiết, tôi muốn chắc chắn rằng mã cơ sở cho quá trình này là tốt. Cụ thể, tôi muốn:
- giữ thời gian thực hiện tối thiểu và tránh các hoạt động không cần thiết, chẳng hạn như sao chép nhiều hình ảnh hơn mức cần thiết. Tôi chỉ muốn giữ lại các hoạt động thiết yếu
- hiểu xem sự chậm trễ trong quá trình tính toán trên mỗi khung có thể có ảnh hưởng bất lợi đến cách hiển thị hình ảnh không (nghĩa là nếu nó không được in như mong đợi) hoặc một số hình ảnh bị bỏ qua
- ngăn chặn các lỗi nghiêm trọng hơn, chẳng hạn như lỗi do bộ nhớ hoặc quản lý luồng hoặc hiển thị hình ảnh.
Vì mục đích này, tôi thiết lập một số dòng mã thử nghiệm (bên dưới), nhưng tôi không thể giải thích kết quả của những gì tôi đã tìm thấy. Nếu bạn có các tệp thực thi của OpenCv, bạn có thể tự mình thử.Thử nghiệm hiển thị bitmap được lấy từ máy ảnh trên Picturebox

using System; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 
using System.Threading; 

public partial class FormX : Form 
{ 
private delegate void setImageCallback(); 
Bitmap _bmp; 
Bitmap _bmp_draw; 
bool _exit; 
double _x; 
IntPtr _ImgBuffer; 

bool buffercopy; 
bool copyBitmap; 
bool refresh; 

public FormX() 
{ 
    InitializeComponent(); 
    _x = 10.1; 

    // set experimemental parameters 
    buffercopy = false; 
    copyBitmap = false; 
    refresh = true; 
} 

private void buttonStart_Click(object sender, EventArgs e) 
{ 
    Thread camThread = new Thread(new ThreadStart(Cycle)); 
    camThread.Start(); 
} 

private void buttonStop_Click(object sender, EventArgs e) 
{ 
    _exit = true; 
} 

private void Cycle() 
{ 
    _ImgBuffer = IntPtr.Zero; 
    _exit = false; 

    IntPtr vcap = cvCreateCameraCapture(0); 
    while (!_exit) 
    { 
     IntPtr frame = cvQueryFrame(vcap); 

     if (buffercopy) 
     { 
      UnmanageCopy(frame); 
      _bmp = SharedBitmap(_ImgBuffer); 
     } 
     else 
     { _bmp = SharedBitmap(frame); } 

     // make calculations 
     int N = 1000000; /*1000000*/ 
     for (int i = 0; i < N; i++) 
      _x = Math.Sin(0.999999 * _x); 

     ShowFrame(); 
    } 

    cvReleaseImage(ref _ImgBuffer); 
    cvReleaseCapture(ref vcap); 
} 


private void ShowFrame() 
{ 
    if (pbCam.InvokeRequired) 
    { 
     this.Invoke(new setImageCallback(ShowFrame)); 
    } 
    else 
    { 
     Pen RectangleDtPen = new Pen(Color.Azure, 3); 

     if (copyBitmap) 
     { 
      if (_bmp_draw != null) _bmp_draw.Dispose(); 
      //_bmp_draw = new Bitmap(_bmp); // deep copy 
      _bmp_draw = _bmp.Clone(new Rectangle(0, 0, _bmp.Width, _bmp.Height), _bmp.PixelFormat); 
     } 
     else 
     { 
      _bmp_draw = _bmp; // add reference to the same object 
     } 

     Graphics g = Graphics.FromImage(_bmp_draw); 
     String drawString = _x.ToString(); 
     Font drawFont = new Font("Arial", 56); 
     SolidBrush drawBrush = new SolidBrush(Color.Red); 
     PointF drawPoint = new PointF(10.0F, 10.0F); 
     g.DrawString(drawString, drawFont, drawBrush, drawPoint); 
     drawPoint = new PointF(10.0F, 300.0F); 
     g.DrawString(drawString, drawFont, drawBrush, drawPoint); 
     g.DrawRectangle(RectangleDtPen, 12, 12, 200, 400); 
     g.Dispose(); 

     pbCam.Image = _bmp_draw; 
     if (refresh) pbCam.Refresh(); 
    } 
} 

public void UnmanageCopy(IntPtr f) 
{ 
    if (_ImgBuffer == IntPtr.Zero) 
     _ImgBuffer = cvCloneImage(f); 
    else 
     cvCopy(f, _ImgBuffer, IntPtr.Zero); 
} 

// only works with 3 channel images from camera! (to keep code minimal) 
public Bitmap SharedBitmap(IntPtr ipl) 
{ 
    // gets unmanaged data from pointer to IplImage: 
    IntPtr scan0; 
    int step; 
    Size size; 
    OpenCvCall.cvGetRawData(ipl, out scan0, out step, out size); 
    return new Bitmap(size.Width, size.Height, step, PixelFormat.Format24bppRgb, scan0); 
} 

// based on older version of OpenCv. Change dll name if different 
[DllImport("opencv_highgui246", CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr cvCreateCameraCapture(int index); 

[DllImport("opencv_highgui246", CallingConvention = CallingConvention.Cdecl)] 
public static extern void cvReleaseCapture(ref IntPtr capture); 

[DllImport("opencv_highgui246", CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr cvQueryFrame(IntPtr capture); 

[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)] 
public static extern void cvGetRawData(IntPtr arr, out IntPtr data, out int step, out Size roiSize); 

[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)] 
public static extern void cvCopy(IntPtr src, IntPtr dst, IntPtr mask); 

[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr cvCloneImage(IntPtr src); 

[DllImport("opencv_core246", CallingConvention = CallingConvention.Cdecl)] 
public static extern void cvReleaseImage(ref IntPtr image); 
} 

quả [lõi kép 2 Duo T6600 2,2 GHz]:

Một. buffercopy = false; copyBitmap = false; refresh = false;
Đây là cấu hình đơn giản hơn. Mỗi khung được lấy lại lần lượt, hoạt động được thực hiện (trong thực tế chúng được dựa trên cùng một khung hình, đây chỉ là tính toán), sau đó kết quả tính toán được in trên đầu hình ảnh và cuối cùng nó được hiển thị trên hình ảnh.
tài liệu OpenCV nói:

OpenCV chức năng 1.x cvRetrieveFrame và cv.RetrieveFrame trở lại hình ảnh lưu trữ bên trong cấu trúc chụp video. Không được phép sửa đổi hoặc giải phóng hình ảnh! Bạn có thể sao chép khung bằng cách sử dụng cvCloneImage() và sau đó thực hiện bất cứ điều gì bạn muốn với bản sao.

Nhưng điều này không ngăn chúng tôi thực hiện thử nghiệm.
Nếu tính toán không mãnh liệt (số lần lặp lại thấp, N), mọi thứ đều ổn và thực tế là chúng ta thao tác bộ đệm ảnh do trình thu thập khung không được quản lý không gây ra vấn đề ở đây.
Lý do là có lẽ họ khuyên nên để nguyên vẹn bộ đệm, trong trường hợp mọi người sẽ sửa đổi cấu trúc của nó (không phải giá trị của nó) hoặc thực hiện các hoạt động không đồng bộ mà không nhận ra nó. Bây giờ chúng tôi lấy khung hình và sửa đổi nội dung của họ lần lượt.
Nếu N tăng (N = 1000000 trở lên), khi số khung hình trên giây không cao, ví dụ với ánh sáng nhân tạo và phơi sáng thấp, mọi thứ có vẻ ok, nhưng sau một thời gian video bị trễ và đồ họa gây ấn tượng trên đó đang nhấp nháy. Với tỷ lệ khung hình cao hơn, nhấp nháy xuất hiện ngay từ đầu, ngay cả khi video vẫn còn là chất lỏng.
Đây có phải là do cơ chế hiển thị hình ảnh trên điều khiển (hoặc làm mới hay bất kỳ thứ gì khác) bằng cách nào đó không đồng bộ và khi hình ảnh đang tìm nạp bộ đệm dữ liệu thì máy ảnh đã sửa đổi trong khi đó, xóa đồ họa?
Hoặc có lý do nào khác không?
Tại sao hình ảnh bị tụt hậu theo cách đó, tức làTôi hy vọng rằng sự chậm trễ do tính toán chỉ có tác dụng bỏ qua các khung nhận được bởi máy ảnh khi tính toán chưa được thực hiện, và thực tế chỉ làm giảm tốc độ khung hình; hoặc cách khác là tất cả các khung hình được nhận và độ trễ do tính toán mang đến cho hệ thống xử lý hình ảnh nhận được phút trước, bởi vì hàng đợi hình ảnh để xử lý tăng theo thời gian.
Thay vào đó, hành vi được quan sát dường như lai giữa hai: có sự chậm trễ vài giây, nhưng điều này dường như không tăng nhiều khi quá trình chụp tiếp tục.

B. buffercopy = true; copyBitmap = false; refresh = false;
Ở đây tôi tạo một bản sao sâu của bộ đệm vào bộ đệm thứ hai, theo lời khuyên của tài liệu OpenCv.
Không có thay đổi nào. Bộ đệm thứ hai không thay đổi địa chỉ của nó trong bộ nhớ trong khi chạy.

C. buffercopy = false; copyBitmap = true; refresh = false;
Bây giờ bản sao (sâu) của bitmap được phân bổ mỗi lần một không gian mới trong bộ nhớ.
Hiệu ứng nhấp nháy đã biến mất, nhưng sự chậm trễ tiếp tục phát sinh sau một thời gian nhất định.

D. buffercopy = false; copyBitmap = false; refresh = true;
Như trước đây.

Vui lòng giúp tôi giải thích các kết quả này!

+0

Thực sự không rõ bạn đang hỏi câu hỏi gì ở đây. Hãy nhớ rằng SO là một diễn đàn Q & A. IIUC, một điều mà bạn dường như đang hỏi là dọc theo dòng "tài liệu nói không làm điều này, nhưng nếu tôi làm điều đó đôi khi nó hoạt động và đôi khi nó không". Vâng - đừng làm thế! –

+0

Không. Có vẻ như bạn vừa đọc một nửa văn bản. Dù sao, nếu bạn đi đến dòng cuối cùng nó sẽ yêu cầu giải thích về kết quả. –

+0

Tôi sợ rằng bạn đang đặt câu hỏi bạn chỉ có thể trả lời vì bạn là người đã thiết kế thử nghiệm. Tôi khuyên bạn nên lên kế hoạch cho một thử nghiệm khác để bạn có thể hiểu kết quả của nó. Việc tái cơ cấu câu hỏi của bạn sẽ rất hay vì khó theo dõi logic hoặc câu hỏi của bạn. –

Trả lời

2

Nếu tôi có thể thẳng thắn, có một chút tẻ nhạt để hiểu tất cả chi tiết câu hỏi của bạn, nhưng hãy để tôi thực hiện một vài điểm để giúp bạn phân tích kết quả của mình.

Trong trường hợp A, bạn nói bạn thực hiện các phép tính trực tiếp trên bộ đệm. Tài liệu nói rằng bạn không nên làm điều này, vì vậy nếu bạn làm, bạn có thể mong đợi kết quả không xác định. OpenCV giả định bạn sẽ không chạm vào nó, vì vậy nó có thể làm những thứ như đột nhiên xóa phần đó của bộ nhớ, cho phép một số ứng dụng khác xử lý nó, v.v. Có vẻ như nó hoạt động, nhưng bạn không bao giờ có thể biết chắc chắn, vì vậy đừng làm điều đó * tát cổ tay của bạn * Đặc biệt, nếu quá trình xử lý của bạn mất nhiều thời gian, máy ảnh có thể ghi đè bộ đệm trong khi bạn đang xử lý nó.

Cách bạn nên làm là sao chép bộ đệm trước khi thực hiện bất kỳ điều gì. Điều này sẽ cung cấp cho bạn một phần của bộ nhớ đó là của bạn để làm với bất cứ điều gì bạn muốn. Bạn có thể tạo một Bitmap đề cập đến bộ nhớ này, và tự giải phóng bộ nhớ khi bạn không cần nó nữa.

Nếu tốc độ xử lý của bạn (khung hình được xử lý mỗi giây) nhỏ hơn số khung hình được chụp mỗi giây bởi máy ảnh, bạn phải mong đợi một số khung hình sẽ bị xóa. Nếu bạn muốn hiển thị trực tiếp các hình ảnh đã xử lý, nó sẽ bị trễ và không có cách nào đơn giản xung quanh nó. Nếu điều quan trọng là ứng dụng của bạn xử lý video chất lỏng (ví dụ: điều này có thể cần thiết nếu bạn đang theo dõi một đối tượng), sau đó xem xét lưu trữ video vào đĩa để bạn không phải xử lý trong thời gian thực. Bạn cũng có thể xem xét xử lý đa luồng để xử lý nhiều khung hình cùng một lúc, nhưng chế độ xem trực tiếp sẽ có độ trễ.

Nhân tiện, có lý do cụ thể nào khiến bạn không sử dụng EmguCV không? Nó có trừu tượng cho máy ảnh và một hệ thống làm tăng sự kiện bất cứ khi nào máy ảnh đã chụp một khung hình mới.Bằng cách này, bạn không cần phải liên tục gọi cvQueryFrame trên một chuỗi nền.

+0

Tôi luôn tôn trọng quy tắc “tài liệu nói điều đó và vì vậy tôi sẽ làm điều đó”, nhưng tôi nghĩ rằng việc hiểu lý do tại sao nó nêu rõ điều đó là hữu ích. Chắc chắn, nếu bạn tôn trọng nó, bạn sẽ không gặp vấn đề gì, nhưng nếu bạn không hiểu chính xác lý do bạn có thể gặp phải vấn đề ở nơi khác. Ví dụ: tôi đã tìm thấy nhiều triển khai (thậm chí từ các trang web có thẩm quyền) tạo bản sao khung của riêng nó bên trong một ảnh đệm. Tôi nghi ngờ rằng họ chỉ đơn giản là chuyển một phần của các vấn đề ở nơi khác: nếu bộ đệm đó được truy cập trong khi một phần khác của chương trình đang viết trên đó thì sao? –

+0

Có thể điều này thường không xảy ra trong nhiều lần triển khai vì cùng một lý do tại sao nó không gây ra vấn đề cho bộ đệm gốc: các hoạt động viết và đọc là tuần tự. Không chắc chắn về điều đó, nhưng tôi chỉ đang tìm kiếm xác nhận. –

+0

Có thể bạn sẽ không có ứng dụng nào khác truy cập vào cùng một lúc, nhưng có thể hiểu rằng nếu bạn thực hiện quá trình xử lý chậm trên một hình ảnh thì máy ảnh sẽ thay thế dữ liệu hình ảnh trước khi bạn hoàn thành. Nếu mọi ứng dụng tạo ra một bản sao, thì chắc chắn không có chiến đấu nào. Nó cũng có thể hiểu rằng máy ảnh sẽ xóa hoặc thay thế dữ liệu trong bộ đệm. Tôi nói "có thể hiểu được", ngụ ý rằng chúng ta không biết chắc chắn, nhưng tài liệu rõ ràng đủ – MariusUt

0

Tôi nghĩ rằng bạn vẫn gặp sự cố với phương pháp UnmanageCopy của mình ở chỗ bạn chỉ sao chép hình ảnh lần đầu tiên được gọi và sau đó bạn sao chép hình ảnh đó. Tôi tin rằng bạn cần phải làm một cvCloneImage (f) mỗi thời gian như bản sao thực hiện chỉ là một bản sao nông, không phải là một bản sao sâu như bạn nghĩ.

+0

Tôi đã viết "Bộ đệm thứ hai không thay đổi địa chỉ của nó trong bộ nhớ trong khi chạy". Vì vậy, trong trường hợp B, bản sao sâu trong thực hiện trong một bộ đệm. –

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