add examples
Browse files- .gitattributes +2 -0
- js/poseTweek.js +0 -862
- main.py +9 -4
- static/sample.png +0 -0
- static/sample2.png +0 -0
- static/sample3.png +0 -0
.gitattributes
CHANGED
|
@@ -32,3 +32,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 32 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 32 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth filter=lfs diff=lfs merge=lfs -text
|
js/poseTweek.js
DELETED
|
@@ -1,862 +0,0 @@
|
|
| 1 |
-
var canvas = null;
|
| 2 |
-
var ctx = null;
|
| 3 |
-
var offscreen = null;
|
| 4 |
-
var initialPoseField = null;
|
| 5 |
-
var canvasBg = null;
|
| 6 |
-
var debugLines = [];
|
| 7 |
-
|
| 8 |
-
const wheelDisplayTime = 500;
|
| 9 |
-
|
| 10 |
-
const colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0],
|
| 11 |
-
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255],
|
| 12 |
-
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]];
|
| 13 |
-
|
| 14 |
-
function cutOffLimb(pose, cutOffIndex) {
|
| 15 |
-
console.log(`cutOffLimb: ${cutOffIndex}`);
|
| 16 |
-
// 末端ノードの座標を削除する
|
| 17 |
-
var newPose = deepCopy(pose);
|
| 18 |
-
for (let i = 0; i < 18; i++) {
|
| 19 |
-
if (newPose[i] == null) {continue;}
|
| 20 |
-
// ルートまで検索し、その間にcuttOffIndexがあれば削除
|
| 21 |
-
var curr = i;
|
| 22 |
-
while (curr !== 1) {
|
| 23 |
-
console.log(`checking: ${i} -> ${curr}`);
|
| 24 |
-
let parent = findParentNodeIndex(curr);
|
| 25 |
-
if (parent === cutOffIndex) {
|
| 26 |
-
console.log(`cutOffLimb: ${i} -> ${cutOffIndex}`);
|
| 27 |
-
newPose[i] = null;
|
| 28 |
-
break;
|
| 29 |
-
}
|
| 30 |
-
curr = parent;
|
| 31 |
-
}
|
| 32 |
-
}
|
| 33 |
-
return newPose;
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
function repairPose(sourcePose) {
|
| 37 |
-
// TODO: ループには対応してないかも
|
| 38 |
-
var pose = sourcePose;
|
| 39 |
-
var newPose = new Array(18)
|
| 40 |
-
for (var k = 0; k < 3; k++) {
|
| 41 |
-
var processed = 0; // イテレーション用
|
| 42 |
-
for (let i = 0; i < 18; i++) {
|
| 43 |
-
if (pose[i] == null) {
|
| 44 |
-
let parent = findParentNodeIndex(i);
|
| 45 |
-
if (parent === -1) {continue;} // あり得ない
|
| 46 |
-
if (pose[parent] == null) {
|
| 47 |
-
console.log(`repair failed(A): ${i} -> parent loss`);
|
| 48 |
-
continue;
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
// サンプルデータから引っ張ってくる
|
| 52 |
-
var v = sampleCandidateSource[i].map((x, j) => x - sampleCandidateSource[parent][j]);
|
| 53 |
-
newPose[i] = pose[parent].map((x, j) => x + v[j]);
|
| 54 |
-
console.log(`repaired: ${i} -> ${newPose[newPose.length - 1]}`);
|
| 55 |
-
processed++;
|
| 56 |
-
} else {
|
| 57 |
-
newPose[i] = pose[i].map(x => x);
|
| 58 |
-
}
|
| 59 |
-
}
|
| 60 |
-
if (processed === 0) {break;}
|
| 61 |
-
pose = newPose;
|
| 62 |
-
}
|
| 63 |
-
return newPose;
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
function deepCopy(arr) {
|
| 67 |
-
return JSON.parse(JSON.stringify(arr));
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
function distSq(p0, p1) {
|
| 71 |
-
return (p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2;
|
| 72 |
-
}
|
| 73 |
-
|
| 74 |
-
// poseDataの形式:[[[x1, y1], [x2, y2], ...],[[x3, y3], [x4, y4], ...], ...]
|
| 75 |
-
// 各要素が人間
|
| 76 |
-
// 人間の各要素が関節
|
| 77 |
-
|
| 78 |
-
function poseDataToCandidateAndSubset(poseData) {
|
| 79 |
-
let candidate = [];
|
| 80 |
-
let subset = [];
|
| 81 |
-
for (let i = 0; i < poseData.length; i++) {
|
| 82 |
-
let person = poseData[i];
|
| 83 |
-
let subsetElement = [];
|
| 84 |
-
for (let j = 0; j < person.length; j++) {
|
| 85 |
-
candidate.push(person[j]);
|
| 86 |
-
subsetElement.push(candidate.length - 1);
|
| 87 |
-
}
|
| 88 |
-
subset.push(subsetElement);
|
| 89 |
-
}
|
| 90 |
-
return [candidate, subset];
|
| 91 |
-
}
|
| 92 |
-
|
| 93 |
-
// サンプルデータ
|
| 94 |
-
const sampleOffset = [0, -70];
|
| 95 |
-
const sampleCandidateSource = [[235, 158],[234, 220],[193, 222],[138, 263],[89, 308],[276, 220],[325, 264],[375, 309],[207, 347],[203, 433],[199, 523],[261, 347],[262, 430],[261, 522],[227, 148],[245, 148],[208, 158],[258, 154]].map((p) => [p[0] + sampleOffset[0], p[1] + sampleOffset[1]]);
|
| 96 |
-
const sampleSubsetElementSource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
|
| 97 |
-
|
| 98 |
-
// const sampleCandidateSource = [[618.00, 0.00], [618.00, 44.00], [304.00, 81.00], [482.00, 96.00], [66.00, 270.00], [171.00, 280.00], [618.00, 82.00], [307.00, 112.00], [460.00, 143.00], [0.00, 301.00], [65.00, 301.00], [172.00, 303.00], [584.00, 86.00], [275.00, 119.00], [420.00, 139.00], [0.00, 301.00], [41.00, 301.00], [144.00, 303.00], [544.00, 131.00], [348.00, 139.00], [262.00, 160.00], [0.00, 337.00], [52.00, 339.00], [130.00, 348.00], [570.00, 175.00], [283.00, 177.00], [78.00, 338.00], [172.00, 380.00], [651.00, 78.00], [338.00, 111.00], [505.00, 144.00], [92.00, 301.00], [198.00, 305.00], [661.00, 132.00], [349.00, 156.00], [541.00, 179.00], [106.00, 336.00], [203.00, 348.00], [305.00, 159.00], [665.00, 160.00], [563.00, 192.00], [80.00, 343.00], [181.00, 385.00], [614.00, 205.00], [291.00, 220.00], [432.00, 320.00], [152.00, 372.00], [43.00, 380.00], [0.00, 386.00], [623.00, 281.00], [306.00, 290.00], [92.00, 357.00], [509.00, 434.00], [304.00, 357.00], [622.00, 368.00], [47.00, 394.00], [0.00, 395.00], [142.00, 405.00], [535.00, 565.00], [655.00, 200.00], [337.00, 217.00], [467.00, 322.00], [191.00, 372.00], [83.00, 375.00], [344.00, 282.00], [655.00, 282.00], [103.00, 343.00], [237.00, 368.00], [22.00, 377.00], [0.00, 379.00], [460.00, 459.00], [305.00, 352.00], [638.00, 355.00], [0.00, 401.00], [110.00, 412.00], [411.00, 570.00], [608.00, 0.00], [608.00, 40.00], [297.00, 75.00], [469.00, 84.00], [0.00, 261.00], [58.00, 263.00], [165.00, 275.00], [625.00, 0.00], [625.00, 39.00], [309.00, 74.00], [486.00, 83.00], [71.00, 264.00], [180.00, 276.00], [599.00, 0.00], [599.00, 44.00], [284.00, 80.00], [440.00, 93.00], [48.00, 271.00], [0.00, 272.00], [157.00, 277.00], [634.00, 0.00], [633.00, 41.00], [319.00, 77.00], [79.00, 269.00], [190.00, 277.00]];
|
| 99 |
-
// const sampleSubsetElementSource = [1.00,6.00,12.00,18.00,24.00,28.00,33.00,39.00,43.00,49.00,54.00,59.00,65.00,72.00,77.00,84.00,90.00,97.00,32.98,18.00],[5.00,11.00,17.00,23.00,27.00,32.00,37.00,42.00,46.00,-1.00,-1.00,62.00,67.00,-1.00,82.00,88.00,95.00,100.00,25.45,15.00],[4.00,10.00,16.00,22.00,26.00,31.00,36.00,41.00,47.00,51.00,57.00,63.00,66.00,74.00,81.00,87.00,93.00,99.00,26.97,18.00],[3.00,8.00,14.00,19.00,25.00,30.00,35.00,40.00,45.00,52.00,58.00,61.00,70.00,75.00,79.00,86.00,92.00,-1.00,30.45,17.00],[2.00,7.00,13.00,20.00,-1.00,29.00,34.00,38.00,44.00,50.00,53.00,60.00,64.00,71.00,78.00,85.00,91.00,98.00,27.89,17.00],[0.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,76.00,83.00,-1.00,96.00,3.33,4.00];
|
| 100 |
-
|
| 101 |
-
function makePoseFromCandidateAndSubsetElement(candidate, subsetElement) {
|
| 102 |
-
var pose = [];
|
| 103 |
-
for (let j = 0 ; j < 18; j++) {
|
| 104 |
-
let i = subsetElement[j];
|
| 105 |
-
pose.push(i < 0 || candidate[i] == null ? null : candidate[i].map((x)=>x));
|
| 106 |
-
}
|
| 107 |
-
return pose;
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
function makePoseDataFromCandidateAndSubset(candidate, subset) {
|
| 111 |
-
return subset.map(subsetElement => makePoseFromCandidateAndSubsetElement(candidate, subsetElement));
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
-
function addPerson() {
|
| 115 |
-
var dx = Math.random() * 100;
|
| 116 |
-
var dy = Math.random() * 100;
|
| 117 |
-
|
| 118 |
-
poseData.push(
|
| 119 |
-
makePoseFromCandidateAndSubsetElement(
|
| 120 |
-
sampleCandidateSource.map(point => [point[0] + dx, point[1] + dy]),
|
| 121 |
-
sampleSubsetElementSource));
|
| 122 |
-
|
| 123 |
-
addHistory();
|
| 124 |
-
Redraw();
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
function removePerson(personIndex) {
|
| 128 |
-
poseData.splice(personIndex, 1);
|
| 129 |
-
addHistory();
|
| 130 |
-
Redraw();
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
function repairPerson(personIndex) {
|
| 134 |
-
poseData[personIndex] = repairPose(poseData[personIndex]);
|
| 135 |
-
addHistory();
|
| 136 |
-
Redraw();
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
function cutOffPersonLimb(personIndex, limbIndex) {
|
| 140 |
-
poseData[personIndex] = cutOffLimb(poseData[personIndex], limbIndex);
|
| 141 |
-
console.log(poseData[personIndex]);
|
| 142 |
-
console.log(poseData);
|
| 143 |
-
addHistory();
|
| 144 |
-
Redraw();
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
// ドラッグ中の各キーが押されているかどうかのフラグ
|
| 148 |
-
var keyDownFlags = {};
|
| 149 |
-
// マウスカーソル
|
| 150 |
-
var mouseCursor = [-1, -1];
|
| 151 |
-
|
| 152 |
-
function cross(lhs, rhs) {return lhs[0] * rhs[1] - lhs[1] * rhs[0];}
|
| 153 |
-
function dot(lhs, rhs) {return lhs[0] * rhs[0] + lhs[1] * rhs[1];}
|
| 154 |
-
function directedAngleTo(lhs, rhs) {return Math.atan2(cross(lhs, rhs), dot(lhs, rhs));}
|
| 155 |
-
|
| 156 |
-
function isMouseOnCanvas() {
|
| 157 |
-
// mouseCursorがcanvasの範囲内にあるかどうかを判定
|
| 158 |
-
var rect = canvas.getBoundingClientRect();
|
| 159 |
-
var f = 0 <= mouseCursor[0] && mouseCursor[0] <= rect.width && 0 <= mouseCursor[1] && mouseCursor[1] <= rect.height;
|
| 160 |
-
return f;
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
function clearCanvas() {
|
| 164 |
-
var w = canvas.width;
|
| 165 |
-
var h = canvas.height;
|
| 166 |
-
ctx.fillStyle = 'black';
|
| 167 |
-
ctx.fillRect(0, 0, w, h);
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
function drawBackground() {
|
| 171 |
-
if (canvasBg != null) {
|
| 172 |
-
ctx.drawImage(canvasBg, 0, 0);
|
| 173 |
-
}
|
| 174 |
-
}
|
| 175 |
-
|
| 176 |
-
function resizeCanvas(width, height) {
|
| 177 |
-
canvas.width = width ? width : canvas.width;
|
| 178 |
-
canvas.height = height ? height : canvas.height;
|
| 179 |
-
Redraw();
|
| 180 |
-
}
|
| 181 |
-
|
| 182 |
-
function calculateCenter(shape) {
|
| 183 |
-
var center = shape.reduce(function(acc, point) {
|
| 184 |
-
if (point === null) {
|
| 185 |
-
acc[0] += point[0];
|
| 186 |
-
acc[1] += point[1];
|
| 187 |
-
}
|
| 188 |
-
return acc;
|
| 189 |
-
}, [0, 0]);
|
| 190 |
-
center[0] /= shape.length;
|
| 191 |
-
center[1] /= shape.length;
|
| 192 |
-
return center;
|
| 193 |
-
}
|
| 194 |
-
|
| 195 |
-
// v2d -> v3d
|
| 196 |
-
function rotateX(vector, angle) {
|
| 197 |
-
var x = vector[0];
|
| 198 |
-
var y = vector[1];
|
| 199 |
-
var z = 0;
|
| 200 |
-
|
| 201 |
-
// X軸に対して回転する
|
| 202 |
-
var x1 = x;
|
| 203 |
-
var y1 = y * Math.cos(angle) - z * Math.sin(angle);
|
| 204 |
-
var z1 = y * Math.sin(angle) + z * Math.cos(angle);
|
| 205 |
-
|
| 206 |
-
return [x1, y1, z1];
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
// v2d -> v3d
|
| 210 |
-
function rotateY(vector, angle) {
|
| 211 |
-
var x = vector[0];
|
| 212 |
-
var y = vector[1];
|
| 213 |
-
var z = 0;
|
| 214 |
-
|
| 215 |
-
// Y軸に対して回転する
|
| 216 |
-
var x1 = x * Math.cos(angle) + z * Math.sin(angle);
|
| 217 |
-
var y1 = y;
|
| 218 |
-
var z1 = -x * Math.sin(angle) + z * Math.cos(angle);
|
| 219 |
-
|
| 220 |
-
return [x1, y1, z1];
|
| 221 |
-
}
|
| 222 |
-
|
| 223 |
-
// v3d -> v2d
|
| 224 |
-
function perspectiveProjection(vector, cameraDistance) {
|
| 225 |
-
var x = vector[0];
|
| 226 |
-
var y = vector[1];
|
| 227 |
-
var z = vector[2];
|
| 228 |
-
|
| 229 |
-
if (z === 0) {
|
| 230 |
-
return [x, y];
|
| 231 |
-
}
|
| 232 |
-
|
| 233 |
-
var scale = cameraDistance / (cameraDistance - z);
|
| 234 |
-
var x1 = x * scale;
|
| 235 |
-
var y1 = y * scale;
|
| 236 |
-
|
| 237 |
-
return [x1, y1];
|
| 238 |
-
}
|
| 239 |
-
|
| 240 |
-
// v2d -> v3d
|
| 241 |
-
function rotateAndProject(f, p, c, angle) {
|
| 242 |
-
var v = [p[0] - c[0], p[1] - c[1]];
|
| 243 |
-
var v1 = f(v, angle);
|
| 244 |
-
var v2 = perspectiveProjection(v1, 500);
|
| 245 |
-
return [v2[0] + c[0], v2[1] + c[1]];
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
function drawPicture() {
|
| 249 |
-
ctx.globalAlpha = 1.0;
|
| 250 |
-
// if (picture != null) {
|
| 251 |
-
//var [w, h] = [canvas.width, canvas.height];
|
| 252 |
-
//var [pw, ph] = [picture.width, picture.height];
|
| 253 |
-
//var [x, y] = [(w-pw)/2, (h-ph)/2];
|
| 254 |
-
// ctx.drawImage(picture, sampleOffset[0], sampleOffset[1]);
|
| 255 |
-
// ctx.drawImage(picture, 0, 0);
|
| 256 |
-
// }
|
| 257 |
-
|
| 258 |
-
ctx.globalAlpha = 1.0;
|
| 259 |
-
if (offscreen != null) {
|
| 260 |
-
ctx.drawImage(offscreen, 0, 0);
|
| 261 |
-
}
|
| 262 |
-
}
|
| 263 |
-
|
| 264 |
-
function drawBodyPose() {
|
| 265 |
-
drawBodyPoseTo(ctx, poseData);
|
| 266 |
-
}
|
| 267 |
-
|
| 268 |
-
function drawBodyPoseTo(ctx, poseData) {
|
| 269 |
-
let stickWidth = 4;
|
| 270 |
-
let imageSize = Math.min(canvas.width, canvas.height);
|
| 271 |
-
stickWidth *= imageSize / 512;
|
| 272 |
-
|
| 273 |
-
ctx.globalAlpha = 0.6;
|
| 274 |
-
|
| 275 |
-
// edge
|
| 276 |
-
for (let i = 0; i < poseData.length; i++) {
|
| 277 |
-
const pose = poseData[i];
|
| 278 |
-
|
| 279 |
-
for (let j = 0; j < 17; j++) {
|
| 280 |
-
const p = pose[limbSeq[j][0]];
|
| 281 |
-
const q = pose[limbSeq[j][1]];
|
| 282 |
-
if (p == null || q == null) continue;
|
| 283 |
-
const [X0, Y0] = p;
|
| 284 |
-
const [X1, Y1] = q;
|
| 285 |
-
let angle = Math.atan2(Y1 - Y0, X1 - X0);
|
| 286 |
-
let magnitude = ((X0 - X1) ** 2 + (Y0 - Y1) ** 2) ** 0.5
|
| 287 |
-
let polygon = new Path2D();
|
| 288 |
-
polygon.ellipse((X0+X1)/2, (Y0+Y1)/2, magnitude / 2, stickWidth, angle, 0, 2 * Math.PI);
|
| 289 |
-
//ctx.fillStyle = `rgb(${colors[j].join(',')})`;
|
| 290 |
-
ctx.fillStyle = 'gray';
|
| 291 |
-
ctx.fill(polygon);
|
| 292 |
-
}
|
| 293 |
-
}
|
| 294 |
-
|
| 295 |
-
ctx.globalAlpha = 1.0;
|
| 296 |
-
|
| 297 |
-
// node
|
| 298 |
-
for (let i = 0; i < poseData.length; i++) {
|
| 299 |
-
const pose = poseData[i];
|
| 300 |
-
|
| 301 |
-
ctx.font = '12px serif';
|
| 302 |
-
for (let j = 0; j < 18; j++) {
|
| 303 |
-
const p = pose[j];
|
| 304 |
-
if (p == null) continue;
|
| 305 |
-
const [x, y] = p;
|
| 306 |
-
ctx.beginPath();
|
| 307 |
-
ctx.arc(x, y, stickWidth, 0, 2 * Math.PI);
|
| 308 |
-
ctx.fillStyle = `rgb(${colors[j].join(',')})`;
|
| 309 |
-
ctx.fill();
|
| 310 |
-
// ctx.fillStyle = 'rgb(255,255,255)'
|
| 311 |
-
// ctx.fillText(j, x-3, y+4);
|
| 312 |
-
}
|
| 313 |
-
}
|
| 314 |
-
}
|
| 315 |
-
|
| 316 |
-
let lastWheeling = 0;
|
| 317 |
-
|
| 318 |
-
function drawUI() {
|
| 319 |
-
if (keyDownFlags['Space'] || keyDownFlags['BracketLeft'] || keyDownFlags['BracketRight'] ||
|
| 320 |
-
new Date().getTime() - lastWheeling < wheelDisplayTime) {
|
| 321 |
-
ctx.beginPath();
|
| 322 |
-
ctx.lineWidth=4;
|
| 323 |
-
ctx.arc(mouseCursor[0], mouseCursor[1], dragRange, 0, 2 * Math.PI);
|
| 324 |
-
ctx.strokeStyle = 'rgb(255,255,255)';
|
| 325 |
-
ctx.stroke();
|
| 326 |
-
}
|
| 327 |
-
|
| 328 |
-
if (isDragging && (dragMode == "rotate" || dragMode == "rotate2")) {
|
| 329 |
-
ctx.beginPath();
|
| 330 |
-
ctx.lineWidth=1;
|
| 331 |
-
ctx.strokeStyle = 'rgb(255,255,255)';
|
| 332 |
-
ctx.moveTo(dragStart[0], dragStart[1]);
|
| 333 |
-
ctx.lineTo(dragStart[0]+rotateBaseVector[0], dragStart[1]+rotateBaseVector[1]);
|
| 334 |
-
ctx.stroke();
|
| 335 |
-
}
|
| 336 |
-
|
| 337 |
-
ctx.globalAlpha = 0.1;
|
| 338 |
-
|
| 339 |
-
for (let i = 0 ; i < debugLines.length ; i++) {
|
| 340 |
-
const p = debugLines[i];
|
| 341 |
-
const l = 50;
|
| 342 |
-
ctx.beginPath();
|
| 343 |
-
ctx.lineWidth= 2;
|
| 344 |
-
ctx.strokeStyle = `rgb(${colors[i].join(',')})`;
|
| 345 |
-
ctx.moveTo(p[0][0], p[0][1]);
|
| 346 |
-
ctx.lineTo(p[0][0]+p[1][0] * l, p[0][1]+p[1][1] * l);
|
| 347 |
-
ctx.stroke();
|
| 348 |
-
}
|
| 349 |
-
ctx.globalAlpha = 1.0;
|
| 350 |
-
|
| 351 |
-
let operationTextFlags = {
|
| 352 |
-
"Space": "Range Move",
|
| 353 |
-
"AltLeft": "Body Move",
|
| 354 |
-
"AltRight": "Body Move",
|
| 355 |
-
"ControlLeft": "Scale",
|
| 356 |
-
"ControlRight": "Scale",
|
| 357 |
-
"ShiftLeft": "Rotate",
|
| 358 |
-
"ShiftRight": "Rotate",
|
| 359 |
-
"KeyQ": "CutOff",
|
| 360 |
-
"KeyD": "Delete",
|
| 361 |
-
"KeyX": "X-Axis",
|
| 362 |
-
"KeyC": "Y-Axis",
|
| 363 |
-
"KeyR": "Repair",
|
| 364 |
-
}
|
| 365 |
-
|
| 366 |
-
// operationTextFlagsに含まれるものがkeyDownFlagsに含まれるばあい、そのキーの文字列を取得
|
| 367 |
-
let activeOperations = Object.keys(operationTextFlags).filter(key => keyDownFlags[key]);
|
| 368 |
-
if (activeOperations.length > 0) {
|
| 369 |
-
// 左上に表示
|
| 370 |
-
ctx.font = '20px serif';
|
| 371 |
-
ctx.fillStyle = 'rgb(255,255,255)';
|
| 372 |
-
ctx.fillText(operationTextFlags[activeOperations[0]], 10, 30);
|
| 373 |
-
}
|
| 374 |
-
|
| 375 |
-
// draw mouse cursor position at right bottom
|
| 376 |
-
ctx.font = '20px serif';
|
| 377 |
-
ctx.fillStyle = 'rgb(255,255,255)';
|
| 378 |
-
ctx.fillText(`(${mouseCursor[0]}, ${mouseCursor[1]})`, canvas.width-200, canvas.height-10);
|
| 379 |
-
|
| 380 |
-
}
|
| 381 |
-
|
| 382 |
-
function Redraw() {
|
| 383 |
-
clearCanvas();
|
| 384 |
-
drawBackground();
|
| 385 |
-
drawPicture();
|
| 386 |
-
drawBodyPose();
|
| 387 |
-
drawUI();
|
| 388 |
-
}
|
| 389 |
-
|
| 390 |
-
function getNearestNode(p) {
|
| 391 |
-
let minDistSq = Infinity;
|
| 392 |
-
let personIndex = -1;
|
| 393 |
-
let nodeIndex = -1;
|
| 394 |
-
for (let i = 0; i < poseData.length; i++) {
|
| 395 |
-
const pose = poseData[i];
|
| 396 |
-
for (let j = 0; j < pose.length; j++) {
|
| 397 |
-
const q = pose[j];
|
| 398 |
-
if (q == null) continue;
|
| 399 |
-
const d = distSq(p, q);
|
| 400 |
-
if (d < minDistSq) {
|
| 401 |
-
minDistSq = d;
|
| 402 |
-
personIndex = i;
|
| 403 |
-
nodeIndex = j;
|
| 404 |
-
}
|
| 405 |
-
}
|
| 406 |
-
}
|
| 407 |
-
return [personIndex, nodeIndex, Math.sqrt(minDistSq)];
|
| 408 |
-
}
|
| 409 |
-
|
| 410 |
-
let dragRange = 64;
|
| 411 |
-
let dragRangeDelta = 16;
|
| 412 |
-
|
| 413 |
-
// ドラッグ中に座標を保持するための変数
|
| 414 |
-
let isDragging = false;
|
| 415 |
-
let dragStart = [0, 0];
|
| 416 |
-
let dragPersonIndex = -1;
|
| 417 |
-
let dragMarks = [];
|
| 418 |
-
let dragMode = "";
|
| 419 |
-
let rotateBaseVector = null;
|
| 420 |
-
let history = [];
|
| 421 |
-
let historyIndex = 0;
|
| 422 |
-
|
| 423 |
-
function clearHistory() {
|
| 424 |
-
history = [];
|
| 425 |
-
historyIndex = 0;
|
| 426 |
-
}
|
| 427 |
-
|
| 428 |
-
function addHistory() {
|
| 429 |
-
history = history.slice(0, historyIndex);
|
| 430 |
-
history.push(JSON.parse(JSON.stringify(poseData)));
|
| 431 |
-
historyIndex = history.length;
|
| 432 |
-
}
|
| 433 |
-
|
| 434 |
-
function undo() {
|
| 435 |
-
if (1 < historyIndex) {
|
| 436 |
-
historyIndex--;
|
| 437 |
-
poseData = deepCopy(history[historyIndex-1]);
|
| 438 |
-
Redraw();
|
| 439 |
-
}
|
| 440 |
-
}
|
| 441 |
-
|
| 442 |
-
function redo() {
|
| 443 |
-
if (historyIndex < history.length) {
|
| 444 |
-
historyIndex++;
|
| 445 |
-
poseData = deepCopy(history[historyIndex-1]);
|
| 446 |
-
Redraw();
|
| 447 |
-
}
|
| 448 |
-
}
|
| 449 |
-
|
| 450 |
-
function fetchLatestPoseData() {
|
| 451 |
-
return history[historyIndex-1];
|
| 452 |
-
}
|
| 453 |
-
|
| 454 |
-
function getCanvasPosition(event) {
|
| 455 |
-
const rect = canvas.getBoundingClientRect();
|
| 456 |
-
const x = Math.floor(event.clientX - rect.left);
|
| 457 |
-
const y = Math.floor(event.clientY - rect.top);
|
| 458 |
-
return [x, y];
|
| 459 |
-
}
|
| 460 |
-
|
| 461 |
-
function forEachMarkedNodes(fn) {
|
| 462 |
-
for (let i = 0; i < dragMarks.length; i++) {
|
| 463 |
-
for (let j = 0; j < dragMarks[i].length; j++) {
|
| 464 |
-
if (dragMarks[i][j]) {
|
| 465 |
-
fn(i, j, poseData[i][j]);
|
| 466 |
-
}
|
| 467 |
-
}
|
| 468 |
-
}
|
| 469 |
-
}
|
| 470 |
-
|
| 471 |
-
// Canvas要素上でマウスが押された場合に呼び出される関数
|
| 472 |
-
function handleMouseDown(event) {
|
| 473 |
-
const p = getCanvasPosition(event);
|
| 474 |
-
const [personIndex, nodeIndex, minDist] = getNearestNode(p);
|
| 475 |
-
|
| 476 |
-
if (keyDownFlags["KeyD"]) {removePerson(personIndex);return;}
|
| 477 |
-
if (keyDownFlags["KeyR"]) {repairPerson(personIndex);return;}
|
| 478 |
-
|
| 479 |
-
if (keyDownFlags["KeyQ"] && minDist < 16) {
|
| 480 |
-
console.log("pressed KeyQ");
|
| 481 |
-
cutOffPersonLimb(personIndex, nodeIndex);
|
| 482 |
-
return;
|
| 483 |
-
}
|
| 484 |
-
|
| 485 |
-
// ドラッグ処理の開始
|
| 486 |
-
dragStart = p;
|
| 487 |
-
dragMarks = poseData.map(pose => pose.map(node => false));
|
| 488 |
-
|
| 489 |
-
if (event.altKey || event.ctrlKey || event.shiftKey ||
|
| 490 |
-
keyDownFlags["KeyX"] || keyDownFlags["KeyC"]) {
|
| 491 |
-
// dragMarksを設定
|
| 492 |
-
dragMarks[personIndex] =
|
| 493 |
-
poseData[personIndex].map((node) => node != null);
|
| 494 |
-
isDragging = true;
|
| 495 |
-
if (event.altKey) {
|
| 496 |
-
dragMode = "move";
|
| 497 |
-
} else if (event.ctrlKey) {
|
| 498 |
-
dragMode = "scale";
|
| 499 |
-
} else if (event.shiftKey) {
|
| 500 |
-
dragMode = "rotate";
|
| 501 |
-
rotateBaseVector = [0, 0];
|
| 502 |
-
} else if (keyDownFlags["KeyX"]) {
|
| 503 |
-
dragMode = "rotateX";
|
| 504 |
-
} else if (keyDownFlags["KeyC"]) {
|
| 505 |
-
dragMode = "rotateY";
|
| 506 |
-
}
|
| 507 |
-
} else if (keyDownFlags["Space"]) {
|
| 508 |
-
dragMarks[personIndex] =
|
| 509 |
-
poseData[personIndex].map(
|
| 510 |
-
(node) => node != null && distSq(p, node) < dragRange ** 2);
|
| 511 |
-
isDragging = dragMarks[personIndex].some((mark) => mark);
|
| 512 |
-
dragMode = "move";
|
| 513 |
-
} else if (minDist < 16) {
|
| 514 |
-
dragMarks[personIndex][nodeIndex] = true;
|
| 515 |
-
isDragging = true;
|
| 516 |
-
dragMode = "move";
|
| 517 |
-
}
|
| 518 |
-
}
|
| 519 |
-
|
| 520 |
-
// Canvas要素上でマウスが動いた場合に呼び出される関数
|
| 521 |
-
function handleMouseMove(event) {
|
| 522 |
-
mouseCursor = getCanvasPosition(event);
|
| 523 |
-
if (isDragging) {
|
| 524 |
-
const p = getCanvasPosition(event);
|
| 525 |
-
const dragOffset = [p[0] - dragStart[0], p[1] - dragStart[1]];
|
| 526 |
-
const latestPoseData = fetchLatestPoseData();
|
| 527 |
-
|
| 528 |
-
if (dragMode == "scale") {
|
| 529 |
-
// 拡大縮小
|
| 530 |
-
let xScale = 1 + dragOffset[0] / canvas.width;
|
| 531 |
-
let yScale = 1 + dragOffset[0] / canvas.height;
|
| 532 |
-
forEachMarkedNodes((i, j, node) => {
|
| 533 |
-
const lp = latestPoseData[i][j];
|
| 534 |
-
node[0] = (lp[0] - dragStart[0]) * xScale + dragStart[0];
|
| 535 |
-
node[1] = (lp[1] - dragStart[1]) * yScale + dragStart[1];
|
| 536 |
-
});
|
| 537 |
-
} else if (dragMode == "rotate") {
|
| 538 |
-
rotateBaseVector = dragOffset;
|
| 539 |
-
if (!event.shiftKey) {
|
| 540 |
-
dragMode = "rotate2";
|
| 541 |
-
}
|
| 542 |
-
} else if (dragMode == "rotate2") {
|
| 543 |
-
// 回転
|
| 544 |
-
let angle = directedAngleTo(rotateBaseVector, dragOffset);
|
| 545 |
-
forEachMarkedNodes((i, j, node) => {
|
| 546 |
-
const lp = latestPoseData[i][j];
|
| 547 |
-
let x = lp[0] - dragStart[0];
|
| 548 |
-
let y = lp[1] - dragStart[1];
|
| 549 |
-
let sin = Math.sin(angle);
|
| 550 |
-
let cos = Math.cos(angle);
|
| 551 |
-
node[0] = x * cos - y * sin + dragStart[0];
|
| 552 |
-
node[1] = x * sin + y * cos + dragStart[1];
|
| 553 |
-
});
|
| 554 |
-
} else if (dragMode == "rotateX") {
|
| 555 |
-
const center = dragStart;
|
| 556 |
-
const angle = dragOffset[1] / -40;
|
| 557 |
-
forEachMarkedNodes((i, j, node) => {
|
| 558 |
-
const lp = latestPoseData[i][j];
|
| 559 |
-
const np = rotateAndProject(rotateX, lp, center, angle);
|
| 560 |
-
node[0] = np[0];
|
| 561 |
-
node[1] = np[1];
|
| 562 |
-
});
|
| 563 |
-
} else if (dragMode == "rotateY") {
|
| 564 |
-
const center = dragStart;
|
| 565 |
-
const angle = dragOffset[0] / 40;
|
| 566 |
-
forEachMarkedNodes((i, j, node) => {
|
| 567 |
-
const lp = latestPoseData[i][j];
|
| 568 |
-
const np = rotateAndProject(rotateY, lp, center, angle);
|
| 569 |
-
node[0] = np[0];
|
| 570 |
-
node[1] = np[1];
|
| 571 |
-
});
|
| 572 |
-
} else if (dragMode == "move") {
|
| 573 |
-
// 移動
|
| 574 |
-
forEachMarkedNodes((i, j, node) => {
|
| 575 |
-
const lp = latestPoseData[i][j];
|
| 576 |
-
node[0] = lp[0] + dragOffset[0];
|
| 577 |
-
node[1] = lp[1] + dragOffset[1];
|
| 578 |
-
});
|
| 579 |
-
}
|
| 580 |
-
|
| 581 |
-
// animatePicture(document.getElementById("canvas4"),[canvas.width, canvas.height], history[0][0], poseData[0], pictureImageData, initialPoseField);
|
| 582 |
-
}
|
| 583 |
-
|
| 584 |
-
Redraw();
|
| 585 |
-
}
|
| 586 |
-
|
| 587 |
-
function handleMouseUp(event) {
|
| 588 |
-
console.profile("animate");
|
| 589 |
-
animatePicture(
|
| 590 |
-
document.getElementById("canvas4"),
|
| 591 |
-
[canvas.width, canvas.height], history[0][0], poseData[0], pictureImageData, initialPoseField);
|
| 592 |
-
console.profileEnd("animate");
|
| 593 |
-
isDragging = false;
|
| 594 |
-
addHistory();
|
| 595 |
-
Redraw();
|
| 596 |
-
}
|
| 597 |
-
|
| 598 |
-
function handleMouseLeave(event) {
|
| 599 |
-
mouseCursor = [-1,-1];
|
| 600 |
-
if (isDragging) {
|
| 601 |
-
handleMouseUp(event);
|
| 602 |
-
}
|
| 603 |
-
keyDownFlags = {};
|
| 604 |
-
}
|
| 605 |
-
|
| 606 |
-
function ModifyDragRange(delta) { dragRange = Math.max(dragRangeDelta, Math.min(512, dragRange + delta)); }
|
| 607 |
-
|
| 608 |
-
document.addEventListener('wheel', function(event) {
|
| 609 |
-
if (!isMouseOnCanvas()) {return;}
|
| 610 |
-
if (!event.altKey && !keyDownFlags['Space']) {return;}
|
| 611 |
-
|
| 612 |
-
event.preventDefault();
|
| 613 |
-
const deltaY = event.deltaY;
|
| 614 |
-
if (deltaY < 0) {ModifyDragRange(-dragRangeDelta);}
|
| 615 |
-
if (0 < deltaY) {ModifyDragRange(dragRangeDelta);}
|
| 616 |
-
lastWheeling = new Date().getTime();
|
| 617 |
-
Redraw();
|
| 618 |
-
window.setTimeout(function() { Redraw(); }, wheelDisplayTime+10);
|
| 619 |
-
}, {passive: false});
|
| 620 |
-
|
| 621 |
-
document.addEventListener("keydown", (event) => {
|
| 622 |
-
if (!isMouseOnCanvas()) {return;}
|
| 623 |
-
|
| 624 |
-
if (event.code == "BracketLeft") { ModifyDragRange(-dragRangeDelta); }
|
| 625 |
-
if (event.code == "BracketRight") { ModifyDragRange(dragRangeDelta); }
|
| 626 |
-
keyDownFlags[event.code] = true;
|
| 627 |
-
Redraw();
|
| 628 |
-
event.preventDefault();
|
| 629 |
-
});
|
| 630 |
-
document.addEventListener("keyup", (event) => {
|
| 631 |
-
if (!isMouseOnCanvas()) {return;}
|
| 632 |
-
|
| 633 |
-
keyDownFlags[event.code] = false;
|
| 634 |
-
if (event.ctrlKey && event.code == "KeyE") {
|
| 635 |
-
addPerson();
|
| 636 |
-
} else if (event.ctrlKey && event.code == "KeyZ") {
|
| 637 |
-
if (event.shiftKey) {
|
| 638 |
-
redo();
|
| 639 |
-
} else {
|
| 640 |
-
undo();
|
| 641 |
-
}
|
| 642 |
-
}
|
| 643 |
-
Redraw();
|
| 644 |
-
event.preventDefault();
|
| 645 |
-
});
|
| 646 |
-
|
| 647 |
-
function initializeEditor() {
|
| 648 |
-
console.log("initializeEditor");
|
| 649 |
-
|
| 650 |
-
canvas = document.getElementById('canvas');
|
| 651 |
-
ctx = canvas.getContext('2d');
|
| 652 |
-
|
| 653 |
-
canvas.addEventListener('mousedown', handleMouseDown);
|
| 654 |
-
canvas.addEventListener('mousemove', handleMouseMove);
|
| 655 |
-
canvas.addEventListener('mouseup', handleMouseUp);
|
| 656 |
-
canvas.addEventListener('mouseleave', handleMouseLeave);
|
| 657 |
-
poseData = [];
|
| 658 |
-
clearHistory();
|
| 659 |
-
}
|
| 660 |
-
|
| 661 |
-
function importPose(jsonData) {
|
| 662 |
-
if (jsonData != null) {
|
| 663 |
-
newPoseData = makePoseDataFromCandidateAndSubset(jsonData.candidate, jsonData.subset);
|
| 664 |
-
if (jsonData.width != null && jsonData.height != null) {
|
| 665 |
-
resizeCanvas(jsonData.width, jsonData.height);
|
| 666 |
-
}
|
| 667 |
-
} else {
|
| 668 |
-
newPoseData = makePoseDataFromCandidateAndSubset(sampleCandidateSource, [sampleSubsetElementSource]);
|
| 669 |
-
}
|
| 670 |
-
poseData = poseData.concat(newPoseData);
|
| 671 |
-
addHistory();
|
| 672 |
-
Redraw();
|
| 673 |
-
}
|
| 674 |
-
|
| 675 |
-
// crc32
|
| 676 |
-
// CRC32を初期化
|
| 677 |
-
function initCrc32Table() {
|
| 678 |
-
const crcTable = new Uint32Array(256);
|
| 679 |
-
for (let i = 0; i < 256; i++) {
|
| 680 |
-
let c = i;
|
| 681 |
-
for (let j = 0; j < 8; j++) {
|
| 682 |
-
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
| 683 |
-
}
|
| 684 |
-
crcTable[i] = c;
|
| 685 |
-
}
|
| 686 |
-
return crcTable;
|
| 687 |
-
}
|
| 688 |
-
|
| 689 |
-
// データのCRC32を計算
|
| 690 |
-
function getCrc32(data, crc=0) {
|
| 691 |
-
const crcTable = initCrc32Table();
|
| 692 |
-
crc = (crc ^ 0xFFFFFFFF) >>> 0;
|
| 693 |
-
for (let i = 0; i < data.length; i++) {
|
| 694 |
-
crc = crcTable[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);
|
| 695 |
-
}
|
| 696 |
-
return (crc ^ 0xFFFFFFFF) >>> 0;
|
| 697 |
-
}
|
| 698 |
-
|
| 699 |
-
function stringToUint8Array(str) {
|
| 700 |
-
var arr = new Uint8Array(str.length);
|
| 701 |
-
for (var i = 0; i < str.length; i++) {
|
| 702 |
-
arr[i] = str.charCodeAt(i);
|
| 703 |
-
}
|
| 704 |
-
return arr;
|
| 705 |
-
}
|
| 706 |
-
|
| 707 |
-
function base64ToUint8Array(base64Str) {
|
| 708 |
-
return stringToUint8Array(atob(base64Str));
|
| 709 |
-
}
|
| 710 |
-
|
| 711 |
-
function visitPng(png, type) {
|
| 712 |
-
var dataLength;
|
| 713 |
-
var chunkType;
|
| 714 |
-
var nextChunkPos;
|
| 715 |
-
var Signature = String.fromCharCode(137, 80, 78, 71, 13, 10, 26, 10);
|
| 716 |
-
var rpos = 0;
|
| 717 |
-
|
| 718 |
-
// シグネチャの確認
|
| 719 |
-
if (String.fromCharCode.apply(null, png.subarray(rpos, rpos += 8)) !== Signature) {
|
| 720 |
-
throw new Error('invalid signature');
|
| 721 |
-
}
|
| 722 |
-
|
| 723 |
-
// チャンクの探索
|
| 724 |
-
while (rpos < png.length) {
|
| 725 |
-
dataLength = (
|
| 726 |
-
(png[rpos++] << 24) |
|
| 727 |
-
(png[rpos++] << 16) |
|
| 728 |
-
(png[rpos++] << 8) |
|
| 729 |
-
(png[rpos++] )
|
| 730 |
-
) >>> 0;
|
| 731 |
-
|
| 732 |
-
nextChunkPos = rpos + dataLength + 8;
|
| 733 |
-
|
| 734 |
-
chunkType = String.fromCharCode.apply(null, png.subarray(rpos, rpos += 4));
|
| 735 |
-
|
| 736 |
-
if (chunkType === type) {
|
| 737 |
-
return [rpos - 8, dataLength, nextChunkPos];
|
| 738 |
-
}
|
| 739 |
-
|
| 740 |
-
rpos = nextChunkPos;
|
| 741 |
-
}
|
| 742 |
-
}
|
| 743 |
-
|
| 744 |
-
function createChunk(type, data) {
|
| 745 |
-
var dataLength = data.length;
|
| 746 |
-
var chunk = new Uint8Array(4 + 4 + dataLength + 4);
|
| 747 |
-
var type = stringToUint8Array(type);
|
| 748 |
-
var pos = 0;
|
| 749 |
-
|
| 750 |
-
// length
|
| 751 |
-
chunk[pos++] = (dataLength >> 24) & 0xff;
|
| 752 |
-
chunk[pos++] = (dataLength >> 16) & 0xff;
|
| 753 |
-
chunk[pos++] = (dataLength >> 8) & 0xff;
|
| 754 |
-
chunk[pos++] = (dataLength ) & 0xff;
|
| 755 |
-
|
| 756 |
-
// type
|
| 757 |
-
chunk[pos++] = type[0];
|
| 758 |
-
chunk[pos++] = type[1];
|
| 759 |
-
chunk[pos++] = type[2];
|
| 760 |
-
chunk[pos++] = type[3];
|
| 761 |
-
|
| 762 |
-
// data
|
| 763 |
-
for (let i = 0; i < dataLength; ++i) {
|
| 764 |
-
chunk[pos++] = data[i];
|
| 765 |
-
}
|
| 766 |
-
|
| 767 |
-
//crc
|
| 768 |
-
initCrc32Table();
|
| 769 |
-
let crc = getCrc32(type);
|
| 770 |
-
crc = getCrc32(data, crc);
|
| 771 |
-
chunk[pos++] = (crc >> 24) & 0xff;
|
| 772 |
-
chunk[pos++] = (crc >> 16) & 0xff;
|
| 773 |
-
chunk[pos++] = (crc >> 8) & 0xff;
|
| 774 |
-
chunk[pos++] = (crc ) & 0xff;
|
| 775 |
-
|
| 776 |
-
return chunk;
|
| 777 |
-
}
|
| 778 |
-
|
| 779 |
-
function insertChunk(destBuffer, sourceBuffer, rpos, chunk) {
|
| 780 |
-
var pos = 0;
|
| 781 |
-
|
| 782 |
-
// IDAT チャンクの前までコピー
|
| 783 |
-
destBuffer.set(sourceBuffer.subarray(0, rpos), pos);
|
| 784 |
-
pos += rpos;
|
| 785 |
-
|
| 786 |
-
// hoGe チャンクをコピー
|
| 787 |
-
destBuffer.set(chunk, pos);
|
| 788 |
-
pos += chunk.length;
|
| 789 |
-
|
| 790 |
-
// IDAT チャンク以降をコピー
|
| 791 |
-
destBuffer.set(sourceBuffer.subarray(rpos), pos);
|
| 792 |
-
}
|
| 793 |
-
|
| 794 |
-
function mergeCanvasWithPose(keyword, content) {
|
| 795 |
-
const canvasUrl = canvas.toDataURL();
|
| 796 |
-
|
| 797 |
-
var insertion = stringToUint8Array(`${keyword}\0${content}`);
|
| 798 |
-
var chunk = createChunk("tEXt", insertion);
|
| 799 |
-
var sourceBuffer = base64ToUint8Array(canvasUrl.split(',')[1]);
|
| 800 |
-
var destBuffer = new Uint8Array(sourceBuffer.length + insertion.length + 12);
|
| 801 |
-
|
| 802 |
-
var [rpos, dataLength, nextChunkPos] = visitPng(sourceBuffer, "IHDR");
|
| 803 |
-
insertChunk(destBuffer, sourceBuffer, nextChunkPos, chunk);
|
| 804 |
-
|
| 805 |
-
var blob = new Blob([destBuffer], {type: "image/png"});
|
| 806 |
-
var url = URL.createObjectURL(blob);
|
| 807 |
-
return url;
|
| 808 |
-
}
|
| 809 |
-
|
| 810 |
-
function savePose() {
|
| 811 |
-
var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
|
| 812 |
-
let jsonData = {candidate: candidate, subset: subset};
|
| 813 |
-
|
| 814 |
-
var url = mergeCanvasWithPose("openpose", JSON.stringify(jsonData));
|
| 815 |
-
|
| 816 |
-
const createEl = document.createElement('a');
|
| 817 |
-
createEl.href = url;
|
| 818 |
-
|
| 819 |
-
// This is the name of our downloaded file
|
| 820 |
-
createEl.download = "pose.png";
|
| 821 |
-
|
| 822 |
-
createEl.click();
|
| 823 |
-
createEl.remove();
|
| 824 |
-
|
| 825 |
-
return jsonData;
|
| 826 |
-
}
|
| 827 |
-
|
| 828 |
-
function importPicture(image) {
|
| 829 |
-
var pic = new Image();
|
| 830 |
-
pic.onload = () => {
|
| 831 |
-
const size = [canvas.width, canvas.height];
|
| 832 |
-
|
| 833 |
-
const canvas3 = document.getElementById("canvas3");
|
| 834 |
-
pictureImageData = makeImageDataFromPicture(canvas3, size, pic);
|
| 835 |
-
|
| 836 |
-
const canvas4 = document.getElementById("canvas4");
|
| 837 |
-
offscreen = canvas4;
|
| 838 |
-
|
| 839 |
-
initialPoseField = buildWeightMap(size, poseData[0]);
|
| 840 |
-
animatePicture(canvas4, size, poseData[0], poseData[0], pictureImageData, initialPoseField);
|
| 841 |
-
|
| 842 |
-
Redraw();
|
| 843 |
-
};
|
| 844 |
-
pic.src = image;
|
| 845 |
-
|
| 846 |
-
initMicroscope();
|
| 847 |
-
}
|
| 848 |
-
|
| 849 |
-
function importBackground(image) {
|
| 850 |
-
if (image == null) {
|
| 851 |
-
canvasBg = null;
|
| 852 |
-
Redraw();
|
| 853 |
-
return;
|
| 854 |
-
}
|
| 855 |
-
|
| 856 |
-
let m = new Image();
|
| 857 |
-
m.src = image;
|
| 858 |
-
m.onload = function() {
|
| 859 |
-
canvasBg = m;
|
| 860 |
-
Redraw();
|
| 861 |
-
}
|
| 862 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main.py
CHANGED
|
@@ -28,10 +28,15 @@ with gr.Blocks(css="""button { min-width: 80px; }""") as demo:
|
|
| 28 |
with gr.Column(scale=1):
|
| 29 |
source = gr.Image(type="pil")
|
| 30 |
info = gr.Markdown("""info""")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
btn = gr.Button("Import")
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
| 35 |
with gr.Accordion(label="Json", open=False):
|
| 36 |
json = gr.JSON(label="Json")
|
| 37 |
with gr.Column(scale=3):
|
|
@@ -53,7 +58,7 @@ with gr.Blocks(css="""button { min-width: 80px; }""") as demo:
|
|
| 53 |
outputs = [],
|
| 54 |
_js="(frontImage, backImage, json) => { initializeEditor(); importPose(json); importPicture(frontImage); importBackground(backImage); return []; }")
|
| 55 |
|
| 56 |
-
demo.load(fn=None, inputs=[], outputs=[], _js="() => { initializeEditor(); importPose();
|
| 57 |
|
| 58 |
print("mount")
|
| 59 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
| 28 |
with gr.Column(scale=1):
|
| 29 |
source = gr.Image(type="pil")
|
| 30 |
info = gr.Markdown("""info""")
|
| 31 |
+
gr.Examples(
|
| 32 |
+
examples=["static/sample1.png", "static/sample2.png", "static/sample3.png"],
|
| 33 |
+
inputs=source,
|
| 34 |
+
)
|
| 35 |
btn = gr.Button("Import")
|
| 36 |
+
with gr.Accordion(label="Parts", open=False):
|
| 37 |
+
mask = gr.Image()
|
| 38 |
+
frontImage = gr.Image(image_mode="RGBA")
|
| 39 |
+
backImage = gr.Image(image_mode="RGBA")
|
| 40 |
with gr.Accordion(label="Json", open=False):
|
| 41 |
json = gr.JSON(label="Json")
|
| 42 |
with gr.Column(scale=3):
|
|
|
|
| 58 |
outputs = [],
|
| 59 |
_js="(frontImage, backImage, json) => { initializeEditor(); importPose(json); importPicture(frontImage); importBackground(backImage); return []; }")
|
| 60 |
|
| 61 |
+
demo.load(fn=None, inputs=[], outputs=[], _js="() => { initializeEditor(); importPose(); return []; }")
|
| 62 |
|
| 63 |
print("mount")
|
| 64 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
static/sample.png
DELETED
|
Binary file (86.9 kB)
|
|
|
static/sample2.png
CHANGED
|
|
static/sample3.png
CHANGED
|
|