<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>angrimi의 개발 기록지</title>
    <link>https://angrimilog.tistory.com/</link>
    <description>개발 흔적 남기기</description>
    <language>ko</language>
    <pubDate>Sat, 18 Jul 2026 08:31:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>angrimilog</managingEditor>
    <image>
      <title>angrimi의 개발 기록지</title>
      <url>https://tistory1.daumcdn.net/tistory/8652068/attach/d7e21de4120449a9a4c5f95de8425dc5</url>
      <link>https://angrimilog.tistory.com</link>
    </image>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (6) &amp;mdash; UV 텍스처 베이킹 vs 버텍스 컬러, 사용자 평가, 논문 제출</title>
      <link>https://angrimilog.tistory.com/7</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  이번 글에서 다룬 것&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;가구 복원이랑 방 구성까지 다 잡고 나서 마지막으로 해결해야 했던 게 색상 재현 방식이었다. UV 텍스처 베이킹이랑 버텍스 컬러 중 뭘 쓸지 결정해야 했는데, 막상 실험해 보니까 결과가 생각과 달랐다. 그리고 논문 제출까지 마무리한 내용을 정리한다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  UV 텍스처 베이킹 vs 버텍스 컬러&lt;/h3&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;뭔 차이일까?&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UV 텍스처 베이킹&lt;/b&gt;은 3D Gaussian Splatting을 100개 시점에서 렌더링 한 다음 그 결과를 UV 텍스처에 합산하는 방식이다. 메쉬 추출 이후에 별도의 렌더링 파이프라인이 돌아간다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;버텍스 컬러&lt;/b&gt;는 SAM3D의 메쉬 디코더(SLatMeshDecoder)가 색상을 직접 회귀하는 방식이다. 추가 렌더링 없이 네트워크 출력이 곧 색상이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;코드를 파고들어보니 SAM3D 내부에서 SparseFeatures2Mesh의 _calc_layout() 메서드가 color 피처(48채널)를 FlexiCubes 메쉬 추출기에 voxelgrid_colors로 직접 전달해서 버텍스 색상을 결정하는 구조였다. 즉 버텍스 색상은 렌더링 투영이 아니라 end-to-end 학습된 신경망의 직접 출력이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;처음엔 UV 텍스처 베이킹이 당연히 더 예쁠 거라 생각했다. 근데 막상 해보니까 달랐다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsy5lN/dJMcab5CJUo/cveKXqRKJKfumBRef8n8p1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsy5lN/dJMcab5CJUo/cveKXqRKJKfumBRef8n8p1/img.png&quot; data-alt=&quot;버텍스 컬러 사용했을때! 색상도 잘 뽑히고 예쁘게 잘 나온듯 하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsy5lN/dJMcab5CJUo/cveKXqRKJKfumBRef8n8p1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsy5lN%2FdJMcab5CJUo%2FcveKXqRKJKfumBRef8n8p1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;415&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;버텍스 컬러 사용했을때! 색상도 잘 뽑히고 예쁘게 잘 나온듯 하다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RuGjz/dJMcadh53Wd/i25RGwXiCiwfjau9M57y4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RuGjz/dJMcadh53Wd/i25RGwXiCiwfjau9M57y4k/img.png&quot; data-alt=&quot;UV 텍스처 베이킹 버전이다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RuGjz/dJMcadh53Wd/i25RGwXiCiwfjau9M57y4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRuGjz%2FdJMcadh53Wd%2Fi25RGwXiCiwfjau9M57y4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;405&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UV 텍스처 베이킹 버전이다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 심지어 UV 텍스처 베이킹은 버텍스 컬러보다 시간이 엄청 오래 걸린다.&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;객체&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;복셀 수&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;UV 텍스처 베이킹&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;버텍스 컬&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;단축 배율&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;테이블&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;6,383&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;53초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;32초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;1.7배&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;문&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;10,322&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;58초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;32초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;1.8배&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;의자&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;26,910&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;2,550초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;40초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;64배&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;침대&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;21,826&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;5,243초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;39초&lt;/td&gt;&lt;td style=&quot;width: 20.0%; height: 21px; text-align: justify;&quot;&gt;134배&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;의자 2,550초... 약 42분이다. 복셀 수가 더 적은 침대가 의자보다 2배 이상 긴 것도 특이했다. UV 베이킹은 복셀 수뿐만 아니라 메쉬 형상 복잡도(UV 아일랜드 구성 방식)에도 크게 좌우되기 때문이다. 곡면이 많고 구조적 구성 요소가 복잡할수록 UV 아일랜드 수가 폭발적으로 늘어난다.&lt;br&gt;색상 품질도 버텍스 컬러가 더 안정적이었다. UV 베이킹은 단일 이미지 조건에서 미관측 표면(뒷면, 측면)에 대한 실제 색상 정보가 없어서 인접 표면 색상이 UV 아일랜드 경계를 넘어 번지는 color bleeding 현상이 발생했다. 버텍스 컬러는 그런 문제없이 일관된 색상이 나왔다.&lt;br&gt;처리 시간 안정성, 색상 품질, SAM3D 아키텍처 설계 의도와의 정합성을 종합해서 &lt;b&gt;버텍스 컬러를 최종 채택&lt;/b&gt;했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그렇지만 나는 Meta가 SAM3D 사용할 때 UV 텍스처 베이킹으로 잘 만든다는 사실로 알고 있었다.&amp;nbsp;&lt;br&gt;그럼 왜 우리는 안되지?... 궁금해서 여러 번 시도했지만 결국 이게 최선이었다.&amp;nbsp;&lt;br&gt;클로드에게는 물어보니..&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MxORT/dJMcahEJm8h/O6Cnqk6x2qeYv9JiwAqDC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MxORT/dJMcahEJm8h/O6Cnqk6x2qeYv9JiwAqDC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MxORT/dJMcahEJm8h/O6Cnqk6x2qeYv9JiwAqDC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMxORT%2FdJMcahEJm8h%2FO6Cnqk6x2qeYv9JiwAqDC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;567&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 알려주는 건 지 모르겠지만(?) 향후 연구에 참고해 봐야겠다...&lt;br&gt;우리가 nvdiffrast 대신 pytoch3d를 사용해서 그런가??...&amp;nbsp; 둘 차이는 이렇다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;pytorch3d:&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;페이스북이 만든 3D 딥러닝 라이브러리&lt;/li&gt;&lt;li&gt;소프트 래스터라이저 기반&lt;/li&gt;&lt;li&gt;gradient가 근사값이라 UV 최적화 정확도가 낮음&lt;/li&gt;&lt;li&gt;설치 쉬움&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;nvdiffrast:&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;NVIDIA가 만든 미분 가능 래스터라이저&lt;/li&gt;&lt;li&gt;해석적 안티앨리어싱 기반&lt;/li&gt;&lt;li&gt;gradient가 정확해서 UV 최적화 품질이 높음&lt;/li&gt;&lt;li&gt;CUDA 직접 컴파일이라 설치 까다로움&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;여기가 의심되는데,,,, 다음 연구 때 시도 해봐야겠다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  사용자 평가 — 20명, 7점 만점에 6.33점&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;논문 제출 전에 사용자 평가를 진행했다. 사용자 20명들은 인테리어 비전공자들이며 20대 초중반 사람들이다. 20명한테 실제로 시스템 써보게 하고 7점 리커트 척도 10개 문항에 답하게 했다.&lt;br&gt;드래그앤드롭(6.60)이 제일 높았고 Occlusion 자동완성(6.45)이 표준편차 0.60으로 가장 낮은 편차를 보였다. 가려진 가구를 3D로 자동 완성해주는 기능에 대해서 평가자 간 이견 없이 일관되게 좋은 평가를 받았다는 거다.&lt;br&gt;주관식 응답으로는 &quot;상용화를 기원합니다&quot;, &quot;잘 만든 것 같아요&quot; 같은 얘기도 나왔고, 처리 속도랑 세그멘테이션 오인식이 개선점으로 지적됐다. 솔직히 처리 속도는 나도 알고 있는 문제긴 하다...&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  논문 제출 후기&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;KCGS(한국컴퓨터그래픽스학회) 2026에 제출했다. 교수님이랑 계속 피드백 주고받으면서 v40까지 수정했다.&lt;br&gt;제일 힘들었던 건 실험이었다. 처리 시간 측정도 실제로 다 돌려보면서 재야 했고. 제대로 결과 값이 안 나올 때도 있었다.&amp;nbsp;&lt;br&gt;최종 논문 주요 기여&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;LaMa + SD ControlNet(Canny) 2단계 빈방 재구성&lt;/li&gt;&lt;li&gt;SAM3D 실내 적용 탐색 + 한계 분석&lt;/li&gt;&lt;li&gt;카테고리별 다중 시드 선택 기준&lt;/li&gt;&lt;li&gt;UV 텍스처 베이킹 vs 버텍스 컬러 비교&lt;/li&gt;&lt;li&gt;사용자 평가 20명 (7점 만점 6.33점)&lt;/li&gt;&lt;/ul&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 향후 계획&lt;/h3&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;b&gt;처리 시간 단축&lt;/b&gt; — 가구 1개당 35초가 한계다. 모델 경량화나 파이프라인 최적화가 필요하다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;SAM3D 방 재구성&lt;/b&gt; — 이번에 한계를 확인했으니까 실내 특화 3D 복원 모델 방향으로 연구할 여지가 있다. MoGe depth 기반 평면 피팅 방식도 시도해 볼 만하다.&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;b&gt;서비스 배포&lt;/b&gt; — 지금은 로컬 GPU 서버에서만 돌아간다. 클라우드로 올리는 게 목표다.&lt;/li&gt;&lt;/ol&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;길고 힘들었던 프로젝트였는데 학부생 신분으로 이 정도 시스템 만들고 논문까지 제출하고 나니까 뿌듯하다. &lt;br&gt;논문 제출하고 나서 이렇게 개발 과정을 글로 정리하는 시간이 생각보다 좋다. 개발할 때는 마감기한이 다가와서 시간이 없고 문제 해결에만 급급해서 왜 이런 선택을 했는지, 뭘 시도했는지 돌아볼 여유가 없었는데 글로 쓰다 보니 그때 했던 고민들이 다시 정리되는 느낌이다. nvdiffrast 같은 것도 논문 끝나고 나서야 여유롭게 알아볼 수 있게 됐고. 앞으로도 이런 시간을 꾸준히 가져야겠다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>sam2</category>
      <category>SAM3D</category>
      <category>가구</category>
      <category>인테리어</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/7</guid>
      <comments>https://angrimilog.tistory.com/7#entry7comment</comments>
      <pubDate>Sun, 14 Jun 2026 18:17:30 +0900</pubDate>
    </item>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (5) &amp;mdash; SAM3D로 3D 방 만들기 도전!?</title>
      <link>https://angrimilog.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;div&gt; 
 &lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;  이번 &lt;/span&gt;&lt;span data-newtext-seq=&quot;9&quot;&gt;글에서 다룬 것&lt;/span&gt;&lt;/h3&gt; 
