【问题标题】:Resizing animated gif with dotimage使用 dotimage 调整动画 gif 的大小
【发布时间】:2011-07-18 11:45:13
【问题描述】:

我有一段代码可以调整动画 gif 的大小。 如果它有帮助,代码将始终将图像调整为更小的尺寸。 (暂时不需要做大)

我正在使用 Atalasoft 的 dotimage 库及其示例代码来进行实际的重采样。 该代码应该从磁盘读取动画 gif,遍历帧并将每个帧的大小调整为新的大小。 当 gif 动画包含相同大小的帧但使用不同大小的帧调整动画的大小时,这可以正常工作(调整大小后帧不会正确地相互重叠),我认为这是因为代码没有计算新的偏移量正确。

我认为是这行代码没有正确计算偏移量: 点point = new Point((int)(frame.Location.X * ratio), (int)(frame.Location.Y * ratio));

这是完整的调整大小例程:

    static private void GenerateGifImage(FileStream fileStream, int OutputWidth, int OutputHeight)
    {            
        // MemoryStream InputStream = new MemoryStream();
        FileStream InputStream = fileStream;
        // fileStream.Write(InputStream.GetBuffer(), 0, (int)InputStream.Position);
        // InputStream.Seek(0, SeekOrigin.Begin);
        Image InputImage = Image.FromStream(InputStream, true, false);

        // this will invalidate the underlying image object in InputImage but the class properties 
        // will still accessible until the object is disposed
        InputStream.Seek(0, SeekOrigin.Begin);

        ImageInfo imageInfo = RegisteredDecoders.GetImageInfo(InputStream);
        InputStream.Seek(0, SeekOrigin.Begin);

        GifDecoder gifDecoder = new GifDecoder();
        int count = gifDecoder.GetFrameCount(InputStream);

        GifFrameCollection gifFrameCollection = new GifFrameCollection();
        gifFrameCollection.Height = OutputHeight;
        gifFrameCollection.Width = OutputWidth;
        // gifFrameCollection.Height = gifDecoder.Frames.Height;
        // gifFrameCollection.Width = gifDecoder.Frames.Width;

        double ratio;
        if (InputImage.Height > InputImage.Width)
        {
            ratio = (double)OutputHeight / (double)InputImage.Height;
        }
        else
        {
            ratio = (double)OutputWidth / (double)InputImage.Width;
        }

        for (int i = 0; i < count; i++)
        {
            GifFrame frame = gifDecoder.Frames[i];

            Rectangle rectangle = new Rectangle(Point.Empty, frame.Image.Size);

            int frameWidth = (int)(frame.Image.Width * ratio);
            int frameHeight = (int)(frame.Image.Height * ratio);

            // account for erratic rounding, seems illogical but has happened earlier when using floats instead of doubles 
            if (frameWidth > OutputWidth)
            {
                frameWidth = OutputWidth;
            }
            if (frameHeight > OutputHeight)
            {
                frameHeight = OutputHeight;
            }

            Size size = new Size(frameWidth, frameHeight);
            // only resize if we have a measureable dimension
            if (size.Width > 0 && size.Height > 0)
            {
                // ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.NearestNeighbor);
                ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.NearestNeighbor);
                AtalaImage atalaImage = resampleCommand.Apply(frame.Image).Image;
                // save the image for debugging
                // atalaImage.Save("frame" + i.ToString() + ".gif", ImageType.Gif, null);
                // frame.Image.Save("frame-orig" + i.ToString() + ".gif", ImageType.Gif, null);

                // AtalaImage atalaImage = frame.Image;
                Point point = new Point((int)(frame.Location.X * ratio), (int)(frame.Location.Y * ratio));
                // Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
                gifFrameCollection.Add(new GifFrame(atalaImage, point, frame.DelayTime, frame.Interlaced, frame.FrameDisposal, frame.TransparentIndex, frame.UseLocalPalette));
            }
        }
        FileStream saveStream = new FileStream("resized.gif", FileMode.Create, FileAccess.Write, FileShare.Write);
        GifEncoder gifSave = new GifEncoder();
        gifSave.Save(saveStream, gifFrameCollection, null);
        saveStream.Close();
    }

