지금까지 구상한 옷 색상 추천 알고리즘의 대략적인 프로세스는 아래와 같다.

  1. UI단에서 사용자의 선호 색상과 그날 입고 싶은 코디의 테마를 실은 요청을 받는다.
  2. 사용자의 선호 색상 및 최근 3일간의 데이터를 이용해 메인이 될 컬러를 선정한다.
  3. 사용자로부터 입력받은 테마에 따라 2에서 구한 메인 컬러에 톤(비비드 톤/모노 톤/딥톤/파스텔 톤)을 적용해 변환한다.
  4. 3에서 구한 '톤이 적용된 메인 컬러'와 잘 어울릴 만한 사이드 컬러를 ton-in-ton 방식과 ton-on-ton 방식으로 구해 추천해준다.

위 프로세스의 핵심이 되는 부분은 색상에 톤을 적용하는 3번과정과 메인 컬러와 잘 어울릴 만한 사이드 컬러를 추천해주는 4번 과정이다.

기본 색상에 톤 적용하기

모노(mono)톤

모노톤은 무채색을 말하는데, 이 무채색에 해당하는 색들이 RGB 공간좌표에서 그리는 자취는 직선 x=y=z가 된다. 즉 r,g,b 값이 모두 같으면 무채색이 되므로, 주어진 점으로부터 x=y=z 직선에 가장 가까운 점을 구하도록 알고리즘을 구상했다.
우리가 원하는 점을 벡터 v라 하고, 주어진 점(a,b,c)을 벡터 w라 하면, (v-w)와 (1,1,1)을 내적한 값이 0이 되어야 한다. 이 조건을 만족하는 벡터 v의 x,y,z 값은 (a+b+c)/3이다.

비비드(vivid)톤

비비드톤은 주어진 색상에서 채도와 명도를 최대로 높인 색으로 정의했다. 이 정의에 맞게 주어진 색상을 변환하는 함수를 RGB 공간좌표 상에서 나타내 보면 아래와 같다.

image.png

직선 x=y=z와 주어진 점을 지나는 평면은 무조건 아래 6개의 직선 중 하나와 만난다.

  1. x = 255, z = 0
  2. y = 255, z = 0
  3. x = 255, y = 0
  4. z = 255, y = 0
  5. y = 255, x = 0
  6. z = 255, x = 0

이 6개의 직선 중 하나와 만나는 접점이 바로 기존의 색상을 유지하면서 채도와 명도를 최대로 높인 점이다. 하지만 이러한 방식의 알고리즘은 계산과정이 상당히 번거롭고, 이 정도의 정확성이 필요하지도 않으므로 실제로는 조금 더 쉬운 방식을 선택했다. 바로 기존의 점에서 위 6개의 직선에 수선을 내린 다음, 그 수선의 길이가 최소가 되도록 하는 수선의 발을 고르는 것이다.

image.png

주어진 점의 x,y,z 값들에 대해 비교적 간단한 대소 비교를 통해서 위 6개 직선 중 어떤 직선에 수선을 내려야 그 길이가 최소가 될지 판단할 수 있고, 그 수선의 발을 구하는 방법 또한 상당히 간단하기 때문에 이러한 방식을 채택하기로 했다.

사이드 컬러 추천해주기

사이드 컬러를 추천하는 방법은 ton-in-ton 방식과 ton-on-ton 방식을 활용하기로 했고, 구현에 대한 대략적인 아이디어는 알고리즘 구상 part 2에서 설명한 바 있다. 그 아이디어를 코드로 작성한 후 의도한 대로 잘 작동하는지 간단한 실험을 해보았다.

ton in ton

kotlin playground에서 내가 작성한 함수를 실행시켜보았고, RGB 값이 (201, 170, 48)인 색상의 ton in ton 컬러 조합은 아래와 같이 나왔다.

image.png
(↑ 201, 170, 48)

image.png

음.. 잘 나오는것 같다.

ton on ton

마찬가지로 같은 색상을 가지고 톤온톤 배색조합을 리턴하는 함수를 실행시켜 본 결과이다.

image.png

이 역시 별 문제없이 의도하는 대로 잘 작동하는 것 같다.