&lt;/div&gt;&lt;div&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;가구 &lt;/span&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;복원은 어느 정도 &lt;/span&gt;&lt;span data-newtext-seq=&quot;13&quot;&gt;잡혔으니까 이제 방을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;25&quot;&gt;3D로 만들 차례였다. &lt;/span&gt;&lt;span data-newtext-seq=&quot;38&quot;&gt;근데 이게 &lt;/span&gt;&lt;span data-newtext-seq=&quot;44&quot;&gt;진짜 오래 &lt;/span&gt;&lt;span data-newtext-seq=&quot;50&quot;&gt;걸렸고 결국 포기했다. &lt;/span&gt;&lt;span data-newtext-seq=&quot;63&quot;&gt;그 과정을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;69&quot;&gt;솔직하게 기록한다.&lt;/span&gt;&lt;/p&gt; 
 &lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt; 
 &lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;  왜 &lt;/span&gt;&lt;span data-newtext-seq=&quot;8&quot;&gt;SAM3D로 방을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;18&quot;&gt;만들려 했냐&lt;/span&gt;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D를 방 전체에 적용해보면 어떨까 생각했다. 가구를 3D로 만드는 모델이니까 방도 하나의 객체로 보고 넣으면 실제 방 구조를 반영한 3D가 나오지 않을까 하는 기대였다.&amp;nbsp;&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아무도 시도한 사람이 없었다. SAM3D는 단일 객체용으로 설계된 거라 실내 씬에 적용한 사례 자체가 없었다. 하지만 시도해서 어떤 결과를 내는 지 궁금했고, 논문 기여가 될 수도 있겠다 싶었다.&lt;/p&gt; 
 &lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Sk8C/dJMcagyZGSe/olsikkD4YF1q34AYdFKfZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Sk8C/dJMcagyZGSe/olsikkD4YF1q34AYdFKfZk/img.png&quot; data-alt=&quot; SAM3D로 만들어본 방 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Sk8C/dJMcagyZGSe/olsikkD4YF1q34AYdFKfZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Sk8C%2FdJMcagyZGSe%2FolsikkD4YF1q34AYdFKfZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;258&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; SAM3D로 만들어본 방 &lt;/figcaption&gt;
&lt;/figure&gt;
 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt; 
 &lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt; 
 &lt;h3 data-ke-size=&quot;size23&quot;&gt;  뭘 시도했냐&lt;/h3&gt; 
 &lt;h4 data-ke-size=&quot;size20&quot;&gt;1단계: 그냥 넣어봤다&lt;/h4&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;빈방 이미지를 SAM3D에 통째로 넣었다. 마스크는 방 내부 전체 영역으로 잡았다.&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;결과는 예상대로 울퉁불퉁했다. 벽이 평평하지 않고, 바닥이 생기지 않고, 형태가 완전 뒤틀렸다.&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;근데 희망적인 건 벽 3개의 윤곽은 어느 정도 나왔다는 거였다. 완전 실패는 아니었다.&lt;/p&gt; 
 &lt;h4 data-ke-size=&quot;size20&quot;&gt;2단계: 후처리로 뭔가 해보려 했다&lt;/h4&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;결과가 울퉁불퉁하면 후처리로 평평하게 만들면 되지 않을까 생각했다.&lt;/p&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;trimesh 후처리 적용:&lt;/b&gt;&lt;/p&gt; 
 &lt;pre id=&quot;code_1780840846083&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# degenerate face 제거
face_mask = mesh_tri.area_faces &amp;gt; 1e-8
mesh_tri.update_faces(face_mask)

# 구멍 메우기
trimesh.repair.fill_holes(mesh_tri)

# Laplacian smoothing
trimesh.smoothing.filter_laplacian(mesh_tri, iterations=1, lamb=0.2)&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;_flatten_room_mesh 적용:&lt;/b&gt; 벽/바닥 vertex를 각 평면 방향으로 snap하는 함수를 만들었다. 바닥 근처 vertex는 y_min 쪽으로, 뒷벽 근처는 z_min 쪽으로 당기는 방식이다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def _flatten_room_mesh(vertices_np, snap_strength=0.9, snap_zone=0.28):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 각 vertex를 가장 가까운 면(바닥/뒷벽/좌벽/우벽) 방향으로 snap
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d_floor = (v[:, 1] - y_min) / dy
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d_back&amp;nbsp;&amp;nbsp;= (v[:, 2] - z_min) / dz
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d_left&amp;nbsp;&amp;nbsp;= (v[:, 0] - x_min) / dx
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;d_right = (x_max - v[:, 0]) / dx
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# snap_zone 안에 있는 vertex만 해당 면 쪽으로 당김
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하니까 확실히 벽이 더 평평해졌다. 근데 여전히 문제가 많았다.&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3단계: 천장/앞벽 제거, 바닥 강제 추가&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D 결과에서 천장이 생성되면 안으로 들어갔을 때 막혀 보이고, 앞벽이 생기면 내부가 안 보인다. 그래서 face center 기준으로 천장과 앞벽을 잘라냈다.&lt;br&gt;바닥은 아예 안 만들어지는 경우가 많았다. 그래서 바닥 커버리지를 체크해서 부족하면 강제로 평면을 추가했다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 바닥 커버리지 체크
floor_verts = vertices_np[y_arr &amp;lt;= (y_min + 0.15 * dy)]
coverage = (fx * fz) / (bx * bz + 1e-6)

if coverage &amp;lt; 0.4:&amp;nbsp;&amp;nbsp;# 40% 미만이면 바닥 없는 거로 판단
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 바닥 평면 강제 추가
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;floor_verts = np.array([
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[x_min, y_min, z_min], [x_max, y_min, z_min],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[x_max, y_min, z_max], [x_min, y_min, z_max],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;])&lt;/code&gt;&lt;/pre&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4단계: 색상 입히기&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D vertex color를 그대로 쓰면 창문, 문 색깔이 벽에 번져서 이상하게 나왔다. 그래서 빈방 이미지에서 직접 벽색/바닥색을 추출해서 단색으로 입혔다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efkilV/dJMcaiwPtn1/6oXIsxDJpq7Zkkme74lj60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efkilV/dJMcaiwPtn1/6oXIsxDJpq7Zkkme74lj60/img.png&quot; data-alt=&quot;입력한 방 실험 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efkilV/dJMcaiwPtn1/6oXIsxDJpq7Zkkme74lj60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefkilV%2FdJMcaiwPtn1%2F6oXIsxDJpq7Zkkme74lj60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;349&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;입력한 방 실험 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;↓&lt;/h3&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BRLue/dJMcabEssvU/lTHc7xKYjEs0twbkho49BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BRLue/dJMcabEssvU/lTHc7xKYjEs0twbkho49BK/img.png&quot; data-alt=&quot;SAM3D로 만든 방&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BRLue/dJMcabEssvU/lTHc7xKYjEs0twbkho49BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBRLue%2FdJMcabEssvU%2FlTHc7xKYjEs0twbkho49BK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;391&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SAM3D로 만든 방&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  근데 결국 안 됐다&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;후처리를 아무리 해도 근본적인 문제가 해결이 안 됐다.&lt;br&gt;SAM3D 학습 데이터: 볼록한 단일 객체 (소파, 의자 등)&lt;br&gt;우리 입력: 오목한 실내 공간 (방 전체)&lt;/p&gt;&lt;div&gt; 
 &lt;div&gt; &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;SAM3D의 shape prior가 볼록한 물체로 학습돼 있다. 방은 반대로 안쪽이 열린 오목한 공간이다. 그러니까 SAM3D 입장에서 방을 볼록한 덩어리로 만들려고 하는 거고, 그게 울퉁불퉁하고 뒤틀린 결과로 나오는 거다.&lt;/span&gt; 
 &lt;/div&gt; 
