diff options
Diffstat (limited to 'cnn_v3/training/adjust.html')
| -rw-r--r-- | cnn_v3/training/adjust.html | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/cnn_v3/training/adjust.html b/cnn_v3/training/adjust.html new file mode 100644 index 0000000..219145c --- /dev/null +++ b/cnn_v3/training/adjust.html @@ -0,0 +1,239 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<title>Align & Optimize</title> +<style> +body{font-family:sans-serif;text-align:center;background:#f5f5f5} +canvas{border:1px solid #ccc;margin-top:10px;cursor:grab} +</style> +</head> +<body> + +<h3>Align & Optimize (Arrows=move, Shift+Arrows=scale)</h3> + +<input type="file" id="f1" accept="image/*"> +<input type="file" id="f2" accept="image/*"><br><br> + +Alpha <input type="range" id="alpha" min="0.2" max="1" step="0.01" value="0.5"> +<br><br> + +<button id="crop">Crop</button> +<button id="opt">Optimize</button> +<button id="dl" disabled>Download</button> + +<div>MSE: <span id="score">-</span></div> + +<canvas id="c"></canvas> + +<script> +const c=document.getElementById('c'),x=c.getContext('2d'); +let i1=new Image(),i2=new Image(),l1=0,l2=0; + +let ox=0,oy=0,sx=1,sy=1; +let drag=0,lx,ly,mx=0,my=0,a=document.getElementById('alpha'); + +let out1,out2; + +// finer than before +const PAN_STEP=0.3; +const SCALE_STEP=0.002; // 🔬 very fine zoom +const DRAG_SPEED=0.4; + +// MSE buffers (reused) +let buf1=document.createElement('canvas'); +let buf2=document.createElement('canvas'); +let b1=buf1.getContext('2d'); +let b2=buf2.getContext('2d'); +const SAMPLE=128; + +// load images +f1.onchange=e=>{ + i1=new Image(); + i1.onload=()=>{ + l1=1;c.width=i1.width;c.height=i1.height; + ox=oy=0;sx=sy=1;draw(); + }; + i1.src=URL.createObjectURL(e.target.files[0]); +}; + +f2.onchange=e=>{ + i2=new Image(); + i2.onload=()=>{ + l2=1; + ox=(c.width-i2.width)/2; + oy=(c.height-i2.height)/2; + sx=sy=1;draw(); + }; + i2.src=URL.createObjectURL(e.target.files[0]); +}; + +function draw(){ + if(!l1)return; + x.clearRect(0,0,c.width,c.height); + x.drawImage(i1,0,0); + if(l2){ + x.save(); + x.globalAlpha=a.value; + x.translate(ox,oy); + x.scale(sx,sy); + x.drawImage(i2,0,0); + x.restore(); + } + updateScore(); +} + +// --- FAST MSE --- +function computeMSE(){ + let x2=ox,y2=oy,w2=i2.width*sx,h2=i2.height*sy; + let x0=Math.max(0,x2),y0=Math.max(0,y2); + let x1=Math.min(c.width,x2+w2),y1=Math.min(c.height,y2+h2); + let w=Math.floor(x1-x0),h=Math.floor(y1-y0); + if(w<=0||h<=0)return Infinity; + + let scale=Math.min(1,SAMPLE/Math.max(w,h)); + let sw=Math.max(1,Math.floor(w*scale)); + let sh=Math.max(1,Math.floor(h*scale)); + + buf1.width=buf2.width=sw; + buf1.height=buf2.height=sh; + + b1.drawImage(i1,x0,y0,w,h,0,0,sw,sh); + b2.drawImage(i2,(x0-ox)/sx,(y0-oy)/sy,w/sx,h/sy,0,0,sw,sh); + + let d1=b1.getImageData(0,0,sw,sh).data; + let d2=b2.getImageData(0,0,sw,sh).data; + + let mse=0,n=sw*sh; + + for(let i=0;i<d1.length;i+=4){ + let g1=0.299*d1[i]+0.587*d1[i+1]+0.114*d1[i+2]; + let g2=0.299*d2[i]+0.587*d2[i+1]+0.114*d2[i+2]; + let d=g1-g2; + mse+=d*d; + } + + return mse/n; +} + +function updateScore(){ + if(l1&&l2){ + let s=computeMSE(); + score.textContent=isFinite(s)?s.toFixed(2):"-"; + } +} + +// mouse +c.onmousemove=e=>{ + mx=e.offsetX;my=e.offsetY; + if(!drag)return; + ox+=(e.offsetX-lx)*DRAG_SPEED; + oy+=(e.offsetY-ly)*DRAG_SPEED; + lx=e.offsetX;ly=e.offsetY; + draw(); +}; + +c.onmousedown=e=>{drag=1;lx=e.offsetX;ly=e.offsetY;c.style.cursor="grabbing";} +c.onmouseup=c.onmouseleave=()=>{drag=0;c.style.cursor="grab";}; + +a.oninput=draw; + +// keyboard +document.onkeydown=e=>{ + if(!l2)return; + if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)) e.preventDefault(); + + if(e.shiftKey){ + // scale around mouse + let ix=(mx-ox)/sx,iy=(my-oy)/sy; + + if(e.key==="ArrowRight") sx+=SCALE_STEP; + if(e.key==="ArrowLeft") sx-=SCALE_STEP; + if(e.key==="ArrowUp") sy+=SCALE_STEP; + if(e.key==="ArrowDown") sy-=SCALE_STEP; + + ox=mx-ix*sx; + oy=my-iy*sy; + + } else { + // move + if(e.key==="ArrowRight") ox+=PAN_STEP; + if(e.key==="ArrowLeft") ox-=PAN_STEP; + if(e.key==="ArrowUp") oy-=PAN_STEP; + if(e.key==="ArrowDown") oy+=PAN_STEP; + } + + draw(); +}; + +// --- OPTIMIZER (safe + fast) --- +opt.onclick=async ()=>{ + if(!l1||!l2)return; + + let best={ox,oy,sx,sy,score:computeMSE()}; + + for(let iter=0;iter<10;iter++){ + + let stepO=0.5/(iter+1); + let stepS=0.005/(iter+1); + + let valuesO=[-stepO,-stepO/2,0,stepO/2,stepO]; + let valuesS=[-stepS,-stepS/2,0,stepS/2,stepS]; + + for(let dx of valuesO) + for(let dy of valuesO) + for(let dsx of valuesS) + for(let dsy of valuesS){ + + let tox=best.ox+dx; + let toy=best.oy+dy; + let tsx=best.sx+dsx; + let tsy=best.sy+dsy; + + ox=tox;oy=toy;sx=tsx;sy=tsy; + + let s=computeMSE(); + if(s<best.score){ + best={ox:tox,oy:toy,sx:tsx,sy:tsy,score:s}; + } + } + + ox=best.ox;oy=best.oy;sx=best.sx;sy=best.sy; + draw(); + + await new Promise(r=>setTimeout(r,0)); // keep UI responsive + } +}; + +// crop + download +crop.onclick=()=>{ + let x2=ox,y2=oy,w2=i2.width*sx,h2=i2.height*sy; + let x0=Math.max(0,x2),y0=Math.max(0,y2); + let x1=Math.min(c.width,x2+w2),y1=Math.min(c.height,y2+h2); + let w=x1-x0,h=y1-y0; + if(w<=0||h<=0)return alert("no overlap"); + + out1=document.createElement('canvas'); + out2=document.createElement('canvas'); + out1.width=out2.width=w; + out1.height=out2.height=h; + + out1.getContext('2d').drawImage(i1,x0,y0,w,h,0,0,w,h); + out2.getContext('2d').drawImage(i2,(x0-ox)/sx,(y0-oy)/sy,w/sx,h/sy,0,0,w,h); + + dl.disabled=0; +}; + +dl.onclick=()=>{ + let d=(cv,n)=>{ + let a=document.createElement('a'); + a.download=n+'.clipped.png'; + a.href=cv.toDataURL(); + a.click(); + }; + d(out1,'image1');d(out2,'image2'); +}; +</script> + +</body> +</html>
\ No newline at end of file |
