EKİ
14

ASP.NET İle Yüksek Kaliteli Dinamik Thumbnail Image Oluşturmak

1 yorum | Kategori: Web Programlama | 14 Ekim 2009 Çarşamba

Bu yazı, özellikle blog uygumaları yazanlar için oldukça önemli olan thumbnail image (küçültülmüş resimler) oluşturmak ile ilgili.

Normalde .NET Framework içerisinde bu işi Image objesinin metodlarından biri olan GetThumbnailImage() metodunu kullanarak tek satırda halletmek mümkün. Ancak bu yöntemi kullanmışsanız, oluştuduğu thumbnail resimlerinin genelde düşük (bazen de berbat) kalitede olduklarını görmüşsünüzdür. Yazımda bu sorunu ortadan kaldıran ve resim kalitesine önem veren birçok kişinin GetThumbnailImage() metodu yerine tercih ettikleri başka bir yöntemi göstereceğim. Göstereceğim yöntemi kullanarak JPG, PNG, GIF gibi çok kullanılan formatların hepsi için kaliteli thumbnail'ler oluşturabileceksiniz. Ayrıca GetThumbnailImage() metodunun neden her zaman iyi sonuçlar vermediğinden de kısaca bahsedeceğim.

Burada göstereceğim kod, adını query string'ten almış olduğu bir dosyanın thumbnail image'ını oluşturup istemciye gönderecek. Bunun sayesinde .aspx dosyasını aşağıda gösterdiğim şekilde bir resimmiş gibi src tag'lerinde kullanabileceksiniz.

<img src="/thumb.aspx?dosya=deneme.jpg" />

Bu yaptığıma "Dinamik Thumbnail Oluşturma" da deniyor. Ben her ne kadar hakkında daha çok soru sorulduğu için bu örnekte dinamik olarak oluşturup göstermeyi anlatacak olsam da, bu yöntemi kullanmanızı pek tavsiye etmiyorum. Nedeni ise sunucuyu gereksiz yere yoracak olması (Caching de bedava değil). Thumbnail'leri resimler her gösterildiğinde tekrar oluşturmaktansa, resimler upload edildikten hemen sonra oluşturup başka bir isimle kaydetmenizi ve daha sonra o kaydettiğiniz dosyayı kullanmanızı tavsiye ederim. Bunu yapmak için aşağıda yazdığım kodlarda 2-3 satırı değiştirmeniz yeterli zaten.

Neyse lafı fazla uzatmadan kodları yazmaya başlayalım. İlk olarak yapılması gereken şey tabi ki orjinal resmi açmak ve formatını öğrenmek. Örnekte dosya adının query string'teki dosya adlı parametreden geldiğini, resimlerin de .cs dosyası ile aynı klasörde tutulduğunu farzettim. Siz kendi uygulamanızda nereden alacaksanız ona göre değiştirirsiniz.

String dosya_adi = Request.QueryString["dosya"];
Bitmap orjinal = new Bitmap(Server.MapPath(dosya_adi));    
ImageFormat format = orjinal.RawFormat;
Int32 thumbGenislik = 250;

Burada orjinal resmi açmak için Bitmap sınıfını kullandık ve resmi orjinal adlı değişkende sakladık. Resmin formatını ise yine Bitmap sınıfının RawFormat özelliğinden öğrendik. Sonraki satırda tanımlamış olduğum thumbGenislik adlı değişken, oluşturmak istediğimiz thumbnail resimlerinin en fazla hangi genişlikte olmasını istediğimizi gösteriyor. Ben örnek olarak 250 verdim, siz istediğiniz değeri kullanabilirsiniz. Kodların devamında thumbnail resmi oluşturmaya gerek olup olmadığına karar vermek, gerek varsa da resmin yüksekliğinin ne olacağını hesaplamak için bu değişkenin değerini kullanacağız.

if (orjinal.Width > thumbGenislik)
{
...
}

Gördüğünüz gibi thumbnail oluşturma işlemine başlamadan önce resmin genişliğinin yeterince büyük olup olmadığını kontrol ediyoruz. Eğer zaten oluşturmak istediğimiz thubmnail resminden daha küçük bir genişliğe sahipse, thumbnail olarak resmin kendini kullanabiliriz demektir. Bu durumda ne yapacağınız size kalmış. İster resmi istediğiniz bir formatta kaydedin, ister adını değiştirin, ister farklı bir isimde kopyasını çıkartın (mesela deneme_thumb.jpg). Benim amacım thumbnail oluşturmayı anlatmak olduğu için, sadece if koşulunun içerisindeki kısımları yazacağım. Siz isterseniz else kısmı da koyup içerisinde az önce bahsettiğim şeylerden birini yapabilirsiniz.

