2014-10-28 24 views
8

nella mia applicazione WinForms che è collegato ad un database tramite LINQ to SQL sto salvataggio delle immagini (sempre * .png) a una tabella che assomiglia a questo:Da dove viene questa perdita di qualità sulle immagini?

CREATE TABLE [dbo].[Images] (
    [Id]  INT IDENTITY (1, 1) NOT NULL, 
    [Bild]  IMAGE NOT NULL, 
    PRIMARY KEY CLUSTERED ([Id] ASC) 
); 

Prima che io possa memorizzare un'immagine devo convertirlo in byte[] e questo è come lo faccio:

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

Poi se voglio caricare questa stessa immagine in un PictureBox nella mia applicazione converto indietro con questo metodo:

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

In realtà funziona, l'unico problema si presenta quando provo a visualizzare un'immagine dal database in una Picturebox.

Così, quando ho caricare questa immagine al database:

enter image description here

e poi cerco di visualizzarlo. Sembra improvvisamente come questo:

enter image description here

Ho già provato tutte le possibili impostazioni SizeMode per il controllo PictureBox (Normale, Stretchimage, AutoSize, CenterImage, Zoom) e sembra ancora come questo.

Anche ecco come lo caricare le immagini dal database al pictureBox:

Per prima cosa ho recuperare i tutte le immagini appartenenti ad un insieme tramite ID:

public static ImageList GetRezeptImages(int rezeptId) 
{ 
    using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection)) 
    { 
     IEnumerable<RezeptBilder> bilder = from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b; 
     ImageList imageList = new ImageList(); 

     foreach(RezeptBilder b in bilder) 
     { 
      imageList.Images.Add(Helper.ByteArrayToImage(b.Bild.ToArray())); 
     } 

     return imageList;     
    } 
} 

anche nella mia applicazione devo un datagridview dove ID sono memorizzati nella prima colonna. Così, quando ho voglia di recuperare eventuali immagini che compongono il set lo faccio in questo modo:

private void dgvRezeptListe_CellClick(object sender, DataGridViewCellEventArgs e) 
{ 
    pbRezeptBild.Image = DBManager.GetRezeptImages(Int32.Parse(dgvRezeptListe.SelectedRows[0].Cells[0].Value.ToString())).Images[0];  
} 

volta caricato da una directory locale l'immagine appare bene in pictureBox. Ho anche provato a convertire l'immagine iniziale in binario e viceversa (senza caricarla nel database), sembrava ancora bene quando veniva visualizzata nella pictureBox.

C'è qualcos'altro che ho capito durante il debug dell'immagine proveniente dal database. Quando si guarda a ImageSize, larghezza e altezza avevano entrambi il valore 16. Ciò è strano perché l'immagine originale ha dimensioni completamente diverse.

Qualche idea?

+1

In base a questa fonte: http://msdn.microsoft.com/de-de/library/ms187993.aspx, il tipo di immagine è obsoleto e deve essere sostituito da varbinary. Forse questo impedirà anche la perdita di qualità. – DMAN

+3

Ho appena testato i due metodi di conversione dell'immagine in byte e viceversa, con un DataSet normale, la stessa tabella definita e le procedure memorizzate allegate e non vi è alcuna perdita di qualità dell'immagine. Sembra che il tuo problema potrebbe risiedere in 'LINQ to SQL' –

+0

@DMAN Ci proverò. –

risposta

7

Sembra che ImageList stia convertendo le immagini in MemoryBmp quando le si aggiunge in GetRezeptImages all'elenco.
Non sono a conoscenza del motivo per cui lo farebbe, ma questo è il motivo della perdita di qualità dell'immagine. Come hai anche capito, l'immagine viene convertita in un'immagine 16x16 e poi quando viene ridimensionata nel tuo PictureBox alla dimensione originale, sembra solo più scadente.

Edit:
Dal commento di Taw: La collezione ImageList non può gestire variando le immagini di dimensioni, quindi è la conversione di tutte le immagini a una dimensione comune. Poiché non è impostata alcuna dimensione, sembra che sia impostata su 16x16.

