IDL/Data Type & Format

SER 포맷의 파일 읽기

이상우_IDL 2023. 3. 20. 14:35
728x90

SER 포맷은 천문관측 소프트웨어인 SharpCap에서 생성되는 일종의 비디오 파일 형식입니다. 즉 여러 장의 사진들을 묶어서 동영상의 형태로 합쳐놓은 방식으로서 종종 AVI 포맷과도 비교가 됩니다. 이러한 포맷은 SharpCap 이외에도 Lucam Recorder, SIRIL, PIPP 등과 같은 천문관측 이미징 소프트웨어들에서도 지원됩니다. 이러한 SER 포맷의 파일을 IDL에서 읽을 수 있을까요? 일단 IDL에서 기본적으로 지원되는 각종 파일 형식들 중에는 SER 포맷은 포함되어 있지는 않습니다. 하지만 기본 지원되지는 않더라도 IDL에서 SER 포맷의 파일을 읽는 것은 가능합니다. 왜냐하면 SER 포맷의 파일은 기본적으로 바이너리(Binary) 파일이고 그 안에 어떤 형식으로 데이터들이 저장되어있는가에 대한 일종의 스펙(Specification)에 관한 설명 문서가 있다면 그 내용을 기반으로 하여 데이터 파일을 해독(Decoding)할 수 있기 때문입니다. 그러면 웹상으로 제공되고 있는 스펙 문서 및 실제 SER 포맷의 샘플 파일을 이용하여 IDL에서 읽는 과정을 소개해보도록 하겠습니다.

 

먼저 SER 포맷의 샘플 자료 파일은 아래 링크를 통해서 다운로드받을 수 있습니다. 일단 이 링크를 통해서는 ZIP 형식의 파일을 받게 되는데 압축을 풀면 확장자가 .ser인 파일(파일명은 Jup_200415_204534_R_F0001-0300.ser)을 얻을 수 있습니다.

 

https://github.com/cgarry/ser-player/releases/download/v1.4.0/Jup_200415_204534_R_F0001-0300.zip

 

그리고 SER 포맷에 대한 세부적인 스펙 사항들은 아래 링크의 PDF 문서에서 자세히 설명되어 있습니다.

 

http://www.grischa-hahn.homepage.t-online.de/astro/ser/SER%20Doc%20V2.pdf

 

물론 문서의 내용이 그리 짧은 편은 아니긴 하지만 여기서는 파일을 읽기 위하여 핵심적으로 필요한 사항들 몇가지만 확인하면 됩니다. 일단 파일에 담긴 내용의 앞부분에는 헤더(Header)에 해당되는 정보가 178바이트만큼 존재합니다. 이 헤더 정보 내에 세부적인 항목들이 순서대로 수록되어 있는데, 처음 4개 항목들(14+4+4+4=26바이트)은 제끼고 그 뒤에 이어지는 핵심 사항들을 다음과 같습니다.

 

5_ImageWidth

Format: Integer_32

Length: 4 Byte

Content: Width of every image in pixel

 

6_ImageHeight

Format: Integer_32

Length: 4 Byte Content: Height of every image in pixel

 

8_FrameCount

Format: Integer_32

Length: 4 Byte

Content: Amount of image frames in SER file

 

따라서 파일로부터 헤더 정보를 추출한 다음 그 안에서 위의 3종의 세부 항목들을 추출하는 것이 가장 먼저 필요합니다. 그러면 샘플 SER 파일에 대하여 이러한 정보들을 추출하는 과정까지 먼저 살펴봅시다.

 

file = 'data/Jup_200415_204534_R_F0001-0300.ser'
OPENR, lun, file,/GET_LUN
header = BYTARR(178)
READU, lun, header

 

여기서는 SER 파일의 전반부에 위치한 178바이트 용량의 헤더 정보를 읽어서 header라는 바이트형 배열로 가져오는 것이 선행됩니다. 즉 header라는 배열은 178개의 바이트형 값들로 구성된 배열이 되는데, 이 안에서 필요한 항목들만 골라내야 합니다. 물론 이를 위해서는 각 항목의 위치도 확인해야 합니다. 그 과정은 다음과 같습니다.

 

