[python] numpy.copyto 사용법


numpy.copyto

Return: Return copy of an array

copyto 관련링크
https://docs.scipy.org/doc/numpy/reference/generated/numpy.copyto.html



numpy.copyto 기본 사용법

기본적으로 src의 정보를 dst에 복사하는게 copyto 입니다.

import numpy as np

src = np.arange(3).astype(np.uint8)
print("src dtype = {0}, \nsrc = \n{1}\n".format(src.dtype, src))

dst = np.ones(3).astype(np.uint32)
print("before copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst)) 

np.copyto(dst, src)
print("after copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# src dtype = uint8,
# src = [0 1 2]

# before copyto dst dtype = uint32,
# dst = [1 1 1]

# after copyto dst dtype = uint32,
# dst = [0 1 2]

0, 1, 2의 값을 가지는 src를 dst로 복사하는 코드입니다.
src에서 dst로 배열의 값만 복사가 되었으며 dst dtype은 그래로 유지됩니다.

즉. copyto를 사용하면 src에 존재하는 값의 요소만 dst로 복사합니다.
src dtype까지 dst로 변경되지 않습니다.

밑에는 copyto casting 및 where에 대한 설명입니다.



numpy.copyto casting

src를 dst로 복사하는 과정에서 데이터 casting의 조건을 설정합니다.


각각의 casting 조건이 True인 경우에만 numpy.copyto()를 실행할 수 있습니다.
각각의 casting 조건 가능여부는 numpy.can_cast로 확인가능합니다.

casting 가능여부에 따라 True or False를 반환합니다.
default = "safe" option

can_cast 링크
https://docs.scipy.org/doc/numpy/reference/generated/numpy.can_cast.html



casting option no (data types should not be cast at all)

src = np.arange(3).astype(np.uint8)
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint8np.uint8casting="no")
print("can_cast (np.uint8, np.uint8)= {0}".format(cast))
np.copyto(dst, src)
print("copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.uint8)
dst = np.ones(3).astype(np.uint32)
cast = np.can_cast(np.uint8np.uint32casting="no"# False
print("can_cast (np.uint8, np.uint32)= {0}".format(cast))
np.copyto(dst, src, casting="no"# error
print("change uint8 to uint32 copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.uint32)
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint32np.uint8casting="no"# False
print("can_cast (np.uint32, np.uint8)= {0}".format(cast))
np.copyto(dstsrccasting="no"# error
print("change uint8 to uint32 copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtypedst))

# 출력
# can_cast (np.uint8, np.uint8)= True
# copyto dst dtype = uint8,
# dst = [0 1 2]

can_cast (np.uint8, np.uint32)= False
TypeError: Cannot cast scalar from dtype('uint8') to dtype('uint32') according to the rule 'no'

# can_cast (np.uint32, np.uint8)= False
TypeError: Cannot cast scalar from dtype('uint8') to dtype('uint32') according to the rule 'no'

에러 부분은 src uint8 -> uint32, dst uint8 -> uint32가 각각 변했습니다.
numpy.can_cast에서 True를 반환한 경우에는 copyto가 되었으며
False를 반환한 경우에는 copyto가 되지 않음을 볼 수 있습니다.

no의 경우 src와 dst의 dtype이 동일해야지만 copyto가 가능합니다.



casting option equiv (only byte-order changes are allowed)

src = np.arange(3).astype(np.uint8)
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint8, np.uint8, casting="equiv")
print("can_cast (np.uint8, np.uint8)= {0}".format(cast))
np.copyto(dst, src, casting="equiv")
print("equiv copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.uint8)
dst = np.ones(3).astype(np.uint32)
cast = np.can_cast(np.uint8, np.uint32, casting="equiv")
print("can_cast (np.uint8, np.uint32)= {0}".format(cast))
np.copyto(dst, src, casting="equiv")
print("equiv copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.float32)
dst = np.ones(3).astype(np.float32)
cast = np.can_cast(np.float32, np.float32, casting="equiv")
print("can_cast (np.float32, np.float32)= {0}".format(cast))
np.copyto(dst, src, casting="equiv")
print("equiv copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.float16)
dst = np.ones(3).astype(np.float32)
cast = np.can_cast(np.float16, np.float32, casting="equiv")
print("can_cast (np.float16, np.float32)= {0}".format(cast))
np.copyto(dst, src, casting="equiv")
print("equiv copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# can_cast (np.uint8, np.uint8)= True
# equiv copyto dst dtype = uint8,
# dst = [0 1 2]

# can_cast (np.uint8, np.uint32)= False
# TypeError: Cannot cast scalar from dtype('uint8') to dtype('uint32') according to the rule 'equiv'

# can_cast (np.float32, np.float32)= True
# equiv copyto dst dtype = float32,
# dst = [0. 1. 2.]

# can_cast (np.float16, np.float32)= False
# TypeError: Cannot cast scalar from dtype('float16') to dtype('float32') according to the rule 'equiv'

src, dst의 데이터 타입(byte)이 각각 동일할 때에만 copyto가 실행됩니다.
동일하다는 src type(uint8)과 dst type(uint8),  src type(float16)과 dst type(float16)이 같다를 의미합니다.



casting option safe (only casts which can preserve values are allowed)

src = np.arange(3).astype(np.uint32)
dst = np.ones(3).astype(str)
cast = np.can_cast(np.uint32str)
print("can_cast (np.uint32, str) = {0}".format(cast))
np.copyto(dst, src, casting="safe")
print("safe copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype(np.uint32)
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint32np.uint8)
print("can_cast (np.uint32, np.uint8)= {0}".format(cast))
np.copyto(dst, src, casting="safe")
print("safe copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# can_cast (np.uint32, str) = True
# safe copyto dst dtype = <U32,
# dst = ['0' '1' '2']

# can_cast (np.uint32, np.uint8)= False
TypeError: Cannot cast scalar from dtype('uint32') to dtype('uint8') according to the rule 'safe'

safe에서 np.uint8 -> str은 allowed(형 변환 시 값을 보존할 수 있다)가 되었지만
np.uint32 -> np.uint8은 disallowed(형 변환 시 값을 보존할 수 없다)가 됨을 확인할 수 있습니다.

변환이 불가한 이유는
np.uint32는 정수 값을 최대 32비트(2,147,483,637)까지 표현가능하지만
np.uint8는 최대 8비트(255)까지 표현되기에
256 이상의 값을 dst로 복사 할 수 없다고 에러를 발생시킵니다.

safe의 경우 casting 과정에서 값을 보존할 수 있는 dtype들만 copyto가 가능합니다.



casting option same_kind (only safe casts or casts within a kind)
copyto casting의 default 값

src = np.arange(3).astype(np.uint32)
src[0] = 256
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint32, np.uint8, casting="same_kind")
print("can_cast (np.uint32, np.uint8)= {0}".format(cast))
np.copyto(dst, src, casting="same_kind")
print("same_kind copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# can_cast (np.uint32, np.uint8)= True
# same_kind copyto dst dtype = uint8,
# dst = [0 1 2]

same_kind는 safe의 성능을 포함하며
src uint32, dst uint8 같은 casting을 지원합니다.

src는 32비트(2,147,483,637)까지 표현 가능.
dst는 8비트(255)까지 표현 가능.

dst는 255까지 밖에 표현이 안되는데 src[0]의 값이 현재 256 입니다.
safe에서는 형식지원이 불가하여 에러가 발생하지만
same_kind에서는 256을 0으로 인식하여 표현 합니다.

same_kind와 safe의 차이는 dst가 src가 표현할 수 있는 범위의 값 보다 작을 때 casting을 할 수 있냐 없냐의 차이입니다.



casting option unsafe (any data conversions may be done)

src = np.arange(3).astype(np.uint32)
src[0] = 256
dst = np.ones(3).astype(np.uint8)
cast = np.can_cast(np.uint32np.uint8, casting="unsafe")
print("can_cast (np.uint32, np.uint8)= {0}".format(cast))
np.copyto(dst, src, casting="unsafe")
print("unsafe copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

src = np.arange(3).astype('i4'# int32
print("i4 src = {0}".format(src))
dst = np.ones(3).astype('f4'# float32
cast = np.can_cast('i4', 'f4', casting="same_kind")
print("can_cast (i4, f4)= {0}".format(cast))
np.copyto(dst, src, casting="unsafe")
print("unsafe copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# can_cast (np.uint32, np.uint8)= True
# unsafe copyto dst dtype = uint8,
# dst = [0 1 2]

# i4 src = [0 1 2]
# can_cast (i4, f4)= True
# unsafe copyto dst dtype = float32,
# dst = [0. 1. 2.]

어떤 데이터 변환이든 copyto를 지원합니다.



numpy.copyto where
copyto where의 deflaut는 True


src = np.arange(3).astype(np.uint8)
print("src = {0}\n".format(src))

dst = np.ones(3).astype(np.uint8)
print("before copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

np.copyto(dst, src, where=False) # casting default 'same_kind'
print("after copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# src = [0 1 2]

# before copyto dst dtype = uint8,
# dst = [1 1 1]

# after copyto dst dtype = uint8,
# dst = [1 1 1]

where을 False로 바꾸었을 경우 src에 있는 값들 전체가 복사되지 않았습니다.
원래 dst의 값 [1, 1, 1]
copyto 이후 dst의 값 [1, 1, 1]



src = np.arange(3).astype(np.uint8)
print("src = {0}\n".format(src))

mask = np.arange(3).astype(bool)
print("mask dtype = {0}\nmask = {1}, \n".format(mask.dtype, mask))

dst = np.zeros(3).astype(np.uint8)
print("before copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

np.copyto(dst, src, where=mask) # casting default 'same_kind'
print("after copyto dst dtype = {0}, \ndst = {1}\n".format(dst.dtype, dst))

# 출력
# src = [0 1 2]

# mask dtype = bool
# mask = [False  True  True],

# before copyto dst dtype = uint8,
# dst = [0 0 0]

# after copyto dst dtype = uint8,
# dst = [0 1 2]

where에는 array_like of bool이라 하여 bool 배열 값을 넣을 수 있습니다.
where의 의미는 src에서 dst로 값을 복사할 때, where이 True인 값들만 dst로 복사하겠다는 의미입니다.

현재 코드에서 src [0, 1, 2]의 값을 dst[0, 0, 0]에 복사할껀데
where이 [F, T, T]이니 T인 부분에만 값을 복사하여
dst의 값은 0, 1, 2로 src의 1과 2의 값만 복사된 것을 알 수 있습니다.

만약 where이 [T, T, F]라면 dst의 값은 0, 1, 0으로 src의 0과 1의 값만 복사가 됩니다.

where을 True로 설정한다면 모든 src의 값을 dst로 복사하겠다는 의미입니다.


참고문헌


댓글

이 블로그의 인기 게시물

[opencv-python] 이미지 크기조절(resize) 하는 법

[python]파이썬: csv reader header skip (첫번째 행 무시하기, 안읽기)

[python] selenium close와 quit 차이점