简而言之,我有两张图像,我想用蒙版将其中一张覆盖在另一张上,这样只有第二张图像的一部分显示出来。这是实时图像处理例程的一部分,所以我需要操作尽可能快地发生。
具体来说,我有两个32位图像BGR字节数组。此外,我有一个字节数组,表示一个图像掩码。
我想生成一个新的字节数组,其中字节数组a覆盖在字节数组B的顶部,使用掩码数组来决定使用哪个字节。
做这件事最快的方法是什么?
我正在看这篇关于老式精灵掩蔽的维基百科文章,但我不确定如何最好地将其翻译为c#。http://en.wikipedia.org/wiki/Mask_(计算)
编辑:我忘了说,我可以重组任何或所有这些,使它运行得更快。
我可能误解了这个问题,但是:您有两个int数组和一个相同大小的掩码数组,将相应的掩码应用于两个图像数组。如果掩码数组中的位为0,则从a中选择该位;当为1时,选择b中的位。
例如,如果数组的第一个元素中有值
a[0] : 0000 1111
b[0] : 0011 0011
mask[0] : 0101 0101
那么最终结果将是:
dest[i] : 0001 1011
可以表示为:
dest[i] = (a[i] AND (NOT MASK[i]))
OR
(b[i] AND MASK[i])
或者在c#中:
dest[i] = (a[i] & ~mask[i]) | (b[i] & mask[i]);
如果这是目的,那么你可以只运行一个循环:
for (int i = 0; i < len; i++)
{
dest[i] = (a[i] & ~mask[i]) | (b[i] & mask[i]);
}
最后,在性能方面,有一点要注意:您提到创建一个新数组来保存目标图像。如果映像非常大,那么不断创建大型数组可能会成为瓶颈。相反,如果可能的话,只创建一次目标数组,并在需要时重用它。此外,如果您确实需要分配大量的字节数组供临时使用(例如,在一个方法中),您可能需要考虑使用stackalloc操作符在堆栈上分配空间,这可能比创建新的托管数组更有效。
原则上,这样的屏蔽是非常简单的——假设数组大小相同,下面的代码可以做到:
static void MaskImage(byte[] background, byte[] foreground, byte[] mask)
{
for (var i = 0; i < background.Length; ++i)
{
background[i] &= mask[i];
background[i] |= foreground[i];
}
}
在现实中,这将是相当复杂的一点-你的背景可能比你所掩盖的前景图像更大,你必须做一些算术来正确地放置它。
第三方工具是可行的。我用过Lead Tools,但还有很多其他的工具。
如果你想自己做,你可以使用LockBits方法。
LockBits的最佳演示在这里:https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx
这段代码不能处理单独的字节数组,但是我曾经处理过一个需要将一个图像覆盖在另一个图像上的项目。也许这对你有帮助。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
public static Bitmap OverlayImage(Image background, Image foreground, Point foregroundLocation, Size foregroundSize, float opacity, bool resizeBackground)
{
Bitmap returnValue = null;
Graphics returnValueGraphics = null;
int backgroundHeight = background.Height;
Point backgroundLocation = new Point(0, 0);
int backgroundWidth = background.Width;
int foregroundHeight = foreground.Height;
int foregroundWidth = foreground.Width;
int newHeight = 0;
int newWidth = 0;
if (false == resizeBackground)
{
newHeight = backgroundHeight;
newWidth = backgroundWidth;
}
else
{
if (foregroundLocation.X < 0)
{
backgroundLocation = new Point(Math.Abs(foregroundLocation.X), backgroundLocation.Y);
foregroundLocation = new Point(0, foregroundLocation.Y);
}
if (foregroundLocation.Y < 0)
{
backgroundLocation = new Point(backgroundLocation.X, Math.Abs(foregroundLocation.Y));
foregroundLocation = new Point(foregroundLocation.X, 0);
}
newHeight = Math.Max((backgroundLocation.Y + backgroundHeight), (foregroundLocation.Y + Math.Min (foregroundHeight, foregroundSize.Height)));
newWidth = Math.Max((backgroundLocation.X + backgroundWidth), (foregroundLocation.X + Math.Min (foregroundWidth, foregroundSize.Width)));
}
returnValue = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);
returnValue.SetResolution(background.HorizontalResolution, background.VerticalResolution);
returnValueGraphics = Graphics.FromImage(returnValue);
returnValueGraphics.SmoothingMode = SmoothingMode.AntiAlias;
returnValueGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
returnValueGraphics.DrawImage(
background
, new Rectangle(backgroundLocation, background.Size)
, 0
, 0
, backgroundWidth
, backgroundHeight
, GraphicsUnit.Pixel
);
ImageAttributes imageAttributes = new ImageAttributes();
float[][] colorMatrixElements = {
new float[] {1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, opacity, 0.0f},
new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};
ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);
imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
returnValueGraphics.DrawImage(
foreground
, new Rectangle(foregroundLocation, foregroundSize)
, 0
, 0
, foreground.Width
, foreground.Height
, GraphicsUnit.Pixel
, imageAttributes
);
returnValueGraphics.Dispose();
return (returnValue);
}
代码也可在:https://pastebin.com/KXavA9Jr