JPG學習筆記5(附完整代碼)

  JPG壓縮的第4步是哈夫曼編碼。下面主要介紹JPEG是若是進行哈夫曼編碼的。html

圖片引用自"Compressed Image File Formats JPEG, PNG, GIF, XBM, BMP - John Miano"[1]git

1.AC數據的哈夫曼Symbol.

對於AC數據而言,須要編碼的前4位表明這個數據以前有多少個0,後4位表明當前值的Magnitude Value。github

 AC數據的編碼是以ZigZag的順序進行的。學習

以下圖爲例,從1開始前面有0個0, 數值大小爲1, magnitude value爲1,須要編碼的symbol爲0x01;ui

接着走到3處,前面有5個0,數值大小爲3, magnitude value 爲2,須要編碼的symbol爲0x52;編碼

以此類推:spa

惟一有的2個額外的狀況code

0x00表明後面的數據都爲0component

0xF0表明16個0orm

 

 

  總共的symbol數量 = (爲0的個數)16 * 10(不一樣的maginitude) + 2 (特殊狀況) = 162。 

2.DC數據的哈夫曼Symbol

  DC數據存的是difference,即當前Block的DC值減去上一個Block的DC值。

  以下可知DC symbol總共有12個

 

3.JPEG默認哈夫曼編碼

JPEG提供了默認的huffmanTable(emprically good)[2],以下

 

 

 

 

 引用"https://www.impulseadventure.com/photo/optimized-jpeg.html"

 也能夠本身根據圖片生成huffmanCode,代碼以下

