2017-03-22 2 views
1

회전 배열을 사용하여 각 벡터를 변환하려는 3D 배열 (벡터의 2D 배열)이 있습니다. 회전은 colsrows이라는 라디안 각도 값의 두 개의 별도 2D 배열에 있습니다.Numpy에서 값 쌍에 대한 행렬을 생성합니다.

저는 NumPy에서 이미 파이썬 루프없이 각도를 계산할 수있었습니다. 이제 NumPy에서 회전 행렬을 생성하는 방법을 찾고 있습니다. 결과적으로 성능이 크게 향상되었습니다.

size = img.shape[:2] 

# Create an array that assigns each pixel the percentage of 
# the correction (value between -1 and 1, distributed linearly). 
cols = np.array([np.arange(size[1]) for __ in range(size[0])]) /(size[1] - 1) * 2 - 1 
rows = np.array([np.arange(size[0]) for __ in range(size[1])]).T/(size[0] - 1) * 2 - 1 

# Atan distribution based on F-number and Sensor size. 
cols = np.arctan(sh * cols/(2 * f)) 
rows = np.arctan(sv * rows/(2 * f)) 

### This is the loop that I would like to remove and find a 
### clever way to make NumPy do the same operation natively. 
for i in range(size[0]): 
    for j in range(size[1]): 
    ah = cols[i,j] 
    av = rows[i,j] 

    # Y-rotation. 
    mat = np.matrix([ 
     [ np.cos(ah), 0, np.sin(ah)], 
     [0, 1, 0], 
     [-np.sin(ah), 0, np.cos(ah)] 
    ]) 

    # X-rotation. 
    mat *= np.matrix([ 
     [1, 0, 0], 
     [0, np.cos(av), -np.sin(av)], 
     [0, np.sin(av), np.cos(av)] 
    ]) 

    img[i,j] = img[i,j] * mat 

return img 

NumPy 작업에서 루프를 재 작성하는 영리한 방법이 있습니까?

+0

하나'av'를 사용해야합니까? – kennytm

+0

@kennytm 맞습니다.이 오류를 발견해 주셔서 감사합니다! –

답변

1

(img의 모양이 (a, b, 3) 수의 가정하자.)

는 첫째, colsrows가 완전히 (대신 cols[i,j]cols[j]를 작성할 수) (a, b)로 확대 할 필요가 없습니다. 그리고 그들은 쉽게 np.linspace을 사용하여 생성 할 수 있습니다

cols = np.linspace(-1, 1, size[1]) # shape: (b,) 
rows = np.linspace(-1, 1, size[0]) # shape: (a,) 

cols = np.arctan(sh * cols/(2*f)) 
rows = np.arctan(sv * rows/(2*f)) 

그런 다음 우리는 매트릭스의 구성 요소를 미리 계산 얻을.

# shape: (b,) 
cos_ah = np.cos(cols) 
sin_ah = np.sin(cols) 
zeros_ah = np.zeros_like(cols) 
ones_ah = np.ones_like(cols) 

# shape: (a,) 
cos_av = np.cos(rows) 
sin_av = np.sin(rows) 
zeros_av = np.zeros_like(rows) 
ones_av = np.ones_like(rows) 

그리고 구성 회전 행렬 :

# shape: (3, 3, b) 
y_mat = np.array([ 
    [cos_ah, zeros_ah, sin_ah], 
    [zeros_ah, ones_ah, zeros_ah], 
    [-sin_ah, zeros_ah, cos_ah], 
]) 

# shape: (3, 3, a) 
x_mat = np.array([ 
    [ones_av, zeros_av, zeros_av], 
    [zeros_av, cos_av, -sin_av], 
    [zeros_av, sin_av, cos_av], 
]) 

지금 보자. 우리는 행렬 곱셈 밖으로 확장하는 경우,

for i in range(size[0]): 
    for j in range(size[1]): 
     img[i, j, :] = img[i, j, :] @ y_mat[:, :, j] @ x_mat[:, :, i] 

또는 : 우리는 루프가있는 경우 우리가 쓰는 것이 np.einsum를 사용하여 잘 처리 할 수 ​​

img[i,j,n] = sum(img[i,j,k]*y_mat[k,m,j]*x_mat[m,n,i] for m = 1 to 3 for n = 1 to 3)

을합니다 (내가, 주의 j, k, m, n은 위의 방정식과 정확하게 일치합니다.

img = np.einsum('ijk,kmj,mni->ijn', img, y_mat, x_mat) 

요약하면 : 회전 행렬의

size = img.shape[:2] 

cols = np.linspace(-1, 1, size[1]) # shape: (b,) 
rows = np.linspace(-1, 1, size[0]) # shape: (a,) 

cols = np.arctan(sh * cols/(2*f)) 
rows = np.arctan(sv * rows/(2*f)) 

cos_ah = np.cos(cols) 
sin_ah = np.sin(cols) 
zeros_ah = np.zeros_like(cols) 
ones_ah = np.ones_like(cols) 

cos_av = np.cos(rows) 
sin_av = np.sin(rows) 
zeros_av = np.zeros_like(rows) 
ones_av = np.ones_like(rows) 

y_mat = np.array([ 
    [cos_ah, zeros_ah, sin_ah], 
    [zeros_ah, ones_ah, zeros_ah], 
    [-sin_ah, zeros_ah, cos_ah], 
]) 

x_mat = np.array([ 
    [ones_av, zeros_av, zeros_av], 
    [zeros_av, cos_av, -sin_av], 
    [zeros_av, sin_av, cos_av], 
]) 

return np.einsum('ijk,kmj,mni->ijn', img, y_mat, x_mat) 
+0

이 우아한 솔루션에 감사드립니다. 최근까지도 NumPy를 많이 사용하지는 않았지만'cols'와'rows' 생성에 대한 설명은 매우 가치가 있습니다. :) –

+0

나는 또한 시간을내어'np.einsum()'과 당신이 적어 둔 수식을 이해해야 할 것이다. –

관련 문제