UNPKG

mind-ar

Version:

web augmented reality framework

175 lines (139 loc) 5.97 kB
const kHoughBinDelta = 1; // mathces [querypointIndex:x, keypointIndex: x] const computeHoughMatches = (options) => { const {keywidth, keyheight, querywidth, queryheight, matches} = options; const maxX = querywidth * 1.2; const minX = -maxX; const maxY = queryheight * 1.2; const minY = -maxY; const numAngleBins = 12; const numScaleBins = 10; const minScale = -1; const maxScale = 1; const scaleK = 10.0; const scaleOneOverLogK = 1.0 / Math.log(scaleK); const maxDim = Math.max(keywidth, keyheight); const keycenterX = Math.floor(keywidth / 2); const keycenterY = Math.floor(keyheight / 2); // compute numXBins and numYBins based on matches const projectedDims = []; for (let i = 0; i < matches.length; i++) { const queryscale = matches[i].querypoint.scale; const keyscale = matches[i].keypoint.scale; if (keyscale == 0) console.log("ERROR divide zero"); const scale = queryscale / keyscale; projectedDims.push( scale * maxDim ); } // TODO optimize median // weird. median should be [Math.floor(projectedDims.length/2) - 1] ? projectedDims.sort((a1, a2) => {return a1 - a2}); const medianProjectedDim = projectedDims[ Math.floor(projectedDims.length/2) - (projectedDims.length%2==0?1:0) -1 ]; const binSize = 0.25 * medianProjectedDim; const numXBins = Math.max(5, Math.ceil((maxX - minX) / binSize)); const numYBins = Math.max(5, Math.ceil((maxY - minY) / binSize)); const numXYBins = numXBins * numYBins; const numXYAngleBins = numXYBins * numAngleBins; // do voting const querypointValids = []; const querypointBinLocations = []; const votes = {}; for (let i = 0; i < matches.length; i++) { const querypoint = matches[i].querypoint; const keypoint = matches[i].keypoint; const {x, y, scale, angle} = _mapCorrespondence({querypoint, keypoint, keycenterX, keycenterY, scaleOneOverLogK}); // Check that the vote is within range if (x < minX || x >= maxX || y < minY || y >= maxY || angle <= -Math.PI || angle > Math.PI || scale < minScale || scale >= maxScale) { querypointValids[i] = false; continue; } // map properties to bins let fbinX = numXBins * (x - minX) / (maxX - minX); let fbinY = numYBins * (y - minY) / (maxY - minY); let fbinAngle = numAngleBins * (angle + Math.PI) / (2.0 * Math.PI); let fbinScale = numScaleBins * (scale - minScale) / (maxScale - minScale); querypointBinLocations[i] = {binX: fbinX, binY: fbinY, binAngle: fbinAngle, binScale: fbinScale}; let binX = Math.floor(fbinX - 0.5); let binY = Math.floor(fbinY - 0.5); let binScale = Math.floor(fbinScale - 0.5); let binAngle = (Math.floor(fbinAngle - 0.5) + numAngleBins) % numAngleBins; // check can vote all 16 bins if (binX < 0 || binX + 1 >= numXBins || binY < 0 || binY + 1 >= numYBins || binScale < 0 || binScale +1 >= numScaleBins) { querypointValids[i] = false; continue; } for (let dx = 0; dx < 2; dx++) { let binX2 = binX + dx; for (let dy = 0; dy < 2; dy++) { let binY2 = binY + dy; for (let dangle = 0; dangle < 2; dangle++) { let binAngle2 = (binAngle + dangle) % numAngleBins; for (let dscale = 0; dscale < 2; dscale++) { let binScale2 = binScale + dscale; const binIndex = binX2 + binY2 * numXBins + binAngle2 * numXYBins + binScale2 * numXYAngleBins; if (votes[binIndex] === undefined) votes[binIndex] = 0; votes[binIndex] += 1; } } } } querypointValids[i] = true; } let maxVotes = 0; let maxVoteIndex = -1; Object.keys(votes).forEach((index) => { if (votes[index] > maxVotes) { maxVotes = votes[index]; maxVoteIndex = index; } }); if (maxVotes < 3) return []; // get back bins from vote index const binX = Math.floor(((maxVoteIndex % numXYAngleBins) % numXYBins) % numXBins); const binY = Math.floor((((maxVoteIndex - binX) % numXYAngleBins) % numXYBins) / numXBins); const binAngle = Math.floor(((maxVoteIndex - binX - (binY * numXBins)) % numXYAngleBins) / numXYBins); const binScale = Math.floor((maxVoteIndex - binX - (binY * numXBins) - (binAngle * numXYBins)) / numXYAngleBins); //console.log("hough voted: ", {binX, binY, binAngle, binScale, maxVoteIndex}); const houghMatches = []; for (let i = 0; i < matches.length; i++) { if (!querypointValids[i]) continue; const queryBins = querypointBinLocations[i]; // compute bin difference const distBinX = Math.abs(queryBins.binX - (binX+0.5)); if (distBinX >= kHoughBinDelta) continue; const distBinY = Math.abs(queryBins.binY - (binY+0.5)); if (distBinY >= kHoughBinDelta) continue; const distBinScale = Math.abs(queryBins.binScale - (binScale+0.5)); if (distBinScale >= kHoughBinDelta) continue; const temp = Math.abs(queryBins.binAngle - (binAngle+0.5)); const distBinAngle = Math.min(temp, numAngleBins - temp); if (distBinAngle >= kHoughBinDelta) continue; houghMatches.push(matches[i]); } return houghMatches; } const _mapCorrespondence = ({querypoint, keypoint, keycenterX, keycenterY, scaleOneOverLogK}) => { // map angle to (-pi, pi] let angle = querypoint.angle - keypoint.angle; if (angle <= -Math.PI) angle += 2*Math.PI; else if (angle > Math.PI) angle -= 2*Math.PI; const scale = querypoint.scale / keypoint.scale; // 2x2 similarity const cos = scale * Math.cos(angle); const sin = scale * Math.sin(angle); const S = [cos, -sin, sin, cos]; const tp = [ S[0] * keypoint.x + S[1] * keypoint.y, S[2] * keypoint.x + S[3] * keypoint.y ]; const tx = querypoint.x - tp[0]; const ty = querypoint.y - tp[1]; return { x: S[0] * keycenterX + S[1] * keycenterY + tx, y: S[2] * keycenterX + S[3] * keycenterY + ty, angle: angle, scale: Math.log(scale) * scaleOneOverLogK } } export { computeHoughMatches }