void JPG::huffmanCoding() {
    /*****************************************建立yDC_Table*********************************************/
    int lastYDC = 0;
    uint componentID = 1;
    //建立YDC_Table
    for (uint i = 0; i < mcuHeight; i++) {
        for (uint j = 0; j < mcuWidth; j++) {
            MCU& currentMCU = data[i * mcuWidth + j];
            //iterate over 每個component Y, cb cr
            //遍歷block
            for(uint ii = 0; ii < getVerticalSamplingFrequency(componentID); ii++) {
                for(uint jj = 0; jj < getHorizontalSamplingFrequency(componentID); jj++) {
                    Block& currentBlock = currentMCU[componentID][ii * getHorizontalSamplingFrequency(componentID) + jj];
                    int difference = currentBlock[0] - lastYDC; //DC份量是encode difference
                    lastYDC = currentBlock[0];
                    byte symbol = getBinaryLengthByValue(difference); //Y的2進制的長度就是symbol的值
                    yDC.countOfSymbol[symbol]++;
                }
            }
        }
    }
    yDC.generateHuffmanCode(); 
     /*****************************************建立 yAC_Table*********************************************/
    for (uint i = 0; i < mcuHeight; i++) {
        for (uint j = 0; j < mcuWidth; j++) {
            MCU& currentMCU = data[i * mcuWidth + j];
            //遍歷block
            for(uint ii = 0; ii < getVerticalSamplingFrequency(componentID); ii++) {
                for(uint jj = 0; jj < getHorizontalSamplingFrequency(componentID); jj++) {
                    Block& currentBlock = currentMCU[componentID][ii * getHorizontalSamplingFrequency(componentID) + jj];
                    uint numZero = 0;
                    for(uint k = 1; k < 64; k++) {
                        if(currentBlock[ZIG_ZAG[k]] == 0) {
                            numZero++;
                            if(numZero == 16) {
                                if(isRemainingAllZero(currentBlock, k + 1)) {
                                    yAC.countOfSymbol[0x00]++;
                                    break;
                                } else {
                                    yAC.countOfSymbol[0xF0]++;//16個0
                                    numZero = 0;
                                }
                            }
                        } else {
                            byte lengthOfCoefficient = getBinaryLengthByValue(currentBlock[ZIG_ZAG[k]]);
                            byte symbol = (numZero << 4) + lengthOfCoefficient;
                            yAC.countOfSymbol[symbol]++;
                            numZero = 0;
                        }
                    }
                }
            }
        }
    }
    yAC.generateHuffmanCode();
    /*****************************************建立chromaDC_Table*********************************************/

    int lastChromaDC = 0;
    for(uint componentID = 2; componentID <=3; componentID++) {
        for (uint i = 0; i < mcuHeight; i++) {
            for (uint j = 0; j < mcuWidth; j++) {
                MCU& currentMCU = data[i * mcuWidth + j];
                //iterate over 每個component Y, cb cr
                //遍歷block
                for(uint ii = 0; ii < getVerticalSamplingFrequency(componentID); ii++) {
                    for(uint jj = 0; jj < getHorizontalSamplingFrequency(componentID); jj++) {
                        Block& currentBlock = currentMCU[componentID][ii * getHorizontalSamplingFrequency(componentID) + jj];
                        int difference = currentBlock[0] - lastChromaDC; //DC份量是encode difference
                        lastChromaDC = currentBlock[0];
                        byte symbol = getBinaryLengthByValue(difference); //Y的2進制的長度就是symbol的值
                        chromaDC.countOfSymbol[symbol]++;
                    }
                }
            }
        }
    }
    chromaDC.generateHuffmanCode();
    /*****************************************建立chromaAC_Table*********************************************/
    for(uint componentID = 2; componentID <=3; componentID++) {
        for (uint i = 0; i < mcuHeight; i++) {
            for (uint j = 0; j < mcuWidth; j++) {
                MCU& currentMCU = data[i * mcuWidth + j];
                //遍歷block
                for(uint ii = 0; ii < getVerticalSamplingFrequency(componentID); ii++) {
                    for(uint jj = 0; jj < getHorizontalSamplingFrequency(componentID); jj++) {
                        Block& currentBlock = currentMCU[componentID][ii * getHorizontalSamplingFrequency(componentID) + jj];
                        uint numZero = 0;
                        for(uint k = 1; k < 64; k++) {
                            if(currentBlock[ZIG_ZAG[k]] == 0) {
                                numZero++;
                                if(numZero == 16) {
                                    if(isRemainingAllZero(currentBlock, k + 1)) {
                                        chromaAC.countOfSymbol[0x00]++;
                                        break;
                                    } else {
                                        chromaAC.countOfSymbol[0xF0]++;//16個0
                                        numZero = 0;
                                    }
                                }
                            } else {
                                byte lengthOfCoefficient = getBinaryLengthByValue(currentBlock[ZIG_ZAG[k]]);
                                byte symbol = (numZero << 4) + lengthOfCoefficient;
                                chromaAC.countOfSymbol[symbol]++;
                                numZero = 0;
                            }
                        }
                    }
                }
            }
        }
    }
    chromaAC.generateHuffmanCode();

}
 void generateHuffmanCode() {
        std::vector<LinkedSymbol> symbols;
        //遍歷每一個出現的symbol, add to vectors
        for(uint symbol = 0; symbol < 256; symbol++) {
            if(countOfSymbol[symbol] == 0) 
                continue;
            Symbol* s = new Symbol(symbol, countOfSymbol[symbol], 0, nullptr);
            LinkedSymbol linkedSymbol;
            linkedSymbol.symbol = s;
            linkedSymbol.weight = s->weight;
            symbols.push_back(linkedSymbol);
        }
        
        
        // FF是一個不會出現的symbol,做爲咱們的dummy symbol 防止one bit stream 的出現  好比11111, 這樣就能夠防止compressdata中出現FF的可能
        Symbol* dummySymbol = new Symbol(0xFF, 1, 0, nullptr); 
        LinkedSymbol dymmyLinkedSymbol;
        dymmyLinkedSymbol.symbol = dummySymbol;
        dymmyLinkedSymbol.weight = dummySymbol->weight;
        symbols.push_back(dymmyLinkedSymbol);
        
        

        //合併的過程
        while(symbols.size() != 1) {
            
            //leastWeight
            LinkedSymbol least = getLeastWeightLinkedSymbol(symbols);
            //second Least Weight
            LinkedSymbol second = getLeastWeightLinkedSymbol(symbols);
            //add two weights
            least.weight = least.weight + second.weight;

            //linked two linkedsymbols;
            Symbol* temp = second.symbol;
            while(temp->nextSymbol != nullptr)
                temp = temp->nextSymbol;
            temp->nextSymbol = least.symbol;
            least.symbol = second.symbol;
            //把每一個symbol加1 codeLength,而且加入到 
            for(auto i = least.symbol; i != nullptr; i = i->nextSymbol) {
                i->codeLength++;
            }
            symbols.push_back(least);
        }

        //放入sortedSymbols
        for(Symbol* i = symbols[0].symbol; i != nullptr; i = i->nextSymbol) {
            sortedSymbol.push_back(*i);
        }

        //排序,而且把dummy symbol 放在最後面;
        std::sort(sortedSymbol.begin(), sortedSymbol.end(), comp);

        //釋放內存
        Symbol* temp = symbols[0].symbol;
        while(temp != nullptr) {
            auto t = temp->nextSymbol;
            delete temp;
            temp = t;
        }

        //長度爲n的code的個數
        //生成codeLengthCount for each codeLength;
        for (auto it = sortedSymbol.cbegin(); it != sortedSymbol.cend(); it++) {
            codeCountOfLength[it->codeLength]++;
        }

        //規定codeLength不能大於16, 套用書上的方法實現了一下
        for(uint ii = 32; ii >= 17; ii--) {
            while(codeCountOfLength[ii] != 0) {
                uint jj = ii - 2;
                while(codeCountOfLength[jj] == 0)
                    jj--;
                codeCountOfLength[ii] = codeCountOfLength[ii] - 2;
                codeCountOfLength[ii - 1] = codeCountOfLength[ii - 1] + 1;
                codeCountOfLength[jj + 1] = codeCountOfLength[jj + 1] + 2;
                codeCountOfLength[jj] = codeCountOfLength[jj] - 1;
            }
        }

        uint index = 1; //codeLength賦值回去
        for (auto it = sortedSymbol.begin(); it != sortedSymbol.end(); it++) {
            if(codeCountOfLength[index] != 0) {
                it->codeLength = index;
                codeCountOfLength[index]--;
            } else {
                index++;
                it--;
            }
        }

        
        //生成huffmanCode for each symbol
        uint huffmanCode = 0;
        uint currentLength = 1;
        for (auto it = sortedSymbol.begin(); it != sortedSymbol.end(); it++) {
            if(currentLength == it->codeLength) {
                it->code = huffmanCode++;
                codeOfSymbol[it->symbol] = it->code;
                codeLengthOfSymbol[it->symbol] = it->codeLength;
            } else {
                huffmanCode = huffmanCode << 1;
                currentLength++;
                it--;
            }
        }
    }

 所有代碼在https://github.com/Cheemion/JPEG_COMPRESS/tree/main/Day5

 完結

 Thanks for reading.

                                                                                                                                                                                                                                                                                >>>> JPG學習筆記6


 

參考資料

[1]https://github.com/Cheemion/JPEG_COMPRESS/blob/main/resource/Compressed%20Image%20File%20Formats%20JPEG%2C%20PNG%2C%20GIF%2C%20XBM%2C%20BMP%20-%20John%20Miano.pdf

[2]https://www.impulseadventure.com/photo/optimized-jpeg.html

相關文章
相關標籤/搜索