-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.html
330 lines (320 loc) · 47.2 KB
/
README.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
<!doctype html>
<html>
<head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
<title>README</title><link href='https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext' rel='stylesheet' type='text/css' /><style type='text/css'>html {overflow-x: initial !important;}:root { --bg-color:#ffffff; --text-color:#333333; --select-text-bg-color:#B5D6FC; --select-text-font-color:auto; --monospace:"Lucida Console",Consolas,"Courier",monospace; }
html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
body { margin: 0px; padding: 0px; height: auto; bottom: 0px; top: 0px; left: 0px; right: 0px; font-size: 1rem; line-height: 1.42857; overflow-x: hidden; background: inherit; }
iframe { margin: auto; }
a.url { word-break: break-all; }
a:active, a:hover { outline: 0px; }
.in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); }
#write { margin: 0px auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; white-space: normal; padding-bottom: 70px; overflow-x: visible; }
.first-line-indent #write div, .first-line-indent #write li, .first-line-indent #write p { text-indent: 2em; }
.first-line-indent #write div :not(p):not(div), .first-line-indent #write div.md-htmlblock-container, .first-line-indent #write p *, .first-line-indent pre { text-indent: 0px; }
.for-image #write { padding-left: 8px; padding-right: 8px; }
body.typora-export { padding-left: 30px; padding-right: 30px; }
@media screen and (max-width: 500px) {
body.typora-export { padding-left: 0px; padding-right: 0px; }
.CodeMirror-sizer { margin-left: 0px !important; }
.CodeMirror-gutters { display: none !important; }
}
#write > blockquote:first-child, #write > div:first-child, #write > figure:first-child, #write > ol:first-child, #write > p:first-child, #write > pre:first-child, #write > ul:first-child { margin-top: 30px; }
#write li > figure:first-child { margin-top: -20px; }
#write ol, #write ul { position: relative; }
img { max-width: 100%; vertical-align: middle; }
button, input, select, textarea { color: inherit; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: inherit; line-height: inherit; font-family: inherit; }
input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; }
*, ::after, ::before { box-sizing: border-box; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; }
#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; }
h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 2; }
p { orphans: 4; }
h1 { font-size: 2rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.6rem; }
h4 { font-size: 1.4rem; }
h5 { font-size: 1.2rem; }
h6 { font-size: 1rem; }
.md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; }
.hidden { display: none; }
.md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; }
a { cursor: pointer; }
sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.7); color: rgb(85, 85, 85); border-radius: 4px; cursor: pointer; }
sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; }
figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; }
figure > table { margin: 0px !important; }
tr { break-inside: avoid; break-after: auto; }
thead { display: table-header-group; }
table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; }
table.md-table td { min-width: 80px; }
.CodeMirror-gutters { border-right: 0px; background-color: inherit; }
.CodeMirror { text-align: left; }
.CodeMirror-placeholder { opacity: 0.3; }
.CodeMirror pre { padding: 0px 4px; }
.CodeMirror-lines { padding: 0px; }
div.hr:focus { cursor: none; }
#write pre { white-space: pre-wrap; }
#write.fences-no-line-wrapping pre { white-space: pre; }
#write pre.ty-contain-cm { white-space: normal; }
.CodeMirror-gutters { margin-right: 4px; }
.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background: inherit; position: relative !important; }
.md-diagram-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; }
#write .md-fences.mock-cm { white-space: pre-wrap; }
.md-fences.md-fences-with-lineno { padding-left: 0px; }
#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; }
.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; }
.CodeMirror-line, twitterwidget { break-inside: avoid; }
.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; }
.footnotes + .footnotes { margin-top: 0px; }
.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; background: 0px 0px; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; -webkit-tap-highlight-color: transparent; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; }
li div { padding-top: 0px; }
blockquote { margin: 1rem 0px; }
li .mathjax-block, li p { margin: 0.5rem 0px; }
li { margin: 0px; position: relative; }
blockquote > :last-child { margin-bottom: 0px; }
blockquote > :first-child, li > :first-child { margin-top: 0px; }
.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; }
#write .footnote-line { white-space: pre-wrap; }
@media print {
body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; }
#write { margin-top: 0px; border-color: transparent !important; }
.typora-export * { -webkit-print-color-adjust: exact; }
html.blink-to-pdf { font-size: 13px; }
.typora-export #write { padding-left: 1cm; padding-right: 1cm; padding-bottom: 0px; break-after: avoid; }
.typora-export #write::after { height: 0px; }
@page { margin: 20mm 0px; }
}
.footnote-line { margin-top: 0.714em; font-size: 0.7em; }
a img, img a { cursor: pointer; }
pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background: rgb(204, 204, 204); display: block; overflow-x: hidden; }
p > img:only-child { display: block; margin: auto; }
p > .md-image:only-child { display: inline-block; width: 100%; text-align: center; }
#write .MathJax_Display { margin: 0.8em 0px 0px; }
.md-math-block { width: 100%; }
.md-math-block:not(:empty)::after { display: none; }
[contenteditable="true"]:active, [contenteditable="true"]:focus { outline: 0px; box-shadow: none; }
.md-task-list-item { position: relative; list-style-type: none; }
.task-list-item.md-task-list-item { padding-left: 0px; }
.md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); }
.math { font-size: 1rem; }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-radius: 10px; }
.md-toc-content { position: relative; margin-left: 0px; }
.md-toc-content::after, .md-toc::after { display: none; }
.md-toc-item { display: block; color: rgb(65, 131, 196); }
.md-toc-item a { text-decoration: none; }
.md-toc-inner:hover { }
.md-toc-inner { display: inline-block; cursor: pointer; }
.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; }
.md-toc-h2 .md-toc-inner { margin-left: 2em; }
.md-toc-h3 .md-toc-inner { margin-left: 4em; }
.md-toc-h4 .md-toc-inner { margin-left: 6em; }
.md-toc-h5 .md-toc-inner { margin-left: 8em; }
.md-toc-h6 .md-toc-inner { margin-left: 10em; }
@media screen and (max-width: 48em) {
.md-toc-h3 .md-toc-inner { margin-left: 3.5em; }
.md-toc-h4 .md-toc-inner { margin-left: 5em; }
.md-toc-h5 .md-toc-inner { margin-left: 6.5em; }
.md-toc-h6 .md-toc-inner { margin-left: 8em; }
}
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; }
.footnote-line a:not(.reversefootnote) { color: inherit; }
.md-attr { display: none; }
.md-fn-count::after { content: "."; }
code, pre, samp, tt { font-family: var(--monospace); }
kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; }
.md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: var(--monospace); }
code { text-align: left; vertical-align: initial; }
a.md-print-anchor { white-space: pre !important; border-width: initial !important; border-style: none !important; border-color: initial !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; background: 0px 0px !important; text-decoration: initial !important; text-shadow: initial !important; }
.md-inline-math .MathJax_SVG .noError { display: none !important; }
.md-math-block .MathJax_SVG_Display { text-align: center; margin: 0px; position: relative; text-indent: 0px; max-width: none; max-height: none; min-height: 0px; min-width: 100%; width: auto; overflow-y: hidden; display: block !important; }
.MathJax_SVG_Display, .md-inline-math .MathJax_SVG_Display { width: auto; margin: inherit; display: inline-block !important; }
.MathJax_SVG .MJX-monospace { font-family: var(--monospace); }
.MathJax_SVG .MJX-sans-serif { font-family: sans-serif; }
.MathJax_SVG { display: inline; font-style: normal; font-weight: 400; line-height: normal; zoom: 90%; text-indent: 0px; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; padding: 0px; margin: 0px; }
.MathJax_SVG * { transition: none; }
.MathJax_SVG_Display svg { vertical-align: middle !important; margin-bottom: 0px !important; }
.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; }
.md-diagram-panel > svg { max-width: 100%; }
[lang="mermaid"] svg, [lang="flow"] svg { max-width: 100%; }
[lang="mermaid"] .node text { font-size: 1rem; }
table tr th { border-bottom: 0px; }
video { max-width: 100%; display: block; margin: 0px auto; }
iframe { max-width: 100%; width: 100%; border: none; }
.highlight td, .highlight tr { border: 0px; }
.CodeMirror { height: auto; }
.CodeMirror.cm-s-inner { background: inherit; }
.CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; z-index: 3; }
.CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255); }
.CodeMirror-gutters { border-right: 1px solid rgb(221, 221, 221); background: inherit; white-space: nowrap; }
.CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153); }
.cm-s-inner .cm-keyword { color: rgb(119, 0, 136); }
.cm-s-inner .cm-atom, .cm-s-inner.cm-atom { color: rgb(34, 17, 153); }
.cm-s-inner .cm-number { color: rgb(17, 102, 68); }
.cm-s-inner .cm-def { color: rgb(0, 0, 255); }
.cm-s-inner .cm-variable { color: rgb(0, 0, 0); }
.cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170); }
.cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85); }
.cm-s-inner .cm-string { color: rgb(170, 17, 17); }
.cm-s-inner .cm-property { color: rgb(0, 0, 0); }
.cm-s-inner .cm-operator { color: rgb(152, 26, 26); }
.cm-s-inner .cm-comment, .cm-s-inner.cm-comment { color: rgb(170, 85, 0); }
.cm-s-inner .cm-string-2 { color: rgb(255, 85, 0); }
.cm-s-inner .cm-meta { color: rgb(85, 85, 85); }
.cm-s-inner .cm-qualifier { color: rgb(85, 85, 85); }
.cm-s-inner .cm-builtin { color: rgb(51, 0, 170); }
.cm-s-inner .cm-bracket { color: rgb(153, 153, 119); }
.cm-s-inner .cm-tag { color: rgb(17, 119, 0); }
.cm-s-inner .cm-attribute { color: rgb(0, 0, 204); }
.cm-s-inner .cm-header, .cm-s-inner.cm-header { color: rgb(0, 0, 255); }
.cm-s-inner .cm-quote, .cm-s-inner.cm-quote { color: rgb(0, 153, 0); }
.cm-s-inner .cm-hr, .cm-s-inner.cm-hr { color: rgb(153, 153, 153); }
.cm-s-inner .cm-link, .cm-s-inner.cm-link { color: rgb(0, 0, 204); }
.cm-negative { color: rgb(221, 68, 68); }
.cm-positive { color: rgb(34, 153, 34); }
.cm-header, .cm-strong { font-weight: 700; }
.cm-del { text-decoration: line-through; }
.cm-em { font-style: italic; }
.cm-link { text-decoration: underline; }
.cm-error { color: red; }
.cm-invalidchar { color: red; }
.cm-constant { color: rgb(38, 139, 210); }
.cm-defined { color: rgb(181, 137, 0); }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0); }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34); }
.cm-s-inner .CodeMirror-activeline-background { background: inherit; }
.CodeMirror { position: relative; overflow: hidden; }
.CodeMirror-scroll { height: 100%; outline: 0px; position: relative; box-sizing: content-box; background: inherit; }
.CodeMirror-sizer { position: relative; }
.CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none; }
.CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden; }
.CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: hidden; }
.CodeMirror-scrollbar-filler { right: 0px; bottom: 0px; }
.CodeMirror-gutter-filler { left: 0px; bottom: 0px; }
.CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 30px; z-index: 3; }
.CodeMirror-gutter { white-space: normal; height: 100%; box-sizing: content-box; padding-bottom: 30px; margin-bottom: -32px; display: inline-block; }
.CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: 0px 0px !important; border: none !important; }
.CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4; }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; }
.CodeMirror-lines { cursor: text; }
.CodeMirror pre { border-radius: 0px; border-width: 0px; background: 0px 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; word-wrap: normal; color: inherit; z-index: 2; position: relative; overflow: visible; }
.CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; }
.CodeMirror-code pre { border-right: 30px solid transparent; width: fit-content; }
.CodeMirror-wrap .CodeMirror-code pre { border-right: none; width: auto; }
.CodeMirror-linebackground { position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 0; }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; }
.CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; }
.CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden; }
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right: none; width: 0px; }
.CodeMirror div.CodeMirror-cursor { visibility: hidden; }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit; }
.cm-searching { background: rgba(255, 255, 0, 0.4); }
@media print {
.CodeMirror div.CodeMirror-cursor { visibility: hidden; }
}
:root { --side-bar-bg-color: #fafafa; --control-text-color: #777; }
@font-face { font-family: "Open Sans"; font-style: normal; font-weight: normal; src: local("Open Sans Regular"), url("./github/400.woff") format("woff"); }
@font-face { font-family: "Open Sans"; font-style: italic; font-weight: normal; src: local("Open Sans Italic"), url("./github/400i.woff") format("woff"); }
@font-face { font-family: "Open Sans"; font-style: normal; font-weight: bold; src: local("Open Sans Bold"), url("./github/700.woff") format("woff"); }
@font-face { font-family: "Open Sans"; font-style: italic; font-weight: bold; src: local("Open Sans Bold Italic"), url("./github/700i.woff") format("woff"); }
html { font-size: 16px; }
body { font-family: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; color: rgb(51, 51, 51); line-height: 1.6; }
#write { max-width: 860px; margin: 0px auto; padding: 20px 30px 100px; }
#write > ul:first-child, #write > ol:first-child { margin-top: 30px; }
body > :first-child { margin-top: 0px !important; }
body > :last-child { margin-bottom: 0px !important; }
a { color: rgb(65, 131, 196); }
h1, h2, h3, h4, h5, h6 { position: relative; margin-top: 1rem; margin-bottom: 1rem; font-weight: bold; line-height: 1.4; cursor: text; }
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { text-decoration: none; }
h1 tt, h1 code { font-size: inherit; }
h2 tt, h2 code { font-size: inherit; }
h3 tt, h3 code { font-size: inherit; }
h4 tt, h4 code { font-size: inherit; }
h5 tt, h5 code { font-size: inherit; }
h6 tt, h6 code { font-size: inherit; }
h1 { padding-bottom: 0.3em; font-size: 2.25em; line-height: 1.2; border-bottom: 1px solid rgb(238, 238, 238); }
h2 { padding-bottom: 0.3em; font-size: 1.75em; line-height: 1.225; border-bottom: 1px solid rgb(238, 238, 238); }
h3 { font-size: 1.5em; line-height: 1.43; }
h4 { font-size: 1.25em; }
h5 { font-size: 1em; }
h6 { font-size: 1em; color: rgb(119, 119, 119); }
p, blockquote, ul, ol, dl, table { margin: 0.8em 0px; }
li > ol, li > ul { margin: 0px; }
hr { height: 2px; padding: 0px; margin: 16px 0px; background-color: rgb(231, 231, 231); border: 0px none; overflow: hidden; box-sizing: content-box; }
body > h2:first-child { margin-top: 0px; padding-top: 0px; }
body > h1:first-child { margin-top: 0px; padding-top: 0px; }
body > h1:first-child + h2 { margin-top: 0px; padding-top: 0px; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { margin-top: 0px; padding-top: 0px; }
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { margin-top: 0px; padding-top: 0px; }
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { margin-top: 0px; }
li p.first { display: inline-block; }
ul, ol { padding-left: 30px; }
ul:first-child, ol:first-child { margin-top: 0px; }
ul:last-child, ol:last-child { margin-bottom: 0px; }
blockquote { border-left: 4px solid rgb(223, 226, 229); padding: 0px 15px; color: rgb(119, 119, 119); }
blockquote blockquote { padding-right: 0px; }
table { padding: 0px; word-break: initial; }
table tr { border-top: 1px solid rgb(223, 226, 229); margin: 0px; padding: 0px; }
table tr:nth-child(2n), thead { background-color: rgb(248, 248, 248); }
table tr th { font-weight: bold; border-width: 1px 1px 0px; border-top-style: solid; border-right-style: solid; border-left-style: solid; border-top-color: rgb(223, 226, 229); border-right-color: rgb(223, 226, 229); border-left-color: rgb(223, 226, 229); border-image: initial; border-bottom-style: initial; border-bottom-color: initial; text-align: left; margin: 0px; padding: 6px 13px; }
table tr td { border: 1px solid rgb(223, 226, 229); text-align: left; margin: 0px; padding: 6px 13px; }
table tr th:first-child, table tr td:first-child { margin-top: 0px; }
table tr th:last-child, table tr td:last-child { margin-bottom: 0px; }
.CodeMirror-lines { padding-left: 4px; }
.code-tooltip { box-shadow: rgba(0, 28, 36, 0.3) 0px 1px 1px 0px; border-top: 1px solid rgb(238, 242, 242); }
.md-fences, code, tt { border: 1px solid rgb(231, 234, 237); background-color: rgb(248, 248, 248); border-radius: 3px; padding: 2px 4px 0px; font-size: 0.9em; }
code { background-color: rgb(243, 244, 244); padding: 0px 4px 2px; }
.md-fences { margin-bottom: 15px; margin-top: 15px; padding: 8px 1em 6px; }
.md-task-list-item > input { margin-left: -1.3em; }
@media screen and (min-width: 914px) {
}
@media print {
html { font-size: 13px; }
table, pre { break-inside: avoid; }
pre { word-wrap: break-word; }
}
.md-fences { background-color: rgb(248, 248, 248); }
#write pre.md-meta-block { padding: 1rem; font-size: 85%; line-height: 1.45; background-color: rgb(247, 247, 247); border: 0px; border-radius: 3px; color: rgb(119, 119, 119); margin-top: 0px !important; }
.mathjax-block > .code-tooltip { bottom: 0.375rem; }
.md-mathjax-midline { background: rgb(250, 250, 250); }
#write > h3.md-focus::before { left: -1.5625rem; top: 0.375rem; }
#write > h4.md-focus::before { left: -1.5625rem; top: 0.285714rem; }
#write > h5.md-focus::before { left: -1.5625rem; top: 0.285714rem; }
#write > h6.md-focus::before { left: -1.5625rem; top: 0.285714rem; }
.md-image > .md-meta { border-radius: 3px; padding: 2px 0px 0px 4px; font-size: 0.9em; color: inherit; }
.md-tag { color: rgb(167, 167, 167); opacity: 1; }
.md-toc { margin-top: 20px; padding-bottom: 20px; }
.sidebar-tabs { border-bottom: none; }
#typora-quick-open { border: 1px solid rgb(221, 221, 221); background-color: rgb(248, 248, 248); }
#typora-quick-open-item { background-color: rgb(250, 250, 250); border-color: rgb(254, 254, 254) rgb(229, 229, 229) rgb(229, 229, 229) rgb(238, 238, 238); border-style: solid; border-width: 1px; }
.on-focus-mode blockquote { border-left-color: rgba(85, 85, 85, 0.12); }
header, .context-menu, .megamenu-content, footer { font-family: "Segoe UI", Arial, sans-serif; }
.file-node-content:hover .file-node-icon, .file-node-content:hover .file-node-open-state { visibility: visible; }
.mac-seamless-mode #typora-sidebar { background-color: var(--side-bar-bg-color); }
.md-lang { color: rgb(180, 101, 77); }
.html-for-mac .context-menu { --item-hover-bg-color: #E6F0FE; }
</style>
</head>
<body class='typora-export os-windows' >
<div id='write' class = 'is-node'><h1><a name='header-n10437' class='md-header-anchor '></a><a href='https://github.com/Wosser1sProductions/ImageEncoder'>ImageEncoder</a> <img src='https://img.shields.io/badge/release-v0.0.1.5-brightgreen.svg' alt='version' referrerPolicy='no-referrer' /></h1><div class='md-toc' mdtype='toc'><p class="md-toc-content"><span class="md-toc-item md-toc-h1" data-ref="n10437"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10437">ImageEncoder <img src="https://img.shields.io/badge/release-v0.0.1.5-brightgreen.svg" alt="version" referrerpolicy="no-referrer"></a></span><span class="md-toc-item md-toc-h2" data-ref="n10440"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10440">Build</a></span><span class="md-toc-item md-toc-h2" data-ref="n10450"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10450">Info Image Encoder</a></span><span class="md-toc-item md-toc-h3" data-ref="n10497"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10497">Extra</a></span><span class="md-toc-item md-toc-h2" data-ref="n10542"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10542">Info Video Encoder</a></span><span class="md-toc-item md-toc-h2" data-ref="n10562"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10562">Examples</a></span><span class="md-toc-item md-toc-h3" data-ref="n10563"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10563">Images</a></span><span class="md-toc-item md-toc-h3" data-ref="n10575"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10575">Video</a></span><span class="md-toc-item md-toc-h2" data-ref="n10608"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10608">Test results</a></span><span class="md-toc-item md-toc-h3" data-ref="n10609"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10609">Size statistics</a></span><span class="md-toc-item md-toc-h3" data-ref="n10675"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10675">Timing statistics</a></span><span class="md-toc-item md-toc-h2" data-ref="n10769"><a class="md-toc-inner" style="cursor: pointer;" href="#header-n10769">Class diagram </a></span></p></div><h2><a name='header-n10440' class='md-header-anchor '></a>Build</h2><ol start='' ><li><p>Install g++-7 with the commands in install_g++.sh</p></li><li><p>Build the encoder with:
<code>make encoder</code></p><p>Build the decoder with:
<code>make decoder</code></p><p>Or build both with:
<code>make</code> or <code>make all</code></p></li><li><p>Got to the ./bin folder and run the encoder/decoder
with a file containing the settings.</p></li></ol><h2><a name='header-n10450' class='md-header-anchor '></a>Info Image Encoder</h2><ul><li><p>Linux builds (through Win10 bash) and Windows builds are provided in <code>./bin</code></p></li><li><p>A QtCreator .qbs project file is included for debugging, the makefile will always build for release by default.</p></li><li><p>Everything was implemented according to the assignment, i.e. nothing is excluded.</p></li><li><p>One extra thing is an offset for each pixel before the DCT step (and after the iDCT), here the value of 128 is subtracted from the pixels (and added during decoding) to make the DCT components smaller and easier to fit in less space.</p></li><li><p>The encoded image has the following structure:</p><figure><table><thead><tr><th>Property</th><th style='text-align:center;' >Amount of bits</th></tr></thead><tbody><tr><td>Bit length for quant matrix coeff</td><td style='text-align:center;' ><code>5</code></td></tr><tr><td>Quant matrix coeffs</td><td style='text-align:center;' ><code>16 * bit_len</code></td></tr><tr><td>Whether to use RLE</td><td style='text-align:center;' ><code>1</code></td></tr><tr><td>Image width</td><td style='text-align:center;' ><code>15</code></td></tr><tr><td>Image height</td><td style='text-align:center;' ><code>15</code></td></tr><tr><td>Block data</td><td style='text-align:center;' >different for every block</td></tr><tr><td>Bit length for data in block</td><td style='text-align:center;' ><code>5</code></td></tr><tr><td>Data length (if using RLE)</td><td style='text-align:center;' ><code>block bit_len</code></td></tr></tbody></table></figure><p>For the example quant matrix in the assignment, the header is 20.5 bytes of data.</p></li><li><p>The en/decoder will give a compression percentage after writing the resulting file. (<code>< 100.0</code>: result is smaller, <code>> 100.0</code>: result is bigger )</p></li><li><p>The Block size is provided as a Template argument and can be changed in Block.hpp.
Everything should work as expected, only an 8x8 quant matrix is needed to continue.
However, resulting images do not seem as good in comparison with a 4x4 block size.</p></li><li><p>A single example is provided (the image from the iPyhton notebooks) for convenience.
Other testing images used during development can be added on request.</p></li></ul><h3><a name='header-n10497' class='md-header-anchor '></a>Extra</h3><ul><li><p>The encoder now has an additional step to apply Huffman encoding on the final bitstream. This will also be decompressed by the decoder, if set in the encoded image. The inclusion of Huffman encoding can be toggled through the <code>ENABLE_HUFFMAN</code> macro in <code>main.hpp</code> or through the makefile.</p><p>When enabled, the first bit of the resulting stream will be set to <code>1</code>. The following structure will appear instead of the encoded image:</p><figure><table><thead><tr><th>Property</th><th style='text-align:center;' >Amount of bits</th></tr></thead><tbody><tr><td>Huffman encoding used</td><td style='text-align:center;' ><code>1</code></td></tr><tr><td>Huffman table header group</td><td style='text-align:center;' ><code>12</code></td></tr><tr><td><strong>[HDR]</strong> Has sequence</td><td style='text-align:center;' ><code>1 of 12</code></td></tr><tr><td><strong>[HDR]</strong> Sequence length</td><td style='text-align:center;' ><code>7 of 12</code></td></tr><tr><td><strong>[HDR]</strong> Amount of bits for vals</td><td style='text-align:center;' ><code>4 of 12</code></td></tr><tr><td>Sequence entry {key:val}</td><td style='text-align:center;' ><code>(8 + val_bit_len) * seq_len</code></td></tr><tr><td>Huffman encoded data</td><td style='text-align:center;' >rest</td></tr></tbody></table></figure><p>The occurrences of every byte in the stream are counted, and a heap is constructed to build a tree from. This tree is then converted to a dictionary containing keys and the paths to follow in the tree.
After creating the Huffman dictionary, the frequencies of each path length are determined.
<code>{key: value}</code>-pairs are grouped by their path length. </p><p>A header contains <code>1</code> bit to indicate whether there is a sequence, 7 bits for the length of the sequence (= amount of <code>{key: value}</code>-pairs until the next header) and 4 bits for the amount of bits for each value in this group.
Afterwards, every byte (size can be changed with template arguments, but using 8 bits limits the
Huffman dictionary to a manageable 256 entries) is encoded according to the Huffman dictionary.</p><p>If the addition of Huffman encoding results in a bigger image than the already encoded image, the Huffman dictionary will not be included and the original encoded stream will be restored.</p></li><li><p>An elapsed time in milliseconds is now provided after en/decoding.</p></li><li><p>A progress bar indicates how many blocks are already done.</p></li><li><p>By including the <code>-DENABLE_OPENMP</code> compiler flag, the code will build with OpenMP enabled (set in <code>main.hpp</code> or in the makefile). This causes Block functions to run in parallel (where possible) and improves the en/decoding speed. This is however not supported with MSVC, since the loop uses iterators and support for iterators came in OpenMP 3.0 while MSVC only implements features up to version 2.0.</p></li><li><p>Timings and <a href='#header-n10608'>test results</a> were added below.</p></li><li><p>A <a href='#header-n10769'>class diagram</a> giving an overview of the code was generated through Visual Studio.</p></li><li><p>The git repo (title link) will be made public after presenting the video en/decoder for evaluation.</p></li></ul><h2><a name='header-n10542' class='md-header-anchor '></a>Info Video Encoder</h2><ul><li><p>The <code>Block<></code> class was expanded with a second template specification:</p><pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="C++"><div class="CodeMirror cm-s-inner CodeMirror-wrap" lang="c++"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 0px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div><div class="CodeMirror-gutter-filler" cm-not-content="true"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"></div><div style="position: relative; z-index: 1;"></div><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"></div><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">using</span> <span class="cm-variable">MicroBlock</span> <span class="cm-operator">=</span> <span class="cm-variable">dc::Block</span><span class="cm-operator"><</span><span class="cm-variable">dc::BlockSize</span><span class="cm-operator">></span>;<span class="cm-tab" role="presentation" cm-text=" "> </span><span class="cm-tab" role="presentation" cm-text=" "> </span><span class="cm-comment">// dc::BlockSize = 4</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">using</span> <span class="cm-variable">MacroBlock</span> <span class="cm-operator">=</span> <span class="cm-variable">dc::Block</span><span class="cm-operator"><</span><span class="cm-variable">dc::MacroBlockSize</span><span class="cm-operator">></span>;<span class="cm-tab" role="presentation" cm-text=" "> </span><span class="cm-comment">// dc::MacroBlockSize = 16</span></span></pre></div></div></div></div></div><div style="position: absolute; height: 0px; width: 1px; border-bottom: 0px solid transparent; top: 46px;"></div><div class="CodeMirror-gutters" style="display: none; height: 46px;"></div></div></div></pre><p>Methods were added to process data at Macroblock level.</p></li><li><p>The new <code>VideoEn-/Decoder</code> classes inherit from <code>VideoProcessor</code> (instead of ImageProcessor) that holds a list of Frame instances for the video.</p></li><li><p>A frame is used to process Micro- or MacroBlocks according to the GOP setting. MacroBlock's get a reference to their previous frame and can request a new MacroBlock at an arbitrary pixel coordinate.
For simplicity, these blocks cannot be chosen outside of a frame, their coordinates will be restricted to be within the frame boundaries.</p></li><li><p>Motion vectors are estimated according to a fixed recursive pattern, as given by a pre-cached list of pixel offsets. These follow the same pattern as the 2D log search approach. The picture below indicates the considered points at each level: first the 9 points at level 0 are evaluated, then the best offset is chosen and the search window decreases by a factor 2. The pixels at level 1 around the chosen level 0 pixel will be compared, ... up to level 3. At this depth, no other pixels can be chosen and the level 3 pixel becomes the best value for the motion vector estimation.
With a merange of 16 as shown below, the border pixels will not be evaluated. The amount of motion vectors considered for each MacroBlock has an upper limit of 36. If no better match was found than the previous best, the search will have an early exit. This is obviously much better than the standard of 1089 considered points when just iteration over the entire block.</p><p> <img src='doc/MER_pattern.png' alt='MER pattern' referrerPolicy='no-referrer' /></p><p>The current scoring function uses Sum of Absolute Difference (SAD). After finding the best match, the actual difference will be expanded to the corresponding Microblocks that lay within the considered Macroblock.</p></li><li><p>After all MacroBlocks have their motion vectors assigned, these vectors will be written to the outputstream, but only the relative offsets with the block coordinate, not the full (x, y) pixel coordinates. The already expanded Microblocks (that now contain the difference between the current frame and the corresponding blocks in the previous one) will be encoded as a normal image (or IFrame). The strength of the motion estimation algorithm lays in finding the motion estimate vectors and only encoding the relative difference with the match instead of only info in the current frame.</p></li><li><p>No MacroBlocks are encoded as is (following the assignment). A motion vector is always found, because (0, 0) is included as start vector (same location, but in reference frame).</p></li><li><p>Extra Huffman should work when compiled with the extra flag, I ran into some decoding problems on a test frame and have yet to find out what went wrong. OpenMP is not fully included, since it ran into problems as well (mostly due to time constraints).</p></li></ul><h2><a name='header-n10562' class='md-header-anchor '></a>Examples</h2><h3><a name='header-n10563' class='md-header-anchor '></a>Images</h3><ul><li><p>Original image:</p><p><img src='doc/ex6.png' alt='Original image' referrerPolicy='no-referrer' /></p></li><li><p>The decoder had an "error" where signed encoded data was interpreted as unsigned, and
consequently every pixel that would contain signed data was wrongly transformed during iDCT, giving these weird uniform areas:</p><p> <img src='doc/ex6_dec2.png' alt='Signed errors' referrerPolicy='no-referrer' /></p></li><li><p>This was solved by providing a more specific function to check the minimal required bit length to represent a data value.</p><p>e.g. The new function would check if a value could be encoded in x bits, by shifting the value to the left of an int16, and re-shifting it back (to create a signed value), if the result is the same as the original, the value can be encoded using x bits. If not, increase x until it does.
This way the decoder could do the same thing, because it knows the bit length, and the original value is properly decoded (signed or unsigned).</p><p><img src='doc/ex6_dec.png' alt='Final result' referrerPolicy='no-referrer' /></p></li></ul><h3><a name='header-n10575' class='md-header-anchor '></a>Video</h3><ul><li><p>A couple of error were noticed after the encoder and decoder process was finally finished. This is one of the first frames I saw after running the en/decoder:</p><p><img src='doc/video/sample00_prediction_errors_are_absdiff.png' alt='Video artefacts 1' referrerPolicy='no-referrer' /></p><p>Here the woman in the center is barely visible, and it seems that a lot of Macroblocks are similar, which is obviously not supposed to happen. After some digging it looked like there was no data passed to the error prediction frame, it was filled at random. After fixing this the following image appeared: </p><p><img src='doc/video/sample00_diff_not_expanded.png' alt='Video artefacts 2' referrerPolicy='no-referrer' /></p><p>Now it was even worse. There was no actual image data left in the PFrame. I found that the expanded motion prediction errors where not taken into account when decoding a frame, and the effect of a motion vector where completely negated.</p><p>I added a new function to copy the motion prediction error Macroblock data to its corresponding Microblocks (within the Macroblock). This resulted in an actually better looking image.</p><p><img src='doc/video/sample00_wrong_block_mvecs.png' alt='Video artefacts 3' referrerPolicy='no-referrer' /><img src='doc/video/sample00_wrong_block_mvecs_z.png' alt='Video artefacts 3' referrerPolicy='no-referrer' /></p><p>However, certain Macroblocks seem to be completely off-target. Since nothing was wrong with the way I searched for them, I looked elsewhere.
I appeared I had used the bit size of the GOP parameter to store the motion vectors instead of the actual correct value of the merange parameter. In the above frame, all motion vectors would only be about 3 bits in size.</p><p><img src='doc/video/sample00_correct.png' alt='Video correct' referrerPolicy='no-referrer' /></p><p>After a quick correction, everything went as expected as seen above. Scene switches also appered smoothly as seen below.</p><p><img src='doc/video/sample00_scene_switch_minimal_artefacts.png' alt='Video scene switch' referrerPolicy='no-referrer' /></p></li></ul><p>Due to some time constraints (other overlapping deadlines), I only recently finished the video encoder, so not much else could be tested. I did however make two comparison videos with a randomly selected subject. On the left is the encoded and then decoded video with motion compensation enabled, and on the bottom-right the same video, but with the setting disabled. The artefacts are clearly visible on fast moving objects in each video.</p><figure><table><thead><tr><th style='text-align:left;' >Link</th><th style='text-align:center;' >GOP</th><th style='text-align:center;' >MERANGE</th><th style='text-align:center;' >Motion comp</th></tr></thead><tbody><tr><td style='text-align:left;' ><a href='https://www.youtube.com/watch?v=sTdJlQB5bEo'>Example 1</a></td><td style='text-align:center;' >4</td><td style='text-align:center;' >16</td><td style='text-align:center;' >1/0 (top-left/bottom-right)</td></tr><tr><td style='text-align:left;' ><a href='https://www.youtube.com/watch?v=3n3cSAYzSbc'>Example 2</a></td><td style='text-align:center;' >6</td><td style='text-align:center;' >32</td><td style='text-align:center;' >1/0 (top-left/bottom-right)</td></tr></tbody></table></figure><p>Both were encoded to YUV frames with ffmpeg, then encoded and decoded by my video en/decoder and then encoded back with the rawvideo coded on ultrafast to an mp4 with minimal loss in pixelation. The total encoding process took around one minute for 10 seconds of 1280x720@25fps video. I also added the audio from the original videos back before uploading the results.</p><p>I am not going to upload the 3.4 Gb ( * 2) of decoded videos (628 Mb encoded) as the assignments specifies, but I will keep them as reference if requested.</p><h2><a name='header-n10608' class='md-header-anchor '></a>Test results</h2><h3><a name='header-n10609' class='md-header-anchor '></a>Size statistics</h3><figure><table><thead><tr><th>Example image</th><th style='text-align:center;' >Resolution</th><th style='text-align:right;' >Raw size</th><th style='text-align:right;' >Encoded</th><th style='text-align:right;' >Ratio</th><th style='text-align:right;' >Huffman</th><th style='text-align:right;' >Ratio</th></tr></thead><tbody><tr><td>ex0</td><td style='text-align:center;' ><code>8x8</code></td><td style='text-align:right;' >64b</td><td style='text-align:right;' >82b</td><td style='text-align:right;' >128%</td><td style='text-align:right;' >82b</td><td style='text-align:right;' >128%</td></tr><tr><td>ex1</td><td style='text-align:center;' ><code>936x936</code></td><td style='text-align:right;' >876 096b</td><td style='text-align:right;' >413 210b</td><td style='text-align:right;' >47%</td><td style='text-align:right;' >327 658b</td><td style='text-align:right;' >37%</td></tr><tr><td>ex2</td><td style='text-align:center;' ><code>512x512</code></td><td style='text-align:right;' >262 144b</td><td style='text-align:right;' >104 597b</td><td style='text-align:right;' >40%</td><td style='text-align:right;' >83 274b</td><td style='text-align:right;' >32%</td></tr><tr><td>ex3</td><td style='text-align:center;' ><code>400x400</code></td><td style='text-align:right;' >160 000b</td><td style='text-align:right;' >76 033b</td><td style='text-align:right;' >48%</td><td style='text-align:right;' >61 230b</td><td style='text-align:right;' >38%</td></tr><tr><td>ex4</td><td style='text-align:center;' ><code>4096x912</code></td><td style='text-align:right;' >3 735 552b</td><td style='text-align:right;' >1 834 256b</td><td style='text-align:right;' >49%</td><td style='text-align:right;' >1 473 058b</td><td style='text-align:right;' >39%</td></tr><tr><td>ex5</td><td style='text-align:center;' ><code>2160x2160</code></td><td style='text-align:right;' >4 665 600b</td><td style='text-align:right;' >1 598 931b</td><td style='text-align:right;' >34%</td><td style='text-align:right;' >1 369 376b</td><td style='text-align:right;' >29%</td></tr><tr><td>ex6</td><td style='text-align:center;' ><code>512x256</code></td><td style='text-align:right;' >131 072b</td><td style='text-align:right;' >42 198b</td><td style='text-align:right;' >32%</td><td style='text-align:right;' >34 191b</td><td style='text-align:right;' >26%</td></tr></tbody></table></figure><h3><a name='header-n10675' class='md-header-anchor '></a>Timing statistics</h3><figure><table><thead><tr><th>Example image</th><th style='text-align:center;' >Resolution</th><th style='text-align:right;' >Enc Time</th><th style='text-align:right;' >Dec Time</th><th style='text-align:right;' >Huffman enc</th><th style='text-align:right;' >Huffman dec</th><th style='text-align:right;' >OpenMP enc</th><th style='text-align:right;' >OpenMP dec</th></tr></thead><tbody><tr><td>ex0</td><td style='text-align:center;' ><code>8x8</code></td><td style='text-align:right;' >7.0ms</td><td style='text-align:right;' >5.1ms</td><td style='text-align:right;' >8.0ms</td><td style='text-align:right;' >5.5ms</td><td style='text-align:right;' >11.9ms</td><td style='text-align:right;' >5.6ms</td></tr><tr><td>ex1</td><td style='text-align:center;' ><code>936x936</code></td><td style='text-align:right;' >239.8ms</td><td style='text-align:right;' >201.1ms</td><td style='text-align:right;' >266.4ms</td><td style='text-align:right;' >220.2ms</td><td style='text-align:right;' >126.0ms</td><td style='text-align:right;' >79.5ms</td></tr><tr><td>ex2</td><td style='text-align:center;' ><code>512x512</code></td><td style='text-align:right;' >74.8ms</td><td style='text-align:right;' >65.6ms</td><td style='text-align:right;' >85.7ms</td><td style='text-align:right;' >67.9ms</td><td style='text-align:right;' >43.0ms</td><td style='text-align:right;' >29.0ms</td></tr><tr><td>ex3</td><td style='text-align:center;' ><code>400x400</code></td><td style='text-align:right;' >49.0ms</td><td style='text-align:right;' >42.0ms</td><td style='text-align:right;' >57.7ms</td><td style='text-align:right;' >45.6ms</td><td style='text-align:right;' >29.5ms</td><td style='text-align:right;' >20.6ms</td></tr><tr><td>ex4</td><td style='text-align:center;' ><code>4096x912</code></td><td style='text-align:right;' >1019.0ms</td><td style='text-align:right;' >842.1ms</td><td style='text-align:right;' >1139.5ms</td><td style='text-align:right;' >851.26ms</td><td style='text-align:right;' >461.9ms</td><td style='text-align:right;' >327.3ms</td></tr><tr><td>ex5</td><td style='text-align:center;' ><code>2160x2160</code></td><td style='text-align:right;' >1241.7ms</td><td style='text-align:right;' >1060.8ms</td><td style='text-align:right;' >1334.7ms</td><td style='text-align:right;' >1046.7ms</td><td style='text-align:right;' >506.1ms</td><td style='text-align:right;' >373.1ms</td></tr><tr><td>ex6</td><td style='text-align:center;' ><code>512x256</code></td><td style='text-align:right;' >38.5ms</td><td style='text-align:right;' >33.8ms</td><td style='text-align:right;' >46.2ms</td><td style='text-align:right;' >35.3ms</td><td style='text-align:right;' >23.9ms</td><td style='text-align:right;' >17.4ms</td></tr></tbody></table></figure><p><em>Regular</em>, <em>With extra Huffman compression</em> and <em>Huffman with OpenMP</em>. All were determined on a machine with an <code>Intel i7 7700k</code> CPU with the default turbo boost enabled (3.5 to 4.90 GHz clock speed).</p><p>No expansive video en/decoding statistics as of yet.</p><p>The given sample videos on Blockboard take between 3 and 4.5 seconds to encode and about 85% of that to decode, resulting in a compression between 11 (the moving blocks) and 27%.</p><p>The two example videos above (1280x720@25fps) yielded the following data:</p><figure><table><thead><tr><th>Example video</th><th style='text-align:center;' >Resolution</th><th style='text-align:right;' >Enc Time</th><th style='text-align:right;' >Dec Time</th><th>Raw size</th><th>Enc size</th></tr></thead><tbody><tr><td>10 second segments</td><td style='text-align:center;' >1280x720@25fps</td><td style='text-align:right;' >~60s</td><td style='text-align:right;' >~48s</td><td>345 600 000b</td><td>62 860 230b</td></tr></tbody></table></figure><p>Or a total raw size of 3 456 000 000b and encoded size of 628 602 300b, about 18% of the original.
Mind that in these results, Huffman was not enabled, since it crashed the decoder on certain frames (I haven't found out where yet).</p><h2><a name='header-n10769' class='md-header-anchor '></a>Class diagram </h2><p><img src='doc/ClassDiagram.png' alt='Class Diagram' referrerPolicy='no-referrer' /></p><p> </p></div>
</body>
</html>