1. 디지털 이미지의 구조
픽셀
픽셀은 picture(그림)과 element(요소)의 합성어
단일 픽셀 값은 흑백 이미지에서 밝기를 나타내는 숫자, 컬러 이미지에서 픽셀은 빨강, 녹색, 파랑 등 서로 다른 색상을 채널을 나타내는 여러 픽셀 값을 가질 수 있다.
해상도는 이미지가 보유하고 있는 픽셀의 양
해상도 1920x1080는 이미지의 1920픽셀 너비와 1080픽셀 높이를 가지고 있다. 카메라 센서 사이즈에 의해 주로 결정된다. 4k는 가로 픽셀이 4000 이상을 뜻함
픽셀밀도는 (pixel per inch)인치당 픽셀 수, 센티미터당 픽셀 수로 측정되며 픽셀이 얼마나 촘촘하게 배열되어 있는지 척도이다. 픽셀밀도가 높을 수록 텍스트와 그래픽이 더 부드럽고 선명해지기에 더 현실감있게 느껴질 수 있다. 이로 인해 VR과 AR 같은 증강 현실에 중요하다
서브 픽셀은 본질적으로 화면의 픽셀을 구성하는 작은 컬러 요소
RGB 서브 픽셀에서 방출되는 빛의 강도를 조작해서 색상을 구현한다. 더 밝고 에너지 효율적인 디스플레이는 흰색 서브 픽셀이 추가된 RGBW를 사용한다. 흰색 서브 픽셀을 사용하면 RGB를 모두 활성화하지 않고도 흰색을 표현할 수 있어 에너지를 절약할 수 있다.
무손실 압축과 손실 압축
이미지 압축으로 빠른 훈련과 전송 속도, 컴퓨터 비전 모델 성능 향상이라는 장점이 있다. 압축으로 인한 이미지의 왜곡은 품질이 저하된 이미지에서도 모델이 인식할 수 있도록 학습시킬 수 있다.
무손실 압축
portable network graphics의 약자인 PNG 형식은 무손실 압축을 사용한다. 디플레이트 알고리즘을 사용하는데 LZ77과 허프만 코딩이다.
- LZ77 : 반복 시퀀스를 효과적으로 축소
- 허프만 코딩 : 빈도가 높은 패턴을 짧은 코드로 변환
손실 압축
원본 데이터가 일부 손실되는 압축으로 시각으로 인지하기 어려운 특징 데이터를 제거한다. 무손실 압축보다 높은 압축률을 달성할 수 있다. joint photogrephic experts group(JPEG)는 손실 압축으로 그림이나 글자처럼 가장자리가 날카롭고 대비가 있는 이미지에는 아티팩트가 생성되기 때문에 적합하지 않다. 변환, 양자화 및 엔트로피 코딩의 조합을 사용
- RGB에서 YCbCr로 변환(서브 샘플링) : Y는 휘도(밝기), Cb와 Cr은 색상을 나타낸다
- 양자화 : 어려운 픽셀의 패턴은 인간의 시각으로 모두 확인하기 어렵기 때문에 단순화하는데 이 단계에서 손실이 발생한다
- 엔트로피 코딩 : 데이터 심볼이 발생할 확률에 따라 심볼을 적절한 길이로 부호화하여 표한하는 것으로 종류로는 허프만 코딩, 산술 부호화, LZW 부호화가 있다
2. 색 공간
그레이 스케일
색상 스펙트럼은 없고 밝기값만 주어진다. 0(검정)~255(흰색)으로 이런 단순성 덕분에 계산 저장 공간을 줄이고 처리 속도를 높일 수 있다. 가장자리 감지나 텍스처 분석과 같은 특정 이미지 처리 작업의 경우 색상이 방해되는 경우가 있기에 그레이 스케일이 더 적합하다
RGB
red, green, blue에서 첫 글자를 딴 이름으로 세 가지 색상으로 모든 색을 표현할 수 있다. 세 가지 색상은 채널이라는 이름으로 표현되는데 채널은 색상의 정보를 담고 있는 저장공간으로 빨간색 정보를 담고 있는 빨간색 채널, 초록색 채널, 파란색 채널로 구성된다. 빛의 강도를 조절하여 색상을 조합하여 구현한다
CMYK
인쇄물에는 빛과 달리 다른 접근 방식이 필요해서 생긴 방법으로 청록색, 자홍색, 노란색 잉크로 결합하여 색상을 구현한다. 하지만 잉크의 불완전성으로 인해 완벽한 검은색이 나오지 않은 경우가 많아 검정 잉크가 따로 포함된다.
HSV
HSV는 색상을 세 가지 요소로 나눈다.
- Hue(색조) : 색상을 말한다. 빨강, 파랑, 초록 등
- Saturation(채도) : 색조의 선명도로 채도가 낮을 수록 색이 바랜 것처럼 보인다
- Value(값, 밝기) : 색상의 밝기로 값이 낮을수록 어둡고 칙칙해보인다
비트
- 1비트 : 두 가지 가능한 값(0과 1)을 제공한다. 그레이 스케일에서 회색 음영이 없는 흑백 이미지
- 8비트 : 0(검정)부터 255(흰색)까지 색으로 표현 가능한 이미지
- 16비트 : 0부터 65,535까지 색으로 표현 가능 이미지
- 24비트(채널당 8비트) : 256x256x256 = 16,777,216개의 색으로 표현 가능한 이미지
- 48비트(채널당 16비트)
비트 심도의 중요성
색상, 그레이 스케일 음영을 표현하고 구분할 수 있는 정밀도를 결정한다.
- 이미지 품질 : 비트 심도가 높을수록 그라데이션이 더 부드러워져서 아티팩트가 줄어든다
- 파일 사이즈 : 비트 심도가 높을수록 파일 사이즈가 커진다
- 편집의 유연성 : 비트가 높을수록 이미지 품질 저하 없이 편집할 수 있다
- 특수 이미징 : 천체 사진, 의료 영상과 같은 이미지를 디테일 손실 없이 모든 데이터를 캡쳐하기 위해 특정 비트 심도가 필요하다
3. 이미지에서의 텐서 이해하기
텐서의 이미지 표현
- 256x256 사이즈의 그레이 스케일 이미지 (256, 256)
- 256x256 사이즈의 RGB 컬러 이미지 (256, 256, 3)
- 이미지 배치로 작업할 때나 영상의 경우 4차원 텐서를 사용하는데 여기서 첫 번째 차원 값은 배치에 포함된 이미지 수이다
- 100개의 이미지 배치의 256x256 사이즈의 컬러 이미지 (100, 256, 256, 3)
이미지 불러오기
tf.keras.utils.get_file : url 이미지를 다운로드하고 저장된 로컬 경로를 반환한다
tf.io.read_file : 경로에서 이미지 파일을 바이너리 문자열로 읽어온다
tf.image.decode_jpeg : 바이너리 문자열을 숫자 텐서로 디코딩한다
import tensorflow as tf
url = 'https://cobslab.com/wp-content/uploads/2022/02/ai-009-1.jpg'
image_path = tf.keras.utils.get_file('/content/image.jpg', origin=url)
image = tf.io.read_file(image_path)
image = tf.image.decode_jpeg(image, channels=3)
image
-> <tf.Tensor: shape=(952, 1048, 3), dtype=uint8, numpy= array([[[ 1, 10, 39], [ 1, 10, 39], [ 1, 10, 39], …(중략)… [ 1, 10, 39], [ 1, 10, 39], [ 1, 10, 39]]], dtype=uint8)>
결과 분석
- tf.Tensor : 출력물이 데이터를 캡슐화하는데 사용되는 텐서플로의 핵심 데이터 구조인 텐서 객체
- shape=(952,1048,3) : 텐서 사이즈
- dtype=unit8 : dtype은 텐서 내 요소의 데이터 유형으로 unit8은 값이 0에서 255 사이의 8비트 부호 없는 정수로 효시된다는 의미
- numpy=array : 이미지의 실제 픽셀 값
import matplotlib.pyplot as plt
plt.imshow(image)
plt.axis('off')
plt.show()

