오늘은 Colorization의 대표 Network인 Let there be color! 에 대해 리뷰해보려고 합니다. 역시 잘못된 부분은 댓글로 알려주시면 감사하겠습니다! 👀 개인적으로 재밌게 읽었던 논문입니다 ㅎㅎ
우선 Colorization이란, 간단히 말하면 흑백 사진을 칼라 사진으로 딥러닝을 통해 바꾸는 방법론을 뜻합니다. 아래 예시와 같이 말이죠.
이러한 Colorization에 대한 연구는 꽤 오래전부터 이루어져왔는데요, 크게 User가 개입해 힌트를 주는경우와 reference image를 참고하는 경우, 그리고 흑백 이미지만 넣으면 자동으로 색을 입혀주는 총 3가지의 방법론으로 나뉩니다. 특히 요즘은 transfomer의 발전과 더불어 흑백사진만 넣으면 user의 개입 없이 automatic 하게 색을 입혀주는 알고리즘의 연구가 활발히 이루어지고 있습니다.
따라서 오늘 리뷰할 Let there be color! (2016, Iizuka) 논문 또한 automatic 한 방법론 중 하나입니다. 그럼 어떻게 흑백사진 (grayscale image) 만 넣었는데 칼라 이미지를 출력할 수 있는걸까요? 이제 모델 아키텍처를 하나하나 짚어보겠습니다.
Overall Architecture
우선 전반적인 모델 아키텍처는 위와 같습니다. 언뜻 보면 복잡해보이기도 하지만, 매우 흥미로운 구조를 가지고 있습니다. 우선 가장 눈에 띄는 것은 크게 두가지 방향으로 input이 들어오고 마지막에 fusion layer를 거친다는 점인데요, 특히 Colnet(2016) 은 다음과 같이 크게 4가지 부분으로 나눌 수 있습니다.
1. Low-level features network
2. Mid-level features network
3. Global features network
4. Colorization network
결론부터 요약하자면, Colnet은 Low-level features network에서 나온 outputs을 각각 Mid-level, Global features network에 input으로 넣습니다. 그리고 각각 Mid-level, Global features network에서 출력한 output을 fusion layer를 통해 결합한 후, Colorization network에 넣어 색상 map을 출력하게 하는 구조인 것입니다 ! 이러한 두가지 input을 수용하는 구조는 Global image와 Local image features 를 동시에 고려할 수 있다는 장점이 있다고 합니다.
그럼 각각 Network의 역할과 구조를 좀 더 세부적으로 알아보겠습니다.
Low-level features network
맨 처음 흑백(grayscale) 이미지는 scaling 후 처음으로 Low-level features network를 거치게 됩니다. 앞서 말씀드린대로 이 low-level features 는 각각 mid net과 global net에 각각 input으로 부여되기 때문에, 애초에 두갈래로 나뉘어 6개의 Conv layer가 구성됩니다. 이 Low-level network는 Local image features를 추출하는 역할을 합니다.
주의해야할 점은 이 두 갈래로 이루어진 Conv layer들은 convolution filter bank를 공유한다는 것입니다. (그림에서는 Shared weights로 표시되어 있죠) 또한 흔히 conv에서 사용하는 pooling 대신 stride를 증가시키는 기법을 사용하였습니다. Code로 보면 다음과 같이 구현될 수 있겠네요.
class LowLevelFeatures(nn.Module):
def __init__(self):
super(LowLevelFeatures, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=64,stride=2, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, stride=1, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(in_channels=128, out_channels=128, stride=2, kernel_size=3, padding=1)
self.conv4 = nn.Conv2d(in_channels=128, out_channels=256, stride=1, kernel_size=3, padding=1)
self.conv5 = nn.Conv2d(in_channels=256, out_channels=256, stride=2, kernel_size=3, padding=1)
self.conv6 = nn.Conv2d(in_channels=256, out_channels=512, stride=1, kernel_size=3, padding=1)
def forward(self, x):
out = F.relu(self.conv1(x))
out = F.relu(self.conv2(out))
out = F.relu(self.conv3(out))
out = F.relu(self.conv4(out))
out = F.relu(self.conv5(out))
out = F.relu(self.conv6(out))
return out
Mid-Level features network
다음은 Mid-level features network 입니다. Mid level features network는 두개의 Conv layer로 구성되어있으며, 따라서 output은 low-level features의 scaled version이 됩니다. (low net과 mid net은 모두 단순 conv로 이루어진 layer이기 때문이죠)
code는 다음과 같이 구성될 수 있습니다.
class MidLevelFeatures(nn.Module):
def __init__(self):
super(MidLevelFeatures, self).__init__()
self.conv7 = nn.Conv2d(in_channels=512, out_channels=512, stride=1, kernel_size=3, padding=1)
self.conv8 = nn.Conv2d(in_channels=512, out_channels=256, stride=1, kernel_size=3, padding=1)
def forward(self, x):
out = F.relu(self.conv7(x))
out = F.relu(self.conv8(out))
return out
Global Image features network
사실 이 global features network가 이 Colnet의 가장 큰 특징이라고 말할 수 있을 것 같은데요, 이는 image의 global한 특징을 추출하는 역할을 합니다. Low level features를 4개의 Convolution과 3개의 Fully connected layer에 통과시키는데요, 이러한 global net의 output은 'Image priors'를 제공해준다고 본 고에서는 말합니다. 이 Image prior란 쉽게말해서 대충 그 이미지의 사전정보를 제공해주는 것을 뜻하는데요, 예를들면 이 이미지가 실내인지, 야외인지 등의 정보가 될 수 있겠죠?
단순히 이미지의 색상만 알아맞추는데, 이러한 이미지의 사전 정보가 주어지는 것이 얼마나 중요한지 아직 와닿지 않으실 수도 있을 것 같습니다. 하지만 다음 예시를 보시죠.
왼쪽 Ground truth는 원래 원본 사진이고, 오른쪽 baseline은 global feature를 포함시키지 않은 network입니다. 오른쪽을 자세히보시면, 위의 사진의 경우 물 부분에 황토색이 나타나있고, 아래 사진의 경우 천장 부분에 하늘처럼 하늘색이 나타나있죠? 위 사진의 경우 network가 물 부분을 실내라고 착각해 황토색이 나타난 것이고, 아래 사진의 경우 실내인데 야외라고 network가 착각해 하늘색이 나타난 것으로 보입니다. 따라서 이처럼 image에 대한 prior 정보는 생각보다 꽤 큰 영향을 미칩니다.
따라서 이 global network가 최대한 이미지에 대한 판단을 정확히 내리게 하는 것이 중요하겠죠? 따라서 Colnet에는 image label을 target으로 분류하는 classification net이 추가로 존재합니다. Loss는 cross entropy를 사용하며, 최대한 global net의 잘못된 판단을 줄이기 위한 시도로 보입니다. 이러한 Cross entropy와 color간의 MSE를 합해 만들어진 Joint global loss는 다음과 같습니다.
MSE와 cross entropy가 합쳐진 식이 생성되었고, 여기서 알파는 cross entropy에 대한 weight를 뜻하겠죠? 논문에서 Default 는 1/300을 사용하고 있습니다. Global net의 code는 다음과 같습니다.
class GlobalFeatures(nn.Module):
def __init__(self):
super(GlobalFeatures, self).__init__()
self.conv1 = nn.Conv2d(in_channels=512, out_channels=512, stride=2, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(in_channels=512, out_channels=512, stride=1, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(in_channels=512, out_channels=512, stride=2, kernel_size=3, padding=1)
self.conv4 = nn.Conv2d(in_channels=512, out_channels=512, stride=1, kernel_size=3, padding=1)
self.fc1 = nn.Linear(7*7*512, 1024)
self.fc2 = nn.Linear(1024, 512)
self.fc3 = nn.Linear(512, 256)
def forward(self, x):
y = F.relu(self.conv1(x))
y = F.relu(self.conv2(y))
y = F.relu(self.conv3(y))
y = F.relu(self.conv4(y))
y = y.view(-1, 7*7*512)
y = F.relu(self.fc1(y))
y = F.relu(self.fc2(y))
out = y
classification_in = y
out = F.relu(self.fc3(out))
return out, classification_in
Colorization network
마지막으로는 Colorization network 입니다. 최종적으로 칼라 이미지를 출력하는 마지막 network라고 할 수 있는데요, 이 colorization network의 input으로는 앞서 말씀드린 mid net과 global net의 output을 Fusion 한 값이 들어옵니다.
Fusion layer는 다음과 같은데요, 생각보다 간단한 linear layer입니다. 이러한 Fusion 과정을 통해 local image feature와 global image features를 동시에 고려할 수 있게 된 것이죠.
그럼 이 network의 output은 어떤 것일까요?
Colorization network의 output은 CIE L*a*b* color space의 a*, b* 값입니다. CIE L*a*b*는 색상을 나타내는 map의 종류로, L*는 밝기, a* 와 b* 는 각각 색상의 방향을 나타냅니다. 따라서 Colorization network는 이 a*, b*의 component를 예측하는 것을 목적으로 하고있습니다. Sigmoid function 사용을 위해 a*, b* 값은 0과 1사이로 normalize 합니다. 또한, a*와 b* 같은 연속적인 값을 예측하는 것은 회귀 문제로 풀 수 있으므로, loss는 MSE를 사용합니다.
Results
굉장히 우수한 채색 성능을 보이고 있죠? 성능을 측정하는 metric는 따로 존재하지 않지만, 눈으로 보았을 때 꽤 준수한 성능으로 보입니다.
Limitations
하지만 한계 또한 존재합니다. 우선 data-driven task이기 때문에 train data와 유사한 image만 채색할 수 있다는 단점이 있습니다. 또한 '남성의 셔츠' 등의 어느 색이어도 가능한 모호한 색의 범주의 경우 train data에 단순히 의존해 색을 정할 것이므로, 이를 control 할 수 있는 별다른 방법은 존재하지 않는다는 것이 한계입니다. 다음 그림과 같이 말이죠.
지금까지 Let be colors! (2016)에 대해 리뷰해보았습니다 :)
References
[0] https://dl.acm.org/doi/pdf/10.1145/2897824.2925974
[1] https://github.com/kainoj/colnet - github codes
[2] https://sensing.konicaminolta.asia/what-is-cie-1976-lab-color-space/