Как объединить много изображений JPG в одно большое (огромное) изображение, используя C # - PullRequest
0 голосов
/ 14 июня 2019

У меня есть сотни изображений в формате JPG, и я хочу объединить их в один большой JPG. И для этого я использую следующий код:

using (var combinedBitmap = new Bitmap(combinedWidth, combinedHeights)) {
    combinedBitmap.SetResolution(96, 96);
    using (var g = Graphics.FromImage(combinedBitmap))
    {
        g.Clear(Color.White);

        foreach (var imagePiece in imagePieces)
        {
            var imagePath = Path.Combine(slideFolderPath, imagePiece.FileName);

            using (var image = Image.FromFile(imagePath)) 
            {
                var x = columnXs[imagePiece.Column];
                var y = rowYs[imagePiece.Row];

                g.DrawImage(image, new Point(x, y));
             }
        }
    }

    combinedBitmap.Save(combinedImagePath, ImageFormat.Jpeg);
}

Все в порядке, пока размеры (combinedWidth, combinedHeights) не превысят пороговое значение шторки, как здесь сказано https://stackoverflow.com/a/29175905/623190

Объединенный файл JPG с размерами 23170 x 23170 пикселей составляет около 50 МБ - не слишком большой, чтобы уничтожить память.

Но растровое изображение не может быть создано с большими размерами - просто разрывается с неправильным исключением параметра.

Есть ли другой способ объединить фрагменты изображения JPG в один большой JPG с размерами, превышающими 23170 x 23170, с использованием C #?

1 Ответ

2 голосов
/ 20 июня 2019

Вот решение, использующее libvips .Это библиотека обработки потоковых изображений, поэтому вместо того, чтобы манипулировать огромными объектами в памяти, она строит конвейеры, а затем запускает их параллельно, передавая изображения в виде последовательности небольших областей.

В этом примере используется net-vips , привязка C # для libvips.

using System;
using System.Linq;
using NetVips;

class Merge
{
    static void Main(string[] args)
    {
        if (args.Length < 2)
        {
            Console.WriteLine("Usage: [output] [images]");
            return;
        }

        var image = Image.Black(60000, 60000);
        var random = new Random();

        foreach (var filename in args.Skip(1))
        {
            var tile = Image.NewFromFile(filename, access: "sequential");

            var x = random.Next(0, image.Width - tile.Width);
            var y = random.Next(0, image.Height - tile.Height);

            image = image.Insert(tile, x, y);
        }

        image.WriteToFile(args[0]);
    }
}

Я создал набор из 1000 изображений jpg, каждое из которых имеет размер 1450 x 2048 RGB, используя:

for ($i = 0; $i -lt 1000; $i++)
{
    # https://commons.wikimedia.org/wiki/File:Taiaroa_Head_-_Otago.jpg
    Copy-Item "$PSScriptRoot\..\Taiaroa_Head_-_Otago.jpg" -Destination "$PSScriptRoot\$i.jpg"
}

Чтобы измерить время, необходимое для выполнения приведенного выше кода, я использовал встроенную в PowerShell «Measure-Command» (которая аналогична команде Bash «time»):

$fileNames = (Get-ChildItem -Path $PSScriptRoot -Recurse -Include *.jpg).Name
$results = Measure-Command { dotnet Merge.dll x.jpg $fileNames }
$results | Format-List *

С подготовленными изображениями и приведенным выше сценарием я вижу следующее:

C:\merge>.\merge.ps1
Ticks             : 368520029
Days              : 0
Hours             : 0
Milliseconds      : 852
Minutes           : 0
Seconds           : 36
TotalDays         : 0.000426527811342593
TotalHours        : 0.0102366674722222
TotalMilliseconds : 36852.0029
TotalMinutes      : 0.614200048333333
TotalSeconds      : 36.8520029

Итак, на моем ПК с ОС Intel Core i5 8-го поколения он объединил 1000 изображений JPG в большое изображение JPG 60k x 60k в 36секунд.

...