合并多个图像

时间:2014-05-14 12:41:19

标签: c# windows-runtime bitmapimage windows-phone-8.1

我试图将多个图像合并为一个图像。问题是大多数具有此类功能的库在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

适用于我的所有图片但只有一张。有一个图像在与其他图像合并后看起来很奇怪。没有找出原因。

3 个答案:

答案 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)

答案 2 :(得分:0)

一种选择是像往常一样在Canvas中绘制它们然后渲染Canvas。唯一的问题是它们必须同时出现在屏幕上。

不幸的是,就没有类似WriteableBitmapEx之类的简单解决方案而言,这就是它。他们的BitmapContext类抽象了更改图像宽度时发生的许多更复杂的数学运算。你可以在这里查看WinRTXamlToolkitblit实现,但是它有一个限制,即源文件和目标文件必须是相同的宽度(由于烦人的数学运算)。

一个选项可能是尝试增加图像的大小而不进行缩放,希望在适当的位置创建一些空白,然后使用blit实现的传真将它们分层,但这似乎会也很麻烦。

你最好的选择,IMO,是要删除你需要的WriteableBitmapEx块,特别是它们提供的BitmapContext和Blit Extensions,然后创建一个空白图像并将每个图像叠加到目标图像上(正如你现在所做的那样)。

这不是法律建议。

WriteableBitmapEx是Microsoft许可证,非常宽松,所以你应该可以这样做。

无论如何,添加引用可能更容易,但是如果你不需要,你仍然可以删除你需要的部分(在这种情况下)并使用它们“点菜”