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 SetPixel
là LockBits
, 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?
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
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. –
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