본문 바로가기
DataScience/딥러닝

딥러닝 :: 이미지 처리 구현 _TIL#52

by 올커 2022. 11. 16.

■ JITHub 개발일지 52일차

□  TIL(Today I Learned) ::

딥러닝 : OpenCV, 이미지 처리 구현

 - 딥러닝 모델을 사용하여 이미지 처리를 구현해보았다. 아래에 있는 사진의 액자부분만 유화처리를 하여 두 번째 사진처럼 변경해보았다.

 - 작성했던 코드는 아래와 같다.

import cv2
import numpy as np


net = cv2.dnn.readNetFromTorch('models/eccv16/composition_vii.t7')
net2 = cv2.dnn.readNetFromTorch('models/eccv16/la_muse.t7')
net3 = cv2.dnn.readNetFromTorch('models/eccv16/starry_night.t7')

img = cv2.imread('imgs/hw.jpg')

# cv2.rectangle(img, pt1=(480, 145), pt2=(810, 365), color=(255,0,0), thickness=2)

cropped_img = img[145:366, 480:811]
h1, w1, c1 = cropped_img.shape


print(cropped_img.shape)

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)

# 후처리(1)
net.setInput(blob)
output = net.forward()
print(output.shape)
output = output.squeeze().transpose((1, 2, 0))
output += MEAN_VALUE

output = np.clip(output, 0, 255)
output = output.astype('uint8')


# 후처리(2)
net2.setInput(blob)
output2 = net2.forward()

output2 = output2.squeeze().transpose((1, 2, 0))
output2 += MEAN_VALUE

output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')


# 후처리(3)
net3.setInput(blob)
output3 = net3.forward()

output3 = output3.squeeze().transpose((1, 2, 0))
output3 += MEAN_VALUE

output3 = np.clip(output3, 0, 255)
output3 = output3.astype('uint8')

# 결과를 각각 슬라이싱하기
output = output[:, :111]
print(output.shape)
output2 = output2[:, 111:221]
print(output2.shape)
output3 = output3[:, 221:331]
print(output3.shape)



# 슬라이싱한 결과를 합치기
output4 = np.concatenate([output, output2, output3], axis=1)         # axis = 1 : x축 방향이 1
output4 = cv2.resize(output4, (w1, h1))

img[145:366, 480:811] = output4


cv2.imshow('img', img)
# cv2.imshow('output', output)
# cv2.imshow('output2', output2)
# cv2.imshow('output3', output3)
cv2.imshow('output4', output4)
cv2.waitKey(0)

 - 먼저 imgs 폴더에 변경할 이미지를, 그리고 models 폴더에 사용할 모델을 저장해두어야 한다.

 - 코드를 하나하나 살펴보면, 전체 코드에는 OpenCV2와 numpy를 활용하고 있다.

 - 그리고 net, net2, net3에는 각각 Torch를 활용하여 모델들을 가져오고 있다.

import cv2
import numpy as np


net = cv2.dnn.readNetFromTorch('models/eccv16/composition_vii.t7')
net2 = cv2.dnn.readNetFromTorch('models/eccv16/la_muse.t7')
net3 = cv2.dnn.readNetFromTorch('models/eccv16/starry_night.t7')

 - 변경할 이미지를 가져온다.

img = cv2.imread('imgs/hw.jpg')

 - 액자부분만 잘라서 효과를 적용해야 했다. 액자부분만 따올려면 액자부분에 대한 정확한 좌표값이 필요했다. 다른 좋은 방법들이 있을지는 모르겠지만, 아래처럼 rectangle을 사용해서 좌표값을 일일이 확인해가며 액자의 양 끄트머리의 좌표를 찾아내었고 cropped_img라는 변수에 잘라낸 이미지를 저장하였다.