&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;후처리로 평면 snap을 아무리 해도 근본적으로 잘못 생성된 메쉬를 완전히 고칠 수는 없었다. 후처리를 해도 3D 메쉬가 랜덤으로 나와 결과가 잘 나올때도 있고 정말 이상하게 나올때도 있었다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;trimesh 후처리 ✅ (어느 정도 개선)&lt;/li&gt;&lt;li&gt;flatten snap ✅ (벽 평평하게)&lt;/li&gt;&lt;li&gt;천장/앞벽 제거 ⚠️ (너무 많이 자르면 벽도 같이 잘림)&lt;/li&gt;&lt;li&gt;바닥 강제 추가 ✅ (없을 때만)&lt;/li&gt;&lt;li&gt;색상 단색 적용 ✅&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 시도를 해봤다. 그래도 결과물이 인테리어 시뮬레이션으로 쓰기엔 너무 불안정했다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Three.js 절차적 방으로 전환&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;결국 SAM3D 방을 포기하고 Three.js 박스 방으로 돌아왔다. 근데 기존 박스 방에서 개선한 것들이 있다.&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;b&gt;텍스처 적용&lt;/b&gt; — 단순 단색이 아니라 빈방 이미지에서 벽/바닥 텍스처를 추출해서 그대로 입혔다. 바닥에는 나무 판자 텍스처도 오버레이했다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;색상 추출 방식 개선&lt;/b&gt; — 이미지 상단 40~60% 영역을 벽색으로, 하단 15% 중앙 80% 영역을 바닥색으로 추출했다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;천장 흰색 고정&lt;/b&gt; — 대부분 방 천장이 흰색이라 그냥 고정했다.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 SAM3D 방보다 훨씬 안정적이고 보기에도 자연스러웠다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  근데 이게 논문에서는 기여가 되지 않을까?&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D로 방을 만들려고 삽질한 게 아깝지 않은 이유가 있다.&lt;br&gt;&quot;단일 객체용 모델을 실내 씬에 적용하면 어떤 문제가 생기는지&quot;를 실험으로 규명했다는 게 학술적으로 의미가 있다. 아무도 이걸 시도한 사람이 없었으니까.&lt;br&gt;논문에는 이렇게 썼다.&lt;br&gt;&quot;SAM3D를 실내 공간 전체에 적용하는 탐색적 실험을 수행하였다. 객체용 shape prior가 오목한 실내 공간에 적용될 때 발생하는 메쉬 품질 저하 문제를 분석하고 완화시킬 수 있는 방법을 제안하였다. 이는 향후 실내 특화 3D 복원 모델 연구의 방향을 제시한다.&quot;&lt;br&gt;&amp;nbsp;&lt;br&gt;실패도 논문이 된다. 오히려 &quot;시도해봤더니 이런 한계가 있더라&quot;는 게 더 정직하고 신뢰감 있는 연구다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 UV 텍스처 베이킹 vs 버텍스 컬러 비교 실험, 그리고 사용자 평가 20명 + 논문 제출 후기를 정리할 예정이다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>sam2</category>
      <category>SAM3D</category>
      <category>가구</category>
      <category>인테리어</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/6</guid>
      <comments>https://angrimilog.tistory.com/6#entry6comment</comments>
      <pubDate>Sun, 7 Jun 2026 23:13:47 +0900</pubDate>
    </item>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (4) &amp;mdash; SAM3D 가구 복원 최적화</title>
      <link>https://angrimilog.tistory.com/5</link>
      <description>&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;  이번 글에서 &lt;/span&gt;&lt;span data-newtext-seq=&quot;13&quot;&gt;다룬 것&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;지난 &lt;/span&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;글에서 SAM3D로 &lt;/span&gt;&lt;span data-newtext-seq=&quot;14&quot;&gt;가구를 3D로 변환하고 &lt;/span&gt;&lt;span data-newtext-seq=&quot;27&quot;&gt;Three.js 방에 배치하는 &lt;/span&gt;&lt;span data-newtext-seq=&quot;44&quot;&gt;기능까지 구현했다. &lt;/span&gt;&lt;span data-newtext-seq=&quot;55&quot;&gt;근데 막상 여러 가구로 &lt;/span&gt;&lt;span data-newtext-seq=&quot;68&quot;&gt;테스트해보니까 문제가 한두 &lt;/span&gt;&lt;span data-newtext-seq=&quot;83&quot;&gt;가지가 아니었다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;화분은 &lt;/span&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;테이블로 &lt;/span&gt;&lt;span data-newtext-seq=&quot;9&quot;&gt;복원되고, 조명은 &lt;/span&gt;&lt;span data-newtext-seq=&quot;19&quot;&gt;납작한 판때기가 되고, 같은 소파인데 실행할 때마다 형태가 &lt;/span&gt;&lt;span data-newtext-seq=&quot;52&quot;&gt;달라지고... SAM3D를 실내 사진에 그냥 들이밀면 안 된다는 걸 &lt;/span&gt;&lt;span data-newtext-seq=&quot;90&quot;&gt;몸으로 배웠다.&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVQsTg/dJMcaijh1uo/ZlPmYSm1ZD4DxG3z4QLVr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVQsTg/dJMcaijh1uo/ZlPmYSm1ZD4DxG3z4QLVr0/img.png&quot; data-alt=&quot;조명과 화분을 SAM3D로 제작했을 때 문제 상황&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVQsTg/dJMcaijh1uo/ZlPmYSm1ZD4DxG3z4QLVr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVQsTg%2FdJMcaijh1uo%2FZlPmYSm1ZD4DxG3z4QLVr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;338&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;조명과 화분을 SAM3D로 제작했을 때 문제 상황&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;이번 글에서는 이 문제들을 어떻게 해결했는지 &lt;/span&gt;&lt;span data-newtext-seq=&quot;25&quot;&gt;정리한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-newtext-seq=&quot;3&quot;&gt;  문제 1: 얇은 가구에서 depth &lt;/span&gt;&lt;span data-newtext-seq=&quot;26&quot;&gt;추정 실패&lt;/span&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;뭐가 문제였냐&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;화분, 조명, 시계 같은 얇고 작은 &lt;/span&gt;&lt;span data-newtext-seq=&quot;20&quot;&gt;가구를 SAM3D에 넣으면 직육면체 덩어리가 나왔다. 아무리 봐도 &lt;/span&gt;&lt;span data-newtext-seq=&quot;57&quot;&gt;화분이 아닌...&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;원인을 파고들어보니 SAM3D 내부에서 &lt;/span&gt;&lt;span data-newtext-seq=&quot;22&quot;&gt;MoGe-v2가 depth를 추정할 때 마스크 영역 안에 유효한 &lt;/span&gt;&lt;span data-newtext-seq=&quot;58&quot;&gt;depth값이 하나도 없으면 scale calibration이 실패하는 &lt;/span&gt;&lt;span data-newtext-seq=&quot;98&quot;&gt;거였다. 얇은 물체는 마스크가 작아서 depth값이 전부 NaN으로 &lt;/span&gt;&lt;span data-newtext-seq=&quot;136&quot;&gt;나오는 케이스가 많았다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-newtext-seq=&quot;4&quot;&gt;해결 방법: &lt;/span&gt;&lt;span data-newtext-seq=&quot;11&quot;&gt;Progressive Dilation &lt;/span&gt;&lt;span data-newtext-seq=&quot;32&quot;&gt;Patch&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-newtext-seq=&quot;0&quot;&gt;마스크를 &lt;/span&gt;&lt;span data-newtext-seq=&quot;5&quot;&gt;점점 키워가면서 유효한 &lt;/span&gt;&lt;span data-newtext-seq=&quot;18&quot;&gt;depth값을 탐색하는 방식을 &lt;/span&gt;&lt;span data-newtext-seq=&quot;35&quot;&gt;구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1780812378920&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 커널 크기를 5 &amp;rarr; 11 &amp;rarr; 21 &amp;rarr; 41 순으로 팽창
for kernel_size in [5, 11, 21, 41]:
    dilated = cv2.dilate(mask, np.ones((kernel_size, kernel_size)))
    valid_depth = depth[dilated &amp;gt; 0]
    if len(valid_depth[~np.isnan(valid_depth)]) &amp;gt; 0:
        break  # 유효값 찾으면 탈출&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 5&amp;times;5 커널로 살짝만 팽창시키고, 그래도 유효값이 없으면 11, 21, 41 순서로 점점 크게 키워가는 방식이다. 이렇게 하면 얇은 물체에서도 주변 depth값을 안정적으로 확보할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 적용하고 나서 화분, 시계, 조명 같은 얇은 가구들도 제대로 복원되기 시작했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  문제 2: 패딩 비율에 따른 형상 오염 &amp;mdash; 우리가 처음 발견한 거&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;뭐가 문제였냐&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D는 단일 클린 객체 이미지를 전제로 설계된 모델이다. 근데 우리는 실내 사진에서 가구를 크롭해서 넣어야 한다. 이때 가구 주변을 얼마나 포함시키냐가 생각보다 엄청 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패딩을 안 주면(0%) MoGe가 공간 구조 정보 없이 depth를 추정하게 돼서 scale calibration이 불안정해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 패딩을 너무 많이 주면(50%) 주변 가구나 구조물의 형태가 SAM3D의 shape prior에 혼입돼서 형상이 왜곡된다. 실제로 화분 복원할 때 50% 패딩 주니까 화분 옆에 있던 테이블 형태가 prior에 섞여서 화분이 테이블로 복원되는 황당한 일이 벌어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패딩&amp;nbsp;0%&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;depth&amp;nbsp;추정&amp;nbsp;불안정&amp;nbsp;&amp;rarr;&amp;nbsp;scale&amp;nbsp;calibration&amp;nbsp;실패 &lt;br /&gt;패딩&amp;nbsp;15%&amp;nbsp;&amp;rarr;&amp;nbsp;균형점&amp;nbsp;✅ &lt;br /&gt;패딩&amp;nbsp;50%&amp;nbsp;&amp;rarr;&amp;nbsp;주변&amp;nbsp;가구&amp;nbsp;shape&amp;nbsp;prior&amp;nbsp;오염&amp;nbsp;&amp;rarr;&amp;nbsp;형상&amp;nbsp;왜곡&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0%, 15%, 50% 세 가지를 비교 실험해서 15%가 균형점임을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 트레이드오프는 SAM3D 원 논문에도 없는 내용이고 검색해봐도 아무도 분석한 게 없었다. 실내 멀티 오브젝트 환경에 단일 객체 모델을 적용하면서 생기는 도메인 갭 문제인데 우리가 실험하면서 처음 발견한 거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 나중에 Mesh Postprocessing을 활성화하면서 패딩 없이 회색 배경(RGB 128)만으로도 안정적인 복원이 가능하다는 걸 확인했고, 최종 시스템은 그냥 회색 배경 방식을 채택했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxeKtO/dJMcacwCYYI/TIVhOYO2khkG5FzNDALrzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxeKtO/dJMcacwCYYI/TIVhOYO2khkG5FzNDALrzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxeKtO/dJMcacwCYYI/TIVhOYO2khkG5FzNDALrzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxeKtO%2FdJMcacwCYYI%2FTIVhOYO2khkG5FzNDALrzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;308&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  문제 3: 랜덤 시드 의존성 &amp;mdash; 같은 소파인데 매번 달라&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;뭐가 문제였냐&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D 내부에 Diffusion 모델이 있어서 동일한 입력이어도 시드에 따라 결과가 완전히 달라진다. 어떤 시드에서는 시계가 입체적으로 잘 나오고, 어떤 시드에서는 납작한 판때기가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 같은 시계 이미지로 10번 돌려봤더니 입체성 지표 표준편차가 0.162나 됐다. 서비스에 쓰기엔 너무 불안정했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법: 카테고리별 다중 시드 선택&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stage 1(초기 3D 형상 생성 단계)을 5번 반복 실행하고, 카테고리별로 다른 기준으로 최적 시드를 고르는 방식을 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1780812522111&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 일반 가구 (소파, 의자, 테이블 등)
# &amp;rarr; 세 축(x, y, z) 중 최소 범위를 최대화
# 납작한 결과 방지
ranges = voxel.max(axis=0) - voxel.min(axis=0)
score = float(ranges.min())