다양한 색 공간으로 작업하기
랜덤 텐서 생성
tf.random.uniform을 사용하여 랜덤한 값을 가진 텐서를 생성할 수 있다. 랜덤 텐서는 알고리즘 성능을 평가하거나 색상 패턴 실험에서 사용된다.
rgb_image = tf.random.uniform([100,100,3],maxval=255,dtype=tf.float32)
plt.imshow(rgb_image)
plt.title('RGB Image')
plt.axis('off')
plt.show()

그레이 스케일로 변환
rgb_to_grayscale를 사용해서 RGB 이미지를 그레이 스케일로 바꿀 수 있다. 그레이 스케일은 하나의 채널만 같기에 squeeze 를 사용해서 RGB에 해당하는 차원을 삭제한다.
grayscale_image = tf.image.rgb_to_grayscale(rgb_image)
plt.imshow(grayscale_image.numpy().squeeze(),cmap='gray')
plt.title('Grayscale Image')
plt.axis('off')
plt.show()

RGB 이미지를 그레이 스케일로 변경하는 공식(grayscale = R x 0.299 + G x 0.587 + B x 0.114)으로 코드로 구현할 수 있다
R = rgb_image[0][0][0]*0.299
G = rgb_image[0][0][1]*0.587
B = rgb_image[0][0][2]*0.114
Y=R+G+B
print('그레이 스케일 값 :'+ str(grayscale_image[0][0]))
print('공식으로 구한 그레이 스케일 값 :'+str(Y))
-> 그레이 스케일 값 :tf.Tensor([123.073326], shape=(1,), dtype=float32)
공식으로 구한 그레이 스케일 값 :tf.Tensor(123.076744, shape=(), dtype=float32)
결과 분석
이미지의 RGB 채널값을 각각 처리해주고 합쳐서 그레이 스케일로 변경한 값과 실제 그레이 스케일 이미지의 값이 비슷한 것을 확인할 수 있다. 동일하지 않은 이유는 컴퓨터의 부동 소수점 연산이나 텐서플로 함수의 최적화과정 차이 등으로 인한 차이가 발생한다
HSV로 변환
rgb_to_hsv는 RGB 이미지를 HSV로 변환하는데 사용한다. HSV에서 색소 채널만 추출해서 시각화 시키면서 cmap을 hsv 인수로 사용하면 이미지가 색조 색상 맵으로 렌더링 되어 빨간색(낮은 색조 값)부터 무지개의 모든 색을 거쳐 다시 빨간색(높은 색조 값)까지의 색상을 표시한다
hsv_image = tf.image.rgb_to_hsv(rgb_image)
hue_channel = hsv_image[:,:,0]
plt.imshow(hsv_image, cmap='hsv')
plt.title('Hue Channel of HSV Image')
plt.axis('off')
plt.colorbar(label='Hue Value')
plt.show

