2009-12-07 17 views
10

Mam problem ze skalowaniem obrazu w .NET. Używam standardowego typu Graphics, aby zmienić rozmiar zdjęć, jak w poniższym przykładzie:Skalowane obrazy mają czarne obramowania

public static Image Scale(Image sourceImage, int destWidth, int destHeight) 
{ 
     Bitmap toReturn = new Bitmap(sourceImage, destWidth, destHeight); 

     toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); 

     using (Graphics graphics = Graphics.FromImage(toReturn)) 
     { 
      graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; 
      graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 
      graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 
      graphics.DrawImage(sourceImage, 0, 0, destWidth, destHeight); 
     } 
     return toReturn; 
    } 

Ale mam duży problem z przeskalowane obrazy: mają szare i czarne obramowanie i jest to bardzo ważne, aby mieć obraz bez nich.

Dlaczego pojawiają się i co mogę zrobić, aby zniknęły? Wyjście

Próbka:

sample output

+2

Co HTML wyglądać to wysyłane do przeglądarki w poszukiwaniu tych obrazów? – DOK

+2

Jaki jest oryginalny typ obrazu? –

+2

opublikować obraz wejściowy –

Odpowiedz

6

Spróbuj:

graphic.CompositingMode = CompositingMode.SourceCopy; 
+4

Niestety, to nie jest żadne rozwiązanie. Nie ma żadnego efektu. Szara ramka jest nadal generowana. Jeśli działało na PO, to przez zwykły wypadek. Rozwiązanie polega na określeniu atrybutów obrazu wskazanych w tej odpowiedzi: http://stackoverflow.com/questions/2319983/resizing-an-image-in-asp-net-without-losing-the-image-quality/2324414# 2324414 –

+3

@Developer Art, czy Twoje źródło jest 24-bitowe czy 32-bitowe z alfa? Zmiana rozmiaru obrazu za pomocą alfa spowoduje półprzezroczyste piksele wzdłuż krawędzi; po ustawieniu tego trybu piksele będą mieszane z tłem, w przeciwnym razie będą mieszane z czernią, powodując tym samym granice. Nie wiem, co dzieje się z obrazami 24-bitowymi. –

+1

@ Mark Ransom, Widzisz moją odpowiedź, częściowo masz rację, ale nie podałeś właściwego rozwiązania. – Jeroen

2

Jak wykonuje następujące prace dla Ciebie? To jest kod, którego użyłem do zrobienia tego samego. Główną różnicą, którą zauważam jest to, że nie używam SetResolution (i zakładam kwadratowe wejście i wyjście, ponieważ tak było w moim przypadku).

/// <summary> 
/// Resizes a square image 
/// </summary> 
/// <param name="OriginalImage">Image to resize</param> 
/// <param name="Size">Width and height of new image</param> 
/// <returns>A scaled version of the image</returns> 
internal static Image ResizeImage(Image OriginalImage, int Size) 
{ 
    Image finalImage = new Bitmap(Size, Size); 

    Graphics graphic = Graphics.FromImage(finalImage); 

    graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; 
    graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; 
    graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 

    Rectangle rectangle = new Rectangle(0, 0, Size, Size); 

    graphic.DrawImage(OriginalImage, rectangle); 

    return finalImage; 
} 
+2

Stworzył obraz od zera, który ma domyślne czarne tło. Ładujesz obraz, który może nie mieć tła. – Jeroen

3

To dlatego, że próbkowanie zostało zrobione z krawędzi zdjęcia.

1

Wynika to z wygładzania (mieszania się z tłem) na krawędziach podczas rysowania obrazu.

Możesz narysować dwa razy, raz bez i jeden z włączonym wygładzaniem. Lub możesz narysować nieco większy. Lub jeśli znany jest oryginalny kolor tła, możesz najpierw wypełnić obraz kolorem tła.

+0

@Dvvovator, Byłbym bardzo wdzięczny za komentarz, dlaczego moja odpowiedź była tak niewłaściwa lub zła ... – Lucero

5

Problem polega na tym, że bitmapy toReturn ma czarne tło domyślnie. Kopiowanie nowego obrazu powoduje utworzenie czarnych lub szarych ramek.

Rozwiązaniem jest usunąć czarny domyślne tło, wywołując:

toReturn.MakeTransparent(); 

Ponieważ po tej linii będziesz rysunek na nowy obraz bez koloru tła granice znikną.

7

Może to być spowodowane nieprawidłowym interpolowaniem pikseli wokół krawędzi. Nazwałbym to błędem.

Oto rozwiązanie, choć:

graphics.CompositingMode = CompositingMode.SourceCopy; 
graphics.PixelOffsetMode = PixelOffsetMode.Half; 
graphics.InterpolationMode = InterpolationMode.NearestNeighbor; 

// Draw your image here. 

graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 

// Draw it again. 

Co to jest pierwszy rysunek „czerwony” z krawędziami poprawnie wypełnione, a następnie wyciągnąć go ponownie z interpolacją. Jeśli nie potrzebujesz interpolacji, nie jest to konieczne.

+0

To rozwiązało wiele problemów. Dziękuję Ci bardzo! – Tillito

+0

