Tistory View

Android Develop/image

늘 괴롭히는 이미지의 stride

God Dangchy What should I do? 2017. 10. 30. 05:15

그리 어려운 개념은 아니지만, image의 stride는 늘 나를 괴롭힌다.

image를 다룰 때마다 신경을 안 쓸 수가 없는 부분이며, 정확히 명시되어있지 않는 상황에서는 일일이 테스트를 거쳐 파악을 해야할 경우도 다반사다.



stride는 이미지의 한 줄(row)의 크기를 나타내는 것으로 이미지의 width와는 다른 개념이다. 


이미지를 메모리든 파일이든 저장된 형태를 결정짓는 부분이다.



한 픽셀이 RGB 3바이트로 구성된 이미지를 예를 들어 보자. 이미지의 크기가 3x3으로 가정하고 다음의 구조를 보자.


RGB RGB RGB PPP

RGB RGB RGB PPP

RGB RGB RGB PPP


P : padding


위의 이미지는 width값은 3[RGB]*3[byte per pixel]이지만, 

stride값은 PPP부분의 3byte를 더한 12bytse가 된다.


전체이미지 크기는 12 * 3 = 36bytes가 된다.


Padding을 두는 이유야 당연히 CPU가 메모리에 접근하는 속도때문인데, 실제 4,8,16-bytes등에 잘 맞춰진 이미지는 그렇지 않은 것보다 빠르게 처리된다.

(실제 테스트를 해보면 상당히 빨라지는 것을 알 수 있다.)




위 예제의 이미지 4-bytes로 align되어있다고 표현하는 데,  한 줄(row)이 [4bytes단위에서 끝난다]는 뜻이다.

12 % 4 => 0 이 된다.



다음의 그림을 보자,  동일한 RGB의 3x3 의 이미지이다. 위의 예제에서 padding이 없을 뿐이다.


RGB RGB RGB

RGB RGB RGB

RGB RGB RGB


위 이미지의 width값은 9bytes 이고 stride 또한 9bytes이다.

이미지의 전체 크기는 9bytes * 3 = 27bytes이다.


뭐 어려운 부분은 아니지만, stride값은 저장하는 사람맘이라 다음의 이미지 또한 3x3의 이미지이다.


RGB RGB RGB PPP PPP P

RGB RGB RGB PPP PPP P

RGB RGB RGB PPP PPP P


width = 3bytesx3pixel

stride = 16bytes

이미지 전체 byte크기 = width * stride = 48bytes



위의 이미지는 솔직히 상당히 메모리 낭비가 심한 방식으로 보이지만, 이미지의 크기가 보통 적지 않기 때문에 실제 낭비하는 부분은 그리 많지 않을 수 있다.

예를 좀 심하게 들어서 그렇지, 실제로 16bytes-align으로 처리하는 경우도 많다(SIMD처리용으로 주로 쓰인다. 물론 SIMD에서는 RGBA를 쓰겠지만..)



따라서 이미지를 다룰 경우 단순히 width, height만으로 계산하면, 그림이 물결치거나 한쪽으로 밀리거나 하는 문제가 발생한다.


보통 1,2,4,8,16,32식으로 align을 시키는 데, 이 걸위해 주로 다음과 같은 계산식을 쓴다.


4byte로 align할 경우

stride = w + ( w % 4 != 0 ?  4 - w % 4 : 0 );

8byte로 align할 경우

stride = w + ( w % 8 == 0 ? 8 - w % 8 : 0 );


C-compiler가 이 정도의 코드는 알아서 최적화를 하지만, 좀 쓸데없이 멋있게 만들어야 할 경우 다음의 코드를 쓴다.

(기계어로 컴파일하는 언어가 아닐 경우 다음의 코드를 쓰면 좀 빨라진다.)


4byte로 align할 경우

stride = w + ( (w & 3 ) ? : 4 - ( w & 3 ) : 0 );

8byte로 align할 경우

stride = w + ( ( w & 7 )  ? 8 - ( w & 7 ) : 0 );




이미지를 다를 때는 다음의 값을 미리 계산해 두고 사용하면 편하다. 또한 오류를 줄일 때도 도움이 된다.


bytePerPixel

 픽셀당 byte수

 width 

 이미지 폭

 height

 이미지 높이

 stride 

 width * bytePerPixel + padding

 이미지의 전체 크기

 stride * height

 

 


uint32_t bytePerPixel = 3;

uint32_t width            = 640;

uint32_t height           = 480;

uint32_t padding         = width + ( width % 4 ? 4 - width % 4 : 0  )

uint32_t stride            = width * bytePerPixel + padding;

uint32_t imageSize     = stride * height;



필자의 경우 웬만하면 RGBA식으로 4byte로 끝날수 밖에 없는 이미지로 사용하지만(무조건4byte-align될 수 밖에 없다.), 다른 소스에서 넘어오는 것을 처리하려면, 어쩔 수 없이, 다시 풀어야 해서 이렇게 복붙용으로 만들어 둔다.




주의 사항

stride는 늘 width보다 같거나 크다.

stride값을 byte단위로 주로쓰지만 pixel단위로 표현할 경우도 적지 않다.

stride값은 저장하는 사람이 정하기 나름이므로 이미지를 다루는 모든 상황에서 늘 고려해야 한다.

다른 표현으로 pitch라는 표현도 있다. (같은 표현이다.)


이 포스팅만으로는 별로 도움이 안되므로, 다음에 언젠가 다음의 내용으로 포스팅을 하겠다.

OpenGLES에서 stride적용하기

안드로이드 YV12에서의 stride



혹시나 해서 이 글을 보고 공부하는 친구가 있을 지 몰라 한가지 더 이야기 하면...

4byte-aligned의 또다른 의미는 이미지의 시작하는 첫 픽셀 부분[메모리 시작번지]도 4로 나누어 져야한다.

실제 메모리는 4byte로 처리를 많이 하는데, 16byte-aligned일 경우는 좀 더 다른 함수를 써야 한다.



Replies
Reply Write