add test case

This commit is contained in:
kekee000
2015-04-03 22:08:59 +08:00
parent 1f3d2aaa21
commit 2afcdd59b1
7 changed files with 802 additions and 2 deletions

65
demo/findBreakPoints.html Normal file
View File

@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>查找拐点</title>
<script src="../dep/esl.js"></script>
<script src="../dep/jquery.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#points, #points-break {
position: absolute;
left: 0;
top: 0;
width: 800px;
height: 800px;
}
#points i,
#points-break i {
position: absolute;
width: 1px;
height: 1px;
background: #ddd;
}
#points-break i.break {
width: 2px;
height: 2px;
background: red;
}
#points-break i.tangency {
width: 2px;
height: 2px;
background: blue;
}
#points-break i.inflexion {
width: 2px;
height: 2px;
background: green;
}
</style>
</head>
<body>
<div id="points"></div>
<div id="points-break"></div>
<script>
require.config({
baseUrl: '../src',
paths: {
demo: '../demo/js',
}
});
define('jquery', $);
</script>
<script>require(['demo/findBreakPoints'])</script>
</body>
</html>

50
demo/fitImage.html Normal file
View File

@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>canvas拟合图像</title>
<script src="../dep/esl.js"></script>
<script src="../dep/jquery.min.js"></script>
<script src="../dep/clipper.js"></script>
<link rel="stylesheet" type="text/css" href="./css/editor.css">
<style>
body {
margin: 0;
padding: 0;
}
#canvas {
position: absolute;
max-width: 50%;
}
#render-view {
margin-left: 50%;
width: auto;
height: 90%;
}
</style>
</head>
<body>
<div>
<input id="upload-file" type="file">
灰度阈值:<input id="threshold-gray" type="range" min="0" max="255" value="200">
</div>
<canvas id="canvas"></canvas>
<div id="render-view" class="render-view glyf-editor" oncontextMenu="return false"></div>
<script>
require.config({
baseUrl: '../src',
paths: {
demo: '../demo/js',
}
});
define('jquery', window.jQuery);
define('ClipperLib', window.ClipperLib);
</script>
<script>require(['demo/fitImage'])</script>
</body>
</html>

View File

@@ -114,7 +114,7 @@ define(
else if (p.inflexion) {
ctx.fillStyle = 'blue';
}
ctx.fillRect(p.x, p.y, p.right == 1 ? 4 : 2, p.right == 1 ? 4 : 2);
ctx.fillRect(p.x, p.y, p.right == 1 ? 6 : 3, p.right == 1 ? 6 : 3);
});
}
@@ -215,7 +215,7 @@ define(
refresh();
}
img.src = '../test/rw1.jpg';
img.src = '../test/qg.gif';
}
};

View File

@@ -0,0 +1,75 @@
/**
* @file 寻找关键点
* @author mengke01(kekee000@gmail.com)
*/
define(
function (require) {
var fitBezier = require('graphics/image/contour/fitBezier');
var findBreakPoints = require('graphics/image/contour/findBreakPoints');
var pathUtil = require('graphics/pathUtil');
var data = require('demo/../data/image-contours3');
var drawPath = require('render/util/drawPath');
var entry = {
/**
* 初始化
*/
init: function () {
var breakPoints = [];
var html = '';
data.forEach(function(c) {
c.splice(c.length - 1, 1);
});
data.forEach(function (contour) {
contour.forEach(function(p) {
html += '<i style="left:'+p.x+'px;top:'+p.y+'px;"></i>';
});
});
$('#points').html(html);
data.forEach(function (contour) {
contour = pathUtil.scale(contour, 10);
var points = findBreakPoints(contour, 10);
if (points) {
points.forEach(function (p) {
breakPoints.push(p);
});
}
contour = pathUtil.scale(contour, 0.1);
});
breakPoints.forEach(function (p) {
console.log(p.right);
});
html = '';
for (var i = 0, l = breakPoints.length; i < l; i++) {
var c = "break";
if (breakPoints[i].tangency) {
c = 'tangency';
}
else if(breakPoints[i].inflexion) {
c = 'inflexion';
}
var width = '';
if (breakPoints[i].right == 1) {
width = 'width: 4px;height: 4px';
}
html += '<i style="left:'+breakPoints[i].x+'px;top:'+breakPoints[i].y+'px;'+width+'" class="'+c+'"></i>';
}
$('#points-break').html(html);
}
};
entry.init();
return entry;
}
);

139
demo/js/fitImage.js Normal file
View File

