Spaces:
Runtime error
Runtime error
insert pose meta data into png
Browse files- app.py +1 -1
- static/poseEditor.js +153 -0
app.py
CHANGED
@@ -129,7 +129,7 @@ Points to note for pseudo-3D rotation: When performing pseudo-3D rotation on the
|
|
129 |
- "shift + drag" to **rotate** (move right first, release shift, then up or down)
|
130 |
- "space + drag" to **range-move**
|
131 |
- "[", "]" or mouse "Alt + wheel" to shrink or expand **range**
|
132 |
-
- "ctrl +
|
133 |
- "ctrl + E" **add** new person
|
134 |
- "Q + click" to **delete** person
|
135 |
- "X + drag" to **x-axis** pseudo-3D rotation
|
|
|
129 |
- "shift + drag" to **rotate** (move right first, release shift, then up or down)
|
130 |
- "space + drag" to **range-move**
|
131 |
- "[", "]" or mouse "Alt + wheel" to shrink or expand **range**
|
132 |
+
- "ctrl + Z", "shift + ctrl + Z" to **undo**, **redo**
|
133 |
- "ctrl + E" **add** new person
|
134 |
- "Q + click" to **delete** person
|
135 |
- "X + drag" to **x-axis** pseudo-3D rotation
|
static/poseEditor.js
CHANGED
@@ -578,6 +578,7 @@ function importPose(jsonData) {
|
|
578 |
Redraw();
|
579 |
}
|
580 |
|
|
|
581 |
function savePose() {
|
582 |
const canvasUrl = canvas.toDataURL();
|
583 |
|
@@ -593,3 +594,155 @@ function savePose() {
|
|
593 |
var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
|
594 |
return {candidate: candidate, subset: subset};
|
595 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
Redraw();
|
579 |
}
|
580 |
|
581 |
+
/*
|
582 |
function savePose() {
|
583 |
const canvasUrl = canvas.toDataURL();
|
584 |
|
|
|
594 |
var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
|
595 |
return {candidate: candidate, subset: subset};
|
596 |
}
|
597 |
+
*/
|
598 |
+
|
599 |
+
// crc32
|
600 |
+
// CRC32を初期化
|
601 |
+
function initCrc32Table() {
|
602 |
+
const crcTable = new Uint32Array(256);
|
603 |
+
for (let i = 0; i < 256; i++) {
|
604 |
+
let c = i;
|
605 |
+
for (let j = 0; j < 8; j++) {
|
606 |
+
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
607 |
+
}
|
608 |
+
crcTable[i] = c;
|
609 |
+
}
|
610 |
+
return crcTable;
|
611 |
+
}
|
612 |
+
|
613 |
+
// データのCRC32を計算
|
614 |
+
function getCrc32(data, crc=0) {
|
615 |
+
const crcTable = initCrc32Table();
|
616 |
+
crc = (crc ^ 0xFFFFFFFF) >>> 0;
|
617 |
+
for (let i = 0; i < data.length; i++) {
|
618 |
+
crc = crcTable[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);
|
619 |
+
}
|
620 |
+
return (crc ^ 0xFFFFFFFF) >>> 0;
|
621 |
+
}
|
622 |
+
|
623 |
+
function stringToUint8Array(str) {
|
624 |
+
var arr = new Uint8Array(str.length);
|
625 |
+
for (var i = 0; i < str.length; i++) {
|
626 |
+
arr[i] = str.charCodeAt(i);
|
627 |
+
}
|
628 |
+
return arr;
|
629 |
+
}
|
630 |
+
|
631 |
+
function base64ToUint8Array(base64Str) {
|
632 |
+
return stringToUint8Array(atob(base64Str));
|
633 |
+
}
|
634 |
+
|
635 |
+
function visitPng(png, type) {
|
636 |
+
var dataLength;
|
637 |
+
var chunkType;
|
638 |
+
var nextChunkPos;
|
639 |
+
var Signature = String.fromCharCode(137, 80, 78, 71, 13, 10, 26, 10);
|
640 |
+
var rpos = 0;
|
641 |
+
|
642 |
+
// シグネチャの確認
|
643 |
+
if (String.fromCharCode.apply(null, png.subarray(rpos, rpos += 8)) !== Signature) {
|
644 |
+
throw new Error('invalid signature');
|
645 |
+
}
|
646 |
+
|
647 |
+
// チャンクの探索
|
648 |
+
while (rpos < png.length) {
|
649 |
+
dataLength = (
|
650 |
+
(png[rpos++] << 24) |
|
651 |
+
(png[rpos++] << 16) |
|
652 |
+
(png[rpos++] << 8) |
|
653 |
+
(png[rpos++] )
|
654 |
+
) >>> 0;
|
655 |
+
|
656 |
+
nextChunkPos = rpos + dataLength + 8;
|
657 |
+
|
658 |
+
chunkType = String.fromCharCode.apply(null, png.subarray(rpos, rpos += 4));
|
659 |
+
|
660 |
+
if (chunkType === type) {
|
661 |
+
return [rpos - 8, dataLength, nextChunkPos];
|
662 |
+
}
|
663 |
+
|
664 |
+
rpos = nextChunkPos;
|
665 |
+
}
|
666 |
+
}
|
667 |
+
|
668 |
+
function createChunk(type, data) {
|
669 |
+
var dataLength = data.length;
|
670 |
+
var chunk = new Uint8Array(4 + 4 + dataLength + 4);
|
671 |
+
var type = stringToUint8Array(type);
|
672 |
+
var pos = 0;
|
673 |
+
|
674 |
+
// length
|
675 |
+
chunk[pos++] = (dataLength >> 24) & 0xff;
|
676 |
+
chunk[pos++] = (dataLength >> 16) & 0xff;
|
677 |
+
chunk[pos++] = (dataLength >> 8) & 0xff;
|
678 |
+
chunk[pos++] = (dataLength ) & 0xff;
|
679 |
+
|
680 |
+
// type
|
681 |
+
chunk[pos++] = type[0];
|
682 |
+
chunk[pos++] = type[1];
|
683 |
+
chunk[pos++] = type[2];
|
684 |
+
chunk[pos++] = type[3];
|
685 |
+
|
686 |
+
// data
|
687 |
+
for (let i = 0; i < dataLength; ++i) {
|
688 |
+
chunk[pos++] = data[i];
|
689 |
+
}
|
690 |
+
|
691 |
+
//crc
|
692 |
+
initCrc32Table();
|
693 |
+
let crc = getCrc32(type);
|
694 |
+
crc = getCrc32(data, crc);
|
695 |
+
chunk[pos++] = (crc >> 24) & 0xff;
|
696 |
+
chunk[pos++] = (crc >> 16) & 0xff;
|
697 |
+
chunk[pos++] = (crc >> 8) & 0xff;
|
698 |
+
chunk[pos++] = (crc ) & 0xff;
|
699 |
+
|
700 |
+
return chunk;
|
701 |
+
}
|
702 |
+
|
703 |
+
function insertChunk(destBuffer, sourceBuffer, rpos, chunk) {
|
704 |
+
var pos = 0;
|
705 |
+
|
706 |
+
// IDAT チャンクの前までコピー
|
707 |
+
destBuffer.set(sourceBuffer.subarray(0, rpos), pos);
|
708 |
+
pos += rpos;
|
709 |
+
|
710 |
+
// hoGe チャンクをコピー
|
711 |
+
destBuffer.set(chunk, pos);
|
712 |
+
pos += chunk.length;
|
713 |
+
|
714 |
+
// IDAT チャンク以降をコピー
|
715 |
+
destBuffer.set(sourceBuffer.subarray(rpos), pos);
|
716 |
+
}
|
717 |
+
|
718 |
+
function mergeCanvasWithPose(keyword, content) {
|
719 |
+
const canvasUrl = canvas.toDataURL();
|
720 |
+
|
721 |
+
var insertion = stringToUint8Array(`${keyword}\0${content}`);
|
722 |
+
var chunk = createChunk("tEXt", insertion);
|
723 |
+
var sourceBuffer = base64ToUint8Array(canvasUrl.split(',')[1]);
|
724 |
+
var destBuffer = new Uint8Array(sourceBuffer.length + insertion.length + 12);
|
725 |
+
|
726 |
+
var [rpos, dataLength, nextChunkPos] = visitPng(sourceBuffer, "IHDR");
|
727 |
+
insertChunk(destBuffer, sourceBuffer, nextChunkPos, chunk);
|
728 |
+
|
729 |
+
var blob = new Blob([destBuffer], {type: "image/png"});
|
730 |
+
var url = URL.createObjectURL(blob);
|
731 |
+
return url;
|
732 |
+
}
|
733 |
+
|
734 |
+
function savePose() {
|
735 |
+
var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
|
736 |
+
let jsonData = {candidate: candidate, subset: subset};
|
737 |
+
|
738 |
+
var url = mergeCanvasWithPose("pose", JSON.stringify(jsonData));
|
739 |
+
|
740 |
+
const createEl = document.createElement('a');
|
741 |
+
createEl.href = url;
|
742 |
+
|
743 |
+
// This is the name of our downloaded file
|
744 |
+
createEl.download = "pose.png";
|
745 |
+
|
746 |
+
createEl.click();
|
747 |
+
createEl.remove();
|
748 |
+
}
|