2011-02-01 30 views
6

Tôi có máy ảnh và tôi đang đọc hình ảnh trong thời gian thực thành một mảng. Tôi đang áp dụng một số thuật toán cho hình ảnh và hiển thị nó. Sau đó, tôi nhận được hình ảnh tiếp theo và hiển thị nó là tốt. Vì vậy, tôi đang truyền hình ảnh từ máy ảnh đến màn hình. Tuy nhiên tôi cũng muốn lưu hình ảnh vào đĩa cứng khi tôi đã hiển thị chúng. Tôi đã cố gắng bằng cách sử dụng các chủ đề chính nhưng tất cả mọi thứ chậm lại quá nhiều. Sau đó tôi đã thử sử dụng ThreadPool (xem mã bên dưới). Điều này không làm chậm màn hình hiển thị nhưng tôi thấy các hình ảnh không được lưu đúng cách. Dường như họ không theo thứ tự mong đợi và sau khoảng 50 hình ảnh đã được lưu, dữ liệu hình ảnh tiếp theo sẽ bị cắt xén. Tôi đoán quá nhiều chủ đề đang được bắt đầu.Làm cách nào để lưu tệp vào đĩa cứng trong một chuỗi riêng biệt?

Có cách nào tốt hơn để thực hiện việc này không? Tôi nghĩ rằng tôi chỉ cần một sợi để lưu hình ảnh. Có thể một số loại hàng đợi lưu từng ảnh một cách liên tục. Chỉ cần miễn là nó được thực hiện trong nền và không làm chậm màn hình. Nếu ai đó có thể đăng đoạn mã sẽ tuyệt vời.

short[] image1 = new short[20000]; 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data;  

    data.fileName = imageNumber; 
    data.image = image1; 

    ThreadPool.QueueUserWorkItem(WriteImageToFile, data); // Send the writes to the queue 
} 


private void WriteImageToFile(object imageData) { 

    try { 
     ImageData data = (ImageData)imageData; 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 

     string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 

     using (Stream myStream = new FileStream(fName, FileMode.Create)) { 
      bf.Serialize(myStream, data.image); 
     } 
    } 
    catch (Exception) { } 
} 
+0

ngắn [] image1 = new short [20000]; Tôi đang gessing bạn chắc chắn rằng hình ảnh của bạn không mất nhiều hơn 40000 byte. –

+0

Liệu dữ liệu sao chép 'ReadImageFromCamera' vào mảng mà bạn truyền hay tạo một mảng mới được gán cho' image1'? –

+0

vâng xin lỗi thats chỉ để minh họa. Các hình ảnh có kích thước khác nhau tùy thuộc vào cài đặt người dùng (nhưng không phải khi phát trực tuyến đã bắt đầu). Im đọc tất cả dữ liệu hình ảnh OK để không có vấn đề ở đó. – Doug

Trả lời

0

Khi giao dịch với chuỗi, đặt hàng không còn nằm trong tầm kiểm soát của bạn. Các hồ bơi thread có thể chọn để sắp xếp các chủ đề theo thứ tự nào nó thích. Nếu bạn cần mọi thứ xảy ra tuần tự theo một thứ tự cụ thể, luồng không có ý nghĩa gì nhiều.

Về hình ảnh bị hỏng, có vẻ như phiên bản short[] image1 đang được chuyển đi. Không rõ điều gì sẽ xảy ra bên trong ReadImageFromCamera, nhưng vì bạn chuyển một mảng được khởi tạo trước vào nó, rất có thể là phương thức sẽ sử dụng mảng đó và chỉ sao chép dữ liệu vào đó (mặc dù từ khóa ref cho biết rằng nó có thể tạo ra một mảng hoàn toàn mới ví dụ và chỉ định thay thế). Sau đó, bạn chuyển thể hiện mảng đó tới WriteImageToFile trên một chuỗi riêng biệt.

Trong khi đó, trong trạng thái song song, bạn sẽ có được hình ảnh tiếp theo. Bây giờ bạn có một kịch bản nơi ReadImageFromCamera có thể ghi dữ liệu vào mảng cùng lúc với WriteImageToFile đang lưu trữ dữ liệu trên đĩa. Có bạn có hình ảnh bị hỏng của bạn. Điều này có thể tránh được bằng cách đi qua một ví dụ mảng mới để WriteImageToFile:

ReadImageFromCamera(ref image1) 
ImageData data;  

