【问题标题】:Huffman Coding Issue with file size after compression压缩后文件大小的霍夫曼编码问题
【发布时间】:2015-01-23 12:44:59
【问题描述】:

我正在使用 Huffman 代码创建用于压缩任何类型文件的压缩算法,但我可以看到压缩后的大小与原始大小几乎相同。例如,25 mb 视频压缩后占用 24 mb,606 kb 图像压缩后占用 605 kb。下面是我的全部代码。如果我做错了什么,请告诉我。

     public static class ByteValues
      {
        public static Dictionary<byte, string> ByteDictionary;

        public static void AddValues(byte b, string values)
         {
          if (ByteDictionary == null)
          {
          ByteDictionary = new Dictionary<byte, string>();
           }

      ByteDictionary.Add(b, values);
        }

  public static List<List<T>> Split<T>(this List<T> list, int parts)
  {
      int i = 0;
      var splits = from item in list
                   group item by i++ % parts into part
                   select part.ToList();
      return splits.ToList();
  }
}


    public class Node
    {
    public byte value;
    public long freq;
    public Node LeftNode;
    public Node RightNode;

    public void Traverse(string path)
    {
        if (LeftNode == null)
        {
            ByteValues.AddValues(value, path);
        }
        else
        {

            LeftNode.Traverse(path + "0");
            RightNode.Traverse(path + "1");
        }
    }

}

   public partial class MainWindow : Window
{
    Dictionary<byte, long> Bytefreq = new Dictionary<byte, long>();
    string filename;
    List<Node> Nodes = new List<Node>();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {

        OpenFileDialog dialog = new OpenFileDialog();
        dialog.ShowDialog();
        filename = dialog.FileName;
        if (!string.IsNullOrEmpty(filename))
        {
            for (int i = 0; i <= byte.MaxValue; i++)
            {
                Bytefreq.Add((byte)i, 0);
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            worker.RunWorkerAsync();

        }
    }


    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        using (BinaryReader reader = new BinaryReader(File.OpenRead(filename)))
        {
            long length = reader.BaseStream.Length;
            int pos = 0;
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                pbProgress.Maximum = length;
            });

