生活随笔
收集整理的這篇文章主要介紹了
算术编码压缩算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
????????算術編碼,是圖像壓縮的主要算法之一。 是一種無損數據壓縮方法,也是一種熵編碼的方法。和其它熵編碼方法不同的地方在于,其他的熵編碼方法通常是把輸入的消息分割為符號,然后對每個符號進行編碼,而算術編碼是直接把整個輸入的消息編碼為一個數,一個滿足(0.0 ≤ n < 1.0)的小數n。
? ? 工作原理:
在給定符號集和符號概率的情況下,算術編碼可以給出接近最優的編碼結果。使用算術編碼的壓縮算法通常先要對輸入符號的概率進行估計,然后再編碼。這個估計越準,編碼結果就越接近最優的結果。
例: 對一個簡單的信號源進行觀察,得到的統計模型如下:
60% 的機會出現符號 中性
20% 的機會出現符號 陽性
10% 的機會出現符號 陰性
10% 的機會出現符號 數據結束符.?(出現這個符號的意思是該信號源'內部中止',在進行數據壓縮時這樣的情況是很常見的。當第一次也是唯一的一次看到這個符號時,解碼器就知道整個信號流都被解碼完成了。)
算術編碼可以處理的例子不止是這種只有四種符號的情況,更復雜的情況也可以處理,包括高階的情況。所謂高階的情況是指當前符號出現的概率受之前出現符號的影響,這時候之前出現的符號,也被稱為上下文。比如在英文文檔編碼的時候,例如,在字母Q或者q出現之后,字母u出現的概率就大大提高了。這種模型還可以進行自適應的變化,即在某種上下文下出現的概率分布的估計隨著每次這種上下文出現時的符號而自適應更新,從而更加符合實際的概率分布。不管編碼器使用怎樣的模型,解碼器也必須使用同樣的模型。
編碼過程的每一步,除了最后一步,都是相同的。編碼器通常需要考慮下面三種數據:
下一個要編碼的符號
當前的區間(在編第一個符號之前,這個區間是[0,1), 但是之后每次編碼區間都會變化)
編碼器將當前的區間分成若干子區間,每個子區間的長度與當前上下文下可能出現的對應符號的概率成正比。當前要編碼的符號對應的子區間成為在下一步編碼中的初始區間。
例: 對于前面提出的4符號模型:
中性對應的區間是 [0, 0.6)
陽性對應的區間是 [0.6, 0.8)
陰性對應的區間是 [0.8, 0.9)
數據結束符對應的區間是 [0.9, 1)
當所有的符號都編碼完畢,最終得到的結果區間即唯一的確定了已編碼的符號序列。任何人使用該區間和使用的模型參數即可以解碼重建得到該符號序列。
實際上我們并不需要傳輸最后的結果區間,實際上,我們只需要傳輸該區間中的一個小數即可。在實用中,只要傳輸足夠的該小數足夠的位數(不論幾進制),以保證以這些位數開頭的所有小數都位于結果區間就可以了。
例: 下面對使用前面提到的4符號模型進行編碼的一段信息進行解碼。編碼的結果是0.538(為了容易理解,這里使用十進制而不是二進制;我們也假設我們得到的結果的位數恰好夠我們解碼。下面會討論這兩個問題)。
像編碼器所作的那樣我們從區間[0,1)開始,使用相同的模型,我們將它分成編碼器所必需的四個子區間。分數0.538落在NEUTRAL坐在的子區間[0,0.6);這向我們提示編碼器所讀的第一個符號必然是NEUTRAL,這樣我們就可以將它作為消息的第一個符號記下來。
然后我們將區間[0,0.6)分成子區間:
中性 的區間是 [0, 0.36) -- [0, 0.6) 的 60%
陽性 的區間是 [0.36, 0.48) -- [0, 0.6) 的 20%
陰性 的區間是 [0.48, 0.54) -- [0, 0.6) 的 10%
數據結束符 的區間是 [0.54, 0.6). -- [0, 0.6) 的 10%
我們的分數 .538 在 [0.48, 0.54) 區間;所以消息的第二個符號一定是NEGATIVE。
我們再一次將當前區間劃分成子區間:
中性 的區間是 [0.48, 0.516)
陽性 的區間是 [0.516, 0.528)
陰性 的區間是 [0.528, 0.534)
數據結束符 的區間是 [0.534, 0.540).
我們的分數 .538 落在符號 END-OF-DATA 的區間;所以,這一定是下一個符號。由于它也是內部的結束符號,這也就意味著編碼已經結束。(如果數據流沒有內部結束,我們需要從其它的途徑知道數據流在何處結束——否則我們將永遠將解碼進行下去,錯誤地將不屬于實際編碼生成的數據讀進來。)
同樣的消息能夠使用同樣短的分數來編碼實現如 .534、.535、.536、.537或者是.539,這表明使用十進制而不是二進制會帶來效率的降低。這是正確的是因為三位十進制數據能夠表達的信息內容大約是9.966位;我們也能夠將同樣的信息使用二進制分數表示為.10001010(等同于0.5390625),它僅需8位。這稍稍大于信息內容本身或者消息的信息熵,大概是概率為0.6%的 7.361位信息熵。(注意最后一個0必須在二進制分數中表示,否則消息將會變得不確定起來。)
算法代碼(java):
????
import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.*;
/*** 算術編碼:無損數據壓縮方法,將整個輸入的消息編碼為一個數,* 一個滿足(0.0<=n<1.0)的小數n。* 編碼只有三塊數據需要考慮:下一個需要編碼的符號、當前時間間隔、各符號的概率* * 1.首先的準備工作——按照各信源信號好出現的頻率,將[0, 1)這個區間分成若干段,那么每個信號源就會有自己對應的區間了;* 2.將[0, 1)這個區間設置為初始間隔;* 3.然后編碼過程就是,按照待處理的信號,一個一個信號源讀入,每讀入一個信號,就將該信號源在[0, 1)上的范圍等比例的縮小到最新得到的間隔中。* 4.然后依次迭代,不斷重復進行步驟3,直到最后信號中的信源信號全部讀完為止;* @author librah**/
public class ArithmeticCoding extends JFrame implements ActionListener{private static String str=null;//每個字符出現的頻率private static TreeMap<Character, Integer> hm=null;//將每個字符固定頻率private static TreeMap<Character, Double> hm1=null;//計算每個字符區間的最小值private static TreeMap<Character, Double> hm_min=null;//計算每個字符區間的最大值private static TreeMap<Character, Double> hm_max=null;private static int allCounts=0;//算術編碼(小數形式)private static double code=0.0;//算術解碼(字符串)private static String decode="";private JTextField jtf=null;private JTextField jtf2=null;private JButton jb2=null;private JTextField jtf3=null;private JButton jb3=null;public static void main(String[] args) {// TODO 自動生成的方法存根new ArithmeticCoding();}public ArithmeticCoding(){hm=new TreeMap<Character,Integer>();hm1=new TreeMap<Character,Double>();hm_min=new TreeMap<Character,Double>();hm_max=new TreeMap<Character,Double>();JLabel jl=new JLabel("請輸入編碼字符串:");jtf=new JTextField(20);JPanel jp1=new JPanel();jp1.add(jl);jp1.add(jtf);JLabel jl2=new JLabel("編碼:");jtf2=new JTextField(20);jb2=new JButton("編碼");jb2.addActionListener(this);JPanel jp2=new JPanel();jp2.add(jl2);jp2.add(jtf2);jp2.add(jb2);JLabel jl3=new JLabel("解碼:");jtf3=new JTextField(20);jb3=new JButton("解碼");jb3.addActionListener(this);JPanel jp3=new JPanel();jp3.add(jl3);jp3.add(jtf3);jp3.add(jb3);JPanel jp=new JPanel();jp.add(jp1);jp.add(jp2);jp.add(jp3);this.add(jp);this.setSize(400,300);this.setLocation(500,200);this.setTitle("算術壓縮編碼");this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}//統計各字符出現的頻率public static void Count(){for(int i=0;i<str.length();i++){char c=str.charAt(i);if(!hm.containsKey(c))hm.put(c, 1);else{int count=hm.get(c);hm.replace(c, ++count);}}}//輸出字符的頻率public static void Print(){for(char c:hm.keySet()){int count=hm.get(c);System.out.println(c+":"+count);}}//統計各字符的概率區間public static void MinMax(double min,double max){//區間差double dec=max-min;int counts=0;for(char c:hm.keySet()){int count=hm.get(c);hm_min.put(c,min+((double)counts/allCounts)*dec);counts+=count;hm_max.put(c,min+((double)counts/allCounts)*dec);//System.out.println(c+":["+hm_min.get(c)+","+hm_max.get(c)+")");}}//算術編碼(縮小概率區間)public static void Encoding(){double min=0;double max=1.0;for(int i=0;i<allCounts;i++){char c=str.charAt(i);MinMax(min, max);code=hm_min.get(c);//不斷修改其概率區間min=hm_min.get(c);max=hm_max.get(c); }//生成指定范圍內的隨機數//code=Math.random()*(max-min)+min;System.out.println("Encode:"+code);}//算術解碼public static void Decoding(){double min=0;double max=1.0;for(int i=0;i<allCounts;i++){MinMax(min, max);for(char c:hm.keySet()){if(code>=hm_min.get(c)&&code<hm_max.get(c)){decode+=c;//不斷修改其概率區間min=hm_min.get(c);max=hm_max.get(c);}}MinMax(min, max);}System.out.println("Decode:"+decode);}@Overridepublic void actionPerformed(ActionEvent e) {// TODO 自動生成的方法存根str=jtf.getText().trim();hm=new TreeMap<Character,Integer>();hm1=new TreeMap<Character,Double>();hm_min=new TreeMap<Character,Double>();hm_max=new TreeMap<Character,Double>();allCounts=str.length();decode="";Count();if(e.getSource()==jb2){jtf2.setText("");Encoding();jtf2.setText(code+"");}else if(e.getSource()==jb3){Decoding();jtf3.setText(decode+"");}}
}
結果實現:
總結
以上是生活随笔為你收集整理的算术编码压缩算法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。