Качество изображения ухудшается при преобразовании кода MMX SSE в C

Я конвертирую MMX SSE в эквивалентный код C. Я почти преобразовал его, но качество изображения, которое я получаю, не соответствует требованиям, или я вижу, что на изображении появляется некоторый шум. Я отлаживаю код за последние 5 дней, но не понимаю, почему это происходит. Я буду очень-очень рад, если вы, ребята, изучите проблему и поможете мне.

ОРИГИНАЛЬНЫЙ КОД SSE:

void unpack_8bit_to_16bit( __m128i *a, __m128i* b0, __m128i* b1 ) 
{
    __m128i zero = _mm_setzero_si128();
    b0 = _mm_unpacklo_epi8( a, zero );
    b1 = _mm_unpackhi_epi8( a, zero );
}

void convolve_cols_3x3( const unsigned char* in, int16_t* out_v, int16_t* out_h, int w, int h )
{
    using namespace std;
    assert( w % 16 == 0 && "width must be multiple of 16!" );
    const int w_chunk  = w/16;
    __m128i*    i0       = (__m128i*)( in );
    __m128i*    i1       = (__m128i*)( in ) + w_chunk*1;
    __m128i*    i2       = (__m128i*)( in ) + w_chunk*2;
    __m128i* result_h  = (__m128i*)( out_h ) + 2*w_chunk;
    __m128i* result_v  = (__m128i*)( out_v ) + 2*w_chunk;
    __m128i* end_input = (__m128i*)( in ) + w_chunk*h;

    for( ; i2 != end_input; i0++, i1++, i2++, result_v+=2, result_h+=2 ) 
    {
        *result_h     = _mm_setzero_si128();
        *(result_h+1) = _mm_setzero_si128();
        *result_v     = _mm_setzero_si128();
        *(result_v+1) = _mm_setzero_si128();
        __m128i ilo, ihi;
        unpack_8bit_to_16bit( *i0, ihi, ilo ); 
        *result_h     = _mm_add_epi16( ihi, *result_h );
        *(result_h+1) = _mm_add_epi16( ilo, *(result_h+1) );
        *result_v     = _mm_add_epi16( *result_v, ihi );
        *(result_v+1) = _mm_add_epi16( *(result_v+1), ilo );
        unpack_8bit_to_16bit( *i1, ihi, ilo );
        *result_v     = _mm_add_epi16( *result_v, ihi );
        *(result_v+1) = _mm_add_epi16( *(result_v+1), ilo );
        *result_v     = _mm_add_epi16( *result_v, ihi );
        *(result_v+1) = _mm_add_epi16( *(result_v+1), ilo );
        unpack_8bit_to_16bit( *i2, ihi, ilo );
        *result_h     = _mm_sub_epi16( *result_h, ihi );
        *(result_h+1) = _mm_sub_epi16( *(result_h+1), ilo );
        *result_v     = _mm_add_epi16( *result_v, ihi );
        *(result_v+1) = _mm_add_epi16( *(result_v+1), ilo );
    }
}

Код, который я преобразовал, приведен ниже

void convolve_cols_3x3( const unsigned char* in, int16_t* out_v, int16_t* out_h, int w, int h )
{
    using namespace std;
    assert( w % 16 == 0 && "width must be multiple of 16!" );
    const int w_chunk  = w/16;

    uint8_t*    i0       = (uint8_t*)( in );
    uint8_t*    i1       = (uint8_t*)( in ) + w_chunk*1*16;
    uint8_t*    i2       = (uint8_t*)( in ) + w_chunk*2*16;
    int16_t* result_h  = (int16_t*)( out_h ) + 2*w_chunk*16;
    int16_t* result_v  = (int16_t*)( out_v ) + 2*w_chunk*16;
    uint8_t* end_input = (uint8_t*)( in ) + w_chunk*h*16;
    for( ; i2 != end_input; i0+= 16, i1+= 16, i2+= 16, result_v+= 16, result_h+= 16 ) 
    {
        for (int i=0; i<8;i++)
        {
            result_h[i]     = 0;
            result_h[i + 8] = 0;
            result_v[i]        = 0;
            result_v[i + 8] = 0;
            result_h[i]     = (int16_t)(i0[i]) + result_h[i] ;
            result_h[i + 8] = (int16_t)(i0[i + 8]) + result_h[i + 8] ;
            result_v[i]     = (int16_t)(i0[i]) + result_v[i] ;
            result_v[i + 8] = (int16_t)(i0[i + 8]) + result_v[i + 8] ;
            result_v[i]     = (int16_t)(i1[i]) + result_v[i] ;
            result_v[i + 8] = (int16_t)(i1[i + 8]) + result_v[i + 8] ;
            result_v[i]     = (int16_t)(i1[i]) + result_v[i] ;
            result_v[i + 8] = (int16_t)(i1[i + 8]) + result_v[i + 8] ;
            result_h[i]     = result_h[i] - (int16_t)(i2[i]);
            result_h[i + 8] = result_h[i + 8] - (int16_t)(i2[i + 8]);
            result_v[i]     = (int16_t)(i2[i]) + result_v[i] ;
            result_v[i + 8] = (int16_t)(i2[i + 8]) + result_v[i + 8] ;
        }
    }
}

Извините, если код не так читаем. w и h представляют ширину и высоту. out_h и out_v — это два параметра, которые позже используются для других целей.


person user1717323    schedule 03.10.2012    source источник
comment
Кажется, в коде SSE есть как минимум одна ошибка: ilo и ihi не инициализируются перед использованием. Или вы случайно удалили unpack_8bit_to_16bit строку?   -  person Paul R    schedule 03.10.2012
comment
@paul:: ihi и ilo передаются по ссылке на функцию unpack_8bit_to_16bit(---), поэтому они инициализируются там в исходном коде sse... а в преобразованном коде я напрямую использовал i0, i1 и i2 для минимизации вызовов функций. ..   -  person user1717323    schedule 03.10.2012
comment
Посмотрите еще раз на код, особенно на эти две строки: __m128i ilo, ihi; *result_h = _mm_add_epi16( ihi, *result_h );   -  person Paul R    schedule 03.10.2012
comment
я очень сожалею, Пол... есть одна строка после __ma128i ilo,ihi. строка unpack_8bit_to_16bit(*i0,ihi,ilo);   -  person user1717323    schedule 03.10.2012


Ответы (1)


Ошибки кажутся в вашей математике указателя и чтении исходных данных. Переменные указателя i0, i1, i2 являются беззнаковыми символами. Такие строки в вашем коде:

 result_h[i + 8] = (int16_t)(i0[i + 8]) + result_h[i + 8] ;

Должно быть так:

result_h[i + 8] = (int16_t)(i0[i*2 + 16]) + result_h[i + 8] ;

Приведение к int16_t не влияет на смещение в квадратных скобках i0. Вы работаете с 16-байтовыми структурами (__m128i), но обращаетесь к ним с 8-байтовыми смещениями. Вы также используете только младшие 8 бит целых чисел, на которые указывают i0 и i1. В исходном коде SSE вы читаете 16-битные целые числа. Окончательный исправленный код может выглядеть так, если вам нужно прочитать 16-битные целые числа перед добавлением:

result_h[i + 8] = *(int16_t *)(&i0[i*2 + 16]) + result_h[i + 8];
person BitBank    schedule 03.10.2012
comment
@user1717323 user1717323 - вы пробовали мое решение? - person BitBank; 11.10.2012