imageWidth = byteToInt32(header[26:29])
imageHeight = byteToInt32(header[30:33])
frameCount = byteToInt32(header[38:41])
PRINT, imageWidth, imageHeight, frameCount

 

이와 같이 header 내에서 처음 26개는 넘기고 27번째부터 30번째(인덱스로는 26~29)까지 4개의 바이트 값들이 image width 즉 이미지의 가로 방향 픽셀 크기 정보가 됩니다. 바로 뒤 31번째부터 34번째(인덱스로는 30~33)까지 4개의 바이트 값들은 image height 즉 이미지의 세로 방향 픽셀 크기 정보가 됩니다. 그 다음으로 필요한 정보는 frame count 즉 이미지 프레임들의 총 갯수인데 순서상으로는 39번째부터 42번째(인덱스로는 38~41)에 위치합니다. 따라서 이러한 근거에 의하여 위에서는 header 배열 내에서 해당 인덱스 범위에 대한 바이트 값들을 추출하습니다.

 

다만 여기서 끝나는 것이 아니고, 이렇게 추출된 4개의 바이트 값들을 4바이트(32비트) 정수로 변환해야 실제로 우리가 인지할 수 있는 값이 됩니다. 그래서 위의 내용을 보면 byteToInt32라는 정체 불명의 함수가 사용된 것을 볼 수 있는데, 이 함수는 IDL에 기본 제공되는 것이 아니라 다음과 같이 함수형 부프로그램으로 직접 만들어줘야 합니다.

 

FUNCTION byteToInt32, byteArr

IF N_ELEMENTS(byteArr) EQ 4 THEN BEGIN
  int32 = byteArr[0]+byteArr[1]*2^8+byteArr[2]*2^16+byteArr[3]*2^24
ENDIF
RETURN, int32

END

 

즉 이러한 함수형 부프로그램을 정의하여 함께 사용해야 바이트 값 배열을 정수의 형태로 해독할 수 있습니다. 이러한 해독 과정을 거쳐 실제로 출력된 정보들을 보면 다음과 같습니다.

 

     640     480     100

 

이 출력 결과를 보면 640x480의 2차원 구조를 갖는 이미지 데이터가 총 100개 수록되어 있다는 것을 확인할 수 있습니다. 그러면 178 바이트의 헤더 정보 바로 뒤부터는 실제 이미지 데이터가 이러한 구조로 수록되어 있다는 뜻이기 때문에 반복형 구문을 사용하여 모든 프레임 이미지들을 순차적으로 읽어주면 됩니다. 그 과정은 다음과 같습니다.

 

allFrames = BYTARR(imageWidth, imageHeight, frameCount)
frame = BYTARR(imageWidth, imageHeight)
FOR i = 0, frameCount-1 DO BEGIN
  READU, lun, frame
  allFrames[*, *, i] = frame
ENDFOR
FREE_LUN, lun

 

이와 같이 allFrames라는 배열 내에 100장의 이미지 데이터들이 수록됩니다. 그러면 SER 포맷 파일의 내용을 읽는 것은 다 끝났고 이제부터는 실제로 표출을 해보면 됩니다. 예를 들어 100장의 이미지들 중 20번째 이미지만 골라서 표출해본다면 다음과 같이 처리해보면 됩니다.

 

win = WINDOW(DIMENSIONS=[imageWidth, imageHeight], /NO_TOOLBAR)
img = allFrames[*, *, 19]
i = IMAGE(img, MARGIN=0, /CURRENT)

 

실제 표출된 모습은 다음 그림과 같습니다.

 

 

파일의 제목에서 짐작할 수 있듯이 목성(Jupiter)을 촬영한 스냅샷들 100개 중 하나입니다. 따라서 이 100개의 이미지들을 동영상의 형태로 볼 수 있다면 더 좋을 것입니다. 이를 위하여 IDL의 애니메이션 전용 명령인 XINTERANIMATE를 사용해봅시다. 그 과정은 다음과 같습니다.

 

