jonigata commited on
Commit
49dd077
1 Parent(s): 1b0aeb4

add pseudo-3d rotation

Browse files
Files changed (2) hide show
  1. app.py +7 -0
  2. static/poseEditor.js +118 -11
app.py CHANGED
@@ -110,6 +110,13 @@ with gr.Blocks() as demo:
110
  - "shift + drag" to rotate(move right first, release shift, then up or down)
111
  - "space + drag" to move within range
112
  - "[", "]" or mouse wheel to shrink or expand range
 
 
 
 
 
 
 
113
  """)
114
 
115
  source.change(
 
110
  - "shift + drag" to rotate(move right first, release shift, then up or down)
111
  - "space + drag" to move within range
112
  - "[", "]" or mouse wheel to shrink or expand range
113
+ - "ctrl + z", "shift + ctrl + z" to undo, redo
114
+ - "a" add new person
115
+ - "q + click" to delete person
116
+ - "x + drag" to x-axis pseudo-3D rotation
117
+ - "c + drag" to y-axis pseudo-3D rotation
118
+
119
+ Points to note for pseudo-3D rotation: When performing pseudo-3D rotation on the X and Y axes, the projection is converted to 2D and Z-axis information is lost when the mouse button is released. This means that if you finish dragging while the shape is collapsed, you may not be able to restore it to its original state. In such a case, please use the "undo" function.
120
  """)
121
 
122
  source.change(
static/poseEditor.js CHANGED
@@ -10,10 +10,24 @@ function distSq(p0, p1) {
10
  return (p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2;
11
  }
12
 
13
- // poseDataの形式:[[[x1, y1], [x2, y2], ...]]
14
  // 各要素が人間
15
  // 人間の各要素が関節
16
- let poseData = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  // サンプルデータ
19
  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], p[1] - 70]);
