Быстрый Resize Bitmap C#

132
11 ноября 2019, 01:50

камрады!

Суть проблемы: нужно в интерактивном режиме ресайзить Bitmap порядка 3*4 тыщи пикселей или даже больше. Грубо говоря, зум.

Пробовал:

scaled = new Bitmap(baseImage, newSize);

Второй вариант:

Bitmap scaled = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage((Image)scaled))
{
  g.DrawImage(baseImage, 0, 0, size.Width, size.Height);
}
return scaled;

И разные вариации, но толку чуть меньше чем ноль. Единственное, что даёт прирост производительности - подключить OpenCV и использовать их ресайз, но таскать за собой такой хвост - идея не лучшая.

Возможно есть существующие библиотеки как на чистом C# или обёртки (у меня WinForms .net 3.5 проект) полегче того же OpenCV... ТОП Nugeta - обёртки над тем же new Bitmap и результата не дают. Или я плохо искал. В общем, я открыт для предложений.

UPD: информация к размышлению. У меня уже черепушка плавится.

Запускаю вот такой код с отрисовкой получившегося изображения прямо на контрол. Всё летает.

//прицеплено на Form1.paint
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
    g.DrawImage(baseImage, new RectangleF(0, 0, baseImage.Width * Scale, baseImage.Height * Scale));
}

Но! Если получать Graphics объект не из PaintEventArgs, а из Graphics.FromImage(baseImage), то скорость работы строчки

g.DrawImage(baseImage, new RectangleF(0, 0, width, height));

разнится в разы. Вот это-то и смущает. Мне нужно не просто отрисовать изображение на контрол, а отрисовать его именно в конкретный объект. То есть, код:

scaledImage.Dispose();
scaledImage = new Bitmap(width, height);
var g = Graphics.FromImage(scaledImage);
g.DrawImage(baseImage, new RectangleF(0, 0, width, height));

Работает примерно в 10-12 раз медленнее кода:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
   var g = e.Graphics;
   g.DrawImage(baseImage, new RectangleF(0, 0, baseImage.Width * Scale, baseImage.Height * Scale));
   pictureBox1.Invalidate();
}

Здесь идёт отрисовка на контрол сразу и выполняется это почти моментально. Но мне нужно записать всё в Bitmap объект.

Самое забавное, что если перейти внутрь g.DrawImage(), то в обоих случаях вызывается один и тот же метод DrawImage из System.Drawing, но при одном объекте Graphics он летает, а при том, что мне нужно - полученном через Graphics.FromImage() - тормозит

Answer 1

Может быть выгодно разбить битмап на прямоугольные части.

Изображение такого размера ведь в большинстве случаев целиком не показывают, а масштабирование одной или, скажем, четырёх из 16 частей явно быстрее, чем всего большого битмапа.

Кроме того, можно предрассчитать - или заранее, или в начале работы программы - несколько копий в других масштабах.

Для показа целой картинки на стандартных мониторах отображаем готовую копию с масштабом 0.5 или 0.25. Если масштаб промежуточный - можно из тех же уменьшенных сделать.

Для увеличенного масштаба нарезанные куски хорошо подойдут.

Answer 2

Может, я что-то упустил, но почему-бы не использовать функционал доступный из коробки?

Ты используешь Winforms. У него есть PictureBox. У него есть режим Zoom. Просто меняешь скролом размер этого PictureBox и получаешь зум. Перемещение по картинке -- реализуй перетягивание PictureBox внутри некой панели по клику.

Ни тормозов не должно быть ни излишних ресайзов оригинального битмапа.

READ ALSO
Вывод всех элементов XElements

Вывод всех элементов XElements

Имею XML с сложной структурой (CommerceML) и нужно выпарсить некоторые значения оттуда

123
Roslyn ошибка сборки System.Runtime

Roslyn ошибка сборки System.Runtime

Коллеги, добрый день!

119
Sql запрос с параметрами в цикле для добавления данных в базу

Sql запрос с параметрами в цикле для добавления данных в базу

Сделал такую конструкцию, для добавления данных в базу, вопрос, Правильно ли написан запрос, и если учитывать то, что это цикл, будет ли эффективным...

129