vq.html 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <html>
  2. <head>
  3. <title>Compress colors using VQ</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <style type="text/css">
  6. @import "../../../dojo/resources/dojo.css";
  7. @import "../../../dijit/tests/css/dijitTests.css";
  8. .pane { margin-top: 2em; }
  9. </style>
  10. <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true"></script>
  11. <script type="text/javascript">
  12. dojo.require("dojox.encoding.tests.colors");
  13. dojo.require("dojox.encoding.splay");
  14. dojo.require("dojox.encoding.bits");
  15. var colors = dojox.encoding.tests.colors;
  16. var dist = function(a, b){
  17. var r = a[0] - b[0], g = a[1] - b[1], b = a[2] - b[2];
  18. return r * r + g * g + b * b;
  19. };
  20. var hexcolor = function(c){
  21. return "#" + (c[0] < 16 ? "0" : "") + c[0].toString(16) +
  22. (c[1] < 16 ? "0" : "") + c[1].toString(16) +
  23. (c[2] < 16 ? "0" : "") + c[2].toString(16);
  24. };
  25. var maxdist = function(a, b, maxdist){
  26. var r = Math.abs(a[0] - b[0]), g = Math.abs(a[1] - b[1]), b = Math.abs(a[2] - b[2]);
  27. ++maxdist[bits(r)];
  28. ++maxdist[bits(g)];
  29. ++maxdist[bits(b)];
  30. };
  31. var encodeColor = function(a, b, splay, stream){
  32. var r = a[0] - b[0], g = a[1] - b[1], b = a[2] - b[2];
  33. stream.putBits(r < 0 ? 1 : 0, 1);
  34. splay.encode(Math.abs(r), stream);
  35. stream.putBits(g < 0 ? 1 : 0, 1);
  36. splay.encode(Math.abs(g), stream);
  37. stream.putBits(b < 0 ? 1 : 0, 1);
  38. splay.encode(Math.abs(b), stream);
  39. };
  40. var bits = function(x){
  41. var w = 1;
  42. for(var v = 2; x >= v; v <<= 1, ++w);
  43. return w;
  44. };
  45. var runVQ = function(n){
  46. dojo.byId("status").innerHTML = "<em>Initializing...</em>";
  47. dojo.byId("report").innerHTML = "<em>Running VQ...</em>";
  48. var clusters = [];
  49. // select initial cluster centers
  50. var empty = {};
  51. for(var i in colors){
  52. if(i in empty){ continue; }
  53. clusters.push({center: colors[i]});
  54. if(clusters.length == n){ break; }
  55. }
  56. /*
  57. for(var i = 0; i < n; ++i){
  58. var r = Math.floor(Math.random() * 256), g = Math.floor(Math.random() * 256), b = Math.floor(Math.random() * 256);
  59. clusters.push({center: [r, g, b]});
  60. }
  61. */
  62. // do runs
  63. dojo.byId("status").innerHTML = "<div>Starting runs...</div>";
  64. var jitter = 0, niter = 1;
  65. do {
  66. // save previous centers
  67. var old_clusters = [];
  68. dojo.forEach(clusters, function(c){ old_clusters.push({center: c.center}); c.members = []; });
  69. // assign colors to clusters
  70. for(var i in colors){
  71. if(i in empty){ continue; }
  72. var c = colors[i], k = -1, kd = Number.MAX_VALUE;
  73. for(var j = 0; j < clusters.length; ++j){
  74. var jd = dist(clusters[j].center, c);
  75. if(jd < kd){ k = j, kd = jd; }
  76. }
  77. clusters[k].members.push(i);
  78. }
  79. // recalculate cluster centers
  80. for(var i = 0; i < clusters.length; ++i){
  81. if(!clusters[i].members.length){ continue; }
  82. var r = 0, g = 0, b = 0;
  83. dojo.forEach(clusters[i].members, function(name){
  84. var c = colors[name];
  85. r += c[0];
  86. g += c[1];
  87. b += c[2];
  88. });
  89. r = Math.round(r / clusters[i].members.length);
  90. g = Math.round(g / clusters[i].members.length);
  91. b = Math.round(b / clusters[i].members.length);
  92. clusters[i].center = [r, g, b];
  93. }
  94. // calculate the jitter
  95. jitter = 0;
  96. for(var i = 0; i < clusters.length; ++i){
  97. jitter = Math.max(jitter, dist(clusters[i].center, old_clusters[i].center));
  98. }
  99. var node = dojo.doc.createElement("div");
  100. node.innerHTML = "Run #" + niter + ", jitter = " + jitter;
  101. dojo.byId("status").appendChild(node);
  102. ++niter;
  103. }while(jitter > 1 && niter < 1000);
  104. // calculate the required number of bytes
  105. var output = new dojox.encoding.bits.OutputStream(),
  106. splay = new dojox.encoding.Splay(256);
  107. for(var i = 0; i < clusters.length; ++i){
  108. var c = clusters[i], m = c.members, d = 0, ol = output.getWidth();
  109. output.putBits(c.center[0], 8);
  110. output.putBits(c.center[1], 8);
  111. output.putBits(c.center[2], 8);
  112. splay.init();
  113. c.maxdist = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  114. for(var j = 0; j < m.length; ++j){
  115. var color = colors[m[j]];
  116. maxdist(c.center, color, c.maxdist);
  117. encodeColor(c.center, color, splay, output);
  118. }
  119. c.bits = output.getWidth() - ol;
  120. }
  121. var node = dojo.doc.createElement("div");
  122. node.innerHTML = "Required " + Math.ceil(output.getWidth() / 8) + " bytes";
  123. dojo.byId("status").appendChild(node);
  124. // generate color tables
  125. var reps = [];
  126. for(var i = 0; i < clusters.length; ++i){
  127. var c = clusters[i], m = c.members;
  128. reps.push("<p>Cluster #" + i + " contains " + m.length + " members. Length histogram:");
  129. for(var j = 0; j < c.maxdist.length; ++j){
  130. if(c.maxdist[j]){
  131. reps.push(" " + j + "&mdash;" + c.maxdist[j]);
  132. }
  133. }
  134. reps.push(". It requires " + c.bits + " bits (" + Math.ceil(c.bits / 8) + " bytes) to be encoded.</p>");
  135. reps.push("<table>");
  136. var wd = dist([255,255,255], c.center), bd = dist([0,0,0], c.center);
  137. reps.push("<tr><td style='background: " + hexcolor(c.center) + "; color: " +
  138. (wd < bd ? "black" : "white") + "'><strong>CENTER</strong></td><td>" +
  139. c.center[0] + "</td><td>" + c.center[1] + "</td><td>" + c.center[2] + "</td></tr>");
  140. for(var j = 0; j < m.length; ++j){
  141. var color = colors[m[j]];
  142. wd = dist([255,255,255], color);
  143. bd = dist([0,0,0], color);
  144. reps.push("<tr><td style='background: " + m[j] + "; color: " +
  145. (wd < bd ? "black" : "white") + "'><strong>" + m[j] + "</strong></td><td>" +
  146. color[0] + "</td><td>" + color[1] + "</td><td>" + color[2] + "</td></tr>");
  147. }
  148. reps.push("</table>");
  149. }
  150. dojo.byId("report").innerHTML = reps.join("\n");
  151. };
  152. run = function(){
  153. var n = parseInt(dojo.byId("ncluster").value);
  154. runVQ(n);
  155. };
  156. dojo.addOnLoad(function(){
  157. dojo.connect(dojo.byId("run"), "onclick", run);
  158. });
  159. </script>
  160. </head>
  161. <body>
  162. <h1>Compress colors using VQ</h1>
  163. <p>Select desirable number of clusters:&nbsp;<select id="ncluster">
  164. <option value="1">1</option>
  165. <option value="2">2</option>
  166. <option value="4">4</option>
  167. <option value="8">8</option>
  168. <option value="16">16</option>
  169. <option value="32">32</option>
  170. <option value="64">64</option>
  171. </select>&nbsp;<button id="run">Run</button></p>
  172. <div id="status" class="pane"><em>No status yet.</em></div>
  173. <div id="report" class="pane"><em>No results yet.</em></div>
  174. </body>
  175. </html>