Zmiana rozmiaru przezroczystych obrazów za pomocą C#

Czy ktoś ma tajną formułę zmiany rozmiaru przezroczystych obrazów (głównie GIFów) Bez jakiejkolwiek utraty jakości - co takiego?

Próbowałem kilka rzeczy, najbliższy dostaję nie jest wystarczająco dobry.

Spójrz na moje główne zdjęcie:

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

A następnie przeskalowany obraz:

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);
  }
}

Używam powyższego kodu, aby zindeksować zmiana rozmiaru.

Czy ktoś ma pomysły na poprawę?

Author: Juha Syrjälä, 2008-08-27

5 answers

Jeśli nie ma wymogu zachowania typu pliku po skalowaniu, polecam następujące podejście.

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);
}

Wynik będzie miał naprawdę ładne anty aliased krawędzi

  • usunięto obrazek, który został zastąpiony przez ogłoszenie

Jeśli musisz wyeksportować obrazek w gif-ie, to jesteś na przejażdżce; GDI+ nie gra dobrze z gif-em. Zobacz ten wpis na blogu o nim, aby uzyskać więcej informacji

Edit: zapomniałem pozbyć się bitmapy w przykładzie; zostało poprawione

 49
Author: Markus Olsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-17 17:14:49

Jest to podstawowa funkcja zmiany rozmiaru, której używałem w kilku moich aplikacjach, która wykorzystuje 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; 
}
Nie pamiętam, czy będzie działać z gifami, ale możesz spróbować.

Uwaga: nie mogę w pełni przypisać sobie tej funkcji. Poskładałem kilka rzeczy z innych sampli online i sprawiłem, że działał zgodnie z moimi potrzebami 8^D

 5
Author: Dillie-O,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-27 16:24:52

Myślę, że problem polega na tym, że robisz skanowanie liniowe oparte na zmianie rozmiaru, co doprowadzi do jaggies bez względu na to, jak mocno go poprawisz. Dobra jakość zmiany rozmiaru obrazu wymaga więcej pracy, aby ustalić średni kolor wstępnie zmienionych pikseli, które obejmuje zmieniony piksel.

Facet, który prowadzi tę stronę ma wpis na blogu, który omawia kilka algorytmów zmiany rozmiaru obrazu. Prawdopodobnie potrzebujesz dwubiegunowego algorytmu skalowania obrazu.

Lepszy Wizerunek Zmiana rozmiaru

 3
Author: Jonathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-27 16:24:48

Dla każdego, kto próbuje użyć rozwiązania Markusa Olssona do dynamicznej zmiany rozmiaru obrazów i zapisu ich do strumienia odpowiedzi.

To nie zadziała:

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

Ale to będzie:

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

    stream.WriteTo( Response.OutputStream );
}
 1
Author: Bela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-15 20:13:01

Podczas gdy PNG jest zdecydowanie lepszy niż GIF, czasami istnieje przypadek użycia, aby pozostać w formacie GIF.

W GIF lub 8-bitowym PNG musisz rozwiązać problem kwantyzacji.

Kwantyzacja to miejsce, w którym wybierasz, które 256 (lub mniej) kolorów będzie najlepiej zachowywało i przedstawiało obraz, a następnie zamieniasz wartości RGB z powrotem na indeksy. Podczas wykonywania operacji zmiany rozmiaru idealna paleta kolorów zmienia się podczas mieszania kolorów i zmiany sald.

Dla niewielkie zmiany rozmiaru, takie jak 10-30%, możesz być OK, zachowując oryginalną paletę kolorów.

Jednak w większości przypadków trzeba będzie ponownie kwantyzować.

Dwa podstawowe algorytmy do wyboru to Octree i nQuant. Octree jest bardzo szybki i robi bardzo dobrą robotę, zwłaszcza jeśli możesz nakładać inteligentny algorytm ditheringu. nQuant wymaga co najmniej 80MB pamięci RAM do kodowania (tworzy kompletny histogram) i jest zwykle 20-30x wolniejszy (1-5 sekund na kodowanie na przeciętnym obrazie). Jednak czasami daje wyższą jakość obrazu niż Octree, ponieważ nie "zaokrągla" wartości, aby utrzymać stałą wydajność.

Przy implementacji przezroczystego GIF-a i animowanego GIF-a w imageresizing.net projekt, wybrałem Octree. Obsługa przezroczystości nie jest trudna, gdy masz kontrolę nad paletą obrazów.

 0
Author: Lilith River,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-07 23:02:07