@@ -0,0 +1,139 @@
/**
* @file canvas读取图片
* @author mengke01(kekee000@gmail.com)
*/
define(
function (require) {
var lang = require('common/lang');
var ImageProcessor = require('graphics/image/ImageProcessor');
var ContourPointsProcessor = require('graphics/image/ContourPointsProcessor');
var pathsUtil = require('graphics/pathsUtil');
var editor = require('editor/main');
var ctx = null;
var canvas = null;
var curImage = null;
function onUpFileChange(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var image = curImage = new Image();
image.onload = function () {
getContours(image);
};
image.src = e.target.result;
}
reader.onerror = function(e) {
console.error(e);
};
reader.readAsDataURL(file);
}
function getContours(image) {
ctx.clearRect(0,0, canvas.width, canvas.height);
var width = image.width;
var height = image.height;
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, width, height);
var imgData = ctx.getImageData(0, 0, width, height);
var result = new ImageProcessor(imgData).binarize(+$('#threshold-gray').val()).get();
var putData = imgData.data;
for (var y = 0; y < height; y ++) {
var line = width * y;
for (var x = 0; x < width; x++) {
var offset = line + x;
if (result.data[offset] === 0) {
putData[offset * 4] = 208;
putData[offset * 4 + 1] = 247;
putData[offset * 4 + 2] = 113;
putData[offset * 4 + 3] = 255;
}
else {
putData[offset * 4] = 255;
putData[offset * 4 + 1] = 255;
putData[offset * 4 + 2] = 255;
putData[offset * 4 + 3] = 255;
}
}
}
contourProcessor = new ContourPointsProcessor();
contourProcessor.import(result);
var contours = contourProcessor.get();
contours.forEach(function (contour) {
contour.forEach(function (p) {
var offset = p.y * width + p.x;
putData[offset * 4] = 255;
putData[offset * 4 + 1] = 0;
putData[offset * 4 + 2] = 0;
putData[offset * 4 + 3] = 255;
});
});
ctx.putImageData(imgData, 0, 0);
getBreakPoint();
var contours = contourProcessor.getContours();
window.editor.setFont({
contours: pathsUtil.flip(contours)
});
}
function getBreakPoint() {
var breakPoints = contourProcessor.getBreakPoints();
breakPoints.forEach(function (p) {
ctx.beginPath();
if (p.breakPoint) {
ctx.fillStyle = 'red';
}
else if (p.inflexion) {
ctx.fillStyle = 'blue';
}
ctx.fillRect(p.x, p.y, p.right == 1 ? 6 : 3, p.right == 1 ? 6 : 3);
});
}
function refresh() {
curImage && getContours(curImage);
}
var entry = {
/**
* 初始化
*/
init: function () {
window.editor = editor.create($('#render-view').get(0));
document.getElementById('upload-file').addEventListener('change', onUpFileChange);
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
$('#threshold-gray').on('change', refresh);
var img = new Image();
img.onload = function () {
curImage = img;
refresh();
};
img.src = '../test/qg.gif';
}
};
entry.init();
return entry;
}
);

471
test/findBreakPoints.js Normal file
View File