# cv2.rectangle(img, pt1=(480, 145), pt2=(810, 365), color=(255,0,0), thickness=2)
cropped_img = img[145:366, 480:811]

 - 이미지 전처리를 위해 blobFromImage를 사용했다. 모델마다 최적의 MEAN_VALUE가 있다고 하는데, 사실은 아직 blobFromImage를 통해 어떻게 전처리 되는지 정확하게 이해하지 못하고 있다. 얼핏 MEAN_VALUE를 빼주고 나중에 output에서 더해준다고 이해하고 넘어갔다.

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)

 - 후처리는 아래와 같이 코드를 작성했다. setInput에 위에서 만들어준 blob을 넣고, output으로는 net.forward를 준다.

net.setInput(blob)
output = net.forward()

차원 변경을 위해 output.squeeze().transpose((1, 2, 0))을 해주는데, transpose를 해주면서 num(1), c(채널), h(높이), w(가로)로 되어있던 shape이 h(높이), w(가로), c(채널)로 변경된다. *여기서 num은 전처리를 해줄 때 붙고, 후처리를 해줄 때 알아서 사라지는데 정확히 이해하지 못한부분이다.

output = output.squeeze().transpose((1, 2, 0))

 - 위에서 빼주었다고 했던 MEAN_VALUE를 후처리때 여기서 다시 더해준다.

output += MEAN_VALUE

 - MEAN_VALUE를 더할 경우 RGB 색상 최대값 255를 넘어가는 경우를 방지하기 위하여 np.clip(output, 0, 255)를 사용하여 min_value = 0, max_value = 255로 제한해주었다.

 

output = np.clip(output, 0, 255)

 - 후처리 마지막으로 output을 사람이 볼 수 있는 이미지 형태(정수)로 변환하기 위하여 아래와 같이 코드를 더해주었다.

output = output.astype('uint8')

 - 마지막으로 기존 이미지에서 빼주었던 부분에 output을 넣어준다.

img[140:370, 480:810] = output

 - 비교를 위해 output을 그냥도 imshow로 출력하고, 최종 이미지 img도 출력해보았다.

cv2.imshow('output', output)
cv2.imshow('img', img)
cv2.waitKey(0)

 - 모두 잘 끝났다고 생각했는데, 여기서 에러가 난다. 에러 내용은 아래와 같았다.

ValueError: could not broadcast input array from shape (224,331,3) into shape (221,331,3)

 - 난 세로 shape을 손댄 적 없다고 생각했는데, 221에서 224로 늘어나서 output이 img에 못들어간다는 내용인 것으로 유추했다. 그래서 각 단계별로 print(img.shape), print(cropped_img.shape), print(output.shape)를 찍어보았는데, 아래 코드를 지나면서 shape이 변경됨을 확인했다.

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)

 - MEAN_VALUE를 빼주는데, 이게 이미지 size에 영향을 주는 것 같다. 구글링을 해보아도 정확하게 일치하는 상황은 찾기가 어려운 듯 하다. 하지만 dnn의 blobFromImage를 찾아보면 아래와 같은 구문을 쉽게 볼 수 있다. mean을 빼고, factor에 따라 다시 scaling한다는 내용인데, 연산작업이 정수로 딱 떨어지지 않거나, 반올림된다거나 하는 연산이 수행되는 것으로 추측되었다.

https://pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/

- 이 문제를 해결하기 위해서 최초 잘라냈던 이미지 shape을 가져와서 다시 resize하는 방법을 생각했다.

h1, w1, c1 = cropped_img.shape
...
output4 = cv2.resize(output4, (w1, h1))

img[145:366, 480:811] = output4

 - 위의 코드를 통해 정상적으로 이미지가 들어감을 확인하였다. 딥러닝 패키지들을 사용하게 되면서 각 모듈의 내부 소스코드를 이해하지 않는다면, 이런 오류를 맞닥뜨렸을 때 해결 방법이 모호한 문제들이 있다.(이전에 제네릭 함수들을 사용할 때에도 경계해야 하는 부분인데 유사하게 생각된다.) 자주 사용되는 모듈이라면 어떻게 구동되고있는지 잘 인지하고 사용해야 빠른 오류해결에 도움이 될 듯 싶다.

 

 

반응형

댓글