【问题讨论】:

    标签: c# graphics animated-gif dotimage


    【解决方案1】:

    我在 Atalasoft 工作

    我对此进行了研究——您的代码绝对正确,并且可以在大小不等的帧上正常工作。你计算的点是正确的。

    问题在于,在您的 3 帧 GIF 中,您的第二帧和第三帧被精确地覆盖在第一帧之上,并使用非常复杂的透明蒙版通过它们显示第一帧。当您的图像被重新采样为新尺寸时,遮罩可能不再精确 - 因为您将大小调整为宽度和高度上只有一个像素的差异,所以此遮罩无法匹配。

    这个问题有几种解决方案

    1. 将第 2 帧叠加到第 1 帧上,然后重新采样并改用该图像
    2. 执行 #1,然后提取第 2 帧的矩形
    3. 使用裁剪而不是重新采样 - 这似乎是最好的,因为它只有 1 个像素。

    我为你编写了 #3 代码——效果很好

        static private void GenerateGifImage(FileStream fileStream, int OutputWidth, int OutputHeight)
        {            
            // MemoryStream InputStream = new MemoryStream();
            FileStream InputStream = fileStream;
            // fileStream.Write(InputStream.GetBuffer(), 0, (int)InputStream.Position);
            // InputStream.Seek(0, SeekOrigin.Begin);
            Image InputImage = Image.FromStream(InputStream, true, false);
    
            // this will invalidate the underlying image object in InputImage but the class properties 
            // will still accessible until the object is disposed
            InputStream.Seek(0, SeekOrigin.Begin);
    
            ImageInfo imageInfo = RegisteredDecoders.GetImageInfo(InputStream);
            InputStream.Seek(0, SeekOrigin.Begin);
    
            GifDecoder gifDecoder = new GifDecoder();
            int count = gifDecoder.GetFrameCount(InputStream);
    
            GifFrameCollection gifFrameCollection = new GifFrameCollection();
            gifFrameCollection.Height = OutputHeight;
            gifFrameCollection.Width = OutputWidth;
    
            double ratio;
            if (InputImage.Height > InputImage.Width)
            {
                ratio = (double)OutputHeight / (double)InputImage.Height;
            }
            else
            {
                ratio = (double)OutputWidth / (double)InputImage.Width;
            }
    
            for (int i = 0; i < count; i++)
            {
                GifFrame frame = gifDecoder.Frames[i];
    
                Rectangle rectangle = new Rectangle(Point.Empty, frame.Image.Size);
    
                int newframeWidth = frame.Image.Width;
                int newframeHeight = frame.Image.Height;
                if (newframeWidth > OutputWidth || newframeHeight > OutputHeight)
                {
                    newframeWidth = (int)(frame.Image.Width * ratio);
                    newframeHeight = (int)(frame.Image.Height * ratio);
                }
    
                // account for erratic rounding, seems illogical but has happened earlier when using floats instead of doubles 
                if (newframeWidth > OutputWidth)
                {
                    newframeWidth = OutputWidth;
                }
                if (newframeHeight > OutputHeight)
                {
                    newframeHeight = OutputHeight;
                }
    
                Size size = new Size(newframeWidth, newframeHeight);
                // only resize if we have a measureable dimension
                if (size.Width > 0 && size.Height > 0)
                {
                    //ResampleCommand resampleCommand = new ResampleCommand(rectangle, size, ResampleMethod.);
                    AtalaImage atalaImage = frame.Image;
                    if (newframeWidth != frame.Image.Width || newframeHeight != frame.Image.Height)
                    {
                        CropCommand command = new CropCommand(new Rectangle(new Point(0, 0), size));
                        atalaImage = command.Apply(frame.Image).Image;
                    }
                    // AtalaImage atalaImage = frame.Image;
                    Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
                    // Point point = new Point((int)(frame.Location.X), (int)(frame.Location.Y));
                    gifFrameCollection.Add(new GifFrame(atalaImage, point, frame.DelayTime, frame.Interlaced, frame.FrameDisposal, frame.TransparentIndex, frame.UseLocalPalette));
                }
            }
            FileStream saveStream = new FileStream("resized.gif", FileMode.Create, FileAccess.Write, FileShare.Write);
            GifEncoder gifSave = new GifEncoder();
            gifSave.Save(saveStream, gifFrameCollection, null);
            saveStream.Close();
        }
    

    【讨论】:

      【解决方案2】:

      如果您使用不同的帧大小,则计算出的比率值不正确。您应该计算每个单独帧的比率,以便您关注的行使用正确的比率。我对框架不熟悉,所以不能给你一个准确的例子;但它应该看起来像这样:

      GifFrame frame = gifDecoder.Frames[i];
      double frameRatio;
      if (frame.Height > frame.Width)
      {
         frameRatio = (double)OutputHeight / (double)frame.Height;
      }
      else
      {
         frameRatio = (double)OutputWidth / (double)frame.Width;
      }
      
      ...
      
      Point point = new Point((int)(frame.Location.X * frameRatio), (int)(frame.Location.Y * frameRatio));
      

      【讨论】:

      • 我明白了,我没有考虑 x 和 y 偏移量。
      • 我尝试重新计算每帧的比率,然后重新采样每一帧,但结果偏移量和图像大小超过了 gif 动画的大小。在上面的代码中,您正在计算比率,假设每个帧都将调整为 OutputWidth - 情况并非如此,如果 gif 动画是 480 x 120 并且我们必须将其调整为 479 x 119,那么可能会有类似的帧80 x 20(使用 x y 偏移来对齐)在这种情况下,每个帧都必须“按比例”调整大小,因此代码在 for 循环之外“全局”计算比率并应用每帧的比率
      猜你喜欢
      • 2011-08-09
      • 2011-08-31
      • 2018-04-18
      • 2012-09-07
      • 2011-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      相关资源
      最近更新 更多