프로젝트 배경
보석 십자수(Diamond Painting)는 최근 많은 인기를 얻고 있는 취미입니다. 하지만 개인적인 사진(가족, 반려동물 등)을 보석 십자수로 만들고 싶어도 전문 서비스를 이용하면 비용이 많이 듭니다.
이 문제를 해결하기 위해, 누구나 무료로 자신만의 보석 십자수 도안을 만들 수 있는 웹 애플리케이션 Diastr를 개발했습니다.
기술 스택
- HTML5 Canvas API: 이미지 처리 및 픽셀 분석
- JavaScript: 클라이언트 사이드 이미지 처리
- Delta E 2000 알고리즘: 색상 매칭 시스템
- CSS3: 반응형 UI 디자인
핵심 기능 구현
1. 이미지 업로드 및 처리
사용자가 이미지를 업로드하면 Canvas API를 사용하여 이미지를 읽고 픽셀 데이터를 추출합니다.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 픽셀 데이터 처리
};
2. 색상 매칭 시스템 (Delta E 2000)
보석 십자수용 보석 색상 팔레트와 업로드된 이미지의 픽셀 색상을 비교하여 가장 유사한 보석 색상을 찾습니다.
Delta E 2000 알고리즘은 두 색상 간의 차이를 계산하는 가장 정확한 방법 중 하나입니다.
function deltaE2000(lab1, lab2) {
const L1 = lab1.L, a1 = lab1.a, b1 = lab1.b;
const L2 = lab2.L, a2 = lab2.a, b2 = lab2.b;
const kL = 1, kC = 1, kH = 1;
const C1 = Math.sqrt(a1 * a1 + b1 * b1);
const C2 = Math.sqrt(a2 * a2 + b2 * b2);
const Cab = (C1 + C2) / 2;
const G = 0.5 * (1 - Math.sqrt(Math.pow(Cab, 7) / (Math.pow(Cab, 7) + Math.pow(25, 7))));
const a1p = a1 * (1 + G);
const a2p = a2 * (1 + G);
const C1p = Math.sqrt(a1p * a1p + b1 * b1);
const C2p = Math.sqrt(a2p * a2p + b2 * b2);
const h1p = Math.atan2(b1, a1p) * 180 / Math.PI;
const h2p = Math.atan2(b2, a2p) * 180 / Math.PI;
const dLp = L2 - L1;
const dCp = C2p - C1p;
const dhp = calculateDhp(h1p, h2p);
const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin(dhp * Math.PI / 360);
const Lp = (L1 + L2) / 2;
const Cp = (C1p + C2p) / 2;
const Hp = calculateHp(h1p, h2p, Cp);
const T = 1 - 0.17 * Math.cos((Hp - 30) * Math.PI / 180)
+ 0.24 * Math.cos(2 * Hp * Math.PI / 180)
+ 0.32 * Math.cos((3 * Hp + 6) * Math.PI / 180)
- 0.20 * Math.cos((4 * Hp - 63) * Math.PI / 180);
const SL = 1 + (0.015 * Math.pow(Lp - 50, 2)) / Math.sqrt(20 + Math.pow(Lp - 50, 2));
const SC = 1 + 0.045 * Cp;
const SH = 1 + 0.015 * Cp * T;
const RT = -2 * Math.sqrt(Math.pow(Cp, 7) / (Math.pow(Cp, 7) + Math.pow(25, 7)))
* Math.sin(60 * Math.exp(-Math.pow((Hp - 275) / 25, 2)) * Math.PI / 180);
const dE = Math.sqrt(
Math.pow(dLp / (kL * SL), 2) +
Math.pow(dCp / (kC * SC), 2) +
Math.pow(dHp / (kH * SH), 2) +
RT * (dCp / (kC * SC)) * (dHp / (kH * SH))
);
return dE;
}
3. 도안 생성
색상 매칭 결과를 기반으로 보석 십자수 도안을 생성하고 사용자에게 다운로드 옵션을 제공합니다.
개발 과정에서의 도전 과제
색상 공간 변환
RGB 색상 공간에서 LAB 색상 공간으로 변환해야 Delta E 2000 알고리즘을 적용할 수 있습니다.
function rgbToLab(r, g, b) {
// RGB to XYZ
let rNorm = r / 255, gNorm = g / 255, bNorm = b / 255;
rNorm = rNorm > 0.04045 ? Math.pow((rNorm + 0.055) / 1.055, 2.4) : rNorm / 12.92;
gNorm = gNorm > 0.04045 ? Math.pow((gNorm + 0.055) / 1.055, 2.4) : gNorm / 12.92;
bNorm = bNorm > 0.04045 ? Math.pow((bNorm + 0.055) / 1.055, 2.4) : bNorm / 12.92;
const x = rNorm * 0.4124564 + gNorm * 0.3575761 + bNorm * 0.1804375;
const y = rNorm * 0.2126729 + gNorm * 0.7151522 + bNorm * 0.0721750;
const z = rNorm * 0.0193339 + gNorm * 0.1191920 + bNorm * 0.9503041;
// XYZ to LAB
const xRef = 95.047, yRef = 100.000, zRef = 108.883;
const fx = x / xRef > 0.008856 ? Math.pow(x / xRef, 1/3) : (7.787 * x / xRef) + 16/116;
const fy = y / yRef > 0.008856 ? Math.pow(y / yRef, 1/3) : (7.787 * y / yRef) + 16/116;
const fz = z / zRef > 0.008856 ? Math.pow(z / zRef, 1/3) : (7.787 * z / zRef) + 16/116;
const L = 116 * fy - 16;
const a = 500 * (fx - fy);
const b = 200 * (fy - fz);
return { L, a, b };
}
성능 최적화
대용량 이미지 처리 시 브라우저 성능 저하 문제가 있었습니다. 이를 해결하기 위해:
- 이미지 크기 제한 (최대 2000x2000 픽셀)
- Web Worker 사용하여 메인 스레드 차단 방지
- 색상 캐싱으로 중복 계산 제거
결론
Diastr를 통해 누구나 쉽게 자신만의 보석 십자수 도안을 만들 수 있게 되었습니다. Delta E 2000 알고리즘을 적용하여 정확한 색상 매칭을 구현했고, Canvas API를 활용하여 클라이언트 사이드에서 모든 처리를 수행합니다.
프로젝트는 https://diastr.dreamurl.biz/에서 사용해볼 수 있습니다.