modify contours fitting

This commit is contained in:
kekee000 2015-04-06 13:40:03 +08:00
parent 1aa3f9ccfb
commit d7d93aba8a
10 changed files with 240 additions and 98 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,10 +5,10 @@
define(
function (require) {
var reducePoints = require('graphics/image/contour/douglasPeuckerReducePoints');
var reducePoints = require('graphics/image/contour/reducePoints');
var findBreakPoints = require('graphics/image/contour/findBreakPoints');
var pathUtil = require('graphics/pathUtil');
var data = require('demo/../data/image-contours10');
var data = require('demo/../data/image-contours2');
var entry = {
@ -61,7 +61,7 @@ define(
if (p.right == 1) {
width = 'width: 4px;height: 4px';
}
html += '<i data-index="'+ p.index +'" '+ (p.tangency ? 'data-tangency="1"' : '') + (p.visited ? 'data-visited="1"' : '') +' title="'+ p.theta +'" style="left:'+p.x+'px;top:'+p.y+'px;'+width+'" class="'+c+'"></i>';
html += '<i data-index="'+ p.index +'" '+ (p.ntiny || p.ptiny ? 'data-tiny="1"' : '') + (p.tangency ? 'data-tangency="1"' : '') + (p.visited ? 'data-visited="1"' : '') +' title="'+ p.theta +'" style="left:'+p.x+'px;top:'+p.y+'px;'+width+'" class="'+c+'"></i>';
}
$('#points-break').html(html);

View File

@ -7,7 +7,7 @@ define(
function (require) {
var fitContour = require('graphics/image/contour/fitContour');
var data = require('demo/../data/image-contours10');
var data = require('demo/../data/image-contours5');
var drawPath = require('render/util/drawPath');
var pathUtil = require('graphics/pathUtil');

View File

@ -13,7 +13,7 @@ define(
function reduce(contour, firstIndex, lastIndex, tolerance, splitArray) {
if (lastIndex - firstIndex <= 3) {
if (lastIndex - firstIndex < 3) {
return;
}
@ -55,12 +55,17 @@ define(
if (splitArray.length) {
splitArray.unshift(firstIndex);
return splitArray.map(function (index) {
splitArray = splitArray.map(function (index) {
contour[index].index = index;
return contour[index];
}).sort(function (a, b) {
})
// 去除临近的点
splitArray.sort(function (a, b) {
return a.index - b.index;
});
return splitArray;
}
return contour;

View File

@ -11,7 +11,7 @@ define(
var vector = require('graphics/vector');
var getCos = vector.getCos;
var getDist = vector.getDist;
var THETA_CORNER = 0.8; // 拐点抑制
var THETA_CORNER = 1.0; // 拐点抑制
function dist(p0, p1) {
return Math.sqrt(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2));
@ -27,132 +27,153 @@ define(
contour = makeLink(contour);
var start = contour[0];
var cur = start;
var longDist = 80 * scale;
var longDist = 30 * scale;
var shortDist = 20 * scale;
var lineDist = 10 * scale;
var tinyDist = 2 * scale;
var tinyDist = 3 * scale;
var cur = start;
do {
cur.ndist = cur.next.pdist = dist(cur, cur.next);
// 距离比较近的点判断切角会不准确,需要特殊处理
if (Math.abs(cur.x - cur.next.x) <= tinyDist && Math.abs(cur.y - cur.next.y) <= tinyDist) {
cur.ntiny = true;
cur.next.ptiny = true;
}
var cos = getCos(
cur.ptiny ? cur.prev.prev : cur.prev,
cur,
cur.ntiny ? cur.next.next : cur.next
);
var cos = getCos(cur.ptiny ? cur.prev.prev : cur.prev, cur, cur.ntiny ? cur.next.next : cur.next);
cur.theta = Math.acos(cos > 1 ? 1 : cos) ;
if (cur.theta > THETA_CORNER) {
cur.breakPoint = true;
cur.corner = true;
}
// 判断水平和竖直线
if (
(Math.abs(cur.next.x - cur.x) < scale)
&& Math.abs(cur.next.y - cur.y) > lineDist
) {
cur.next.vertical = true;
cur.vertical = true;
cur.right = 1;
}
else if (
(Math.abs(cur.next.y - cur.y) < scale)
&& Math.abs(cur.next.x - cur.x) > lineDist
) {
cur.next.hoz = true;
cur.hoz = true;
cur.right = 1;
}
// 判断水平和竖直线
if (
(Math.abs(cur.next.x - cur.x) < scale)
&& Math.abs(cur.next.y - cur.y) > lineDist
) {
cur.next.vertical = true;
cur.vertical = true;
}
else if (
(Math.abs(cur.next.y - cur.y) < scale)
&& Math.abs(cur.next.x - cur.x) > lineDist
) {
cur.next.hoz = true;
cur.hoz = true;
}
// 判断顶角
if(cur.x <= cur.prev.x && cur.x <= cur.next.x
|| cur.y <= cur.prev.y && cur.y <= cur.next.y
) {
cur.apexTop = true;
if(cur.x <= cur.prev.x && cur.x <= cur.next.x) {
cur.xTop = true;
cur.apex = true;
}
if (cur.x >= cur.prev.x && cur.x >= cur.next.x
|| cur.y >= cur.prev.y && cur.y >= cur.next.y
) {
cur.apexBottom = true;
if(cur.y <= cur.prev.y && cur.y <= cur.next.y) {
cur.yTop = true;
cur.apex = true;
}
// 标记上一个点和下一个点的距离
if (Math.abs(cur.x - cur.next.x) > longDist) {
cur.nxl = true;
cur.next.pxl = true;
if (cur.x >= cur.prev.x && cur.x >= cur.next.x) {
cur.xBottom = true;
cur.apex = true;
}
if (Math.abs(cur.y - cur.next.y) > longDist) {
cur.nyl = true;
cur.next.pyl = true;
}
if (Math.abs(cur.x - cur.next.x) < shortDist) {
cur.nxs = true;
cur.next.pxs = true;
}
if (Math.abs(cur.y - cur.next.y) < shortDist) {
cur.nys = true;
cur.next.pys = true;
if (cur.y >= cur.prev.y && cur.y >= cur.next.y) {
cur.yBottom = true;
cur.apex = true;
}
cur = cur.next;
} while (cur !== start);
cur = start;
// 判断角点和切线点
do {
if (cur.visited) {
cur = cur.next;
continue;
}
// 修正直角连接点的xy坐标
if (cur.breakPoint && cur.hoz && cur.vertical) {
if (cur.corner && cur.hoz && cur.vertical) {
cur.x = (Math.abs(cur.prev.x - cur.x) <= scale) ? cur.prev.x : cur.next.x;
cur.y = (Math.abs(cur.prev.y - cur.y) <= scale) ? cur.prev.y : cur.next.y;
cur.rightAngle = true;
cur.prev.right = 1;
cur.right = 1;
cur.visited = true;
cur.breakPoint = true;
}
// 判断单独的角点
else if (cur.corner) {
cur.visited = true;
cur.breakPoint = true;
}
// 判断单独的顶角切线点
else if (cur.apex && !cur.prev.apex && !cur.next.apex) {
if (cur.theta < 0.3) {
cur.tangency = true;
}
cur.visited = true;
cur.breakPoint = true;
}
// 判断切线
if(cur.theta < 0.1 && (cur.pxl || cur.pyl || cur.nxl || cur.nyl)) {
// 判断边界点中的切线点
// else if (cur.theta < 0.3 && (cur.ndist > longDist || cur.pdist > longDist)) {
// cur.visited = true;
// cur.tangency = true;
// cur.breakPoint = true;
// }
// 判断边界点中的切线点
else if (cur.apex && (cur.ndist > longDist || cur.pdist > longDist)) {
cur.visited = true;
cur.breakPoint = true;
}
// 直线段
else if (cur.theta > 0.8 && (cur.ndist > longDist || cur.pdist > longDist)) {
cur.visited = true;
cur.breakPoint = true;
}
// 判断成对的顶角切线点
else if (cur.apex && cur.next.apex && cur.prev.theta < 0.4 && cur.next.theta < 0.4) {
// 修正切线点位置
if (cur.xTop && cur.next.xTop || cur.xBottom && cur.next.xBottom) {
var minus = Math.max(Math.floor(Math.abs(cur.next.y - cur.y) / 4), 4 * scale);
cur.y = cur.y > cur.next.y ? cur.y - minus : cur.y + minus;
cur.next.y = cur.next.y > cur.y ? cur.next.y - minus : cur.next.y + minus;
}
if (cur.yTop && cur.next.yTop || cur.yBottom && cur.next.yBottom) {
var minus = Math.max(Math.floor(Math.abs(cur.next.x - cur.x) / 4), 4 * scale);
cur.x = cur.x > cur.next.x ? cur.x - minus : cur.x + minus;
cur.next.x = cur.next.x > cur.x ? cur.next.x - minus : cur.next.x + minus;
}
cur.visited = true;
cur.tangency = true;
}
// 移除非真正的breakPoint
if (cur.breakPoint && !cur.ptiny && !cur.ntiny && (cur.nxs && cur.pxs)) {
delete cur.breakPoint;
}
cur = cur.next;
} while (cur !== start);
var breakPoints = [];
cur = start;
do {
if (cur.rightAngle) {
breakPoints.push(cur);
}
else if (cur.right === 1) {
breakPoints.push(cur);
}
else if (cur.ntiny || cur.rtiny) {
breakPoints.push(cur);
}
else if (cur.apex) {
breakPoints.push(cur);
}
else if (cur.pxl && cur.next.pxs || cur.pyl && cur.next.pys) {
breakPoints.push(cur);
cur.breakPoint = true;
cur.next.visited = true;
cur.next.tangency = true;
cur.next.breakPoint = true;
}
cur = cur.next;
} while (cur !== start);
var breakPoints = contour.filter(function (p) {
return p.breakPoint;
});
breakPoints.sort(function (a, b) {
return a.index - b.index;
});

View File

@ -21,7 +21,7 @@ define(
function fitBezier(points, scale, tHat1, tHat2) {
scale = scale || 1;
var size = points.length;
var maxError = 10 * scale * scale;
var maxError = 4 * scale * scale;
var cubicBezier = fitCurve(points, maxError, tHat1, tHat2);
var start = points[0];

View File

@ -7,7 +7,7 @@
define(
function (require) {
var reducePoints = require('graphics/image/contour/douglasPeuckerReducePoints');
var reducePoints = require('graphics/image/contour/reducePoints');
var findBreakPoints = require('./findBreakPoints');
var fitBezier = require('./fitBezier');
var pathUtil = require('graphics/pathUtil');
@ -49,13 +49,11 @@ define(
curvePoints = reducedData.slice(start.index, end.index + 1);
}
if (curvePoints.length <= 2) {
tHat1Point = end;
continue;
}
if ((start.inflexion || start.tangency) && tHat1Point) {
if (start.tangency && tHat1Point && start !== tHat1Point) {
tHat1 = vector.normalize({
x: start.x - tHat1Point.x,
y: start.y - tHat1Point.y
@ -65,7 +63,7 @@ define(
tHat1 = null;
}
var bezierCurve = fitBezier(curvePoints, scale, tHat1);
var bezierCurve = fitBezier(curvePoints, scale);
if (bezierCurve.length) {
bezierCurve.forEach(function (p) {
if (!isNaN(p.x) && !isNaN(p.y)) {
@ -75,6 +73,9 @@ define(
onCurve: p.onCurve
});
}
else {
console.log('nan');
}
});
end = bezierCurve[bezierCurve.length - 2];
@ -98,19 +99,19 @@ define(
// 去除直线
if (resultContour.length <= 2) {
return null;
return [];
}
// 去除拟合后变成了直线轮廓
else if (
resultContour.length <= 4
&& vector.getDist(resultContour[0], resultContour[1], resultContour[2]) < scale
) {
return null;
return [];
}
return pathUtil.deInterpolate(reducePath(resultContour).map(function (p) {
p.x = Math.floor(p.x);
p.y = Math.floor(p.y);
p.x = Math.round(p.x);
p.y = Math.round(p.y);
return p;
}));

View File

@ -0,0 +1,97 @@
/**
* @file 消减点
* @author mengke01(kekee000@gmail.com)
*/
define(
function (require) {
var douglasPeuckerReducePoints = require('graphics/image/contour/douglasPeuckerReducePoints');
var makeLink = require('graphics/pathUtil').makeLink;
var vector = require('graphics/vector');
var getCos = vector.getCos;
var getDist = vector.getDist;
function dist(p0, p1) {
return Math.sqrt(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2));
}
/**
* 消减非必要的点
*
* @param {Array} contour 轮廓点集
* @param {number} firstIndex 起始索引
* @param {number} lastIndex 结束索引
* @return {Array} 消减后的点集
*/
function reducePoints(contour, firstIndex, lastIndex, scale, tolerance) {
var points = douglasPeuckerReducePoints(contour, firstIndex, lastIndex, scale, tolerance);
points = makeLink(points);
var start = points[0];
var tinyDist = 3 * scale;
var shortDistance = 10 * scale;
var longDistance = 40 * scale;
var rightAngle = Math.PI / 2;
var cur = start;
do {
cur.ndist = cur.next.pdist = dist(cur, cur.next);
cur = cur.next;
} while (cur !== start);
cur = start;
do {
if (cur.visited) {
cur = cur.next;
continue;
}
if (Math.abs(cur.x - cur.next.x) <= tinyDist && Math.abs(cur.y - cur.next.y) <= tinyDist) {
var theta = Math.acos(getCos(cur.x - cur.prev.x, cur.y - cur.prev.y, cur.next.next.x - cur.next.x, cur.next.next.y - cur.next.y)) || 1;
// 小于直角则考虑移除点
if (theta < rightAngle) {
// 顶角
if (
cur.x >= cur.prev.x && cur.x >= cur.next.x
|| cur.x <= cur.prev.x && cur.x <= cur.next.x
|| cur.y >= cur.prev.y && cur.y >= cur.next.y
|| cur.y <= cur.prev.y && cur.y <= cur.next.y
) {
cur.next.deleted = true;
}
else {
cur.deleted = true;
}
}
cur.visited = cur.next.visited = true;
}
// 一个点距离比较近一个非常远
else if (
cur.ndist > longDistance && cur.pdist < shortDistance
|| cur.pdist > longDistance && cur.ndist < shortDistance
&& getCos(cur.prev, cur, cur.next) > 0.9
) {
cur.deleted = true;
cur.visited = cur.next.visited = true;
}
cur = cur.next;
} while (cur !== start);
return points.filter(function (p) {
delete p.visited;
return !p.deleted;
});
}
return reducePoints;
}
);