ppu-ocv
Version:
A type-safe, modular, chainable image processing library built on top of OpenCV.js with a fluent API leveraging pipeline processing.
1 lines • 3.84 kB
JavaScript
import{cv}from"./cv-provider.js";export function calculateLineAngle(points){if(points.length<2)return 0;let n=points.length;let sumX=0,sumY=0,sumXY=0,sumXX=0;for(let p of points){sumX+=p.x;sumY+=p.y;sumXY+=p.x*p.y;sumXX+=p.x*p.x}let denominator=n*sumXX-sumX*sumX;if(Math.abs(denominator)<0.0000000001)return 0;let slope=(n*sumXY-sumX*sumY)/denominator;let angle=Math.atan(slope)*180/Math.PI;if(angle>45)angle-=90;if(angle<-45)angle+=90;return angle}export function calculateMinRectAngles(textRegions){let angles=[];for(let region of textRegions){try{let minRect=cv.minAreaRect(region.contour);if(!minRect)continue;let angle=minRect.angle;if(angle>45){angle-=90}else if(angle<-45){angle+=90}let areaWeight=Math.log(region.area+1);let aspectWeight=Math.min(region.aspectRatio,1/region.aspectRatio)*2;let weight=areaWeight*aspectWeight;angles.push({angle,weight})}catch{continue}}return angles}export function calculateBaselineAngles(textRegions){let angles=[];for(let region of textRegions){try{let points=region.contour.data32S;if(!points||points.length<8)continue;let minX=1/0;let maxX=-1/0;for(let i=0;i<points.length;i+=2){let x=points[i];if(x!==undefined){if(x<minX)minX=x;if(x>maxX)maxX=x}}if(minX===1/0)continue;let bucketMaxY=[null,null,null];let xRange=maxX-minX||1;for(let i=0;i<points.length;i+=2){let x=points[i];let y=points[i+1];if(x===undefined||y===undefined)continue;let bucket=Math.min(2,Math.floor((x-minX)/xRange*3));let current=bucketMaxY[bucket];if(current===null||y>current.y){bucketMaxY[bucket]={x,y}}}let baselinePoints=bucketMaxY.filter((p)=>p!==null);if(baselinePoints.length>=2){let angle=calculateLineAngle(baselinePoints);let weight=region.area*Math.min(region.aspectRatio,1/region.aspectRatio);angles.push({angle,weight})}}catch{continue}}return angles}export function calculateHoughAngles(mat,minAngle,maxAngle,log){let angles=[];try{let kernel=cv.getStructuringElement(cv.MORPH_RECT,new cv.Size(3,1));let morphed=new cv.Mat;cv.morphologyEx(mat,morphed,cv.MORPH_CLOSE,kernel);let lines=new cv.Mat;cv.HoughLinesP(morphed,lines,1,Math.PI/180,30,50,10);for(let i=0;i<lines.rows;i++){let line=lines.data32S.subarray(i*4,(i+1)*4);const[x1,y1,x2,y2]=line;if(x1!==undefined&&y1!==undefined&&x2!==undefined&&y2!==undefined){let dx=x2-x1;let dy=y2-y1;if(Math.abs(dx)>1){let angle=Math.atan2(dy,dx)*180/Math.PI;if(angle>45)angle-=90;if(angle<-45)angle+=90;if(angle>=minAngle&&angle<=maxAngle){let lineLength=Math.sqrt(dx*dx+dy*dy);angles.push({angle,weight:lineLength})}}}}morphed.delete();lines.delete();kernel.delete()}catch{log("Hough transform failed, skipping this method.")}return angles}export function calculateConsensusAngle(angles,minAngle,maxAngle,log){if(angles.length===0)return 0;let sortedAngles=[...angles].sort((a,b)=>a.angle-b.angle);let q1Index=Math.floor(sortedAngles.length*0.25);let q3Index=Math.floor(sortedAngles.length*0.75);let q1=sortedAngles[q1Index]?.angle||0;let q3=sortedAngles[q3Index]?.angle||0;let iqr=q3-q1;let lowerBound=q1-1.5*iqr;let upperBound=q3+1.5*iqr;let filteredAngles=angles.filter((a)=>a.angle>=lowerBound&&a.angle<=upperBound&&a.angle>=minAngle&&a.angle<=maxAngle);if(filteredAngles.length===0){log("All angles filtered out as outliers, using median of original set.");let medianIndex=Math.floor(sortedAngles.length/2);return sortedAngles[medianIndex]?.angle||0}let totalWeight=0;let weightedSum=0;let unweightedSum=0;let methodCounts={};for(let a of filteredAngles){totalWeight+=a.weight;weightedSum+=a.angle*a.weight;unweightedSum+=a.angle;methodCounts[a.method]=(methodCounts[a.method]||0)+1}if(totalWeight===0){return unweightedSum/filteredAngles.length}log(`Angle methods used: ${Object.entries(methodCounts).map(([method,count])=>`${method}:${count}`).join(", ")}`);let weightedAverage=weightedSum/totalWeight;return Math.max(minAngle,Math.min(maxAngle,weightedAverage))}