ImageMagick`resize`:3分钟输出10种不同尺寸....如何提升性能?

时间:2014-09-23 12:22:09

标签: image performance image-processing imagemagick

我正在开展一个项目,涉及从用户上传DSLR相机图像并调整为10种不同尺寸...

我正在使用ImageMagick在服务器端调整大小....但是处理图像需要花费太多时间..这超过3分钟......最终用户会因为等待它而感到恼火。

所以我想减少时间并提高性能....请帮助我做出改变。

当我尝试使用相同的文件(4mb - 6mb)在Flickr,500px和facebook上传时,他们在更短的时间内完成了它....

我不是专业的程序员.....我只是使用简单的机制通过输入上传文件并处理服务器端控制器操作的图像......

我使用以下代码调整每张图片的大小......

以下是我处理图像的控制器操作

根据建议大约1.6分钟处理代码中的以下差异大小

更新下面的代码
       #region Actions

    /// <summary>
    /// Uploads the file.
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    public virtual ActionResult UploadImg()
    {
        HttpPostedFileBase myFile = Request.Files["UploadImage"];
        bool isUploaded = false;
        string message = "File upload failed";
        var filename = Path.GetExtension(myFile.FileName).ToLowerInvariant();

        if ((filename == ".jpg" || filename == ".jpeg") && myFile != null && myFile.ContentLength != 0)
        {


                // Paths
                string Upath = Server.MapPath(@"~/photos/");
                string ImgName = "_org.jpg";
                string imageTo = "";

                //Image names
                string OrgImgName = System.IO.Path.GetFileName(myFile.FileName);

                myFile.SaveAs(Path.Combine(Upath, myFile.FileName));



                if (this.CreateFolderIfNeeded(Upath))
                {
                    try
                    {
                        using (MagickImage original = new MagickImage(Upath + OrgImgName))
                        {
                            original.AutoOrient();
                            original.Write(Upath + ImgName);
                            original.ColorSpace = ColorSpace.Lab;
                            original.SetAttribute("density", "72x72");



                            int[] sizes = new int[] { 2048, 1600, 1024, 800, 500, 640, 320, 240, 150, 100, 75, 50 };

                            Parallel.For(0, sizes.Length, delegate(int index)
                            {
                                int size = sizes[index];

                                if (original.Width > size || original.Height > size)
                                {
                                    if (size == 150 || size == 75 || size == 50)
                                    {
                                        string gmt = size.ToString() + 'x' + size.ToString();
                                        MagickGeometry g = new MagickGeometry(gmt);
                                        using (MagickImage resized = original.Clone())
                                        {

                                            resized.SetDefine(MagickFormat.Jpeg, "sampling-factor", "4:4:4");

                                            resized.Blur(1, 0.375);

                                            resized.FilterType = FilterType.LanczosSharp;

                                            g.FillArea = true;
                                            resized.Resize(g);
                                            resized.Crop(size, size, Gravity.Center);
                                            resized.ColorSpace = ColorSpace.sRGB;
                                            Unsharpmask(resized, size);

                                            resized.Quality = 85;


                                            if (size == 150)
                                            {
                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 75)
                                            {
                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 50)
                                            {
                                                imageTo = Upath +GetOutputName(size);
                                            }

                                            resized.Write(imageTo);

                                        }
                                    }
                                    else
                                    {
                                        using (MagickImage resized = original.Clone())
                                        {

                                            resized.SetDefine(MagickFormat.Jpeg, "sampling-factor", "4:4:4");

                                            resized.Blur(1, 0.375);

                                            resized.FilterType = FilterType.LanczosSharp;
                                            resized.Resize(size, size);

                                            resized.ColorSpace = ColorSpace.sRGB;
                                            Unsharpmask(resized, size);

                                            resized.Quality = 85;


                                            if (size == 2048)
                                            {
                                               imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 1600)
                                            {

                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 1024)
                                            {

                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 800)
                                            {

                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 640)
                                            {

                                                imageTo = Upath + GetOutputName(size); ;
                                            }
                                            else if (size == 500)
                                            {

                                                imageTo = Upath + GetOutputName(size);
                                            }
                                            else if (size == 320)
                                            {

                                                imageTo = Upath +GetOutputName(size);
                                            }
                                            else if (size == 240)
                                            {

                                                imageTo = Upath +GetOutputName(size);
                                            }
                                            else if (size == 100)
                                            {

                                                imageTo = Upath +GetOutputName(size);
                                            }
                                            else
                                            {
                                                imageTo = "";
                                            }

                                            resized.Write(imageTo);
                                        }
                                    }

                                }
                            });
                        }

                        isUploaded = true;
                        message = "File uploaded successfully!";

                    }
                    catch (Exception ex)
                    {
                        message = string.Format("File upload failed: {0}", ex.Message);
                    }
                }
        }
        return Json(new { isUploaded = isUploaded, message = message }, "text/html");
    }

    #endregion

    #region Private Methods

    /// <summary>
    /// Creates the folder if needed.
    /// </summary>
    /// <param name="path">The path.</param>
    /// <returns></returns>
    private bool CreateFolderIfNeeded(string path)
    {
        bool result = true;
        if (!Directory.Exists(path))
        {
            try
            {
                Directory.CreateDirectory(path);
            }
            catch (Exception)
            {
                /*TODO: You must process this exception.*/
                result = false;
            }
        }
        return result;
    }

    private void Unsharpmask(MagickImage resized, int size)
    {
        if (size == 2048)
            resized.Unsharpmask(2, 1, 1.7, 0.2);
        else if (size == 1600)
            resized.Unsharpmask(1.6, 0.5, 1.7, 0.25);
        else if (size == 1024)
            resized.Unsharpmask(2.8, 1, 0.7, 0.2);
        else if (size == 800)
            resized.Unsharpmask(1.2, 0.8, 0.7, 0.08);
        else if (size == 640)
            resized.Unsharpmask(2, 1, 0.7, 0.02);
        else if (size == 500)
            resized.Unsharpmask(1.5, 0.8, 1, 0.02);
        else if (size == 320)
            resized.Unsharpmask(1.5, 0.6, 0.7, 0.02);
        else if (size == 240)
            resized.Unsharpmask(1.3, 1.5, 1.9, 0.01);
        else if (size == 150)
            resized.Unsharpmask(0.5, 1, 0.5, 0.002);
        else if (size == 100)
            resized.Unsharpmask(0.8, 0.4, 2.5, 0);
        else if (size == 75)
            resized.Unsharpmask(2, 1, 1.8, 0.05);
        else if (size == 50)
            resized.Unsharpmask(1, 0.4, 1.8, 0.02);
        else
            throw new NotImplementedException();
    }

    private string GetOutputName(int size)
    {
        string imagename = "";



        if (size == 2048)
        {
            imagename = "_1.jpg";

        }
        else if (size == 1600)
        {

            imagename = "_2.jpg";

        }
        else if (size == 1024)
        {

            imagename = "_3.jpg";

        }
        else if (size == 800)
        {
            imagename = "_4.jpg";

        }
        else if (size == 640)
        {
            imagename = "_5.jpg";

        }
        else if (size == 500)
        {

            imagename = "_6.jpg";

        }
        else if (size == 320)
        {

            imagename = "_7.jpg";

        }
        else if (size == 240)
        {

            imagename = "_8.jpg";

        }
        else if (size == 150)
        {

            imagename = "_9.jpg";

        }
        else if (size == 100)
        {

            imagename = "_10.jpg";

        }
        else if (size == 75)
        {

            imagename = "_11.jpg";

        }
        else if (size == 50)
        {

            imagename = "_12.jpg";

        }
        else
        {
            imagename = "_noimage.jpg";
        }

        return imagename;
    }

    #endregion