@@ -77,6 +91,70 @@ function resizeCanvas(width, height) {
77
  drawBodyPose();
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  function drawBodyPose() {
81
  const stickWidth = 6;
82
  const limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10],
@@ -91,6 +169,7 @@ function drawBodyPose() {
91
 
92
  for (let i = 0; i < poseData.length; i++) {
93
  const pose = poseData[i];
 
94
 
95
  // edge
96
  for (let j = 0; j < 17; j++) {
@@ -242,7 +321,8 @@ function handleMouseDown(event) {
242
  dragStart = p;
243
  dragMarks = poseData.map(pose => pose.map(node => false));
244
 
245
- if (event.altKey || event.ctrlKey || event.shiftKey) {
 
246
  // dragMarksを設定
247
  dragMarks[personIndex] =
248
  poseData[personIndex].map((node) => node != null);
@@ -254,6 +334,10 @@ function handleMouseDown(event) {
254
  } else if (event.shiftKey) {
255
  dragMode = "rotate";
256
  rotateBaseVector = [0, 0];
 
 
 
 
257
  }
258
  } else if (keyDownFlags["Space"]) {
259
  dragMarks[personIndex] =
@@ -302,6 +386,25 @@ function handleMouseMove(event) {
302
  node[0] = x * cos - y * sin + dragStart[0];
303
  node[1] = x * sin + y * cos + dragStart[1];
304
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  } else if (dragMode == "move") {
306
  // 移動
307
  forEachMarkedNodes((i, j, node) => {
@@ -370,18 +473,22 @@ function initializePose(jsonData,w,h) {
370
  canvas.addEventListener('mouseup', handleMouseUp);
371
 
372
  resizeCanvas(w, h);
 
 
373
  }
374
 
375
  function savePose() {
376
- const canvasUrl = canvas.toDataURL();
 
 
 
377
 
378
- const createEl = document.createElement('a');
379
- createEl.href = canvasUrl;
380
 
381
- // This is the name of our downloaded file
382
- createEl.download = "pose.png";
383
 
384
- createEl.click();
385
- createEl.remove();
386
- return { "candidate": candidate, "subset": subset };
387
  }
 
10
  return (p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2;
11
  }
12
 
13
+ // poseDataの形式:[[[x1, y1], [x2, y2], ...],[[x3, y3], [x4, y4], ...], ...]
14
  // 各要素が人間
15
  // 人間の各要素が関節
16
+
17
+ function poseDataToCandidateAndSubset(poseData) {
18
+ let candidate = [];
19
+ let subset = [];
20
+ for (let i = 0; i < poseData.length; i++) {
21
+ let person = poseData[i];
22
+ let subsetElement = [];
23
+ for (let j = 0; j < person.length; j++) {
24
+ candidate.push(person[j]);
25
+ subsetElement.push(candidate.length - 1);
26
+ }
27
+ subset.push(subsetElement);
28
+ }
29
+ return [candidate, subset];
30
+ }
31
 
32
  // サンプルデータ
33
  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], p[1] - 70]);
 
91
  drawBodyPose();
92
  }
93
 
94
+ function calculateCenter(shape) {
95
+ var center = shape.reduce(function(acc, point) {
96
+ acc[0] += point[0];
97
+ acc[1] += point[1];
98
+ return acc;
99
+ }, [0, 0]);
100
+ center[0] /= shape.length;
101
+ center[1] /= shape.length;
102
+ return center;
103
+ }
104
+
105
+ // v2d -> v3d
106
+ function rotateX(vector, angle) {
107
+ var x = vector[0];
108
+ var y = vector[1];
109
+ var z = 0;
110
+
111
+ // X軸に対して回転する
112
+ var x1 = x;
113
+ var y1 = y * Math.cos(angle) - z * Math.sin(angle);
114
+ var z1 = y * Math.sin(angle) + z * Math.cos(angle);
115
+
116
+ return [x1, y1, z1];
117
+ }
118
+
119
+ // v2d -> v3d
120
+ function rotateY(vector, angle) {
121
+ var x = vector[0];
122
+ var y = vector[1];
123
+ var z = 0;
124
+
125
+ // Y軸に対して回転する
126
+ var x1 = x * Math.cos(angle) + z * Math.sin(angle);
127
+ var y1 = y;
128
+ var z1 = -x * Math.sin(angle) + z * Math.cos(angle);
129
+
130
+ return [x1, y1, z1];
131
+ }
132
+
133
+ // v3d -> v2d
134
+ function perspectiveProjection(vector, cameraDistance) {
135
+ var x = vector[0];
136
+ var y = vector[1];
137
+ var z = vector[2];
138
+
139
+ if (z === 0) {
140
+ return [x, y];
141
+ }
142
+
143
+ var scale = cameraDistance / (cameraDistance - z);
144
+ var x1 = x * scale;
145
+ var y1 = y * scale;
146
+
147
+ return [x1, y1];
148
+ }
149
+
150
+ // v2d -> v3d
151
+ function rotateAndProject(f, p, c, angle) {
152
+ var v = [p[0] - c[0], p[1] - c[1]];
153
+ var v1 = f(v, angle);
154
+ var v2 = perspectiveProjection(v1, 500);
155
+ return [v2[0] + c[0], v2[1] + c[1]];
156
+ }
157
+
158
  function drawBodyPose() {
159
  const stickWidth = 6;
160
  const limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10],
 
169
 
170
  for (let i = 0; i < poseData.length; i++) {
171
  const pose = poseData[i];
172
+ const center = calculateCenter(pose);
173
 
174
  // edge
175
  for (let j = 0; j < 17; j++) {
 
321
  dragStart = p;
322
  dragMarks = poseData.map(pose => pose.map(node => false));
323
 
324
+ if (event.altKey || event.ctrlKey || event.shiftKey ||
325
+ keyDownFlags["KeyX"] || keyDownFlags["KeyC"]) {
326
  // dragMarksを設定
327
  dragMarks[personIndex] =
328
  poseData[personIndex].map((node) => node != null);
 
334
  } else if (event.shiftKey) {
335
  dragMode = "rotate";
336
  rotateBaseVector = [0, 0];
337
+ } else if (keyDownFlags["KeyX"]) {
338
+ dragMode = "rotateX";
339
+ } else if (keyDownFlags["KeyC"]) {
340
+ dragMode = "rotateY";
341
  }
342
  } else if (keyDownFlags["Space"]) {
343
  dragMarks[personIndex] =
 
386
  node[0] = x * cos - y * sin + dragStart[0];
387
  node[1] = x * sin + y * cos + dragStart[1];
388
  });
389
+ } else if (dragMode == "rotateX") {
390
+ const center = dragStart;
391
+ const angle = dragOffset[1] / -40;
392
+ forEachMarkedNodes((i, j, node) => {
393
+ const lp = latestPoseData[i][j];
394
+ const np = rotateAndProject(rotateX, lp, center, angle);
395
+ console.log(np);
396
+ node[0] = np[0];
397
+ node[1] = np[1];
398
+ });
399
+ } else if (dragMode == "rotateY") {
400
+ const center = dragStart;
401
+ const angle = dragOffset[0] / 40;
402
+ forEachMarkedNodes((i, j, node) => {
403
+ const lp = latestPoseData[i][j];
404
+ const np = rotateAndProject(rotateY, lp, center, angle);
405
+ node[0] = np[0];
406
+ node[1] = np[1];
407
+ });
408
  } else if (dragMode == "move") {
409
  // 移動
410
  forEachMarkedNodes((i, j, node) => {
 
473
  canvas.addEventListener('mouseup', handleMouseUp);
474
 
475
  resizeCanvas(w, h);
476
+
477
+ // setInterval(Redraw, 1000 / 30);
478
  }
479
 
480
  function savePose() {
481
+ const canvasUrl = canvas.toDataURL();
482
+
483
+ const createEl = document.createElement('a');
484
+ createEl.href = canvasUrl;
485
 
486
+ // This is the name of our downloaded file
487
+ createEl.download = "pose.png";
488
 
489
+ createEl.click();
490
+ createEl.remove();
491
 
492
+ var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
493
+ return {candidate: candidate, subset: subset};
 
494
  }