|
39 | 39 | .inner-circle { |
40 | 40 | border: 2px solid blue; |
41 | 41 | } |
| 42 | + .reference-circle { |
| 43 | + position: absolute; |
| 44 | + border-radius: 50%; |
| 45 | + border: 2px solid limegreen; |
| 46 | + background: transparent; |
| 47 | + pointer-events: auto; |
| 48 | + cursor: grab; |
| 49 | + } |
42 | 50 | </style> |
43 | 51 | </head> |
44 | 52 | <body> |
|
73 | 81 | let img = new Image(); |
74 | 82 | let circles = []; |
75 | 83 | let radius = 20; |
| 84 | +let nHist = 30; |
76 | 85 | let innerRadius = 15; |
77 | 86 | let draggingCircle = null; |
| 87 | +let referenceCircle = null; |
78 | 88 | let offsetX, offsetY; |
79 | 89 |
|
80 | 90 | const canvas = document.getElementById("canvas"); |
|
139 | 149 | } |
140 | 150 | } |
141 | 151 |
|
142 | | -function makeDraggable(outerEl, innerEl) { |
| 152 | +function createReferenceCircle() { |
| 153 | + if (referenceCircle) { |
| 154 | + referenceCircle.remove(); // Remove existing |
| 155 | + } |
| 156 | + |
| 157 | + const x = canvas.width / 2; |
| 158 | + const y = canvas.height / 2; |
| 159 | + |
| 160 | + const el = document.createElement("div"); |
| 161 | + el.className = "reference-circle"; |
| 162 | + el.style.width = radius * 2 + "px"; |
| 163 | + el.style.height = radius * 2 + "px"; |
| 164 | + el.style.left = (x - radius) + "px"; |
| 165 | + el.style.top = (y - radius) + "px"; |
| 166 | + |
| 167 | + container.appendChild(el); |
| 168 | + referenceCircle = el; |
| 169 | + |
| 170 | + makeDraggable(el, null, true); // last param = isReference |
| 171 | +} |
| 172 | + |
| 173 | +function makeDraggable(outerEl, innerEl, isReference = false) { |
143 | 174 | outerEl.onmousedown = (e) => { |
144 | 175 | draggingCircle = outerEl; |
145 | 176 | offsetX = e.offsetX; |
|
156 | 187 | const x = newX + radius; |
157 | 188 | const y = newY + radius; |
158 | 189 |
|
159 | | - innerEl.style.left = (x - innerRadius) + "px"; |
160 | | - innerEl.style.top = (y - innerRadius) + "px"; |
161 | | - |
162 | | - updateCirclePosition(e.target); |
| 190 | + if (innerEl) { |
| 191 | + innerEl.style.left = (x - innerRadius) + "px"; |
| 192 | + innerEl.style.top = (y - innerRadius) + "px"; |
| 193 | + updateCirclePosition(e.target); |
| 194 | + } |
163 | 195 | }; |
164 | 196 |
|
165 | 197 | document.onmouseup = () => { |
|
260 | 292 | img.onload = () => { |
261 | 293 | resizeCanvasToImage(); |
262 | 294 | drawImage(); |
| 295 | + createReferenceCircle(); |
263 | 296 | clearCircles(); |
264 | 297 | }; |
265 | 298 | img.src = e.target.result; |
|
275 | 308 |
|
276 | 309 | let sum = 0; |
277 | 310 | let count = 0; |
278 | | - let bin_width = 255/30 |
279 | | - let hist = [ |
280 | | - 0,0,0,0,0,0,0,0,0,0, |
281 | | - 0,0,0,0,0,0,0,0,0,0, |
282 | | - 0,0,0,0,0,0,0,0,0,0, |
283 | | - ]; |
| 311 | + let bin_width = 255/nHist; |
| 312 | + let hist = Array(nHist).fill(0);; |
284 | 313 | for (let j = 0; j < radius * 2; j++) { |
285 | 314 | for (let i = 0; i < radius * 2; i++) { |
286 | 315 | const dx = i - radius; |
|
313 | 342 | return hist; |
314 | 343 | } |
315 | 344 |
|
| 345 | +function getGrayHistThreshold(x, y, radius) { |
| 346 | + x += 2 |
| 347 | + y += 2 |
| 348 | + const imgData = ctx.getImageData(x - radius, y - radius, radius * 2, radius * 2); |
| 349 | + const data = imgData.data; |
| 350 | + |
| 351 | + let sum = 0; |
| 352 | + let count = 0; |
| 353 | + let bin_width = 255/nHist; |
| 354 | + let hist = Array(nHist).fill(0);; |
| 355 | + for (let j = 0; j < radius * 2; j++) { |
| 356 | + for (let i = 0; i < radius * 2; i++) { |
| 357 | + const dx = i - radius; |
| 358 | + const dy = j - radius; |
| 359 | + if (dx * dx + dy * dy <= radius * radius) { |
| 360 | + if (dx * dx + dy * dy >= innerRadius * innerRadius) { |
| 361 | + const idx = (j * radius * 2 + i) * 4; |
| 362 | + |
| 363 | + const r = data[idx]; |
| 364 | + const g = data[idx + 1]; |
| 365 | + const b = data[idx + 2]; |
| 366 | + const gray = 0.299 * r + 0.587 * g + 0.114 * b; |
| 367 | + const grayIdx = Math.floor(gray/bin_width) |
| 368 | + sum += gray; |
| 369 | + hist[grayIdx]++; |
| 370 | + count++; |
| 371 | + } |
| 372 | + } |
| 373 | + } |
| 374 | + } |
| 375 | + |
| 376 | + cumsum = 0; |
| 377 | + console.log("Ref Hist:", hist) |
| 378 | + for(let i = nHist-1; i>=0; i--) { |
| 379 | + cumsum += hist[i]; |
| 380 | + if(cumsum >= count*0.9) { |
| 381 | + return i; |
| 382 | + } |
| 383 | + } |
| 384 | + |
| 385 | + return 0; |
| 386 | +} |
| 387 | + |
316 | 388 | function showGrayLevels() { |
317 | 389 | let m = circles.length; |
318 | 390 | let n = (m > 0) ? circles[0].length : 0; |
319 | 391 | let result = "[\n"; |
320 | | - let excelResult = "" |
| 392 | + let excelResult = ""; |
| 393 | + |
| 394 | + const x = parseInt(referenceCircle.style.left) + referenceCircle.offsetWidth / 2; |
| 395 | + const y = parseInt(referenceCircle.style.top) + referenceCircle.offsetHeight / 2; |
| 396 | + |
| 397 | + let thresh = getGrayHistThreshold(x, y, radius); |
| 398 | + console.log("Hist thresh:", thresh); |
| 399 | + |
321 | 400 | for (let i = 0; i < m; i++) { |
322 | 401 | for (let j = 0; j < n; j++) { |
323 | 402 | let x = circles[i][j].x; |
324 | 403 | let y = circles[i][j].y; |
325 | 404 | let grayHist = getGrayHist(x, y, radius); |
326 | | - result += grayHist.join(',')+'\n'; |
327 | | - excelResult += grayHist.join('\t')+'\r\n'; |
| 405 | + grayHist = grayHist.slice(0, thresh+1); |
| 406 | + let threshGray = 0; |
| 407 | + for(let i = 0; i < thresh; i++) { |
| 408 | + threshGray += grayHist[i]*(nHist-i); |
| 409 | + } |
| 410 | + excelResult += threshGray+'\r\n'; |
| 411 | + //excelResult += grayHist.join('\t')+'\r\n'; |
328 | 412 | } |
329 | 413 | } |
330 | 414 | navigator.clipboard.writeText(excelResult).then(() => { |
|
0 commit comments