Görüntüyü Bayt dizisine dönüştürmenin en hızlı yolu


106

Masaüstünün bir görüntüsünü yakalayıp Sıkıştırıp alıcıya Gönderdiğim Uzak Masaüstü paylaşım uygulaması yapıyorum. Görüntüyü sıkıştırmak için onu bayta [] dönüştürmem gerekiyor.

Şu anda bunu kullanıyorum:

public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
    MemoryStream ms = new MemoryStream();
    imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
    return  ms.ToArray();
}

public Image byteArrayToImage(byte[] byteArrayIn)
{
     MemoryStream ms = new MemoryStream(byteArrayIn);
     Image returnImage = Image.FromStream(ms);
     return returnImage;
}

Ama bundan hoşlanmıyorum çünkü onu bir ImageFormat'a kaydetmem gerekiyor ve bu aynı zamanda kaynakları kullanabilir (Yavaşlama) ve farklı sıkıştırma sonuçları üretebilir. Marshal.Copy ve memcpy kullanmayı okudum ama yapamıyorum onları anla.

Peki bu hedefe ulaşmak için başka bir yöntem var mı?


hem MemoryStream hem de Image bir imha yöntemine sahiptir, bunları İmha ettiğinizden emin olun, çünkü bu MemoryLeaks'e neden olabilir.
abc123

3
@ abc123: a MemoryStream; atmanıza gerek yok ; uzaktan kumandada kullanmadığınız sürece, tamamen yönetilen bir kaynaktır. Her iki durumda da kaynağı elden çıkarmak uygun olmaz.
Jon Skeet

1
@JonSkeet ilginç, bununla ilgili bir kıyaslama yaptınız mı? .net'in nesneyi hangi hızda bıraktığını görmek için? DataTable için benzer bir argüman olduğunu biliyorum ve yine de GarbageCollector'ın bir dispose kullanıldığında ayrılan belleği toplama hızında gözle görülür bir fark var.
abc123

@ abc123: Gerçekten olmasını beklemiyordum - akışın elden çıkarılması diziye hiçbir şey yapmaz ve MemoryStream bir sonlandırıcıya sahip değildir (MarshalByValueComponent'ten bir tane miras alan DataTable'ın aksine).
Jon Skeet

2
tam kaynak kodlu herhangi bir nihai çözüm var mı?
Kiquenet

Yanıtlar:


39

Peki bu hedefe ulaşmak için başka bir yöntem var mı?

Hayır. Bir görüntüyü bayt dizisine dönüştürmek için bir görüntü formatı belirlemeniz gerekir - tıpkı metni bayt dizisine dönüştürdüğünüzde bir kodlama belirtmeniz gerektiği gibi.

Sıkıştırma eserlerinden endişeleniyorsanız, kayıpsız bir format seçin. CPU kaynakları konusunda endişeleniyorsanız, sıkıştırmayı zahmet etmeyen bir format seçin - örneğin sadece ham ARGB pikselleri. Ancak elbette bu daha büyük bir bayt dizisine yol açacaktır.

Not Eğer bir format seçerseniz o yapar sıkıştırma içerir, sonra sonra bayt dizisi sıkıştırarak anlamı yok - bu bir yararlı etkisi bulunmadığı için neredeyse kesin.


12
'kayıpsız bir format seçmek' yerine, daha imageIn.RawFormatfazla yeniden kodlama yapmadan ham görüntü baytlarını hangi girişimleri kaydetmeyi deneyebilirsiniz .
Chris F Carroll

52

Görüntünün dosya biçimini döndüren Görüntü parametresinin bir RawFormat özelliği vardır. Aşağıdakileri deneyebilirsiniz:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}

9
MemoryStream'in atılmasını veya bu yöntemin gövdesini using () {} ifadesine
sarmanızı öneririm

@ Neil.Allen Ben burada yeniyim, lütfen nedenini söyleyebilir misiniz?
Khalil Khalaf

3
@FirstStep Çünkü kendinizden sonra temizleyin :)
Sinaesthetic

@Sinaesthetic Görüyorum. Ve rutin, yürütmek istediğim işlevi kullanarak () {}?
Khalil Khalaf

2
@FirstStep Pek değil. Daha doğru bir şekilde: IDisposable uygulayan bir nesne kullanıyorsanız, onunla işiniz bittiğinde Dispose () 'u çağırdığınızdan emin olmalısınız, böylece bağlı olduğu kaynakları temizleyecektir. Using () {} ifadesi, nesne bu ifadenin kapsamının dışına çıktığında onu sizin için çağırır. Yani yapabilirsiniz myObject.Dispose()veya using(myObject){}- her ikisi de aynı şeyi yapabilirsiniz, ancak using ifadesi temelde sizin için temizleyecek bir kapsam oluşturur.
Sinaesthetic

14

Jon Skeet'in işaret ettiği nedenlerle büyük kazançlar elde edip etmeyeceğinizden emin değilim. Ancak, TypeConvert.ConvertTo yöntemini deneyebilir ve karşılaştırmalı olarak değerlendirebilir ve mevcut yönteminizi kullanarak nasıl karşılaştırıldığını görebilirsiniz.

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));

'System.Byte []' türündeki nesne 'System.Drawing.Image' yazmak için dönüştürülemiyor.
user123

14
public static byte[] ReadImageFile(string imageLocation)
    {
        byte[] imageData = null;
        FileInfo fileInfo = new FileInfo(imageLocation);
        long imageFileLength = fileInfo.Length;
        FileStream fs = new FileStream(imageLocation, FileMode.Open, FileAccess.Read);
        BinaryReader br = new BinaryReader(fs);
        imageData = br.ReadBytes((int)imageFileLength);
        return imageData;
    }

5
Stackoverflow.com'a hoş geldiniz, yukarıdaki kod örneğinin neden yardımcı olduğunu açıklayan biraz ayrıntı ekleyebilir misiniz? Tamamen anlamayan diğer SO kullanıcıları için ... stackoverflow.com/help/how-to-answer
Mack

Bu, dosyalar için baytlar içindir, ancak OP, bayta dönüştürülmüş bir çizim nesnesi istedi. Çizim nesneleri, bir bayt dizisi olarak dosya sisteminde değil, veritabanlarında depolanabilir ve bu nedenle ileri geri dönüştürülmeleri gerekir ... ancak bir FileStream'de bayta dönüştürülecek dosyalar olarak değil - belki ilk yükleme.
vapcguy

Dosyalarla bunu yapmaya çalışırken bu bana yardımcı oldu. İlgili olduğu için etrafta olması güzel.
Justin

5
public static class HelperExtensions
{
    //Convert Image to byte[] array:
    public static byte[] ToByteArray(this Image imageIn)
    {
        var ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        return ms.ToArray();
    }

    //Convert byte[] array to Image:
    public static Image ToImage(this byte[] byteArrayIn)
    {
        var ms = new MemoryStream(byteArrayIn);
        var returnImage = Image.FromStream(ms);
        return returnImage;
    }
}

2

Bulabileceğim en hızlı yol şudur:

var myArray = (byte[]) new ImageConverter().ConvertTo(InputImg, typeof(byte[]));

Faydalı olmayı umuyoruz


Buna dikkat edin, özellikle System.Windows.Controls.Imagenesnelerin olduğu yerlerde WPF kullanıyorsanız . Bunlardan birini bayta dönüştürmek istiyorsanız ve bu satıra geçirirseniz InputImg, bu işe yaramaz. Bir System.Drawing.Imagenesneyi bekler .
vapcguy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.