# 얇은 가구 (화분, 조명, 시계 등)
# &amp;rarr; 복셀 개수를 최대화
# 구조적 완전성 우선
score = float(len(voxel))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 가구는 세 축 중 가장 짧은 축이 최대한 길어야 입체적인 거니까 최솟값을 최대화하는 기준을 썼다. 얇은 가구는 형태 자체가 납작한 게 맞으니까 복셀 수가 많을수록 더 완전하게 복원된 거라고 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계로 단일 시드 vs 5회 다중 시드를 각각 10회 실험한 결과, 표준편차가 0.162에서 0.032로 줄었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형상 일관성이 확 올라간 거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Stage 1을 5번 돌리니까 처리시간이 10초에서 41초로 늘어나는 트레이드오프가 있다. 근데 형상 품질 안정성이 훨씬 중요하다고 판단해서 그냥 감수했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chmDuF/dJMcaijh1uj/Uxc6Lkgn63OHNdhMvyblYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chmDuF/dJMcaijh1uj/Uxc6Lkgn63OHNdhMvyblYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chmDuF/dJMcaijh1uj/Uxc6Lkgn63OHNdhMvyblYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchmDuF%2FdJMcaijh1uj%2FUxc6Lkgn63OHNdhMvyblYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;223&quot; data-origin-width=&quot;557&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세가지 방법을 합쳐서 돌려보면 어떤 가구를 넣어도 잘 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3072&quot; data-origin-height=&quot;4096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dY26dV/dJMcaiczKoy/lkHBRSmObhkWwirdmxEan1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dY26dV/dJMcaiczKoy/lkHBRSmObhkWwirdmxEan1/img.png&quot; data-alt=&quot;세가지 방법을 사용해서 가구를 3D 모델링한 결과들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dY26dV/dJMcaiczKoy/lkHBRSmObhkWwirdmxEan1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdY26dV%2FdJMcaiczKoy%2FlkHBRSmObhkWwirdmxEan1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;1024&quot; data-origin-width=&quot;3072&quot; data-origin-height=&quot;4096&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세가지 방법을 사용해서 가구를 3D 모델링한 결과들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  최종 입력 파이프라인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지를 합치면 최종 가구 입력 전처리 파이프라인이 이렇게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;가구&amp;nbsp;이미지&amp;nbsp;(RGBA) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;마스크&amp;nbsp;bbox&amp;nbsp;크롭&amp;nbsp;(512~1024px) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;카테고리별&amp;nbsp;Morphological&amp;nbsp;CLOSE &lt;br /&gt;(일반:&amp;nbsp;25&amp;times;25&amp;nbsp;/&amp;nbsp;얇은&amp;nbsp;가구:&amp;nbsp;5&amp;times;5) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;회색&amp;nbsp;배경(RGB&amp;nbsp;128)&amp;nbsp;+&amp;nbsp;마스크&amp;nbsp;정규화&amp;nbsp;(*255) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;Stage&amp;nbsp;1&amp;nbsp;&amp;times;&amp;nbsp;5회&amp;nbsp;&amp;rarr;&amp;nbsp;카테고리별&amp;nbsp;기준으로&amp;nbsp;최적&amp;nbsp;시드&amp;nbsp;선택 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;Stage&amp;nbsp;2&amp;nbsp;&amp;rarr;&amp;nbsp;3D&amp;nbsp;메쉬&amp;nbsp;+&amp;nbsp;버텍스&amp;nbsp;컬러&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 SAM3D로 빈방을 3D로 만들려고 시도했던 것들, 그리고 왜 결국 Three.js 박스 방으로 전환했는지 정리할 예정이다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>3D모델링</category>
      <category>MoGe</category>
      <category>sam2</category>
      <category>SAM3D</category>
      <category>가구</category>
      <category>인테리어</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/5</guid>
      <comments>https://angrimilog.tistory.com/5#entry5comment</comments>
      <pubDate>Sun, 7 Jun 2026 15:19:37 +0900</pubDate>
    </item>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (3)</title>
      <link>https://angrimilog.tistory.com/4</link>
      <description>&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;  이번 글에서 구현한 것&lt;/h3&gt;&lt;p data-ke-size=&quot;size14&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;지난 글에서 2D 빈 방 사진으로 3D 방을 구현하는 것 까지 했다. 그리고 SAM3D로 가구를 3D 메시로 변환하고 Three.js 방에 드래그해서 배치하는 기능까지 구현했었다.&amp;nbsp; 좀 늦게 글 쓴 이유가 학회 논문 포스터 발표 준비 때문에 할 게 너무 많고 정신이 없었다........ㅎㅎ....&amp;nbsp;&lt;br&gt;&lt;br&gt;먼저 방 구현 퀄리티를 높여보았다.&lt;br&gt;  빈방 사진 한 장으로 3D 방이 만들어지는 원리를 다시 구체적으로 설명해보겠다.&lt;br&gt;&lt;br&gt;사진을&amp;nbsp;올리면&amp;nbsp;어떻게&amp;nbsp;3D&amp;nbsp;방이&amp;nbsp;뚝딱&amp;nbsp;생기는&amp;nbsp;걸까?&amp;nbsp;&amp;nbsp;AI가&amp;nbsp;방&amp;nbsp;구조를&amp;nbsp;이해하는&amp;nbsp;게&amp;nbsp;아니라,&amp;nbsp;생각보다&amp;nbsp;단순한&amp;nbsp;원리로&amp;nbsp;동작한다. &lt;br&gt;&lt;br&gt;&lt;b&gt;① 가구를 지워서 빈방 만들기&lt;/b&gt;&lt;br&gt;먼저&amp;nbsp;업로드한&amp;nbsp;방&amp;nbsp;사진에서&amp;nbsp;SAM2가&amp;nbsp;가구를&amp;nbsp;인식하고&amp;nbsp;마스크를&amp;nbsp;만든다.&amp;nbsp;그&amp;nbsp;마스크&amp;nbsp;영역을&amp;nbsp;LaMa(딥러닝&amp;nbsp;인페인팅&amp;nbsp;모델)가&amp;nbsp;배경으로&amp;nbsp;자연스럽게&amp;nbsp;채워서&amp;nbsp;가구가&amp;nbsp;없는&amp;nbsp;빈방&amp;nbsp;사진을&amp;nbsp;생성한다.&amp;nbsp;이&amp;nbsp;빈방&amp;nbsp;사진이&amp;nbsp;이후&amp;nbsp;모든&amp;nbsp;과정의&amp;nbsp;기준이&amp;nbsp;된다. &lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;② 벽 색상과 바닥 색상 추출&lt;/b&gt;&lt;br&gt;빈방 사진이 생기면 픽셀값을 분석해서 벽과 바닥의 색상을 뽑아낸다. 방식은 간단하다.&lt;br&gt;-&amp;nbsp;이미지&amp;nbsp;상단&amp;nbsp;영역&amp;nbsp;→&amp;nbsp;벽으로&amp;nbsp;간주 &lt;br&gt;-&amp;nbsp;이미지&amp;nbsp;하단&amp;nbsp;영역&amp;nbsp;→&amp;nbsp;바닥으로&amp;nbsp;간주 &lt;br&gt;&lt;br&gt;해당&amp;nbsp;영역&amp;nbsp;픽셀들의&amp;nbsp;RGB&amp;nbsp;평균값을&amp;nbsp;계산하면&amp;nbsp;그게&amp;nbsp;벽&amp;nbsp;색상,&amp;nbsp;바닥&amp;nbsp;색상이&amp;nbsp;된다.&amp;nbsp;복잡한&amp;nbsp;세그멘테이션&amp;nbsp;없이&amp;nbsp;영역을&amp;nbsp;단순하게&amp;nbsp;나눠서&amp;nbsp;평균을&amp;nbsp;내는&amp;nbsp;방식인데,&amp;nbsp;일반적인&amp;nbsp;방&amp;nbsp;사진에서는&amp;nbsp;꽤&amp;nbsp;그럴듯하게&amp;nbsp;색상이&amp;nbsp;추출된다. &lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;③ 추출한 색상으로 Three.js 방 렌더링&lt;/b&gt;&lt;br&gt;뽑아낸 색상값을 그대로 Three.js에 넘겨서 3D 방을 만든다. 구조는 단순하게 PlaneGeometry 6개를 조합한 박스 형태다.&amp;nbsp;&lt;br&gt;참고로 &lt;b&gt;PlaneGeometry&lt;/b&gt;는 Three.js에서 평평한 사각형 판을 만드는 기본 도형이다.&lt;br&gt;&lt;br&gt;-&amp;nbsp;바닥:&amp;nbsp;추출된&amp;nbsp;바닥&amp;nbsp;색상&amp;nbsp;+&amp;nbsp;나무&amp;nbsp;판자&amp;nbsp;텍스처&amp;nbsp;오버레이 &lt;br&gt;-&amp;nbsp;벽(뒤,&amp;nbsp;좌,&amp;nbsp;우):&amp;nbsp;추출된&amp;nbsp;벽&amp;nbsp;색상으로&amp;nbsp;단색&amp;nbsp;렌더링 &lt;br&gt;-&amp;nbsp;천장:&amp;nbsp;흰색&amp;nbsp;고정 &lt;br&gt;&lt;br&gt;방 크기는 사용자가 입력한 가로/세로/높이 수치를 그대로 써서 PlaneGeometry의 width, height에 넣는다.&lt;br&gt;&lt;br&gt;&lt;b&gt;④ 결과&lt;/b&gt;&lt;br&gt;실제&amp;nbsp;방&amp;nbsp;사진의&amp;nbsp;색감이&amp;nbsp;3D&amp;nbsp;공간에&amp;nbsp;그대로&amp;nbsp;반영되기&amp;nbsp;때문에&amp;nbsp;단순히&amp;nbsp;회색&amp;nbsp;박스를&amp;nbsp;만드는&amp;nbsp;것보다&amp;nbsp;훨씬&amp;nbsp;현실감&amp;nbsp;있는&amp;nbsp;시뮬레이션&amp;nbsp;공간이&amp;nbsp;된다.&amp;nbsp;AI가&amp;nbsp;방&amp;nbsp;구조를&amp;nbsp;이해한&amp;nbsp;게&amp;nbsp;아니라,&amp;nbsp;색상&amp;nbsp;정보를&amp;nbsp;추출해서&amp;nbsp;3D&amp;nbsp;박스에&amp;nbsp;입힌&amp;nbsp;것이지만&amp;nbsp;인테리어&amp;nbsp;배치&amp;nbsp;시뮬레이션&amp;nbsp;용도로는&amp;nbsp;충분히&amp;nbsp;그럴듯한&amp;nbsp;결과물이&amp;nbsp;나온다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;&lt;p data-ke-size=&quot;size14&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;근데 완성하고 막상 보니 미흡한 점이 한두 가지가 아니었다. 3D 뷰어가 전체 화면을 못 차지하고 작은 박스에 갇혀 있었고, 배치한 가구가 바닥 위로 둥둥 떠다니고, 벽을 그냥 통과해버리고, 가구들끼리 겹치고, 가구를 선택해도 아무것도 할 수 없는 구조였다. 인테리어 시뮬레이션이라고 하기엔 너무 허술했다. &lt;br&gt;&lt;br&gt;그래서&amp;nbsp;이번에는&amp;nbsp;배치&amp;nbsp;단계&amp;nbsp;전반을&amp;nbsp;뜯어고쳤다.&amp;nbsp;UI를&amp;nbsp;이케아&amp;nbsp;플래너나&amp;nbsp;RoomSketcher처럼&amp;nbsp;3D&amp;nbsp;뷰어가&amp;nbsp;화면을&amp;nbsp;꽉&amp;nbsp;채우는&amp;nbsp;구조로&amp;nbsp;바꾸고,&amp;nbsp;가구&amp;nbsp;물리&amp;nbsp;배치도&amp;nbsp;제대로&amp;nbsp;잡았다.&lt;br&gt;&amp;nbsp;&lt;br&gt;내가 생각한 UI는 이런 식이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2854&quot; data-origin-height=&quot;1613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biW5uB/dJMcabxjs59/OxKXjkYJoUoyr8ggz779JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biW5uB/dJMcabxjs59/OxKXjkYJoUoyr8ggz779JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biW5uB/dJMcabxjs59/OxKXjkYJoUoyr8ggz779JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiW5uB%2FdJMcabxjs59%2FOxKXjkYJoUoyr8ggz779JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;297&quot; data-origin-width=&quot;2854&quot; data-origin-height=&quot;1613&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;방 인테리어 화면을 크게하고 왼쪽에 내가 배치해볼 가구들을 놓는것&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;방&amp;nbsp;사진&amp;nbsp;업로드 &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ &lt;br&gt;SAM2&amp;nbsp;+&amp;nbsp;LaMa로&amp;nbsp;가구&amp;nbsp;제거&amp;nbsp;→&amp;nbsp;빈방&amp;nbsp;생성 &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ &lt;br&gt;빈방&amp;nbsp;사진에서&amp;nbsp;벽/바닥&amp;nbsp;색상&amp;nbsp;자동&amp;nbsp;추출 &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ &lt;br&gt;추출한&amp;nbsp;색상으로&amp;nbsp;3D&amp;nbsp;방&amp;nbsp;자동&amp;nbsp;렌더링&amp;nbsp;(Three.js) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ &lt;br&gt;&lt;span style=&quot;color: #FFFFFF;&quot;&gt;&lt;span style=&quot;background-color: #006DD7;&quot;&gt;3D&amp;nbsp;변환된&amp;nbsp;가구를&amp;nbsp;드래그해서&amp;nbsp;방에&amp;nbsp;배치&lt;/span&gt;&lt;/span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ↓ &lt;br&gt;&lt;span style=&quot;color: #FFFFFF;&quot;&gt;&lt;span style=&quot;background-color: #006DD7;&quot;&gt;가구&amp;nbsp;클릭&amp;nbsp;→&amp;nbsp;회전&amp;nbsp;/&amp;nbsp;복사&amp;nbsp;/&amp;nbsp;삭제 &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;이번에&amp;nbsp;새로&amp;nbsp;추가하거나&amp;nbsp;개선한&amp;nbsp;기능은&amp;nbsp;크게&amp;nbsp;다섯&amp;nbsp;가지다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;1. &amp;nbsp;3D&amp;nbsp;배치&amp;nbsp;UI를&amp;nbsp;전면&amp;nbsp;개편&lt;/span&gt;&lt;/b&gt; &lt;br&gt;기존에는 3D 뷰어가 작은 영역 안에 갇혀 있고 양 옆에 패널들이 나란히 붙어 있는 구조였는데, 뷰어가 화면 전체를 차지하고 가구 목록은 왼쪽에 반투명 오버레이 패널로 띄우는 방식으로 바꿨다. 컨트롤 버튼들은 우측 하단에 플로팅으로 배치했다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;927&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wGv4t/dJMcaipHmPt/lOtYbP3r406J4NaKnWqMgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wGv4t/dJMcaipHmPt/lOtYbP3r406J4NaKnWqMgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wGv4t/dJMcaipHmPt/lOtYbP3r406J4NaKnWqMgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwGv4t%2FdJMcaipHmPt%2FlOtYbP3r406J4NaKnWqMgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1889&quot; height=&quot;927&quot; data-origin-width=&quot;1889&quot; data-origin-height=&quot;927&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;&lt;b&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;2. 가구가&amp;nbsp;바닥&amp;nbsp;위에&amp;nbsp;붕&amp;nbsp;뜨는&amp;nbsp;문제&lt;/span&gt;&lt;/b&gt;&lt;br&gt;기존에는 가구 y 위치를 그냥 고정값으로 박아놨는데, Three.js에서 메시를 -PI/2 회전시키면 로컬 y축이 z축으로 바뀌는 걸 고려하지 않은 거였다. 메시 바운딩박스에서 실제 높이를 꺼내 스케일 보정까지 해서 정확하게 바닥에 붙도록 수정했다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oeItm/dJMcafzKFYB/rmuDPPANpfFkG1nJexnyD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oeItm/dJMcafzKFYB/rmuDPPANpfFkG1nJexnyD0/img.png&quot; data-alt=&quot;침대 가구 아래가 떠있는 상황.... 귀신 침대야 뭐야&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oeItm/dJMcafzKFYB/rmuDPPANpfFkG1nJexnyD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoeItm%2FdJMcafzKFYB%2FrmuDPPANpfFkG1nJexnyD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;359&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;침대 가구 아래가 떠있는 상황.... 귀신 침대야 뭐야&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TrViT/dJMcabxjtNZ/nvN9Rkgl7Jne4GKJ4ktsM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TrViT/dJMcabxjtNZ/nvN9Rkgl7Jne4GKJ4ktsM1/img.png&quot; data-alt=&quot;바닥에 잘 배치된 상황&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TrViT/dJMcabxjtNZ/nvN9Rkgl7Jne4GKJ4ktsM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTrViT%2FdJMcabxjtNZ%2FnvN9Rkgl7Jne4GKJ4ktsM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;307&quot; data-origin-width=&quot;1574&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바닥에 잘 배치된 상황&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: Noto Sans Light;&quot;&gt;3.&lt;/span&gt; 벽 통과 방지와 가구끼리 겹침 방지&lt;/b&gt;&lt;br&gt;드래그 중에 방 크기 기반으로 XZ 좌표를 클램프해서 벽 밖으로 못 나가게 하고, 다른 가구와의 거리를 계산해서 일정 거리 이내로 겹치면 밀어내는 방식으로 구현했다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Rs0o6/dJMb990CC02/7wEr8s5FqwY02xnlVJi4W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Rs0o6/dJMb990CC02/7wEr8s5FqwY02xnlVJi4W0/img.png&quot; data-alt=&quot;침대 가구들이 겹쳐져 있는 상황&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Rs0o6/dJMb990CC02/7wEr8s5FqwY02xnlVJi4W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRs0o6%2FdJMb990CC02%2F7wEr8s5FqwY02xnlVJi4W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;243&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;침대 가구들이 겹쳐져 있는 상황&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;&lt;b&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;4. 가구 클릭시 컨텍스트 메뉴 기능 추가&lt;/span&gt;&lt;/b&gt;&lt;br&gt;네&amp;nbsp;번째로&amp;nbsp;가구&amp;nbsp;클릭&amp;nbsp;시&amp;nbsp;컨텍스트&amp;nbsp;메뉴를&amp;nbsp;추가했다.&amp;nbsp;배치된&amp;nbsp;가구를&amp;nbsp;클릭하면&amp;nbsp;회전&amp;nbsp;/&amp;nbsp;복사&amp;nbsp;/&amp;nbsp;삭제&amp;nbsp;버튼이&amp;nbsp;나타나는&amp;nbsp;방식이다.&amp;nbsp;드래그와&amp;nbsp;클릭을&amp;nbsp;구분하기&amp;nbsp;위해&amp;nbsp;mousedown과&amp;nbsp;mouseup&amp;nbsp;사이의&amp;nbsp;이동&amp;nbsp;거리를&amp;nbsp;측정해서&amp;nbsp;5px&amp;nbsp;이하면&amp;nbsp;클릭으로&amp;nbsp;판단하도록&amp;nbsp;했다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;947&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TUsEu/dJMcabD6Gtv/MvH11l3Xt7pOXoQlHv2YH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TUsEu/dJMcabD6Gtv/MvH11l3Xt7pOXoQlHv2YH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TUsEu/dJMcabD6Gtv/MvH11l3Xt7pOXoQlHv2YH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTUsEu%2FdJMcabD6Gtv%2FMvH11l3Xt7pOXoQlHv2YH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1904&quot; height=&quot;947&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;947&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;&lt;span style=&quot;font-family: Noto Sans Demilight, Noto Sans KR;&quot;&gt;5.&lt;/span&gt; 마우스 휠 줌을 추가&lt;/b&gt; &lt;br&gt;다섯&amp;nbsp;번째로&amp;nbsp;마우스&amp;nbsp;휠&amp;nbsp;줌을&amp;nbsp;추가했다.&amp;nbsp;기존엔&amp;nbsp;고정된&amp;nbsp;카메라&amp;nbsp;거리로만&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있었는데,&amp;nbsp;휠&amp;nbsp;이벤트를&amp;nbsp;캡처해서&amp;nbsp;카메라의&amp;nbsp;구면&amp;nbsp;좌표&amp;nbsp;반지름을&amp;nbsp;조절하는&amp;nbsp;방식으로&amp;nbsp;줌&amp;nbsp;인/아웃이&amp;nbsp;가능해졌다.&amp;nbsp;이때&amp;nbsp;{&amp;nbsp;passive:&amp;nbsp;false&amp;nbsp;}를&amp;nbsp;꼭&amp;nbsp;넣어야&amp;nbsp;preventDefault()가&amp;nbsp;먹혀서&amp;nbsp;페이지&amp;nbsp;스크롤을&amp;nbsp;막을&amp;nbsp;수&amp;nbsp;있다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  Troubleshooting&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;&lt;b&gt;1.&amp;nbsp;height:&amp;nbsp;100%가&amp;nbsp;먹히지&amp;nbsp;않는&amp;nbsp;문제&lt;/b&gt; &lt;br&gt;3D&amp;nbsp;뷰어를&amp;nbsp;전체화면으로&amp;nbsp;만들려고&amp;nbsp;height:&amp;nbsp;100%를&amp;nbsp;줬는데&amp;nbsp;전혀&amp;nbsp;반응이&amp;nbsp;없었다.&amp;nbsp;원인은&amp;nbsp;부모&amp;nbsp;컴포넌트&amp;nbsp;체인&amp;nbsp;어딘가에&amp;nbsp;height가&amp;nbsp;명시되지&amp;nbsp;않아서였다.&amp;nbsp;CSS에서&amp;nbsp;height:&amp;nbsp;100%는&amp;nbsp;부모가&amp;nbsp;높이를&amp;nbsp;가지고&amp;nbsp;있어야&amp;nbsp;전달되는데,&amp;nbsp;App.jsx의&amp;nbsp;main&amp;nbsp;컨테이너부터&amp;nbsp;Interior3DStep까지&amp;nbsp;이어지는&amp;nbsp;전체&amp;nbsp;체인에&amp;nbsp;height를&amp;nbsp;잡아줘야&amp;nbsp;했다.&amp;nbsp;결국&amp;nbsp;최상위&amp;nbsp;div에&amp;nbsp;height:&amp;nbsp;100vh를&amp;nbsp;주고,&amp;nbsp;Three.js&amp;nbsp;캔버스가&amp;nbsp;붙는&amp;nbsp;div까지&amp;nbsp;height:&amp;nbsp;100%로&amp;nbsp;연결해서&amp;nbsp;해결했다. &lt;br&gt;&lt;br&gt;추가로&amp;nbsp;Three.js&amp;nbsp;renderer&amp;nbsp;초기화&amp;nbsp;시&amp;nbsp;높이를&amp;nbsp;el.clientHeight로&amp;nbsp;받아야&amp;nbsp;하는데,&amp;nbsp;이게&amp;nbsp;0으로&amp;nbsp;찍힐&amp;nbsp;경우&amp;nbsp;window.innerHeight로&amp;nbsp;폴백하도록&amp;nbsp;처리했다. &lt;br&gt;&lt;br&gt;&lt;b&gt;2.&amp;nbsp;가로&amp;nbsp;스크롤이&amp;nbsp;생기는&amp;nbsp;문제&lt;/b&gt; &lt;br&gt;3D&amp;nbsp;뷰어를&amp;nbsp;100vw로&amp;nbsp;확장하면서&amp;nbsp;스크롤바&amp;nbsp;너비(약&amp;nbsp;15px)만큼&amp;nbsp;화면이&amp;nbsp;넘쳐서&amp;nbsp;가로&amp;nbsp;스크롤이&amp;nbsp;생겼다.&amp;nbsp;App.css에&amp;nbsp;overflow-x:&amp;nbsp;hidden을&amp;nbsp;추가해서&amp;nbsp;해결했다. &lt;br&gt;&lt;br&gt;&lt;b&gt;3.&amp;nbsp;SAM3D&amp;nbsp;포인트클라우드&amp;nbsp;품질&amp;nbsp;개선&lt;/b&gt; &lt;br&gt;생성된&amp;nbsp;3D&amp;nbsp;메시가&amp;nbsp;너무&amp;nbsp;낡고&amp;nbsp;왜곡된&amp;nbsp;형태로&amp;nbsp;나오는&amp;nbsp;문제가&amp;nbsp;있었다.&amp;nbsp;원인은&amp;nbsp;두&amp;nbsp;가지였다.&amp;nbsp;하나는&amp;nbsp;포인트&amp;nbsp;샘플링&amp;nbsp;stride를&amp;nbsp;2로&amp;nbsp;설정해서&amp;nbsp;픽셀을&amp;nbsp;건너뛰며&amp;nbsp;찍다&amp;nbsp;보니&amp;nbsp;포인트&amp;nbsp;수가&amp;nbsp;4분의&amp;nbsp;1로&amp;nbsp;줄어든&amp;nbsp;것이었고,&amp;nbsp;다른&amp;nbsp;하나는&amp;nbsp;카메라&amp;nbsp;초점거리&amp;nbsp;fx,&amp;nbsp;fy를&amp;nbsp;500으로&amp;nbsp;고정해놔서&amp;nbsp;이미지&amp;nbsp;크기와&amp;nbsp;상관없이&amp;nbsp;왜곡이&amp;nbsp;생기는&amp;nbsp;거였다.&amp;nbsp;stride를&amp;nbsp;1로&amp;nbsp;바꾸고&amp;nbsp;fx&amp;nbsp;=&amp;nbsp;fy&amp;nbsp;=&amp;nbsp;max(h,&amp;nbsp;w)&amp;nbsp;*&amp;nbsp;1.2로&amp;nbsp;이미지&amp;nbsp;크기&amp;nbsp;기반으로&amp;nbsp;계산하도록&amp;nbsp;수정했다. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; ️&amp;nbsp;향후&amp;nbsp;계획&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;1.&amp;nbsp;처리&amp;nbsp;시간&amp;nbsp;단축 &lt;br&gt;SAM3D로&amp;nbsp;하나의&amp;nbsp;가구를&amp;nbsp;3D&amp;nbsp;메시로&amp;nbsp;변환하는&amp;nbsp;데&amp;nbsp;현재&amp;nbsp;20~30분이&amp;nbsp;걸린다.&amp;nbsp;논문에서&amp;nbsp;제시한&amp;nbsp;구조&amp;nbsp;그대로&amp;nbsp;돌리다&amp;nbsp;보니&amp;nbsp;최적화가&amp;nbsp;전혀&amp;nbsp;안&amp;nbsp;된&amp;nbsp;상태다.&amp;nbsp;모델&amp;nbsp;경량화나&amp;nbsp;추론&amp;nbsp;파이프라인&amp;nbsp;개선을&amp;nbsp;통해&amp;nbsp;대기&amp;nbsp;시간을&amp;nbsp;줄일&amp;nbsp;예정이다. &lt;br&gt;&lt;br&gt;2.&amp;nbsp;3D&amp;nbsp;모델링&amp;nbsp;정확도&amp;nbsp;향상 &lt;br&gt;현재 SAM3D로 생성된 3D 메시가 원본 가구 형태와 많이 다르게 복원되는 문제가 있다. 포인트 클라우드 밀도를 높이고 fx/fy 초점거리 계산을 개선해서 가구 형태가 더 정확하게 복원되도록 할 예정이다.&lt;/p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;945&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq3PV9/dJMcaa6hp83/YmLQCGOyge4XqZLNWkeIw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq3PV9/dJMcaa6hp83/YmLQCGOyge4XqZLNWkeIw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq3PV9/dJMcaa6hp83/YmLQCGOyge4XqZLNWkeIw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq3PV9%2FdJMcaa6hp83%2FYmLQCGOyge4XqZLNWkeIw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;945&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;945&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;color: #FFFFFF;&quot;&gt; 낡은 신데렐라가 쓸 거 같은 침대가...... 만들어지고옷장도 왜 납작 떡이&lt;br&gt;&lt;br&gt;&lt;/span&gt;3.&amp;nbsp;사용자&amp;nbsp;가구&amp;nbsp;사진&amp;nbsp;직접&amp;nbsp;등록&amp;nbsp;기능 &lt;br&gt;현재는 업로드한 방 사진에서 인식된 가구만 3D로 변환할 수 있어서 배치할 수 있는 아이템이 너무 한정적이다. 사용자가 원하는 가구를 직접 사진 찍어서 올리면 바로 3D 아이템으로 만들어주는 기능을 추가할 예정이다. &lt;s&gt;빡셀듯하다..&lt;/s&gt;&lt;br&gt;&lt;br&gt;4.&amp;nbsp;서비스&amp;nbsp;배포 &lt;br&gt;지금은&amp;nbsp;WSL&amp;nbsp;로컬&amp;nbsp;환경에서만&amp;nbsp;돌아가는&amp;nbsp;상태다.&amp;nbsp;클라우드&amp;nbsp;GPU&amp;nbsp;서버에&amp;nbsp;올려서&amp;nbsp;실제로&amp;nbsp;사용&amp;nbsp;가능한&amp;nbsp;웹&amp;nbsp;서비스로&amp;nbsp;배포하는&amp;nbsp;것을&amp;nbsp;목표로&amp;nbsp;하고&amp;nbsp;있다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/4</guid>
      <comments>https://angrimilog.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 6 May 2026 21:29:34 +0900</pubDate>
    </item>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (2)</title>
      <link>https://angrimilog.tistory.com/3</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  이번 글에서 구현한 것&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서 예고했던 &quot;단일 사진으로 3D 방 자동 생성&quot; 기능을 구현했다.&amp;nbsp;&lt;br&gt;기존에는 가로 5m, 세로 4m 이런 식으로 사용자가 방 크기를 직접 입력하면 그냥 단색 박스 방이 만들어지는 수준이었다. 근데 생각해보면 사진을 이미 올렸는데 굳이 숫자를 또 입력받는 게 불편하기도 하고, 색상도 실제 방이랑 전혀 달라서 인테리어 시뮬레이션으로 쓰기엔 아쉬움이 있었다.&amp;nbsp;&lt;br&gt;그래서 이번에는 방 사진에서 가구를 제거해 빈방을 만들고, 그 빈방 사진에서 벽 색상과 바닥 색상을 자동으로 뽑아서 3D 방에 그대로 적용하는 방식으로 개선했다. 덕분에 실제 방이랑 훨씬 비슷한 느낌의 3D 공간이 만들어졌다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1m3S4/dJMcaaSDh1q/8y7fk5uiWAxn1lTEUdKNk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1m3S4/dJMcaaSDh1q/8y7fk5uiWAxn1lTEUdKNk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1m3S4/dJMcaaSDh1q/8y7fk5uiWAxn1lTEUdKNk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1m3S4%2FdJMcaaSDh1q%2F8y7fk5uiWAxn1lTEUdKNk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;434&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;div&gt; 
 &lt;div&gt;
   방&amp;nbsp;사진&amp;nbsp;업로드 
  &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ 
  &lt;br&gt;SAM2&amp;nbsp;+&amp;nbsp;LaMa로&amp;nbsp;가구&amp;nbsp;제거&amp;nbsp;→&amp;nbsp;빈방&amp;nbsp;생성 
  &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ 
  &lt;br&gt;빈방&amp;nbsp;사진에서&amp;nbsp;벽/바닥&amp;nbsp;색상&amp;nbsp;자동&amp;nbsp;추출 
  &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ 
  &lt;br&gt;추출한&amp;nbsp;색상으로&amp;nbsp;3D&amp;nbsp;방&amp;nbsp;자동&amp;nbsp;렌더링&amp;nbsp;(Three.js) 
  &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;↓ 
  &lt;br&gt;3D&amp;nbsp;변환된&amp;nbsp;가구를&amp;nbsp;드래그해서&amp;nbsp;방에&amp;nbsp;배치 
 &lt;/div&gt; 
 &lt;div&gt;
   &amp;nbsp; 
 &lt;/div&gt; 
 &lt;div&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;이번에 새로 추가한 기능은 크게 세 가지다.&lt;/p&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로 기존 2D 웹앱에서 쓰던 LaMa 인페인팅을 3D 웹앱에도 붙여서 가구를 클릭하면 자동으로 지워진 빈방 사진을 만들어준다.&lt;/p&gt; 
  &lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 그 빈방 사진의 픽셀값을 분석해서 벽 영역과 바닥 영역의 평균 색상을 뽑아낸다. 복잡한 AI 없이 이미지 영역을 나눠서 평균을 내는 방식인데, 생각보다 꽤 그럴듯하게 색상이 추출됐다.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/div&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Troubleshooting&lt;/h3&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. sam3d 환경에 LaMa 설치 문제&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;3D 웹앱 서버에 LaMa를 붙이려고 했는데 서버 실행하자마자 바로 에러가 떴다. albumentations, webdataset, kornia 같은 모듈들이 없다는 거였다. sam3d 환경은 Meta SAM3D 돌리는 데 필요한 것들만 설치해둔 상태라 2D 웹앱에서 쓰던 LaMa 의존성들이 하나도 없었던 거다. 순서대로 하나씩 설치하면서 해결했다.&lt;br&gt;거기에 SD(Stable Diffusion) 로드할 때는 또 다른 문제가 생겼다.&lt;/p&gt;&lt;div&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;angelscript&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;torch.load 보안 취약점 (CVE-2025-32434)
