지난 글에서는 SAM 2.1 모델을 이용해 여행 사진에서 특정 객체를 추출하는 작업을 진행했습니다.
전통 정자가 있는 사진에서 건축물의 영역을 분리하고 mask.png, overlay.png, subject.png 세 가지 결과물을 생성하는 데 성공했습니다.
이번에는 추출한 이미지를 이용해 사진의 색상과 형태를 단순화하는 이미지 추상화(Image Abstraction) 작업을 진행해 보았습니다.
최종 목표는 여행지의 특징을 유지하면서 복잡한 디테일을 제거하고, 간결한 일러스트로 변환하는 것입니다.
이를 위해 OpenCV의 Bilateral Filter, K-Means, CLAHE 등을 활용해 총 6가지 실험을 진행했습니다.
1. 이미지 추상화란?
이미지 추상화는 원본 이미지에서 불필요한 세부 정보를 줄이고 중요한 형태와 색상을 강조하는 작업입니다.
예를 들어 전통 정자 사진에는 기와지붕, 기둥, 난간, 단청 장식, 그림자 등 다양한 정보가 포함되어 있습니다.
우리가 원하는 것은 이러한 정보를 모두 표현하는 것이 아니라, 정자를 알아볼 수 있는 특징만 남기는 것입니다.
이번 프로젝트에서는 다음과 같은 방향으로 추상화를 진행하려고 합니다.
- 기와지붕의 곡선과 형태 유지
- 붉은 기둥과 난간 구조 보존
- 복잡한 질감과 그림자 단순화
- 제한된 색상으로 일러스트 표현
- 배경이 투명한 PNG 이미지 생성
다만 OpenCV는 사진 속 건축물의 의미를 이해하고 새로운 그림으로 재구성하는 생성형 AI는 아닙니다.
따라서 이번 실험은 OpenCV만으로 어느 정도까지 추상화할 수 있는지 확인하는 과정입니다.
2. 첫 번째 실험: 색상 수를 줄여보자
첫 번째 실험에서는 색상 수에 따라 이미지가 어떻게 달라지는지 확인했습니다.
사용한 기술은 크게 세 가지입니다.
Bilateral Filter
이미지의 주요 경계를 비교적 유지하면서 작은 색상 변화를 줄이는 필터입니다.
K-Means 색상 양자화
비슷한 색상을 그룹으로 묶어 이미지를 제한된 색상으로 표현하는 방법입니다.
Median Filter
주변 픽셀의 중앙값을 이용해 작은 노이즈와 색상 변화를 줄이는 필터입니다.
이 세 가지를 조합해 A, B, C 이미지를 생성했습니다.
실험 설정
| 실험 | 색상 수 | 단순화 강도 |
| A | 12색 | 약함 |
| B | 8색 | 중간 |
| C | 5색 | 강함 |
Python 구현
파일 위치: src/abstraction/abstract_image.py
먼저 SAM 2에서 추출한 이미지를 불러옵니다.
from pathlib import Path
import cv2
import numpy as np
from PIL import Image
PROJECT_ROOT = Path(__file__).resolve().parents[2]
INPUT_PATH = (
PROJECT_ROOT
/ "outputs/segmentation/subject.png"
)
image = np.array(
Image.open(INPUT_PATH).convert("RGBA")
)
rgb = image[:, :, :3]
alpha = image[:, :, 3]
여기서 RGB와 Alpha 채널을 분리했습니다.
RGB는 이미지의 색상 정보이고 Alpha는 투명도 정보입니다.
투명한 배경을 유지하기 위해 Alpha 채널은 따로 보관했습니다.
Bilateral Filter 적용
smoothed = cv2.bilateralFilter(
rgb,
d=7,
sigmaColor=80,
sigmaSpace=80
)
Bilateral Filter는 이미지의 작은 색상 변화를 줄이면서 주요 경계는 상대적으로 유지하는 역할을 합니다.
정자의 기와지붕처럼 세밀한 질감이 많은 영역을 단순화하기 위해 적용했습니다.
K-Means 색상 양자화
이번 실험에서 가장 중요한 부분입니다.
valid_mask = alpha > 127
pixels = rgb[valid_mask]
samples = np.float32(pixels)
criteria = (
cv2.TERM_CRITERIA_EPS
+ cv2.TERM_CRITERIA_MAX_ITER,
30,
0.2
)
cv2.setRNGSeed(42)
_, labels, centers = cv2.kmeans(
samples,
12,
None,
criteria,
5,
cv2.KMEANS_PP_CENTERS
)
K-Means는 픽셀의 색상을 분석해 비슷한 색끼리 그룹화합니다.
위 코드에서는 12를 지정했으므로 이미지에 사용되는 색상을 최대 12개로 줄입니다.
실험 B에서는 8개, C에서는 5개로 변경했습니다.
색상을 줄이면 이미지가 단순해질 것으로 예상했습니다.
하지만 실제 결과는 생각과 조금 달랐습니다.
3. A·B·C 실험 결과



