如何正确地调整大小/重新压缩图像

3
我已经花费了最近10-12小时的时间来尝试在C#中编写一个正在开发的Windows Store应用程序中,正确地缩小下载的Web图像的大小和像素。无论我做什么,最终的图像上都会出现伪影,比如“半张图片”,灰色/同色区域等。就好像流还没有被正确刷新一样,尽管我相信我已经这样做了(因为下面的代码可以正常工作...)。以下是我检索图像的方法 - 此部分有效,但包括在此处以确保所有信息都在此处(请参见下面的代码):
1. 获取图像的URL 2. 使用HttpWebRequest获取响应 3. 创建流以获取响应流 4. 创建空StorageFile并打开进行写入 5. 将响应流复制到存储文件中。 6. 关闭所有内容
从那里开始,我需要做以下事情:
1. 确定大小(例如使用BitmapDecoder) 2. 如果图像的宽度超过一定量(例如700像素),则必须调整大小。 3. 无论如何,文件总是太大,需要进一步压缩 4. 图像需要以jpg格式保存,并将图像质量设置为中等/半高水平。
我尝试了许多方法,包括与BitmapEncoder / BitmapDecoder混合处理,但无论如何,我仍然得到了半处理的图像。请问是否有人能帮我找到正确的压缩和调整图像大小的方法?
我的代码处于当前状态:
using (var response = await HttpWebRequest.CreateHttp(internetUri).GetResponseAsync())
{
    using (var stream = response.GetResponseStream())
    {
        var imageFolder = await localFolder.CreateFolderAsync(
               CachedImagesFolderEndFolderPath, CreationCollisionOption.OpenIfExists);

        string fileName = string.Format("{0}.jpg", 
               Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));

        var file = await imageFolder.CreateFileAsync(fileName, 
               CreationCollisionOption.ReplaceExisting);

        using (var filestream = await file.OpenStreamForWriteAsync())
        {
            await stream.CopyToAsync(filestream);
        }
    }
}

你能否也发布一段代码,用于处理你的图像一半? - Cosmin Vană
你已经展示了能够运行的代码,那么不能运行的代码呢? - Jim Mischel
我不是网络专家,但为什么不使用Image类呢?它可以从流和文件中加载,并且可以用于调整大小(使用Graphics类,drawImage)...并以所需格式保存。 http://msdn.microsoft.com/en-us/library/system.drawing.image(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/system.drawing.graphics(v=vs.110).aspx - wondra
看起来当有回复时我没有收到邮件 :-( 不过,我现在已经更多地进行了一些尝试,并且我将会更新我的帖子并附上答案。基本部分是我需要确保所有设置都正确,并在重新打开流之前关闭了源文件和目标文件中的图像流。所以现在我正在分两步进行操作,但希望我能优化这个... - StefanDK
@StefanDK 答案应该作为回答发布,而不是编辑到问题中。我已经将您的解决方案发布为答案了。 - Servy
谢谢你,@Servy - 我还是新手在这里发布...感谢你接受我的重新格式化代码使其更可读。 - StefanDK
1个回答

1

以下解决方案由StefanDKthis edit中提供:

似乎我的前一个解决方案存在问题,即我没有正确关闭流并且没有正确设置。

基本上,该解决方案结合了这些文章的元素:

从主要代码中,我为需要下载、调整大小和压缩的每个图像进行了这些调用:

主要代码

请注意,我非常清楚在分配字符串值然后再次设置它时的“不最佳实践”。这是原型代码,尚未经过精细调整。

var img = await ArticleStorage.GetLocalImageAsync(src);
img = await ArticleStorage.ResizeAndCompressLocalImage(img);

ArticleStorage中方法的源代码

public const string CachedImagesFolderFullPath = "ms-appdata:///local/cache/";
public const string CachedImagesFolderEndFolderPath = "cache";
public const string OfflinePhotoImgPath = "ms-appx:///Assets/OfflinePhoto.png";
public const int MaximumColumnWidth = 700;

public static async Task<string> GetLocalImageAsync(string internetUri)
{
    if (string.IsNullOrEmpty(internetUri))
    {
        return null;
    }

    // Show default image if local folder does not exist
    var localFolder = ApplicationData.Current.LocalFolder;
    if (localFolder == null)
    {
        return OfflinePhotoImgPath;
    }

    // Default to offline photo
    string src = OfflinePhotoImgPath;

    try
    {
        using (var response = await HttpWebRequest.CreateHttp(internetUri)
                                                  .GetResponseAsync())
        {
            using (var stream = response.GetResponseStream())
            {
                // New random filename (e.g. x53fjtje.jpg)
                string fileName = string.Format("{0}.jpg",
                    Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));

                var imageFolder = await localFolder.CreateFolderAsync(
                    CachedImagesFolderEndFolderPath, 
                    CreationCollisionOption.OpenIfExists);

                var file = await imageFolder.CreateFileAsync(fileName, 
                    CreationCollisionOption.ReplaceExisting);

                // Copy bytes from stream to local file 
                // without changing any file information
                using (var filestream = await file.OpenStreamForWriteAsync())
                {
                    await stream.CopyToAsync(filestream);

                    // Send back the local path to the image 
                    // (including 'ms-appdata:///local/cache/')
                    return string.Format(CachedImagesFolderFullPath + "{0}", 
                         fileName);
                }
            }
        }
    }
    catch (Exception)
    {
        // Is implicitly handled with the setting 
        // of the initilized value of src
    }

    // If not succesfull, return the default offline image
    return src;
}

