Beispiel #1
0
        /// <summary>
        /// ハフマン圧縮
        /// </summary>
        /// <param name="data">圧縮前データ</param>
        /// <returns>圧縮後データ</returns>
        internal static byte[] Encode(byte[] data)
        {
            // 圧縮前データのサイズ確認(32bit=4byteの数値)
            byte[] size = BitConverter.GetBytes(data.Count());
            // byteの中のバイト別出現回数を集計
            // ハフマン木を作成
            HaffmanNode root = GenerateHaffmanTree(GetFrequency(data));

            // debug
            if (debugMode)
            {
                DebugHaffmanTree(root, 0, "");
            }
            // ハフマンコード表を作成
            Dictionary <byte, bool[]> haffmanDict = GetHaffmanCodeTable(root);

            // debug
            if (debugMode)
            {
                DebugHaffmanTable(haffmanDict);
            }
            // ハフマン符号化(匿名メソッド使用)
            IEnumerable <bool[]> haffmanCodes = data.Select(b => { return(haffmanDict[b]); });
            // ハフマン木をbit化
            IEnumerable <bool> treeBits = ConvertHaffmanTreeToBits(root);

            // 書き込む全ビットデータからバイトデータ取得
            // sizeデータの後ろに取得したバイトデータを連結
            return(size.Concat(GetAllBytes(treeBits.ToArray(), haffmanCodes).ToArray()).ToArray());
        }
Beispiel #2
0
        private static void DebugHaffmanTree(HaffmanNode node, int indent, string bitCode)
        {
            if (indent == 0)
            {
                Console.WriteLine();
                Console.WriteLine("DebugHaffmanTree");
            }
            List <bool> bits = new List <bool>();

            bits.AddRange(bits);

            int    indent_   = indent > 0 ? indent - 1 : 0;
            string indentStr = new string(' ', 2 * indent_) + "  --";

            if (node.IsLeaf)
            {
                Console.WriteLine(indentStr + $"{node.Value.ToString("X2")}({bitCode})");
            }
            else
            {
                Console.WriteLine(indentStr + $"");
                if (node.Left != null)
                {
                    DebugHaffmanTree(node.Left, indent + 1, bitCode + "0");
                }
                if (node.Right != null)
                {
                    DebugHaffmanTree(node.Right, indent + 1, bitCode + "1");
                }
            }
        }
Beispiel #3
0
 /// <summary>
 /// 子ノードを持つ節を生成するためのコンストラクタ
 /// </summary>
 /// <param name="left">左子ノード</param>
 /// <param name="right">右子ノード</param>
 public HaffmanNode(HaffmanNode left, HaffmanNode right)
 {
     this.IsLeaf = false;
     // 節の出現頻度は、左右ノードの出現頻度の和
     this.Frequency = left.Frequency + right.Frequency;
     // 左右のノード情報を代入
     this.Left  = left;
     this.Right = right;
 }
Beispiel #4
0
        /// <summary>
        /// ハフマン解凍
        /// </summary>
        /// <param name="data">解凍前データ</param>
        /// <returns>解凍後データ</returns>
        internal static byte[] Decode(byte[] data)
        {
            // 圧縮済みデータ
            IEnumerable <bool> bits = data.Skip(4).BytesToBits();
            // 前4byte 圧縮前ファイルサイズ
            int size = BitConverter.ToInt32(data.Take(4).ToArray(), 0);

            // ハフマン木を再構成
            HaffmanNode root = RegenerateHaffmanTree(ref bits);

            // debug
            if (debugMode)
            {
                DebugHaffmanTree(root, 0, "");
            }
            // この時点でbitsデータから前方のハフマン木データが消えている

            // デコード処理本体
            // 処理済みバイト数カウンタ
            int         cnt    = 0;
            HaffmanNode node   = root;
            List <byte> output = new List <byte>();

            foreach (bool bit in bits.ToArray())
            {
                if (node.IsLeaf)
                {
                    // 葉ノード到達
                    output.Add(node.Value);
                    // 先頭に戻る
                    node = root;
                    // カウンタが圧縮前ファイルサイズと一致したら処理終了
                    cnt++;
                    if (size <= cnt)
                    {
                        break;
                    }
                }
                // ここはelse集約不可
                if (!node.IsLeaf)
                {
                    // 葉ノードでない
                    if (bit)
                    {
                        // bitがtrueなら右の子ノードを探索
                        node = node.Right;
                    }
                    else
                    {
                        // bitがfalseなら左の子ノードを探索
                        node = node.Left;
                    }
                }
            }
            return(output.ToArray());
        }
Beispiel #5
0
        public static void DebugString(string str)
        {
            byte[]      bytes = Encoding.ASCII.GetBytes(str);
            int[]       freq  = GetFrequency(bytes);
            HaffmanNode root  = GenerateHaffmanTree(freq);

            DebugHaffmanTree(root, 0, "");
            Dictionary <byte, bool[]> table = GetHaffmanCodeTable(root);

            DebugHaffmanTable(table);
        }
