바이트 버퍼 (0-255)를 플로트 버퍼로 변환 (0.0-1.0)
-
20-08-2019 - |
문제
바이트 버퍼 (0 ~ 255)를 플로트 버퍼 (0.0 ~ 1.0)로 변환하려면 어떻게해야합니까? 물론 두 값 사이에는 관계가 있어야합니다. 예를 들어 : 0은 바이트 버퍼에서 0.0.f가 플로트 버퍼에서 .0.f, 바이트 버퍼는 플로트 버퍼에서 .5f, 바이트 버퍼의 255는 1.f가됩니다. 플로트 버퍼.
실제로 이것은 내가 가진 코드입니다.
for (int y=0;y<height;y++) {
for (int x=0;x<width;x++) {
float* floatpixel = floatbuffer + (y * width + x) * 4;
BYTE* bytepixel = (bytebuffer + (y * width + x) * 4);
floatpixel[0] = bytepixel[0]/255.f;
floatpixel[1] = bytepixel[1]/255.f;
floatpixel[2] = bytepixel[2]/255.f;
floatpixel[3] = 1.0f; // A
}
}
이것은 매우 느리게 실행됩니다. 내 친구가 전환 테이블을 사용하라고 제안했지만 다른 사람이 다른 접근 방식을 줄 수 있는지 알고 싶었습니다.
해결책
조회 테이블을 사용하든 아니든, 코드는 실제로 모든 루프 반복을 많이 할 필요가없는 많은 작업을 수행하고 있습니다.
포인터가 제한을 제한하고 Const에서만 읽은 포인터를 선언하십시오. 내부 루프의 각 반복에서 포인터를 계산하지 말고 초기 값을 계산하고 증가시키지 마십시오. 내부 루프를 몇 번 뽑으십시오. 대상이 지원하는 경우 벡터 SIMD 작업을 사용하십시오. 최대 값과 증가하고 비교하지 말고 대신 0과 비교하십시오.
같은 것
float* restrict floatpixel = floatbuffer;
BYTE const* restrict bytepixel = bytebuffer;
for( int size = width*height; size > 0; --size )
{
floatpixel[0] = bytepixel[0]*(1.f/255.f);
floatpixel[1] = bytepixel[1]*(1.f/255.f);
floatpixel[2] = bytepixel[2]*(1.f/255.f);
floatpixel[3] = 1.0f; // A
floatpixel += 4;
bytepixel += 4;
}
시작이 될 것입니다.
다른 팁
나는 이것이 오래된 질문이라는 것을 알고 있지만, 아무도 IEEE 플로트 표현을 사용하여 해결책을주지 않았기 때문에 여기에 하나가 있습니다.
// Use three unions instead of one to avoid pipeline stalls
union { float f; uint32_t i; } t, u, v, w;
t.f = 32768.0f;
float const b = 256.f / 255.f;
for(int size = width * height; size > 0; --size)
{
u.i = t.i | bytepixel[0]; floatpixel[0] = (u.f - t.f) * b;
v.i = t.i | bytepixel[1]; floatpixel[1] = (v.f - t.f) * b;
w.i = t.i | bytepixel[2]; floatpixel[2] = (w.f - t.f) * b;
floatpixel[3] = 1.0f; // A
floatpixel += 4;
bytepixel += 4;
}
이것은 그 이상입니다 두 배 빠른 로서 int
에게 float
내 컴퓨터의 변환 (Core 2 Duo CPU).
다음은 한 번에 16 개의 플로트를 수행하는 위 코드의 SSE3 버전입니다. 필요합니다 bytepixel
그리고 floatpixel
128 비트 정렬되고 총 크기는 4의 배수입니다. 나는 이것이 지시적으로 진행하는 가장 짧은 방법이라고 생각하지만, 컴파일러가 충분히 영리하지 않으면 잠복하고 손으로 일정을 잡기를 원할 수 있습니다.
/* Magic values */
__m128i zero = _mm_set_epi32(0, 0, 0, 0);
__m128i magic1 = _mm_set_epi32(0xff000000, 0xff000000, 0xff000000, 0xff000000);
__m128i magic2 = _mm_set_epi32(0x47004700, 0x47004700, 0x47004700, 0x47004700);
__m128 magic3 = _mm_set_ps(32768.0f, 32768.0f, 32768.0f, 32768.0f);
__m128 magic4 = _mm_set_ps(256.0f / 255.0f, 256.0f / 255.0f, 256.0f / 255.0f, 256.0f / 255.0f);
for(int size = width * height / 4; size > 0; --size)
{
/* Load bytes in vector and force alpha value to 255 so that
* the output will be 1.0f as expected. */
__m128i in = _mm_load_si128((__m128i *)bytepixel);
in = _mm_or_si128(in, magic1);
/* Shuffle bytes into four ints ORed with 32768.0f and cast
* to float (the cast is free). */
__m128i tmplo = _mm_unpacklo_epi8(in, zero);
__m128i tmphi = _mm_unpackhi_epi8(in, zero);
__m128 in1 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmplo, magic2));
__m128 in2 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmplo, magic2));
__m128 in3 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmphi, magic2));
__m128 in4 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmphi, magic2));
/* Subtract 32768.0f and multiply by 256.0f/255.0f */
__m128 out1 = _mm_mul_ps(_mm_sub_ps(in1, magic3), magic4);
__m128 out2 = _mm_mul_ps(_mm_sub_ps(in2, magic3), magic4);
__m128 out3 = _mm_mul_ps(_mm_sub_ps(in3, magic3), magic4);
__m128 out4 = _mm_mul_ps(_mm_sub_ps(in4, magic3), magic4);
/* Store 16 floats */
_mm_store_ps(floatpixel, out1);
_mm_store_ps(floatpixel + 4, out2);
_mm_store_ps(floatpixel + 8, out3);
_mm_store_ps(floatpixel + 12, out4);
floatpixel += 16;
bytepixel += 16;
}
편집하다: 사용하여 정확도를 향상시킵니다 (f + c/b) * b
대신에 f * b + c
.
편집하다: SSE3 버전을 추가하십시오.
이를 위해 정적 조회 테이블을 사용하십시오. 컴퓨터 그래픽 회사에서 일할 때 우리는 프로젝트와 연결된 하드 코드 조회 테이블을 얻었습니다.
병목 현상이 무엇인지 알아야합니다.
- 데이터 테이블을 '잘못된'방향으로 반반하면 끊임없이 캐시 미스를 누르십시오. 조회는 그 일을 해결하는 데 도움이되지 않습니다.
- 프로세서가 찾는 것보다 스케일링이 느려지면 조회 테이블이 캐시에 맞는 경우 찾아 보면 성능을 향상시킬 수 있습니다.
또 다른 팁 :
struct Scale {
BYTE operator()( const float f ) const { return f * 1./255; }
};
std::transform( float_table, float_table + itssize, floatpixel, Scale() );
예, 조회 테이블은 루프에서 많은 부서를 수행하는 것보다 확실히 빠릅니다. 256 미리 계산 된 플로트 값의 테이블을 생성하고 바이트 값을 사용하여 해당 테이블을 색인하십시오.
인덱스 계산을 제거하여 루프를 조금 최적화하고 다음과 같은 작업을 수행 할 수도 있습니다.
float *floatpixel = floatbuffer;
BYTE *bytepixel = bytebuffer;
for (...) {
*floatpixel++ = float_table[*bytepixel++];
*floatpixel++ = float_table[*bytepixel++];
*floatpixel++ = float_table[*bytepixel++];
*floatpixel++ = 1.0f;
}
룩업 테이블은 변환하는 가장 빠른 방법입니다 :) 여기에서 간다 :
byte_to_float.h 파일을 생성하는 Python 코드 :
#!/usr/bin/env python
def main():
print "static const float byte_to_float[] = {"
for ii in range(0, 255):
print "%sf," % (ii/255.0)
print "1.0f };"
return 0
if __name__ == "__main__":
main()
전환을 얻기위한 C ++ 코드 :
floatpixel[0] = byte_to_float[ bytepixel[0] ];
단순하지 않습니까?
매번 1/255를 계산하지 마십시오. 컴파일러가 이것을 제거하기에 충분히 똑똑할지 모르겠습니다. 한 번 계산하고 매번 다시 적용하십시오. 더 좋은 점은 상수로 정의하십시오.