세 가지 결과물을 비교했을 때 A가 가장 자연스러웠습니다.
| 실험 | 결과 |
| A - 12색 | 기와지붕과 기둥이 비교적 선명함 |
| B - 8색 | 기둥과 난간의 색상 구분이 약해짐 |
| C - 5색 | 어두운 영역이 통합되어 구조 일부 손실 |
A - 12색
기와지붕의 형태와 붉은 기둥이 비교적 잘 유지되었습니다.
하지만 사진의 세부 질감이 많이 남아 있어 우리가 원하는 일러스트와는 차이가 있었습니다.
B - 8색
색상 수를 줄이면서 이미지가 조금 더 단순해졌습니다.
그러나 붉은 기둥과 난간이 주변의 어두운 영역과 비슷한 색상으로 표현되기 시작했습니다.
C - 5색
가장 단순한 이미지가 생성되었지만 건축물의 특징이 많이 사라졌습니다.
특히 지붕 아래의 기둥과 난간이 어두운 색상으로 합쳐졌습니다.
결과적으로 색상 수를 줄인다고 반드시 좋은 추상화 이미지가 만들어지는 것은 아니라는 점을 확인했습니다.
왜 검은색이 많아졌을까?
처음에는 색상 수를 줄이면서 검은색이 많아지는 것이 문제라고 생각했습니다.
하지만 실제로는 여러 어두운 색상이 하나의 대표 색상으로 통합되는 것이 더 큰 문제였습니다.
K-Means는 RGB 값이 비슷한 픽셀을 그룹화할 뿐, 정자의 지붕과 기둥이 서로 다른 구조물이라는 사실은 이해하지 못합니다.
따라서 그림자 때문에 어둡게 촬영된 붉은 기둥이 다른 어두운 영역과 같은 색상으로 묶일 수 있습니다.
이 문제를 해결하기 위해 두 번째 개선 실험을 진행했습니다.
4. 두 번째 실험: LAB와 CLAHE로 밝기 개선
이번에는 단순히 색상 수를 줄이는 대신, 이미지의 어두운 영역을 먼저 보정하기로 했습니다.
특히 정자의 지붕 아래쪽과 기둥, 난간이 지나치게 어둡게 표현되는 문제를 개선하는 것이 목표였습니다.
이를 위해 LAB 색 공간과 CLAHE를 사용했습니다.
LAB 색 공간이란?
LAB 색 공간은 밝기와 색상 성분을 분리해서 표현합니다.
- L: 밝기
- A: 녹색과 빨간색 방향의 색상 성분
- B: 파란색과 노란색 방향의 색상 성분
RGB 이미지의 밝기를 조정하면 색상 표현도 함께 변할 수 있지만, LAB에서는 밝기 성분을 분리해서 조정할 수 있습니다.
CLAHE란?
CLAHE는 Contrast Limited Adaptive Histogram Equalization의 약자입니다.
이미지를 작은 영역으로 나누어 국소 대비를 개선하는 방법입니다.
이번에는 CLAHE를 적용한 뒤 감마 보정을 추가했습니다.
밝기 보정 코드
파일 위치: src/abstraction/improve_abstraction.py
def correct_brightness(rgb):
lab = cv2.cvtColor(
rgb,
cv2.COLOR_RGB2LAB
)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(
clipLimit=2.0,
tileGridSize=(8, 8)
)
enhanced = clahe.apply(l)
blended = cv2.addWeighted(
l, 0.4,
enhanced, 0.6,
0
)
normalized = (
blended.astype(np.float32) / 255.0
)
gamma = 0.72
corrected = np.power(
normalized, gamma
) * 255.0
corrected = np.clip(
corrected, 0, 255
).astype(np.uint8)
result_lab = cv2.merge(
[corrected, a, b]
)
return cv2.cvtColor(
result_lab,
cv2.COLOR_LAB2RGB
)
CLAHE로 국소 대비를 개선하고, 감마 보정을 통해 어두운 영역의 밝기를 추가로 조정했습니다.
감마값은 0.72로 설정했습니다.
이 값은 이번 실험을 위한 초기 설정이며, 이미지에 따라 조정할 필요가 있습니다.
5. 투명 영역의 검은색 문제 개선
첫 번째 실험에서 발견한 또 다른 문제는 투명 배경입니다.
SAM 2로 객체를 추출하면 배경은 투명하게 처리되지만, 투명한 픽셀의 RGB 값이 검은색으로 남아 있을 수 있습니다.
이 상태에서 이미지 필터를 적용하면 주변 검은색이 피사체의 경계에 영향을 줄 수 있습니다.
따라서 이번에는 OpenCV의 inpaint()를 사용해 투명 영역의 RGB 값을 주변 색상으로 보완했습니다.
파일 위치: src/abstraction/improve_abstraction.py
missing = np.where(
crop_alpha == 0,
255,
0
).astype(np.uint8)
filled = cv2.inpaint(
crop,
missing,
3,
cv2.INPAINT_TELEA
)
inpaint()는 지정한 영역을 주변 픽셀 정보를 이용해 채우는 기능입니다.
이번에는 투명 영역의 검은 RGB 값이 필터 계산에 직접 영향을 주는 문제를 줄이는 데 활용했습니다.
다만 이미지 경계나 복잡한 객체 주변에서는 완벽하게 처리되지 않을 수 있습니다.
6. D·E·F 개선 실험
두 번째 실험에서는 세 가지 결과물을 생성했습니다.
실험적용 기술목적
| 실험 | 적용 기술 | 목적 |
| D | LAB + CLAHE + 감마 보정 | 밝기 개선 |
| E | D + Bilateral + K-Means 12색 | 구조 보존과 색상 단순화 |
| F | E + 색상 팔레트 변환 | 일러스트 색감 적용 |
D - 밝기 보정
첫 번째 결과는 색상 수를 줄이지 않고 밝기만 보정한 이미지입니다.
기존 A보다 정자의 기둥과 난간이 밝아졌으며 붉은색과 청록색을 더 쉽게 구분할 수 있었습니다.
지붕의 세밀한 기와 무늬도 비교적 잘 유지되었습니다.
다만 사진의 디테일이 그대로 남아 있어 추상화보다는 밝기를 개선한 이미지에 가까웠습니다.
E - 밝기 보정 + 12색
이번 실험에서 가장 중요하게 살펴본 결과입니다.
D에 Bilateral Filter와 K-Means를 적용해 색상을 12개로 줄였습니다.
기존 A와 같은 색상 수를 사용하지만 밝기 보정을 먼저 적용했다는 차이가 있습니다.
그 결과 A보다 기둥과 난간의 색상이 명확해졌고 건축물의 구조도 비교적 잘 보존되었습니다.
F - 색상 팔레트 적용
마지막으로 미리 정의한 색상 팔레트를 적용했습니다.
지붕에 사용할 회색 계열, 기둥에 사용할 붉은 갈색, 단청에 사용할 청록색 등을 준비했습니다.
파일 위치: src/abstraction/improve_abstraction.py
PALETTE = [
"#35444B",
"#64747A",
"#9AA5A0",
"#D4C9BA",
"#F1E7D6",
"#7E433D",
"#A75B4D",
"#D78B6F",
"#305E62",
"#4F8986",
"#86B4A7",
"#B1ADA1",
]
K-Means로 계산한 대표 색상을 LAB 색 공간에서 가장 가까운 팔레트 색상으로 변경했습니다.
전체적인 색감은 통일되었지만, 원본에서 중요한 붉은 기둥과 단청의 색상이 일부 약해지는 문제가 발생했습니다.
이는 팔레트 매핑이 이미지의 구조를 이해하는 방식이 아니라 색상 유사도를 기준으로 동작하기 때문입니다.
7. A~F 전체 결과 비교
여섯 가지 실험 결과를 하나의 이미지로 만들어 비교했습니다.
비교 이미지 생성에는 Pillow를 사용했습니다.
파일 위치: src/abstraction/compare_results.py
from PIL import Image, ImageOps
image = Image.open(path).convert("RGBA")
background = Image.new(
"RGBA",
image.size,
(255, 255, 255, 255)
)
background.alpha_composite(image)
preview = ImageOps.contain(
background.convert("RGB"),
(360, 480)
)
투명한 배경을 흰색으로 표시해 실제 객체의 어두운 색상과 투명 영역을 구분할 수 있도록 했습니다.

