fix(044): hand rect edit stability
Browse files- Use keypoint BBox as transform refRect (canvas coords)
- Unify resolution source (metadata.resolution → resolution → 512)
- Clamp relative ratios to 0–1 to avoid overshoot/flip
- Convert rect move delta from canvas→data coords
- Remove wrong ‘hands are canvas coords’ assumption
- Gate debug logs via window.poseEditorDebug
Hands’ resize now follows direction/amount like face ✅🖐️
- .gitignore +1 -1
- static/pose_editor.js +282 -48
.gitignore
CHANGED
|
@@ -115,4 +115,4 @@ development_tests/
|
|
| 115 |
issues/
|
| 116 |
CLAUDE.md
|
| 117 |
.claude/
|
| 118 |
-
|
|
|
|
| 115 |
issues/
|
| 116 |
CLAUDE.md
|
| 117 |
.claude/
|
| 118 |
+
AGENTS.md
|
static/pose_editor.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
| 1 |
// Canvas操作用JavaScript for dwpose-editor
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
// グローバル変数
|
| 4 |
window.poseEditorGlobals = {
|
| 5 |
canvas: null,
|
|
@@ -593,6 +600,12 @@ function handleMouseDown(event) {
|
|
| 593 |
const currentRect = window.poseEditorGlobals.currentRects[rectType];
|
| 594 |
if (currentRect) {
|
| 595 |
window.poseEditorGlobals.originalRect = { ...currentRect };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 596 |
}
|
| 597 |
|
| 598 |
// コントロールポイントドラッグ(リサイズ)
|
|
@@ -1056,11 +1069,13 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1056 |
const canvasWidth = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.width : 512;
|
| 1057 |
const canvasHeight = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.height : 512;
|
| 1058 |
|
| 1059 |
-
|
| 1060 |
-
let
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
|
|
|
|
|
|
| 1064 |
dataResolutionWidth = currentPoseData.resolution[0];
|
| 1065 |
dataResolutionHeight = currentPoseData.resolution[1];
|
| 1066 |
}
|
|
@@ -1068,21 +1083,87 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1068 |
const coordScaleX = canvasWidth / dataResolutionWidth;
|
| 1069 |
const coordScaleY = canvasHeight / dataResolutionHeight;
|
| 1070 |
|
| 1071 |
-
//
|
| 1072 |
let isNormalized = false;
|
| 1073 |
-
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1080 |
}
|
| 1081 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1082 |
}
|
| 1083 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1084 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1085 |
// キーポイントを一括変換(元データから毎回変換)
|
|
|
|
| 1086 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
| 1087 |
if (i + 2 < originalTargetKeypoints.length && i + 2 < targetKeypoints.length) {
|
| 1088 |
const confidence = originalTargetKeypoints[i + 2];
|
|
@@ -1091,38 +1172,89 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1091 |
let x = originalTargetKeypoints[i];
|
| 1092 |
let y = originalTargetKeypoints[i + 1];
|
| 1093 |
|
| 1094 |
-
// データ座標→Canvas
|
| 1095 |
let canvasX, canvasY;
|
| 1096 |
if (isNormalized) {
|
| 1097 |
canvasX = (x * dataResolutionWidth) * coordScaleX;
|
| 1098 |
canvasY = (y * dataResolutionHeight) * coordScaleY;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1099 |
} else {
|
| 1100 |
canvasX = x * coordScaleX;
|
| 1101 |
canvasY = y * coordScaleY;
|
| 1102 |
}
|
| 1103 |
|
| 1104 |
-
//
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
|
|
|
|
|
|
|
|
|
| 1108 |
// 新矩形での新しい位置を計算
|
| 1109 |
const newCanvasX = newRect.x + (relativeX * newRect.width);
|
| 1110 |
const newCanvasY = newRect.y + (relativeY * newRect.height);
|
| 1111 |
|
| 1112 |
-
// Canvas
|
|
|
|
| 1113 |
if (isNormalized) {
|
| 1114 |
const dataX = newCanvasX / coordScaleX;
|
| 1115 |
const dataY = newCanvasY / coordScaleY;
|
| 1116 |
-
|
| 1117 |
-
|
|
|
|
|
|
|
| 1118 |
} else {
|
| 1119 |
-
|
| 1120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1121 |
}
|
| 1122 |
}
|
| 1123 |
}
|
| 1124 |
}
|
| 1125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1126 |
}
|
| 1127 |
|
| 1128 |
// 🔧 元座標からキーポイントを移動(累積移動防止版)
|
|
@@ -1285,13 +1417,32 @@ function moveKeypointsWithRect(rectType, deltaX, deltaY) {
|
|
| 1285 |
return;
|
| 1286 |
}
|
| 1287 |
|
| 1288 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1289 |
for (let i = 0; i < keypoints.length; i += 3) {
|
| 1290 |
const confidence = keypoints[i + 2];
|
| 1291 |
|
| 1292 |
if (confidence > 0.1) { // 有効なキーポイントのみ移動
|
| 1293 |
-
keypoints[i] +=
|
| 1294 |
-
keypoints[i + 1] +=
|
| 1295 |
}
|
| 1296 |
}
|
| 1297 |
|
|
@@ -1554,10 +1705,10 @@ function syncBodiesToPeople(poseData) {
|
|
| 1554 |
|
| 1555 |
// Gradioにデータ送信(refs互換・強制changeイベント版)
|
| 1556 |
function sendPoseDataToGradio() {
|
| 1557 |
-
console.log('🔍 [sendPoseDataToGradio] Called');
|
| 1558 |
// グローバルposeDataを参照
|
| 1559 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1560 |
-
console.log('🔍 [sendPoseDataToGradio] currentPoseData state:', {
|
| 1561 |
exists: !!currentPoseData,
|
| 1562 |
hasPeople: !!currentPoseData?.people,
|
| 1563 |
peopleCount: currentPoseData?.people?.length || 0,
|
|
@@ -1677,7 +1828,7 @@ function sendPoseDataToGradio() {
|
|
| 1677 |
}
|
| 1678 |
|
| 1679 |
// 送信前の最終データ確認
|
| 1680 |
-
console.log('🔍 [sendPoseDataToGradio] Final canvasData being sent:', {
|
| 1681 |
hasPeople: !!canvasData.people,
|
| 1682 |
peopleCount: canvasData.people?.length || 0,
|
| 1683 |
hasHandLeft: !!canvasData.people?.[0]?.hand_left_keypoints_2d,
|
|
@@ -1689,7 +1840,7 @@ function sendPoseDataToGradio() {
|
|
| 1689 |
});
|
| 1690 |
|
| 1691 |
const jsonString = JSON.stringify(canvasData);
|
| 1692 |
-
console.log('🎯 [sendPoseDataToGradio] People形式でGradioに送信:', canvasData.people.length, 'people');
|
| 1693 |
|
| 1694 |
// 専用の隠しテキストボックスを探して更新
|
| 1695 |
const jsUpdateTextbox = document.querySelector('#js_pose_update textarea');
|
|
@@ -1778,7 +1929,7 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1778 |
];
|
| 1779 |
|
| 1780 |
// 🔧 Issue #043: 手データのデバッグ情報追加
|
| 1781 |
-
console.log('🫳 Hand data debug:', {
|
| 1782 |
enableHands: enableHands,
|
| 1783 |
leftHandLength: handsDataForDrawing[0].length,
|
| 1784 |
rightHandLength: handsDataForDrawing[1].length,
|
|
@@ -1788,14 +1939,14 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1788 |
// 💖 people形式のみサポート、古いhands形式は削除
|
| 1789 |
} else {
|
| 1790 |
handsDataForDrawing = [[], []]; // 空の手データ
|
| 1791 |
-
console.log('🚫 No people data available for hands');
|
| 1792 |
}
|
| 1793 |
|
| 1794 |
if (handsDataForDrawing && handsDataForDrawing.length >= 2) {
|
| 1795 |
-
console.log('✅ Calling drawHands function');
|
| 1796 |
drawHands(handsDataForDrawing, originalRes, scaleX, scaleY);
|
| 1797 |
} else {
|
| 1798 |
-
console.log('❌ Invalid hands data for drawing');
|
| 1799 |
}
|
| 1800 |
} catch (error) {
|
| 1801 |
console.error("❌ Error drawing hands:", error);
|
|
@@ -2623,13 +2774,16 @@ function calculateHandRect(handData, originalRes, scaleX, scaleY) {
|
|
| 2623 |
const confidence = handData[i + 2];
|
| 2624 |
|
| 2625 |
if (confidence > 0.3) { // refs互換の閾値
|
| 2626 |
-
|
| 2627 |
-
|
| 2628 |
-
|
| 2629 |
-
|
| 2630 |
-
|
| 2631 |
-
|
| 2632 |
-
|
|
|
|
|
|
|
|
|
|
| 2633 |
validPointCount++;
|
| 2634 |
}
|
| 2635 |
}
|
|
@@ -2638,12 +2792,61 @@ function calculateHandRect(handData, originalRes, scaleX, scaleY) {
|
|
| 2638 |
|
| 2639 |
// 10px余白付きで矩形返却(refs互換)
|
| 2640 |
const margin = 10;
|
| 2641 |
-
|
| 2642 |
x: minX - margin,
|
| 2643 |
y: minY - margin,
|
| 2644 |
width: (maxX - minX) + (margin * 2),
|
| 2645 |
height: (maxY - minY) + (margin * 2)
|
| 2646 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2647 |
}
|
| 2648 |
|
| 2649 |
// 🔧 顔キーポイントから矩形を計算(refs互換)
|
|
@@ -2685,6 +2888,13 @@ function calculateFaceRect(faceData, originalRes, scaleX, scaleY) {
|
|
| 2685 |
function drawEditableRect(ctx, rect, color, id) {
|
| 2686 |
if (!rect) return;
|
| 2687 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2688 |
// 太め破線で矩形描画(refs互換)
|
| 2689 |
ctx.strokeStyle = color;
|
| 2690 |
ctx.lineWidth = 3;
|
|
@@ -2885,12 +3095,21 @@ function updateRectControlDrag(mouseX, mouseY) {
|
|
| 2885 |
const controlPoint = window.poseEditorGlobals.draggedRectControl;
|
| 2886 |
const rectType = window.poseEditorGlobals.rectEditMode;
|
| 2887 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2888 |
|
| 2889 |
if (!controlPoint) {
|
|
|
|
| 2890 |
return;
|
| 2891 |
}
|
| 2892 |
|
| 2893 |
if (!rectType) {
|
|
|
|
| 2894 |
return;
|
| 2895 |
}
|
| 2896 |
|
|
@@ -2960,9 +3179,24 @@ function updateRectControlDrag(mouseX, mouseY) {
|
|
| 2960 |
// 🔧 矩形を更新(累積変形防止のため直接設定)
|
| 2961 |
window.poseEditorGlobals.currentRects[rectType] = newRect;
|
| 2962 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2963 |
|
| 2964 |
// 🔧 手・顔キーポイントの座標も更新(直接矩形変換版)
|
| 2965 |
if (controlPoint) {
|
|
|
|
| 2966 |
transformKeypointsDirectly(rectType, originalRect, newRect);
|
| 2967 |
}
|
| 2968 |
|
|
@@ -3004,10 +3238,10 @@ function initializeRectEditInfo() {
|
|
| 3004 |
// 💥 ベースは初回のみ保存!編集済みデータで上書きしない
|
| 3005 |
if (!window.poseEditorGlobals.baseOriginalKeypoints) {
|
| 3006 |
window.poseEditorGlobals.baseOriginalKeypoints = JSON.parse(JSON.stringify(currentPoseData));
|
| 3007 |
-
console.log('💖 [initializeRectEditInfo] Base original keypoints saved for FIRST editing session');
|
| 3008 |
|
| 3009 |
// ベースデータの詳細確認
|
| 3010 |
-
console.log('🔍 [initializeRectEditInfo] Base data details:', {
|
| 3011 |
hasHandLeft: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.hand_left_keypoints_2d,
|
| 3012 |
hasHandRight: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.hand_right_keypoints_2d,
|
| 3013 |
hasFace: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.face_keypoints_2d,
|
|
@@ -3016,15 +3250,15 @@ function initializeRectEditInfo() {
|
|
| 3016 |
faceLength: window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.face_keypoints_2d?.length || 0
|
| 3017 |
});
|
| 3018 |
} else {
|
| 3019 |
-
console.log('💖 [initializeRectEditInfo] Base original keypoints already exists - keeping original data');
|
| 3020 |
}
|
| 3021 |
|
| 3022 |
// 作業用は常にベースからコピー(編集済みデータではなく!)
|
| 3023 |
window.poseEditorGlobals.originalKeypoints = JSON.parse(JSON.stringify(window.poseEditorGlobals.baseOriginalKeypoints));
|
| 3024 |
-
console.log('💖 [initializeRectEditInfo] Working original keypoints restored from base for current editing session');
|
| 3025 |
|
| 3026 |
// 作業用データの詳細確認
|
| 3027 |
-
console.log('🔍 [initializeRectEditInfo] Working data details:', {
|
| 3028 |
hasHandLeft: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.hand_left_keypoints_2d,
|
| 3029 |
hasHandRight: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.hand_right_keypoints_2d,
|
| 3030 |
hasFace: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.face_keypoints_2d,
|
|
@@ -3577,4 +3811,4 @@ function drawEstimatedConnections(candidates, originalRes, scaleX, scaleY) {
|
|
| 3577 |
ctx.globalAlpha = 1.0; // 不透明に戻す
|
| 3578 |
}
|
| 3579 |
|
| 3580 |
-
// 🎨 pose_editor.js initialization complete
|
|
|
|
| 1 |
// Canvas操作用JavaScript for dwpose-editor
|
| 2 |
|
| 3 |
+
// 🔧 最小限デバッグフラグ(必要時のみONにする)
|
| 4 |
+
window.poseEditorDebug = window.poseEditorDebug || {
|
| 5 |
+
rect: false,
|
| 6 |
+
hands: false,
|
| 7 |
+
send: false
|
| 8 |
+
};
|
| 9 |
+
|
| 10 |
// グローバル変数
|
| 11 |
window.poseEditorGlobals = {
|
| 12 |
canvas: null,
|
|
|
|
| 600 |
const currentRect = window.poseEditorGlobals.currentRects[rectType];
|
| 601 |
if (currentRect) {
|
| 602 |
window.poseEditorGlobals.originalRect = { ...currentRect };
|
| 603 |
+
// 🔧 このドラッグ操作の基準となる"元キーポイント"も現在の状態からスナップショット
|
| 604 |
+
// 以前はセッション開始時のベースを使っていたため、連続リサイズで倍率/方向が狂っていた
|
| 605 |
+
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 606 |
+
if (currentPoseData) {
|
| 607 |
+
window.poseEditorGlobals.originalKeypoints = JSON.parse(JSON.stringify(currentPoseData));
|
| 608 |
+
}
|
| 609 |
}
|
| 610 |
|
| 611 |
// コントロールポイントドラッグ(リサイズ)
|
|
|
|
| 1069 |
const canvasWidth = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.width : 512;
|
| 1070 |
const canvasHeight = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.height : 512;
|
| 1071 |
|
| 1072 |
+
// 解像度情報の取得(metadata.resolution → resolution → 512x512)
|
| 1073 |
+
let dataResolutionWidth = 512;
|
| 1074 |
+
let dataResolutionHeight = 512;
|
| 1075 |
+
if (currentPoseData.metadata && currentPoseData.metadata.resolution && Array.isArray(currentPoseData.metadata.resolution) && currentPoseData.metadata.resolution.length >= 2) {
|
| 1076 |
+
dataResolutionWidth = currentPoseData.metadata.resolution[0];
|
| 1077 |
+
dataResolutionHeight = currentPoseData.metadata.resolution[1];
|
| 1078 |
+
} else if (currentPoseData.resolution && Array.isArray(currentPoseData.resolution) && currentPoseData.resolution.length >= 2) {
|
| 1079 |
dataResolutionWidth = currentPoseData.resolution[0];
|
| 1080 |
dataResolutionHeight = currentPoseData.resolution[1];
|
| 1081 |
}
|
|
|
|
| 1083 |
const coordScaleX = canvasWidth / dataResolutionWidth;
|
| 1084 |
const coordScaleY = canvasHeight / dataResolutionHeight;
|
| 1085 |
|
| 1086 |
+
// 正規化/ピクセル/Canvas座標を判定(元データ優先で判定)
|
| 1087 |
let isNormalized = false;
|
| 1088 |
+
let isCanvasUnit = false;
|
| 1089 |
+
let sampleX = null, sampleY = null;
|
| 1090 |
+
if (originalTargetKeypoints.length > 0) {
|
| 1091 |
+
let overCanvasCount = 0;
|
| 1092 |
+
let validCount = 0;
|
| 1093 |
+
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
| 1094 |
+
if (i + 2 < originalTargetKeypoints.length && originalTargetKeypoints[i + 2] > 0) {
|
| 1095 |
+
const x = originalTargetKeypoints[i];
|
| 1096 |
+
const y = originalTargetKeypoints[i + 1];
|
| 1097 |
+
if (sampleX === null) { sampleX = x; sampleY = y; }
|
| 1098 |
+
validCount++;
|
| 1099 |
+
if (x > dataResolutionWidth * 1.01 || y > dataResolutionHeight * 1.01) {
|
| 1100 |
+
overCanvasCount++;
|
| 1101 |
+
}
|
| 1102 |
+
// 正規化判定
|
| 1103 |
+
if (x >= 0 && x <= 1 && y >= 0 && y <= 1) {
|
| 1104 |
+
isNormalized = true;
|
| 1105 |
+
break;
|
| 1106 |
+
}
|
| 1107 |
+
if (validCount >= 20) break; // サンプル十分
|
| 1108 |
}
|
| 1109 |
}
|
| 1110 |
+
if (!isNormalized && validCount > 0) {
|
| 1111 |
+
// 多数がデータ解像度を超える場合はCanvas座標とみなす
|
| 1112 |
+
isCanvasUnit = (overCanvasCount / validCount) > 0.5;
|
| 1113 |
+
}
|
| 1114 |
}
|
| 1115 |
|
| 1116 |
+
// 🔍 デバッグログ: 手と顔の座標データ比較
|
| 1117 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [DEBUG ${rectType}] 座標データ分析:`, {
|
| 1118 |
+
rectType: rectType,
|
| 1119 |
+
isNormalized: isNormalized,
|
| 1120 |
+
isCanvasUnit: isCanvasUnit,
|
| 1121 |
+
sampleCoord: { x: sampleX, y: sampleY },
|
| 1122 |
+
originalRect: { x: originalRect.x, y: originalRect.y, width: originalRect.width, height: originalRect.height },
|
| 1123 |
+
newRect: { x: newRect.x, y: newRect.y, width: newRect.width, height: newRect.height },
|
| 1124 |
+
coordScale: { x: coordScaleX, y: coordScaleY },
|
| 1125 |
+
resolution: { data: dataResolutionWidth + 'x' + dataResolutionHeight, canvas: canvasWidth + 'x' + canvasHeight },
|
| 1126 |
+
keypointsLength: targetKeypoints.length
|
| 1127 |
+
});
|
| 1128 |
+
|
| 1129 |
|
| 1130 |
+
// 参照矩形は常にポイント群のBBox(Canvas座標)を使用して相対比を安定化
|
| 1131 |
+
let refRect = (function () {
|
| 1132 |
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
| 1133 |
+
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
| 1134 |
+
if (i + 2 >= originalTargetKeypoints.length) break;
|
| 1135 |
+
const conf = originalTargetKeypoints[i + 2];
|
| 1136 |
+
if (conf > 0.1) {
|
| 1137 |
+
let x = originalTargetKeypoints[i];
|
| 1138 |
+
let y = originalTargetKeypoints[i + 1];
|
| 1139 |
+
let cx, cy;
|
| 1140 |
+
if (isNormalized) {
|
| 1141 |
+
cx = (x * dataResolutionWidth) * coordScaleX;
|
| 1142 |
+
cy = (y * dataResolutionHeight) * coordScaleY;
|
| 1143 |
+
} else if (isCanvasUnit) {
|
| 1144 |
+
cx = x; cy = y;
|
| 1145 |
+
} else {
|
| 1146 |
+
cx = x * coordScaleX;
|
| 1147 |
+
cy = y * coordScaleY;
|
| 1148 |
+
}
|
| 1149 |
+
minX = Math.min(minX, cx);
|
| 1150 |
+
minY = Math.min(minY, cy);
|
| 1151 |
+
maxX = Math.max(maxX, cx);
|
| 1152 |
+
maxY = Math.max(maxY, cy);
|
| 1153 |
+
}
|
| 1154 |
+
}
|
| 1155 |
+
const margin = 8;
|
| 1156 |
+
const bx = isFinite(minX) ? minX - margin : originalRect.x;
|
| 1157 |
+
const by = isFinite(minY) ? minY - margin : originalRect.y;
|
| 1158 |
+
const bw = (isFinite(maxX) && isFinite(minX)) ? (maxX - minX) + margin * 2 : originalRect.width;
|
| 1159 |
+
const bh = (isFinite(maxY) && isFinite(minY)) ? (maxY - minY) + margin * 2 : originalRect.height;
|
| 1160 |
+
const rr = { x: bx, y: by, width: Math.max(1, bw), height: Math.max(1, bh) };
|
| 1161 |
+
if (window.poseEditorDebug.rect) console.log('🔧 [transformKeypointsDirectly] Using bbox as refRect', { refRect: rr, originalRect });
|
| 1162 |
+
return rr;
|
| 1163 |
+
})();
|
| 1164 |
+
|
| 1165 |
// キーポイントを一括変換(元データから毎回変換)
|
| 1166 |
+
let debugSampleIndex = -1;
|
| 1167 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
| 1168 |
if (i + 2 < originalTargetKeypoints.length && i + 2 < targetKeypoints.length) {
|
| 1169 |
const confidence = originalTargetKeypoints[i + 2];
|
|
|
|
| 1172 |
let x = originalTargetKeypoints[i];
|
| 1173 |
let y = originalTargetKeypoints[i + 1];
|
| 1174 |
|
| 1175 |
+
// データ座標→Canvas座標(入力の座標系に応じて)
|
| 1176 |
let canvasX, canvasY;
|
| 1177 |
if (isNormalized) {
|
| 1178 |
canvasX = (x * dataResolutionWidth) * coordScaleX;
|
| 1179 |
canvasY = (y * dataResolutionHeight) * coordScaleY;
|
| 1180 |
+
} else if (isCanvasUnit) {
|
| 1181 |
+
// 既にCanvas座標(過去のバグで混入している場合)
|
| 1182 |
+
canvasX = x;
|
| 1183 |
+
canvasY = y;
|
| 1184 |
} else {
|
| 1185 |
canvasX = x * coordScaleX;
|
| 1186 |
canvasY = y * coordScaleY;
|
| 1187 |
}
|
| 1188 |
|
| 1189 |
+
// 元矩形内での相対位置を計算(参照矩形に対して)
|
| 1190 |
+
let relativeX = (canvasX - refRect.x) / refRect.width;
|
| 1191 |
+
let relativeY = (canvasY - refRect.y) / refRect.height;
|
| 1192 |
+
// 安定化のために0-1へクランプ
|
| 1193 |
+
relativeX = Math.max(0, Math.min(1, relativeX));
|
| 1194 |
+
relativeY = Math.max(0, Math.min(1, relativeY));
|
| 1195 |
+
|
| 1196 |
// 新矩形での新しい位置を計算
|
| 1197 |
const newCanvasX = newRect.x + (relativeX * newRect.width);
|
| 1198 |
const newCanvasY = newRect.y + (relativeY * newRect.height);
|
| 1199 |
|
| 1200 |
+
// Canvas座標→データ座標に戻す(常にデータ座標で保存)
|
| 1201 |
+
let finalX, finalY;
|
| 1202 |
if (isNormalized) {
|
| 1203 |
const dataX = newCanvasX / coordScaleX;
|
| 1204 |
const dataY = newCanvasY / coordScaleY;
|
| 1205 |
+
finalX = dataX / dataResolutionWidth;
|
| 1206 |
+
finalY = dataY / dataResolutionHeight;
|
| 1207 |
+
targetKeypoints[i] = finalX;
|
| 1208 |
+
targetKeypoints[i + 1] = finalY;
|
| 1209 |
} else {
|
| 1210 |
+
finalX = newCanvasX / coordScaleX;
|
| 1211 |
+
finalY = newCanvasY / coordScaleY;
|
| 1212 |
+
targetKeypoints[i] = finalX;
|
| 1213 |
+
targetKeypoints[i + 1] = finalY;
|
| 1214 |
+
}
|
| 1215 |
+
|
| 1216 |
+
// 🔍 最初のサンプルポイントのみ詳細ログ出力
|
| 1217 |
+
if (debugSampleIndex < 0) {
|
| 1218 |
+
debugSampleIndex = i / 3;
|
| 1219 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [DEBUG ${rectType}] 座標変換詳細 (Point ${debugSampleIndex}):`, {
|
| 1220 |
+
originalData: { x: x, y: y },
|
| 1221 |
+
canvasCoord: { x: canvasX, y: canvasY },
|
| 1222 |
+
relative: { x: relativeX, y: relativeY },
|
| 1223 |
+
newCanvas: { x: newCanvasX, y: newCanvasY },
|
| 1224 |
+
finalData: { x: finalX, y: finalY },
|
| 1225 |
+
deltaCanvas: { x: newCanvasX - canvasX, y: newCanvasY - canvasY },
|
| 1226 |
+
deltaData: { x: finalX - x, y: finalY - y }
|
| 1227 |
+
});
|
| 1228 |
}
|
| 1229 |
}
|
| 1230 |
}
|
| 1231 |
}
|
| 1232 |
|
| 1233 |
+
// 🔍 手のデータの場合:変換前後の全データ比較ログ
|
| 1234 |
+
if (rectType === 'leftHand' || rectType === 'rightHand') {
|
| 1235 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [${rectType}] 変換前後データ比較:`, {
|
| 1236 |
+
rectType: rectType,
|
| 1237 |
+
originalFirstPoint: originalTargetKeypoints.length >= 3 ?
|
| 1238 |
+
{ x: originalTargetKeypoints[0], y: originalTargetKeypoints[1], conf: originalTargetKeypoints[2] } : null,
|
| 1239 |
+
transformedFirstPoint: targetKeypoints.length >= 3 ?
|
| 1240 |
+
{ x: targetKeypoints[0], y: targetKeypoints[1], conf: targetKeypoints[2] } : null,
|
| 1241 |
+
originalSample3Points: [
|
| 1242 |
+
originalTargetKeypoints.slice(0, 9), // 最初の3点
|
| 1243 |
+
originalTargetKeypoints.slice(18, 27), // 中間3点
|
| 1244 |
+
originalTargetKeypoints.slice(54, 63) // 最後の3点
|
| 1245 |
+
],
|
| 1246 |
+
transformedSample3Points: [
|
| 1247 |
+
targetKeypoints.slice(0, 9), // 最初の3点
|
| 1248 |
+
targetKeypoints.slice(18, 27), // 中間3点
|
| 1249 |
+
targetKeypoints.slice(54, 63) // 最後の3点
|
| 1250 |
+
],
|
| 1251 |
+
coordinateShift: targetKeypoints.length >= 3 ? {
|
| 1252 |
+
deltaX: targetKeypoints[0] - originalTargetKeypoints[0],
|
| 1253 |
+
deltaY: targetKeypoints[1] - originalTargetKeypoints[1]
|
| 1254 |
+
} : null
|
| 1255 |
+
});
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
}
|
| 1259 |
|
| 1260 |
// 🔧 元座標からキーポイントを移動(累積移動防止版)
|
|
|
|
| 1417 |
return;
|
| 1418 |
}
|
| 1419 |
|
| 1420 |
+
// Canvas→データ座標への変換係数
|
| 1421 |
+
let dataResolutionWidth = 512;
|
| 1422 |
+
let dataResolutionHeight = 512;
|
| 1423 |
+
if (currentPoseData.metadata && currentPoseData.metadata.resolution && Array.isArray(currentPoseData.metadata.resolution) && currentPoseData.metadata.resolution.length >= 2) {
|
| 1424 |
+
dataResolutionWidth = currentPoseData.metadata.resolution[0];
|
| 1425 |
+
dataResolutionHeight = currentPoseData.metadata.resolution[1];
|
| 1426 |
+
} else if (currentPoseData.resolution && Array.isArray(currentPoseData.resolution) && currentPoseData.resolution.length >= 2) {
|
| 1427 |
+
dataResolutionWidth = currentPoseData.resolution[0];
|
| 1428 |
+
dataResolutionHeight = currentPoseData.resolution[1];
|
| 1429 |
+
}
|
| 1430 |
+
const canvasWidth = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.width : 512;
|
| 1431 |
+
const canvasHeight = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.height : 512;
|
| 1432 |
+
const coordScaleX = canvasWidth / dataResolutionWidth;
|
| 1433 |
+
const coordScaleY = canvasHeight / dataResolutionHeight;
|
| 1434 |
+
|
| 1435 |
+
// Canvasの移動量→データ座標の移動量へ変換
|
| 1436 |
+
const dataDeltaX = deltaX / coordScaleX;
|
| 1437 |
+
const dataDeltaY = deltaY / coordScaleY;
|
| 1438 |
+
|
| 1439 |
+
// すべてのキーポイントを移動(データ座標系)
|
| 1440 |
for (let i = 0; i < keypoints.length; i += 3) {
|
| 1441 |
const confidence = keypoints[i + 2];
|
| 1442 |
|
| 1443 |
if (confidence > 0.1) { // 有効なキーポイントのみ移動
|
| 1444 |
+
keypoints[i] += dataDeltaX; // X座標(データ座標)
|
| 1445 |
+
keypoints[i + 1] += dataDeltaY; // Y座標(データ座標)
|
| 1446 |
}
|
| 1447 |
}
|
| 1448 |
|
|
|
|
| 1705 |
|
| 1706 |
// Gradioにデータ送信(refs互換・強制changeイベント版)
|
| 1707 |
function sendPoseDataToGradio() {
|
| 1708 |
+
if (window.poseEditorDebug.send) console.log('🔍 [sendPoseDataToGradio] Called');
|
| 1709 |
// グローバルposeDataを参照
|
| 1710 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1711 |
+
if (window.poseEditorDebug.send) console.log('🔍 [sendPoseDataToGradio] currentPoseData state:', {
|
| 1712 |
exists: !!currentPoseData,
|
| 1713 |
hasPeople: !!currentPoseData?.people,
|
| 1714 |
peopleCount: currentPoseData?.people?.length || 0,
|
|
|
|
| 1828 |
}
|
| 1829 |
|
| 1830 |
// 送信前の最終データ確認
|
| 1831 |
+
if (window.poseEditorDebug.send) console.log('🔍 [sendPoseDataToGradio] Final canvasData being sent:', {
|
| 1832 |
hasPeople: !!canvasData.people,
|
| 1833 |
peopleCount: canvasData.people?.length || 0,
|
| 1834 |
hasHandLeft: !!canvasData.people?.[0]?.hand_left_keypoints_2d,
|
|
|
|
| 1840 |
});
|
| 1841 |
|
| 1842 |
const jsonString = JSON.stringify(canvasData);
|
| 1843 |
+
if (window.poseEditorDebug.send) console.log('🎯 [sendPoseDataToGradio] People形式でGradioに送信:', canvasData.people.length, 'people');
|
| 1844 |
|
| 1845 |
// 専用の隠しテキストボックスを探して更新
|
| 1846 |
const jsUpdateTextbox = document.querySelector('#js_pose_update textarea');
|
|
|
|
| 1929 |
];
|
| 1930 |
|
| 1931 |
// 🔧 Issue #043: 手データのデバッグ情報追加
|
| 1932 |
+
if (window.poseEditorDebug.hands) console.log('🫳 Hand data debug:', {
|
| 1933 |
enableHands: enableHands,
|
| 1934 |
leftHandLength: handsDataForDrawing[0].length,
|
| 1935 |
rightHandLength: handsDataForDrawing[1].length,
|
|
|
|
| 1939 |
// 💖 people形式のみサポート、古いhands形式は削除
|
| 1940 |
} else {
|
| 1941 |
handsDataForDrawing = [[], []]; // 空の手データ
|
| 1942 |
+
if (window.poseEditorDebug.hands) console.log('🚫 No people data available for hands');
|
| 1943 |
}
|
| 1944 |
|
| 1945 |
if (handsDataForDrawing && handsDataForDrawing.length >= 2) {
|
| 1946 |
+
if (window.poseEditorDebug.hands) console.log('✅ Calling drawHands function');
|
| 1947 |
drawHands(handsDataForDrawing, originalRes, scaleX, scaleY);
|
| 1948 |
} else {
|
| 1949 |
+
if (window.poseEditorDebug.hands) console.log('❌ Invalid hands data for drawing');
|
| 1950 |
}
|
| 1951 |
} catch (error) {
|
| 1952 |
console.error("❌ Error drawing hands:", error);
|
|
|
|
| 2774 |
const confidence = handData[i + 2];
|
| 2775 |
|
| 2776 |
if (confidence > 0.3) { // refs互換の閾値
|
| 2777 |
+
// 🔧 手のキーポイント描画と完全に統一した座標変換
|
| 2778 |
+
// drawHands関数では scaledX = x * scaleX しているため、
|
| 2779 |
+
// 矩形計算でも同じ変換を適用して座標系を統一
|
| 2780 |
+
const finalX = x * scaleX;
|
| 2781 |
+
const finalY = y * scaleY;
|
| 2782 |
+
|
| 2783 |
+
minX = Math.min(minX, finalX);
|
| 2784 |
+
minY = Math.min(minY, finalY);
|
| 2785 |
+
maxX = Math.max(maxX, finalX);
|
| 2786 |
+
maxY = Math.max(maxY, finalY);
|
| 2787 |
validPointCount++;
|
| 2788 |
}
|
| 2789 |
}
|
|
|
|
| 2792 |
|
| 2793 |
// 10px余白付きで矩形返却(refs互換)
|
| 2794 |
const margin = 10;
|
| 2795 |
+
const rect = {
|
| 2796 |
x: minX - margin,
|
| 2797 |
y: minY - margin,
|
| 2798 |
width: (maxX - minX) + (margin * 2),
|
| 2799 |
height: (maxY - minY) + (margin * 2)
|
| 2800 |
};
|
| 2801 |
+
|
| 2802 |
+
// 🔍 デバッグログ: 手の矩形計算結果
|
| 2803 |
+
const canvas = window.poseEditorGlobals.canvas;
|
| 2804 |
+
|
| 2805 |
+
// 🔍 全キーポイントの詳細ログを追加
|
| 2806 |
+
const allPoints = [];
|
| 2807 |
+
for (let i = 0; i < handData.length; i += 3) {
|
| 2808 |
+
if (i + 2 < handData.length && handData[i + 2] > 0.3) {
|
| 2809 |
+
allPoints.push({
|
| 2810 |
+
index: i / 3,
|
| 2811 |
+
x: handData[i],
|
| 2812 |
+
y: handData[i + 1],
|
| 2813 |
+
confidence: handData[i + 2]
|
| 2814 |
+
});
|
| 2815 |
+
}
|
| 2816 |
+
}
|
| 2817 |
+
|
| 2818 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [calculateHandRect] 矩形計算結果:`, {
|
| 2819 |
+
rawBounds: { minX, minY, maxX, maxY },
|
| 2820 |
+
finalRect: rect,
|
| 2821 |
+
validPointCount,
|
| 2822 |
+
firstPoint: handData.length >= 3 ? { x: handData[0], y: handData[1], conf: handData[2] } : null,
|
| 2823 |
+
scaleFactors: { scaleX, scaleY },
|
| 2824 |
+
coordinateDetection: handData[0] > 10 ? 'ピクセル座標' : '正規化座標',
|
| 2825 |
+
canvasInfo: canvas ? {
|
| 2826 |
+
width: canvas.width,
|
| 2827 |
+
height: canvas.height,
|
| 2828 |
+
clientWidth: canvas.clientWidth,
|
| 2829 |
+
clientHeight: canvas.clientHeight,
|
| 2830 |
+
scale: canvas.width / canvas.clientWidth
|
| 2831 |
+
} : 'Canvas未取得'
|
| 2832 |
+
});
|
| 2833 |
+
|
| 2834 |
+
// 🔍 キーポイント座標を詳細表示
|
| 2835 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [calculateHandRect] 全有効キーポイント座標:`, allPoints);
|
| 2836 |
+
|
| 2837 |
+
// 🔍 座標の範囲をさらに詳細分析
|
| 2838 |
+
const xCoords = allPoints.map(p => p.x);
|
| 2839 |
+
const yCoords = allPoints.map(p => p.y);
|
| 2840 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [calculateHandRect] 座標範囲詳細:`, {
|
| 2841 |
+
xCoords: xCoords,
|
| 2842 |
+
yCoords: yCoords,
|
| 2843 |
+
xRange: `${Math.min(...xCoords).toFixed(2)} 〜 ${Math.max(...xCoords).toFixed(2)}`,
|
| 2844 |
+
yRange: `${Math.min(...yCoords).toFixed(2)} 〜 ${Math.max(...yCoords).toFixed(2)}`,
|
| 2845 |
+
xSpread: (Math.max(...xCoords) - Math.min(...xCoords)).toFixed(2),
|
| 2846 |
+
ySpread: (Math.max(...yCoords) - Math.min(...yCoords)).toFixed(2)
|
| 2847 |
+
});
|
| 2848 |
+
|
| 2849 |
+
return rect;
|
| 2850 |
}
|
| 2851 |
|
| 2852 |
// 🔧 顔キーポイントから矩形を計算(refs互換)
|
|
|
|
| 2888 |
function drawEditableRect(ctx, rect, color, id) {
|
| 2889 |
if (!rect) return;
|
| 2890 |
|
| 2891 |
+
// 🔍 デバッグログ: 実際の描画座標
|
| 2892 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [drawEditableRect] ${id} 描画座標:`, {
|
| 2893 |
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
| 2894 |
+
color,
|
| 2895 |
+
canvasTransform: ctx.getTransform()
|
| 2896 |
+
});
|
| 2897 |
+
|
| 2898 |
// 太め破線で矩形描画(refs互換)
|
| 2899 |
ctx.strokeStyle = color;
|
| 2900 |
ctx.lineWidth = 3;
|
|
|
|
| 3095 |
const controlPoint = window.poseEditorGlobals.draggedRectControl;
|
| 3096 |
const rectType = window.poseEditorGlobals.rectEditMode;
|
| 3097 |
|
| 3098 |
+
// 🔍 関数呼び出しログ
|
| 3099 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [updateRectControlDrag] Called:`, {
|
| 3100 |
+
rectType: rectType,
|
| 3101 |
+
controlPoint: controlPoint,
|
| 3102 |
+
mousePos: { x: mouseX, y: mouseY },
|
| 3103 |
+
hasOriginalRect: !!window.poseEditorGlobals.originalRect
|
| 3104 |
+
});
|
| 3105 |
|
| 3106 |
if (!controlPoint) {
|
| 3107 |
+
if (window.poseEditorDebug.rect) console.log(`⚠️ [updateRectControlDrag] No controlPoint, exiting`);
|
| 3108 |
return;
|
| 3109 |
}
|
| 3110 |
|
| 3111 |
if (!rectType) {
|
| 3112 |
+
if (window.poseEditorDebug.rect) console.log(`⚠️ [updateRectControlDrag] No rectType, exiting`);
|
| 3113 |
return;
|
| 3114 |
}
|
| 3115 |
|
|
|
|
| 3179 |
// 🔧 矩形を更新(累積変形防止のため直接設定)
|
| 3180 |
window.poseEditorGlobals.currentRects[rectType] = newRect;
|
| 3181 |
|
| 3182 |
+
// 🔍 矩形変換ログ
|
| 3183 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [updateRectControlDrag] 矩形変換:`, {
|
| 3184 |
+
rectType: rectType,
|
| 3185 |
+
controlPosition: controlPoint.position,
|
| 3186 |
+
originalRect: originalRect,
|
| 3187 |
+
newRect: newRect,
|
| 3188 |
+
mousePos: { x: mouseX, y: mouseY },
|
| 3189 |
+
rectDelta: {
|
| 3190 |
+
x: newRect.x - originalRect.x,
|
| 3191 |
+
y: newRect.y - originalRect.y,
|
| 3192 |
+
width: newRect.width - originalRect.width,
|
| 3193 |
+
height: newRect.height - originalRect.height
|
| 3194 |
+
}
|
| 3195 |
+
});
|
| 3196 |
|
| 3197 |
// 🔧 手・顔キーポイントの座標も更新(直接矩形変換版)
|
| 3198 |
if (controlPoint) {
|
| 3199 |
+
if (window.poseEditorDebug.rect) console.log(`🔍 [updateRectControlDrag] Calling transformKeypointsDirectly...`);
|
| 3200 |
transformKeypointsDirectly(rectType, originalRect, newRect);
|
| 3201 |
}
|
| 3202 |
|
|
|
|
| 3238 |
// 💥 ベースは初回のみ保存!編集済みデータで上書きしない
|
| 3239 |
if (!window.poseEditorGlobals.baseOriginalKeypoints) {
|
| 3240 |
window.poseEditorGlobals.baseOriginalKeypoints = JSON.parse(JSON.stringify(currentPoseData));
|
| 3241 |
+
if (window.poseEditorDebug.rect) console.log('💖 [initializeRectEditInfo] Base original keypoints saved for FIRST editing session');
|
| 3242 |
|
| 3243 |
// ベースデータの詳細確認
|
| 3244 |
+
if (window.poseEditorDebug.rect) console.log('🔍 [initializeRectEditInfo] Base data details:', {
|
| 3245 |
hasHandLeft: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.hand_left_keypoints_2d,
|
| 3246 |
hasHandRight: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.hand_right_keypoints_2d,
|
| 3247 |
hasFace: !!window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.face_keypoints_2d,
|
|
|
|
| 3250 |
faceLength: window.poseEditorGlobals.baseOriginalKeypoints.people?.[0]?.face_keypoints_2d?.length || 0
|
| 3251 |
});
|
| 3252 |
} else {
|
| 3253 |
+
if (window.poseEditorDebug.rect) console.log('💖 [initializeRectEditInfo] Base original keypoints already exists - keeping original data');
|
| 3254 |
}
|
| 3255 |
|
| 3256 |
// 作業用は常にベースからコピー(編集済みデータではなく!)
|
| 3257 |
window.poseEditorGlobals.originalKeypoints = JSON.parse(JSON.stringify(window.poseEditorGlobals.baseOriginalKeypoints));
|
| 3258 |
+
if (window.poseEditorDebug.rect) console.log('💖 [initializeRectEditInfo] Working original keypoints restored from base for current editing session');
|
| 3259 |
|
| 3260 |
// 作業用データの詳細確認
|
| 3261 |
+
if (window.poseEditorDebug.rect) console.log('🔍 [initializeRectEditInfo] Working data details:', {
|
| 3262 |
hasHandLeft: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.hand_left_keypoints_2d,
|
| 3263 |
hasHandRight: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.hand_right_keypoints_2d,
|
| 3264 |
hasFace: !!window.poseEditorGlobals.originalKeypoints.people?.[0]?.face_keypoints_2d,
|
|
|
|
| 3811 |
ctx.globalAlpha = 1.0; // 不透明に戻す
|
| 3812 |
}
|
| 3813 |
|
| 3814 |
+
// 🎨 pose_editor.js initialization complete
|