|
fabric.Object.prototype.transparentCorners = false; |
|
fabric.Object.prototype.cornerColor = '#108ce6'; |
|
fabric.Object.prototype.borderColor = '#108ce6'; |
|
fabric.Object.prototype.cornerSize = 10; |
|
fabric.Object.prototype.lockRotation = true; |
|
|
|
let count = 0; |
|
let executed = false; |
|
|
|
let lockMode = false; |
|
const undo_history = []; |
|
const redo_history = []; |
|
|
|
coco_body_keypoints = [ |
|
"nose", |
|
"neck", |
|
"right_shoulder", |
|
"right_elbow", |
|
"right_wrist", |
|
"left_shoulder", |
|
"left_elbow", |
|
"left_wrist", |
|
"right_hip", |
|
"right_knee", |
|
"right_ankle", |
|
"left_hip", |
|
"left_knee", |
|
"left_ankle", |
|
"right_eye", |
|
"left_eye", |
|
"right_ear", |
|
"left_ear", |
|
] |
|
|
|
let connect_keypoints = [[0, 1], [1, 2], [2, 3], [3, 4], [1, 5], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10], [1, 11], [11, 12], [12, 13], [0, 14], [14, 16], [0, 15], [15, 17]] |
|
|
|
let connect_color = [[0, 0, 255], [255, 0, 0], [255, 170, 0], [255, 255, 0], [255, 85, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], |
|
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [85, 0, 255], |
|
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]] |
|
|
|
let openpose_obj = { |
|
|
|
resolution: [512, 512], |
|
|
|
fps: 1, |
|
|
|
frames: [ |
|
{ |
|
frame_current: 1, |
|
|
|
armatures: { |
|
}, |
|
} |
|
] |
|
} |
|
|
|
const default_keypoints = [[241,77],[241,120],[191,118],[177,183],[163,252],[298,118],[317,182],[332,245],[225,241],[213,359],[215,454],[270,240],[282,360],[286,456],[232,59],[253,60],[225,70],[260,72]] |
|
|
|
function gradioApp() { |
|
const elems = document.getElementsByTagName('gradio-app') |
|
const gradioShadowRoot = elems.length == 0 ? null : elems[0].shadowRoot |
|
return !!gradioShadowRoot ? gradioShadowRoot : document; |
|
} |
|
|
|
function calcResolution(resolution){ |
|
const width = resolution[0] |
|
const height = resolution[1] |
|
const viewportWidth = window.innerWidth / 2.25; |
|
const viewportHeight = window.innerHeight * 0.75; |
|
const ratio = Math.min(viewportWidth / width, viewportHeight / height); |
|
return {width: width * ratio, height: height * ratio} |
|
} |
|
|
|
function resizeCanvas(width, height){ |
|
const elem = openpose_editor_elem; |
|
const canvas = openpose_editor_canvas; |
|
|
|
let resolution = calcResolution([width, height]) |
|
|
|
canvas.setWidth(width); |
|
canvas.setHeight(height); |
|
elem.style.width = resolution["width"] + "px" |
|
elem.style.height = resolution["height"] + "px" |
|
elem.nextElementSibling.style.width = resolution["width"] + "px" |
|
elem.nextElementSibling.style.height = resolution["height"] + "px" |
|
elem.parentElement.style.width = resolution["width"] + "px" |
|
elem.parentElement.style.height = resolution["height"] + "px" |
|
} |
|
|
|
function undo() { |
|
const canvas = openpose_editor_canvas; |
|
if (undo_history.length > 0) { |
|
lockMode = true; |
|
if (undo_history.length > 1) redo_history.push(undo_history.pop()); |
|
const content = undo_history[undo_history.length - 1]; |
|
canvas.loadFromJSON(content, function () { |
|
canvas.renderAll(); |
|
lockMode = false; |
|
}); |
|
} |
|
} |
|
|
|
function redo() { |
|
const canvas = openpose_editor_canvas; |
|
if (redo_history.length > 0) { |
|
lockMode = true; |
|
const content = redo_history.pop(); |
|
undo_history.push(content); |
|
canvas.loadFromJSON(content, function () { |
|
canvas.renderAll(); |
|
lockMode = false; |
|
}); |
|
} |
|
} |
|
|
|
function setPose(keypoints){ |
|
const canvas = openpose_editor_canvas; |
|
|
|
canvas.clear() |
|
|
|
canvas.backgroundColor = "#000" |
|
|
|
const res = []; |
|
for (let i = 0; i < keypoints.length; i += 18) { |
|
const chunk = keypoints.slice(i, i + 18); |
|
res.push(chunk); |
|
} |
|
|
|
for (item of res){ |
|
addPose(item) |
|
openpose_editor_canvas.discardActiveObject(); |
|
} |
|
} |
|
|
|
function addPose(keypoints=undefined){ |
|
if (keypoints === undefined){ |
|
keypoints = default_keypoints; |
|
} |
|
|
|
const canvas = openpose_editor_canvas; |
|
const group = new fabric.Group() |
|
|
|
function makeCircle(color, left, top, line1, line2, line3, line4, line5) { |
|
var c = new fabric.Circle({ |
|
left: left, |
|
top: top, |
|
strokeWidth: 1, |
|
radius: 5, |
|
fill: color, |
|
stroke: color |
|
}); |
|
c.hasControls = c.hasBorders = false; |
|
|
|
c.line1 = line1; |
|
c.line2 = line2; |
|
c.line3 = line3; |
|
c.line4 = line4; |
|
c.line5 = line5; |
|
|
|
return c; |
|
} |
|
|
|
function makeLine(coords, color) { |
|
return new fabric.Line(coords, { |
|
fill: color, |
|
stroke: color, |
|
strokeWidth: 10, |
|
selectable: false, |
|
evented: false, |
|
}); |
|
} |
|
|
|
const lines = [] |
|
const circles = [] |
|
|
|
for (i = 0; i < connect_keypoints.length; i++){ |
|
|
|
const item = connect_keypoints[i] |
|
const line = makeLine(keypoints[item[0]].concat(keypoints[item[1]]), `rgba(${connect_color[i].join(", ")}, 0.7)`) |
|
lines.push(line) |
|
canvas.add(line) |
|
} |
|
|
|
for (i = 0; i < keypoints.length; i++){ |
|
list = [] |
|
connect_keypoints.filter((item, idx) => { |
|
if(item.includes(i)){ |
|
list.push(lines[idx]) |
|
return idx |
|
} |
|
}) |
|
circle = makeCircle(`rgb(${connect_color[i].join(", ")})`, keypoints[i][0], keypoints[i][1], ...list) |
|
circle["id"] = i |
|
circles.push(circle) |
|
|
|
group.addWithUpdate(circle); |
|
} |
|
|
|
canvas.discardActiveObject(); |
|
canvas.setActiveObject(group); |
|
canvas.add(group); |
|
group.toActiveSelection(); |
|
canvas.requestRenderAll(); |
|
} |
|
|
|
function initCanvas(elem){ |
|
const canvas = window.openpose_editor_canvas = new fabric.Canvas(elem, { |
|
backgroundColor: '#000', |
|
|
|
preserveObjectStacking: true |
|
}); |
|
|
|
window.openpose_editor_elem = elem |
|
|
|
canvas.on('object:moving', function(e) { |
|
if ("_objects" in e.target) { |
|
const rtop = e.target.top |
|
const rleft = e.target.left |
|
for (const item of e.target._objects){ |
|
let p = item; |
|
const top = rtop + p.top * e.target.scaleY + e.target.height * e.target.scaleY / 2; |
|
const left = rleft + p.left * e.target.scaleX + e.target.width * e.target.scaleX / 2; |
|
if (p["id"] === 0) { |
|
p.line1 && p.line1.set({ 'x1': left, 'y1': top }); |
|
}else{ |
|
p.line1 && p.line1.set({ 'x2': left, 'y2': top }); |
|
} |
|
p.line2 && p.line2.set({ 'x1': left, 'y1': top }); |
|
p.line3 && p.line3.set({ 'x1': left, 'y1': top }); |
|
p.line4 && p.line4.set({ 'x1': left, 'y1': top }); |
|
p.line5 && p.line5.set({ 'x1': left, 'y1': top }); |
|
} |
|
}else{ |
|
var p = e.target; |
|
if (p["id"] === 0) { |
|
p.line1 && p.line1.set({ 'x1': p.left, 'y1': p.top }); |
|
}else{ |
|
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top }); |
|
} |
|
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top }); |
|
p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top }); |
|
p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top }); |
|
p.line5 && p.line5.set({ 'x1': p.left, 'y1': p.top }); |
|
} |
|
canvas.renderAll(); |
|
}); |
|
|
|
canvas.on('object:scaling', function(e) { |
|
if ("_objects" in e.target) { |
|
const rtop = e.target.top |
|
const rleft = e.target.left |
|
for (const item of e.target._objects){ |
|
let p = item; |
|
const top = rtop + p.top * e.target.scaleY + e.target.height * e.target.scaleY / 2; |
|
const left = rleft + p.left * e.target.scaleX + e.target.width * e.target.scaleX / 2; |
|
if (p["id"] === 0) { |
|
p.line1 && p.line1.set({ 'x1': left, 'y1': top }); |
|
}else{ |
|
p.line1 && p.line1.set({ 'x2': left, 'y2': top }); |
|
} |
|
p.line2 && p.line2.set({ 'x1': left, 'y1': top }); |
|
p.line3 && p.line3.set({ 'x1': left, 'y1': top }); |
|
p.line4 && p.line4.set({ 'x1': left, 'y1': top }); |
|
p.line5 && p.line5.set({ 'x1': left, 'y1': top }); |
|
} |
|
} |
|
canvas.renderAll(); |
|
}); |
|
|
|
canvas.on('object:rotating', function(e) { |
|
if ("_objects" in e.target) { |
|
const rtop = e.target.top |
|
const rleft = e.target.left |
|
for (const item of e.target._objects){ |
|
let p = item; |
|
const top = rtop + p.top |
|
const left = rleft + p.left |
|
if (p["id"] === 0) { |
|
p.line1 && p.line1.set({ 'x1': left, 'y1': top }); |
|
}else{ |
|
p.line1 && p.line1.set({ 'x2': left, 'y2': top }); |
|
} |
|
p.line2 && p.line2.set({ 'x1': left, 'y1': top }); |
|
p.line3 && p.line3.set({ 'x1': left, 'y1': top }); |
|
p.line4 && p.line4.set({ 'x1': left, 'y1': top }); |
|
p.line5 && p.line5.set({ 'x1': left, 'y1': top }); |
|
} |
|
} |
|
canvas.renderAll(); |
|
}); |
|
|
|
canvas.on("object:added", function () { |
|
if (lockMode) return; |
|
undo_history.push(JSON.stringify(canvas)); |
|
redo_history.length = 0; |
|
}); |
|
|
|
canvas.on("object:modified", function () { |
|
if (lockMode) return; |
|
undo_history.push(JSON.stringify(canvas)); |
|
redo_history.length = 0; |
|
}); |
|
|
|
resizeCanvas(...openpose_obj.resolution) |
|
|
|
setPose(default_keypoints) |
|
|
|
undo_history.push(JSON.stringify(canvas)); |
|
|
|
const json_observer = new MutationObserver((m) => { |
|
if(gradioApp().querySelector('#tab_openpose_editor').style.display!=='block') return; |
|
try { |
|
const raw = gradioApp().querySelector("#hide_json").querySelector("textarea").value.replaceAll("'", '"') |
|
const json = JSON.parse(raw) |
|
|
|
let candidate = json["candidate"] |
|
let subset = json["subset"] |
|
const li = [] |
|
subset = subset.splice(0, 18) |
|
for (i=0; subset.length > i; i++){ |
|
if (Number.isInteger(subset[i]) && subset[i] >= 0){ |
|
li.push(candidate[subset[i]]) |
|
}else{ |
|
const ra_width = Math.floor(Math.random() * canvas.width) |
|
const ra_height = Math.floor(Math.random() * canvas.height) |
|
li.push([ra_width, ra_height]) |
|
} |
|
} |
|
|
|
setPose(li); |
|
|
|
const fileReader = new FileReader(); |
|
fileReader.onload = function() { |
|
const dataUri = this.result; |
|
canvas.setBackgroundImage(dataUri, canvas.renderAll.bind(canvas), { |
|
opacity: 0.5 |
|
}); |
|
const img = new Image(); |
|
img.onload = function() { |
|
resizeCanvas(this.width, this.height) |
|
} |
|
img.src = dataUri; |
|
} |
|
fileReader.readAsDataURL(gradioApp().querySelector("#openpose_editor_input").querySelector("input").files[0]); |
|
} catch(e){console.log(e)} |
|
}) |
|
json_observer.observe(gradioApp().querySelector("#hide_json"), { "attributes": true }) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
function resetCanvas(){ |
|
const canvas = openpose_editor_canvas; |
|
canvas.clear() |
|
canvas.backgroundColor = "#000" |
|
} |
|
|
|
function savePNG(){ |
|
openpose_editor_canvas.getObjects("image").forEach((img) => { |
|
img.set({ |
|
opacity: 0 |
|
}); |
|
}) |
|
if (openpose_editor_canvas.backgroundImage) openpose_editor_canvas.backgroundImage.opacity = 0 |
|
openpose_editor_canvas.discardActiveObject(); |
|
openpose_editor_canvas.renderAll() |
|
openpose_editor_elem.toBlob((blob) => { |
|
const a = document.createElement("a"); |
|
a.href = URL.createObjectURL(blob); |
|
a.download = "pose.png"; |
|
a.click(); |
|
URL.revokeObjectURL(a.href); |
|
}); |
|
openpose_editor_canvas.getObjects("image").forEach((img) => { |
|
img.set({ |
|
opacity: 1 |
|
}); |
|
}) |
|
if (openpose_editor_canvas.backgroundImage) openpose_editor_canvas.backgroundImage.opacity = 0.5 |
|
openpose_editor_canvas.renderAll() |
|
return |
|
} |
|
|
|
function saveJSON(){ |
|
const canvas = openpose_editor_canvas |
|
const json = JSON.stringify({ |
|
"width": canvas.width, |
|
"height": canvas.height, |
|
"keypoints": openpose_editor_canvas.getObjects().filter((item) => { |
|
if (item.type === "circle") return item |
|
}).map((item) => { |
|
return [Math.round(item.left), Math.round(item.top)] |
|
}) |
|
}, null, 4) |
|
const blob = new Blob([json], { |
|
type: 'text/plain' |
|
}); |
|
const a = document.createElement("a"); |
|
a.href = URL.createObjectURL(blob); |
|
a.download = "pose.json"; |
|
a.click(); |
|
URL.revokeObjectURL(a.href); |
|
} |
|
|
|
function loadJSON(){ |
|
const input = document.createElement("input"); |
|
input.type = "file" |
|
input.click() |
|
input.addEventListener("change", function(e){ |
|
const file = e.target.files[0]; |
|
var fileReader = new FileReader(); |
|
fileReader.onload = function() { |
|
try { |
|
const json = JSON.parse(this.result) |
|
if (json["width"] && json["height"]) { |
|
resizeCanvas(json["width"], json["height"]) |
|
}else{ |
|
throw new Error('width, height is invalid'); |
|
} |
|
if (json["keypoints"].length % 18 === 0) { |
|
setPose(json["keypoints"]) |
|
}else{ |
|
throw new Error('keypoints is invalid') |
|
} |
|
return [json["width"], json["height"]] |
|
}catch(e){ |
|
console.error(e) |
|
alert("Invalid JSON") |
|
} |
|
} |
|
fileReader.readAsText(file); |
|
}) |
|
input.click() |
|
} |
|
|
|
function addBackground(){ |
|
const input = document.createElement("input"); |
|
input.type = "file" |
|
input.accept = "image/*" |
|
input.addEventListener("change", function(e){ |
|
const canvas = openpose_editor_canvas |
|
const file = e.target.files[0]; |
|
var fileReader = new FileReader(); |
|
fileReader.onload = function() { |
|
var dataUri = this.result; |
|
canvas.setBackgroundImage(dataUri, canvas.renderAll.bind(canvas), { |
|
opacity: 0.5 |
|
}); |
|
const img = new Image(); |
|
img.onload = function() { |
|
resizeCanvas(this.width, this.height) |
|
} |
|
img.src = dataUri; |
|
} |
|
fileReader.readAsDataURL(file); |
|
}) |
|
input.click() |
|
} |
|
|
|
function detectImage(){ |
|
gradioApp().querySelector("#openpose_editor_input").querySelector("input").click() |
|
} |
|
|
|
function sendImage(){ |
|
openpose_editor_canvas.getObjects("image").forEach((img) => { |
|
img.set({ |
|
opacity: 0 |
|
}); |
|
}) |
|
if (openpose_editor_canvas.backgroundImage) openpose_editor_canvas.backgroundImage.opacity = 0 |
|
openpose_editor_canvas.discardActiveObject(); |
|
openpose_editor_canvas.renderAll() |
|
openpose_editor_elem.toBlob((blob) => { |
|
const file = new File(([blob]), "pose.png") |
|
const dt = new DataTransfer(); |
|
dt.items.add(file); |
|
const list = dt.files |
|
gradioApp().querySelector("#txt2img_script_container").querySelectorAll("span.transition").forEach((elem) => { |
|
if (elem.previousElementSibling.textContent === "ControlNet"){ |
|
switch_to_txt2img() |
|
elem.className.includes("rotate-90") && elem.parentElement.click(); |
|
const input = elem.parentElement.parentElement.querySelector("input[type='file']"); |
|
const button = elem.parentElement.parentElement.querySelector("button[aria-label='Clear']") |
|
button && button.click(); |
|
input.value = ""; |
|
input.files = list; |
|
const event = new Event('change', { 'bubbles': true, "composed": true }); |
|
input.dispatchEvent(event); |
|
} |
|
}) |
|
}); |
|
openpose_editor_canvas.getObjects("image").forEach((img) => { |
|
img.set({ |
|
opacity: 1 |
|
}); |
|
}) |
|
if (openpose_editor_canvas.backgroundImage) openpose_editor_canvas.backgroundImage.opacity = 0.5 |
|
openpose_editor_canvas.renderAll() |
|
} |
|
|
|
window.addEventListener('DOMContentLoaded', () => { |
|
const observer = new MutationObserver((m) => { |
|
if(!executed && gradioApp().querySelector('#openpose_editor_canvas')){ |
|
executed = true; |
|
initCanvas(gradioApp().querySelector('#openpose_editor_canvas')) |
|
|
|
|
|
|
|
observer.disconnect(); |
|
} |
|
}) |
|
observer.observe(gradioApp(), { childList: true, subtree: true }) |
|
}) |
|
|