Şimdi if bloğunun içerisinde ... ile gösterilmiş olan yere yazacağımız kodlardan bahsedeyim:

ImageCodecInfo cInfo;
if (format.Equals(ImageFormat.Gif) || format.Equals(ImageFormat.Png))
{
    cInfo = GetEncoder(ImageFormat.Png);
    Response.ContentType = "image/png";
}
else
{
    cInfo = GetEncoder(ImageFormat.Jpeg);
    Response.ContentType = "image/jpeg";
}

Burada thumbnail resmini  hangi formatta kaydedeceğimizi ve buna bağlı olarak kaydederken hangi codec'i kullanacağımızı belirledik. Bunu belirleyen faktör tabi ki orjinal resmin ve oluşturmak istediğimiz thumbnail resminin formatı. Ben buradaki kodu eğer orjinal resim GIF veya PNG formatında ise PNG, diğer formatlarda ise JPG formatında thumbnail oluşturacak şekilde yazdım. Böyle yapmamın sebebi artık şeffaf resimler için GIF yerine yüksek kaliteli PNG dosyalarının kullanılıyor olması ve GIF dosyalarını zaten düşük olan kalitelerini bozmadan düzgün bir şekilde küçültmenin diğer formatları küçültmekten daha fazla işlem içermesi.

Diğer belirlediğimiz şey ise istemciye gönderilecek olan cevabın içerik türü. Bu türü doğru olarak belirlemezsek oluşturacağımız resim istemciye text/html olarak gidecek ve ziyaretçilerimiz tarayıcılarında resmimiz yerine saçma sapan karakterler göreceklerdir.

Burada kullanmış olduğum GetEncoder adlı fonksiyon kendi yazmış olduğum bir fonksiyon. Kodu da aşağıdaki gibi.

private ImageCodecInfo GetEncoder(ImageFormat format)
{
    ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

    foreach (ImageCodecInfo codec in codecs)
    {
        if (codec.FormatID == format.Guid)
        {
            return codec;
        }
    }
}

Bu fonksiyonun tek yaptığı şey, kodun çalıştığı sistemin desteklediği codec'lerin listesini almak ve liste içerisinde parametre olarak vermiş olduğumuz formatı kaydetmek için kullanılması gereken codec ile ilgili olan ImageCodecInfo objesini döndürmek.

If bloğunun içerisindeki kodlara devam edersek:

Encoder enc = Encoder.Quality;
EncoderParameters eParams = new EncoderParameters(1);
EncoderParameter eParam = new EncoderParameter(enc, 85L);
eParams.Param[0] = eParam;

Burada thumbnail resmini kaydederken kullanacağımız encoder parametrelerini oluşturduk. Verebileceğimiz bir çok parametre var, ancak burada konumuzla ilgili olan parametre Quality parametresi. Bu paremetrenin değeri ne kadar yüksek olursa sonuçta oluşacak resmin kalitesi de o kadar yüksek olacaktır. Ancak bu dediğim tabi ki JPG formatındaki resimler için geçerli. PNG formatındaki resimler zaten kayıpsız sıkıştırılmış oldukları için bu parametreye verdiğiniz değerden bağımsız olarak yüksek kalitede kaydedileceklerdir.  Parametrenin değeri 0-100 arasında değişebilmekte. 100 yaparsanız en yüksek kalitede (ve en büyük dosya boyutuna sahip) resmi elde edersiniz. Ben denemelerim sonucunda 85 değerinin kalite/boyut oranı bakımından en mantıklı değer olduğu sonucuna vardım. Siz kendi ihtiyacınıza göre bu değeri değiştirebilirsiniz.

Yazmaya devam edelim:

Int32 thumbYukseklik = (Int32)((float)thumbGenislik / orjinal.Width * orjinal.Height);
Bitmap thumbResim = new Bitmap(thumbGenislik, thumbYukseklik);

Burada orjinal resmin genişliğini 200'e indirdiğimizde en/boy oranının bozulmaması için hangi yüksekliğe sahip olması gerektiğini hesapladık ve hesapladığımız yeni boyutlara sahip olan thumbResim adında boş bir Bitmap objesi yarattık.

Şimdi gelelim kodun en önemli kısmına:

Graphics g = Graphics.FromImage(thumbResim);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(orjinal, 0, 0, thumbGenislik, thumbYukseklik);