결과 분석
무작위 랜덤 이미지여서 색도 채널도 무작위인 것을 확인할 수 있다
픽셀 값의 정규화와 표준화
모델의 성능과 수렴 속도를 위해 픽셀 값의 사이즈를 조정한다
정규화
픽셀 값을 [0,1] 범위로 스케일링 하는 것을 말한다. 픽셀값이 0에서 255 사이인 8비트 이미지라면 255로 나누기만 하면 된다
표준화
이미지의 픽셀 값을 평균 0, 표준 편차 1이 되도록 스케일링하는 과정을 말한다. 표준화는 학습 과정을 가속화할 수 있으므로 머신 러닝 알고리즘에서 중요한 전처리 단계이다. 표준화를 하면 [0,255] 범위 였던 픽셀 값이 [-1, 1] 범위에 속하게 되어서 훈련에 좀 더 일관된 데이터 세트를 제공할 수 있다
#reduce_mean 함수는 텐서의 평균 값을 계산한다. 모든 픽셀의 평균 값을 계산한다. 이는 표준화 과정에서 픽셀 값을 0을 중심으로 정렬하는데 사용
mean = tf.reduce_mean(rgb_image)
#reduce_std 함수는 텐서의 표준 편차를 계산한다. 표준 편차는 픽셀 값의 확산 또는 분산을 측정한다
stddev = tf.math.reduce_std(rgb_image)
#앞서 구한 평균과 표준 편차로 표준화를 진행
normalized_image = (rgb_image-mean)/stddev
rgb_image[0][0], normalized_image[0][0]
-> (<tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 34.185913, 171.11664 , 108.85696 ], dtype=float32)>,
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-1.2692811, 0.5908463, -0.2549167], dtype=float32)>)