HiDream skeleton: openpose ref 보다 prompt가 더 강력함 (8가지 패턴 실증)
요약
HiDream-O1-Image의 skeleton 모드에서 OpenPose 참조 이미지가 오히려 포즈의 자유도를 제한하는 현상을 8가지 패턴으로 분석했습니다. 실험 결과, 동적인 포즈를 구현하려면 OpenPose 참조를 제외하고 프롬프트로 지시하는 것이 더 효과적이며, 참조 이미지 개수를 줄여 해상도를 유지하는 것이 품질 향상에 유리함을 확인했습니다.
핵심 포인트
- OpenPose 참조 이미지를 사용하면 모델이 해당 구도에 고착되어 동적인 포즈 구현이 어려워짐
- 참조 이미지를 6장에서 3~4장으로 압축하면 개별 이미지 해상도가 높아져 세부 디테일 품질이 향상됨
- 용도에 따른 적절한 shift 값 설정이 중요함 (시착: 1.0, 포즈/의상 교체: 2.0-2.5, 장면 생성: 3.0)
- skeleton 모드는 내부적으로 multi-ref 경로를 공유하며, 특정 참조는 프롬프트로만 전달되는 구조적 특성을 가짐
TL;DR
HiDream-O1-Image (2026-05 출시, OpenWeight 8B, Artificial Analysis Text-to-Image Arena 8위의 T2I 모델)의 skeleton (= 시착) 모드에서 「포즈가 말을 듣지 않는」 현상을 8가지 패턴 + layout 3가지 패턴으로 실증 벤치마크한 결과, 3가지의 반직관적인 사실이 발견되었다.
openpose ref를 전달하면, 오히려 포즈가 ref의 구도에 고착된다. 동적인 포즈를 원할 때는 openpose ref를 제외하고 prompt를 통해 지시하는 것이 더 강력하다 - ref를 6장(face+bg+pose+parts의 풀 세트)으로 구성하면 개별 ref의 해상도가 768px로 떨어져 세부 사항이 무너진다. 3~4장으로 구성하여 1024px를 유지하는 편이 품질이 더 높다 - README에서 권장하는 shift=1.0은
시착 용도 전용이다. 포즈·의상 교체라면 shift=2.0-2.5, 장면 전체를 새로 만든다면 shift=3.0이다.
pipeline.py를 읽어보면 "skeleton 모드에 전용 처리가 없다"는 것을 알 수 있다. /generate/skeleton도 /generate/ip도 내부적으로는 완전히 동일한 multi-ref 경로를 통과하며, ref가 "얼굴/배경/openpose/의상" 중 무엇인지에 따라 프롬프트로만 전달되는 부분이 있다. 이것이 현상의 근본 원인이다.
동기
HiDream-O1-Image를 로컬 GPU (RTX PRO 6000 Blackwell, 96 GB)에 상주시켜 자체 플랫폼에 통합한 결과, skeleton (시착) 모드가 프롬프트 지시를 따르지 않는 문제에 직면했다. "양손을 들고 점프해줘"라고 써도, 뻣뻣하게 서 있는 시착 사진만 나온다.
가드레일(NSFW 검열이나 안전 정책 등) 때문인가 싶어 safety|nsfw|guard|filter|moderate|censor로 grep 해보았으나, HiDream의 codebase에는 그러한 기제는 전혀 없었다 (CSS의 backdrop-filter: blur만 검색됨). MIT 라이ace 대로 OpenWeight이며, 검열 프리이다.
그렇다면 무엇이 문제인가. pipeline.py를 읽으면서 8 + 3 패턴을 실제 기기에서 실행한 결과는 다음과 같다.
환경
- GPU: NVIDIA RTX PRO 6000 Blackwell Max-Q (96 GB VRAM)
- PyTorch: 2.12.0 + CUDA 13.0
- flash-attn: 2.8.3 (sm_120 한정 빌드)
- 모델: HiDream-O1-Image Full (8B, bf16, ~16.4 GiB resident)
- 추론 서버: 자체 Python BaseHTTPRequestHandler 상주 (port 8895)
- 해상도: pipeline 내부 버킷에서 강제로 2048×2048로 스냅
50 스텝 생성 1장당 실측:
| 모드 | 시간 | iter speed |
|---|---|---|
| t2i (ref 없음) | ~33s | 1.52 it/s |
| ... |
검증 소재
HiDream 리포지토리의 assets/IP_skeleton/에 스켈레톤용 풀 세트가 들어있다. 이를 원재료로 사용한다.
| ref | 내용 | 용도 상정 |
|---|---|---|
| 인물의 얼굴 사진 | Identity 참조 | |
| ... |
8 패턴 skeleton 벤치
각 패턴에서 /api/studio/skeleton (= HiDream의 generate_image()를 skeleton 모드에 상응하는 인자로 호출)을 실행하고 있다. shift와 guidance_scale 이외의 파라미터는 고정 (50 steps, seed=42).
A — 베이스라인 (README 기본값, 6 refs 풀 투입)
curl -X POST http://localhost:8895/generate/skeleton \
-H 'Content-Type: application/json' \
-d '{
...
결과: 원래의 bg ref의 벽·선반이 완전히 재현됨. 포즈도 openpose ref와 같이 부동 자세(standing still). 가상 피팅(try-on)으로서는 충실하지만 「움직임」 「자유도」는 전무함.
B — shift 높이기 (동일 6 refs, shift=2.5)
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "Create a realistic try-on image of the person wearing the provided clothing.",
"ref_image_paths": ["face","bg","openpose","part_1","part_2","part_3"],
...
결과: 선반이 흐릿해짐, 인물의 디자인이 미세하게 변화. 배경은 여전히 bg ref에 고착됨. shift를 높이는 것만으로는 bg ref의 영향력을 완전히 벗어날 수 없음.
C — guidance도 높이기 (shift=2.5, guidance=7.0)
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "...",
"ref_image_paths": [...6 refs...],
...
결과: 목걸이가 기묘한 형태로 변형됨. guidance를 높이면 artifacts (아티팩트)가 나타나기 시작함. Full 모델의 sweet spot (최적 지점)은 5.0이며, 7.0은 과도함.
D — ref를 3장으로 압축 (face + openpose + sweater) + 구체적 프롬프트
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "A young Asian woman wearing a gray oversized sweater dress, standing in a relaxed pose, full body shot, soft natural lighting, white studio background.",
"ref_image_paths": ["face","openpose","part_1"],
...
결과: 대폭 개선. 배경이 순백색 스튜디오로 바뀌고, 의상이 유지되며, 포즈도 자연스러움. bg ref를 제외한 효과가 매우 큼. 이것이 「올바른 가상 피팅」의 출력임.
E — 4 refs + ref 번호 명시 프롬프트
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "Full body try-on photograph. Subject: the woman from image 1. Pose: identical to the skeleton in image 2. Wearing: the gray oversized knit sweater dress shown in image 3, brown leather ankle boots shown in image 4. Studio lighting, plain background.",
"ref_image_paths": ["face","openpose","part_1","part_2"],
...
결과: D와 동등한 품질, boots (부츠)도 반영됨 (약간 절제된 형태). ref 번호를 명시하는 것은 효과가 있으나, 결정적인 차이는 아님.
제외하고 prompt로 포즈 지정
F — openpose를 제외하고 prompt로 포즈 지정
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "Full body photograph of the woman wearing the gray sweater dress and brown ankle boots, dynamic dancing pose with both arms raised above her head, joyful expression, photo studio with white seamless background, professional lighting.",
"ref_image_paths": ["face","part_1","part_2"],
...
결과: 🏆 양손을 들고 점프하는 포즈 완전 성공. openpose ref 없음 + prompt의 동적 지시를 통해 처음으로 움직임이 생성됨. openpose ref를 넣으면 prompt가 패배한다는 사실이 여기서 확정됨.
G — face 1장만 + 자유 prompt (의상도 완전 교체)
/generate/skeleton은 최소 2개의 refs 검증 (validation)이 있으므로 /generate/ip를 경유:
curl -X POST http://localhost:8895/generate/ip -d '{
"prompt": "Elegant full-body portrait of the woman wearing a vibrant red sequined evening gown with a thigh-high slit, standing confidently with one hand on her hip, soft cinematic lighting, dark blurred background.",
"ref_image_paths": ["face"],
...
결과: 🏆 붉은색 섹시 드레스 완전 생성. 얼굴의 동일성은 유지되며, 그 외의 모든 요소는 자유로움. face 1장 + shift 3.0이 최대 자유도 패턴임.
H — E와 동일 설정에서 seed=999 (변동성 확인)
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "Full body try-on photograph. ...",
"ref_image_paths": ["face","openpose","part_1","part_2"],
...
결과: E와 미세한 차이가 있으며, boots가 더 명확한 갈색으로 표현됨. seed를 변경하는 것은 세부 최적화에 효과적이므로, 실무에서는 3~5개의 seed를 돌려 best of N을 찾는 것이 정석임.
레이아웃 모드도 확인 (덤 3가지 패턴)
layout_bboxes를 통해 "여러 피사체를 이미지 내 어느 위치에 배치할지"를 상대 좌표 [x1, x2, y1, y2]로 지정할 수 있도록 되어 있음. 실제 동작을 확인했음.
입력 ref는 2인분의 얼굴 사진 (여성, 남성):
L1 — 가로 나열 (여성 left, 남성 right)
"layout_bboxes": "[[0.0,0.5,0.1,0.95],[0.5,1.0,0.1,0.95]]"
결과: 좌우가 뒤바뀜 (남성 left, 여성 right). ref 순서와 bbox 순서의 대응은 보장되지 않음.
L2 — 상하 분할 (여성 top, 남성 bottom)
"layout_bboxes": "[[0.2,0.8,0.0,0.5],[0.2,0.8,0.5,1.0]]"
결과: 여성이 뒤에, 남성이 앞에 있는 레이어 구도로 나타남. "상하"가 아니라 "깊이(depth)"로 해석됨.
L3 — 크기 차이 (여성 대, 남성 소)
"layout_bboxes": "[[0.1,0.65,0.1,0.95],[0.7,0.97,0.05,0.45]]"
결과: 양쪽 모두 거의 동일한 사이즈로 가로 나열됨. bbox의 사이즈 지정은 작동하지 않음.
→ Layout 모드는 **「단체 사진 생성의 힌트」**로 파악하는 것이 옳다. Photoshop의 정밀 배치와는 다르다. 여러 피사체를 한 장의 이미지로 만들 때의 구도 힌트 수준이며, 좌표 정밀도는 기대하지 않는다.
pipeline.py를 읽기
왜 그렇게 되는가 — HiDream의 동작을 지배하는 것은 models/pipeline.py의 generate_image() 함수이다. 읽어보고 알게 된 3가지 구조적 이유:
1. ref 수가 늘어나면 개별 해상도가 떨어진다
pipeline.py:198-202:
if K == 1: max_size = max(height, width) # 2048
elif K == 2: max_size = max(height, width) * 48 // 64 # 1536
elif K <= 4: max_size = max(height, width) // 2 # 1024
...
6장의 ref를 투입하면 각 ref가 768px로 압축된다. openpose의 가는 선, 의상의 세밀한 무늬, 얼굴의 표정이 한꺼번에 뭉개진다. 3~4장으로 압축하면 1024px로 유지되어 디테일이 살아난다.
2. skeleton 모드에 전용 처리가 없다
pipeline.py:178-275를 보면, skeleton용 특별 분기는 존재하지 않는다. /generate/skeleton도 /generate/ip도 내부적으로는 완전히 동일한 multi-ref 경로를 통과한다:
content = [{"type": "image"} for _ in range(K)]
content.append({"type": "text", "text": caption})
messages = [{"role": "user", "content": content}]
"이것은 얼굴", "이것은 openpose", "이것은 의상 파츠"라는 role hint는 모델에 전달되지 않는다. 모두 "단순한 참조 이미지 K장"으로서 병렬로 처리된다. 역할을 전달하고 싶다면 prompt의 텍스트로 명시할 수밖에 없다.
이것이 "openpose ref보다 prompt가 더 강력함" 현상의 정체이다. openpose ref는 어디까지나 "이미지의 어딘가에 이런 선화가 있는 참조"로 처리되며, 포즈 지정자로서의 역할은 명시적으로 전달되지 않는다. 반면 prompt의 dynamic dancing pose with both arms raised는 명시적인 동사·명사로서 어휘 레벨에서 처리된다.
3. shift 파라미터의 영향
shift는 scheduler의 noise schedule 강도를 조정한다. 값에 따른 체감:
- 1.0 = ref 구도에 최대 충실, 자유도 제로 → 가상 피팅(Virtual Try-on) 용도 전문 -
- 2.0-2.5 = 실용 범위, ref로부터의 이탈을 허용 -
- 3.0+ = 거의 자유 생성, ref는 정체성(Identity) 참조로만 활용
README에서 IP/Skeleton/Layout에 대해 1.0을 권장하는 것은 "전형적인 피팅·캐릭터 일관성"을 상정했기 때문이다. "포즈 전환", "의상 교체", "장면 생성"과 같이 ref와 다른 구도를 만들고 싶다면 2.0 이상이 필수적이다.
용도별 베스트 프랙티스 (실증 완료 요약)
용도별 베스트 프랙티스 (실증 완료 요약)
| 하고 싶은 것 | endpoint | refs | shift | 비고 |
|---|---|---|---|---|
| 원본 장면 충실한 시착 | /skeleton | 6 (face+bg+pose+3parts) | 1.0 | README 기본값. ref 전체에 강하게 충실함 |
| 의상 유지 + 자연스러운 서 있는 자세 | /skeleton | 3-4 (face+의상, bg/pose 제외) | 2.0 | bg ref를 빼면 흰 스튜디오화됨, ref 개수 줄이면 각 ref 해상도 768→1024 |
| 포즈 대담하게 변경 | /skeleton | 3 (openpose 제외) | 2.5 | openpose ref보다 prompt가 움직임을 지배함 |
| 의상도 완전 변경 | /ip | 1 (face만) | 3.0 | 최대 자유도, 얼굴만 유지. skeleton 모드는 최소 2 refs 검증으로 거부됨 |
| 단체 사진 | /layout | 다수 인원 ref + 대략적인 bbox | 1.0 | bbox는 대충 구도 힌트일 뿐, 크기 계층은 효과 없음, ref 순서 ↔ bbox 순서 보장 안 됨 |
| 디테일 최적화 | 동일 설정 | 동일 | 동일 | seed를 3-5번 바꿔가며 best of N 시도 |
요약
HiDream-O1-Image의 skeleton 모드를 '시착 시뮬레이터'라고 생각하고 사용하면, 가드레일이 없는데도 불구하고 '말을 안 듣는다'는 체감이 든다. 원인은 모델이나 정책이 아니라, 파이프라인 구조적인 문제 (ref 개수에 따라 해상도가 떨어짐 + skeleton 전용 처리가 없음 + shift가 ref 끌어당기는 힘)에 있다.
실용적인 절충점:
- 시착 = 6 refs full + shift 1.0 (README 기본값대로)
- 움직임을 바꾸고 싶을 때 = openpose ref 제외 + prompt로 동사 지정 + shift 2.5
- 완전히 자유로운 장면 만들기 = face 1장 + shift 3.0 +
/ip
경유
Layout 모드도 '정밀 bbox'가 아니라 '단체 사진 힌트'로 이해하면 기대치에서 벗어나지 않는다.
벤치마크에 사용한 소재/명령어는 HiDream-O1-Image 리포지토리의 assets/IP_skeleton/와 assets/IP_layout/를 그대로 사용할 수 있어 재현 가능하다. shift와 ref 개수를 바꾸기만 해도 동작이 크게 변하는 놀이터처럼, 시행착오를 거치며 감을 잡는 것이 빠르다.
추가: openpose ref를 바꿔봤다 — 'prompt가 항상 강하다'는 조건부였다
공개 후, '다른 형태의 openpose를 넣으면 어떻게 될까'를 추가 검증했더니, 위의 결론을 수정할 필요가 있다는 것을 알게 되었다.
가공한 openpose를 ref에 넣기 (4 패턴)
원래 openpose 이미지 (0.openpose.jpg, 서 있는 자세)를 상하 반전 및 90도 회전하여 '부자연스러운 포즈'로 가공하고, prompt는 일반적인 서 있는 자세를 지정하며 동작을 관찰했다.
| 가공 | 이미지 |
|---|---|
| 상하 반전 (거꾸로 매달린) | |
| 90도 회전 (옆으로 누운) |
| Test | openpose ref | prompt | 결과 |
|---|---|---|---|
| O1 baseline | 원본 (서 있는 자세) | 서 있는 자세 | 기대대로 서 있는 자세 |
| O2 | 🙃 상하 반전 | 서 있는 자세 | 서 있는 자세 (openpose 완전히 무시, prompt 승리) |
| O3 | 🙃 상하 반전 | 점프 | 양손 들고 점프 (openpose 무시, prompt 승리) |
| O4 | ↻ 90도 회전 | 서 있는 자세 | 서 있는 자세지만 캔버스 자체가 90도 회전! |
여기까지는 '부자연스러운 ref는 모델이 기각하고 prompt에 따르게 한다'라는 추가적인 발견이었다. '다만 거시적인 구도 방향 (세로 vs 가로)은 ref가 영향을 준다.'
하지만 dramatic한 ref + 포즈 미지정 prompt라면, ref가 완전히 승리했다
HiDream의 T2I에서 '화려한 해부학적 스켈레톤, 양팔 T자로 펼치고, 한 다리를 높이 들어 나무 요가 자세'를 생성하여 ref로 투입:
prompt는 포즈 언급 일절 없이, 피사체와 의상만 지정:
curl -X POST http://localhost:8895/generate/skeleton -d '{
"prompt": "Full body photograph of a young Asian woman wearing a gray sweater dress, soft natural lighting, white studio background.",
"ref_image_paths": ["face","SYNTHETIC_WARRIOR_SKELETON","sweater"],
...
결과:
완전한 나무 요가 자세가 재현됨 — 양팔 T자 + 외다리 서기, 그야말로 ref의 스켈레톤 (skeleton) 그대로.
수정된 결론 (3가지 규칙)
지금까지의 총 12가지 패턴을 종합하면, HiDream의 동작은 사실 다음과 같이 움직이고 있었다:
prompt가 포즈를 언급하면, 그것이 최우선 — ref와 모순되더라도 prompt가 승리
prompt에 포즈 언급이 없는 경우, ref의 포즈가 채택됨 — dramatic한 ref일수록 효과가 명확함
ref가 "부자연스럽다" (물구나무서기 스켈레톤 등)라고 판단되면, 모델이 기본 자세로 되돌림 — 단, 대국적인 구도 방향은 반영될 수 있음
즉, "openpose ref는 실질적으로 효과가 없다"는 말은 과장이며, 정확하게는 "prompt가 포즈를 설명하는 상황에서는 ref가 덮어씌워진다"가 정확하다. 본 기사의 8가지 패턴은 "prompt가 동적인 포즈를 지정하는 시나리오"였기 때문에, 결과적으로 "openpose ref는 무력하다"고 보였던 것이다.
실용에 미치는 영향
포즈를 완전히 제어하고 싶을 때: prompt에 포즈를 쓰지 않음 + dramatic한 openpose / skeleton ref를 넣음 → ref의 포즈가 전송됨
prompt로 움직임을 지시하고 싶을 때: openpose ref는 빼도 무방함 (ref를 넣어도 prompt에 의해 덮어씌워짐)
ref와 prompt가 모순될 때: prompt가 승리 (ref를 넣어도 무의미함)
요컨대 "포즈의 출처를 어느 쪽으로 기울일 것인가"를 prompt에서 언급하느냐 하지 않느냐로 전환할 수 있다. openpose ref를 사용하고 싶다면 "포즈를 prompt에 쓰지 않는 것"이 요령이다.
관련:
- HiDream-O1-Image: https://huggingface.co/HiDream-ai/HiDream-O1-Image
- 리포지토리 (Repository): https://github.com/HiDream-ai/HiDream-O1-Image
- 자체 구현한
/studio경로 (Python image_server + Rust handler + Next.js UI)의 설계 기사는 별도로
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기