Burada ilk olarak önceki adımda yaratmış olduğumuz Bitmap objesinin üzerine çizim yapabilmek için bir Graphics objesi yarattık. Daha sonra da bu objenin interpolasyon modunu HighQualityBicubic olarak ayarladık. Bu ayar sayesinde hangi formatta olursa olsun kaydettiğimiz tüm küçültülmüş resimlerin daha yüksek kaliteli olacaklar. Son satırda yaptığımız şey ise orjinal resmi, boyutunu küçülterek yeni yaratmış olduğumuz boş Bitmap üzerine çizmek. Bu adımdan sonra thumbnail resmimiz kaydedilmeye hazır hale geldi.

Son olarak, önceki adımda oluşturduğumuz thumbnail'i istemciye resim olarak gönderiyoruz:

Response.Clear();
thumbResim.Save(Response.OutputStream, cInfo, eParams);
thumbResim.Dispose();

Burada resmi cevap olarak sunucuya göndermeden önce cevabı temizleyerek başka bir şey içermediğinden emin olduk. Daha sonra ise Bitmap sınıfının Save fonksiyonuna parametre olarak OutputStream'i vererek, resmi istemciye gönderilecek olan cevabın içerisine yazdık. Son satır ise pisliğimizi temizlemek için.

Hayırlı olsun, nurtopu gibi bir thumbnail'imiz oldu.smiley

Örnek kodun tamamını aşağıda tekrar veriyorum.

using System;
using System.Data;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;

public partial class Thumb : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {  
        String dosya_adi = Request.QueryString["dosya"];
        Bitmap orjinal = new Bitmap(Server.MapPath(dosya_adi));    
        ImageFormat format = orjinal.RawFormat;
        Int32 thumbGenislik = 250;
        if (orjinal.Width > thumbGenislik)
        {
            ImageCodecInfo cInfo;
            if (format.Equals(ImageFormat.Gif) || format.Equals(ImageFormat.Png))
            {
                cInfo = GetEncoder(ImageFormat.Png);
                Response.ContentType = "image/png";
            }
            else
            {
                cInfo = GetEncoder(ImageFormat.Jpeg);
                Response.ContentType = "image/jpeg";
            }

            Encoder enc = Encoder.Quality;
            EncoderParameters eParams = new EncoderParameters(1);
            EncoderParameter eParam = new EncoderParameter(enc, 85L);
            eParams.Param[0] = eParam;

            Int32 thumbYukseklik = (Int32)((float)thumbGenislik / orjinal.Width * orjinal.Height);
            Bitmap thumbResim = new Bitmap(thumbGenislik, thumbYukseklik);
            Graphics g = Graphics.FromImage(thumbResim);

            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.DrawImage(orjinal, 0, 0, thumbGenislik, thumbYukseklik);

            Response.Clear();
            thumbResim.Save(Response.OutputStream, cInfo, eParams);
            thumbResim.Dispose();
        }
        else
        {
            //Burada da mime-type kontrolü gerekli ama yer kaplamasın diye jpeg deyip geçtim:)
            Response.Clear();
            Response.ContentType = "image/jpeg";
            orjinal.Save(Response.OutputStream, format);
        }
        orjinal.Dispose();
    }

    private ImageCodecInfo GetEncoder(ImageFormat format)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }
        return null;
    }
}

Son olarak GetThumbnailImage() metodunun neden bazı durumlarda "epic fail" olduğundan bahsedeyim. Bazı resim formatları, resmin kendinin yanında bir thumbnail'ini de aynı dosya içerisinde tutabiliyorlar. GetThumbnailImage()  metodu böyle durumlarda yeni bir thumbnail image oluşturmak yerine, dosyanın içerisinde bulunan thumbnail image'ı alıp boyutunu sizin parametre olarak verdiğiniz boyutta olacak şekilde değiştiriyor. Yani aslında siz orjinal resmi değil, resim dosyası içerisinde bulunan thubmnail'i küçültmüş oluyorsunuz. Hele bir de büyük boyutta bir thumbnail oluşturmaya kalkarsanız, bu durumda da dosya içerisindeki thumbnail'i büyütmüş oluyorsunuz. Bu da sonuçta ortaya çıkan resmin kalitesinin çok düşük olmasına sebep oluyor.

Yorumlar

Bu yazı hakkında toplam 1 yorum bulunmaktadır. Siz de yorum ekleyebilirsiniz.
Yorum alper özden | 22 Eylül 2010 Çarşamba 08:19:43
Çok yararlı bir yazı olmuş, teşekkürler.

Yorum Yazın

İsim ve soyadınız : E-Posta adresiniz : Güvenlik kodu : Güvenlik Kodu Yorumunuz : Gönder