Brak opisu

index.html 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <script src="js/vendor/interact/interact.min.js"></script>
  6. <script src="js/vendor/opencv/opencv.js"></script>
  7. <script src="js/vendor/load-image/load-image.all.min.js"></script>
  8. <script src="js/vendor/plotly/plotly-latest.min.js"></script>
  9. <script src="js/vendor/panzoom/panzoom.js"></script>
  10. <script src="js/xword.js"></script>
  11. <style>
  12. canvas {
  13. width: 400px;
  14. }
  15. #sourceImageContainer {
  16. width: 400px;
  17. overflow: hidden;
  18. }
  19. #sourceImage {
  20. position: relative;
  21. width: min-content;
  22. }
  23. #sourceImage canvas {
  24. width: auto;
  25. }
  26. #sourceImage .overlay {
  27. position: absolute;
  28. left: 0;
  29. top: 0;
  30. }
  31. .handle {
  32. position: absolute;
  33. box-sizing: border-box;
  34. width: 301px;
  35. height: 301px;
  36. border-radius: 50%;
  37. border: 6px solid red;
  38. transform: translate(-50%, -50%);
  39. }
  40. #graph {
  41. width: 1000px;
  42. height: 800px;
  43. }
  44. </style>
  45. </head>
  46. <body>
  47. <div>
  48. <input type="file" id="fileInput">
  49. </div>
  50. <div id="sourceImageContainer">
  51. <div id="sourceImage"></div>
  52. </div>
  53. <button id="recalculateButton">Recalculate</button>
  54. <div>
  55. <canvas id="output"></canvas>
  56. </div>
  57. <div id="graph"></div>
  58. <script>
  59. 'use strict';
  60. let sourceImage = document.getElementById("sourceImage");
  61. let sourceImageParent = sourceImage.parentElement;
  62. let fileInput = document.getElementById("fileInput");
  63. let isSquare = true;
  64. let gaussianBlurSize = 11;
  65. let adaptiveThresholdBlockSize = 11;
  66. let adaptiveThresholdMeanAdjustment = 2;
  67. let numDilations = 1;
  68. let contourErosionKernelSize = 5;
  69. let contourErosionIterations = 5;
  70. let lineDetectorElementSize = 51;
  71. let samplingBlockSizeRatio = 0.25;
  72. let samplingThresholdQuantile = 0.3;
  73. let samplingThreshold = null;
  74. let gridLineThickness = 4;
  75. let gridSquareInternalSize = 64;
  76. let gridMarginSize = 20;
  77. let img = null;
  78. let zoomCallback = null;
  79. let zoomer = Panzoom(sourceImage, {
  80. excludeClass: 'handle'
  81. });
  82. sourceImage.addEventListener('panzoomzoom', function(event) {
  83. if (zoomCallback != null) zoomCallback(event);
  84. });
  85. sourceImageParent.addEventListener('wheel', function(event) {
  86. if (! event.shiftKey) return;
  87. zoomer.zoomWithWheel(event, {disablePan: true});
  88. });
  89. fileInput.onchange = function(e) {
  90. if (e.target.files.length > 0) {
  91. loadImage(
  92. e.target.files[0],
  93. function(canvas) {
  94. let imageWidth = canvas.width;
  95. let imageHeight = canvas.height;
  96. let imageScale = sourceImageParent.offsetWidth / imageWidth;
  97. sourceImageParent.style.height = Math.round(imageHeight * imageScale) + 'px';
  98. if (img != null) {
  99. img.delete();
  100. img = null;
  101. }
  102. while (sourceImage.firstChild) {
  103. sourceImage.removeChild(sourceImage.firstChild);
  104. }
  105. sourceImage.appendChild(canvas);
  106. zoomer.setOptions({
  107. minScale: imageScale,
  108. maxScale: 1
  109. });
  110. zoomer.zoomToPoint(imageScale, {clientX: sourceImageParent.offsetLeft, clientY: sourceImageParent.offsetTop});
  111. let overlay = document.createElement('canvas');
  112. overlay.className = 'overlay';
  113. overlay.width = imageWidth;
  114. overlay.height = imageHeight;
  115. sourceImage.appendChild(overlay);
  116. let context = overlay.getContext('2d');
  117. context.globalAlpha = 0.6;
  118. context.strokeStyle = 'red';
  119. let handles = [];
  120. function getNewCorners() {
  121. let corners = [];
  122. handles.forEach(function(handle) {
  123. corners.push(new cv.Point(
  124. parseFloat(handle.dataset.left),
  125. parseFloat(handle.dataset.top)
  126. ));
  127. });
  128. return corners;
  129. }
  130. function redrawOverlay() {
  131. let corners = getNewCorners();
  132. context.lineWidth = 1 / zoomer.getScale();
  133. context.clearRect(0, 0, imageWidth, imageHeight);
  134. context.beginPath();
  135. let corner = corners[corners.length - 1];
  136. context.moveTo(corner.x, corner.y);
  137. corners.forEach(function(corner) {
  138. context.lineTo(corner.x, corner.y);
  139. });
  140. context.stroke();
  141. }
  142. function dragMoveListener(event) {
  143. let target = event.target;
  144. let newLeft = Math.min(
  145. imageWidth - 1,
  146. Math.max(0, parseFloat(target.dataset.left) + event.dx / zoomer.getScale())
  147. );
  148. let newTop = Math.min(
  149. imageHeight - 1,
  150. Math.max(0, parseFloat(target.dataset.top) + event.dy / zoomer.getScale())
  151. );
  152. target.dataset.left = newLeft;
  153. target.dataset.top = newTop;
  154. target.style.left = newLeft + 'px';
  155. target.style.top = newTop + 'px';
  156. redrawOverlay();
  157. }
  158. function recalculate() {
  159. let square = extractSquare(img, getNewCorners());
  160. try {
  161. let numRows = getLineFrequency(square, lineDetectorElementSize, 1);
  162. let numCols = getLineFrequency(square, lineDetectorElementSize, 0);
  163. if (isSquare && (numRows !== numCols)) {
  164. console.log("WARNING: crossword is not square");
  165. }
  166. cv.imshow('output', square);
  167. } finally {
  168. square.delete();
  169. }
  170. }
  171. document.getElementById('recalculateButton').onclick = recalculate;
  172. zoomCallback = function(event) {
  173. redrawOverlay();
  174. };
  175. let src = cv.imread(canvas);
  176. try {
  177. //removeColours(src, src, 48);
  178. //cv.imshow('output', src);
  179. cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
  180. img = new cv.Mat();
  181. preprocessImage(src, img, gaussianBlurSize, adaptiveThresholdBlockSize, adaptiveThresholdMeanAdjustment, numDilations);
  182. //cv.imshow('output', img);
  183. let corners;
  184. let biggest = findBiggestContour(img);
  185. try {
  186. let eroded = erodeContour(img.size(), biggest, contourErosionKernelSize, contourErosionIterations);
  187. try {
  188. corners = getContourCorners(img.size(), eroded);
  189. } finally {
  190. eroded.delete();
  191. }
  192. } finally {
  193. biggest.delete();
  194. }
  195. corners.forEach(function(corner) {
  196. let handle = document.createElement('div');
  197. handle.className = 'handle';
  198. handle.dataset.left = corner.x;
  199. handle.dataset.top = corner.y;
  200. handle.style.left = handle.dataset.left + 'px';
  201. handle.style.top = handle.dataset.top + 'px';
  202. sourceImage.appendChild(handle);
  203. interact(handle).draggable({
  204. onmove: dragMoveListener
  205. });
  206. handles.push(handle);
  207. });
  208. redrawOverlay();
  209. recalculate();
  210. } finally {
  211. src.delete();
  212. }
  213. },
  214. {
  215. orientation: true
  216. }
  217. );
  218. }
  219. };
  220. </script>
  221. </body>
  222. </html>
  223. <!--
  224. vim:ts=4:sw=4:expandtab
  225. -->