我试图将多个图像合并为一个图像。问题是大多数具有此类功能的库在Windows 8.1应用程序中不可用。我更喜欢不必使用外部库,如WriteableBitmapEx
这是我目前的代码,遗憾的是,它不起作用:
int count = 4;
int size = 150;
WriteableBitmap destination = new WriteableBitmap(300, 300);
BitmapFrame frame = await (await BitmapDecoder.CreateAsync(randomAccessStream)).GetFrameAsync(0);
PixelDataProvider pixelData = await frame.GetPixelDataAsync();
byte[] test = pixelData.DetachPixelData();
MemoryStream mem = new MemoryStream();
for (int row = 0; row < frame.PixelHeight; row++) {
for (int i = 0; i < count; i++)
{
mem.Write(test, row * (int)frame.PixelWidth * 4, (int)frame.PixelWidth * 4);
}
}
mem.Seek(0, SeekOrigin.Begin);
BitmapImage bmp = new BitmapImage();
bmp.SetSourceAsync(mem.AsRandomAccessStream());
如果我将bmp设置为Image UIElement的源,则不会发生任何事情。 我的想法是将Pixeldata作为字节数组并逐行写入(每个图像的像素行,使它们彼此相邻)到一个内存流,然后将其用作内存流的源。的BitmapImage。
感谢Aditya和Romasz,我可以解决这个问题。 问题是我必须将像素数据编码回图像。
如果有人有相同的问题,下面的类合并多个图像的像素数据并返回一个BitmapImage:
public class ImageMerger
{
public static async Task<BitmapImage> MergeImages(int singleWidth, int singleHeight, params byte[][] pixelData)
{
int perRow = (int) Math.Ceiling(Math.Sqrt(pixelData.Length));
byte[] mergedImageBytes = new byte[singleHeight * singleWidth * perRow * perRow * 4];
for (int i = 0; i < pixelData.Length; i++ )
{
LoadPixelBytesAt(ref mergedImageBytes, pixelData[i], (i % perRow) * singleWidth, (i / perRow) * singleHeight, perRow * singleWidth, singleWidth, singleHeight);
}
InMemoryRandomAccessStream mem = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, mem);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)(singleHeight * perRow), (uint)(singleWidth * perRow), 91, 91, mergedImageBytes);
await encoder.FlushAsync();
BitmapImage bmp = new BitmapImage();
bmp.SetSourceAsync(mem);
return bmp;
}
private static void LoadPixelBytesAt(ref byte[] dest, byte[] src, int destX, int destY, int destW, int srcW, int srcH)
{
for (int i = 0; i < srcH; i++)
{
for (int j = 0; j < srcW; j++)
{
if (src.Length < ((i * srcW + j + 1) * 4)) return;
for (int p = 0; p < 4; p++)
dest[((destY + i) * destW + destX + j) * 4 + p] = src[(i * srcW + j) * 4 + p];
}
}
}
}
这会拍摄任意数量的图像并将它们彼此相邻,从左到右依次为自上而下的图像。 即对于4个图像,它将返回一个图像,它们像这样对齐:
1 2
3 4
适用于我的所有图片但只有一张。有一个图像在与其他图像合并后看起来很奇怪。没有找出原因。
答案 0 :(得分:2)
这应该这样做:
byte[] PutOnCanvas(byte[] Canvas,byte[] Image,uint x,uint y,uint imageheight,uint imagewidth,uint CanvasWidth)
{
for (uint row = y; row < y+imageheight; row++)
for (uint col = x; col < x+imagewidth; col++)
for (int i = 0; i < 4; i++)
Canvas[(row * CanvasWidth + col) * 4 + i] = Image[((row-y) * imagewidth + (col - x)) * 4 + i];
return Canvas;
}
现在说我想并排放置30x30的两个图像(Image1和Image2中的像素字节),它们之间的垂直边距为10px。我会用以下方式调用该函数:
byte[] Canvas = new byte[30 * 70 * 4];
Canvas=PutOnCanvas(Canvas,Image1,0,0,30,30,70);
Canvas=PutOnCanvas(Canvas,Image2,40,0,30,30,70);
然后将像素字节转换为BMP,你应该完成!
编辑: 这是将像素字节转换为图像的正确方法:
memStream.Size = 0;
var encoder = await BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId, memStream);
encoder.SetPixelData(
Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,
Windows.Graphics.Imaging.BitmapAlphaMode.Straight,
CanvasWidth, // pixel width
CanvasHeight, // pixel height
96, // horizontal DPI
96, // vertical DPI
PixelData);
try { await encoder.FlushAsync(); }
catch { }
memStream.Dispose();
答案 1 :(得分:0)
前一段时间尝试过这种方法,它对我有用。 http://www.codeproject.com/Articles/502249/Combineplusseveralplusimagesplustoplusformplusaplu
答案 2 :(得分:0)
一种选择是像往常一样在Canvas中绘制它们然后渲染Canvas。唯一的问题是它们必须同时出现在屏幕上。
不幸的是,就没有类似WriteableBitmapEx
之类的简单解决方案而言,这就是它。他们的BitmapContext
类抽象了更改图像宽度时发生的许多更复杂的数学运算。你可以在这里查看WinRTXamlToolkit
的blit
实现,但是它有一个限制,即源文件和目标文件必须是相同的宽度(由于烦人的数学运算)。
一个选项可能是尝试增加图像的大小而不进行缩放,希望在适当的位置创建一些空白,然后使用blit
实现的传真将它们分层,但这似乎会也很麻烦。
你最好的选择,IMO,是要删除你需要的WriteableBitmapEx
块,特别是它们提供的BitmapContext和Blit Extensions,然后创建一个空白图像并将每个图像叠加到目标图像上(正如你现在所做的那样)。
这不是法律建议。
WriteableBitmapEx是Microsoft许可证,非常宽松,所以你应该可以这样做。
无论如何,添加引用可能更容易,但是如果你不需要,你仍然可以删除你需要的部分(在这种情况下)并使用它们“点菜”