이 기능을 사용하려면 torch 2.6 이상이 필요합니다.&lt;/code&gt;&lt;/pre&gt; 
 &lt;/div&gt; 
&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 설치된 torch가 2.5.1이라 SD가 아예 로드가 안 됐다. cu121 버전의 torch 2.6이 없어서 cu124 버전으로 업그레이드해서 해결했다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. w is not defined ReferenceError&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;3D 방에 텍스처를 입히려고 loadTexture 함수를 추가했는데 방이 까맣게 뜨면서 콘솔에 이런 에러가 나왔다.&lt;/p&gt;&lt;div&gt; 
 &lt;div&gt; 
  &lt;pre class=&quot;xquery&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;ReferenceError: w is not defined at RoomViewer&lt;/code&gt;&lt;/pre&gt; 
 &lt;/div&gt; 
&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;원인을 찾아보니 텍스처 로더 코드를 useEffect 밖에 선언해버린 거였다. w, h, d 같은 방 크기 변수들은 useEffect 안에서 선언되는데, 텍스처 코드는 그 바깥에 있으니 당연히 변수를 못 찾는 거였다. 텍스처 관련 코드 전체를 useEffect 안으로 이동시켜서 해결했다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 향후 계획&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 바닥/벽 재질 개선&lt;/b&gt; 현재는 색상만 추출해서 단색으로 칠하는 방식이라 실제 방이랑 비교하면 많이 단조롭다. 빈방 사진에서 바닥과 벽의 재질 자체를 인식해서 나무 무늬, 타일, 벽지 패턴 등을 실제처럼 표현하는 방향으로 개선할 예정이다.&lt;br&gt;&lt;b&gt;2. SAM2 + LaMa 처리 속도 개선&lt;/b&gt; 가구 인식부터 빈방 생성까지 지금은 시간이 꽤 걸린다. 모델 경량화나 추론 최적화를 통해 사용자가 체감하는 대기 시간을 줄이는 작업을 진행할 예정이다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/3</guid>
      <comments>https://angrimilog.tistory.com/3#entry3comment</comments>
      <pubDate>Sun, 26 Apr 2026 15:53:20 +0900</pubDate>
    </item>
    <item>
      <title>Meta SAM3D 인테리어 시스템 개발 기록 (1)</title>
      <link>https://angrimilog.tistory.com/2</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  프로젝트 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;실제 생활 공간은 이미 다양한 가구로 구성되어 있는 경우가 대부분이다. 그런데 IKEA Place, 오늘의집 등 기존 인테리어 앱들은 모두&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;빈 방에 새 가구를 배치&lt;/b&gt;하는 방향으로만 동작한다. 이미 보유한 가구를 활용해 레이아웃을 바꿔보는 기능은 지원하지 않는다. 그리고&amp;nbsp;&amp;nbsp;기존의 인테리어 프로그램들은 빈 도면에서 시작하여 카탈로그 내 수많은 모델 중 자신의 가구와 가장 유사한 것을 일일이 찾아 배치해야 하는 번거로움으로 인하여 전문가가 아닌 일반 사용자가 사용하기에는 큰 어려움이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2D 가구 제거 웹앱에 이어, 이번에는 &lt;b&gt;Meta SAM3D&lt;/b&gt;를 활용해 방 사진에서 가구를 추출하고 3D 메쉬로 변환하는 인테리어 시뮬레이션 시스템을 구축했다. 기존 2D 웹앱의 한계를 넘어, 실제 인테리어 디자이너가 사용하는 툴처럼 &lt;b&gt;3D 공간 안에 가구를 자유롭게 배치&lt;/b&gt;할 수 있는 시스템 구현 목표다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  기술 스택&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 76.5101%; height: 122px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 21px; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 21px; text-align: center;&quot;&gt;사용 기술&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 21px;&quot;&gt;&lt;b&gt;3D 모델 생성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 21px;&quot;&gt;Meta SAM3D (facebookresearch/sam-3d-objects)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 21px;&quot;&gt;&lt;b&gt;가구 인식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 21px;&quot;&gt;SAM2 (Segment Anything Model 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 21px;&quot;&gt;&lt;b&gt;백엔드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 21px;&quot;&gt;FastAPI (Python, sam3d 전용 conda 환경, 포트 8001)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 21px;&quot;&gt;&lt;b&gt;프론트엔드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 21px;&quot;&gt;React.js + Three.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.1608%; height: 17px;&quot;&gt;&lt;b&gt;3D 렌더링&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 58.1662%; height: 17px;&quot;&gt;Three.js (CDN r128)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;⚙️ 시스템 파이프라인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방&amp;nbsp;사진&amp;nbsp;업로드 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;SAM2로&amp;nbsp;가구&amp;nbsp;클릭&amp;nbsp;인식&amp;nbsp;+&amp;nbsp;마스크&amp;nbsp;생성 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;가구&amp;nbsp;누끼&amp;nbsp;추출&amp;nbsp;(Extract&amp;nbsp;Service) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;Meta&amp;nbsp;SAM3D로&amp;nbsp;가구&amp;nbsp;&amp;rarr;&amp;nbsp;3D&amp;nbsp;메쉬&amp;nbsp;변환 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr; &lt;br /&gt;Three.js&amp;nbsp;3D&amp;nbsp;방에&amp;nbsp;가구&amp;nbsp;배치&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Meta SAM3D란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Meta SAM3D는 Facebook Research가 2025년 공개한 모델로, 단일 이미지 하나만으로 완성된 3D 메쉬를 직접 생성한다.&amp;nbsp; 트랜스포머 기반 아키텍처로 DINO v2 임베딩을 거쳐 Sparse Structure를 생성하고, 이를 디코딩해 최종 메쉬를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Three.js란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Three.js는 웹 브라우저에서 3D 그래픽을 구현할 수 있는 JavaScript 라이브러리다. WebGL을 직접 다루면 코드가 엄청 복잡해지는데, Three.js가 이걸 추상화해줘서 훨씬 간단하게 쓸 수 있다. 이 프로젝트에서는 SAM3D가 만들어준 3D 메쉬 데이터를 화면에 그려주는 용도로 사용했다. 사용자가 입력한 방 크기로 와이어프레임 방을 만들고, 가구 메쉬를 그 안에 배치한다. 마우스로 가구를 클릭해 바닥 위에서 자유롭게 드래그할 수 있고, 우클릭으로 카메라를 돌려가며 다양한 각도에서 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN 방식으로 사용해서 별도 빌드 설정 없이 window.THREE로 접근한다:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #eaecf0;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CDN란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CDN(Content Delivery Network)&lt;/b&gt;은 라이브러리를 직접 설치하지 않고 인터넷에서 바로 불러오는 방식이다. &lt;br /&gt;예를들어 npm install three 대신 &amp;lt;script src=&quot;https://...&quot;&amp;gt; 한 줄로 Three.js를 사용하는 것!!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경 구성&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;왜 별도 conda 환경을 만들었나?&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;기존 furniture 환경은 PyTorch 2.7.1+cu118로 구성되어 있었다. Meta SAM3D의 핵심 의존성인 &lt;/span&gt;&lt;b&gt;pytorch3d&lt;/b&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;가 CUDA 12.1 기반으로 빌드되어야 해서 CUDA 버전 충돌이 발생했다.&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;RuntimeError: The detected CUDA version (13.2) mismatches 