Si consiglia di modificare il metodo GetRezeptImages per restituire uno List<Image> anziché uno ImageList e utilizzarlo di conseguenza per visualizzare le immagini.

In alternativa, se si utilizza sempre il metodo GetRezeptImages nello stesso modo mostrato nella domanda, è possibile modificarlo in modo da restituire sempre la prima immagine in un oggetto Image e buttare via tutti gli elenchi.

+2

Direi che sembra letteralmente meno di formaggio. –

4

ImageList è bello per quello che può fare: un sacco Negozio delle immagini senza perdere risorse GDI.

Ma ciò che non può fare è quello che probabilmente è necessario: Salva immagini di varie dimensioni & proporzioni.

Si può e deve impostarlo Imagesize e ColorDepth proprietà (vedi le impostazioni predefinite, che sono chiaramente pensati per l'uso come StateImageList; 16x16px e profondità 8bit è ancora male per un LargeImageList ..), ma non è possibile utilizzarlo se le tue immagini devono avere proporzioni diverse

Se non lo fanno, cioè se possono condividere almeno le loro proporzioni, basta fissare lo ImageList selezionando una bella dimensione e sei pronto! Tutte le immagini che aggiungi a ImageList verranno ridimensionate automaticamente a quello Size e convertite in ColorDepth.

Altrimenti sostituirlo con un List<Image> o List<Bitmap> ..!

Se si conosce la dimensione comune e ColorDepth delle immagini si può fare:

using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection)) 
{ 
    IEnumerable<RezeptBilder> bilder = 
      from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b; 
    ImageList imageList = new ImageList(); 

    imageList.ColorDepth = ColorDepth.Depth24Bit; // 
    imageList.ImageSize = yourImageSize;   // 

    foreach(RezeptBilder b in bilder) 
    { 
     imageList.Images.Add(Helper.ByteArrayToImage(b.Bild.ToArray())); 
    } 

    return imageList;     
} 

è possibile non utilizzare un ImageList ma un List<Image> o List<Bitmap> al posto o rendono loro - vale a dire: li Trim al stessa proporzione, in fondo non molto di più di qualche riga in più ..:

Bitmap expandCanvas(Bitmap bmp, Size size) 
{ 
    float f1 = 1f * bmp.Width/bmp.Height; 
    float f2 = 1f * size.Width/size.Height; 
    Size newSize = size; 
    if (f1 > f2) newSize = new Size(bmp.Width, (int)(bmp.Height * f1)); 
    else if (f1 < f2) newSize = new Size((int)(bmp.Width/f1), bmp.Height); 

    Bitmap bmp2 = new Bitmap(newSize.Width, newSize.Height); 
    bmp2.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution); 

    Rectangle RDest = new Rectangle(Point.Empty, bmp.Size); 
    Rectangle RTgt = new Rectangle(Point.Empty, newSize); 

    using (Graphics G = Graphics.FromImage(bmp2)) 
    { 
     G.DrawImage(bmp, RDest, RDest, GraphicsUnit.Pixel); 
    } 
    return bmp2; 
} 

Questa routine espande le dimensioni dell'area di disegno dell'immagine con pixel trasparenti a destra o in basso senza ridimensionare i pixel, quindi dovrebbe rimanere nitidamente senza perdite.

0

Inoltre, si potrebbe pensare di convertirlo in una stringa64 anziché in byte []. Il codice è un po 'più pulito e una volta che si tratta di una stringa può essere eliminato in quasi tutti i file perché è solo testo.

+0

Qualche esempio con spiegazione sarebbe grandioso. – Nilambar

+0

È stato discusso in questa discussione. http://stackoverflow.com/questions/10889764/how-to-convert-bitmap-to-a-base64-string Ma diciamo che vuoi salvare la tua immagine in un database SQL o in un database di file flat , è più facile lavorare con una stringa e poi con i byte. I byte sono, la radice, solo numeri e se presi direttamente nel testo non sempre hanno un equivalente stampato. I numeri 10 e 13 sono ottimi esempi di questo. Né vengono stampati sullo schermo e sono considerati spazi bianchi. –