In a nutshell I have two images I want to overlay one over the other using a mask so that only parts of the second image show up. This is part of a real time image processing routine so I need the operation to happen as fast as possible.
Specifically, I have two 32 bit image BGR byte arrays. In addition, I have a byte array that represents an image mask.
I want to generate a new byte array where byte array A is overlayed on top of byte array B using the mask array to decide which byte is used.
What is the quickest way to do this?
I was looking at this wikipedia article about old fashioned sprite masking but I am not sure how to best translate this to C#. http://en.wikipedia.org/wiki/Mask_(computing)
EDIT: I forgot to mention 开发者_StackOverflowthat I can restruct any or all of this to make it run faster.
I might have misunderstood the question, but: you have two int arrays and a mask aray of the same size that applies the corresponding mask to both image arrays. Where a bit in the mask array is 0, you select the bit from A; when 1, you select the bit from b.
So if, for example, you had values in the first array element of:
a[0] : 0000 1111
b[0] : 0011 0011
mask[0] : 0101 0101
Then the destination result would be:
dest[i] : 0001 1011
This can be expressed as:
dest[i] = (a[i] AND (NOT MASK[i]))
OR
(b[i] AND MASK[i])
Or, in C#:
dest[i] = (a[i] & ~mask[i]) | (b[i] & mask[i]);
If this is the intent, then you can just run as a loop:
for (int i = 0; i < len; i++)
{
dest[i] = (a[i] & ~mask[i]) | (b[i] & mask[i]);
}
Finally, performance-wise, one note: you mention creating a new array to hold the destination image. If the images are of significant size, constantly creating large arrays can be a bottleneck. Instead, if possible, create the destination array once and reuse it as needed. Also, if you do need to allocate large numbers of byte arrays for transient use (e.g., within a method), you might want to consider using the stackalloc operator to allocate space on the stack, which can be more efficient than creating new managed arrays.
In principle, masking like this is very simple - assuming the arrays are the same size, this code would do it:
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];
}
}
In a reality this will be quite a bit more complicated - your background is likely to be larger than whatever foreground image you're masking onto it, and you'll have to do some arithmetic to place it properly. While tedious to get right, it's not hard.
Third party tools are the way to go. I've used Lead Tools but there are many others.
If you want to do it yourself, you can use the LockBits method.
The best demonstration of LockBits is here: https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx
This code doesn't work with individual byte arrays, but I worked on a project that required overlaying one image over another. Maybe this will help you.
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);
}
Code also available at: https://pastebin.com/KXavA9Jr
精彩评论