@Tillito Nie ma problemu :) Należy jednak pamiętać, że jest to, choć efektywne, nie najbardziej optymalne rozwiązanie pod względem wydajności. Nie powinno to mieć większego znaczenia, chyba że masz naprawdę dużą liczbę obrazów lub robisz niestandardowy komponent do rozprowadzania, ponownego użycia itp., Gdzie możesz potrzebować bardziej skomplikowanej metody naprawy, na przykład za pomocą [lockbitów] ] (http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.lockbits.aspx) i rysowanie/interpolowanie "ręcznie". –

6

Prawdziwym rozwiązaniem jest użycie przeciążenia urządzenia DrawImage, które umożliwia przekazanie obiektu ImageAttributes.

Na przykład ImageAttributes, zadzwoń następującą metodę przed przekazaniem go do DrawImage:

using (var ia = new ImageAttributes()) 
{ 
    ia.SetWrapMode(WrapMode.TileFlipXY); 
    aGraphic.DrawImage(..., ia); 
} 

Zobacz także this answer

+0

To działało idealnie dla mnie. Dzięki. – user892381

+0

To jest prawdziwe rozwiązanie tego problemu. – kaiyaq

+0

Próbuję rozwiązać ten problem przez około 3 godziny. Ta odpowiedź jest jedyną, która działała. Chciałbym, żeby to było akceptowane rozwiązanie. – Andy

1

Żaden z nich nie pracował dla mnie.

Jednak zmiany formatu z

System.Drawing.Imaging.PixelFormat.Format24bppRgb 

do

System.Drawing.Imaging.PixelFormat.Format32bppArgb 

nie rozwiązuje problemu

using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(newWidth, newHeight, 
       // System.Drawing.Imaging.PixelFormat.Format24bppRgb // OMG bug 
        System.Drawing.Imaging.PixelFormat.Format32bppArgb 
       )) 
      { 
0

Prawidłowa odpowiedź może być poskładane z kilku innych odpowiedzi, ale żaden z nich nie jest kompletny, a niektóre przedstawiają bardzo złe pomysły (jak dwukrotne rysowanie obrazu).

Są trzy powody, dla artefaktów widzisz:

  1. Domyślna Graphics.PixelOffsetMode ustawienie powoduje, że wartości pikseli być próbą nieprawidłowo, powodując lekkie zniekształcenie obrazu, zwłaszcza na brzegach.
  2. InterpolationMode.HighQualityBicubic próbki pikseli spoza krawędzi obrazu, które domyślnie są przezroczyste. Te przezroczyste piksele są mieszane z pikselami krawędzi przez sampler, co powoduje półprzezroczyste krawędzie.
  3. Po zapisaniu obrazu półprzezroczystego w formacie, który nie obsługuje przezroczystości (na przykład JPEG), przezroczyste wartości są zastępowane przez czerń.

Wszystko to składa się na pół-czarne (tzn. Szare) krawędzie.

Prawidłowym sposobem usunięcia tych artefaktów jest użycie PixelOffsetMode.Half i użycie obiektu ImageAttributes do określenia grubości krawędzi, aby próbnik HighQualityBicubic miał coś innego niż przezroczyste piksele do wypróbowania.

Istnieje kilka innych problemów związanych z kodem Ci wysłane także:

Bitmap konstruktor został użyty jest inicjowanie nowych Bitmap od rozmiaru oryginalnego obrazu, tak robisz operację zmiany rozmiaru dwukrotnie. Powinieneś użyć przeciążenia konstruktora o pożądanych wymiarach, aby utworzyć puste płótno.

Pamiętaj, że klasa Bitmap reprezentuje niezarządzaną kopię obrazu w pamięci. Musi zostać usunięty, aby GDI + mógł zostać poinformowany o zwolnieniu tej pamięci, gdy skończysz. Zakładam, że robisz to w kodzie, który otrzymuje zwrot Image, ale wskazuję to na wypadek, gdyby ktoś inny zapożyczył ten kod.

Ustawienie CompositingQuality.HighQuality użyte w kodzie nie będzie miało żadnego efektu wizualnego, jeśli uzyskasz inne ustawienia w prawidłowy sposób i faktycznie będzie to miało negatywny wpływ na wydajność w połączeniu z domyślną wartością CompositingMode.SourceOver. Możesz pominąć ustawienie CompositingQuality i ustawić CompositingMode.SourceCopy, aby uzyskać takie same wyniki i lepszą wydajność.

Ustawienie użyte w kodzie SmoothingMode nie ma żadnego wpływu na DrawImage(), więc można je usunąć.

Możesz przeczytać więcej informacji na temat ustawień Graphics klasowych i ich wpływu na jakość obrazu i wydajność tutaj: http://photosauce.net/blog/post/image-scaling-with-gdi-part-3-drawimage-and-the-settings-that-affect-it

Zmieniony kod powinien wyglądać następująco:

public static Image Scale(Image sourceImage, int destWidth, int destHeight) 
{ 
    var toReturn = new Bitmap(destWidth, destHeight); 

    using (var graphics = Graphics.FromImage(toReturn)) 
    using (var attributes = new ImageAttributes()) 
    { 
     toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); 

     attributes.SetWrapMode(WrapMode.TileFlipXY); 

     graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 
     graphics.PixelOffsetMode = PixelOffsetMode.Half; 
     graphics.CompositingMode = CompositingMode.SourceCopy; 
     graphics.DrawImage(sourceImage, Rectangle.FromLTRB(0, 0, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attributes); 
    } 

    return toReturn; 
}