PageSourceSearch

https://yunpeng1998.github.io/Qwen-Video-Edit-Page/

html yunpeng1998.github.io collected 2026-10-03 10:13:45 UTC 14,214 bytes, 266 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8">
5<meta name="viewport" content="width=device-width, initial-scale=1">
6<title>Qwen-Video-Edit — Instruction-based video editing by repurposing an image editing model</title>
7<link rel="preconnect" href="https://fonts.googleapis.com">
8<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;800&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
9<style>
10:root{
11  --ink:#191a23; --muted:#5c5f70; --bg:#fbfaf8; --card:#ffffff;
12  --line:#e8e6e1; --accent:#5b5bd6; --accent2:#d6409f;
13  --grad:linear-gradient(92deg,#5b5bd6 0%,#8e4ec6 55%,#d6409f 100%);
14  --shadow:0 1px 2px rgba(25,26,35,.05),0 12px 32px -12px rgba(25,26,35,.14);
15}
16*{box-sizing:border-box;margin:0;padding:0}
17html{scroll-behavior:smooth}
18body{font-family:Inter,system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--ink);line-height:1.65}
19.wrap{max-width:1120px;margin:0 auto;padding:0 24px}
20a{color:var(--accent);text-decoration:none}
21a:hover{text-decoration:underline}
22
23/* ---------- hero header ---------- */
24header{padding:72px 0 36px;text-align:center;background:
25  radial-gradient(900px 420px at 50% -160px, rgba(91,91,214,.10), transparent 70%),
26  radial-gradient(700px 380px at 82% -60px, rgba(214,64,159,.07), transparent 70%)}
27h1.title{font-size:clamp(34px,5.4vw,58px);font-weight:800;letter-spacing:-.02em;line-height:1.15}
28.strike{position:relative;display:inline-block;color:var(--muted)}
29.strike::after{content:"";position:absolute;left:-2%;right:-2%;top:54%;height:.09em;border-radius:2px;
30  background:var(--accent2);transform:scaleX(0);transform-origin:left center;animation:strike .5s .7s ease-out forwards;rotate:-4deg}
31@keyframes strike{to{transform:scaleX(1)}}
32.videoword{position:relative;display:inline-block;background:var(--grad);
33  -webkit-background-clip:text;background-clip:text;color:transparent;
34  opacity:0;translate:0 .35em;animation:pop .55s 1.15s cubic-bezier(.2,1.4,.4,1) forwards}
35@keyframes pop{to{opacity:1;translate:0 0}}
36.subtitle{margin-top:14px;font-size:clamp(16px,2vw,21px);color:var(--muted);font-weight:500}
37.authors{margin-top:16px;font-size:clamp(15px,1.8vw,19px);color:var(--ink);font-weight:600}
38.authors span{margin:0 10px;white-space:nowrap}
39.authors a{color:inherit;text-decoration:none;border-bottom:1.5px solid var(--accent)}
40.authors a:hover{color:var(--accent)}
41.byline{margin-top:10px;color:var(--muted);font-size:14.5px}
42.btns{margin-top:26px;display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
43.btn{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;border-radius:999px;font-weight:600;font-size:15px;
44  border:1.5px solid var(--line);background:var(--card);color:var(--ink);box-shadow:var(--shadow);transition:.15s}
45.btn:hover{border-color:var(--accent);text-decoration:none;translate:0 -1px}
46.btn.primary{background:var(--grad);color:#fff;border:none}
47
48/* ---------- sections ---------- */
49section{padding:44px 0}
50h2{font-size:clamp(24px,3vw,32px);font-weight:800;letter-spacing:-.01em;margin-bottom:8px}
51.sec-sub{color:var(--muted);margin-bottom:26px;max-width:820px}
52.center{text-align:center}.center .sec-sub{margin-left:auto;margin-right:auto}
53
54/* ---------- comparison slider ---------- */
55.cmp{position:relative;border-radius:16px;overflow:hidden;background:#111;box-shadow:var(--shadow);
56  user-select:none;-webkit-user-select:none;touch-action:pan-y}
57.cmp video{display:block;width:100%;height:auto;pointer-events:none}
58.cmp .after-wrap{position:absolute;inset:0}
59.cmp .divider{position:absolute;top:0;bottom:0;width:2px;background:rgba(255,255,255,.9);box-shadow:0 0 8px rgba(0,0,0,.35);cursor:ew-resize}
60.cmp .handle{position:absolute;top:50%;left:50%;translate:-50% -50%;width:26px;height:26px;border-radius:50%;
61  background:rgba(255,255,255,.75);backdrop-filter:blur(2px);box-shadow:0 1px 6px rgba(0,0,0,.3);
62  display:flex;align-items:center;justify-content:center;font-size:10px;color:#444;font-weight:700;
63  transition:opacity .3s;opacity:.85}
64.cmp:hover .handle{opacity:1}
65.cmp .tag{position:absolute;top:10px;padding:4px 11px;border-radius:999px;font-size:12px;font-weight:700;
66  background:rgba(255,255,255,.92);color:#333;pointer-events:none}
67.cmp .tag.before{left:10px}.cmp .tag.after{right:10px}
68
69/* ---------- hero videos ---------- */
70/* Landscape slightly narrower than the exact equal-height ratio (3.82:1);
71   the taller portrait gets more width and the landscape card is vertically
72   centered against it. */
73.heroWrap{max-width:1240px}
74.heroGrid{display:grid;grid-template-columns:2.9fr 1fr;gap:26px;align-items:center}
75@media(max-width:840px){.heroGrid{grid-template-columns:1fr}}
76.heroCard .cap{margin-top:10px;color:var(--muted);font-size:14px;text-align:center}
77
78/* ---------- gallery ---------- */
79.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:22px}
80.card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:12px;box-shadow:var(--shadow)}
81.prompt{margin-top:10px;font-size:14px;color:var(--ink)}
82.prompt b{color:var(--accent)}
83.empty{padding:40px;text-align:center;color:var(--muted);border:2px dashed var(--line);border-radius:18px}
84
85/* ---------- method ---------- */
86.methodCard{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:28px;box-shadow:var(--shadow)}
87.flow{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:center;margin:22px 0 6px}
88.node{padding:10px 16px;border-radius:12px;font-size:13.5px;font-weight:600;border:1.5px solid var(--line);background:#fff}
89.node.frozen{border-style:dashed;color:var(--muted)}
90.node.train{background:linear-gradient(92deg,rgba(91,91,214,.10),rgba(214,64,159,.10));border-color:var(--accent)}
91.arrow{color:var(--muted);font-weight:700}
92.legend{display:flex;gap:18px;justify-content:center;margin-top:14px;font-size:12.5px;color:var(--muted)}
93.dotT{display:inline-block;width:10px;height:10px;border-radius:3px;background:linear-gradient(92deg,rgba(91,91,214,.5),rgba(214,64,159,.5));margin-right:6px}
94.dotF{display:inline-block;width:10px;height:10px;border-radius:3px;border:1.5px dashed var(--muted);margin-right:6px}
95
96/* ---------- bibtex / footer ---------- */
97pre.bib{background:#f4f2ee;border:1px solid var(--line);border-radius:14px;padding:20px;overflow-x:auto;
98  font-family:"JetBrains Mono",monospace;font-size:13px;line-height:1.6}
99footer{padding:44px 0 60px;color:var(--muted);font-size:14px;text-align:center;border-top:1px solid var(--line);margin-top:30px}
100</style>
101</head>
102<body>
103
104<header>
105  <div class="wrap">
106    <h1 class="title">Qwen-<span class="strike">Image</span>&nbsp;<span class="videoword">Video</span>-Edit</h1>
107    <p class="subtitle">Instruction-based video editing by repurposing an image editing model</p>
108    <p class="authors"><span><a href="https://yunpeng1998.github.io/">Yunpeng Bai</a></span><span>Yossi Gandelsman</span><span>Micha&euml;l Gharbi</span><span>Qixing Huang</span></p>
109    <p class="byline">No video-pretrained transformer required — an image editing DiT edits video-VAE latents directly.</p>
110    <div class="btns">
111      <a class="btn primary" href="https://arxiv.org/abs/2608.14790">📄 Report</a>  <!-- TODO: arXiv link -->
112      <a class="btn" href="https://github.com/yunpeng1998/Qwen-Video-Edit">Code</a>
113      <a class="btn" href="https://huggingface.co/yunpeng1998/Qwen-Video-Edit">🤗 Model</a>
114      <a class="btn" href="#method">Method</a>
115    </div>
116  </div>
117</header>
118
119<section>
120  <div class="wrap heroWrap">
121    <div class="heroGrid">
122      <div class="heroCard">
123        <div class="cmp" data-before="assets/hero2_before.mp4" data-after="assets/hero2_after.mp4"></div>
124        <p class="cap">Long-video editing — every 45-frame segment edited with a <em>different</em> instruction. Drag to compare.</p>
125      </div>
126      <div class="heroCard">
127        <div class="cmp" data-before="assets/hero1_before.mp4" data-after="assets/hero1_after.mp4"></div>
128        <p class="cap">Portrait videos supported natively (aspect-preserving).</p>
129      </div>
130    </div>
131  </div>
132</section>
133
134<section id="method">
135  <div class="wrap">
136    <h2>How it works</h2>
137    <p class="sec-sub">
138      Video generation models read and write <b>video-VAE latents</b>. We teach
139      <a href="https://huggingface.co/Qwen/Qwen-Image-Edit">Qwen-Image-Edit</a>'s transformer to edit those latents
140      directly: two tiny projections bridge Wan&nbsp;2.1's latent space into the DiT's token space, warm-started from the
141      DiT's own input/output layers so that a <em>static</em> video is embedded exactly like an image the model already
142      understands. The latent frames are arranged as tiles of one big virtual image — the same positional treatment the
143      image model was pretrained on. Fine-tuned with LoRA or full parameters on
144      <a href="https://github.com/EzioBy/Ditto">Ditto-1M</a> (source, edited, instruction) triplets, then refined by a
145      few steps of Wan&nbsp;2.2 denoising-enhancement.
146    </p>
147    <div class="methodCard">
148      <div class="flow">
149        <span class="node frozen">Source video</span><span class="arrow">→</span>
150        <span class="node frozen">Wan 2.1 VAE ❄</span><span class="arrow">→</span>
151        <span class="node train">In-projection 🔥</span><span class="arrow">→</span>
152        <span class="node train">Qwen-Image-Edit DiT 🔥<br><small>grid RoPE over latent frames · prompt via Qwen2.5-VL</small></span><span class="arrow">→</span>
153        <span class="node train">Out-projection 🔥</span><span class="arrow">→</span>
154        <span class="node frozen">Wan 2.1 VAE ❄</span><span class="arrow">→</span>
155        <span class="node frozen">Wan 2.2 enhance ❄</span><span class="arrow">→</span>
156        <span class="node frozen">Edited video</span>
157      </div>
158      <div class="legend"><span><span class="dotT"></span>trained (LoRA / full FT)</span><span><span class="dotF"></span>frozen</span></div>
159    </div>
160  </div>
161</section>
162
163<section>
164  <div class="wrap center">
165    <h2>Results</h2>
166    <p class="sec-sub">Drag the divider on any clip to compare the original (left) with the edited result (right).</p>
167    <div class="grid" id="gallery"></div>
168    <div class="empty" id="galleryEmpty" style="display:none">
169      Gallery is empty — drop <code>{name}_enhanced_f4b4.mp4 / {name}_input.mp4 / {name}.txt</code> triples into
170      <code>assets/gallery/</code> and run <code>python build_gallery.py</code>.
171    </div>
172  </div>
173</section>
174
175<section>
176  <div class="wrap">
177    <h2>BibTeX</h2>
178<pre class="bib">@misc{qwenvideoedit2026,
179  title  = {Qwen-Video-Edit: Instruction-based Video Editing by Repurposing an Image Editing Model},
180  author = {Yunpeng Bai, Yossi Gandelsman, Michaël Gharbi, Qixing Huang},
181  year   = {2026},
182  url    = {https://github.com/yunpeng1998/Qwen-Video-Edit}
183}</pre>
184  </div>
185</section>
186
187<footer>
188  Built on <a href="https://github.com/modelscope/DiffSynth-Studio">DiffSynth-Studio</a>,
189  <a href="https://huggingface.co/Qwen/Qwen-Image-Edit-2511">Qwen-Image-Edit</a>,
190  <a href="https://github.com/Wan-Video">Wan 2.1 / 2.2</a> and
191  <a href="https://github.com/EzioBy/Ditto">Ditto</a>.
192</footer>
193
194<script src="assets/gallery.js"></script>
vendor: 1 bytes, line 194
194
195<script>
196/* ---------- before/after comparison slider with frame-synced videos ---------- */
197function buildCompare(el){
198  const before=document.createElement("video"), after=document.createElement("video");
199  for(const v of [before,after]){v.muted=true;v.loop=true;v.playsInline=true;v.setAttribute("playsinline","");v.preload="metadata";}
200  before.src=el.dataset.before; after.src=el.dataset.after;
201  const wrapA=document.createElement("div"); wrapA.className="after-wrap"; wrapA.appendChild(after);
202  const divider=document.createElement("div"); divider.className="divider";
203  const handle=document.createElement("div"); handle.className="handle"; handle.textContent="◂▸";
204  divider.appendChild(handle);
205  const tagB=document.createElement("span"); tagB.className="tag before"; tagB.textContent="Original";
206  const tagA=document.createElement("span"); tagA.className="tag after"; tagA.textContent="Edited";
207  el.append(before,wrapA,divider,tagB,tagA);
208
209  let pos=0.5;
210  function render(){
211    wrapA.style.clipPath=`inset(0 0 0 ${pos*100}%)`;
212    divider.style.left=`calc(${pos*100}% - 1.25px)`;
213  }
214  render();
215  function setFromEvent(e){
216    const r=el.getBoundingClientRect();
217    pos=Math.min(1,Math.max(0,(e.clientX-r.left)/r.width));
218    render();
219  }
220  let dragging=false;
221  el.addEventListener("pointerdown",e=>{dragging=true;el.setPointerCapture(e.pointerId);setFromEvent(e);});
222  el.addEventListener("pointermove",e=>{if(dragging)setFromEvent(e);});
223  el.addEventListener("pointerup",()=>dragging=false);
224  el.addEventListener("pointercancel",()=>dragging=false);
225
226  /* keep the two videos frame-locked: 'before' is the clock */
227  function tick(){
228    if(!before.paused && Math.abs(after.currentTime-before.currentTime)>0.08){
229      after.currentTime=before.currentTime;
230    }
231    requestAnimationFrame(tick);
232  }
233  requestAnimationFrame(tick);
234
235  /* play only while on screen (dozens of videos on one page) */
236  new IntersectionObserver(entries=>{
237    for(const en of entries){
238      if(en.isIntersecting){before.play().catch(()=>{});after.play().catch(()=>{});}
239      else{before.pause();after.pause();}
240    }
241  },{threshold:0.15}).observe(el);
242}
243
244document.querySelectorAll(".cmp").forEach(buildCompare);
245
246/* ---------- gallery from manifest ---------- */
247const gallery=document.getElementById("gallery");
248const items=(window.GALLERY||[]);
249if(items.length===0){document.getElementById("galleryEmpty").style.display="block";}
250for(const item of items){
251  const card=document.createElement("div"); card.className="card";
252  const cmp=document.createElement("div"); cmp.className="cmp";
253  cmp.dataset.before=item.before; cmp.dataset.after=item.after;
254  card.appendChild(cmp);
255  if(item.prompt){
256    const p=document.createElement("p"); p.className="prompt";
257    p.innerHTML="<b>Prompt:</b> ";
258    p.appendChild(document.createTextNode(item.prompt));
259    card.appendChild(p);
260  }
261  gallery.appendChild(card);
262  buildCompare(cmp);
263}
264</script>
264
265</body>
266</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.