the version that was used to compile PyTorch (11.8).&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 환경을 건드리면 SAM2, LaMa 등 다른 모델들이 망가질 수 있어서 &lt;b&gt;sam3d 전용 conda 환경&lt;/b&gt;을 새로 생성했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #eaecf0;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;conda create -n sam3d python=3.11
conda activate sam3d
pip install torch==2.5.1+cu121 torchvision --extra-index-url https://download.pytorch.org/whl/cu121&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 의존성 설치 과정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pytorch3d 빌드 시 g++ 컴파일러가 없어서 추가 설치가 필요했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;nginx&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;conda install -c conda-forge gxx_linux-64 -y
pip install &quot;git+https://github.com/facebookresearch/pytorch3d.git@stable&quot; --no-build-isolation&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 spconv-cu121, kaolin, gsplat, utils3d, MoGe 등 다수의 패키지를 순차적으로 설치했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. utils3d.numpy.depth_edge ImportError&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAM3D 임포트 테스트를 하는데 갑자기 이런 에러가 떴다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;capnproto&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;from utils3d.numpy import depth_edge  # ImportError!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 utils3d가 제대로 설치가 안 된 줄 알고 재설치를 반복했는데 계속 똑같은 에러였다. 알고 보니 utils3d 라이브러리가 버전업 되면서 함수명이 depth_edge에서 depth_map_edge로 바뀌어 있었다. SAM3D 코드는 구버전 기준으로 작성되어 있어서 생긴 충돌이었다.&amp;nbsp;패키지를 다운그레이드하는 방법도 있었지만, SAM3D 소스코드를 직접 한 줄 패치하는 게 더 깔끔했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;capnproto&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;sed -i 's/from utils3d.numpy import depth_edge/from utils3d.numpy import depth_map_edge as depth_edge/' \
  ~/sam-3d-objects/sam3d_objects/pipeline/layout_post_optimization_utils.py&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. mask_points.isfinite().max() RuntimeError&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 임포트까지 성공하고 3D 변환 버튼을 눌렀더니 이번엔 500 에러가 떴다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vim&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;RuntimeError: max(): Expected reduction dim to be specified 
