Sau đây là cách tôi sẽ thao tác pixel trong C# sử dụng mảng đa chiều:
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
public PixelColor[,] GetPixels(BitmapSource source)
{
if(source.Format!=PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
int width = source.PixelWidth;
int height = source.PixelHeight;
PixelColor[,] result = new PixelColor[width, height];
source.CopyPixels(result, width * 4, 0);
return result;
}
sử dụng:
var pixels = GetPixels(image);
if(pixels[7, 3].Red > 4)
...
Nếu bạn muốn cập nhật pixel, rất giống mã số hoạt động ngoại trừ bạn sẽ tạo ra một WritableBitmap
và sử dụng điều này:
public void PutPixels(WritableBitmap bitmap, PixelColor[,] pixels, int x, int y)
{
int width = pixels.GetLength(0);
int height = pixels.GetLength(1);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width*4, x, y);
}
do đó:
var pixels = new PixelColor[4, 3];
pixel[2,2] = new PixelColor { Red=128, Blue=0, Green=255, Alpha=255 };
PutPixels(bitmap, pixels, 7, 7);
Lưu ý rằng mã này chuyển đổi bitmap thành Bgra32 nếu chúng đến định dạng khác. Điều này nói chung là nhanh, nhưng trong một số trường hợp có thể là một nút cổ chai hiệu suất, trong trường hợp này, kỹ thuật này sẽ được sửa đổi để phù hợp với định dạng đầu vào cơ bản chặt chẽ hơn.
Cập nhật
Kể từ BitmapSource.CopyPixels
không chấp nhận một mảng hai chiều nó là cần thiết để chuyển đổi mảng giữa một chiều và hai chiều. Phương pháp mở rộng sau đây sẽ làm các trick:
public static class BitmapSourceHelper
{
#if UNSAFE
public unsafe static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
fixed(PixelColor* buffer = &pixels[0, 0])
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
(IntPtr)(buffer + offset),
pixels.GetLength(0) * pixels.GetLength(1) * sizeof(PixelColor),
stride);
}
#else
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
var height = source.PixelHeight;
var width = source.PixelWidth;
var pixelBytes = new byte[height * width * 4];
source.CopyPixels(pixelBytes, stride, 0);
int y0 = offset/width;
int x0 = offset - width * y0;
for(int y=0; y<height; y++)
for(int x=0; x<width; x++)
pixels[x+x0, y+y0] = new PixelColor
{
Blue = pixelBytes[(y*width + x) * 4 + 0],
Green = pixelBytes[(y*width + x) * 4 + 1],
Red = pixelBytes[(y*width + x) * 4 + 2],
Alpha = pixelBytes[(y*width + x) * 4 + 3],
};
}
#endif
}
Có hai triển khai ở đây: Đầu tiên là nhanh chóng nhưng sử dụng mã không an toàn để có được một IntPtr đến một mảng (phải biên dịch với/tùy chọn không an toàn). Cách thứ hai chậm hơn nhưng không yêu cầu mã không an toàn. Tôi sử dụng phiên bản không an toàn trong mã của mình.
WritePixels chấp nhận mảng hai chiều, do đó không có phương pháp mở rộng là bắt buộc.
Vì mục đích gì? Và tại sao bạn thêm 7 và chia cho 8 trong tính toán nStride? – Jviaches
@Jviaches Thêm 7 và chia cho 8 để chính xác vòng thành đủ byte (f.i. 10 bit sẽ cần 2 byte.) – erikH