【发布时间】:2016-11-26 07:56:00
【问题描述】:
TL;博士:我有byte[]。我想要Bgra32Pixel[]。不想抄袭。如果需要复制,我想要尽可能快的优化复制,不复制单个字节。有可能吗?
完整说明:
结构如下:
/// <summary>
/// Represents a PixelFormats.Bgra32 format pixel
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct Bgra32Pixel {
[FieldOffset(0)]
public readonly int Value;
[FieldOffset(0)]
public byte B;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte R;
[FieldOffset(3)]
public byte A;
}
我有一个字节数组,我们称之为data。我想以Bgra32Pixel[] 的身份访问它。
内存中的相同字节。是否需要为它复制字节?
我希望这样的事情能奏效:
var pixels = data as Bgra32Pixel[];
但事实并非如此。最快的方法是什么?
我的猜测是使用索引器直接从原始 byte[] 引用返回 Bgra32Pixel 来制作自定义类型。但这不会很快。不需要复制,但每次访问实际上都会从 4 个字节创建一个新结构。不,似乎不必要的慢。一定有办法让 C# 认为 byte[] 在某种程度上是 Bgra32Pixel[]。
这是我在阅读所有答案后找到的解决方案:
TL;DR:不需要结构。
转换为 struct 需要不安全的上下文和固定的语句。这对性能没有好处。这是从位图中删除背景的代码,它假定左上角的像素具有背景颜色。此代码在每个像素上调用特殊的“颜色到 alpha”巫毒:
/// <summary>
/// Extensions for bitmap manipulations.
/// </summary>
static class BitmapSourceExtensions {
/// <summary>
/// Removes the background from the bitmap assuming the first pixel is background color.
/// </summary>
/// <param name="source">Opaque bitmap.</param>
/// <returns>Bitmap with background removed.</returns>
public static BitmapSource RemoveBackground(this BitmapSource source) {
if (source.Format != PixelFormats.Bgr32) throw new NotImplementedException("Pixel format not implemented.");
var target = new WriteableBitmap(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, PixelFormats.Bgra32, null);
var pixelSize = source.Format.BitsPerPixel / 8;
var pixelCount = source.PixelWidth * source.PixelHeight;
var pixels = new uint[pixelCount];
var stride = source.PixelWidth * pixelSize;
source.CopyPixels(pixels, stride, 0);
var background = new LABColor(pixels[0]);
for (int i = 0; i < pixelCount; i++) pixels[i] &= background.ColorToAlpha(pixels[i]);
var bounds = new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight);
target.WritePixels(bounds, pixels, stride, 0);
return target;
}
}
如果你很好奇,使用的voodoo类是什么,这里:
/// <summary>
/// CIE LAB color space structure with BGRA pixel support.
/// </summary>
public struct LABColor {
/// <summary>
/// Lightness (0..100).
/// </summary>
public readonly double L;
/// <summary>
/// A component (0..100)
/// </summary>
public readonly double A;
/// <summary>
/// B component (0..100)
/// </summary>
public readonly double B;
/// <summary>
/// Creates CIE LAB color from BGRA pixel.
/// </summary>
/// <param name="bgra">Pixel.</param>
public LABColor(uint bgra) {
const double t = 1d / 3d;
double r = ((bgra & 0x00ff0000u) >> 16) / 255d;
double g = ((bgra & 0x0000ff00u) >> 8) / 255d;
double b = (bgra & 0x000000ffu) / 255d;
r = (r > 0.04045 ? Math.Pow((r + 0.055) / 1.055, 2.4) : r / 12.92) * 100d;
g = (g > 0.04045 ? Math.Pow((g + 0.055) / 1.055, 2.4) : g / 12.92) * 100d;
b = (b > 0.04045 ? Math.Pow((b + 0.055) / 1.055, 2.4) : b / 12.92) * 100d;
double x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 95.047;
double y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 100.000;
double z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 108.883;
x = x > 0.0088564516790356311 ? Math.Pow(x, t) : (903.2962962962963 * x + 16d) / 116d;
y = y > 0.0088564516790356311 ? Math.Pow(y, t) : (903.2962962962963 * y + 16d) / 116d;
z = z > 0.0088564516790356311 ? Math.Pow(z, t) : (903.2962962962963 * z + 16d) / 116d;
L = Math.Max(0d, 116d * y - 16d);
A = 500d * (x - y);
B = 200d * (y - z);
}
/// <summary>
/// Calculates color space distance between 2 CIE LAB colors.
/// </summary>
/// <param name="c">CIE LAB color.</param>
/// <returns>A color space distance between 2 colors from 0 (same colors) to 100 (black and white)</returns>
public double Distance(LABColor c) {
double dl = L - c.L;
double da = A - c.A;
double db = B - c.B;
return Math.Sqrt(dl * dl + da * da + db * db);
}
/// <summary>
/// Calculates bit mask for alpha calculated from difference between this color and another BGRA color.
/// </summary>
/// <param name="bgra">Pixel.</param>
/// <returns>Bit mask for alpha in BGRA pixel format.</returns>
public uint ColorToAlpha(uint bgra) => 0xffffffu | ((uint)(Distance(new LABColor(bgra)) * 2.55d) << 24);
}
我正在回馈社区。我在 StackOverflow 和 Github 上找到了所有必要的数学。 我猜 GIMP 正在使用非常相似的东西来实现“颜色到 alpha”的效果。
问题仍然悬而未决:有没有更快的方法来做到这一点?
【问题讨论】:
-
字节数组的格式是什么?是不是已经序列化的Bgra32Pixel?或者可能是一个字节数组,其值恰好以特定顺序与 B/G/R/A 字段啮合?还是字节数组是指向内存中包含 Bgra32Pixel 数据的地址的指针?还是……?
-
一些样本数据对于获得正确答案大有帮助。
-
我知道如何将字节复制到结构中,但我问如何将字节数组转换为结构数组。最好不要抄袭。是的,我想做这种不安全的方式,因为我的字节数组是严格定义的格式,这不足为奇。
-
我不明白你的问题,你已经有一个类似 Bgra32Pixel[] 的变量,它是 p1,p1 是你的 Bgra32Pixel 数组。你可以做 p1[0].B = 45;
标签: c#