2012-04-18 56 views
12

Tôi đang làm việc với máy ảnh (trong trường hợp này là đơn sắc) trong VB.NET. API của máy ảnh nằm trong C++ và nhà sản xuất cung cấp trình bao bọc. Như là điển hình trong những trường hợp này, hình ảnh được trả về dưới dạng mảng Byte. Trong trường hợp cụ thể của tôi nó là 8 bit cho mỗi pixel vì vậy không có bitpacking ưa thích để hoàn tác.Làm thế nào để bạn chuyển đổi một mảng byte thành một thể hiện Bitmap (.NET)?

Tôi đã được phát tán là rằng .NET có quá ít hỗ trợ cho loại điều này. Tìm kiếm trực tuyến Tôi tìm thấy các chủ đề khác nhau, nhưng nó không giúp trong nhiều trường hợp mọi người có một mảng byte tương ứng với dữ liệu hình ảnh (nghĩa là, đọc một tệp hình ảnh thành một mảng byte và sau đó có .NET giải thích mảng dưới dạng tệp, với tiêu đề và mọi thứ).

Trong trường hợp này, tôi cần phải chuyển đổi một mảng giá trị pixel thô thành, đặc biệt là một thứ tôi có thể hiển thị trên màn hình.

Có vẻ như không có bất kỳ cách tích hợp nào để thực hiện điều này, vì định dạng 8bpp được tích hợp vào .NET được lập chỉ mục (cần bảng màu) nhưng dường như không đặt bảng màu được hỗ trợ. Một lần nữa, điều này thực sự làm tôi ngạc nhiên. This là chủ đề hứa hẹn nhất mà tôi tìm thấy.

Vì vậy, cách duy nhất tôi tìm thấy để thực hiện việc này là tạo một Bitmap và sau đó sao chép giá trị pixel theo pixel. Trong trường hợp của tôi, hình ảnh 8 MPixel mất vài giây trên i7 nhanh nhất khi sử dụng SetPixel.

Cách thay thế tôi tìm thấy để sử dụng SetPixelLockBits, sau đó cung cấp cho bạn quyền truy cập vào mảng (không được quản lý) các byte cấu thành hình ảnh. Nó có vẻ lãng phí, bởi vì tôi phải thao tác các mảng trong. NET và sau đó sao chép nó trở lại không gian unmananaged. Tôi đã sao chép dữ liệu hình ảnh ban đầu một lần (vì vậy bản gốc có thể được sử dụng lại hoặc bị loại bỏ bởi phần còn lại của những gì đang diễn ra) vì vậy mặc dù nhanh hơn đáng kể so với sử dụng SetPixel điều này vẫn có vẻ lãng phí.

Đây là mã VB Tôi có:

Public Function GetBitmap(ByRef TheBitmap As System.Drawing.Bitmap) As Integer 
    Dim ImageData() As Byte 
    Dim TheWidth, TheHeight As Integer 
    'I've omitted the code here (too specific for this question) which allocates 
    'ImageData and then uses Array.Copy to store a copy of the pixel values 
    'in it. It also assigns the proper values to TheWidth and TheHeight. 

    TheBitmap = New System.Drawing.Bitmap(TheWidth, TheHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb) 
    Dim TheData As System.Drawing.Imaging.BitmapData = TheBitmap.LockBits(
     New System.Drawing.Rectangle(0, 0, TheWidth, TheHeight), Drawing.Imaging.ImageLockMode.ReadWrite, TheBitmap.PixelFormat) 
    Dim Image24(Math.Abs(TheData.Stride) * TheHeight) As Byte 
    'Then we have two choices: to do it in parallel or not. I tried both; the sequential version is commented out below. 
    System.Threading.Tasks.Parallel.For(0, ImageData.Length, Sub(i) 
                   Image24(i * 3 + 0) = ImageData(i) 
                   Image24(i * 3 + 1) = ImageData(i) 
                   Image24(i * 3 + 2) = ImageData(i) 
                  End Sub) 
    'Dim i As Integer 
    'For i = 0 To ImageData.Length - 1 
    ' Image24(i * 3 + 0) = ImageData(i) 
    ' Image24(i * 3 + 1) = ImageData(i) 
    ' Image24(i * 3 + 2) = ImageData(i) 
    'Next 
    System.Runtime.InteropServices.Marshal.Copy(Image24, 0, TheData.Scan0, Image24.Length) 
    TheBitmap.UnlockBits(TheData) 

    'The above based on 
    'http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx 

    Return 1 

