42 lines
34 KiB
HTML
42 lines
34 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN" data-theme="light">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<meta name="generator" content="VuePress 2.0.0-rc.24" />
|
||
<meta name="theme" content="VuePress Theme Hope 2.0.0-rc.94" />
|
||
<style>
|
||
:root {
|
||
--vp-c-bg: #fff;
|
||
}
|
||
|
||
[data-theme="dark"] {
|
||
--vp-c-bg: #1b1b1f;
|
||
}
|
||
|
||
html,
|
||
body {
|
||
background: var(--vp-c-bg);
|
||
}
|
||
</style>
|
||
<script>
|
||
const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
|
||
const systemDarkMode =
|
||
window.matchMedia &&
|
||
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||
|
||
if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
|
||
document.documentElement.setAttribute("data-theme", "dark");
|
||
}
|
||
</script>
|
||
<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","headline":"JPEG算法解密(四)","image":["https://thejinchao.github.io/images/2014/08/hufman7.gif"],"dateModified":"2025-08-31T08:33:30.000Z","author":[{"@type":"Person","name":"JinChao","url":"https://thecodeway.com"}]}</script><meta property="og:url" content="https://thejinchao.github.io/blog/2025/02/JPEG004.html"><meta property="og:site_name" content="我的博客和笔记"><meta property="og:title" content="JPEG算法解密(四)"><meta property="og:description" content="JPEG算法解密(四) 步骤五:哈弗曼编码 JPEG压缩的最后一步是对数据进行哈弗曼编码(Huffman coding),哈弗曼几乎是所有压缩算法的基础,它的基本原理是根据数据中元素的使用频率,调整元素的编码长度,以得到更高的压缩比。 举个例子,比如下面这段数据 这段数据里面包含了33个字符,每种字符出现的次数统计如下 如果我们用我们常见的定长编码,每..."><meta property="og:type" content="article"><meta property="og:image" content="https://thejinchao.github.io/images/2014/08/hufman7.gif"><meta property="og:locale" content="zh-CN"><meta property="og:updated_time" content="2025-08-31T08:33:30.000Z"><meta property="article:modified_time" content="2025-08-31T08:33:30.000Z"><title>JPEG算法解密(四) | 我的博客和笔记</title><meta name="description" content="JPEG算法解密(四) 步骤五:哈弗曼编码 JPEG压缩的最后一步是对数据进行哈弗曼编码(Huffman coding),哈弗曼几乎是所有压缩算法的基础,它的基本原理是根据数据中元素的使用频率,调整元素的编码长度,以得到更高的压缩比。 举个例子,比如下面这段数据 这段数据里面包含了33个字符,每种字符出现的次数统计如下 如果我们用我们常见的定长编码,每...">
|
||
<link rel="preload" href="/assets/style-DeGCDZm0.css" as="style"><link rel="stylesheet" href="/assets/style-DeGCDZm0.css">
|
||
<link rel="modulepreload" href="/assets/app-DkPs9iUo.js"><link rel="modulepreload" href="/assets/JPEG004.html-DW3enIUW.js"><link rel="modulepreload" href="/assets/plugin-vue_export-helper-DlAUqK2U.js">
|
||
<link rel="prefetch" href="/assets/about.html-D8ZEewqx.js" as="script"><link rel="prefetch" href="/assets/index.html-B3M5iyTc.js" as="script"><link rel="prefetch" href="/assets/index.html-Bea7Ljc1.js" as="script"><link rel="prefetch" href="/assets/index.html-7NgUIJQI.js" as="script"><link rel="prefetch" href="/assets/flip_coin.html-Dqb7yVXa.js" as="script"><link rel="prefetch" href="/assets/index.html-Bq5DBW64.js" as="script"><link rel="prefetch" href="/assets/mental_poker.html-upgIFQg-.js" as="script"><link rel="prefetch" href="/assets/rsa.html-C2MAz3q_.js" as="script"><link rel="prefetch" href="/assets/index.html-XSjkcFNM.js" as="script"><link rel="prefetch" href="/assets/index.html-BlLDa5wi.js" as="script"><link rel="prefetch" href="/assets/group.html-BlUxnrdB.js" as="script"><link rel="prefetch" href="/assets/index.html-Dwh4wjjI.js" as="script"><link rel="prefetch" href="/assets/number_theory_1.html-CCFBIDVc.js" as="script"><link rel="prefetch" href="/assets/number_theory_2.html-E_1t2N8D.js" as="script"><link rel="prefetch" href="/assets/number_theory_3.html-C7mKWaPe.js" as="script"><link rel="prefetch" href="/assets/probability.html-BMb2DoB6.js" as="script"><link rel="prefetch" href="/assets/symbol.html-D3F4QCvD.js" as="script"><link rel="prefetch" href="/assets/DH.html-DiwoCoT2.js" as="script"><link rel="prefetch" href="/assets/Ellipse.html-CICsz7N8.js" as="script"><link rel="prefetch" href="/assets/Fibonacci.html-DwNCRM9r.js" as="script"><link rel="prefetch" href="/assets/JPEG001.html-CYxZXTFd.js" as="script"><link rel="prefetch" href="/assets/JPEG002.html-vZLSAKIw.js" as="script"><link rel="prefetch" href="/assets/JPEG003.html-7k9cQbY0.js" as="script"><link rel="prefetch" href="/assets/JPEG005.html-X283q0WU.js" as="script"><link rel="prefetch" href="/assets/Martingle.html-DuzCTdr9.js" as="script"><link rel="prefetch" href="/assets/MentalPoker01.html-Bdim8zF8.js" as="script"><link rel="prefetch" href="/assets/MentalPoker02.html-BUMR90xg.js" as="script"><link rel="prefetch" href="/assets/RandRound.html-BUyJ1lJc.js" as="script"><link rel="prefetch" href="/assets/SPH001.html-bJMWk1Hs.js" as="script"><link rel="prefetch" href="/assets/SPH002.html-RDRFrjsP.js" as="script"><link rel="prefetch" href="/assets/SPH003.html-CXiZtz-k.js" as="script"><link rel="prefetch" href="/assets/SPH004.html-C9HwaUMK.js" as="script"><link rel="prefetch" href="/assets/SegmentCircle.html-DN0PC88v.js" as="script"><link rel="prefetch" href="/assets/BezierLine01.html-DSOurUee.js" as="script"><link rel="prefetch" href="/assets/BezierLine02.html-o6CA4gWl.js" as="script"><link rel="prefetch" href="/assets/index.html-vvgXTSoi.js" as="script"><link rel="prefetch" href="/assets/ibl_01.html-C0MMJ2Qi.js" as="script"><link rel="prefetch" href="/assets/ibl_02.html-CDJTuRsv.js" as="script"><link rel="prefetch" href="/assets/index.html-6kuE0ncx.js" as="script"><link rel="prefetch" href="/assets/index.html-C_mrng8z.js" as="script"><link rel="prefetch" href="/assets/math_01.html-zuecFNlw.js" as="script"><link rel="prefetch" href="/assets/math_02.html-iWb5l07C.js" as="script"><link rel="prefetch" href="/assets/math_03.html-xFITL_XP.js" as="script"><link rel="prefetch" href="/assets/transform_01.html-CksmrUHg.js" as="script"><link rel="prefetch" href="/assets/transform_02.html-1CVsioyX.js" as="script"><link rel="prefetch" href="/assets/transform_03.html-BAQ_NXXo.js" as="script"><link rel="prefetch" href="/assets/transform_04.html-ByAt1SaR.js" as="script"><link rel="prefetch" href="/assets/index.html-DuxyD8S5.js" as="script"><link rel="prefetch" href="/assets/raster01.html-BI35OP1h.js" as="script"><link rel="prefetch" href="/assets/raster02.html-Cbv1P85A.js" as="script"><link rel="prefetch" href="/assets/raster03.html-B5s3WYUL.js" as="script"><link rel="prefetch" href="/assets/function.html-Bm4MvBPx.js" as="script"><link rel="prefetch" href="/assets/grammar.html-CMgQ_iI7.js" as="script"><link rel="prefetch" href="/assets/helloworld.html-CgXNBpH7.js" as="script"><link rel="prefetch" href="/assets/index.html-Byg_N-Qq.js" as="script"><link rel="prefetch" href="/assets/object_and_class.html-DlE-spI2.js" as="script"><link rel="prefetch" href="/assets/BRDF.html-CtU43GcR.js" as="script"><link rel="prefetch" href="/assets/illumination_model_01.html-DI-AkNf4.js" as="script"><link rel="prefetch" href="/assets/luminosity.html-D22X8fHI.js" as="script"><link rel="prefetch" href="/assets/microfacets_1.html-NZvxM6qf.js" as="script"><link rel="prefetch" href="/assets/microfacets_2.html-xOlkk0AO.js" as="script"><link rel="prefetch" href="/assets/microfacets_3.html-Dc3XD-of.js" as="script"><link rel="prefetch" href="/assets/render_function_1.html-Mv-UHURo.js" as="script"><link rel="prefetch" href="/assets/404.html-CAmZAJ57.js" as="script"><link rel="prefetch" href="/assets/index.html-CODMUdzk.js" as="script"><link rel="prefetch" href="/assets/index.html-7RC8zQRd.js" as="script"><link rel="prefetch" href="/assets/index.html-Bg4xIDq3.js" as="script"><link rel="prefetch" href="/assets/index.html-Lt4dhVBQ.js" as="script"><link rel="prefetch" href="/assets/index.html-kNgjgymO.js" as="script"><link rel="prefetch" href="/assets/index.html-C5LW7NZm.js" as="script"><link rel="prefetch" href="/assets/index.html-DdK-CrSq.js" as="script"><link rel="prefetch" href="/assets/index.html-CadT90Og.js" as="script"><link rel="prefetch" href="/assets/photoswipe.esm-CKV1Bsxh.js" as="script">
|
||
</head>
|
||
<body>
|
||
<div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="vp-skip-link sr-only">跳至主要內容</a><!--]--><!--[--><div class="theme-container external-link-icon" vp-container><!--[--><header id="navbar" class="vp-navbar" vp-navbar><div class="vp-navbar-start"><button type="button" class="vp-toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><a class="route-link vp-brand" href="/" aria-label="带我回家"><img class="vp-nav-logo" src="/favicon.png" alt><!----><span class="vp-site-name hide-in-pad">我的博客和笔记</span></a><!--]--></div><div class="vp-navbar-center"><!--[--><nav class="vp-nav-links"><div class="vp-nav-item hide-in-mobile"><a class="route-link auto-link" href="/" aria-label="主页"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:house" sizing="height" height="1em"></iconify-icon><!--]-->主页<!----></a></div><div class="vp-nav-item hide-in-mobile"><a class="route-link route-link-active auto-link" href="/blog/" aria-label="文章"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:book" sizing="height" height="1em"></iconify-icon><!--]-->文章<!----></a></div><div class="vp-nav-item hide-in-mobile"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="开源项目"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:diagram-project" sizing="height" height="1em"></iconify-icon>开源项目<!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://github.com/thejinchao/turbolink" aria-label="TurboLink" rel="noopener noreferrer" target="_blank"><!---->TurboLink<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://github.com/thejinchao/TinyEncrypt" aria-label="TinyEncrypt" rel="noopener noreferrer" target="_blank"><!---->TinyEncrypt<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://github.com/thejinchao/ue5-style-guide" aria-label="UnrealStyleGuide" rel="noopener noreferrer" target="_blank"><!---->UnrealStyleGuide<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://github.com/thejinchao/AxTrace" aria-label="AxTrace" rel="noopener noreferrer" target="_blank"><!---->AxTrace<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://github.com/thejinchao/cyclone" aria-label="Cyclone" rel="noopener noreferrer" target="_blank"><!---->Cyclone<!----></a></li></ul></button></div></div><div class="vp-nav-item hide-in-mobile"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="个人笔记"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:lightbulb" sizing="height" height="1em"></iconify-icon>个人笔记<!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="route-link auto-link" href="/note/math/" aria-label="数学相关"><!---->数学相关<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/note/graphics/" aria-label="图形学"><!---->图形学<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/note/cryptography/" aria-label="密码学"><!---->密码学<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/note/language/" aria-label="编程语言"><!---->编程语言<!----></a></li></ul></button></div></div><div class="vp-nav-item hide-in-mobile"><a class="route-link auto-link" href="/about.html" aria-label="关于"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:user-astronaut" sizing="height" height="1em"></iconify-icon><!--]-->关于<!----></a></div></nav><!--]--></div><div class="vp-navbar-end"><!--[--><!----><!----><div class="vp-nav-item hide-in-mobile"><button type="button" class="vp-color-mode-switch" id="color-mode-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" name="auto" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" name="dark" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" name="light" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><!----><!--]--><button type="button" class="vp-toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span><span class="vp-top"></span><span class="vp-middle"></span><span class="vp-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow start"></span></div><aside id="sidebar" class="vp-sidebar" vp-sidebar><!----><ul class="vp-sidebar-links"><li><a class="route-link auto-link vp-sidebar-link" href="/" aria-label="主页"><!--[--><iconify-icon class="vp-icon" icon="fa6-solid:house" sizing="both" width="1em" height="1em"></iconify-icon><!--]-->主页<!----></a></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable active" type="button"><iconify-icon class="vp-icon" icon="fa6-solid:book" sizing="both" width="1em" height="1em"></iconify-icon><span class="vp-sidebar-title">我的文章</span><span class="vp-arrow down"></span></button><ul class="vp-sidebar-links"><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">从抛币协议到智能合约</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable active" type="button"><!----><span class="vp-sidebar-title">JPEG算法解密</span><span class="vp-arrow down"></span></button><ul class="vp-sidebar-links"><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/JPEG001.html" aria-label="Part1"><!---->Part1<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/JPEG002.html" aria-label="Part2"><!---->Part2<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/JPEG003.html" aria-label="Part3"><!---->Part3<!----></a></li><li><a class="route-link route-link-active auto-link vp-sidebar-link active" href="/blog/2025/02/JPEG004.html" aria-label="Part4"><!---->Part4<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/JPEG005.html" aria-label="Part5"><!---->Part5<!----></a></li><li><a class="auto-link external-link vp-sidebar-link" href="http://github.com/thejinchao/jpeg_encoder" aria-label="Github" rel="noopener noreferrer" target="_blank"><!---->Github<!----></a></li></ul></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">SPH算法简介</span><span class="vp-arrow end"></span></button><!----></section></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/Martingle.html" aria-label="赌博中的数学:Martingle策略"><!---->赌博中的数学:Martingle策略<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/RandRound.html" aria-label="如何生成一个随机的圆形"><!---->如何生成一个随机的圆形<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/DH.html" aria-label="一个简单的DH密钥协商算法的实现"><!---->一个简单的DH密钥协商算法的实现<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/SegmentCircle.html" aria-label="如何计算线段和圆的交点"><!---->如何计算线段和圆的交点<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/Ellipse.html" aria-label="一道数学趣题"><!---->一道数学趣题<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/blog/2025/02/Fibonacci.html" aria-label="斐波那契数列和1/89"><!---->斐波那契数列和1/89<!----></a></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">匀速贝塞尔曲线运动的实现</span><span class="vp-arrow end"></span></button><!----></section></li></ul></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><iconify-icon class="vp-icon" icon="fa6-solid:diagram-project" sizing="both" width="1em" height="1em"></iconify-icon><span class="vp-sidebar-title">开源项目</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><iconify-icon class="vp-icon" icon="fa6-solid:lightbulb" sizing="both" width="1em" height="1em"></iconify-icon><span class="vp-sidebar-title">学习笔记</span><span class="vp-arrow end"></span></button><!----></section></li></ul><!----></aside><!--[--><main id="main-content" class="vp-page"><!--[--><!----><!----><nav class="vp-breadcrumb disable"></nav><div class="vp-page-title"><h1><!---->JPEG算法解密(四)</h1><!----><hr></div><!----><div class="" vp-content><!----><div id="markdown-content"><h1 id="jpeg算法解密-四" tabindex="-1"><a class="header-anchor" href="#jpeg算法解密-四"><span>JPEG算法解密(四)</span></a></h1><h3 id="步骤五-哈弗曼编码" tabindex="-1"><a class="header-anchor" href="#步骤五-哈弗曼编码"><span>步骤五:哈弗曼编码</span></a></h3><hr><p>JPEG压缩的最后一步是对数据进行哈弗曼编码(Huffman coding),哈弗曼几乎是所有压缩算法的基础,它的基本原理是根据数据中元素的使用频率,调整元素的编码长度,以得到更高的压缩比。<br> 举个例子,比如下面这段数据</p><div class="language-" data-highlighter="shiki" data-ext="" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34;"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-"><span class="line"><span>AABCBABBCDBBDDBAABDBBDABBBBDDEDBD</span></span></code></pre></div><p>这段数据里面包含了33个字符,每种字符出现的次数统计如下</p><table class="gridtable" style="width:400px;text-align:center;border-collapse:collapse;"><tbody><tr><th style="width:100px;">字符</th><td style="width:60px;">A</td><td style="width:60px;">B</td><td style="width:60px;">C</td><td style="width:60px;">D</td><td style="width:60px;">E</td></tr><tr><th>次数</th><td>6</td><td>15</td><td>2</td><td>9</td><td>1</td></tr></tbody></table> 如果我们用我们常见的定长编码,每个字符都是3个bit。 <table class="gridtable" style="width:400px;text-align:center;border-collapse:collapse;"><tbody><tr><th style="width:100px;">字符</th><td style="width:60px;">A</td><td style="width:60px;">B</td><td style="width:60px;">C</td><td style="width:60px;">D</td><td style="width:60px;">E</td></tr><tr><th>编码</th><td>001</td><td>010</td><td>011</td><td>100</td><td>101</td></tr></tbody></table><p>那么这段文字共需要<code>3*33=99</code>个bit来保存,但如果我们根据字符出现的概率来编码,也就是出现频率较高的字符,使用较短的编码,如下:</p><table class="gridtable" style="width:400px;text-align:center;border-collapse:collapse;"><tbody><tr><th style="width:100px;">字符</th><td style="width:60px;">A</td><td style="width:60px;">B</td><td style="width:60px;">C</td><td style="width:60px;">D</td><td style="width:60px;">E</td></tr><tr><th>编码</th><td>110</td><td>0</td><td>1110</td><td>10</td><td>1111</td></tr></tbody></table><p>那么这段文字共需要<code>3*6+1*15+4*2+2*9+4*1=63</code>个bit来保存,压缩比为63%,哈弗曼编码一般都是使用二叉树来生成的,这样得到的编码符合前缀规则,也就是较短的编码不能够是较长编码的前缀,比如字符'B'使用的编码是'0',那么其他字符的编码的第一个字符都不能是‘0’。<br> 上面这个编码实例,就是由下面的这颗二叉树生成的。</p><figure><img src="/images/2014/08/hufman7.gif" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure><p>我们回到JPEG压缩上,回顾上一节的内容,经过数据量化,我们现在要处理的数据是一串一维数组,举例如下:</p><table class="gridtable" style="width:600px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:500px;"><!----></td></tr></tbody></table> 在实际的压缩过程中,数据中的0出现的概率非常高,所以首先要做的事情,是使用RLE编码对其中的0进行处理,把数据中的非零的数据,以及数据前面0的个数作为一个处理单元。 <table class="gridtable" align="center" style="width:600px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:500px;" colspan="8"><!----></td></tr><tr><th style="text-align:left;"><strong>②RLE编码</strong></th><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td>0,0,…,0,8</td><td>0,0,…,0</td></tr></tbody></table> 如果其中某个单元的0的个数超过16,则需要分成每16个一组,如果最后一个单元全都是0,则使用特殊字符“EOB”表示,EOB意思就是“后面的数据全都是0”, <table class="gridtable" align="center" style="width:600px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:500px;" colspan="8"><!----></td></tr><tr><th style="text-align:left;" rowspan="3"><strong>②RLE编码</strong></th><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td colspan="2">0,0,…,0,8</td><td>0,0,…,0</td></tr><tr><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td>0,0,…,0</td><td>0,0,8</td><td>0,0,…,0</td></tr><tr><td>(0,35)</td><td>(0,7)</td><td>(3,-6)</td><td>(0,-2)</td><td>(2,-9)</td><td>(15,0)</td><td>(2,8)</td><td>EOB</td></tr></tbody></table> 其中(15,0)表示15+1也就是16个0,接下来我们要处理的是括号里右面的数字,这个数字的取值范围在-2047~2047之间,JPEG提供了一张标准的码表用于对这些数字编码: <table class="gridtable" style="width:550px;text-align:center;"><tbody><tr><th style="width:250px;" colspan="2">Value</th><th style="width:50px;">Size</th><th style="width:250px;" colspan="2">Bits </th></tr><tr><td align="center" colspan="2">0</td><td>0</td><td align="center" colspan="2">–</td></tr><tr><td align="right">-1</td><td align="left">1</td><td>1</td><td align="right">0</td><td align="left">1</td></tr><tr><td align="right">-3,-2</td><td align="left">2,3</td><td>2</td><td align="right">00,01</td><td align="left">10,11</td></tr><tr><td align="right">-7,-6,-5,-4</td><td align="left">4,5,6,7</td><td>3</td><td align="right">000,001,010,011</td><td align="left">100,101,110,111</td></tr><tr><td align="right">-15,…,-8</td><td align="left">8,…,15</td><td>4</td><td align="right">0000,…,0111</td><td align="left">1000,…,1111</td></tr><tr><td align="right">-31,…,-16</td><td align="left">16,…,31</td><td>5</td><td align="right">0 0000,…,0 1111</td><td align="left">1 0000,…,1 1111 </td></tr><tr><td align="right">-63,…,-32</td><td align="left">32,…,63</td><td>6</td><td align="right">00 0000,…</td><td align="left">…,11 1111 </td></tr><tr><td align="right">-127,…,-64</td><td align="left">64,…,127</td><td>7</td><td align="right">000 0000,…</td><td align="left">…,111 1111 </td></tr><tr><td align="right">-255,…,-128</td><td align="left">128,…,255</td><td>8</td><td align="right">0000 0000,…</td><td align="left">…,1111 1111 </td></tr><tr><td align="right">-511,…,-256</td><td align="left">256,…,511</td><td>9</td><td align="right">0 0000 0000,…</td><td align="left">…,1 1111 1111 </td></tr><tr><td align="right">-1023,…,-512</td><td align="left">512,…,1023</td><td>10</td><td align="right">00 0000 0000,…</td><td align="left">…,11 1111 1111 </td></tr><tr><td align="right">-2047,…,-1024</td><td align="left">1024,…,2047</td><td>11</td><td align="right">000 0000 0000,…</td><td align="left">…,111 1111 1111</td></tr></tbody></table> 举例来说,第一个单元中的“35”这个数字,在表中的位置是长度为6的那组,所对应的bit码是“100011”,而“-6”的编码是”001″,由于这种编码附带长度信息,所以我们的数据变成了如下的格式。 <table class="gridtable" align="center" style="width:800px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:700px;" colspan="8"><!----></td></tr><tr><th style="text-align:left;" rowspan="3"><strong>②RLE编码</strong></th><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td colspan="2">0,0,…,0,8</td><td>0,0,…,0</td></tr><tr style="padding:3px 3px;"><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td>0,0,…,0</td><td>0,0,8</td><td>0,0,…,0</td></tr><tr><td>(0,35)</td><td>(0,7)</td><td>(3,-6)</td><td>(0,-2)</td><td>(2,-9)</td><td>(15,0)</td><td>(2,8)</td><td>EOB</td></tr><tr><th style="text-align:left;"><strong>③BIT编码</strong></th><td>(0,6, <em><b>100011</b></em>)</td><td>(0,3, <em><b>111</b></em>)</td><td>(3,3, <em><b>001</b></em>)</td><td>(0,2, <em><b>01</b></em>)</td><td>(2,4, <em><b>0110</b></em>)</td><td>(15,-)</td><td>(2,4, <em><b>1000</b></em>)</td><td>EOB</td></tr></tbody></table> 括号中前两个数字分都在0~15之间,所以这两个数可以合并成一个byte,高四位是前面0的个数,后四位是后面数字的位数。 <table class="gridtable" align="center" style="width:800px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:700px;" colspan="8"><!----></td></tr><tr><th style="text-align:left;" rowspan="3"><strong>②RLE编码</strong></th><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td colspan="2">0,0,…,0,8</td><td>0,0,…,0</td></tr><tr style="padding:3px 3px;"><td>35</td><td>7</td><td>0,0,0,-6</td><td>-2</td><td>0,0,-9</td><td>0,0,…,0</td><td>0,0,8</td><td>0,0,…,0</td></tr><tr><td>(0,35)</td><td>(0,7)</td><td>(3,-6)</td><td>(0,-2)</td><td>(2,-9)</td><td>(15,0)</td><td>(2,8)</td><td>EOB</td></tr><tr><th style="text-align:left;" rowspan="2"><strong>③BIT编码</strong></th><td>(0,6, <em><b>100011</b></em>)</td><td>(0,3, <em><b>111</b></em>)</td><td>(3,3, <em><b>001</b></em>)</td><td>(0,2, <em><b>01</b></em>)</td><td>(2,4, <em><b>0110</b></em>)</td><td>(15,-)</td><td>(2,4, <em><b>1000</b></em>)</td><td>EOB</td></tr><tr><td>(0x6,<em><b>100011</b></em>)</td><td>(0x3,<em><b>111</b></em>)</td><td>(0x33,<em><b>001</b></em>)</td><td>(0x2,<em><b>01</b></em>)</td><td>(0x24,<em><b>0110</b></em>)</td><td>(0xF0,-)</td><td>(0x24,<em><b>1000</b></em>)</td><td>EOB</td></tr></tbody></table> 对于括号前面的数字的编码,就要使用到我们提到的哈弗曼编码了,比如下面这张表,就是一张针对数据中的第一个单元,也就是直流(DC)部分的哈弗曼表,由于直流部分没有前置的0,所以取值范围在0~15之间。 <table class="gridtable" style="width:300px;text-align:left;"><tbody><tr><th style="width:50px;">Length</th><th style="width:80px;">Value</th><th style="width:170px;">Bits</th></tr><tr><td>3 bits </td><td>04<br>05<br>03<br>02<br>06<br>01<br>00 (EOB) </td><td>000<br>001<br>010<br>011<br>100<br>101<br>110</td></tr><tr><td>4 bits </td><td>07</td><td>1110</td></tr><tr><td>5 bits </td><td>08</td><td>1111 0</td></tr><tr><td>6 bits </td><td>09</td><td>1111 10</td></tr><tr><td>7 bits </td><td>0A</td><td>1111 110 </td></tr><tr><td>8 bits </td><td>0B</td><td>1111 1110 </td></tr></tbody></table> 举例来说,示例中的DC部分的数据是0x06,对应的二进制编码是“100”,而对于后面的交流部分,取值范围在0~255之间,所以对应的哈弗曼表会更大一些 <table class="gridtable" style="width:300px;text-align:left;"><tbody><tr><th style="width:50px;">Length</th><th style="width:80px;">Value</th><th style="width:170px;">Bits</th></tr><tr><td>2 bits </td><td>01<br>02</td><td>00<br>01</td></tr><tr><td>3 bits </td><td>03</td><td>100</td></tr><tr><td>4 bits </td><td>00 (EOB)<br>04<br>11</td><td>1010<br>1011<br>1100</td></tr><tr><td>5 bits </td><td>05<br>12<br>21</td><td>1101 0<br>1101 1<br>1110 0 </td></tr><tr><td>6 bits </td><td>31<br>41</td><td>1110 10<br>1110 11 </td></tr><tr><td>…</td><td>…</td><td>…</td></tr><tr><td>12 bits </td><td>24<br>33<br>62<br>72</td><td>1111 1111 0100<br>1111 1111 0101<br>1111 1111 0110<br>1111 1111 0111</td></tr><tr><td>15 bits</td><td>82</td><td>1111 1111 1000 000</td></tr><tr><td>16 bits </td><td>09<br>…<br>FA</td><td>1111 1111 1000 0010<br>…<br>1111 1111 1111 1110</td></tr></tbody></table> 这样经过哈弗曼编码,并且序列化后,最终数据成为如下形式 <table class="gridtable" style="width:1000px;text-align:center;"><tbody><tr><th style="width:100px;text-align:left;">①原始数据</th><td style="width:900px;" colspan="14"><!----></td></tr><tr><th rowspan="3" style="text-align:left;"><strong>②RLE编码</strong></th><td colspan="2">35</td><td colspan="2">7</td><td colspan="2">0,0,0,-6</td><td colspan="2">-2</td><td colspan="2">0,0,-9</td><td colspan="3">0,0,…,0,8</td><td>0,0,…,0</td></tr><tr style="padding:3px 3px;"><td colspan="2">35</td><td colspan="2">7</td><td colspan="2">0,0,0,-6</td><td colspan="2">-2</td><td colspan="2">0,0,-9</td><td>0,0,…,0</td><td colspan="2">0,0,8</td><td>0,0,…,0</td></tr><tr><td colspan="2">(0,35)</td><td colspan="2">(0,7)</td><td colspan="2">(3,-6)</td><td colspan="2">(0,-2)</td><td colspan="2">(2,-9)</td><td>(15,0)</td><td colspan="2">(2,8)</td><td>EOB</td></tr><tr><th style="text-align:left;" rowspan="2"><strong>③BIT编码</strong></th><td colspan="2">(0,6, <em><b>100011</b></em>)</td><td colspan="2">(0,3, <em><b>111</b></em>)</td><td colspan="2">(3,3, <em><b>001</b></em>)</td><td colspan="2">(0,2, <em><b>01</b></em>)</td><td colspan="2">(2,4, <em><b>0110</b></em>)</td><td>(15,-)</td><td colspan="2">(2,4, <em><b>1000</b></em>)</td><td>EOB</td></tr><tr><td colspan="2">(0x6,<em><b>100011</b></em>)</td><td colspan="2">(0x3,<em><b>111</b></em>)</td><td colspan="2">(0x33,<em><b>001</b></em>)</td><td colspan="2">(0x2,<em><b>01</b></em>)</td><td colspan="2">(0x24,<em><b>0110</b></em>)</td><td>0xF0</td><td colspan="2">(0x24,<em><b>1000</b></em>)</td><td>EOB</td></tr><tr><th style="text-align:left;"><strong>④哈弗曼编码</strong></th><td><em><b>100</b></em></td><td><em><b>100011</b></em></td><td><em><b>100</b></em></td><td><em><b>111</b></em></td><td><em><b>1111 1111 0101</b></em></td><td><em><b>001</b></em></td><td><em><b>01</b></em></td><td><em><b>01</b></em></td><td><em><b>1111 1111 0100</b></em></td><td><em><b>0110</b></em></td><td><em><b>1111 1111 001</b></em></td><td><em><b>1111 1111 0100</b></em></td><td><em><b>1000</b></em></td><td><em><b>1010</b></em></td></tr><tr><th style="text-align:left;" rowspan="2">⑤序列化</th><td colspan="14"><!----></td></tr><tr><td colspan="14"><!----></td></tr></tbody></table></div><!----><!----><!----></div><footer class="vp-page-meta"><!----><div class="vp-meta-item git-info"><!----><!----></div></footer><nav class="vp-page-nav"><a class="route-link auto-link prev" href="/blog/2025/02/JPEG003.html" aria-label="JPEG算法解密(三)"><div class="hint"><span class="arrow start"></span>上一页</div><div class="link"><!---->JPEG算法解密(三)</div></a><a class="route-link auto-link next" href="/blog/2025/02/JPEG005.html" aria-label="JPEG算法解密(五)"><div class="hint">下一页<span class="arrow end"></span></div><div class="link">JPEG算法解密(五)<!----></div></a></nav><!----><!----><!--]--></main><!--]--><footer class="vp-footer-wrapper" vp-footer><div class="vp-footer">使用 <a href="https://theme-hope.vuejs.press/zh/" target="_blank">VuePress Theme Hope</a> 主题 | <a href="https://beian.miit.gov.cn/" target="_blank">京ICP备16018986号-1</a></div><!----></footer></div><!--]--><!--]--><!--[--><!----><!--[--><!--]--><!--]--><!--]--></div>
|
||
<script type="module" src="/assets/app-DkPs9iUo.js" defer></script>
|
||
</body>
|
||
</html>
|