图像上的质量损失来自哪里?

在我的Winforms应用程序中,它通过Linq连接到数据库到SQL我将图像(总是* .png)保存到一个如下所示的表:

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

在我可以存储图片之前,我必须将其转换为byte[] ,这就是我的工作方式:

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

之后,如果我想将这个相同的图像加载到我的应用程序中的PictureBox,我将使用此方法将其转换回来:

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

它确实有效,当我尝试在Picturebox中显示数据库中的图像时,会出现唯一的问题。

所以当我将此Image加载到数据库时:

在此处输入图像描述

后来我尝试显示它。 它突然看起来像这样:

在此处输入图像描述

我已经尝试了PictureBox的所有可能的SizeMode设置(Normal,Stretchimage,AutoSize,CenterImage,Zoom),它仍然看起来像这样。

以下是我如何将图像从数据库加载到pictureBox:

首先,我通过id检索属于集合的所有图像:

 public static ImageList GetRezeptImages(int rezeptId) { using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection)) { IEnumerable 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; } } 

同样在我的应用程序中,我有一个datagridview,其中Id存储在第一列中。 因此,当我想要检索属于该集合的任何图像时,我这样做:

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

从本地目录加载时,Image在pictureBox中看起来很好。 我还尝试将初始图片转换为二进制并返回(不加载到数据库),它在pictureBox中显示时看起来仍然很好。

在调试来自数据库的Image时,我还有一些其他的东西。 在查看ImageSize时,宽度和高度都具有值16.这很奇怪,因为原始图像具有完全不同的尺寸。

有任何想法吗?

当您将GetRezeptImages中的图像添加到列表中时, ImageList似乎正在将您的图像转换为MemoryBmp。
我不知道它为什么会这样做,但这就是你在图像中失去质量的原因。 正如您也意识到的那样,图像被转换为​​16×16图像,然后当您在PictureBox中将其调整为原始大小时,它看起来更加俗气。

编辑:
从TaW的评论:ImageList集合无法处理不同大小的图像,因此它将所有图像转换为通用大小。 由于没有设置大小,它似乎默认为16×16。

我建议您更改GetRezeptImages方法以返回List而不是ImageList并相应地使用它来显示图像。

或者,如果您始终以与问题中显示的方式相同的方式使用GetRezeptImages方法,则可以将其更改为始终只返回Image对象中的第一个Image并完全丢弃所有列表。

ImageList非常适合它可以做的事情:存储大量图像而不会丢失GDI资源。

但它不能做的就是你可能需要的东西:存储不同大小和比例的图像。

您可以并且应该将其设置为ImagesizeColorDepth属性(查看默认值,明确用作StateImageList;对于LargeImageList,16x16px和8位深度甚至是坏的。)但如果您的图像需要,则无法使用它有不同的尺寸和比例..

如果他们不这样做,那就是他们至少可以分享他们的比例,只需通过挑选一个漂亮的大小来修复ImageList就可以了! 添加到ImageList的所有图像将自动缩放到该Size并转换为ColorDepth

否则,请用ListList替换它。

如果您知道图像的常见尺寸和ColorDepth ,则可以执行以下操作:

 using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection)) { IEnumerable 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; } 

您可以不使用ImageList而是使用ListList制作它们 – 即: 它们修剪为相同的比例 ,基本上不会多于几条额外的线..:

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

此例程使用透明像素向右或向下扩展图像canvas大小而不缩放像素,因此它应保持无损清晰。

您也可以考虑将其转换为string64而不是byte []。 代码有点干净,一旦它是一个字符串,它就可以放在任何文件中,因为它只是文本。