파이썬으로 이미지를 다루기 전, 디지털 이미지에 대한 이해가 우선으로 필요하다.
디지털 화면은 색상을 가지는 점인 화소로 이루어져 있으며 각 화소는 RGB(Red, Green, Blue) 세 개의 색의 조합으로 색상이 표현된다.
디지털 이미지를 저장할 때는 각 점마다 색상 값을 저장하는 방식으로 저장한다.
이를 래스터(raster) 또는 비트맵(bitmap)이라고 하며 한 점마다 각 색상별로 8비트를 사용한다. 또 0 ~ 255 사이의 값(2^8=256)으로 해당 색의 감도를 표시한다.
또 다른 디지털 이미지 저장 방식으로는 벡터(vector) 방식이 있다. 이는 상대적인 점과 선의 위치를 방정식으로 기록했다가 확대 및 축소에 따라 각 화소가 어떻게 표현되는지를 재계산하기 때문에 디지털 이미지의 깨짐 현상은 없다.
이러한 색상 정보를 저장하려면 많은 용량을 필요로 하기 때문에 사진을 저장할 땐 압축을 한다.
압축 방식으로 사진의 확장자가 정해지는데 흔하게 사용되는 jpg 이미지는 근처 화소들을 묶어 비슷한 색을 뭉뚱그리는 방식으로 이미지를 압축한다. 때문에 jpg 이미지는 색상 정보의 손실 위험이 있다. 또 디지털 풍화 현상도 일어난다.
최근에 많이 사용되고 있는 png 이미지는 색상 손실 없이 이미지를 압축한다. 이미지에 사용된 색상을 미리 정의해두어 저장된 색상을 참조하는 팔레트 방식이기 때문에 색상이 적은 이미지을 경우 jpg 파일보다 용량을 적게 차지하기도 한다. 하지만 색상이 많아지면 jpg 파일보다 용량을 더 많이 차지하기도 한다.
파이썬에서 이미지를 다루는 방법은 많지만 가장 많이, 널리 쓰이는 방법은 Pillow를 사용하는 방법과 opencv를 사용하는 방법이다.
> Pillow <
pillow는 numpy와 결합하여 간편하게 사용할 수 있다.
이미지는 배열 형태를 가지는 데이터이기 때문에 numpy로 표현할 땐 [10,10,3]과 같은 차원으로 표현된다.
이미지 배열의 값의 데이터 타입은 uint8(부호가 없는 9비터 정수)이어야 하며 0 ~ 255 사이의 값으로 나타내어야 한다.
3d array를 만들어 Pillow의 fromarray를 사용하여 이미지로 바꿀 수 있다.
[r,g,b] 순서로 색상 값을 저장하므로 [255,0,0]은 빨간색이 된다.
[255,255,255]는 흰 색이 되며 [0,0,0]은 검은색이 된다.
pillow를 사용하여 이미지를 로드할 땐 .open(path)를 하여 이미지를 부른다.
또, 이미지의 사이즈를 변경하고 싶다면 .resize((w,h))를 하면 된다.
이미지를 원하는 부분만 자르고 싶다면 .crop((좌표))를 하면 된다.
(좌표)는 자르고자 하는 부분의 좌표인데 (a,b,c,d)로 총 4개의 인자를 입력해야 한다.
pillow를 사용하여 데이터 전처리를 할 수도 있다.
CIFAR-100 데이터 셋을 활용하여 데이터 전처리를 했다.
데이터셋의 압축을 해제하면 trian, test, meta 총 3개의 파일이 나오는데 train 파일만 사용하였다.
https://www.cs.toronto.edu/~kriz/cifar.html
해당 페이지에 가면 데이터에 대해 설명되어 있기 때문에 해당 페이지를 참고하면서 데이터를 보았다.
train은 딕셔너리 객체이며 각 키들이 문자열(string)이 아니라 b로 시작하는 bytes 타입이라는 것을 알 수 있다.
따라서 파일명을 보기 위해서는 ' type(train[b'filenames']) ' 를 해주어야 한다.
파일 이름에 해당하는 이미지는 b'data'를 보면 된다는 것도 알 수 있다.
따라서 ' train[b'data'][0:5] ' 로 확인을 하면 numpy array가 나온다.
numpy array는 이미지로 복구할 수 있으며 RGB 순서를 맞추어 .reshape를 해주면 된다.
RGB 순서를 맞춰 앞선 차원부터 데이터를 채우기 위해서는 np.reshap의 order 인자를 F로 지정하면 된다.
이까지 진행하면 numpy array에서 바꾼 이미지를 출력하여 볼 수 있다. 이미지가 회전되어 있으므로 np.swapaxes(0,1)을 해주어 축을 바꾸었다.
위의 코드를 실행시켜 최종 결과로 나온 이미지이다.
이렇게 나온 이미지를 실제 이미지처럼 파일로 저장해주는 과정도 진행했다.
데이터셋에서 파일명과 파일 데이터 배열이 순서대로 저장되어 있기 때문에 numpy array로 읽어 위와 같은 과정으로 이미지 파일로 변환 후 저장을 해주면 된다.
tqdm을 사용하면 반복 작업에 대해 진행 상황을 시각화할 수 있다.
> OpenCV <
opencv는 컴퓨터 비전용 라이브러리로 오픈 소스로 제공된다.
영상 처리에 대한 많은 고오급 기능들이 사용하기 쉽게 되어있다.
이미지를 다룰 때도 pillow보다 opencv를 더 많이 사용하는 것 같다.
pillow는 RGB 순서대로 색상 정보가 저장되어 있지만 opencv는 BGR 순서로 저장되어 있기 때문에 이 점을 유의해서 사용해야 한다.
(때문에 opencv를 이용하여 이미지를 부르고 출력하면 파란색이 강하게 나타난 결과를 볼 수 있다.)
https://docs.opencv.org/master/df/d9d/tutorial_py_colorspaces.html
해당 사이트를 참고하여 특정 색을 가진 부분만 따로 떼어내어 색을 BGR에서 HSV로 변환한 뒤 해당 색과 맞는 영역만 표시하는 예제를 실습했다.
opencv를 이용하여 이미지를 로드할 땐 cv.imread(path)를 한다.
이미지의 색상을 HSV로 바꾸기 위해 컬러 스페이스 변환을 위한 함수인 .convert를 이용하여
cv.cvtColor(img, cv.COLOR_BGR2HSV) 를 한다.
변환을 마쳤으면, 숫자로 파란색을 정의하여 이 값을 기준으로 이미지에서 마스크를 생성해준다.
마스크는 원하는 부분만 떼어낼 수 있도록 해준다.
위 코드에서는 HSV 색 공간에서 색상(Hue) 값 110~130 사이,
채도(Saturation) 및 명도(Value) 값 50~255 사이의 색들을 파란색이라고 정의하고 있다.
cv.inRange()를 사용하여 이 기준들을 적용하여 해당하는 픽셀은 1, 해당하지 않은 픽셀은 0으로 바꾼 배열을 반환한다.
cv.bitewise에 대한 설명은 아래 링크에 있다..
https://docs.opencv.org/3.4/d2/de8/group__core__array.html#ga60b4d04b251ba5eb1392c34425497e14
요약하면 이미지 두 장에 대해 AND 비트 연산을 해주는 것이다.
위의 코드에서는 같은 이미지를 넣는 대신 마스크를 같이 넣어 원하는 영역만 떼어내도록 한다.
이렇게 원하는 영역만 떼어냈다면 matplotlib를 사용하여 결과물을 출력한다.
위에서도 말했지만 opencv는 BGR 순서이기 때문에 출력시 RGB로 바꿔주지 않으면 파란 이미지만 얻게 된다.
꼭 RGB 순서대로 바꾸어서 출력해야 보통의 이미지처럼 출력된다.
opencv를 이용하여 비슷한 이미지를 찾아내는 실습도 했으나 코드 길이도 매우 길고 설명하기엔 글이 너무 길어질 것 같아서 깃허브 링크로 대체한다.
https://github.com/YOOHYOJEONG/lms_practice/blob/master/fundamental/lms_12_histogram_search.py
각 이미지들의 히스토그램을 비교하여 비슷한 이미지를 찾아내는 코드이다.