Beispiel #6
0
        /// <summary>
        /// ハフマンコード辞書を作成
        /// </summary>
        /// <param name="node">ハフマン木ルートノード</param>
        /// <returns>ハフマンコード辞書</returns>
        private static Dictionary <byte, bool[]> GetHaffmanCodeTable(HaffmanNode node)
        {
            Dictionary <byte, bool[]> dictionaly = new Dictionary <byte, bool[]>();

            for (int i = 0; i <= byte.MaxValue; i++)
            {
                byte target = (byte)i;
                dictionaly[target] = GenerateHaffmanCode(node, target);
            }
            return(dictionaly);
        }
Beispiel #7
0
        /// <summary>
        /// ハフマン木生成
        /// </summary>
        /// <param name="freq">頻度配列</param>
        /// <returns>ハフマン木ルートノード</returns>
        private static HaffmanNode GenerateHaffmanTree(int[] freq)
        {
            // ノードリスト
            List <HaffmanNode> nodeList = new List <HaffmanNode>();

            for (int i = 0; i <= byte.MaxValue; i++)
            {
                // 頻度0であれば、ノード追加処理はいらないので次の値へ
                if (freq[i] == 0)
                {
                    continue;
                }
                // ノードリスト最後尾にノード追加
                nodeList.Add(new HaffmanNode((byte)i, freq[i]));
            }

            // ノードリストの中身がのこり1件になるまでループ
            while (1 < nodeList.Count)
            {
                // 先頭に頻度最小値が来るようにソート
                // 1ループで1つノードが増えるので、どうしても毎ループでソートが必要
                nodeList = nodeList.OrderBy(e => e.Frequency).ToList();

                // 出現頻度最小のノード⇒左子ノード
                HaffmanNode left = nodeList[0];
                // 出現頻度が2番目に少ないノード⇒右子ノード
                HaffmanNode right = nodeList[1];
                // 枝にしたノードをリストから消去
                nodeList.RemoveAt(0);
                nodeList.RemoveAt(0);

                // 左右子ノードの親ノードを生成してリストに追加
                nodeList.Add(new HaffmanNode(left, right));
            }
            // キューに残っているノードがハフマン木のルート(最上部)
            HaffmanNode root = nodeList[0];

            // 1種類のデータのみ出現する場合は、ダミーノードを用意
            if (root.IsLeaf)
            {
                return new HaffmanNode()
                       {
                           IsLeaf = false, Left = root, Right = root
                       }
            }
            ;
            // 左右子ノードはルート
            return(root);
        }
Beispiel #8
0
        /// ハフマンコードを作成
        /// </summary>
        /// <param name="node">ハフマン木ルートノード</param>
        /// <param name="target">コードを作成したいデータ</param>
        /// <param name="codeBits">現在のハフマンコード</param>
        /// <returns></returns>
        private static bool[] GenerateHaffmanCode(HaffmanNode node, byte target, List <bool> codeBits = null)
        {
            // 初回呼び出しならbitリスト初期化
            if (codeBits == null)
            {
                codeBits = new List <bool>();
            }
            // 末端のノード & 現在ノードと一致
            if (node.IsLeaf && (node.Value == target))
            {
                return(codeBits.ToArray());
            }
            // 左側探索
            if (node.Left != null)
            {
                // 左側なので0:falseをbitリストに追加
                codeBits.Add(false);
                bool[] result = GenerateHaffmanCode(node.Left, target, codeBits);
                if (result != null)
                {
                    return(result);
                }

                // 見つからないならbitリスト追加取り消し
                codeBits.RemoveAt(codeBits.Count - 1);
            }
            // 右側探索
            if (node.Right != null)
            {
                // 右側なので1:trueをbitリストに追加
                codeBits.Add(true);
                bool[] result = GenerateHaffmanCode(node.Right, target, codeBits);
                if (result != null)
                {
                    return(result);
                }

                // 見つからないならbitリスト追加取り消し
                codeBits.RemoveAt(codeBits.Count - 1);
            }
            // 見つからないならnullを返す
            return(null);
        }
Beispiel #9
0
        /// <summary>
        /// ハフマン木自体をビット化
        /// </summary>
        /// <param name="root">ハフマン木ルートノード</param>
        /// <returns></returns>
        private static bool[] ConvertHaffmanTreeToBits(HaffmanNode root)
        {
            List <bool> bits = new List <bool>();

            // スタックを使用して幅優先探索
            Stack <HaffmanNode> stack = new Stack <HaffmanNode>();

            stack.Push(root);

            // stackが空になる=ビット化完了まで繰り返し
            while (0 < stack.Count)
            {
                HaffmanNode node = stack.Pop();
                if (node.IsLeaf)
                {
                    // 葉ノードはtrue出力
                    bits.Add(true);
                    // 実データを8bit出力
                    for (int i = 0; i < 8; i++)
                    {
                        bits.Add((node.Value >> 7 - i & 1) == 1);
                    }
                }
                else
                {
                    // 葉ノードでないならfalse出力
                    bits.Add(false);
                    // 右左の順番で子ノードを積む
                    if (node.Right != null)
                    {
                        stack.Push(node.Right);
                    }
                    if (node.Left != null)
                    {
                        stack.Push(node.Left);
                    }
                }
            }

            return(bits.ToArray());
        }