실제 결과를 비교한 내용은 다음과 같습니다.
| 실험 | 장점 | 단점 |
| A | 정자 형태 보존 | 전체적으로 어두움 |
| B | 색상 수 감소 | 기둥 색상 손실 |
| C | 가장 단순한 형태 | 구조적 특징 손실 |
| D | 밝기와 색상 표현 개선 | 세부 질감이 많이 남음 |
| E | 형태와 색상 단순화의 균형 | 여전히 사진에 가까움 |
| F | 통일된 색상 팔레트 | 원본의 특징적인 색상 일부 손실 |
가장 좋은 결과는?
이번 실험에서는 E가 가장 균형 잡힌 결과라고 판단했습니다.
D는 원본 형태와 색상을 가장 잘 보존했지만, 디테일이 많이 남았습니다.
F는 색감이 통일되면서 일러스트처럼 보이는 부분도 있었지만, 정자의 붉은 기둥과 단청을 충분히 표현하지 못했습니다.
반면 E는 밝기 보정과 색상 단순화를 적용하면서 건축물의 특징을 비교적 잘 유지했습니다.
따라서 앞으로의 실험에서는 다음과 같이 결과물을 활용하기로 했습니다.
- D: 밝기 보정 기준 이미지
- E: OpenCV 추상화 기준 이미지
- F: 색상 팔레트 실험 참고 이미지
8. OpenCV 이미지 추상화의 한계
총 6가지 실험을 진행하면서 중요한 사실을 확인했습니다.
색상을 단순화하는 것과 일러스트로 재구성하는 것은 서로 다른 문제입니다.
예를 들어 기와지붕을 추상화한다고 생각해 보겠습니다.
우리가 원하는 것은 실제 기왓장 하나하나를 단순한 색으로 표현하는 것이 아닙니다.
기와지붕의 전체적인 곡선과 형태를 유지하면서 내부를 몇 개의 색면으로 재구성하는 것입니다.
하지만 K-Means는 색상이 비슷한 픽셀을 묶을 뿐, 기와지붕이 어떤 구조인지 이해하지 못합니다.
따라서 색상 수를 아무리 줄이더라도 세밀한 패턴이 그대로 남거나 중요한 색상이 사라질 수 있습니다.
이번 실험을 통해 확인한 OpenCV 방식의 장점과 한계는 다음과 같습니다.
| 구분 | 내용 |
| 장점 | 가볍고 처리 속도가 빠름 |
| 장점 | 색상 수와 밝기를 직접 제어할 수 있음 |
| 장점 | 모바일 환경으로 이전하기 비교적 쉬움 |
| 한계 | 객체의 의미를 이해하지 못함 |
| 한계 | 구조별 색상 보존이 어려움 |
| 한계 | 목표 스타일에 맞는 형태 재구성이 어려움 |
이미지 품질을 우선으로 하는 현재 프로젝트에서는 OpenCV만으로 최종 목표에 도달하기에는 한계가 있다고 판단했습니다.
9. 다음 개발 방향
지금까지 다음과 같은 작업을 완료했습니다.
여행 사진
↓
SAM 2 객체 분할
↓
피사체 추출
↓
OpenCV 색상 단순화
↓
LAB + CLAHE 밝기 보정
↓
K-Means 색상 양자화
↓
색상 팔레트 적용
↓
A~F 결과 비교
다음 단계에서는 공개 이미지 생성 모델을 활용해 이미지 추상화를 진행할 예정입니다.
현재 검토하고 있는 기술은 다음과 같습니다.
Stable Diffusion
이미지를 생성하거나 기존 이미지를 새로운 스타일로 변환하는 데 활용할 수 있습니다.
ControlNet
원본 이미지의 윤곽선이나 구조적 정보를 활용해 생성 결과의 구도를 제어하는 데 사용할 수 있습니다.
LoRA
원하는 이미지 스타일을 모델에 학습시키기 위한 파인튜닝 방식입니다.
우선 RTX 5070 Ti에서 공개 모델을 실행하고, 전통 정자 사진을 목표 일러스트 스타일로 변환할 수 있는지 확인하려고 합니다.
이후 결과가 충분히 좋다면 직접 학습 데이터셋을 구축하고 모델을 파인튜닝하는 과정을 진행할 계획입니다.
마치며
이번에는 SAM 2로 추출한 이미지를 OpenCV로 추상화하면서 총 6가지 실험을 진행했습니다.
처음에는 단순히 색상 수를 줄이면 원하는 이미지에 가까워질 것으로 생각했습니다.
하지만 실제 결과는 예상과 달랐습니다.
색상 수를 줄일수록 오히려 정자의 특징이 사라지기도 했고, 밝기 보정과 팔레트 변환을 적용하면서 새로운 문제도 발견했습니다.
이 과정에서 가장 중요하게 느낀 점은 이미지의 정보를 줄이는 것과 이미지의 특징을 보존하는 것은 별개의 문제라는 것입니다.
현재까지는 E 결과가 가장 적합했지만, 최종적으로 만들고 싶은 여행 일러스트와는 여전히 차이가 있습니다.
다음에는 생성형 AI 모델을 활용해 사진의 구조를 유지하면서 단순한 색면 중심의 일러스트로 변환하는 작업을 진행해 보겠습니다.
참고 자료
- OpenCV 공식 문서 — Image Filtering
- OpenCV 공식 문서 — K-Means Clustering
- OpenCV 공식 문서 — Histogram Equalization 및 CLAHE
- NumPy 공식 문서
- Pillow 공식 문서
- Meta SAM 2 공식 GitHub
'개발 & IT > 머신러닝 & 딥러닝' 카테고리의 다른 글
| SAM 2.1 이미지 분할 실습: RTX 5070 Ti로 객체 추출하고 오류 해결하기 (0) | 2026.10.28 |
|---|---|
| RTX 5070 Ti로 시작하는 AI 이미지 모델 개발 - Conda·PyTorch 환경 구축과 이미지 전처리 (0) | 2026.10.28 |
| AI 코딩 에이전트가 CUDA를 10시간 만에 대체했다? 실제 성과와 과장된 해석 (0) | 2026.08.06 |
| Kimi K3 내 컴퓨터에서 돌릴 수 있을까? 현실적인 사용 방법 (0) | 2026.07.29 |
| Jetson Orin Nano 가장 쉽게 시작하는 법: 점퍼·USB 없이 SD카드 하나로 끝내는 설치법 (0) | 2026.01.16 |




