以上是上述代码的工作示例:

2 个答案:

答案 0 :(得分:3)

由Mark Setchell发布,您不应该继续从磁盘读取图像,而是克隆原始图像。模糊操作也是一项昂贵的操作,您只需要执行一次。它可能也有助于在并行中进行循环,但在我的机器上这实际上更慢。

您的代码大致翻译为以下示例。下次也可以在这里寻求帮助:https://magick.codeplex.com/discussions

void Resize()
{
  using (MagickImage original = new MagickImage("original.jpg"))
  {
    original.AutoOrient();
    original.Strip();
    original.ColorSpace = ColorSpace.Lab;
    original.SetAttribute("density", "72x72");
    original.Blur(1, 0.375);

    int[] sizes = new int[] { 2048, 1600, 1024, 800, 640 };
    //Parallel.For(0, sizes.Length, delegate(int index)
    for (int i = 0; i < sizes.Length; i++)
    {
      int size = sizes[index];

      if (original.Width <= size && original.Height <= size)
        return;

      using (MagickImage resized = original.Clone())
      {
        if (size == 2048)
          resized.SetDefine(MagickFormat.Jpeg, "sampling-factor", "4:4:4");

        resized.FilterType = FilterType.LanczosSharp;
        resized.Resize(size, size);

        resized.ColorSpace = ColorSpace.sRGB;
        Unsharpmask(resized, size);

        resized.Quality = 85;

        resized.Write(GetOutputName(size));
      }
    }//);
  }
}

