Изменение размера прозрачных изображений с помощью C#

StackOverflow https://stackoverflow.com/questions/30569

Вопрос

Есть ли у кого-нибудь секретная формула изменения размера прозрачных изображений (в основном GIF-файлов) без ЛЮБАЯ потеря качества - что тогда?

Я перепробовал кучу вещей, но самое близкое, что у меня получилось, оказалось недостаточно хорошим.

Взгляните на мое основное изображение:

http://www.thewallcompany.dk/test/main.gif

И затем масштабированное изображение:

http://www.thewallcompany.dk/test/ScaledImage.gif

//Internal resize for indexed colored images
void IndexedRezise(int xSize, int ySize)
{
  BitmapData sourceData;
  BitmapData targetData;

  AdjustSizes(ref xSize, ref ySize);

  scaledBitmap = new Bitmap(xSize, ySize, bitmap.PixelFormat);
  scaledBitmap.Palette = bitmap.Palette;
  sourceData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
    ImageLockMode.ReadOnly, bitmap.PixelFormat);
  try
  {
    targetData = scaledBitmap.LockBits(new Rectangle(0, 0, xSize, ySize),
      ImageLockMode.WriteOnly, scaledBitmap.PixelFormat);
    try
    {
      xFactor = (Double)bitmap.Width / (Double)scaledBitmap.Width;
      yFactor = (Double)bitmap.Height / (Double)scaledBitmap.Height;
      sourceStride = sourceData.Stride;
      sourceScan0 = sourceData.Scan0;
      int targetStride = targetData.Stride;
      System.IntPtr targetScan0 = targetData.Scan0;
      unsafe
      {
        byte* p = (byte*)(void*)targetScan0;
        int nOffset = targetStride - scaledBitmap.Width;
        int nWidth = scaledBitmap.Width;
        for (int y = 0; y < scaledBitmap.Height; ++y)
        {
          for (int x = 0; x < nWidth; ++x)
          {
            p[0] = GetSourceByteAt(x, y);
            ++p;
          }
          p += nOffset;
        }
      }
    }
    finally
    {
      scaledBitmap.UnlockBits(targetData);
    }
  }
  finally
  {
    bitmap.UnlockBits(sourceData);
  }
}

Я использую приведенный выше код для индексированного изменения размера.

Есть ли у кого-нибудь идеи по улучшению?

Это было полезно?

Решение

Если нет необходимости сохранять тип файла после масштабирования, я бы рекомендовал следующий подход.

using (Image src = Image.FromFile("main.gif"))
using (Bitmap dst = new Bitmap(100, 129))
using (Graphics g = Graphics.FromImage(dst))
{
   g.SmoothingMode = SmoothingMode.AntiAlias;
   g.InterpolationMode = InterpolationMode.HighQualityBicubic;
   g.DrawImage(src, 0, 0, dst.Width, dst.Height);
   dst.Save("scale.png", ImageFormat.Png);
}

Результат будет иметь действительно хорошие сглаженные края.

  • удалено изображение хижины, которое было заменено рекламой

Если вам необходимо экспортировать изображение в формате gif, вас ждет поездка;GDI+ плохо работает с gif.Видеть этот пост в блоге об этом для получения дополнительной информации

Редактировать: Я забыл избавиться от растровых изображений в примере;это было исправлено

Другие советы

Это базовая функция изменения размера, которую я использовал в нескольких своих приложениях, использующих GDI+.

/// <summary>
///    Resize image with GDI+ so that image is nice and clear with required size.
/// </summary>
/// <param name="SourceImage">Image to resize</param>
/// <param name="NewHeight">New height to resize to.</param>
/// <param name="NewWidth">New width to resize to.</param>
/// <returns>Image object resized to new dimensions.</returns>
/// <remarks></remarks>
public static Image ImageResize(Image SourceImage, Int32 NewHeight, Int32 NewWidth)
{
   System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(NewWidth, NewHeight, SourceImage.PixelFormat);

   if (bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format1bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format4bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format8bppIndexed | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Undefined | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.DontCare | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format16bppArgb1555 | bitmap.PixelFormat == Drawing.Imaging.PixelFormat.Format16bppGrayScale) 
   {
      throw new NotSupportedException("Pixel format of the image is not supported.");
   }

   System.Drawing.Graphics graphicsImage = System.Drawing.Graphics.FromImage(bitmap);

   graphicsImage.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality;
   graphicsImage.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
   graphicsImage.DrawImage(SourceImage, 0, 0, bitmap.Width, bitmap.Height);
   graphicsImage.Dispose();
   return bitmap; 
}

Я не сразу помню, будет ли он работать с GIF-файлами, но вы можете попробовать.

Примечание:Я не могу полностью приписать эту функцию.Я собрал несколько вещей из других образцов в Интернете и заставил их работать в соответствии со своими потребностями 8^D

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

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

Лучшее изменение размера изображения

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

Так не пойдет:

Response.ContentType = "image/png";
dst.Save( Response.OutputStream, ImageFormat.Png );

Но это будет:

Response.ContentType = "image/png";
using (MemoryStream stream = new MemoryStream())
{
    dst.Save( stream, ImageFormat.Png );

    stream.WriteTo( Response.OutputStream );
}

Хотя PNG определенно лучше GIF, иногда возникает необходимость остаться в формате GIF.

При использовании GIF или 8-битного PNG вам придется решить проблему квантования.

При квантовании вы выбираете, какие 256 (или меньше) цветов лучше всего сохранят и представляют изображение, а затем преобразуете значения RGB обратно в индексы.Когда вы выполняете операцию изменения размера, идеальная цветовая палитра меняется, поскольку вы смешиваете цвета и меняете баланс.

При небольших изменениях размеров, например 10–30 %, можно сохранить исходную цветовую палитру.

Однако в большинстве случаев вам потребуется повторное квантование.

Два основных алгоритма на выбор — Octree и nQuant.Octree очень быстр и отлично справляется со своей задачей, особенно если вы можете наложить умный алгоритм дизеринга.nQuant требует не менее 80 МБ ОЗУ для выполнения кодирования (он строит полную гистограмму) и обычно работает в 20–30 раз медленнее (1–5 секунд на кодирование среднего изображения).Однако иногда он обеспечивает более высокое качество изображения, чем Octree, поскольку не округляет значения для обеспечения стабильной производительности.

При реализации поддержки прозрачного GIF и анимированного GIF в imageresizing.net проект, я выбрал Octree.Поддержка прозрачности не сложна, если вы контролируете палитру изображения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top