            while (pos < length)
            {
                byte[] inputbytes = reader.ReadBytes(1000000);
                Bytefreq = inputbytes.OrderBy(x => x).GroupBy(x => x).ToDictionary(x => x.Key, x => (long)(Bytefreq[x.Key] + x.Select(l => l).ToList().Count));
                pos = pos + inputbytes.Length;
                worker.ReportProgress(pos);
            }
        }

    }
    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        pbProgress.Value = e.ProgressPercentage;
    }



    void worker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        System.Windows.MessageBox.Show("DONE");
        System.Windows.Application.Current.Shutdown();
    }
    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        pbProgress.Value = 0;
        foreach (KeyValuePair<byte, long> kv in Bytefreq)
        {
            Nodes.Add(new Node() { value = kv.Key, freq = kv.Value });
        }

        while (Nodes.Count > 1)
        {
            Nodes = Nodes.OrderBy(x => x.freq).ThenBy(x => x.value).ToList();
            Node left = Nodes[0];
            Node right = Nodes[1];

            Node newnode = new Node() { LeftNode = left, RightNode = right, freq = left.freq + right.freq };
            Nodes.Remove(left);
            Nodes.Remove(right);
            Nodes.Add(newnode);
        }

        Nodes[0].Traverse(string.Empty);

        BackgroundWorker worker1 = new BackgroundWorker();
        worker1.WorkerReportsProgress = true;
        worker1.DoWork += worker1_DoWork;
        worker1.ProgressChanged += worker_ProgressChanged;
        worker1.RunWorkerCompleted += worker1_RunWorkerCompleted;
        worker1.RunWorkerAsync();

    }
    void worker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        Dictionary<byte, string> bytelookup = ByteValues.ByteDictionary;
        using (BinaryWriter writer = new BinaryWriter(File.Create(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\Test.txt")))
        {
            using (BinaryReader reader = new BinaryReader(File.OpenRead(filename)))
            {
                long length = reader.BaseStream.Length;
                int pos = 0;

                while (pos < length)
                {

                    byte[] inputbytes = reader.ReadBytes(1000000);
                    StringBuilder builder = new StringBuilder();
                    List<string> outputbytelist = inputbytes.Select(b => bytelookup[b]).ToList();

                    outputbytelist.ForEach(x => builder.Append(x));

                    int numOfBytes = builder.ToString().Length / 8;
                    var bytesAsStrings =  builder.ToString().Select((c, i) => new { Char = c, Index = i })
                                                            .GroupBy(x => x.Index / 8)
                                                            .Select(g => new string(g.Select(x => x.Char).ToArray()));
                     byte[] finalbytes = bytesAsStrings.Select(s => Convert.ToByte(s, 2)).ToArray();
                     writer.BaseStream.Write(finalbytes, 0, finalbytes.Length);
                    pos = pos + inputbytes.Length;
                    worker.ReportProgress(pos);
                }
            }
        }
    }


}

【问题讨论】:

  • 我将继续分析您未注释的代码,但您是否期望霍夫曼编码 a) 压缩任何内容,以及 b) 专门压缩视频?
  • 视频和图像通常已经使用霍夫曼或类似的编码,并且可能不容易以无损方式进一步压缩。您可以使用例如更好地压缩您的样本吗? xz还是7z?您期望压缩多少?
  • 压缩压缩不如压缩未压缩。
  • 嗨 Poldie,我想让应用程序适用于所有类型的文件,但必须具备的格式包括 - JPEG、GIF、PNG、TGA、TIFF MOV、QT、AVI、MPEG-1 –
  • 霍夫曼编码只是编写压缩器的第一部分(它本身就是一种压缩器,但通常不是一个非常有效的压缩器)。我认为您需要对它们的确切用途以及它们的工作原理进行一些了解,然后查看数据如何存储在您刚刚指定的格式的文件中,然后您会看到您的'正在做的是通常会增加您处理的文件的大小,而不是减少它们。思想实验:假设你压缩了一个文件;是什么阻止你一次又一次地压缩它?

标签: c# huffman-code


【解决方案1】:

问题在于您尝试压缩的数据的类型。因此,当您说“例如 25 mb 视频压缩后占用 24 mb”时,这里的关键词是 video。众所周知,视频数据很难压缩(很像其他类型的二进制数据,例如音乐或图像)。

如果您需要压缩视频,我会搜索专用编解码器(MP4、MPEG、H.264),但有些可能无法免费使用,因此请注意许可费用。请注意,大多数编解码器都是有损的 - 它们试图保持可见质量,但会从视频中删除其他信息。这些东西大部分都足够好,但在某些时候你可能会注意到伪影。

您也可以尝试使用无损压缩(如 Huffman、gzip、LZ、LZMA、7z,大多数可从7 zip sdk 等获得),但由于其性质,这不会很好地压缩您的数据。基本思想是:数据越像随机噪声,就越难压缩。加分点:您无法使用 any 无损压缩对随机数据进行物理压缩,即使是 1 位 (read about this here)。

【讨论】:

  • 实际上我正在创建 Huffman 压缩器作为我作业的一部分,所以我只能这样做,并且需要支持压缩所需的格式包括 JPEG、GIF、PNG、TGA、 TIFF MOV、QT、AVI、MPEG-1
  • 作为一个作业没问题,但不要指望它压缩太多。如果您将其制作和接口为byte[] Compress(byte[] input) 和类似的解压缩,您可以压缩 any 数据,因为在一天结束时,所有内容都可以表示为 byte[]。您尝试压缩的输入数据已经高度压缩,这意味着它看起来像随机数据。越接近随机性,压缩就越困难,并且在某些时候压缩元数据可能会超过压缩增益,并且输出压缩文件可能会变得更大(因为它包含元数据 + 原始压缩字节)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-08
  • 1970-01-01
相关资源
最近更新 更多