@@ -0,0 +1,471 @@
/**
* @file 查找轮廓中的关键点
* @author mengke01(kekee000@gmail.com)
*
* 查找方法为此处文章的方法
* http://wenku.baidu.com/link?url=Sz1BbYjfA7IeHAMVJ1TPlXiMhACc6sWhHliPJqAUxxtC5K9PYf4j3ile6patUEUMOE_9uJdsS1EGJBFLVbhXcn7Qe8l_FxuT3QVI8nm1aZG
*
* 关键点标记定义:
*
* p.left 左侧类型
* p.right: 右侧类型
* 直线为1,
* 曲线为2
*/
define(
function (require) {
var makeLink = require('graphics/pathUtil').makeLink;
var getCos = require('graphics/vector').getCos;
var getDist = require('graphics/vector').getDist;
var THETA_TANGENCY = 0.1; // 相切抑制
var THETA_CORNER = 0.5; // 拐点抑制
var THETA_MARK_COUNT = 0.8; // 标记点个数抑制
var THETA_INFLEXION_RANGE = 0.05; // 切线点之间的距离抑制
function isSegmentLine(contour, start, end, isLast) {
var contourSize = contour.length;
var mid;
var thetaDistance = 10; // 判断直线点距离
// 判断中间点距离
if (isLast) {
mid = contour[Math.floor(start.index + (contourSize - start.index + end.index) / 2) % contourSize];
}
else {
mid = contour[Math.floor((start.index + end.index) / 2)];
}
if (getDist(start, end, mid) > thetaDistance) {
return false;
}
// 随机选取 几个点进行直线判断
var startIndex = start.index;
var endIndex = end.index;
if (isLast) {
startIndex = start.index;
endIndex = contourSize + end.index;
}
// 距离比较长的话可以适当放大
if (endIndex - startIndex > 100) {
thetaDistance = 20;
}
var step = Math.floor(Math.max((endIndex - startIndex) / 10, 4));
var lineFlag = true;
for (var j = startIndex + step; j < endIndex; j += step) {
var dist = getDist(start, end, contour[j % contourSize]);
if (dist > thetaDistance) {
lineFlag = false;
break;
}
}
return lineFlag;
}
/**
* 计算当前点的theta值
*
* @param {Object} cur 当前点
* @param {Object} leftR 左侧平均
* @param {Object} rightR 右侧平均
* @return {number} theta值
*/
function computeTheta(p, leftR, rightR) {
var leftX = 0;
var leftY = 0;
var t = 0;
var cur = p;
while (t++ < leftR) {
cur = cur.prev;
leftX += cur.x;
leftY += cur.y;
}
leftX = leftX / leftR;
leftY = leftY / leftR;
// 右侧分量
var rightX = 0;
var rightY = 0;
t = 0;
cur = p;
while (t++ < rightR) {
cur = cur.next;
rightX += cur.x;
rightY += cur.y;
}
rightX = rightX / rightR;
rightY = rightY / rightR;
leftX = p.x - leftX;
leftY = p.y - leftY;
rightX = rightX - p.x;
rightY = rightY - p.y;
var theta = Math.acos(getCos(leftX, leftY, rightX, rightY));
var m = leftX * rightY - rightX * leftY;
if (m > 0) {
return theta;
}
else if (m < 0) {
return -theta;
}
return 0;
}
/**
* 标记点的左右已经被访问过了
*
* @param {Object} p 点
* @param {number} rl 左半径
* @param {number} rr 右半径
*/
function markVisited(p, rl, rr) {
var j;
var left = p;
var right = p;
p.visited = true;
if (!rr || rr === rl) {
j = 0;
while (j++ < rl) {
left = left.prev;
right = right.next;
left.visited = right.visited = true;
}
}
else {
j = 0;
while (j++ < rl) {
left = left.prev;
left.visited = true;
}
j = 0;
while (j++ < rr) {
right = right.next;
right.visited = true;
}
}
}
/**
* 查找和标记直线点
*
* @param {Array} contour 轮廓
* @param {Array} breakPoints 关键点
* @param {number} r 查找范围
* @return {Array} 关键点
*/
function findLinePoints(contour, breakPoints, r) {
// 根据角点查找竖直和水平线
var linePoints = [];
var contourSize = contour.length;
for (var i = 0, l = breakPoints.length; i < l; i++) {
var isLast = i === l - 1;
var p = breakPoints[i];
var next = isLast ? breakPoints[0] : breakPoints[i + 1];
if (p.right === 1) {
continue;
}
var range = isLast ? contourSize - p.index + next.index : next.index - p.index;
if (range < r) {
p.right = 1;
next.left = 1;
}
else if (isSegmentLine(contour, p, next, isLast)){
p.right = 1;
next.left = 1;
}
}
return breakPoints;
}
/**
* 查找竖直和水平直线点
*
* @param {Array} contour 轮廓
* @param {Array} breakPoints 关键点
* @param {number} r 查找范围
* @return {Array} 关键点
*/
function findVerticalAndHorizontalPoints(contour, breakPoints, r) {
var contourSize = contour.length;
for (var i = 0, l = contourSize; i < l; i++) {
var p = contour[i];
var cur;
var count;
// 查找竖直直线点
if (p.x === p.next.x && p.x === p.next.next.x) {
cur = p.next.next;
count = 2;
while (cur.x === cur.next.x) {
cur = cur.next;
count++;
}
if (count > 40) {
cur = cur.prev;
//p.right = 1;
if (-1 == breakPoints.indexOf(p)) {
breakPoints.push(p);
}
markVisited(p, r, count + r);
// 判断是否角点
p.theta = computeTheta(p, r, 1);
p.absTheta = Math.abs(p.theta);
if (p.absTheta > THETA_CORNER) {
p.breakPoint = true;
}
else if (p.absTheta <= THETA_TANGENCY) {
p.tangency = true;
}
if (count > 80) {
//cur.left = 1;
if (-1 == breakPoints.indexOf(cur)) {
breakPoints.push(cur);
}
cur.theta = computeTheta(cur, 1, r);
cur.absTheta = Math.abs(cur.theta);
if (cur.absTheta > THETA_CORNER) {
cur.breakPoint = true;
}
else if (cur.absTheta < THETA_TANGENCY) {
cur.tangency = true;
}
}
i += count;
}
}
else if (p.y === p.next.y && p.y === p.next.next.y) {
cur = p.next.next;
count = 2;
while (cur.y === cur.next.y) {
cur = cur.next;
count++;
}
if (count > 40) {
cur = cur.prev;
//p.right = 1;
if (-1 == breakPoints.indexOf(p)) {
breakPoints.push(p);
}
//cur.left = 1;
if (-1 == breakPoints.indexOf(cur)) {
breakPoints.push(cur);
}
markVisited(p, r, count + r);
// 判断是否角点
p.theta = computeTheta(p, r, 1);
p.absTheta = Math.abs(p.theta);
if (p.absTheta > THETA_CORNER) {
p.breakPoint = true;
}
else if (p.absTheta > THETA_TANGENCY) {
p.tangency = true;
}
cur.theta = computeTheta(cur, 1, r);
cur.absTheta = Math.abs(cur.theta);
if (cur.absTheta > THETA_CORNER) {
cur.breakPoint = true;
}
else if (cur.absTheta < THETA_TANGENCY) {
cur.tangency = true;
}
i += count;
}
}
}
breakPoints.sort(function (a, b) {
return a.index - b.index;
});
return breakPoints;
}
/**
* 查找和标记拐点
*
* @param {Array} contour 轮廓
* @param {Array} breakPoints 关键点
* @param {number} r 查找范围
* @return {Array} 关键点
*/
function findBreakPoints(contour, breakPoints, r) {
var j;
var left;
var right;
var max;
contour.forEach(function (p) {
if (p.mark && !p.visited) {
j = 0;
left = p;
right = p;
max = p.absTheta;
while (j++ < r) {
left = left.prev;
right = right.next;
if (left.absTheta > max) {
max = left.absTheta;
}
if (right.absTheta > max) {
max = right.absTheta;
}
}
if (max === p.absTheta) {
p.breakPoint = true;
breakPoints.push(p);
markVisited(p, r);
}
}
});
return breakPoints;
}
function fixStartPoint(contour) {
var i;
var l;
var minY = contour[0].y;
var firstLine = [0];
for (i = 1, l = contour.length; i < l; i++) {
if (contour[i].y < minY) {
minY = contour[i].y;
firstLine = [i];
}
else if (contour[i].y === minY) {
firstLine.push(i);
}
}
var index = firstLine[0];
var minX = contour[index].x;
for (i = 1, l = firstLine.length; i < l; i++) {
if (contour[firstLine[i]].x < minX) {
minX = contour[firstLine[i]].x;
index = firstLine[i];
}
}
if (index !== 0) {
return contour.slice(index).concat(contour.slice(0, index));
}
return contour;
}
/**
* 查找轮廓中的关键点
*
* @param {Array} contour 轮廓点集合
* @return {Array} 轮廓点集合
*/
function getBreakPoints(contour) {
//contour = fixStartPoint(contour);
contour = makeLink(contour);
var r = contour.length > 16 ? 10 : 4;
var contourSize = contour.length;
var breakPoints = [];
if (contour.length > 60) {
breakPoints = findVerticalAndHorizontalPoints(contour, breakPoints, r);
}
for (var i = 0, l = contourSize; i < l; i++) {
p = contour[i];
if (!p.visited) {
p.theta = computeTheta(p, r, r);
p.absTheta = Math.abs(p.theta);
if (p.absTheta > THETA_CORNER) {
p.mark = true;
}
else if (p.absTheta > THETA_TANGENCY) {
p.markTangency = true;
}
}
}
// 非极大抑制查找角点
breakPoints = findBreakPoints(contour, breakPoints, r);
// 没有找到拐点,说明拐点不明显,或者为连续曲线,这里采用4等分
if (breakPoints.length < 2) {
if (contourSize < 100) {
var step = Math.floor(contourSize / 2) + 1;
}
else {
var step = Math.floor(contourSize / 4) + 1;
}
var startIndex = breakPoints[0] ? breakPoints[0].index : 0;
for (var i = 0; i < contourSize; i += step) {
breakPoints.push(contour[(startIndex + i) % contourSize]);
}
}
breakPoints.sort(function (a, b) {
return a.index - b.index;
});
return breakPoints;
}
return getBreakPoints;
}
);

BIN
test/qg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB