DH's Blog
Scalable Vector Graphics (SVG)
Dev/Theory

Scalable Vector Graphics (SVG)

date
Mar 17, 2026
slug
svg
author
status
Public
tags
SVG
Scalable Vector Graphics
PNG2SVG
python
summary
SVG 파일에 대한 설명.
type
Post
thumbnail
updatedAt
Mar 20, 2026 06:40 AM
category
Dev/Theory

SVG 파일의 기본 개념

  • SVG(Scalable Vector Graphics)는 XML(eXtensible Markup Language) 기반의 파일 형식으로, 이미지의 형태(선, 도형, 텍스트 등)를 수학적 좌표와 벡터 정보로 정의하는 파일 확장자.
    • 파일 확장자 : .svg
    • MIME 타입 : image/svg+xml

SVG 파일의 주요 특징

항목
설명
확대/축소 가능
벡터 기반이기 때문에 아무리 확대해도 깨지지 않음.
텍스트 기반
XML 문법을 사용하므로 사람이 읽고 편집이 가능함.
경량성
같은 그래픽이라도 PNG, JPG보다 파일 크기가 작음.
대화형/애니메이션 지원
CSS, JavaScript와 함께 사용하면 동적 그래픽, 인터랙션 구현 가능.
웹 호환성
대부분의 브라우저에서 바로 렌더링 가능. (HTML <svg> 태그로 포함 가능)

구조 예시

  • 원(circle)과 텍스트를 포함한 간단한. SVG 구조
    • <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"> <circle cx="100" cy="100" r="80" fill="skyblue" stroke="black" stroke-width="2"/> <text x="100" y="105" text-anchor="middle" fill="black" font-size="20">SVG</text> </svg>
    • <circle> : 원의 중심(cx , cy ), 반지름(r ), 색상(fill ) 정의
    • <text> : 텍스트 위치, 색상, 크기 설정

SVG vs PNG/JPG

비교 항목
SVG
PNG/JPG
이미지 유형
벡터
비트맵
해상도 독립성
확대 축소에 영향 없음
확대 시 픽셀 깨짐
파일 크기
작음 (복잡도에 따라 다름)
상대적으로 큼
편집
텍스트 편집 가능
이미지 편집 툴 필요
용도
로고, 아이콘, 그래프, 일러스트
사진, 스캔 이미지 등

FreeConvert PNG to SVG

notion image
notion image

Color Mode

  • 설명: 출력 SVG가 컬러 이미지일지, 흑백 이미지일지를 선택.
  • 옵션:
    • Color → 원본의 RGB 값을 그대로 반영. (다채로운 SVG 결과)
    • Black and White → 색상 정보를 제거하고 명암 기반으로 윤곽만 표현.
  • 사용 예시:
    • 로고, 심볼 → Color
    • 마스크, 단일 객체 검출 → Black and White

Filter Speckle

  • 설명: 매우 작은 노이즈(점, 먼지)를 제거하는 기능.
  • 의미: X 픽셀보다 작은 영역(패치)은 무시하고 삭제함.
  • 예시:
    • 값 = 1 → 크기 1px 이하의 노이즈 제거
    • 값 = 5 → 크기 5px 이하의 조그만 점들 제거
  • 활용:
    • 배경 노이즈나 JPEG 압축 잔여물 제거 시 유용함.

Curve Fitting

  • 설명: 직선으로 구성된 경로를 곡선(Bézier curve) 으로 근사하는 방법을 선택함.
  • 의미: 변환된 SVG가 얼마나 “부드러운 곡선” 형태로 표현될지를 결정함.
  • 옵션 예시:
    • None → 모든 윤곽을 직선(Polygon)으로 유지.
    • Quadratic / Cubic → 2차, 3차 Bézier 곡선으로 매끄럽게 표현.
  • 추천:
    • 로고, 도형 → Cubic
    • 픽셀 기반 마스크 → None (직선 유지)

Corner Threshold

  • 설명: “코너(모서리)”로 판단할 각도 임계값
  • 단위: 도(°)
  • 작동 방식: 두 선분 사이의 각도가 이 값보다 작으면 “코너”로 인식함.
  • 예시:
    • 값 = 60 → 60도 이하의 급격한 굴곡만 코너로 유지
    • 값 = 30 → 더 예민하게 코너 감지 (각진 형태 유지)
  • 활용:
    • 자연스러운 곡선 → 큰 값 (60~90)
    • 각진 도형 유지 → 작은 값 (20~40)

Segment Length

  • 설명: 곡선을 세분화(subdivide)할 때 한 세그먼트의 최대 길이(px).
  • 작동 원리: 이 길이보다 긴 구간은 자동으로 더 잘게 나누어 곡선 근사
  • 예시:
    • 값 = 4 → 짧은 곡선 세분화, 부드럽지만 데이터 많음.
    • 값 = 10 → 단순하지만 직선화 경향.
  • 추천:
    • 디테일 중요 → 3~5
    • 파일 크기 최소화 → 8~12

Splice Threshold

  • 설명: 두 곡선 사이의 방향 차이가 이 값(도 단위) 이상이면 별도의 곡선으로 분리함.
  • 예시:
    • 값 = 45 → 45도 이상 꺾이면 새 곡선으로 시작.
    • 값 = 90 → 큰 굴곡만 새로운 경로로 인식.
  • 활용:
    • 곡선이 너무 매끄럽게 연결되는 걸 방지.
    • 각진 디자인을 더 잘 유지.

Code

import cv2 import numpy as np from PIL import Image from autotrace import Bitmap, VectorFormat def preprocess_tone_preserve(input_path: str, output_path: str, k_levels=6): """ Step 1~4: 명암 유지 + 노이즈 억제 기반 전처리 """ print(f"🔹 [INFO] 명암 유지형 전처리 시작: {input_path}") img = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE) if img is None: raise FileNotFoundError("입력 이미지를 찾을 수 없습니다.") # ① CLAHE: 명암 대비 강화 clahe = cv2.createCLAHE(clipLimit=2.5, tileGridSize=(8, 8)) img_eq = clahe.apply(img) # ② Bilateral Filter: 고주파 노이즈(석회화 등) 억제 img_smooth = cv2.bilateralFilter(img_eq, d=9, sigmaColor=75, sigmaSpace=75) # ③ Quantization: 명암 단계를 K개로 줄여 톤 유지 Z = img_smooth.reshape((-1, 1)).astype(np.float32) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0) _, labels, centers = cv2.kmeans(Z, k_levels, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) centers = np.uint8(centers) quantized = centers[labels.flatten()].reshape(img.shape) # ④ Median Blur로 경계 부드럽게 smooth_tone = cv2.medianBlur(quantized, 3) cv2.imwrite(output_path, smooth_tone) print(f"[INFO] 명암 유지형 전처리 완료: {output_path}") return output_path def vectorize_with_autotrace(input_path: str, output_svg: str): """ Step 5: PyAutoTrace로 SVG 벡터화 """ print(f"🔹 [INFO] AutoTrace 기반 벡터화 시작: {input_path}") img = Image.open(input_path).convert("RGB") bmp = Bitmap(np.asarray(img)) vector = bmp.trace() vector.save(output_svg) print(f"[INFO] SVG 생성 완료: {output_svg}") def mammogram_tone_preserve_svg(input_png: str, output_svg: str, k_levels=6): temp_file = "temp_tone.png" preprocessed = preprocess_tone_preserve(input_png, temp_file, k_levels) vectorize_with_autotrace(preprocessed, output_svg) import os if os.path.exists(temp_file): os.remove(temp_file) print("전체 변환 파이프라인 완료.") if __name__ == "__main__": mammogram_tone_preserve_svg("mammo_input.png", "mammo_tone_output.svg", k_levels=6)
  • 더 세밀하게?
import cv2 import numpy as np from PIL import Image from autotrace import Bitmap def preprocess_texture_preserve(input_path: str, output_path: str, tone_levels=20): print(f"🔹 [INFO] Texture-preserving preprocessing: {input_path}") img = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE) if img is None: raise FileNotFoundError("입력 이미지를 찾을 수 없습니다.") # ① CLAHE로 국소 대비 향상 clahe = cv2.createCLAHE(clipLimit=2.5, tileGridSize=(8, 8)) img_clahe = clahe.apply(img) # ② Bilateral + Guided 혼합 (calcification 억제 + 조직 결 유지) bilateral = cv2.bilateralFilter(img_clahe, d=9, sigmaColor=50, sigmaSpace=50) guided = cv2.ximgproc.guidedFilter(guide=bilateral, src=img_clahe, radius=5, eps=1e-2) img_smooth = cv2.addWeighted(bilateral, 0.6, guided, 0.4, 0) # ③ Unsharp mask (실질 경계 복원) blur = cv2.GaussianBlur(img_smooth, (9, 9), 10.0) img_sharp = cv2.addWeighted(img_smooth, 1.5, blur, -0.5, 0) # ④ Soft quantization (세밀 명암 단계 유지) img_norm = cv2.normalize(img_sharp, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) tone_bins = np.linspace(0, 255, tone_levels).astype(np.uint8) quantized = np.zeros_like(img_norm) for i in range(1, len(tone_bins)): mask = (img_norm >= tone_bins[i - 1]) & (img_norm < tone_bins[i]) quantized[mask] = int((tone_bins[i] + tone_bins[i - 1]) / 2) # ⑤ 약한 Gaussian smoothing으로 tone blending blended = cv2.GaussianBlur(quantized, (3, 3), sigmaX=0.5) cv2.imwrite(output_path, blended) print(f"[INFO] Texture-preserving preprocessed image saved: {output_path}") return output_path def vectorize_autotrace(input_path: str, output_svg: str): print("[INFO] Vectorizing via PyAutoTrace...") img = Image.open(input_path).convert("RGB") bmp = Bitmap(np.asarray(img)) vector = bmp.trace() vector.save(output_svg) print(f"[INFO] SVG 파일 생성 완료: {output_svg}") def mammogram_svg_pipeline(input_png: str, output_svg: str, tone_levels=20): temp_file = "temp_texture.png" preprocessed = preprocess_texture_preserve(input_png, temp_file, tone_levels) vectorize_autotrace(preprocessed, output_svg) import os if os.path.exists(temp_file): os.remove(temp_file) print([DONE] Texture-preserving SVG 변환 완료.") if __name__ == "__main__": mammogram_svg_pipeline("mammo_input.png", "mammo_texture_preserve.svg", tone_levels=24)
 
 
 
 

Comments (0)