이미지를 구글 클라우드 스토리지에 올릴때 파일의 픽셀값이 전체 원소의 약 9%가 변경됐다. 이로 인해 이미지를 통해 inference를 수행할 때 값이 달라지는 문제가 발생했다😣 이미지의 원소 하나하나에 큰 영향을 받는 이미지 분류 모델이기 때문에 이 부분은 빠르게 수정해야 했다.
# 파일이 같지 않음AssertionError: Arrays are not equal to 7 decimals Mismatched elements: 215247 / 2362368 (9.11%) Max absolute difference: 255 Max relative difference: 255. x: array([[[ 0, 0, 4], [ 5, 6, 10], [ 8, 9, 13],... y: array([[[ 0, 0, 4], [ 5, 6, 10], [ 8, 9, 13],...
gcp에 업로드할 때 바이너리 파일로 변환해 업로드하는데 이 부분이 문제일까? 파일을 올리고 다운받았을 때 값이 변형됐을 것으로 추측하고 로컬에서 테스트해봤다.
# 바이너리 파일로 변환 후 저장image_bytes = cv2.imencode(".jpg", image)[1].tostring() with open('test.jpg', 'wb') as f: f.write(image_bytes) # 바이너리 파일을 읽은 후 다시 디코딩with open('test.jpg', 'rb') as f: new_image_bytes = f.read() new_image = cv2.imdecode(np.frombuffer(new_image_bytes, np.uint8), cv2.IMREAD_COLOR) # 이미지 파일 비교np.testing.assert_equal(image, new_image) # 역시나 에러가 났다.AssertionError: Arrays are not equal Mismatched elements: 215247 / 2362368 (9.11%) Max absolute difference: 255 Max relative difference: 255. x: array([[[ 0, 0, 4], [ 5, 6, 10], [ 8, 9, 13],... y: array([[[ 0, 0, 4], [ 5, 6, 10], [ 8, 9, 13],...
역시나 파일이 인코딩 후 디코딩하는 과정에서 발생한 문제였다. 그렇다면 원본 이미지와 인코딩, 디코딩된 이미지가 바뀌지 않게 하려면 어떻게 해야할까? 이유가 무엇일까? 원인을 찾아보니 파일 압축 시 jpg 포맷을 사용했기 때문이였다.
jpeg는 손실 압축을 사용하기 때문에 인코딩하는 과정에서 손실이 발생하고 인코딩, 디코딩하는 과정에서 손실이 계속 쌓이게 되어 원본 이미지와 인코딩 후 디코딩된 이미지 값이 완전히 동일하지 않게 된다는 것이다. 해결하기 위해선 png 포맷을 사용하면 된다. png는 용량이 증가하지만 비손실 압축이기 때문에 파일을 압측할 때 손실이 발생하지 않는다고 한다. 이를 확인하기 위해 png 포맷으로 인코딩한 뒤 디코딩한 이미지와 원본 이미지를 비교하는 코드를 작성해보자.
image_bytes = cv2.imencode(".png", image)[1].tostring() with open('test.jpg', 'wb') as f: f.write(image_bytes) with open('test.jpg', 'rb') as f: new_image_bytes = f.read() new_image = cv2.imdecode(np.frombuffer(new_image_bytes, np.uint8), cv2.IMREAD_COLOR) np.testing.assert_almost_equal(image, new_image)
assertionerror 없이 동일한 값임을 확인할 수 있었다. 문제가 해결 됐으니 다시 돌아와서 google cloud storage에 업로드하는 코드에 적용해보면 아래와 같다.
# 업로드 하기import os from google.cloud import storage from google.cloud.storage import Blob bucket_name = '' CREDENTIAL_KEY = '' os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = CREDENTIAL_KEY # 이미지 업로드 storage_client = storage.Client.from_service_account_json(CREDENTIAL_KEY) bucket = storage_client.get_bucket(bucket_name) image_bytes = cv2.imencode(".png", image)[1].tostring() blob = Blob('image_name', bucket) blob.upload_from_string(data=image_bytes, content_type="image/png") # 이미지 다운 blob = bucket.get_blob('image_name') encoded_img = np.frombuffer(blob.download_as_string(), dtype=np.uint8) test_image = cv2.imdecode(encoded_img, cv2.IMREAD_COLOR) # 결과 확인 np.testing.assert_equal(test_image, image)