- Published on
CaptureD2L 프로젝트 소개
- Authors
- Name
- 신주용
CaptureD2L (Dark mode image to Light)
프로젝트 시작 동기
농담이긴 하지만 실제로 많은 사람들이 다크 모드를 사용합니다. 2019년 트위터에서 한 설문에 약 200여 명이 응답을 했는데, 그 중 82.7%가 다크 모드를 사용 중이라고 답했습니다1. 저 또한 다크 모드를 사용 중이고요.
때때로 발표 자료를 준비하다 보면 소스코드의 일부나 터미널 실행 화면을 캡쳐해서 첨부할 때가 있습니다. 이럴 때 다크 모드로 캡쳐한 이미지를 그대로 사용하면 개인적으로는 어두운 이미지의 비중이 너무 커 보이기도 하고 조금 어색한 느낌이 들었습니다.
이런 경우에 소스 코드를 캡쳐한 경우라면 살짝 귀찮긴 하지만 IDE를 열고, 설정 메뉴에 들어가서, 라이트 모드로 바꾸고, 캡쳐를 다시 하면 됩니다. 그러나 만약 CLI로 실행한 프로그램의 터미널 화면을 캡쳐한 경우라면, 그 경우가 심지어 에러 상황을 캡쳐한 것이라면 어떨까요? 터미널 테마를 라이트 모드로 바꾸고, 프로그램을 다시 실행해서 똑같은 에러를 다시 발생시킨다는 것은 쉽지 않을 것입니다.
그래서 "다크 모드로 캡쳐한 이미지를 라이트 모드 이미지처럼 바꿀 수 있으면 좋겠다!" 라는 생각을 하게 되었고, 이 프로젝트를 시작했습니다.
(사실, 소스코드는 이렇게 발표 자료에 포함하면 색깔보다는 어차피 글씨가 너무 작아 잘 안 보이므로 보통은 이렇게 넣는 경우는 많지는 않습니다만... 위에서 말한 내용은 그럴듯한 이유를 만들어 낸 것이고 본심은 그냥 해보고 싶었습니다ㅎ 그런게 토이 프로젝트잖아요? 😏)
과정
1차 시도
첫 번째 생각은 너무나 당연하고 단순하게 '색 반전'을 시키는 것이었습니다. 우리는 일반적으로 흰 종이에 검은 글씨를 쓰지만 다크 모드 캡쳐 이미지는 배경이 검고 글이 밝으니 색깔을 반전시키면 되겠죠.
image_bgra = cv2.imread(input_path, cv2.IMREAD_UNCHANGED)
image_rgba = cv2.cvtColor(image_bgra, cv2.COLOR_BGRA2RGBA)
image_rgba_invert = image_rgba.copy()
image_rgba_invert = 255 - image_rgba_invert
image_rgba_invert = cv2.cvtColor(image_rgba_invert, cv2.COLOR_RGBA2BGR)
plt.imshow(image_rgba_invert)
음. 꽤 잘 되는 것 같네요. 그러면 맥에서 많이 사용하는 iTerms 터미널을 캡쳐한 이미지를 반전시키면 어떻게 될까요?
... 이런, 제가 기대한 것은 이런 이미지가 아니었습니다. 색깔이 이상하게 변했고, 왼쪽 상단의 맥 버튼 색깔도 다 달라졌습니다.
이렇게 된 이유는 우리가 일반적으로 사용하는 이미지가 빛의 삼원색인 RGB 색 모형을 바탕으로 이루어져 있기 때문입니다. 이 색 모형에서 색깔은 R(Red, 빨간색), G(Green, 초록색), B(Blue, 파란색)의 조합으로, 밝기는 각 색깔의 양을 조합하여 나타냅니다. 예를 들어 검은색은 빛이 없는 상태이므로 RGB가 (0,0,0)인 상태이고, 흰색은 모든 빛을 켠 상태와 같으므로 (255,255,255)로 표현됩니다. 그래서 iTerm 터미널 캡쳐 이미지를 색 반전한 경우에 흰색이었던 글씨는 검은색으로 바뀐 것입니다.
하지만, 밝기가 아닌 색의 반전에서 문제가 발생합니다. RGB 삼원색 이미지에서 빨간색의 반대편엔 청록색(Cyan)이 있고, 초록색의 반대는 자주색(Magenta), 파란색의 반대는 노란색(Yellow)으로 나타납니다. 그래서 iTerm 터미널 왼쪽 상단의 빨, 노, 초 버튼은 각각 청록, 파랑, 자주색으로 바뀐 것입니다.
저는 색깔 정보는 그대로 유지한 채 밝기만 반전시킨 것을 원했습니다.
2차 시도
색을 바꾸려면 꼭 빨간색, 초록색, 파란색만 조절해야 될까요? 색을 표현하는 방법은 우리가 가장 익숙한 것이 RGB일 뿐, 인쇄에서 많이 사용되는 CMYK도 있고, 영상에서 자주 사용되는 YUV도 있습니다. 저는 HSL 색 모형을 사용했습니다.
HSL 색 모형은 색을 Hue(색조), Saturation(채도), Lightness(밝기)를 사용해 나타내는 방법입니다. 순서는 상관없이 HLS라고 표현하는 경우도 있고(위키 백과는 HSL로 표기했으나 OpenCV는 HLS로 사용합니다) Lightness 대신 Brightness를 사용해서 HSB로 표현하는 경우도 있습니다.
뭐, 이름은 상관 없습니다. 중요한건 RGB와는 달리 밝기 채널이 구분된다는 것입니다. 캡쳐한 터미널 이미지를 HSL으로 표현한 후, 밝기 채널을 반전시키면 되지 않을까요?
image_hls = cv2.cvtColor(image_bgra, cv2.COLOR_BGR2HLS)
h, l, s = cv2.split(image_hls)
l_new = 255 - l
image_hls_new = cv2.merge([h, l_new, s])
plt.imshow(image_hls_new)
원하던 결과물이 나왔습니다! 다크 모드로 캡쳐한 터미널 이미지의 검은 배경은 하얗게, 하얗던 글씨는 검게 변했습니다. 게다가 이번엔 왼쪽 위 버튼을 보면 색깔 정보도 그대로 유지됐습니다. HSL에서는 색깔 정보는 건드리지 않고 밝기만 반전시킬 수 있기 때문입니다.
결과
image_bgra_new = cv2.cvtColor(cv2.cvtColor(image_hls_new, cv2.COLOR_HLS2BGR), cv2.COLOR_BGR2BGRA)
out_path = input_path.replace('.png', '_d2l.png')
cv2.imwrite(out_path, image_bgra_new)
다시 PPT 예시로 돌아와서 변환한 이미지로 바꿔보았습니다. 저는 개인적으로는 이 버전이 조금 더 나은 것 같습니다. 다크 모드 이미지를 사용했을 때는 이미지의 비중이 너무 큰 느낌이었는데, 지금은 조금 더 안정적인 느낌이랄까요..? 개인적인 느낌이니 동의하지 않으시는 분도 있으시겠지만, 앞서 말씀드렸듯 이런 이유가 크게 중요한건 아닙니다. (그냥 해보고 싶었어요..허헣..😏😏)
프로젝트의 구현은 간단하게 설명하자면 프론트는 React4를 사용해 이미지를 업로드하는 페이지를 만들었고, FastAPI5로 REST API서버를 구현해 이미지를 받아 OpenCV6로 처리하고, 반환해주는 단순한 구조입니다. 앞으로 몇 개의 추가 게시글을 통해 더 자세한 과정을 공유하려고 합니다.
긴 글 읽어주셔서 감사합니다.
여담
- 여기에서는 HSL을 사용했지만, 밝기 채널이 존재하는 YUV나 Lab에서도 충분히 비슷하게 동작하지 않을까 싶습니다.
- 마지막 슬라이드에서 오히려 글이 잘 안 보이는 느낌도 살짝 있습니다. 다크 모드가 사실 완전한 다크가 아니어서(...!!) 생기는 문제로 생각됩니다. 그래서 현재 버전에서는 간단하게 Gamma를 사용하여 밝기를 살짝 높이도록 구현했는데, 다음 업데이트에서 감마 대신 대비(Contrast)를 바꿀 수 있도록 기능 추가를 해보려고 합니다. 이에 대해서도 게시글로 자세히 풀어볼 예정입니다.
- 혹시 이 글을 읽으실 JS 전문가 분들이 계신다면 위와 같이 색 모형을 변경해 이미지 처리를 할 수 있는 라이브러리에 대해 아시는 것을 공유해주시면 감사드리겠습니다!
Footnotes
Thomas Steiner. "Let there be darkness! 🌚 Maybe…" medium.com. https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d (accessed Feb. 5, 2023). ↩
"RGB." ko.wikipedia.org. https://ko.wikipedia.org/wiki/RGB (accessed Feb. 5, 2023). ↩
"HSL and HSV." en.wikipedia.org. https://en.wikipedia.org/wiki/HSL_and_HSV (accessed Feb. 11, 2023). ↩
"React." ko.reactjs.org. https://ko.reactjs.org/ (accessed Feb. 11, 2023). ↩
"FastAPI." fastapi.tiangolo.com. https://fastapi.tiangolo.com/ (accessed Feb. 11, 2023). ↩
"OpenCV." opencv.org. https://opencv.org/ (accessed Feb. 11, 2023). ↩