for input.numel() == 0.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;에러 메시지만 봐서는 뭔 소린지 몰라서 한참 들여다봤다. SAM3D는 내부적으로 가구 이미지의 알파 채널(투명/불투명 정보)을 마스크로 사용해서 &quot;여기가 가구 영역이에요&quot;라고 인식한다. 그 영역에서 3D 좌표값들을 뽑아서 정규화하는 과정이 있는데, 뽑힌 좌표값이 하나도 없는 빈 상태일 때 .max()(최댓값 구하기)를 호출하니까 에러가 난 것이었다. 빈 상자에서 제일 큰 거 꺼내라는 격이었다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;glsl&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;# 문제 코드
if mask_points.isfinite().max() == 0:  # numel()==0이면 에러!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 간단했다. 최댓값을 구하기 전에 먼저 &quot;비어있냐?&quot;를 체크하도록 조건을 바꿔주면 됐다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;mipsasm&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;# 수정 코드
if mask_points.numel() == 0 or (mask_points.numel() &amp;gt; 0 and not mask_points.isfinite().any()):&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. MeshExtractResult AttributeError&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;40~50분을 기다려서 드디어 3D 생성이 완료됐는데 이번엔 또 다른 에러가 떴다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #eaecf0;&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;AttributeError: 'MeshExtractResult' object has no attribute 'verts_packed'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;당연히 pytorch3d의 Meshes 객체겠지 싶어서 verts_packed()를 호출했는데 바로 에러였다. 알고 보니 Meta SAM3D는 자체 MeshExtractResult 클래스를 사용하고 있었고, 이 클래스는 verts_packed() 같은 메서드 없이 .vertices와 .faces 속성을 직접 가지고 있었다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;# inspect로 내부 구조 확인
print(inspect.getsource(MeshExtractResult.__init__))
# &amp;rarr; self.vertices = vertices
# &amp;rarr; self.faces = faces.long()
# &amp;rarr; self.vertex_attrs = vertex_attrs  &amp;larr; 이게 나중에 색상 문제 해결의 열쇠가 됨&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;# 변경 전
vertices = mesh.verts_packed().cpu().numpy().tolist()
faces = mesh.faces_packed().cpu().numpy().tolist()

# 변경 후
vertices = mesh.vertices.cpu().numpy().tolist()
faces = mesh.faces.cpu().numpy().tolist()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 가구 3D 메쉬가 흰색으로만 렌더링되는 문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 3D 메쉬가 화면에 나타났는데... 소파가 완전히 하얗게 렌더링됐다. use_vertex_color=True 옵션까지 넣었는데 왜 색이 없지?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;871&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/siJe7/dJMb990sA7T/DrnQawHYqEfF9v1u92Wfqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/siJe7/dJMb990sA7T/DrnQawHYqEfF9v1u92Wfqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/siJe7/dJMb990sA7T/DrnQawHYqEfF9v1u92Wfqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsiJe7%2FdJMb990sA7T%2FDrnQawHYqEfF9v1u92Wfqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;410&quot; data-origin-width=&quot;1393&quot; data-origin-height=&quot;871&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 코드를 보니 문제가 바로 보였다. 색상 데이터를 아예 추출하지 않고 그냥 회색으로 고정해버리고 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1776852478055&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문제 코드 - 항상 회색으로 고정
colors = [[0.8, 0.8, 0.8]] * len(vertices)&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 inspect로 내부 구조를 확인할 때 봤던 vertex_attrs가 생각났다. 확인해보니 바로 거기에 버텍스별 색상 정보가 들어있었다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;lua&quot; style=&quot;color: #eaecf0;&quot;&gt;&lt;code&gt;# 수정 코드
if mesh.vertex_attrs is not None:
    colors = mesh.vertex_attrs[:, :3].cpu().numpy().tolist()