private void Unsharpmask(MagickImage resized, int size)
{
  if (size == 2048)
    resized.Unsharpmask(2, 1, 1.7, 0.2);
  else if (size == 1600)
    resized.Unsharpmask(1.6, 0.5, 1.7, 0.25);
  else if (size == 1024)
    resized.Unsharpmask(2.8, 1, 0.7, 0.2);
  else if (size == 800)
    resized.Unsharpmask(1.2, 0.8, 0.7, 0.08);
  else if (size == 640)
    resized.Unsharpmask(2, 1, 0.7, 0.02);
  else
    throw new NotImplementedException();
}

string GetOutputName(int size)
{
  return "ModifyThisMethod.jpg";
}

答案 1 :(得分:1)

我不是C#的专家,但我对ImageMagick有相当多的经验,所以我只是想帮助你朝着正确的方向前进,但可能无法给你确切的答案。

您希望避免多次从磁盘读取图像并反复重复执行相同的处理 - 尤其是在较高分辨率下。在命令行中,您将按以下方式实现代码:

convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85%\
        \( +clone -resize 2048x2048! -colorspace sRGB -unsharp 2x1+1.7+0.2      -write 0.jpg +delete \) \
        \( +clone -resize 1200x1200! -colorspace sRGB -unsharp 1.6x0.5+1.7+0.25 -write 1.jpg +delete \) \
        \( +clone -resize 1000x1000! -colorspace sRGB -unsharp 2.8x1+0.7+0.2    -write 2.jpg +delete \) \
        \( +clone -resize 800x800!   -colorspace sRGB -unsharp 2x1+0.7+0.02     -write 3.jpg +delete \) \
        \( +clone -resize 600x600!   -colorspace sRGB -write 4.jpg +delete \) \
        \( +clone -resize 500x500!   -colorspace sRGB -write 5.jpg +delete \) \
        \( +clone -resize 300x300!   -colorspace sRGB -write 6.jpg +delete \) \
        \( +clone -resize 100x100!   -colorspace sRGB -write 7.jpg +delete \) \
          -resize 75x75 -colorspace sRGB 8.jpg

这会在第一行中读取ONCE中的图像,尽可能多地预先处理(方向,条带,颜色空间Lab和Lanczos过滤)以使其为剩余步骤设置,然后,对于每个后续大小它克隆了预先Lanczosed,已经是Lab-space图像(内存到内存,而不是磁盘),并进行过滤,模糊,调整大小和颜色空间转换,并将每个大小写入磁盘。

使用5000 x 3500像素的原始图像,上述序列在合理的规格iMac上运行17秒。也许您可以看到如何使序列适应您的C#环境。在最坏的情况下,也许你可以从C#中解脱出来并使用我的命令变体。

上述代码没有利用并行化,但确实避免了不必要的操作。另一种方法是通过使用后台任务来利用所有CPU内核和并行化(请注意每行末尾的&)但必须重复一些操作,因此您可以考虑采用这种方式。

convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 2048x2048! -colorspace sRGB -unsharp 2x1+1.7+0.2 0.jpg &
convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 1200x1200! -colorspace sRGB -unsharp 2x1+1.7+0.1 1.jpg &
convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 1000x1000! -colorspace sRGB -unsharp 2x1+1.7+0.2 2.jpg &
convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 800x800! -colorspace sRGB -unsharp 2x1+1.7+0.2 3.jpg &
convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 600x600! -colorspace sRGB -unsharp 2x1+1.7+0.2 4.jpg &
convert original.jpg -auto-orient -strip -colorspace Lab -filter lanczos -quality 85% -resize 500x500! -colorspace sRGB -unsharp 2x1+1.7+0.2 5.jpg &
convert original.jpg -auto-orient -strip -quality 85% -resize 300x300! 6.jpg &
convert original.jpg -auto-orient -strip -quality 85% -resize 100x100! 7.jpg &
convert original.jpg -auto-orient -strip -quality 85% -resize 75x75!   8.jpg &
wait  # for all taks to complete

上述脚本大约需要18秒。如果,我从基本上改变它,一切都是并行完成的(最后是&):

convert ... &
convert ... &
...
wait

到此,所有内容按顺序完成,这是您的代码所做的:

convert ...
convert ...
...
convert ...

它需要1分20秒 - 或者长4倍。

我的观点是,或者像我的第一个例子那样,你一次性以优化顺序完成所有事情,或者并行完成所有事情 - 也许你可以在C#中使用线程 - 对不起我不知道那种语言或环境。