data.fileName = imageNumber; 
data.image = (short[])image1.Clone(); // create a new array instance, so that 
             // the next call to ReadImageFromCamera 
             // will not corrupt the data 

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

Tuy nhiên, as has been mentioned by Al Kepp, vì bạn chỉ có một ổ cứng, tung ra nhiều chủ đề có thể không được lựa chọn tốt nhất của bạn ở đây. Bạn có thể xem xét việc có một luồng riêng biệt dài để lưu trữ dữ liệu trên đĩa và đưa hình ảnh vào một số hàng đợi nào đó mà chuỗi lưu trữ sẽ chọn dữ liệu từ và ghi vào đĩa. Điều này đi kèm với bộ các vấn đề của nó đối phó với đồng thời, hạn chế kích thước của hàng đợi và những gì không.

+0

cảm ơn brlliant vì điều đó. – Doug

+0

Tôi muốn tạo một mảng mới [] ngắn thay vì gọi Clone(). Tôi có nghĩa là tạo mới [20000] ngay trước khi gọi ReadImageFromCamera(). Không phải là Clone() không cần thiết chậm cho kịch bản này? Tại sao chúng ta cần phải tạo ra một bản sao khi chúng ta sẽ vứt bỏ bản gốc ngay sau đó. –

6

Tôi nghĩ bạn nên tránh bắt đầu chuỗi mới cho từng hình ảnh cụ thể. Vì bạn chỉ có một ổ cứng duy nhất và lưu trữ tất cả các tệp vào thư mục duy nhất, bạn chỉ nên sử dụng một chuỗi trình ghi đĩa. Sau đó, tôi khuyên bạn nên sử dụng một số hàng đợi đồng thời để chuyển các công việc từ chuỗi máy ảnh sang chủ đề ghi đĩa. Tôi không hiển thị "đoạn mã" vì đây không phải là điều bạn có thể viết với chất lượng tốt trong một vài dòng mã.

Ngoài ra, bạn chắc chắn phải đặt một nơi nào đó 'mới ngắn [20000]' cho mỗi hình ảnh, nếu không nó sẽ bị ghi đè bởi hình ảnh tiếp theo trước khi bạn lưu nó vào đĩa.

Ngoài ra, tôi hy vọng rằng đủ để ghi tệp trong chuỗi chính, vì Windows sử dụng kỹ thuật đồng thời (chủ yếu là bộ nhớ cache đĩa) tự động khi bạn ghi dữ liệu vào đĩa. Bạn có chắc chắn rằng phần cứng của bạn đủ nhanh để viết tất cả các dữ liệu đó trong thời gian thực không?

+0

Tôi đã thử ghi dữ liệu vào đĩa trong chuỗi chính nhưng nó làm chậm đáng kể màn hình. Các giải pháp luồng không làm chậm màn hình – Doug

0

Bạn cần phải tạo bộ đệm riêng biệt cho chuỗi để đọc dữ liệu từ, nếu không chuỗi chính sẽ ghi đè lên nó khi bạn đổ nó vào tệp. Cách bạn đang làm dường như chỉ sao chép tài liệu tham khảo (đặc biệt là image1).

Vì vậy:

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

thay vì dữ liệu bạn sẽ gửi một bản sao sâu của data. Vì có vẻ như bạn đã làm điều đó - nhưng trong chuỗi công nhân - bạn chỉ cần di chuyển bản sao trước khi gửi.

HTH

+2

Một bản sao nông sẽ là khá đủ. Vì các phần tử là một kiểu giá trị bất biến, không có cách nào mà chúng được sửa đổi theo cách thỏa hiệp một tham chiếu mảng nhân bản. –

+0

@Fredrik: Bản sao cạn là đủ. 1 Nhưng lý do không phải là bất biến của các yếu tố, bởi vì mảng như toàn bộ không phải là bất biến. Nó chỉ là không bị ảnh hưởng trong kịch bản này - nó chỉ được hiển thị và lưu vào đĩa, vì vậy không có lý do gì để tạo một bản sao sâu. –

+0

@Fredrik nếu một bản sao nông là đủ, tại sao bạn sao chép nó trong câu trả lời của bạn? – Simone

0

Bạn cần phải kiểm tra trước khi suy nghĩ về chủ đề nếu tốc độ của một ổ đĩa bình thường sẽ là đủ cho công việc của bạn khi bạn có thể tạo ra hình ảnh nhanh hơn so với văn bản cho đĩa. Nếu việc tạo hình ảnh nhanh hơn viết, tôi sẽ xem xét sử dụng một đĩa Bộ nhớ, nhưng sau đó bạn cần phải tính toán nếu kích thước đủ cho đến khi bạn dừng máy ảnh, để bạn có thể ghi vào đĩa bình thường qua đêm.
Nếu bạn sử dụng .NET 4.0, tôi khuyên bạn nên sử dụng Concurrent queue cùng với chuỗi thông thường (khi chuỗi sẽ chạy cho đến khi chương trình kết thúc).

0

Bạn có thể làm như sau.

public class AsyncFileWriter 
    { 
     private readonly FileStream fs; 
     private readonly AsyncCallback callback; 
     public Action FinishedCallback; 
     private IAsyncResult result; 
     private class AsyncState 
     { 
      public FileStream Fs; 

     } 

     private void WriteCore(IAsyncResult ar) 
     { 
      if (result != null) 
      { 
       FileStream stream = ((AsyncState)ar.AsyncState).Fs; 
       stream.EndWrite(result); 
       if (this.FinishedCallback != null) 
       { 
        FinishedCallback(); 
       } 
      } 
     } 

     public AsyncFileWriter(FileStream fs, Action finishNotification) 
     { 
      this.fs = fs; 
      callback = new AsyncCallback(WriteCore); 
      this.FinishedCallback = finishNotification; 
     } 

     public AsyncFileWriter(FileStream fs) 
      : this(fs, null) 
     { 

     } 


     public void Write(Byte[] data) 
     { 
      result = fs.BeginWrite(data, 0, data.Length, callback, new AsyncState() { Fs = fs }); 
     } 
    } 

Sau đó, bạn có thể sử dụng nó dưới dạng.

static void Main(string[] args) 
     { 
      FileStream fs = File.Create("D:\\ror.txt"); 
      ManualResetEvent evt = new ManualResetEvent(false); 
      AsyncFileWriter writer = new AsyncFileWriter(fs,() => 
                   { 
                    Console.Write("Write Finished"); 
                    evt.Set(); 
                   } 

       ); 
      byte[] bytes = File.ReadAllBytes("D:\\test.xml");//Getting some random bytes 

      writer.Write(bytes); 
      evt.WaitOne(); 
      Console.Write("Write Done"); 
     } 
0

Cách nhanh chóng và đơn giản là bắt đầu một chuỗi mới và làm việc với các thành viên toàn cầu - chuỗi mới sẽ có thể truy cập chúng trong khi chủ đề chính sẽ cập nhật chúng.

Trước hết, có những dòng bên ngoài của bất kỳ chức năng:

private List<ImageData> arrGlobalData = new List<ImageData>(); 
private bool keepWritingImages = true; 

Bây giờ thay đổi mã trong các chủ đề "chính" như thế này:

short[] image1 = new short[20000]; 
ThreadPool.QueueUserWorkItem(WriteImageToFile, null); 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data = new ImageData(); 
    data.fileName = imageNumber; 
    data.image = image1; 
    arrGlobalData.Add(data); 
} 
keepWritingImages = false; 

Và cuối cùng có chức năng như vậy cho chủ đề mới:

private void WriteImageToFile(object imageData) 
{ 
    while (keepWritingImages) 
    { 
     if (arrGlobalData.Count > 0) 
     { 
      ImageData data = arrGlobalData[0]; 
      try 
      { 
       System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
       string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 
       using (Stream myStream = new FileStream(fName, FileMode.Create)) 
       { 
        bf.Serialize(myStream, data.image); 
       } 
      } 
      catch 
      { 
      } 
      finally 
      { 
       arrGlobalData.Remove(data); 
      } 
     } 

     Thread.Sleep(10); 
    } 
} 
+1

Tôi không thể tin rằng trang web này tốt đến mức nào. Tuyệt vời của nó, cảm ơn cho tất cả các bài trả lời. – Doug

+0

Tôi không thể tin rằng trang web này tốt đến mức nào. Tuyệt vời của nó, cảm ơn cho tất cả các bài trả lời. Câu trả lời của Frederick Mork dường như đã giải quyết mọi thứ. data.image = (short []) image1.Clone(); – Doug

+0

Shadow Wizard Im sẽ thử mã của bạn như Im vẫn còn một chút có liên quan QueueUserWorkItem sẽ sử dụng quá nhiều luồng tại một số điểm. – Doug

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