End Function 

Đối với một hình ảnh 8 MPixel phiên bản tuần tự tiêu thụ khoảng 220 mili giây từ việc tạo TheBitmap lên đến (và bao gồm) cuộc gọi đến TheBitmap.UnlockBits(). Phiên bản song song có nhiều mâu thuẫn hơn nhưng nằm trong khoảng từ 60 đến 80 mili giây. Xem xét tôi đang mua từ bốn máy ảnh cùng một lúc và streaming vào đĩa với nhiều thời gian để rảnh rỗi, điều này là khá đáng thất vọng. API đi kèm với mã mẫu trong C++ có thể hiển thị từ tất cả bốn máy ảnh cùng một lúc ở tốc độ khung hình đầy đủ (17 khung hình/giây). Nó không bao giờ giao dịch với Bitmap, tất nhiên, kể từ khi GDI truy cập mảng các giá trị pixel nguyên.

Tóm lại, tôi phải đi qua hàng rào được quản lý/không được quản lý đơn giản vì không có cách nào trực tiếp để lấy một mảng giá trị pixel và "công cụ" vào một ví dụ Bitmap. Trong trường hợp này, tôi cũng sao chép các giá trị pixel thành 24bpp Bitmap vì tôi không thể "đặt" bảng màu trong một ảnh được lập chỉ mục để 8bpp không phải là định dạng khả thi để hiển thị.

Không có cách nào tốt hơn?

Trả lời

10

Tạo valid bitmap header và đăng ký nó vào mảng byte. Bạn sẽ chỉ cần tạo 56-128 byte dữ liệu theo cách thủ công.

Thứ hai, create a stream from a byte array.

Tiếp theo, create an image or bitmap class by using Image.FromStream()/Bitmap.FromStream()

tôi sẽ không tưởng tượng được rằng có bất kỳ loại tính năng chụp ảnh thô trong Net. Có rất nhiều thông tin cần thiết cho một hình ảnh, nó sẽ là một danh sách tham số cực kỳ dài cho một phương thức chấp nhận các byte thô và tạo ra một hình ảnh (đó là bitmap, jpeg, gif, png, v.v ...) và tất cả dữ liệu bổ sung những người yêu cầu để tạo ra một hình ảnh hợp lệ) nó sẽ không có ý nghĩa.

+0

FromStream này đòi hỏi rằng các dòng chứa một "đầy đủ" bitmap (với thông tin tiêu đề), không chỉ giá trị pixel --- Tôi đã cố gắng để giải thích điều này trong câu hỏi của tôi. – pelesl

+3

Cập nhật với tham chiếu đến thông tin tiêu đề bmp. Pre-appending một số 128 byte sau đó sử dụng từ dòng nên được substanically nhanh hơn. –

+1

Thật đáng để thử, đặc biệt nếu nó sẽ cho phép tôi thêm một tiêu đề bitmap với một bảng màu xám vì vậy tôi không phải sao chép các giá trị pixel 3 lần để nhận được 24 bpp. – pelesl

4

Có vẻ như tất cả các máy ảnh của bạn cùng loại, vì vậy dữ liệu hình ảnh. Tôi không biết nếu điều này là có thể trong trường hợp của bạn, nhưng bạn có thể tạo ra một "bitmap đầy đủ" từ bất kỳ hình ảnh và sau đó chỉ cần sử dụng tiêu đề như là một mẫu (vì nó sẽ giống nhau cho tất cả các hình ảnh).

+1

+1 Đó là một ý tưởng tuyệt vời! –

6

Hãy thử

var img = new Bitmap(new MemoryStream(yourByteArray)); 
+1

Tôi nghĩ bạn không phải khai báo nó là "var". Bạn có thể tuyên bố nó là "Bitmap" thay vào đó, phải không? Nhưng câu trả lời là tuyệt vời, tất nhiên! – tmighty

+0

Tất nhiên bạn có thể. RTM https://msdn.microsoft.com/ru-ru/library/bb383973.aspx – Cheburek

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