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

时间:2014-01-13 22:25:38

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

我花了最后10到12个小时试图弄清楚如何在正在开发的Windows应用商店应用中正确地使下载的Web图像在C#中的尺寸和像素更小。

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

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

  1. 获取图片网址
  2. 使用HttpWebRequest获取响应
  3. 创建流以获取响应流
  4. 创建空的StorageFile并打开以进行编写
  5. 将响应流复制到存储文件。
  6. 关闭所有内容
  7. 从那里,我需要做以下事情:

    1. 确定大小(例如,使用BitmapDecoder)
    2. 如果图像的宽度超过一定量(例如700像素),则必须调整其大小。
    3. 无论如何,文件总是太大,需要进一步压缩
    4. 图像需要保存为jpg,图像质量设置为中/半高设置
    5. 我尝试了很多东西,包括使用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);
              }
          }
      }
      

1 个答案:

答案 0 :(得分: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);
}