Commit
·
65c2919
1
Parent(s):
be7e89a
feat: 通知機能の改善
Browse files- 通知設定に情報通知の表示時間とエラー通知の表示時間を追加
- 成功通知、警告通知、エラー通知の各関数で表示時間を設定
- static/pose_editor.js +25 -147
- utils/notifications.py +8 -4
static/pose_editor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
// Canvas操作用JavaScript for dwpose-editor
|
| 2 |
|
| 3 |
-
//
|
| 4 |
window.poseEditorGlobals = {
|
| 5 |
canvas: null,
|
| 6 |
ctx: null,
|
|
@@ -59,41 +59,19 @@ let isDragging = false;
|
|
| 59 |
let draggedKeypoint = -1;
|
| 60 |
let dragOffset = { x: 0, y: 0 };
|
| 61 |
|
| 62 |
-
//
|
| 63 |
-
function debugLog(message) {
|
| 64 |
-
// 重要なログのみ出力
|
| 65 |
-
const importantPatterns = [
|
| 66 |
-
/initializePoseEditor/,
|
| 67 |
-
/Failed/,
|
| 68 |
-
/Error/,
|
| 69 |
-
/Entered rectangle edit mode/,
|
| 70 |
-
/Switched to rectangle edit mode/,
|
| 71 |
-
/Drag started/,
|
| 72 |
-
/drag ended/,
|
| 73 |
-
/Canvas initialized/,
|
| 74 |
-
/No poseData available/
|
| 75 |
-
];
|
| 76 |
-
|
| 77 |
-
const shouldLog = importantPatterns.some(pattern => pattern.test(message));
|
| 78 |
-
if (shouldLog) {
|
| 79 |
-
console.log(`[DWPose Editor] ${new Date().toISOString()} - ${message}`);
|
| 80 |
-
}
|
| 81 |
-
}
|
| 82 |
|
| 83 |
-
// Canvas
|
| 84 |
function initializePoseEditor() {
|
| 85 |
-
debugLog("initializePoseEditor called");
|
| 86 |
|
| 87 |
canvas = document.getElementById('pose_canvas');
|
| 88 |
if (!canvas) {
|
| 89 |
-
debugLog("Canvas not found, retrying...");
|
| 90 |
setTimeout(initializePoseEditor, 100);
|
| 91 |
return;
|
| 92 |
}
|
| 93 |
|
| 94 |
ctx = canvas.getContext('2d');
|
| 95 |
if (!ctx) {
|
| 96 |
-
debugLog("Failed to get 2d context");
|
| 97 |
return;
|
| 98 |
}
|
| 99 |
|
|
@@ -116,7 +94,6 @@ function initializePoseEditor() {
|
|
| 116 |
canvas.style.cursor = 'default';
|
| 117 |
|
| 118 |
isInitialized = true;
|
| 119 |
-
debugLog("Canvas initialized successfully");
|
| 120 |
notifyCanvasStateChange('initialized');
|
| 121 |
|
| 122 |
// ドラッグイベントを設定(refs互換)
|
|
@@ -174,10 +151,8 @@ function showCanvasError(message) {
|
|
| 174 |
|
| 175 |
// ドラッグイベント設定(refs互換)
|
| 176 |
function setupDragEvents() {
|
| 177 |
-
debugLog("setupDragEvents called");
|
| 178 |
|
| 179 |
if (!canvas) {
|
| 180 |
-
debugLog("Canvas not available for drag setup");
|
| 181 |
return;
|
| 182 |
}
|
| 183 |
|
|
@@ -191,14 +166,12 @@ function setupDragEvents() {
|
|
| 191 |
canvas.addEventListener('click', function(event) {
|
| 192 |
});
|
| 193 |
|
| 194 |
-
debugLog("Drag events setup completed");
|
| 195 |
}
|
| 196 |
|
| 197 |
// 🔧 Gradioチェックボックス監視設定
|
| 198 |
function setupGradioCheckboxListeners() {
|
| 199 |
// チェックボックスとラジオボタン要素を探す(少し待ってから)
|
| 200 |
setTimeout(() => {
|
| 201 |
-
debugLog("🔍 Setting up Gradio component listeners...");
|
| 202 |
|
| 203 |
// チェックボックス監視
|
| 204 |
const allCheckboxes = document.querySelectorAll('input[type="checkbox"]');
|
|
@@ -219,14 +192,12 @@ function setupGradioCheckboxListeners() {
|
|
| 219 |
|
| 220 |
// ラジオボタン監視(編集モード)
|
| 221 |
const allRadios = document.querySelectorAll('input[type="radio"]');
|
| 222 |
-
debugLog(`🔍 Found ${allRadios.length} radio buttons`);
|
| 223 |
|
| 224 |
allRadios.forEach((radio, index) => {
|
| 225 |
// 親要素のテキストから簡易モード・詳細モードを特定
|
| 226 |
const parentText = radio.parentElement?.textContent?.trim() || '';
|
| 227 |
const grandParentText = radio.parentElement?.parentElement?.textContent?.trim() || '';
|
| 228 |
|
| 229 |
-
debugLog(`🔍 Radio ${index}: value="${radio.value}", parent="${parentText}", grandparent="${grandParentText}"`);
|
| 230 |
|
| 231 |
if (parentText.includes('簡易モード') || parentText.includes('詳細モード') ||
|
| 232 |
grandParentText.includes('編集モード')) {
|
|
@@ -310,12 +281,10 @@ function findNearestKeypoint(mouseX, mouseY, maxDistance = 20) {
|
|
| 310 |
}
|
| 311 |
|
| 312 |
if (!currentPoseData.bodies) {
|
| 313 |
-
debugLog("No poseData.bodies available");
|
| 314 |
return -1;
|
| 315 |
}
|
| 316 |
|
| 317 |
if (!currentPoseData.bodies.candidate) {
|
| 318 |
-
debugLog("No poseData.bodies.candidate available");
|
| 319 |
return -1;
|
| 320 |
}
|
| 321 |
|
|
@@ -457,7 +426,6 @@ function findNearestKeypointInDetailMode(mouseX, mouseY, maxDistance = 15) {
|
|
| 457 |
// 詳細モード用:最寄りのキーポイント検索(手・顔の個別キーポイント対応)
|
| 458 |
function findNearestKeypointInDetailMode(clickX, clickY) {
|
| 459 |
if (!poseData || !poseData.people || poseData.people.length === 0) {
|
| 460 |
-
debugLog("❌ No poseData for detail mode keypoint search");
|
| 461 |
return null;
|
| 462 |
}
|
| 463 |
|
|
@@ -625,7 +593,6 @@ function handleMouseDown(event) {
|
|
| 625 |
const currentRect = window.poseEditorGlobals.currentRects[rectType];
|
| 626 |
if (currentRect) {
|
| 627 |
window.poseEditorGlobals.originalRect = { ...currentRect };
|
| 628 |
-
debugLog(`🔧 Original rect saved for control point drag: ${rectType}`, window.poseEditorGlobals.originalRect);
|
| 629 |
}
|
| 630 |
|
| 631 |
// コントロールポイントドラッグ(リサイズ)
|
|
@@ -641,14 +608,12 @@ function handleMouseDown(event) {
|
|
| 641 |
window.poseEditorGlobals.draggedRect = rectType;
|
| 642 |
window.poseEditorGlobals.dragStartPos = { x: mousePos.x, y: mousePos.y };
|
| 643 |
isDragging = true;
|
| 644 |
-
debugLog(`🔧 Rectangle move drag started: ${rectType}`);
|
| 645 |
return;
|
| 646 |
}
|
| 647 |
|
| 648 |
// 他の矩形クリック → 切り替え
|
| 649 |
if (rectType && rectType !== window.poseEditorGlobals.rectEditMode) {
|
| 650 |
window.poseEditorGlobals.rectEditMode = rectType;
|
| 651 |
-
debugLog(`Switched to rectangle edit mode: ${rectType}`);
|
| 652 |
// 再描画で新しい矩形のコントロールポイントを表示
|
| 653 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 654 |
if (currentPoseData) {
|
|
@@ -680,7 +645,6 @@ function handleMouseDown(event) {
|
|
| 680 |
window.poseEditorGlobals.draggedRect = null;
|
| 681 |
window.poseEditorGlobals.dragStartPos = { x: 0, y: 0 };
|
| 682 |
|
| 683 |
-
debugLog("🔧 Rectangle edit mode ended - all editing states cleared for next editing session");
|
| 684 |
|
| 685 |
// 🚀 矩形位置を保持したまま再描画(再計算させない)
|
| 686 |
if (currentPoseData) {
|
|
@@ -695,12 +659,10 @@ function handleMouseDown(event) {
|
|
| 695 |
if (rectType) {
|
| 696 |
// 矩形モード切り替え時の処理
|
| 697 |
if (window.poseEditorGlobals.rectEditMode !== rectType) {
|
| 698 |
-
debugLog(`💖 Switching rectangle edit mode: ${window.poseEditorGlobals.rectEditMode} → ${rectType}`);
|
| 699 |
window.poseEditorGlobals.rectEditMode = rectType;
|
| 700 |
}
|
| 701 |
|
| 702 |
window.poseEditorGlobals.rectEditModeActive = true;
|
| 703 |
-
debugLog(`💖 Entered rectangle edit mode: ${rectType}`);
|
| 704 |
|
| 705 |
// 🔧 rectEditInfo全体を初期化(全矩形タイプ対応)
|
| 706 |
initializeRectEditInfo();
|
|
@@ -756,10 +718,8 @@ function handleMouseDown(event) {
|
|
| 756 |
}
|
| 757 |
}
|
| 758 |
|
| 759 |
-
debugLog(`Drag started: keypoint ${keypointIndex} at (${mousePos.x}, ${mousePos.y})`);
|
| 760 |
canvas.style.cursor = 'grabbing';
|
| 761 |
} else {
|
| 762 |
-
debugLog("No keypoint found near mouse position");
|
| 763 |
}
|
| 764 |
}
|
| 765 |
}
|
|
@@ -770,7 +730,6 @@ function updateDetailKeypointPosition(detailKeypoint, canvasX, canvasY) {
|
|
| 770 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 771 |
|
| 772 |
if (!currentPoseData) {
|
| 773 |
-
debugLog("No poseData available for detail keypoint update");
|
| 774 |
return;
|
| 775 |
}
|
| 776 |
|
|
@@ -802,7 +761,6 @@ function updateDetailKeypointPosition(detailKeypoint, canvasX, canvasY) {
|
|
| 802 |
handData[arrayIndex] = dataX;
|
| 803 |
handData[arrayIndex + 1] = dataY;
|
| 804 |
// 信頼度は維持
|
| 805 |
-
debugLog(`🫳 Left hand keypoint ${detailKeypoint.index} updated to (${dataX}, ${dataY})`);
|
| 806 |
|
| 807 |
// 元のhandsデータも同期更新
|
| 808 |
syncHandsToOriginal(currentPoseData, 'left', handData);
|
|
@@ -830,7 +788,6 @@ function updateDetailKeypointPosition(detailKeypoint, canvasX, canvasY) {
|
|
| 830 |
handData[arrayIndex] = dataX;
|
| 831 |
handData[arrayIndex + 1] = dataY;
|
| 832 |
// 信頼度は維持
|
| 833 |
-
debugLog(`🫳 Right hand keypoint ${detailKeypoint.index} updated to (${dataX}, ${dataY})`);
|
| 834 |
|
| 835 |
// 元のhandsデータも同期更新
|
| 836 |
syncHandsToOriginal(currentPoseData, 'right', handData);
|
|
@@ -858,7 +815,6 @@ function updateDetailKeypointPosition(detailKeypoint, canvasX, canvasY) {
|
|
| 858 |
faceData[arrayIndex] = dataX;
|
| 859 |
faceData[arrayIndex + 1] = dataY;
|
| 860 |
// 信頼度は維持
|
| 861 |
-
debugLog(`😊 Face keypoint ${detailKeypoint.index} updated to (${dataX}, ${dataY})`);
|
| 862 |
|
| 863 |
// 🚀 元のfacesデータも同期更新
|
| 864 |
syncFacesToOriginal(currentPoseData, faceData);
|
|
@@ -881,7 +837,6 @@ function syncHandsToOriginal(poseData, handType, handData) {
|
|
| 881 |
const handIndex = handType === 'left' ? 0 : 1;
|
| 882 |
if (handIndex < poseData.hands.length) {
|
| 883 |
poseData.hands[handIndex] = [...handData];
|
| 884 |
-
debugLog(`🔄 Hand ${handType} synced to original hands data`);
|
| 885 |
}
|
| 886 |
}
|
| 887 |
|
|
@@ -891,7 +846,6 @@ function syncFacesToOriginal(poseData, faceData) {
|
|
| 891 |
|
| 892 |
if (poseData.faces.length > 0) {
|
| 893 |
poseData.faces[0] = [...faceData];
|
| 894 |
-
debugLog(`🔄 Face synced to original faces data`);
|
| 895 |
}
|
| 896 |
}
|
| 897 |
|
|
@@ -1047,7 +1001,6 @@ function transformKeypointsInRect(control, newMouseX, newMouseY) {
|
|
| 1047 |
// 11. 矩形情報を更新
|
| 1048 |
control.rect = newRect;
|
| 1049 |
|
| 1050 |
-
debugLog(`🎯 Transformed ${fieldName} keypoints: scale=(${scaleX.toFixed(2)}, ${scaleY.toFixed(2)})`);
|
| 1051 |
}
|
| 1052 |
|
| 1053 |
// 🔧 直接矩形変換関数(シンプル版)
|
|
@@ -1056,12 +1009,10 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1056 |
const originalKeypoints = window.poseEditorGlobals.originalKeypoints;
|
| 1057 |
|
| 1058 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
| 1059 |
-
debugLog("❌ transformKeypointsDirectly: No pose data available");
|
| 1060 |
return;
|
| 1061 |
}
|
| 1062 |
|
| 1063 |
if (!originalKeypoints || !originalKeypoints.people || !originalKeypoints.people[0]) {
|
| 1064 |
-
debugLog("❌ transformKeypointsDirectly: No original keypoints available");
|
| 1065 |
return;
|
| 1066 |
}
|
| 1067 |
|
|
@@ -1089,23 +1040,17 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1089 |
fieldName = 'hand_right_keypoints_2d';
|
| 1090 |
break;
|
| 1091 |
default:
|
| 1092 |
-
debugLog(`❌ transformKeypointsDirectly: Unknown rectType ${rectType}`);
|
| 1093 |
return;
|
| 1094 |
}
|
| 1095 |
|
| 1096 |
if (!targetKeypoints || !Array.isArray(targetKeypoints)) {
|
| 1097 |
-
debugLog(`❌ transformKeypointsDirectly: No keypoints for ${fieldName}`);
|
| 1098 |
-
debugLog(`🔍 Person keys: ${Object.keys(person)}`);
|
| 1099 |
return;
|
| 1100 |
}
|
| 1101 |
|
| 1102 |
if (!originalTargetKeypoints || !Array.isArray(originalTargetKeypoints)) {
|
| 1103 |
-
debugLog(`❌ transformKeypointsDirectly: No original keypoints for ${fieldName}`);
|
| 1104 |
-
debugLog(`🔍 Original person keys: ${Object.keys(originalPerson)}`);
|
| 1105 |
return;
|
| 1106 |
}
|
| 1107 |
|
| 1108 |
-
debugLog(`✅ transformKeypointsDirectly: Found ${fieldName} - current: ${targetKeypoints.length}, original: ${originalTargetKeypoints.length}`);
|
| 1109 |
|
| 1110 |
// 座標変換設定
|
| 1111 |
const canvasWidth = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.width : 512;
|
|
@@ -1136,7 +1081,6 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1136 |
}
|
| 1137 |
}
|
| 1138 |
|
| 1139 |
-
debugLog(`🔧 transformKeypointsDirectly: ${fieldName}, original rect: ${JSON.stringify(originalRect)}, new rect: ${JSON.stringify(newRect)}, normalized: ${isNormalized}`);
|
| 1140 |
|
| 1141 |
// キーポイントを一括変換(元データから毎回変換)
|
| 1142 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
|
@@ -1179,7 +1123,6 @@ function transformKeypointsDirectly(rectType, originalRect, newRect) {
|
|
| 1179 |
}
|
| 1180 |
}
|
| 1181 |
|
| 1182 |
-
debugLog(`✅ transformKeypointsDirectly: Transformed ${fieldName} keypoints successfully`);
|
| 1183 |
}
|
| 1184 |
|
| 1185 |
// 🔧 元座標からキーポイントを移動(累積移動防止版)
|
|
@@ -1196,12 +1139,10 @@ function moveKeypointsFromOriginal(rectType, totalDeltaX, totalDeltaY) {
|
|
| 1196 |
const originalKeypoints = window.poseEditorGlobals.originalKeypoints;
|
| 1197 |
|
| 1198 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
| 1199 |
-
debugLog("❌ moveKeypointsFromOriginal: No current pose data available");
|
| 1200 |
return;
|
| 1201 |
}
|
| 1202 |
|
| 1203 |
if (!originalKeypoints || !originalKeypoints.people || !originalKeypoints.people[0]) {
|
| 1204 |
-
debugLog("❌ moveKeypointsFromOriginal: No original keypoints available");
|
| 1205 |
return;
|
| 1206 |
}
|
| 1207 |
|
|
@@ -1229,17 +1170,14 @@ function moveKeypointsFromOriginal(rectType, totalDeltaX, totalDeltaY) {
|
|
| 1229 |
fieldName = 'hand_right_keypoints_2d';
|
| 1230 |
break;
|
| 1231 |
default:
|
| 1232 |
-
debugLog(`❌ moveKeypointsFromOriginal: Unknown rectType ${rectType}`);
|
| 1233 |
return;
|
| 1234 |
}
|
| 1235 |
|
| 1236 |
if (!targetKeypoints || !Array.isArray(targetKeypoints)) {
|
| 1237 |
-
debugLog(`❌ moveKeypointsFromOriginal: No current keypoints for ${fieldName}`);
|
| 1238 |
return;
|
| 1239 |
}
|
| 1240 |
|
| 1241 |
if (!originalTargetKeypoints || !Array.isArray(originalTargetKeypoints)) {
|
| 1242 |
-
debugLog(`❌ moveKeypointsFromOriginal: No original keypoints for ${fieldName}`);
|
| 1243 |
return;
|
| 1244 |
}
|
| 1245 |
|
|
@@ -1272,7 +1210,6 @@ function moveKeypointsFromOriginal(rectType, totalDeltaX, totalDeltaY) {
|
|
| 1272 |
}
|
| 1273 |
}
|
| 1274 |
|
| 1275 |
-
debugLog(`🔧 moveKeypointsFromOriginal: ${fieldName}, delta=(${totalDeltaX}, ${totalDeltaY}), normalized: ${isNormalized}`);
|
| 1276 |
|
| 1277 |
// キーポイントを移動(元データから移動量を適用)
|
| 1278 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
|
@@ -1311,20 +1248,17 @@ function moveKeypointsFromOriginal(rectType, totalDeltaX, totalDeltaY) {
|
|
| 1311 |
}
|
| 1312 |
}
|
| 1313 |
|
| 1314 |
-
debugLog(`✅ moveKeypointsFromOriginal: Moved ${fieldName} keypoints successfully`);
|
| 1315 |
}
|
| 1316 |
|
| 1317 |
// 🔧 矩形移動に合わせてキーポイントを移動(refs互換版)
|
| 1318 |
function moveKeypointsWithRect(rectType, deltaX, deltaY) {
|
| 1319 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1320 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
| 1321 |
-
debugLog("❌ moveKeypointsWithRect: No current pose data available");
|
| 1322 |
return;
|
| 1323 |
}
|
| 1324 |
|
| 1325 |
const person = currentPoseData.people[0];
|
| 1326 |
|
| 1327 |
-
debugLog(`🔧 キーポイント移動: rectType=${rectType}, delta=(${deltaX}, ${deltaY})`);
|
| 1328 |
|
| 1329 |
// 対応するキーポイントを取得
|
| 1330 |
let keypoints = null;
|
|
@@ -1344,12 +1278,10 @@ function moveKeypointsWithRect(rectType, deltaX, deltaY) {
|
|
| 1344 |
fieldName = 'hand_right_keypoints_2d';
|
| 1345 |
break;
|
| 1346 |
default:
|
| 1347 |
-
debugLog(`❌ moveKeypointsWithRect: Unknown rectType ${rectType}`);
|
| 1348 |
return;
|
| 1349 |
}
|
| 1350 |
|
| 1351 |
if (!keypoints || !Array.isArray(keypoints)) {
|
| 1352 |
-
debugLog(`❌ moveKeypointsWithRect: No keypoints for ${fieldName}`);
|
| 1353 |
return;
|
| 1354 |
}
|
| 1355 |
|
|
@@ -1366,7 +1298,6 @@ function moveKeypointsWithRect(rectType, deltaX, deltaY) {
|
|
| 1366 |
// データを更新
|
| 1367 |
person[fieldName] = keypoints;
|
| 1368 |
|
| 1369 |
-
debugLog(`✅ moveKeypointsWithRect: Moved ${fieldName} keypoints successfully`);
|
| 1370 |
}
|
| 1371 |
|
| 1372 |
// 🚀 全データをエクスポート用フォーマットに同期(refs互換)
|
|
@@ -1396,7 +1327,6 @@ function syncAllDataToExportFormat(poseData) {
|
|
| 1396 |
}
|
| 1397 |
}
|
| 1398 |
|
| 1399 |
-
debugLog(`🔄 All data synced to export format - People: ${poseData.people ? poseData.people.length : 0}`);
|
| 1400 |
}
|
| 1401 |
|
| 1402 |
// マウス移動処理(refs互換 + 矩形編集対応)
|
|
@@ -1427,16 +1357,12 @@ function handleMouseMove(event) {
|
|
| 1427 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1428 |
if (currentPoseData) {
|
| 1429 |
// 🫳😊 手と顔データが存在することを確認してから描画
|
| 1430 |
-
debugLog(`🎨 Real-time redraw: hands=${!!(currentPoseData.people && currentPoseData.people[0] && (currentPoseData.people[0].hand_left_keypoints_2d || currentPoseData.people[0].hand_right_keypoints_2d))}, faces=${!!currentPoseData.faces}`);
|
| 1431 |
if (currentPoseData.people && currentPoseData.people[0]) {
|
| 1432 |
const person = currentPoseData.people[0];
|
| 1433 |
-
debugLog(`🎨 People data: left_hand=${!!person.hand_left_keypoints_2d}, right_hand=${!!person.hand_right_keypoints_2d}, face=${!!person.face_keypoints_2d}`);
|
| 1434 |
}
|
| 1435 |
|
| 1436 |
drawPose(currentPoseData, window.poseEditorGlobals.enableHands, window.poseEditorGlobals.enableFace);
|
| 1437 |
-
debugLog("✅ Real-time redraw completed after keypoint update");
|
| 1438 |
} else {
|
| 1439 |
-
debugLog("⚠️ No pose data available for real-time redraw");
|
| 1440 |
}
|
| 1441 |
} catch (error) {
|
| 1442 |
console.error("❌ Error in real-time redraw:", error);
|
|
@@ -1456,12 +1382,9 @@ function handleMouseMove(event) {
|
|
| 1456 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1457 |
if (currentPoseData) {
|
| 1458 |
// 🫳😊 手と顔データの存在確認
|
| 1459 |
-
debugLog(`🎨 Highlighted redraw: hands=${!!(currentPoseData.people && currentPoseData.people[0] && (currentPoseData.people[0].hand_left_keypoints_2d || currentPoseData.people[0].hand_right_keypoints_2d))}, faces=${!!currentPoseData.faces}`);
|
| 1460 |
|
| 1461 |
drawPose(currentPoseData, window.poseEditorGlobals.enableHands, window.poseEditorGlobals.enableFace, draggedKeypoint);
|
| 1462 |
-
debugLog("✅ Real-time redraw with highlight completed");
|
| 1463 |
} else {
|
| 1464 |
-
debugLog("⚠️ No pose data available for highlighted redraw");
|
| 1465 |
}
|
| 1466 |
} catch (error) {
|
| 1467 |
console.error("❌ Error in highlighted redraw:", error);
|
|
@@ -1505,15 +1428,12 @@ function getControlPointCursor(position) {
|
|
| 1505 |
function handleMouseUp(event) {
|
| 1506 |
if (isDragging) {
|
| 1507 |
if (window.poseEditorGlobals.draggedRectControl) {
|
| 1508 |
-
debugLog(`Control point drag ended: ${window.poseEditorGlobals.draggedRectControl.position}`);
|
| 1509 |
window.poseEditorGlobals.draggedRectControl = null;
|
| 1510 |
window.poseEditorGlobals.originalRect = null; // 🔧 元の矩形情報をクリア
|
| 1511 |
} else if (window.poseEditorGlobals.draggedRect) {
|
| 1512 |
-
debugLog(`Rectangle move drag ended: ${window.poseEditorGlobals.draggedRect}`);
|
| 1513 |
window.poseEditorGlobals.draggedRect = null;
|
| 1514 |
} else if (window.poseEditorGlobals.draggedDetailKeypoint) {
|
| 1515 |
// 🎯 詳細モードキーポイントドラッグ終了
|
| 1516 |
-
debugLog(`Detail keypoint drag ended: ${window.poseEditorGlobals.draggedDetailKeypoint.type} ${window.poseEditorGlobals.draggedDetailKeypoint.index}`);
|
| 1517 |
window.poseEditorGlobals.draggedDetailKeypoint = null;
|
| 1518 |
} else if (draggedKeypoint >= 0) {
|
| 1519 |
draggedKeypoint = -1;
|
|
@@ -1533,7 +1453,6 @@ function updateKeypointPosition(keypointIndex, canvasX, canvasY) {
|
|
| 1533 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1534 |
|
| 1535 |
if (!currentPoseData || !currentPoseData.bodies || !currentPoseData.bodies.candidate) {
|
| 1536 |
-
debugLog("No poseData available for keypoint update");
|
| 1537 |
return;
|
| 1538 |
}
|
| 1539 |
|
|
@@ -1583,10 +1502,8 @@ function updateKeypointPosition(keypointIndex, canvasX, canvasY) {
|
|
| 1583 |
syncFacesToOriginal(currentPoseData, person.face_keypoints_2d);
|
| 1584 |
}
|
| 1585 |
|
| 1586 |
-
debugLog(`🔄 Hands and face data synced after keypoint update`);
|
| 1587 |
}
|
| 1588 |
|
| 1589 |
-
debugLog(`🎯 Keypoint ${keypointIndex} updated to (${clampedDataX}, ${clampedDataY})`);
|
| 1590 |
}
|
| 1591 |
|
| 1592 |
// 🚀 bodies.candidateからpeople.pose_keypoints_2dに同期(refs互換)
|
|
@@ -1625,18 +1542,14 @@ function syncBodiesToPeople(poseData) {
|
|
| 1625 |
// 🫳😊 既存の手と顔データを保持(ボディ編集時に消失しないように)
|
| 1626 |
if (!person.hand_left_keypoints_2d && poseData.hands && poseData.hands[0]) {
|
| 1627 |
person.hand_left_keypoints_2d = poseData.hands[0];
|
| 1628 |
-
debugLog(`🫳 Left hand data preserved in people format`);
|
| 1629 |
}
|
| 1630 |
if (!person.hand_right_keypoints_2d && poseData.hands && poseData.hands[1]) {
|
| 1631 |
person.hand_right_keypoints_2d = poseData.hands[1];
|
| 1632 |
-
debugLog(`🫳 Right hand data preserved in people format`);
|
| 1633 |
}
|
| 1634 |
if (!person.face_keypoints_2d && poseData.faces && poseData.faces.length > 0) {
|
| 1635 |
person.face_keypoints_2d = poseData.faces[0] || poseData.faces;
|
| 1636 |
-
debugLog(`😊 Face data preserved in people format`);
|
| 1637 |
}
|
| 1638 |
|
| 1639 |
-
debugLog(`🔄 Bodies synced to people: ${pose_keypoints_2d.length / 3} keypoints, hands preserved: ${!!person.hand_left_keypoints_2d}/${!!person.hand_right_keypoints_2d}, face: ${!!person.face_keypoints_2d}`);
|
| 1640 |
}
|
| 1641 |
|
| 1642 |
// Gradioにデータ送信(refs互換・強制changeイベント版)
|
|
@@ -1654,20 +1567,17 @@ function sendPoseDataToGradio() {
|
|
| 1654 |
});
|
| 1655 |
|
| 1656 |
if (!currentPoseData) {
|
| 1657 |
-
debugLog("No poseData available for Gradio send");
|
| 1658 |
return;
|
| 1659 |
}
|
| 1660 |
|
| 1661 |
// Issue 038: JavaScript側更新フラグチェック(refs issue043準拠)
|
| 1662 |
if (window.poseEditorGlobals && window.poseEditorGlobals.isUpdating) {
|
| 1663 |
-
debugLog("⚠️ JavaScript更新処理中のため、Gradio送信をスキップ");
|
| 1664 |
return;
|
| 1665 |
}
|
| 1666 |
|
| 1667 |
// 処理開始フラグを立てる
|
| 1668 |
if (window.poseEditorGlobals) {
|
| 1669 |
window.poseEditorGlobals.isUpdating = true;
|
| 1670 |
-
debugLog("🔒 JavaScript更新フラグ設定: isUpdating=true");
|
| 1671 |
}
|
| 1672 |
|
| 1673 |
try {
|
|
@@ -1715,29 +1625,22 @@ function sendPoseDataToGradio() {
|
|
| 1715 |
// 💖 手と顔データを確実に設定(配列コピーで安全に保持)
|
| 1716 |
if (leftHandData && leftHandData.length > 0) {
|
| 1717 |
person.hand_left_keypoints_2d = [...leftHandData]; // 配列コピー
|
| 1718 |
-
debugLog(`🫳 Left hand data included (copied): ${leftHandData.length} points`);
|
| 1719 |
} else {
|
| 1720 |
-
debugLog(`⚠️ Left hand data missing or empty`);
|
| 1721 |
}
|
| 1722 |
|
| 1723 |
if (rightHandData && rightHandData.length > 0) {
|
| 1724 |
person.hand_right_keypoints_2d = [...rightHandData]; // 配列コピー
|
| 1725 |
-
debugLog(`🫳 Right hand data included (copied): ${rightHandData.length} points`);
|
| 1726 |
} else {
|
| 1727 |
-
debugLog(`⚠️ Right hand data missing or empty`);
|
| 1728 |
}
|
| 1729 |
|
| 1730 |
if (faceData && faceData.length > 0) {
|
| 1731 |
person.face_keypoints_2d = [...faceData]; // 配列コピー
|
| 1732 |
-
debugLog(`😊 Face data included (copied): ${faceData.length} points`);
|
| 1733 |
} else {
|
| 1734 |
-
debugLog(`⚠️ Face data missing or empty`);
|
| 1735 |
}
|
| 1736 |
|
| 1737 |
canvasData.people = [person];
|
| 1738 |
} else {
|
| 1739 |
// bodies.candidateがない場合でも手と顔データがあれば送信
|
| 1740 |
-
debugLog("⚠️ No bodies data available, trying to send hands/face only");
|
| 1741 |
|
| 1742 |
const person = {
|
| 1743 |
"pose_keypoints_2d": [] // 空のボディデータ
|
|
@@ -1765,7 +1668,6 @@ function sendPoseDataToGradio() {
|
|
| 1765 |
currentPoseData.faces;
|
| 1766 |
if (hasHandsOrFace) {
|
| 1767 |
canvasData.people = [person];
|
| 1768 |
-
debugLog("🔄 Sending hands/face only data");
|
| 1769 |
}
|
| 1770 |
}
|
| 1771 |
|
|
@@ -1795,7 +1697,6 @@ function sendPoseDataToGradio() {
|
|
| 1795 |
if (jsUpdateTextbox) {
|
| 1796 |
jsUpdateTextbox.value = jsonString;
|
| 1797 |
jsUpdateTextbox.dispatchEvent(new Event('input', { bubbles: true }));
|
| 1798 |
-
debugLog('✅ Pose data sent to Gradio via js_pose_update with timestamp');
|
| 1799 |
return;
|
| 1800 |
}
|
| 1801 |
|
|
@@ -1806,20 +1707,17 @@ function sendPoseDataToGradio() {
|
|
| 1806 |
if (currentValue.includes('bodies') || currentValue.includes('candidate') || currentValue.trim() === '') {
|
| 1807 |
textarea.value = jsonString;
|
| 1808 |
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
| 1809 |
-
debugLog('⚠️ Pose data sent to Gradio via fallback method with timestamp');
|
| 1810 |
return;
|
| 1811 |
}
|
| 1812 |
}
|
| 1813 |
|
| 1814 |
|
| 1815 |
} catch (error) {
|
| 1816 |
-
debugLog(`Error sending data to Gradio: ${error.message}`);
|
| 1817 |
} finally {
|
| 1818 |
// Issue 038: 処理完了後は必ずフラグを解除(refs issue043準拠)
|
| 1819 |
if (window.poseEditorGlobals) {
|
| 1820 |
const oldFlag = window.poseEditorGlobals.isUpdating;
|
| 1821 |
window.poseEditorGlobals.isUpdating = false;
|
| 1822 |
-
debugLog(`🔓 JavaScript更新処理完了 - フラグ解除: ${oldFlag} → ${window.poseEditorGlobals.isUpdating}`);
|
| 1823 |
}
|
| 1824 |
}
|
| 1825 |
}
|
|
@@ -1832,7 +1730,9 @@ function isCanvasReady() {
|
|
| 1832 |
|
| 1833 |
// ポーズ全体の描画(ハイライト対応・設定制御���
|
| 1834 |
function drawPose(poseData, enableHands = true, enableFace = true, highlightIndex = -1) {
|
| 1835 |
-
if (!isCanvasReady() || !poseData)
|
|
|
|
|
|
|
| 1836 |
|
| 1837 |
// 🚀 refs互換:常に最新の編集済みデータを使用
|
| 1838 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
|
@@ -1883,7 +1783,6 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1883 |
|
| 1884 |
if (handsDataForDrawing && handsDataForDrawing.length >= 2) {
|
| 1885 |
drawHands(handsDataForDrawing, originalRes, scaleX, scaleY);
|
| 1886 |
-
debugLog(`🫳 Hands drawn: left=${handsDataForDrawing[0].length}, right=${handsDataForDrawing[1].length}`);
|
| 1887 |
}
|
| 1888 |
} catch (error) {
|
| 1889 |
console.error("❌ Error drawing hands:", error);
|
|
@@ -1891,7 +1790,6 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1891 |
|
| 1892 |
// 🔧 簡易モード:手の矩形描画(編集モード中は再計算しない)
|
| 1893 |
if (window.poseEditorGlobals.editMode === "簡易モード") {
|
| 1894 |
-
debugLog("🔧 Simple mode detected - will draw hand rectangles");
|
| 1895 |
if (!window.poseEditorGlobals.rectEditModeActive) {
|
| 1896 |
// 🚀 通常時:編集済みキーポイントから矩形を計算
|
| 1897 |
if (currentPoseData.people && currentPoseData.people[0]) {
|
|
@@ -1912,9 +1810,7 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1912 |
} else {
|
| 1913 |
}
|
| 1914 |
} else if (!enableHands) {
|
| 1915 |
-
debugLog("Hand drawing disabled by setting");
|
| 1916 |
} else {
|
| 1917 |
-
debugLog("No hand data available");
|
| 1918 |
}
|
| 1919 |
|
| 1920 |
// 顔の描画(設定制御・座標変換パラメータ付き・エラー耐性強化)
|
|
@@ -1933,7 +1829,6 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1933 |
|
| 1934 |
if (facesDataForDrawing && facesDataForDrawing.length > 0) {
|
| 1935 |
drawFaces(facesDataForDrawing, originalRes, scaleX, scaleY);
|
| 1936 |
-
debugLog(`😊 Face drawn: ${facesDataForDrawing[0].length} keypoints`);
|
| 1937 |
}
|
| 1938 |
} catch (error) {
|
| 1939 |
console.error("❌ Error drawing face:", error);
|
|
@@ -1941,7 +1836,6 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1941 |
|
| 1942 |
// 🔧 簡易モード:顔の矩形描画(編集モード中は再計算しない)
|
| 1943 |
if (window.poseEditorGlobals.editMode === "簡易モード") {
|
| 1944 |
-
debugLog("🔧 Simple mode detected - will draw face rectangles");
|
| 1945 |
if (!window.poseEditorGlobals.rectEditModeActive) {
|
| 1946 |
// 🚀 通常時:編集済みキーポイントから矩形を計算
|
| 1947 |
if (currentPoseData.people && currentPoseData.people[0] && currentPoseData.people[0].face_keypoints_2d) {
|
|
@@ -1957,9 +1851,7 @@ function drawPose(poseData, enableHands = true, enableFace = true, highlightInde
|
|
| 1957 |
} else {
|
| 1958 |
}
|
| 1959 |
} else if (!enableFace) {
|
| 1960 |
-
debugLog("Face drawing disabled by setting");
|
| 1961 |
} else {
|
| 1962 |
-
debugLog("No face data available");
|
| 1963 |
}
|
| 1964 |
}
|
| 1965 |
|
|
@@ -2007,16 +1899,29 @@ function drawBackground() {
|
|
| 2007 |
|
| 2008 |
// ボディ描画(ハイライト対応)
|
| 2009 |
function drawBody(poseData, highlightIndex = -1) {
|
| 2010 |
-
|
| 2011 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2012 |
return;
|
| 2013 |
}
|
| 2014 |
|
| 2015 |
const canvas = window.poseEditorGlobals.canvas;
|
| 2016 |
const ctx = window.poseEditorGlobals.ctx;
|
| 2017 |
|
| 2018 |
-
const candidates =
|
| 2019 |
-
const subset = poseData.bodies.subset || [];
|
| 2020 |
|
| 2021 |
if (subset.length === 0) {
|
| 2022 |
// No subset data, using all candidates directly
|
|
@@ -2423,7 +2328,6 @@ window.gradioCanvasUpdate = function(pose_json_str) {
|
|
| 2423 |
}
|
| 2424 |
|
| 2425 |
if (!isCanvasReady()) {
|
| 2426 |
-
debugLog("Canvas not ready, initializing...");
|
| 2427 |
// console.log(`[DEBUG] isCanvasReady check: canvas=${!!canvas}, ctx=${!!ctx}, isInitialized=${isInitialized}`);
|
| 2428 |
// console.log(`[DEBUG] window.poseEditorGlobals.canvas=${!!window.poseEditorGlobals.canvas}, window.poseEditorGlobals.ctx=${!!window.poseEditorGlobals.ctx}`);
|
| 2429 |
initializePoseEditor();
|
|
@@ -2445,6 +2349,7 @@ window.gradioCanvasUpdate = function(pose_json_str) {
|
|
| 2445 |
window.poseEditorGlobals.enableHands,
|
| 2446 |
window.poseEditorGlobals.enableFace
|
| 2447 |
);
|
|
|
|
| 2448 |
}
|
| 2449 |
|
| 2450 |
} catch (error) {
|
|
@@ -2461,7 +2366,6 @@ window.gradioCanvasUpdate = function(pose_json_str) {
|
|
| 2461 |
// Gradioからのデータ受信用(後方互換性)
|
| 2462 |
window.updatePoseData = function(data, enableHands = true, enableFace = true) {
|
| 2463 |
if (!isCanvasReady()) {
|
| 2464 |
-
debugLog("Canvas not ready");
|
| 2465 |
return;
|
| 2466 |
}
|
| 2467 |
|
|
@@ -2500,7 +2404,6 @@ window.updateDisplaySettings = function(enableHands, enableFace, editMode) {
|
|
| 2500 |
window.poseEditorGlobals.rectEditInfo = null;
|
| 2501 |
window.poseEditorGlobals.dragStartPos = { x: 0, y: 0 };
|
| 2502 |
|
| 2503 |
-
debugLog("🔧 All rectangle edit states cleared when switching to detailed mode");
|
| 2504 |
}
|
| 2505 |
|
| 2506 |
// 🔧 設定更新時は強制的に再描画(レガシー機能)
|
|
@@ -2513,7 +2416,6 @@ window.updateDisplaySettings = function(enableHands, enableFace, editMode) {
|
|
| 2513 |
|
| 2514 |
// Gradioトースト通知のトリガー
|
| 2515 |
window.showToast = function(type, message) {
|
| 2516 |
-
debugLog(`Showing toast: ${type} - ${message}`);
|
| 2517 |
// Gradioの隠しコンポーネントを使って通知
|
| 2518 |
if (window.triggerToast) {
|
| 2519 |
window.triggerToast(type, message);
|
|
@@ -2525,12 +2427,10 @@ window.showToast = function(type, message) {
|
|
| 2525 |
|
| 2526 |
// 🎨 背景画像設定機能(refs互換)
|
| 2527 |
window.setBackgroundImage = function(imageData) {
|
| 2528 |
-
debugLog("🎨 setBackgroundImage called");
|
| 2529 |
|
| 2530 |
if (!imageData) {
|
| 2531 |
// 背景画像をクリア
|
| 2532 |
window.poseEditorGlobals.backgroundImage = null;
|
| 2533 |
-
debugLog("🎨 Background image cleared");
|
| 2534 |
|
| 2535 |
// 現在のポーズデータがあれば再描画
|
| 2536 |
const currentPoseData = window.poseEditorGlobals.poseData;
|
|
@@ -2543,7 +2443,6 @@ window.setBackgroundImage = function(imageData) {
|
|
| 2543 |
const img = new Image();
|
| 2544 |
img.onload = function() {
|
| 2545 |
window.poseEditorGlobals.backgroundImage = img;
|
| 2546 |
-
debugLog(`🎨 Background image loaded: ${img.width}x${img.height}`);
|
| 2547 |
|
| 2548 |
// 背景画像が設定されたらCanvas再描画
|
| 2549 |
const currentPoseData = window.poseEditorGlobals.poseData;
|
|
@@ -2553,7 +2452,6 @@ window.setBackgroundImage = function(imageData) {
|
|
| 2553 |
};
|
| 2554 |
|
| 2555 |
img.onerror = function(e) {
|
| 2556 |
-
debugLog("🚨 Failed to load background image");
|
| 2557 |
};
|
| 2558 |
|
| 2559 |
// Base64データまたはURLから画像を設定
|
|
@@ -2590,7 +2488,6 @@ function notifyCanvasStateChange(state) {
|
|
| 2590 |
|
| 2591 |
// グローバルエラーハンドラー
|
| 2592 |
window.addEventListener('error', (event) => {
|
| 2593 |
-
debugLog(`Global error: ${event.error.message}`);
|
| 2594 |
if (isCanvasReady()) {
|
| 2595 |
showCanvasError('エラーが発生しました');
|
| 2596 |
}
|
|
@@ -2598,7 +2495,6 @@ window.addEventListener('error', (event) => {
|
|
| 2598 |
|
| 2599 |
// Promise rejection ハンドラ
|
| 2600 |
window.addEventListener('unhandledrejection', (event) => {
|
| 2601 |
-
debugLog(`Unhandled promise rejection: ${event.reason}`);
|
| 2602 |
event.preventDefault();
|
| 2603 |
if (isCanvasReady()) {
|
| 2604 |
showCanvasError('非同期処理でエラーが発生しました');
|
|
@@ -2610,7 +2506,6 @@ function safeExecute(operation, errorMessage = "操作中にエラーが発生
|
|
| 2610 |
try {
|
| 2611 |
return operation();
|
| 2612 |
} catch (error) {
|
| 2613 |
-
debugLog(`Safe execute error: ${error.message}`);
|
| 2614 |
if (isCanvasReady()) {
|
| 2615 |
showCanvasError(errorMessage);
|
| 2616 |
}
|
|
@@ -2628,7 +2523,6 @@ function safeCanvasOperation(operation) {
|
|
| 2628 |
function drawHandRectangles(handsData, originalRes, scaleX, scaleY) {
|
| 2629 |
if (!handsData || handsData.length === 0) return;
|
| 2630 |
|
| 2631 |
-
debugLog(`🎨 Drawing hand rectangles in mode: ${window.poseEditorGlobals.editMode}`);
|
| 2632 |
|
| 2633 |
const ctx = window.poseEditorGlobals.ctx;
|
| 2634 |
|
|
@@ -2645,9 +2539,7 @@ function drawHandRectangles(handsData, originalRes, scaleX, scaleY) {
|
|
| 2645 |
// 🔧 グローバルに矩形情報保存(編集モード中は既存の矩形を保持)
|
| 2646 |
if (!window.poseEditorGlobals.currentRects[handType] || !window.poseEditorGlobals.rectEditModeActive) {
|
| 2647 |
window.poseEditorGlobals.currentRects[handType] = rect;
|
| 2648 |
-
debugLog(`🔧 Hand rect calculated for ${handType}: ${JSON.stringify(rect)}`);
|
| 2649 |
} else {
|
| 2650 |
-
debugLog(`🔧 Hand rect preserved for ${handType} (edit mode active)`);
|
| 2651 |
}
|
| 2652 |
|
| 2653 |
// 描画は現在の矩形を使用(編集中は保存されている矩形)
|
|
@@ -2662,7 +2554,6 @@ function drawHandRectangles(handsData, originalRes, scaleX, scaleY) {
|
|
| 2662 |
function drawFaceRectangles(facesData, originalRes, scaleX, scaleY) {
|
| 2663 |
if (!facesData || facesData.length === 0) return;
|
| 2664 |
|
| 2665 |
-
debugLog(`🎨 Drawing face rectangles in mode: ${window.poseEditorGlobals.editMode}`);
|
| 2666 |
|
| 2667 |
const ctx = window.poseEditorGlobals.ctx;
|
| 2668 |
|
|
@@ -2676,9 +2567,7 @@ function drawFaceRectangles(facesData, originalRes, scaleX, scaleY) {
|
|
| 2676 |
// 🔧 グローバルに矩形情報保存(編集モード中は既存の矩形を保持)
|
| 2677 |
if (!window.poseEditorGlobals.currentRects.face || !window.poseEditorGlobals.rectEditModeActive) {
|
| 2678 |
window.poseEditorGlobals.currentRects.face = rect;
|
| 2679 |
-
debugLog(`🔧 Face rect calculated: ${JSON.stringify(rect)}`);
|
| 2680 |
} else {
|
| 2681 |
-
debugLog(`🔧 Face rect preserved (edit mode active)`);
|
| 2682 |
}
|
| 2683 |
|
| 2684 |
// 描画は現在の矩形を使用(編集中は保存されている矩形)
|
|
@@ -3056,7 +2945,6 @@ function updateRectControlDrag(mouseX, mouseY) {
|
|
| 3056 |
|
| 3057 |
// 🔧 rectEditInfo全体初期化(矩形編集モード開始時)
|
| 3058 |
function initializeRectEditInfo() {
|
| 3059 |
-
debugLog('🚀 Initializing rectEditInfo for all rect types');
|
| 3060 |
|
| 3061 |
// 🔧 前回の状態をクリアしてから初期化(連続編集対応)
|
| 3062 |
window.poseEditorGlobals.rectEditInfo = {};
|
|
@@ -3073,7 +2961,6 @@ function initializeRectEditInfo() {
|
|
| 3073 |
originalRect: { ...currentRect },
|
| 3074 |
keypointIndices: getRectKeypointIndices(rectType)
|
| 3075 |
};
|
| 3076 |
-
debugLog(`🔧 rectEditInfo initialized: ${rectType}`, window.poseEditorGlobals.rectEditInfo[rectType]);
|
| 3077 |
}
|
| 3078 |
}
|
| 3079 |
|
|
@@ -3240,11 +3127,7 @@ function moveDetailKeypoints(rectType, deltaX, deltaY) {
|
|
| 3240 |
} else if (rectType === 'face' && person.face_keypoints_2d) {
|
| 3241 |
moveKeypointsArray(person.face_keypoints_2d, deltaX, deltaY, 'face');
|
| 3242 |
} else {
|
| 3243 |
-
|
| 3244 |
-
hasLeftHand: !!person.hand_left_keypoints_2d,
|
| 3245 |
-
hasRightHand: !!person.hand_right_keypoints_2d,
|
| 3246 |
-
hasFace: !!person.face_keypoints_2d
|
| 3247 |
-
});
|
| 3248 |
}
|
| 3249 |
}
|
| 3250 |
|
|
@@ -3291,7 +3174,6 @@ function updateRectMoveDrag(mouseX, mouseY) {
|
|
| 3291 |
const deltaX = mouseX - startPos.x;
|
| 3292 |
const deltaY = mouseY - startPos.y;
|
| 3293 |
|
| 3294 |
-
debugLog(`🔧 矩形移動ドラッグ: ${rectType}, delta=(${deltaX}, ${deltaY})`);
|
| 3295 |
|
| 3296 |
// 矩形の位置を更新(refs方式)
|
| 3297 |
const newRect = {
|
|
@@ -3344,12 +3226,10 @@ function updateKeypointsByRect(rectType, newRect) {
|
|
| 3344 |
const originalRect = window.poseEditorGlobals.originalRect;
|
| 3345 |
if (!originalRect) return;
|
| 3346 |
|
| 3347 |
-
debugLog(`🔧 Updating ${rectType} keypoints: original=${JSON.stringify(originalRect)}, new=${JSON.stringify(newRect)}`);
|
| 3348 |
|
| 3349 |
// 最小サイズ制限(refs互換)
|
| 3350 |
const minSize = 20;
|
| 3351 |
if (newRect.width < minSize || newRect.height < minSize) {
|
| 3352 |
-
debugLog(`Rectangle too small, skipping update`);
|
| 3353 |
return;
|
| 3354 |
}
|
| 3355 |
|
|
@@ -3390,7 +3270,6 @@ function updateKeypointsByRect(rectType, newRect) {
|
|
| 3390 |
}
|
| 3391 |
|
| 3392 |
if (!targetKeypoints) {
|
| 3393 |
-
debugLog(`No keypoints found for ${rectType}`);
|
| 3394 |
return;
|
| 3395 |
}
|
| 3396 |
|
|
@@ -3539,7 +3418,6 @@ function moveKeypointsByRect(rectType, deltaX, deltaY) {
|
|
| 3539 |
}
|
| 3540 |
|
| 3541 |
if (!targetKeypoints) {
|
| 3542 |
-
debugLog(`No keypoints found for ${rectType}`);
|
| 3543 |
return;
|
| 3544 |
}
|
| 3545 |
|
|
|
|
| 1 |
// Canvas操作用JavaScript for dwpose-editor
|
| 2 |
|
| 3 |
+
// グローバル変数
|
| 4 |
window.poseEditorGlobals = {
|
| 5 |
canvas: null,
|
| 6 |
ctx: null,
|
|
|
|
| 59 |
let draggedKeypoint = -1;
|
| 60 |
let dragOffset = { x: 0, y: 0 };
|
| 61 |
|
| 62 |
+
// デバッグログ機能は削除済み
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
+
// Canvas初期化関数
|
| 65 |
function initializePoseEditor() {
|
|
|
|
| 66 |
|
| 67 |
canvas = document.getElementById('pose_canvas');
|
| 68 |
if (!canvas) {
|
|
|
|
| 69 |
setTimeout(initializePoseEditor, 100);
|
| 70 |
return;
|
| 71 |
}
|
| 72 |
|
| 73 |
ctx = canvas.getContext('2d');
|
| 74 |
if (!ctx) {
|
|
|
|
| 75 |
return;
|
| 76 |
}
|
| 77 |
|
|
|
|
| 94 |
canvas.style.cursor = 'default';
|
| 95 |
|
| 96 |
isInitialized = true;
|
|
|
|
| 97 |
notifyCanvasStateChange('initialized');
|
| 98 |
|
| 99 |
// ドラッグイベントを設定(refs互換)
|
|
|
|
| 151 |
|
| 152 |
// ドラッグイベント設定(refs互換)
|
| 153 |
function setupDragEvents() {
|
|
|
|
| 154 |
|
| 155 |
if (!canvas) {
|
|
|
|
| 156 |
return;
|
| 157 |
}
|
| 158 |
|
|
|
|
| 166 |
canvas.addEventListener('click', function(event) {
|
| 167 |
});
|
| 168 |
|
|
|
|
| 169 |
}
|
| 170 |
|
| 171 |
// 🔧 Gradioチェックボックス監視設定
|
| 172 |
function setupGradioCheckboxListeners() {
|
| 173 |
// チェックボックスとラジオボタン要素を探す(少し待ってから)
|
| 174 |
setTimeout(() => {
|
|
|
|
| 175 |
|
| 176 |
// チェックボックス監視
|
| 177 |
const allCheckboxes = document.querySelectorAll('input[type="checkbox"]');
|
|
|
|
| 192 |
|
| 193 |
// ラジオボタン監視(編集モード)
|
| 194 |
const allRadios = document.querySelectorAll('input[type="radio"]');
|
|
|
|
| 195 |
|
| 196 |
allRadios.forEach((radio, index) => {
|
| 197 |
// 親要素のテキストから簡易モード・詳細モードを特定
|
| 198 |
const parentText = radio.parentElement?.textContent?.trim() || '';
|
| 199 |
const grandParentText = radio.parentElement?.parentElement?.textContent?.trim() || '';
|
| 200 |
|
|
|
|
| 201 |
|
| 202 |
if (parentText.includes('簡易モード') || parentText.includes('詳細モード') ||
|
| 203 |
grandParentText.includes('編集モード')) {
|
|
|
|
| 281 |
}
|
| 282 |
|
| 283 |
if (!currentPoseData.bodies) {
|
|
|
|
| 284 |
return -1;
|
| 285 |
}
|
| 286 |
|
| 287 |
if (!currentPoseData.bodies.candidate) {
|
|
|
|
| 288 |
return -1;
|
| 289 |
}
|
| 290 |
|
|
|
|
| 426 |
// 詳細モード用:最寄りのキーポイント検索(手・顔の個別キーポイント対応)
|
| 427 |
function findNearestKeypointInDetailMode(clickX, clickY) {
|
| 428 |
if (!poseData || !poseData.people || poseData.people.length === 0) {
|
|
|
|
| 429 |
return null;
|
| 430 |
}
|
| 431 |
|
|
|
|
| 593 |
const currentRect = window.poseEditorGlobals.currentRects[rectType];
|
| 594 |
if (currentRect) {
|
| 595 |
window.poseEditorGlobals.originalRect = { ...currentRect };
|
|
|
|
| 596 |
}
|
| 597 |
|
| 598 |
// コントロールポイントドラッグ(リサイズ)
|
|
|
|
| 608 |
window.poseEditorGlobals.draggedRect = rectType;
|
| 609 |
window.poseEditorGlobals.dragStartPos = { x: mousePos.x, y: mousePos.y };
|
| 610 |
isDragging = true;
|
|
|
|
| 611 |
return;
|
| 612 |
}
|
| 613 |
|
| 614 |
// 他の矩形クリック → 切り替え
|
| 615 |
if (rectType && rectType !== window.poseEditorGlobals.rectEditMode) {
|
| 616 |
window.poseEditorGlobals.rectEditMode = rectType;
|
|
|
|
| 617 |
// 再描画で新しい矩形のコントロールポイントを表示
|
| 618 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 619 |
if (currentPoseData) {
|
|
|
|
| 645 |
window.poseEditorGlobals.draggedRect = null;
|
| 646 |
window.poseEditorGlobals.dragStartPos = { x: 0, y: 0 };
|
| 647 |
|
|
|
|
| 648 |
|
| 649 |
// 🚀 矩形位置を保持したまま再描画(再計算させない)
|
| 650 |
if (currentPoseData) {
|
|
|
|
| 659 |
if (rectType) {
|
| 660 |
// 矩形モード切り替え時の処理
|
| 661 |
if (window.poseEditorGlobals.rectEditMode !== rectType) {
|
|
|
|
| 662 |
window.poseEditorGlobals.rectEditMode = rectType;
|
| 663 |
}
|
| 664 |
|
| 665 |
window.poseEditorGlobals.rectEditModeActive = true;
|
|
|
|
| 666 |
|
| 667 |
// 🔧 rectEditInfo全体を初期化(全矩形タイプ対応)
|
| 668 |
initializeRectEditInfo();
|
|
|
|
| 718 |
}
|
| 719 |
}
|
| 720 |
|
|
|
|
| 721 |
canvas.style.cursor = 'grabbing';
|
| 722 |
} else {
|
|
|
|
| 723 |
}
|
| 724 |
}
|
| 725 |
}
|
|
|
|
| 730 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 731 |
|
| 732 |
if (!currentPoseData) {
|
|
|
|
| 733 |
return;
|
| 734 |
}
|
| 735 |
|
|
|
|
| 761 |
handData[arrayIndex] = dataX;
|
| 762 |
handData[arrayIndex + 1] = dataY;
|
| 763 |
// 信頼度は維持
|
|
|
|
| 764 |
|
| 765 |
// 元のhandsデータも同期更新
|
| 766 |
syncHandsToOriginal(currentPoseData, 'left', handData);
|
|
|
|
| 788 |
handData[arrayIndex] = dataX;
|
| 789 |
handData[arrayIndex + 1] = dataY;
|
| 790 |
// 信頼度は維持
|
|
|
|
| 791 |
|
| 792 |
// 元のhandsデータも同期更新
|
| 793 |
syncHandsToOriginal(currentPoseData, 'right', handData);
|
|
|
|
| 815 |
faceData[arrayIndex] = dataX;
|
| 816 |
faceData[arrayIndex + 1] = dataY;
|
| 817 |
// 信頼度は維持
|
|
|
|
| 818 |
|
| 819 |
// 🚀 元のfacesデータも同期更新
|
| 820 |
syncFacesToOriginal(currentPoseData, faceData);
|
|
|
|
| 837 |
const handIndex = handType === 'left' ? 0 : 1;
|
| 838 |
if (handIndex < poseData.hands.length) {
|
| 839 |
poseData.hands[handIndex] = [...handData];
|
|
|
|
| 840 |
}
|
| 841 |
}
|
| 842 |
|
|
|
|
| 846 |
|
| 847 |
if (poseData.faces.length > 0) {
|
| 848 |
poseData.faces[0] = [...faceData];
|
|
|
|
| 849 |
}
|
| 850 |
}
|
| 851 |
|
|
|
|
| 1001 |
// 11. 矩形情報を更新
|
| 1002 |
control.rect = newRect;
|
| 1003 |
|
|
|
|
| 1004 |
}
|
| 1005 |
|
| 1006 |
// 🔧 直接矩形変換関数(シンプル版)
|
|
|
|
| 1009 |
const originalKeypoints = window.poseEditorGlobals.originalKeypoints;
|
| 1010 |
|
| 1011 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
|
|
|
| 1012 |
return;
|
| 1013 |
}
|
| 1014 |
|
| 1015 |
if (!originalKeypoints || !originalKeypoints.people || !originalKeypoints.people[0]) {
|
|
|
|
| 1016 |
return;
|
| 1017 |
}
|
| 1018 |
|
|
|
|
| 1040 |
fieldName = 'hand_right_keypoints_2d';
|
| 1041 |
break;
|
| 1042 |
default:
|
|
|
|
| 1043 |
return;
|
| 1044 |
}
|
| 1045 |
|
| 1046 |
if (!targetKeypoints || !Array.isArray(targetKeypoints)) {
|
|
|
|
|
|
|
| 1047 |
return;
|
| 1048 |
}
|
| 1049 |
|
| 1050 |
if (!originalTargetKeypoints || !Array.isArray(originalTargetKeypoints)) {
|
|
|
|
|
|
|
| 1051 |
return;
|
| 1052 |
}
|
| 1053 |
|
|
|
|
| 1054 |
|
| 1055 |
// 座標変換設定
|
| 1056 |
const canvasWidth = window.poseEditorGlobals.canvas ? window.poseEditorGlobals.canvas.width : 512;
|
|
|
|
| 1081 |
}
|
| 1082 |
}
|
| 1083 |
|
|
|
|
| 1084 |
|
| 1085 |
// キーポイントを一括変換(元データから毎回変換)
|
| 1086 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
|
|
|
| 1123 |
}
|
| 1124 |
}
|
| 1125 |
|
|
|
|
| 1126 |
}
|
| 1127 |
|
| 1128 |
// 🔧 元座標からキーポイントを移動(累積移動防止版)
|
|
|
|
| 1139 |
const originalKeypoints = window.poseEditorGlobals.originalKeypoints;
|
| 1140 |
|
| 1141 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
|
|
|
| 1142 |
return;
|
| 1143 |
}
|
| 1144 |
|
| 1145 |
if (!originalKeypoints || !originalKeypoints.people || !originalKeypoints.people[0]) {
|
|
|
|
| 1146 |
return;
|
| 1147 |
}
|
| 1148 |
|
|
|
|
| 1170 |
fieldName = 'hand_right_keypoints_2d';
|
| 1171 |
break;
|
| 1172 |
default:
|
|
|
|
| 1173 |
return;
|
| 1174 |
}
|
| 1175 |
|
| 1176 |
if (!targetKeypoints || !Array.isArray(targetKeypoints)) {
|
|
|
|
| 1177 |
return;
|
| 1178 |
}
|
| 1179 |
|
| 1180 |
if (!originalTargetKeypoints || !Array.isArray(originalTargetKeypoints)) {
|
|
|
|
| 1181 |
return;
|
| 1182 |
}
|
| 1183 |
|
|
|
|
| 1210 |
}
|
| 1211 |
}
|
| 1212 |
|
|
|
|
| 1213 |
|
| 1214 |
// キーポイントを移動(元データから移動量を適用)
|
| 1215 |
for (let i = 0; i < originalTargetKeypoints.length; i += 3) {
|
|
|
|
| 1248 |
}
|
| 1249 |
}
|
| 1250 |
|
|
|
|
| 1251 |
}
|
| 1252 |
|
| 1253 |
// 🔧 矩形移動に合わせてキーポイントを移動(refs互換版)
|
| 1254 |
function moveKeypointsWithRect(rectType, deltaX, deltaY) {
|
| 1255 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1256 |
if (!currentPoseData || !currentPoseData.people || !currentPoseData.people[0]) {
|
|
|
|
| 1257 |
return;
|
| 1258 |
}
|
| 1259 |
|
| 1260 |
const person = currentPoseData.people[0];
|
| 1261 |
|
|
|
|
| 1262 |
|
| 1263 |
// 対応するキーポイントを取得
|
| 1264 |
let keypoints = null;
|
|
|
|
| 1278 |
fieldName = 'hand_right_keypoints_2d';
|
| 1279 |
break;
|
| 1280 |
default:
|
|
|
|
| 1281 |
return;
|
| 1282 |
}
|
| 1283 |
|
| 1284 |
if (!keypoints || !Array.isArray(keypoints)) {
|
|
|
|
| 1285 |
return;
|
| 1286 |
}
|
| 1287 |
|
|
|
|
| 1298 |
// データを更新
|
| 1299 |
person[fieldName] = keypoints;
|
| 1300 |
|
|
|
|
| 1301 |
}
|
| 1302 |
|
| 1303 |
// 🚀 全データをエクスポート用フォーマットに同期(refs互換)
|
|
|
|
| 1327 |
}
|
| 1328 |
}
|
| 1329 |
|
|
|
|
| 1330 |
}
|
| 1331 |
|
| 1332 |
// マウス移動処理(refs互換 + 矩形編集対応)
|
|
|
|
| 1357 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1358 |
if (currentPoseData) {
|
| 1359 |
// 🫳😊 手と顔データが存在することを確認してから描画
|
|
|
|
| 1360 |
if (currentPoseData.people && currentPoseData.people[0]) {
|
| 1361 |
const person = currentPoseData.people[0];
|
|
|
|
| 1362 |
}
|
| 1363 |
|
| 1364 |
drawPose(currentPoseData, window.poseEditorGlobals.enableHands, window.poseEditorGlobals.enableFace);
|
|
|
|
| 1365 |
} else {
|
|
|
|
| 1366 |
}
|
| 1367 |
} catch (error) {
|
| 1368 |
console.error("❌ Error in real-time redraw:", error);
|
|
|
|
| 1382 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1383 |
if (currentPoseData) {
|
| 1384 |
// 🫳😊 手と顔データの存在確認
|
|
|
|
| 1385 |
|
| 1386 |
drawPose(currentPoseData, window.poseEditorGlobals.enableHands, window.poseEditorGlobals.enableFace, draggedKeypoint);
|
|
|
|
| 1387 |
} else {
|
|
|
|
| 1388 |
}
|
| 1389 |
} catch (error) {
|
| 1390 |
console.error("❌ Error in highlighted redraw:", error);
|
|
|
|
| 1428 |
function handleMouseUp(event) {
|
| 1429 |
if (isDragging) {
|
| 1430 |
if (window.poseEditorGlobals.draggedRectControl) {
|
|
|
|
| 1431 |
window.poseEditorGlobals.draggedRectControl = null;
|
| 1432 |
window.poseEditorGlobals.originalRect = null; // 🔧 元の矩形情報をクリア
|
| 1433 |
} else if (window.poseEditorGlobals.draggedRect) {
|
|
|
|
| 1434 |
window.poseEditorGlobals.draggedRect = null;
|
| 1435 |
} else if (window.poseEditorGlobals.draggedDetailKeypoint) {
|
| 1436 |
// 🎯 詳細モードキーポイントドラッグ終了
|
|
|
|
| 1437 |
window.poseEditorGlobals.draggedDetailKeypoint = null;
|
| 1438 |
} else if (draggedKeypoint >= 0) {
|
| 1439 |
draggedKeypoint = -1;
|
|
|
|
| 1453 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
| 1454 |
|
| 1455 |
if (!currentPoseData || !currentPoseData.bodies || !currentPoseData.bodies.candidate) {
|
|
|
|
| 1456 |
return;
|
| 1457 |
}
|
| 1458 |
|
|
|
|
| 1502 |
syncFacesToOriginal(currentPoseData, person.face_keypoints_2d);
|
| 1503 |
}
|
| 1504 |
|
|
|
|
| 1505 |
}
|
| 1506 |
|
|
|
|
| 1507 |
}
|
| 1508 |
|
| 1509 |
// 🚀 bodies.candidateからpeople.pose_keypoints_2dに同期(refs互換)
|
|
|
|
| 1542 |
// 🫳😊 既存の手と顔データを保持(ボディ編集時に消失しないように)
|
| 1543 |
if (!person.hand_left_keypoints_2d && poseData.hands && poseData.hands[0]) {
|
| 1544 |
person.hand_left_keypoints_2d = poseData.hands[0];
|
|
|
|
| 1545 |
}
|
| 1546 |
if (!person.hand_right_keypoints_2d && poseData.hands && poseData.hands[1]) {
|
| 1547 |
person.hand_right_keypoints_2d = poseData.hands[1];
|
|
|
|
| 1548 |
}
|
| 1549 |
if (!person.face_keypoints_2d && poseData.faces && poseData.faces.length > 0) {
|
| 1550 |
person.face_keypoints_2d = poseData.faces[0] || poseData.faces;
|
|
|
|
| 1551 |
}
|
| 1552 |
|
|
|
|
| 1553 |
}
|
| 1554 |
|
| 1555 |
// Gradioにデータ送信(refs互換・強制changeイベント版)
|
|
|
|
| 1567 |
});
|
| 1568 |
|
| 1569 |
if (!currentPoseData) {
|
|
|
|
| 1570 |
return;
|
| 1571 |
}
|
| 1572 |
|
| 1573 |
// Issue 038: JavaScript側更新フラグチェック(refs issue043準拠)
|
| 1574 |
if (window.poseEditorGlobals && window.poseEditorGlobals.isUpdating) {
|
|
|
|
| 1575 |
return;
|
| 1576 |
}
|
| 1577 |
|
| 1578 |
// 処理開始フラグを立てる
|
| 1579 |
if (window.poseEditorGlobals) {
|
| 1580 |
window.poseEditorGlobals.isUpdating = true;
|
|
|
|
| 1581 |
}
|
| 1582 |
|
| 1583 |
try {
|
|
|
|
| 1625 |
// 💖 手と顔データを確実に設定(配列コピーで安全に保持)
|
| 1626 |
if (leftHandData && leftHandData.length > 0) {
|
| 1627 |
person.hand_left_keypoints_2d = [...leftHandData]; // 配列コピー
|
|
|
|
| 1628 |
} else {
|
|
|
|
| 1629 |
}
|
| 1630 |
|
| 1631 |
if (rightHandData && rightHandData.length > 0) {
|
| 1632 |
person.hand_right_keypoints_2d = [...rightHandData]; // 配列コピー
|
|
|
|
| 1633 |
} else {
|
|
|
|
| 1634 |
}
|
| 1635 |
|
| 1636 |
if (faceData && faceData.length > 0) {
|
| 1637 |
person.face_keypoints_2d = [...faceData]; // 配列コピー
|
|
|
|
| 1638 |
} else {
|
|
|
|
| 1639 |
}
|
| 1640 |
|
| 1641 |
canvasData.people = [person];
|
| 1642 |
} else {
|
| 1643 |
// bodies.candidateがない場合でも手と顔データがあれば送信
|
|
|
|
| 1644 |
|
| 1645 |
const person = {
|
| 1646 |
"pose_keypoints_2d": [] // 空のボディデータ
|
|
|
|
| 1668 |
currentPoseData.faces;
|
| 1669 |
if (hasHandsOrFace) {
|
| 1670 |
canvasData.people = [person];
|
|
|
|
| 1671 |
}
|
| 1672 |
}
|
| 1673 |
|
|
|
|
| 1697 |
if (jsUpdateTextbox) {
|
| 1698 |
jsUpdateTextbox.value = jsonString;
|
| 1699 |
jsUpdateTextbox.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
| 1700 |
return;
|
| 1701 |
}
|
| 1702 |
|
|
|
|
| 1707 |
if (currentValue.includes('bodies') || currentValue.includes('candidate') || currentValue.trim() === '') {
|
| 1708 |
textarea.value = jsonString;
|
| 1709 |
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
| 1710 |
return;
|
| 1711 |
}
|
| 1712 |
}
|
| 1713 |
|
| 1714 |
|
| 1715 |
} catch (error) {
|
|
|
|
| 1716 |
} finally {
|
| 1717 |
// Issue 038: 処理完了後は必ずフラグを解除(refs issue043準拠)
|
| 1718 |
if (window.poseEditorGlobals) {
|
| 1719 |
const oldFlag = window.poseEditorGlobals.isUpdating;
|
| 1720 |
window.poseEditorGlobals.isUpdating = false;
|
|
|
|
| 1721 |
}
|
| 1722 |
}
|
| 1723 |
}
|
|
|
|
| 1730 |
|
| 1731 |
// ポーズ全体の描画(ハイライト対応・設定制御���
|
| 1732 |
function drawPose(poseData, enableHands = true, enableFace = true, highlightIndex = -1) {
|
| 1733 |
+
if (!isCanvasReady() || !poseData) {
|
| 1734 |
+
return;
|
| 1735 |
+
}
|
| 1736 |
|
| 1737 |
// 🚀 refs互換:常に最新の編集済みデータを使用
|
| 1738 |
const currentPoseData = window.poseEditorGlobals.poseData || poseData;
|
|
|
|
| 1783 |
|
| 1784 |
if (handsDataForDrawing && handsDataForDrawing.length >= 2) {
|
| 1785 |
drawHands(handsDataForDrawing, originalRes, scaleX, scaleY);
|
|
|
|
| 1786 |
}
|
| 1787 |
} catch (error) {
|
| 1788 |
console.error("❌ Error drawing hands:", error);
|
|
|
|
| 1790 |
|
| 1791 |
// 🔧 簡易モード:手の矩形描画(編集モード中は再計算しない)
|
| 1792 |
if (window.poseEditorGlobals.editMode === "簡易モード") {
|
|
|
|
| 1793 |
if (!window.poseEditorGlobals.rectEditModeActive) {
|
| 1794 |
// 🚀 通常時:編集済みキーポイントから矩形を計算
|
| 1795 |
if (currentPoseData.people && currentPoseData.people[0]) {
|
|
|
|
| 1810 |
} else {
|
| 1811 |
}
|
| 1812 |
} else if (!enableHands) {
|
|
|
|
| 1813 |
} else {
|
|
|
|
| 1814 |
}
|
| 1815 |
|
| 1816 |
// 顔の描画(設定制御・座標変換パラメータ付き・エラー耐性強化)
|
|
|
|
| 1829 |
|
| 1830 |
if (facesDataForDrawing && facesDataForDrawing.length > 0) {
|
| 1831 |
drawFaces(facesDataForDrawing, originalRes, scaleX, scaleY);
|
|
|
|
| 1832 |
}
|
| 1833 |
} catch (error) {
|
| 1834 |
console.error("❌ Error drawing face:", error);
|
|
|
|
| 1836 |
|
| 1837 |
// 🔧 簡易モード:顔の矩形描画(編集モード中は再計算しない)
|
| 1838 |
if (window.poseEditorGlobals.editMode === "簡易モード") {
|
|
|
|
| 1839 |
if (!window.poseEditorGlobals.rectEditModeActive) {
|
| 1840 |
// 🚀 通常時:編集済みキーポイントから矩形を計算
|
| 1841 |
if (currentPoseData.people && currentPoseData.people[0] && currentPoseData.people[0].face_keypoints_2d) {
|
|
|
|
| 1851 |
} else {
|
| 1852 |
}
|
| 1853 |
} else if (!enableFace) {
|
|
|
|
| 1854 |
} else {
|
|
|
|
| 1855 |
}
|
| 1856 |
}
|
| 1857 |
|
|
|
|
| 1899 |
|
| 1900 |
// ボディ描画(ハイライト対応)
|
| 1901 |
function drawBody(poseData, highlightIndex = -1) {
|
| 1902 |
+
|
| 1903 |
+
// people形式からbodies形式への変換を試行
|
| 1904 |
+
let candidatesData = null;
|
| 1905 |
+
if (poseData.bodies && poseData.bodies.candidate) {
|
| 1906 |
+
candidatesData = poseData.bodies.candidate;
|
| 1907 |
+
} else if (poseData.people && poseData.people[0] && poseData.people[0].pose_keypoints_2d) {
|
| 1908 |
+
// people形式からcandidate形式に変換
|
| 1909 |
+
const pose_keypoints_2d = poseData.people[0].pose_keypoints_2d;
|
| 1910 |
+
candidatesData = [];
|
| 1911 |
+
for (let i = 0; i < pose_keypoints_2d.length; i += 3) {
|
| 1912 |
+
if (i + 2 < pose_keypoints_2d.length) {
|
| 1913 |
+
candidatesData.push([pose_keypoints_2d[i], pose_keypoints_2d[i+1], pose_keypoints_2d[i+2]]);
|
| 1914 |
+
}
|
| 1915 |
+
}
|
| 1916 |
+
} else {
|
| 1917 |
return;
|
| 1918 |
}
|
| 1919 |
|
| 1920 |
const canvas = window.poseEditorGlobals.canvas;
|
| 1921 |
const ctx = window.poseEditorGlobals.ctx;
|
| 1922 |
|
| 1923 |
+
const candidates = candidatesData;
|
| 1924 |
+
const subset = (poseData.bodies && poseData.bodies.subset) || [];
|
| 1925 |
|
| 1926 |
if (subset.length === 0) {
|
| 1927 |
// No subset data, using all candidates directly
|
|
|
|
| 2328 |
}
|
| 2329 |
|
| 2330 |
if (!isCanvasReady()) {
|
|
|
|
| 2331 |
// console.log(`[DEBUG] isCanvasReady check: canvas=${!!canvas}, ctx=${!!ctx}, isInitialized=${isInitialized}`);
|
| 2332 |
// console.log(`[DEBUG] window.poseEditorGlobals.canvas=${!!window.poseEditorGlobals.canvas}, window.poseEditorGlobals.ctx=${!!window.poseEditorGlobals.ctx}`);
|
| 2333 |
initializePoseEditor();
|
|
|
|
| 2349 |
window.poseEditorGlobals.enableHands,
|
| 2350 |
window.poseEditorGlobals.enableFace
|
| 2351 |
);
|
| 2352 |
+
} else {
|
| 2353 |
}
|
| 2354 |
|
| 2355 |
} catch (error) {
|
|
|
|
| 2366 |
// Gradioからのデータ受信用(後方互換性)
|
| 2367 |
window.updatePoseData = function(data, enableHands = true, enableFace = true) {
|
| 2368 |
if (!isCanvasReady()) {
|
|
|
|
| 2369 |
return;
|
| 2370 |
}
|
| 2371 |
|
|
|
|
| 2404 |
window.poseEditorGlobals.rectEditInfo = null;
|
| 2405 |
window.poseEditorGlobals.dragStartPos = { x: 0, y: 0 };
|
| 2406 |
|
|
|
|
| 2407 |
}
|
| 2408 |
|
| 2409 |
// 🔧 設定更新時は強制的に再描画(レガシー機能)
|
|
|
|
| 2416 |
|
| 2417 |
// Gradioトースト通知のトリガー
|
| 2418 |
window.showToast = function(type, message) {
|
|
|
|
| 2419 |
// Gradioの隠しコンポーネントを使って通知
|
| 2420 |
if (window.triggerToast) {
|
| 2421 |
window.triggerToast(type, message);
|
|
|
|
| 2427 |
|
| 2428 |
// 🎨 背景画像設定機能(refs互換)
|
| 2429 |
window.setBackgroundImage = function(imageData) {
|
|
|
|
| 2430 |
|
| 2431 |
if (!imageData) {
|
| 2432 |
// 背景画像をクリア
|
| 2433 |
window.poseEditorGlobals.backgroundImage = null;
|
|
|
|
| 2434 |
|
| 2435 |
// 現在のポーズデータがあれば再描画
|
| 2436 |
const currentPoseData = window.poseEditorGlobals.poseData;
|
|
|
|
| 2443 |
const img = new Image();
|
| 2444 |
img.onload = function() {
|
| 2445 |
window.poseEditorGlobals.backgroundImage = img;
|
|
|
|
| 2446 |
|
| 2447 |
// 背景画像が設定されたらCanvas再描画
|
| 2448 |
const currentPoseData = window.poseEditorGlobals.poseData;
|
|
|
|
| 2452 |
};
|
| 2453 |
|
| 2454 |
img.onerror = function(e) {
|
|
|
|
| 2455 |
};
|
| 2456 |
|
| 2457 |
// Base64データまたはURLから画像を設定
|
|
|
|
| 2488 |
|
| 2489 |
// グローバルエラーハンドラー
|
| 2490 |
window.addEventListener('error', (event) => {
|
|
|
|
| 2491 |
if (isCanvasReady()) {
|
| 2492 |
showCanvasError('エラーが発生しました');
|
| 2493 |
}
|
|
|
|
| 2495 |
|
| 2496 |
// Promise rejection ハンドラ
|
| 2497 |
window.addEventListener('unhandledrejection', (event) => {
|
|
|
|
| 2498 |
event.preventDefault();
|
| 2499 |
if (isCanvasReady()) {
|
| 2500 |
showCanvasError('非同期処理でエラーが発生しました');
|
|
|
|
| 2506 |
try {
|
| 2507 |
return operation();
|
| 2508 |
} catch (error) {
|
|
|
|
| 2509 |
if (isCanvasReady()) {
|
| 2510 |
showCanvasError(errorMessage);
|
| 2511 |
}
|
|
|
|
| 2523 |
function drawHandRectangles(handsData, originalRes, scaleX, scaleY) {
|
| 2524 |
if (!handsData || handsData.length === 0) return;
|
| 2525 |
|
|
|
|
| 2526 |
|
| 2527 |
const ctx = window.poseEditorGlobals.ctx;
|
| 2528 |
|
|
|
|
| 2539 |
// 🔧 グローバルに矩形情報保存(編集モード中は既存の矩形を保持)
|
| 2540 |
if (!window.poseEditorGlobals.currentRects[handType] || !window.poseEditorGlobals.rectEditModeActive) {
|
| 2541 |
window.poseEditorGlobals.currentRects[handType] = rect;
|
|
|
|
| 2542 |
} else {
|
|
|
|
| 2543 |
}
|
| 2544 |
|
| 2545 |
// 描画は現在の矩形を使用(編集中は保存されている矩形)
|
|
|
|
| 2554 |
function drawFaceRectangles(facesData, originalRes, scaleX, scaleY) {
|
| 2555 |
if (!facesData || facesData.length === 0) return;
|
| 2556 |
|
|
|
|
| 2557 |
|
| 2558 |
const ctx = window.poseEditorGlobals.ctx;
|
| 2559 |
|
|
|
|
| 2567 |
// 🔧 グローバルに矩形情報保存(編集モード中は既存の矩形を保持)
|
| 2568 |
if (!window.poseEditorGlobals.currentRects.face || !window.poseEditorGlobals.rectEditModeActive) {
|
| 2569 |
window.poseEditorGlobals.currentRects.face = rect;
|
|
|
|
| 2570 |
} else {
|
|
|
|
| 2571 |
}
|
| 2572 |
|
| 2573 |
// 描画は現在の矩形を使用(編集中は保存されている矩形)
|
|
|
|
| 2945 |
|
| 2946 |
// 🔧 rectEditInfo全体初期化(矩形編集モード開始時)
|
| 2947 |
function initializeRectEditInfo() {
|
|
|
|
| 2948 |
|
| 2949 |
// 🔧 前回の状態をクリアしてから初期化(連続編集対応)
|
| 2950 |
window.poseEditorGlobals.rectEditInfo = {};
|
|
|
|
| 2961 |
originalRect: { ...currentRect },
|
| 2962 |
keypointIndices: getRectKeypointIndices(rectType)
|
| 2963 |
};
|
|
|
|
| 2964 |
}
|
| 2965 |
}
|
| 2966 |
|
|
|
|
| 3127 |
} else if (rectType === 'face' && person.face_keypoints_2d) {
|
| 3128 |
moveKeypointsArray(person.face_keypoints_2d, deltaX, deltaY, 'face');
|
| 3129 |
} else {
|
| 3130 |
+
// No matching detail keypoints found
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3131 |
}
|
| 3132 |
}
|
| 3133 |
|
|
|
|
| 3174 |
const deltaX = mouseX - startPos.x;
|
| 3175 |
const deltaY = mouseY - startPos.y;
|
| 3176 |
|
|
|
|
| 3177 |
|
| 3178 |
// 矩形の位置を更新(refs方式)
|
| 3179 |
const newRect = {
|
|
|
|
| 3226 |
const originalRect = window.poseEditorGlobals.originalRect;
|
| 3227 |
if (!originalRect) return;
|
| 3228 |
|
|
|
|
| 3229 |
|
| 3230 |
// 最小サイズ制限(refs互換)
|
| 3231 |
const minSize = 20;
|
| 3232 |
if (newRect.width < minSize || newRect.height < minSize) {
|
|
|
|
| 3233 |
return;
|
| 3234 |
}
|
| 3235 |
|
|
|
|
| 3270 |
}
|
| 3271 |
|
| 3272 |
if (!targetKeypoints) {
|
|
|
|
| 3273 |
return;
|
| 3274 |
}
|
| 3275 |
|
|
|
|
| 3418 |
}
|
| 3419 |
|
| 3420 |
if (!targetKeypoints) {
|
|
|
|
| 3421 |
return;
|
| 3422 |
}
|
| 3423 |
|
utils/notifications.py
CHANGED
|
@@ -8,25 +8,29 @@ NOTIFICATION_SETTINGS = {
|
|
| 8 |
'show_warnings': True,
|
| 9 |
'show_errors': True,
|
| 10 |
'auto_dismiss': True,
|
| 11 |
-
'
|
|
|
|
| 12 |
}
|
| 13 |
|
| 14 |
def notify_success(message):
|
| 15 |
"""成功通知"""
|
| 16 |
if NOTIFICATION_SETTINGS['show_success']:
|
| 17 |
-
|
|
|
|
| 18 |
return None
|
| 19 |
|
| 20 |
def notify_warning(message):
|
| 21 |
"""警告通知"""
|
| 22 |
if NOTIFICATION_SETTINGS['show_warnings']:
|
| 23 |
-
|
|
|
|
| 24 |
return None
|
| 25 |
|
| 26 |
def notify_error(message):
|
| 27 |
"""エラー通知"""
|
| 28 |
if NOTIFICATION_SETTINGS['show_errors']:
|
| 29 |
-
|
|
|
|
| 30 |
return None
|
| 31 |
|
| 32 |
def notify_progress(message, progress=None):
|
|
|
|
| 8 |
'show_warnings': True,
|
| 9 |
'show_errors': True,
|
| 10 |
'auto_dismiss': True,
|
| 11 |
+
'info_duration': 3.0, # Info系:3秒
|
| 12 |
+
'error_duration': 5.0 # Error系:5秒
|
| 13 |
}
|
| 14 |
|
| 15 |
def notify_success(message):
|
| 16 |
"""成功通知"""
|
| 17 |
if NOTIFICATION_SETTINGS['show_success']:
|
| 18 |
+
duration = NOTIFICATION_SETTINGS['info_duration'] # 2秒
|
| 19 |
+
return gr.Info(message, duration=duration)
|
| 20 |
return None
|
| 21 |
|
| 22 |
def notify_warning(message):
|
| 23 |
"""警告通知"""
|
| 24 |
if NOTIFICATION_SETTINGS['show_warnings']:
|
| 25 |
+
duration = NOTIFICATION_SETTINGS['info_duration'] # 2秒
|
| 26 |
+
return gr.Warning(message, duration=duration)
|
| 27 |
return None
|
| 28 |
|
| 29 |
def notify_error(message):
|
| 30 |
"""エラー通知"""
|
| 31 |
if NOTIFICATION_SETTINGS['show_errors']:
|
| 32 |
+
duration = NOTIFICATION_SETTINGS['error_duration'] # 5秒
|
| 33 |
+
return gr.Error(message, duration=duration)
|
| 34 |
return None
|
| 35 |
|
| 36 |
def notify_progress(message, progress=None):
|