تحويل المخزن المؤقت BYTE (0-255) إلى المخزن المؤقت العائم (0.0-1.0)

StackOverflow https://stackoverflow.com/questions/1043766

سؤال

كيف يمكنني تحويل المخزن المؤقت BYTE (من 0 إلى 255) إلى مخزن مؤقت عائم (من 0.0 إلى 1.0)؟وبطبيعة الحال يجب أن تكون هناك علاقة بين القيمتين، على سبيل المثال:0 في المخزن المؤقت للبايت سيكون .0.f في المخزن المؤقت العائم، و128 في المخزن المؤقت للبايت سيكون .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. ضرب من قبل 1/255 بدلا من القسمة على 255. لا حساب المؤشرات في كل التكرار من الحلقة الداخلية، مجرد حساب القيم الأولية وزيادة لهم. انبسط الحلقة الداخلية عدة مرات. استخدام عمليات ناقلات SIMD إذا تستهدفها يدعم ذلك. لا زيادة ومقارنة مع الحد الأقصى، إنقاص ومقارنة مع صفر بدلا من ذلك.

وشيء من هذا القبيل

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).

فيما يلي إصدار SSE3 من الكود أعلاه الذي يقوم بـ 16 تعويمًا في المرة الواحدة.يتطلب bytepixel و floatpixel أن تكون محاذاة 128 بت، وأن يكون الحجم الإجمالي من مضاعفات 4.لاحظ أن SSE3 المدمج في التحويلات العائمة لن يساعد كثيرًا هنا، حيث سيتطلب ضربًا إضافيًا على أي حال.أعتقد أن هذه هي أقصر طريقة للتعامل مع التعليمات، ولكن إذا لم يكن المترجم ذكيًا بدرجة كافية، فقد ترغب في فتح جدولة الأشياء يدويًا.

/* 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 قيم تعويم precomputed واستخدام قيمة بايت إلى مؤشر هذا الجدول.

ويمكنك أيضا تحسين حلقة قليلا عن طريق إزالة حساب المؤشر ومجرد القيام شيء من هذا القبيل

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 إلى ما يلي:

#!/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 في كل مرة. لا أعرف إذا كان مترجم سوف تكون ذكية بما فيه الكفاية لإزالة هذا. حساب لمرة واحدة وتطبيق ذلك في كل مرة. حتى أفضل، وتعريف بأنها ثابتة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top