XINTERANIMATE, SET=[ImageWidth, imageHeight, 100], /SHOWLOAD
FOR i = 0, frameCount-1 DO $
  XINTERANIMATE, FRAME=i, IMAGE=allFrames[*, *, i], /ORDER
XINTERANIMATE, /KEEP_PIXMAPS

 

이 내용을 실행하면 다음과 같이 간단한 독립적인 UI가 뜨면서 그 안에서 100개의 이미지들로 구성된 애니메이션을 볼 수 있게 됩니다. 이렇게 XINTERANIMATE 명령에 의하여 나타나는 팝업 UI에서는 동영상을 재생하는데 있어서 기본적인 편의성 기능들이 지원되기 때문에, 재생을 멈춰놓고 개별 프레임만 본다든지 재생의 속도를 조정한다든지 하는 작업들이 가능합니다.

 

 

지금까지 제시된 예제 코드의 내용을 보면 주프로그램의 내용만 있는 것이 아니라 byteToInt32라는 함수형 부프로그램을 따로 정의하여 활용하고 있습니다. 따라서 전체적인 프로그램의 내용은 다음과 같이 부프로그램이 먼저 등장하고 그 다음에 주프로그램의 내용이 이어지는 방식으로 작성을 해서 주프로그램의 이름을 따라서 test_SER_file_read.pro라는 파일로 저장한 후 이 프로그램을 컴파일 및 실행하면 결과를 얻을 수 있게 됩니다.

 

FUNCTION byteToInt32, byteArr

IF N_ELEMENTS(byteArr) EQ 4 THEN BEGIN
  int32 = byteArr[0]+byteArr[1]*2^8+byteArr[2]*2^16+byteArr[3]*2^24
ENDIF
RETURN, int32

END

 

PRO test_SER_file_read

 

file = 'data/Jup_200415_204534_R_F0001-0300.ser'
OPENR, lun, file,/GET_LUN
header = BYTARR(178)
READU, lun, header

 

imageWidth = byteToInt32(header[26:29])
imageHeight = byteToInt32(header[30:33])
frameCount = byteToInt32(header[38:41])
PRINT, imageWidth, imageHeight, frameCount

 

allFrames = BYTARR(imageWidth, imageHeight, frameCount)
frame = BYTARR(imageWidth, imageHeight)
FOR i = 0, frameCount-1 DO BEGIN
  READU, lun, frame
  allFrames[*, *, i] = frame
ENDFOR
FREE_LUN, lun

 

win = WINDOW(DIMENSIONS=[imageWidth, imageHeight], /NO_TOOLBAR)
img = allFrames[*, *, 19]
i = IMAGE(img, MARGIN=0, /CURRENT)

 

XINTERANIMATE, SET=[ImageWidth, imageHeight, 100], /SHOWLOAD
FOR i = 0, frameCount-1 DO $
  XINTERANIMATE, FRAME=i, IMAGE=allFrames[*, *, i], /ORDER
XINTERANIMATE, /KEEP_PIXMAPS

 

END

 

어쨌든 SER 포맷의 파일을 해독하여 읽고 그 안에 수록된 다수의 이미지들을 하나씩 표출하거나 아니면 애니메이션의 형태로 보는 작업이 위와 같은 방식에 의하여 IDL에서 얼마든지 가능합니다. IDL의 제작사인 Harris Geospatial의 웹페이지에는 IDL 관련 Q&A가 오가는 포럼 페이지가 있습니다. 오늘 소개된 내용은 얼마전에 이 포럼에 올라왔던 관련 질문에 대하여 기술지원팀에서 답변한 내용을 참조하여 제가 내용을 정리해본 것입니다. 원문의 내용은 아래 링크를 통해서도 보실 수 있습니다.

 

https://www.l3harrisgeospatial.com/Support/Forums/aft/8610

LIST