简体   繁体   English

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

[英]How to correctly resize/recompress image

I have spent the last 10-12 hours trying to figure out how to correctly make a downloaded web image smaller in size and pixels in C# in a Windows Store app under development. 我花了最后10-12个小时试图弄清楚如何在开发中的Windows应用商店应用中正确地使下载的Web图像在C#中的尺寸和像素更小。

Whatever I do, I keep getting artifacts on the final images such as a "half picture", gray/same-colored areas and likewise. 无论我做什么,我都会在最终图像上不停地拍摄文物,例如“半张图片”,灰色/同色区域等等。 Like if the stream has not been flushed correctly although I believe to have done so (not done so in the code below since it works without it...) 就像流没有正确刷新,虽然我相信已经这样做了(在下面的代码中没有这样做,因为没有它可以工作......)

This is my approach for retrieving the image - this part works, but is included here to make sure all info is here (see code below): 这是我检索图像的方法 - 这部分有效,但包含在这里是为了确保所有信息都在这里(见下面的代码):

  1. Get URL of image 获取图片的网址
  2. Use HttpWebRequest to get response 使用HttpWebRequest获取响应
  3. Create stream to get response stream 创建流以获取响应流
  4. Create empty StorageFile and open for writing 创建空的StorageFile并打开以进行写入
  5. Copy the response stream to the storage file. 将响应流复制到存储文件。
  6. Close everything 关闭一切

From there, I need to do the following: 从那里,我需要做以下事情:

  1. Determine the size (eg using BitmapDecoder) 确定大小(例如使用BitmapDecoder)
  2. If the width of the image is above a certain amount (eg 700 px), it must be resized. 如果图像的宽度超过一定量(例如700像素),则必须调整其大小。
  3. No matter what, the files are always too big and need to be compressed further 无论如何,文件总是太大,需要进一步压缩
  4. The image need to be saved as a jpg with image quality set to a medium/semi-high setting 图像需要保存为jpg,图像质量设置为中/半高设置

I have tried many things including messing pretty much around with BitmapEncoder/BitmapDecoder, but no matter what I am still getting half-processed images. 我已经尝试了很多东西,包括使用BitmapEncoder / BitmapDecoder进行混乱,但无论我还在使用半处理图像。

Can somebody please help me find the correct way to compress and resize images? 有人可以帮我找到压缩和调整图像大小的正确方法吗?

My code in the current state: 我在当前状态下的代码:

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

The following solution was provided by StefanDK in this edit : 以下解决方案由StefanDK此编辑中提供

It seems that the problem with my former solution was that I did not properly close the streams and that I did not have the correct settings. 似乎我以前的解决方案的问题是我没有正确关闭流并且我没有正确的设置。

Basically the solution incorporates elements from these articles: 基本上,该解决方案包含了这些文章的元素:

From the main part of the code I make these calls for each image that needs downloading, resizing and compressing: 从代码的主要部分开始,我为每个需要下载,调整大小和压缩的图像进行调用:

Main code 主要代码

Note that I am well aware of the "not best practice" in assigning a string value and then setting it again. 请注意,我很清楚分配字符串值然后再次设置它的“非最佳做法”。 This is prototype code that has not been fine-tuned yet. 这是原型代码,尚未经过微调。

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

Source code of the methods in ArticleStorage 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM