c# - 如何正确调整/重新压缩图像

标签 c# image-processing compression windows-store-apps image-resizing

在过去的 10 到 12 个小时里,我一直在努力弄清楚如何在正在开发的 Windows 应用商店应用程序中使用 C# 正确缩小下载的网络图像的大小和像素。

无论我做什么,我都会在最终图像上不断出现伪像,例如“半幅图片”、灰色/同色区域等。就像流没有被正确刷新一样,尽管我相信已经这样做了(在下面的代码中没有这样做,因为它没有它就可以工作......)

这是我检索图像的方法 - 这部分有效,但包含在此处以确保所有信息都在这里(参见下面的代码):

  1. 获取图片地址
  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);
        }
    }
}

最佳答案

以下解决方案由 StefanDK 提供在 this edit :

看来我以前的解决方案的问题是我没有正确关闭流并且我没有正确的设置。

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

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

主要代码

请注意,我很清楚分配一个字符串值然后再次设置它的“不是最佳实践”。这是尚未微调的原型(prototype)代码。

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

关于c# - 如何正确调整/重新压缩图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21102623/

相关文章:

TreeViewItem 元素中的 C# 选项卡索引

c# - 图像旋转翻转 ASP.NET C#

c# - 如何在 C# 中安全地将 double 转换为小数

matlab - 简单的图像特征比较花费太多时间

python-3.x - 将图像放在另一个图像之上的更有效方法 - OpenCV Python3

google-apps-script - 如何压缩谷歌驱动器中的文件?

c# - 库中的 ConfigureAwait(false) 是否会丢失调用应用程序的同步上下文?

c++ - opencv2模糊图像

c# - 在 Windows 8 C# 上解压 ZIP 文件

PHP/MySQL : How to insert a gzcompress-ed string into a text mysql field?