图像质量损失从何而来?

时间:2014-10-28 15:22:09

标签: c# image winforms png picturebox

在我的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加载到数据库时:

enter image description here

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

enter image description here

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

这里也是我如何将图像从数据库加载到pictureBox:

首先,我通过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;                
    }
}

同样在我的应用程序中,我有一个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.这很奇怪,因为原始图像具有完全不同的尺寸。

有什么想法吗?

3 个答案:

答案 0 :(得分:7)

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

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

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

或者,如果您始终以与问题中显示的方式相同的方式使用Image方法,则可以将其更改为始终只返回{{1}}对象中的第一个图像并完全抛出离开所有名单。

答案 1 :(得分:4)

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

不能做的就是你可能需要的东西:存储不同大小的图像&amp;比例即可。

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

如果他们没有,那就是他们至少可以分享他们的比例,只需选择一个不错的尺寸来修复ImageList,然后重新设置!您添加到ImageList的所有图片都会自动缩放到Size并转换为ColorDepth

否则请用List<Image>List<Bitmap>替换它!

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

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

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

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

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

答案 2 :(得分:0)

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