이미지-투-이미지 강도

img2img strength

입력 이미지를 얼마나 남기고 얼마나 새로 그릴지 정하는 0에서 1 사이의 비율. 낮으면 원본에 가깝고, 높으면 원본을 거의 무시합니다.

다른 이름: Denoising strengthimg2img denoise strength
···
html
<div class="wrap">
  <canvas id="cv"></canvas>
  <div class="panel"><span>strength</span><b id="val">0.0</b><div class="bar"><div class="fill" id="fill"></div></div></div>
</div>
css
.wrap{position:relative;width:100%;height:100%}
#cv{position:absolute;inset:0;width:100%;height:100%}
.panel{position:absolute;left:10px;right:10px;bottom:10px;z-index:2;padding:7px 10px;border-radius:10px;
  background:rgba(13,13,18,0.55);backdrop-filter:blur(6px);border:1px solid rgba(255,255,255,0.15);
  display:flex;align-items:center;gap:8px;font-size:11px;font-weight:700;color:#f1f0ec;flex-wrap:wrap}
.bar{flex:1;min-width:60px;height:5px;border-radius:99px;background:rgba(255,255,255,0.15);overflow:hidden}
.fill{height:100%;width:0;background:var(--accent);transition:width .08s linear}
js
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const val = document.getElementById('val'), fill = document.getElementById('fill');
function fit() { const dpr = Math.min(devicePixelRatio || 1, 2); cv.width = innerWidth * dpr; cv.height = innerHeight * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }
addEventListener('resize', fit); fit();

function mulberry32(a) { return function () { a |= 0; a = a + 0x6D2B79F5 | 0; let t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
function lerp(a, b, t) { return a + (b - a) * t; }

function render(s) {
  const w = innerWidth, h = innerHeight;
  ctx.clearRect(0, 0, w, h);
  const hue = lerp(200, 15, s);
  const g = ctx.createLinearGradient(0, 0, 0, h);
  g.addColorStop(0, 'hsl(' + hue + ' 55% ' + lerp(60, 45, s) + '%)'); g.addColorStop(1, 'hsl(' + (hue + 30) + ' 40% 30%)');
  ctx.fillStyle = g; ctx.fillRect(0, 0, w, h);
  const r = mulberry32(4);
  for (let l = 0; l < 2; l++) {
    ctx.beginPath(); ctx.moveTo(0, h);
    const jitter = r() * 0.12 * s;
    for (let i = 0; i <= 6; i++) { const x = w * i / 6; const y = h * (0.55 + l * 0.15) - h * (0.06 + jitter) * (1 + Math.sin(i + s * 6)); ctx.lineTo(x, y); }
    ctx.lineTo(w, h); ctx.closePath();
    ctx.fillStyle = 'hsl(' + lerp(150, 340, s) + ' 40% ' + (20 + l * 12) + '%)'; ctx.fill();
  }
  ctx.beginPath(); ctx.arc(lerp(w * 0.75, w * 0.3, s), lerp(h * 0.22, h * 0.35, s), Math.min(w, h) * 0.07, 0, Math.PI * 2);
  ctx.fillStyle = 'hsl(50 90% 75%)'; ctx.fill();
}
let s = 0, dir = 1;
setInterval(() => {
  render(s); val.textContent = s.toFixed(2); fill.style.width = (s * 100) + '%';
  s += dir * 0.01;
  if (s >= 1) { s = 1; dir = -1; } if (s <= 0) { s = 0; dir = 1; }
}, 60);
render(0);

이미지-투-이미지는 순수한 노이즈가 아니라 입력 이미지에서 출발합니다. 정확히는 입력 이미지에 노이즈를 어느 정도 더한 상태에서 시작하는데, 강도(strength) 값이 그 노이즈 양 — 그리고 실제로 몇 번의 디노이징 스텝이 도는지 — 를 결정합니다.

강도가 0에 가까우면 더해지는 노이즈가 거의 없어서 디노이징도 거의 일어나지 않고, 결과는 입력과 거의 같습니다. 강도가 1에 가까우면 노이즈가 거의 완전히 뒤덮어서 입력의 흔적이 희미해지고, 사실상 순수 텍스트-투-이미지에 가까워집니다.

스케치나 사진을 다른 스타일로 다시 그리되 구도는 지키고 싶다면 낮은 강도가, 입력을 참고만 하고 전혀 새로운 결과를 얻고 싶다면 높은 강도가 맞습니다. 정답인 값은 없고 원하는 "원본에 대한 충실도"에 따라 고르는 값입니다.

아래 데모는 실제 노이즈 주입·디노이징 대신, 고정된 원본 구도와 완전히 다른 구도 사이를 강도 값만큼 색상·형태를 섞어서 보간하는 절차적 시뮬레이션입니다.

언제 쓰나

원본 구도를 지키고 싶으면 낮게, 원본을 발판 삼아 완전히 새로운 결과를 원하면 높게 잡습니다. 중간값에서는 둘 다 어중간하게 섞일 수 있어 결과를 직접 보며 조정하는 편이 낫습니다.