Match upstream conditioned sequence hand-off
This commit is contained in:
parent
81337b1242
commit
ec868d8e72
@ -384,7 +384,6 @@ func main() {
|
|||||||
for _, segment := range request.Segments {
|
for _, segment := range request.Segments {
|
||||||
totalFrames += segment.Frames
|
totalFrames += segment.Frames
|
||||||
}
|
}
|
||||||
totalFrames -= request.TransitionFrames * (len(request.Segments) - 1)
|
|
||||||
a := &animation{ID: token(), Prompt: request.Segments[0].Prompt, Frames: totalFrames, DiffusionSteps: request.Steps, Seed: request.Seed, CreatedAt: time.Now().UTC().Format(time.RFC3339), Status: "queued", Kind: "generated", Model: request.Model, Segments: request.Segments, TransitionFrames: request.TransitionFrames}
|
a := &animation{ID: token(), Prompt: request.Segments[0].Prompt, Frames: totalFrames, DiffusionSteps: request.Steps, Seed: request.Seed, CreatedAt: time.Now().UTC().Format(time.RFC3339), Status: "queued", Kind: "generated", Model: request.Model, Segments: request.Segments, TransitionFrames: request.TransitionFrames}
|
||||||
g.mu.Lock()
|
g.mu.Lock()
|
||||||
g.items[a.ID] = a
|
g.items[a.ID] = a
|
||||||
|
|||||||
@ -39,7 +39,7 @@ window.addEventListener('load', async () => {
|
|||||||
const count = document.createElement('div'); count.className = 'hint';
|
const count = document.createElement('div'); count.className = 'hint';
|
||||||
const updateCount = () => {
|
const updateCount = () => {
|
||||||
const prompts = sequence.querySelectorAll('.sequence-prompt');
|
const prompts = sequence.querySelectorAll('.sequence-prompt');
|
||||||
count.textContent = `${prompts.length} segment${prompts.length === 1 ? '' : 's'} · 5-frame overlap`;
|
count.textContent = `${prompts.length} segment${prompts.length === 1 ? '' : 's'} · 5-frame conditioned hand-off`;
|
||||||
};
|
};
|
||||||
const addSegment = (text = '', frames = 150) => {
|
const addSegment = (text = '', frames = 150) => {
|
||||||
const row = document.createElement('div'); row.style.cssText = 'display:grid;grid-template-columns:1fr 74px auto;gap:7px;align-items:start';
|
const row = document.createElement('div'); row.style.cssText = 'display:grid;grid-template-columns:1fr 74px auto;gap:7px;align-items:start';
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
namespace kimodo {
|
namespace kimodo {
|
||||||
@ -120,11 +121,17 @@ std::expected<motion_data, std::string> model::generate_text_sequence(
|
|||||||
return std::unexpected("each sequence segment must contain a prompt and have 2..300 frames");
|
return std::unexpected("each sequence segment must contain a prompt and have 2..300 frames");
|
||||||
if (index && transition_frames >= segment.frames) return std::unexpected("transition must be shorter than every following segment");
|
if (index && transition_frames >= segment.frames) return std::unexpected("transition must be shorter than every following segment");
|
||||||
auto embedding=impl_->text->encode(segment.prompt); if (!embedding) return std::unexpected(embedding.error());
|
auto embedding=impl_->text->encode(segment.prompt); if (!embedding) return std::unexpected(embedding.error());
|
||||||
std::vector<float> noise(static_cast<size_t>(segment.frames)*273); for (float &value : noise) value=normal(rng);
|
// NVIDIA's _multiprompt samples an additional conditioned prefix for
|
||||||
|
// every continuation. That prefix replaces the old tail, leaving the
|
||||||
|
// caller-requested number of new frames after it is discarded.
|
||||||
|
const auto sampled_frames = static_cast<size_t>(segment.frames) +
|
||||||
|
(index == 0 ? 0 : transition_frames);
|
||||||
|
std::vector<float> noise(sampled_frames*273); for (float &value : noise) value=normal(rng);
|
||||||
std::vector<float> current;
|
std::vector<float> current;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
auto sampled=detail::sample_motion_from_noise(*impl_->weights,noise,*embedding,segment.frames,steps,text_cfg,constraint_cfg);
|
auto sampled=detail::sample_motion_from_noise(*impl_->weights,noise,*embedding,segment.frames,steps,text_cfg,constraint_cfg);
|
||||||
if (!sampled) return std::unexpected(sampled.error()); current=std::move(*sampled);
|
if (!sampled) return std::unexpected(sampled.error());
|
||||||
|
current=std::move(*sampled);
|
||||||
} else {
|
} else {
|
||||||
// Derived from NVIDIA's Apache-2.0 `_multiprompt` sampler:
|
// Derived from NVIDIA's Apache-2.0 `_multiprompt` sampler:
|
||||||
// https://github.com/nv-tlabs/kimodo/blob/main/kimodo/model/kimodo_model.py
|
// https://github.com/nv-tlabs/kimodo/blob/main/kimodo/model/kimodo_model.py
|
||||||
@ -133,12 +140,18 @@ std::expected<motion_data, std::string> model::generate_text_sequence(
|
|||||||
std::vector<float> observed(noise.size()), observed_mask(noise.size());
|
std::vector<float> observed(noise.size()), observed_mask(noise.size());
|
||||||
const auto overlap=static_cast<size_t>(transition_frames);
|
const auto overlap=static_cast<size_t>(transition_frames);
|
||||||
const auto previous_start=previous.size()-overlap*273;
|
const auto previous_start=previous.size()-overlap*273;
|
||||||
// FullBodyConstraintSet conditions smooth root, heading, local
|
// FullBodyConstraintSet's captured mask is deliberately sparse:
|
||||||
// joint positions, and global rotations (203 values), but not
|
// global root/posed joints [0,71), smooth root [113,125), and
|
||||||
// generated velocities or contact labels.
|
// global rotations [191,203). In particular, the gaps contain
|
||||||
|
// generated velocities and must not be treated as observed.
|
||||||
|
constexpr std::array<std::pair<size_t, size_t>, 3> constrained = {{
|
||||||
|
{0, 71}, {113, 125}, {191, 203},
|
||||||
|
}};
|
||||||
for (size_t frame=0; frame<overlap; ++frame) {
|
for (size_t frame=0; frame<overlap; ++frame) {
|
||||||
std::copy_n(previous.data()+previous_start+frame*273,203,observed.data()+frame*273);
|
std::copy_n(previous.data()+previous_start+frame*273,203,observed.data()+frame*273);
|
||||||
std::fill_n(observed_mask.data()+frame*273,203,1.f);
|
for (const auto &[first, last] : constrained)
|
||||||
|
std::fill(observed_mask.begin()+static_cast<std::ptrdiff_t>(frame*273+first),
|
||||||
|
observed_mask.begin()+static_cast<std::ptrdiff_t>(frame*273+last), 1.f);
|
||||||
}
|
}
|
||||||
const float origin_x=observed[0]*(*gs)[0]+(*gm)[0];
|
const float origin_x=observed[0]*(*gs)[0]+(*gm)[0];
|
||||||
const float origin_z=observed[2]*(*gs)[2]+(*gm)[2];
|
const float origin_z=observed[2]*(*gs)[2]+(*gm)[2];
|
||||||
@ -149,11 +162,12 @@ std::expected<motion_data, std::string> model::generate_text_sequence(
|
|||||||
}
|
}
|
||||||
const auto p=(previous.size()/273-overlap)*273;
|
const auto p=(previous.size()/273-overlap)*273;
|
||||||
const float heading=std::atan2(previous[p+4]*(*gs)[4]+(*gm)[4],previous[p+3]*(*gs)[3]+(*gm)[3]);
|
const float heading=std::atan2(previous[p+4]*(*gs)[4]+(*gm)[4],previous[p+3]*(*gs)[3]+(*gm)[3]);
|
||||||
auto sampled=detail::sample_motion_from_noise_conditioned(*impl_->weights,noise,*embedding,observed,observed_mask,heading,segment.frames,steps,text_cfg,constraint_cfg);
|
auto sampled=detail::sample_motion_from_noise_conditioned(*impl_->weights,noise,*embedding,observed,observed_mask,heading,sampled_frames,steps,text_cfg,constraint_cfg);
|
||||||
if (!sampled) return std::unexpected(sampled.error()); current=std::move(*sampled);
|
if (!sampled) return std::unexpected(sampled.error());
|
||||||
|
current=std::move(*sampled);
|
||||||
// `_multiprompt` samples in the translated local coordinates, then
|
// `_multiprompt` samples in the translated local coordinates, then
|
||||||
// restores the prior segment's planar smooth-root origin.
|
// restores the prior segment's planar smooth-root origin.
|
||||||
for (size_t frame=0; frame<segment.frames; ++frame) {
|
for (size_t frame=0; frame<sampled_frames; ++frame) {
|
||||||
auto *row=current.data()+frame*273;
|
auto *row=current.data()+frame*273;
|
||||||
row[0]=((row[0]*(*gs)[0]+(*gm)[0])+origin_x)/(*gs)[0];
|
row[0]=((row[0]*(*gs)[0]+(*gm)[0])+origin_x)/(*gs)[0];
|
||||||
row[2]=((row[2]*(*gs)[2]+(*gm)[2])+origin_z)/(*gs)[2];
|
row[2]=((row[2]*(*gs)[2]+(*gm)[2])+origin_z)/(*gs)[2];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user