본문 바로가기
Python/NumPy

NumPy(넘파이)에서의 Byte-swapping(바이트스와핑) - NumPy(8)

by 콩돌 2019. 2. 12.
반응형

참고 자료

https://docs.scipy.org/doc/numpy/user/basics.byteswapping.html


파이썬 버전 3.7 기준

NumPy 버전 1.16 기준


본 포스팅에서는 NumPy에서의 Byte-swapping을 다룬다.



NumPy에서의 Byte-swapping


바이트스와핑 개요

바이트스와핑 개요
  ○ ndarray는 메모리에 데이터로 파이썬 배열 인터페이스를 제공해주는 오브젝트이다.

  ○ 사용자가 배열을 사용해서 보길 원하는 메모리와 파이썬을 돌리는 컴퓨터와 같은 바이트 순서(Byte ordering)가 아닌 경우가 종종있다.

    ▷ 대표적으로 Intel Pentium와 같은 리틀 엔디언(Little-endian) CPU에서 빅 엔디언(Big-endian)에서 작성된 데이터를 불러올 경우 등이 있다.

  ○ 바이트에 대해 직접 빅엔디안 컴퓨터에서 작성된 파일에서 4바이트를 불러온다고 예를 들어본다면,
    ▷ 4바이트면 16 비트이다. 
    ▷ 빅엔디안 장치에서는 2바이트 정수형은 최상위바이트(MSB, Most Significant Byte)에 먼저 저장되고 그 다음 최하위바이트(LSB, Least Significant Byte)에 저장된다. 그러므로 메모리 순서는 다음과 같다.
      ① MSB 정수 1
      ② LSB 정수 1
      ③ MSB 정수 2
      ④ LSB 정수 2

바이트스와핑의 예제
  ○ 4바이트에서 2바이트씩 가지는 정수를 만든다고 가정한다.
  ○ 두개의 정수가 1과 770로 가정을 하여 예를 들어볼 경우 
    ▷ 1은 0*256 + 1이기 때문에 각 바이트에는 0과 1이 들어간다.
    ▷ 770은 3*256^1 + 2 이기 때문에 각 바이트에는 3, 2라는 값이 들어가게 된다.
    ▷ 즉 각 바이트에 들어가는 값은 0, 1, 3, 2이다.
  ○ 이 경우 파일에 이 바이트 값을 직접들어가게 하기 위해서는 아래와 같이 입력할 수 있다.
    ▷ 여기서 \x00은 1바이트에서 0를 의미하며, 나머지 \x01, \는03, \x02는 마찬가지로 1바이트에서 1, 3, 2를 각각 의미한다.
  ○ ndarray를 이용하여 위의 정수값을 바이트에 직접 입력하고자하면 아래와 같이 할 수 있다. 
    ▷ 이 경우 이 메모리 주변에 배열을 만들 수 있고, 이 데이터는 16 bit이며 빅엔디안을 가지도록 설정할 수 있다. 

바이트 입력)
# 바이트 데이터 정의
In[19]:byte_data = b'\x00\x01\x03\x02'

# 바이트 데이터를 입력
In[20]: byte_arr_big = np.ndarray(shape=(2), dtype=">i2", buffer=byte_data)

#바이트 데이터 결과
In[21]: byte_arr_big[0]
Out[21]: 1
In[22]: byte_arr_big[1]
Out[22]: 770

※ 여기서 >i2에서 >는 빅엔디안을 의미하며, (<는 리틀엔디안을 의미한다.), i2는 부호있는 2바이트 정수를 의미한다. 

  ○ 위의 데이터를 리틀엔디안에서 부호없는 4바이트로 데이터로 표현하면 아래와 같다.
    ▷ 리틀엔디언의 경우 위의 데이터가 0*256^0 + 1*256^1 + 3*256^2 + 2*256^3이기 때문에 33751296란 값이 나온다.

바이트 입력)
In[23]: byte_arr_little = np.ndarray(shape=(1), dtype="<u4", buffer=byte_data)
In[24]: byte_arr_little
Out[24]: array([33751296], dtype=uint32)

※ 주의  
스칼라에는 바이트 순서에 대한 정보가 입력되지 않으므로 스칼라를 직접 추출하여 정수를 반환할 경우 원래 바이트 순서로 반환된다. 따라서 아래와 같은 상황이 벌어질 수도 있다. 

In[28]: byte_arr_big[0].dtype.byteorder == byte_arr_little[0].dtype.byteorder
Out[28]: True


바이트 순서 변환
  ○ 배열과 메모리의 바이트 순서간에 영향을 미치는 방법으로는 크게 2가지가 있다. 
    ① 배열의 dtype에서 바이트 순서 정보를 변경하여, 이것이 다른 바이트 순서에서 근본적인 데이터가 해석되게 하는 것이 있다. 이것은 arr.newbyteorder()로 수행할 수 있다.
     dtype을 그대로 남겨두고, 바이트 데이터의 바이트 순서를 뒤바꾸는 방법이 있다. arr.byteswap()으로 수행이 가능하다.

  ○ 바이트 순서를 바꿔야할 필요가 있는 공통적인 상황은 다음과 같다.
    ① 데이터와 dtype 에디안이 매치가 되지 않는 상황에서, 서로 매치시키기 위해 dtype이 바뀌길 원할 경우.
     데이터와 dtype 에디안이 매치가 되지 않는 상황에서, 서로 매치시키기 위해 데이터를 바꾸는 경우.
     데이터와 dtype 에디안이 매치가 되어있는 상황에서, 데이터와 dtype을 둘 다 바꾸는 경우.
  
①번 경우
  ○ 이 상황에서는 arr.newbyteorder() 메서드를 이용하여 고칠 수 있다.

사용 예)
# 리틀엔디안에서 빅엔디안으로 바꾸기 위한 예제입력(위의 예제에서 byte_data 입력 활용)
In[34]: not_big_endian = np.ndarray(shape=(2,), dtype='<i2', buffer=byte_data)
In[35]: not_big_endian[0]
Out[35]: 256

# 빅엔디안으로의 변환
In[36]: big_endian = not_big_endian.newbyteorder()
In[37]: big_endian[0]
Out[37]: 1

# 바이트의 비교
In[38]: big_endian.tobytes() == byte_data
Out[38]: True

②번 경우
  ○ 이 상황에서는 arr.byteswap() 메서드를 활용하면 된다.

사용 예)
# 바이트의 변경, 입력 데이터는 위의 not_big_endian 데이터 활용
In[41]: not_big_endian_byte_change = not_big_endian.byteswap()
In[42]: not_big_endian_byte_change[0]
Out[42]: 1

# 바이트의 비교
In[43]: not_big_endian_byte_change.tobytes() == byte_data
Out[43]: False

번 경우 
  ○ 이 상황에서는 arr.newbyteorder()과 arr.byteswap()메서드 둘 다 이용하면 된다.

사용 예)
바이트와 엔디안의 변경, 입력 데이터는 위의 big_endian 데이터 활용
In[48]: all_change = big_endian.byteswap().newbyteorder()
In[49]: all_change[0]
Out[49]: 1

바이트의 비교
In[50]: all_change.tobytes() == byte_data
Out[50]: False


반응형

댓글