public static async Task<string> ResizeAndCompressLocalImage(string imgSrc)
{
    // Remove 'ms-appdata:///local/cache/' from the path ... 
    string sourcepathShort = imgSrc.Replace(
                                 CachedImagesFolderFullPath,
                                 string.Empty);

    // Get the cached images folder
    var folder = await ApplicationData.Current
                          .LocalFolder
                          .GetFolderAsync(
                               CachedImagesFolderEndFolderPath);

    // Get a new random name (e.g. '555jkdhr5.jpg')
    var targetPath = string.Format("{0}.jpg",
                          Path.GetFileNameWithoutExtension(
                              Path.GetRandomFileName()));

    // Retrieve source and create target file
    var sourceFile = await folder.GetFileAsync(sourcepathShort);
    var targetFile = await folder.CreateFileAsync(targetPath);

    using (var sourceFileStream = await sourceFile.OpenAsync(
                   Windows.Storage.FileAccessMode.Read))
    {
        using (var destFileStream = await targetFile.OpenAsync(
                   FileAccessMode.ReadWrite))
        {
            // Prepare decoding of the source image
            BitmapDecoder decoder = await BitmapDecoder.CreateAsync(
                                              sourceFileStream);

            // Find out if image needs resizing
            double proportionWidth = (double)decoder.PixelWidth /
                                     LayoutDimensions.MaximumColumnWidth;

            double proportionImage = decoder.PixelHeight / 
                                     (double)decoder.PixelWidth;

            // Get the new sizes of the image whether it is the same or should be resized
            var newWidth = proportionWidth > 1 ? 
                           (uint)(MaximumColumnWidth) : 
                           decoder.PixelWidth;

            var newHeight = proportionWidth > 1 ? 
                            (uint)(MaximumColumnWidth * proportionImage) : 
                            decoder.PixelHeight;

            // Prepare set of properties for the bitmap
            BitmapPropertySet propertySet = new BitmapPropertySet();

            // Set ImageQuality
            BitmapTypedValue qualityValue = new BitmapTypedValue(0.75, 
                                                    PropertyType.Single);
            propertySet.Add("ImageQuality", qualityValue);

            //BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(
                                            destFileStream, decoder);
            BitmapEncoder enc = await BitmapEncoder.CreateAsync(
                                          BitmapEncoder.JpegEncoderId, 
                                          destFileStream, propertySet);

            // Set the new dimensions
            enc.BitmapTransform.ScaledHeight = newHeight;
            enc.BitmapTransform.ScaledWidth = newWidth;

            // Get image data from the source image
            PixelDataProvider pixelData = await decoder.GetPixelDataAsync();

            // Copy in all pixel data from source to target
            enc.SetPixelData(
                decoder.BitmapPixelFormat,
                decoder.BitmapAlphaMode,
                decoder.PixelWidth, 
                decoder.PixelHeight, 
                decoder.DpiX, 
                decoder.DpiY, 
                pixelData.DetachPixelData()
                );

            // Make the encoder process the image
            await enc.FlushAsync();

            // Write everything to the filestream 
            await destFileStream.FlushAsync();
        }
    }

    try
    {
        // Delete the source file
        await sourceFile.DeleteAsync();
    }
    catch(Exception)
    {
    }

    // Return the new path 
    // including "ms-appdata:///local/cache/"
    return string.Format(CachedImagesFolderFullPath + "{0}", 
         targetPath);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接