else:
    colors = [[0.8, 0.8, 0.8]] * len(vertices)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 줄 수정으로 소파의 베이지색, 나무 다리의 갈색까지 실제 색상이 그대로 렌더링됐다. 뭔가 오래 걸렸는데 결과물 보니까 뿌듯했다  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rpyoP/dJMcacv6E69/bATKaqTnCVsrUttVNbJEy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rpyoP/dJMcacv6E69/bATKaqTnCVsrUttVNbJEy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rpyoP/dJMcacv6E69/bATKaqTnCVsrUttVNbJEy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrpyoP%2FdJMcacv6E69%2FbATKaqTnCVsrUttVNbJEy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;351&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚡ 성능 이슈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Meta SAM3D는 &lt;b&gt;RTX 4070 SUPER (12GB VRAM)&lt;/b&gt; 환경에서 가구 1개당 약 &lt;b&gt;1시간&lt;/b&gt;이 소요된다. 사용자가 실시간 인테리어 툴로 활용하기엔 너무 느리다는 한계가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 향후 계획&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 속도 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inference_steps를 25에서 10으로 줄이거나 float16 변환 등을 통해 처리 시간 단축을 검토할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 단일 사진으로 3D 방 자동 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 사용자가 방 크기를 직접 입력하는 방식이다. 추후 업로드한 방 사진 한 장에서 Depth Estimation을 활용해 3D 방 구조를 자동으로 추출하는 기능을 개발할 예정이다.&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/2</guid>
      <comments>https://angrimilog.tistory.com/2#entry2comment</comments>
      <pubDate>Wed, 22 Apr 2026 18:47:14 +0900</pubDate>
    </item>
    <item>
      <title>SAM2 &amp;middot; LaMa &amp;middot; SD ControlNet으로 구현한 AI 인테리어 역방향 편집 시스템</title>
      <link>https://angrimilog.tistory.com/1</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  프로젝트 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 생활 공간은 이미 다양한 가구로 구성되어 있는 경우가 대부분이다. 그런데 IKEA Place, 오늘의집 등 기존 인테리어 앱들은 모두 &lt;b&gt;빈 방에 새 가구를 배치&lt;/b&gt;하는 방향으로만 동작한다. 이미 보유한 가구를 활용해 레이아웃을 바꿔보는 기능은 지원하지 않는다. 그리고&amp;nbsp;&amp;nbsp;&lt;span&gt;기존의 인테리어 프로그램들은 빈 도면에서 시작하여 카탈로그 내 수많은 모델 중 자신의 가구와 가장 유사한 것을 일일이 찾아 배치해야 하는 번거로움으로 인하여 전문가가 아닌 일반 사용자가 사용하기에는 큰 어려움이 있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 프로젝트는 이 어려움을 해결하기 위해 &lt;b&gt;사진 한 장만으로 가구를 제거하고 재배치&lt;/b&gt;할 수 있는 2D 역방향 공간 편집 시스템을 개발하였다. 이사, 청소, 리모델링 전에 공간 변화를 미리 시뮬레이션할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  기술 스택&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 55.5813%; height: 117px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 22px; text-align: center;&quot;&gt;역할&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 22px; text-align: center;&quot;&gt;기술&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 22px; text-align: left;&quot;&gt;&lt;b&gt;객체 분할&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 22px; text-align: left;&quot;&gt;SAM2 (Meta AI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 22px; text-align: left;&quot;&gt;&lt;b&gt;1차 배경 복원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 22px; text-align: left;&quot;&gt;LaMa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 17px; text-align: left;&quot;&gt;&lt;b&gt;2차 배경 복원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 17px; text-align: left;&quot;&gt;Stable Diffusion ControlNet Inpainting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 17px; text-align: left;&quot;&gt;&lt;b&gt;백엔드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 17px; text-align: left;&quot;&gt;FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 29.0698%; height: 17px; text-align: left;&quot;&gt;&lt;b&gt;프론트엔드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 61.1189%; height: 17px; text-align: left;&quot;&gt;Reat&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚙️ 시스템 파이프라인&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bng8ep/dJMb990p8QF/r65Q51vQji6s6tA2fVyGi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bng8ep/dJMb990p8QF/r65Q51vQji6s6tA2fVyGi1/img.png&quot; data-alt=&quot;2D 시스템 파이프라인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bng8ep/dJMb990p8QF/r65Q51vQji6s6tA2fVyGi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbng8ep%2FdJMb990p8QF%2Fr65Q51vQji6s6tA2fVyGi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;418&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2D 시스템 파이프라인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  주요 기능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;① 가구 객체 분할 및 아이템 저장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제거할 가구 위를 마우스로 클릭하면 SAM2가 자동으로 해당 객체의 마스크를 생성한다. 여러 포인트를 클릭해 개별 마스크를 생성한 후 합산하여 최종 마스크를 구성한다. 선택된 가구는 배경이 투명한 RGBA PNG로 저장되어 재배치 단계에서 활용된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/In9ow/dJMcaf0AN2U/rRTZgXUvt8Yz9yrOKLzpT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/In9ow/dJMcaf0AN2U/rRTZgXUvt8Yz9yrOKLzpT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/In9ow/dJMcaf0AN2U/rRTZgXUvt8Yz9yrOKLzpT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIn9ow%2FdJMcaf0AN2U%2FrRTZgXUvt8Yz9yrOKLzpT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;404&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;889&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② 빈 방 생성 (2단계 인페인팅)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LaMa가 가구 마스크 영역을 1차로 복원하여 배경의 기하학적 구조를 잡고, 이후 SD ControlNet이 Canny 엣지를 조건으로 바닥과 벽면 텍스처를 자연스럽게 완성한다. 두 모델을 순차적으로 결합한 2단계 파이프라인으로 시각적으로 자연스러운 빈 방 이미지를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbjDjO/dJMcagearQY/qphTNVV6tw6q7GYEFDg1mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbjDjO/dJMcagearQY/qphTNVV6tw6q7GYEFDg1mK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbjDjO/dJMcagearQY/qphTNVV6tw6q7GYEFDg1mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbjDjO%2FdJMcagearQY%2FqphTNVV6tw6q7GYEFDg1mK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;343&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③ 인터랙티브 가구 재배치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽에 가구 아이템들이 리스트로 저장되어있고 생성된 빈 방 위에 저장된 가구 아이템을 드래그앤드롭으로 원하는 위치에 배치할 수 있다. 크기 조절, 위치 이동, 좌우 반전, 삭제 기능을 지원하며 다양한 레이아웃을 실시간으로 비교할 수 있다. 최종 결과는 이미지 파일로 저장된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3lLCj/dJMcajaQdQD/wN0oloLHMzKf0J9bwNQG61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3lLCj/dJMcajaQdQD/wN0oloLHMzKf0J9bwNQG61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3lLCj/dJMcajaQdQD/wN0oloLHMzKf0J9bwNQG61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3lLCj%2FdJMcajaQdQD%2FwN0oloLHMzKf0J9bwNQG61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;416&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;821&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;④ &lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Czcj/dJMcaduTPDp/CjW9cG5BBwLKipBJEHIYFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Czcj/dJMcaduTPDp/CjW9cG5BBwLKipBJEHIYFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Czcj/dJMcaduTPDp/CjW9cG5BBwLKipBJEHIYFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Czcj%2FdJMcaduTPDp%2FCjW9cG5BBwLKipBJEHIYFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;353&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 객체 인식 방법 탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 가구의 좌표를 코드에 직접 하드코딩하는 방식으로 시작했다. 당연히 이미지마다 좌표가 달라서 실용성이 없었고, 다른 방법을 찾아야 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xxrqP/dJMcacQldA1/ice31BLLz1BISMzKpKjobk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xxrqP/dJMcacQldA1/ice31BLLz1BISMzKpKjobk/img.png&quot; data-alt=&quot;좌표 객체 인식 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xxrqP/dJMcacQldA1/ice31BLLz1BISMzKpKjobk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxxrqP%2FdJMcacQldA1%2Fice31BLLz1BISMzKpKjobk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;305&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;677&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;좌표 객체 인식 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음으로 SAM2의 &lt;b&gt;Point 방식&lt;/b&gt;을 시도했다. 가구 위에 점을 찍으면 해당 객체를 인식하는 방식인데, 점 하나로는 원하는 객체를 정확하게 잡지 못하는 경우가 많았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJBonb/dJMcacJBwf7/M9T9hPDKiL0WvsMvzrhR31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJBonb/dJMcacJBwf7/M9T9hPDKiL0WvsMvzrhR31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJBonb/dJMcacJBwf7/M9T9hPDKiL0WvsMvzrhR31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJBonb%2FdJMcacJBwf7%2FM9T9hPDKiL0WvsMvzrhR31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;477&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Box 방식&lt;/b&gt;도 써봤다. 가구 주변에 박스를 그려서 영역을 지정하는 방법인데, 박스 안에 여러 객체가 함께 잡히는 문제가 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P0pYx/dJMcaipu70P/DOU8VjblwYyoTKN5eiqCT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P0pYx/dJMcaipu70P/DOU8VjblwYyoTKN5eiqCT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P0pYx/dJMcaipu70P/DOU8VjblwYyoTKN5eiqCT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP0pYx%2FdJMcaipu70P%2FDOU8VjblwYyoTKN5eiqCT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1802&quot; height=&quot;488&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 객체 자동 시각화&lt;/b&gt;도 시도해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmOjlO/dJMcabYdcDi/iOY30e5n4QXKUYcucVK7uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmOjlO/dJMcabYdcDi/iOY30e5n4QXKUYcucVK7uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmOjlO/dJMcabYdcDi/iOY30e5n4QXKUYcucVK7uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmOjlO%2FdJMcabYdcDi%2FiOY30e5n4QXKUYcucVK7uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;590&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Grounded SAM2&lt;/b&gt;를 활용해서 &quot;침대&quot;, &quot;소파&quot; 같은 텍스트를 입력하면 해당 객체를 찾는 방식도 실험해봤다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;683&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CeYPA/dJMcaiC0HZw/MtW1uMTHWmN8gGCvnNlvZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CeYPA/dJMcaiC0HZw/MtW1uMTHWmN8gGCvnNlvZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CeYPA/dJMcaiC0HZw/MtW1uMTHWmN8gGCvnNlvZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCeYPA%2FdJMcaiC0HZw%2FMtW1uMTHWmN8gGCvnNlvZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;683&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;683&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;from &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;groundingdino.util.inference&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; import &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;load_image&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;, predict&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import torch&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이미지 로드&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image_source&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image_tensor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;load_image&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(&quot;/content/drive/&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MyDrive&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;/room1.jpg&quot;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;텍스트로 침대 탐지&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TEXT_PROMPT =&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt; &quot;bed&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BOX_THRESHOLD = 0.35&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TEXT_THRESHOLD = 0.25&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;boxes, logits, phrases = predict(&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;model=model,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;image=&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image_tensor&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;caption=TEXT_PROMPT,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;box_threshold&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=BOX_THRESHOLD,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;text_threshold&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=TEXT_THRESHOLD&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 사용자가 제거할 가구 위를 &lt;b&gt;직접 다중 클릭&lt;/b&gt;하는 방식을 채택했다. 여러 포인트를 찍을수록 마스크 정확도가 높아지고, 사용자가 원하는 객체를 직접 지정할 수 있어서 가장 직관적이고 정확했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 배경 복원 품질 문제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 IOPainting만 단독으로 사용했는데, 가구를 지운 자리가 얼룩지거나 부자연스럽게 남는 문제가 있었다. LaMa로 구조적 복원을 먼저 수행하고 SD ControlNet으로 텍스처를 후처리하는 2단계 파이프라인으로 변경하면서 품질이 크게 개선되었다. 특히 원근감이 있는 나무 바닥 패턴이 가장 복원하기 어려운 케이스였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  GitHub&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/simu1231/Empty_My_Room&quot;&gt;https://github.com/simu1231/Empty_My_Room&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>angrimilog</author>
      <guid isPermaLink="true">https://angrimilog.tistory.com/1</guid>
      <comments>https://angrimilog.tistory.com/1#entry1comment</comments>
      <pubDate>Sun, 19 Apr 2026 16:19:53 +0900</pubDate>
    </item>
  </channel>
</rss>