Я писал некоторые оптимизации для пороговой функции OpenCV, для устройств ARM (мобильные телефоны). Он должен работать как на Android, так и на iPhone.
Однако у меня нет устройства для тестирования, поэтому я ищу добровольцев, чтобы немного помочь. Если это мотивирует вас больше, я планирую отправить его в OpenCV для интеграции в основной репозиторий.
Меня интересовала бы правильность кода, и если это будет работать, как предполагалось, некоторые статистические данные для исходной/оптимизированной производительности. Не забудьте просмотреть все сценарии.
Итак, вот код. Чтобы запустить его, введите opencv/modules/imgproc/src/thresh.cpp
в строке 228 (начиная с 2.4.2) - чуть ниже блока SSE и перекомпилируйте OpenCV.
Также добавьте эту строку в начало файла
#include <arm_neon.h>
Тело основного кода:
#define CV_USE_NEON 1
#if CV_USE_NEON
//if( checkHardwareSupport(CV_CPU_ARM_NEON) )
if( true )
{
uint8x16_t thresh_u = vdupq_n_u8(thresh);
uint8x16_t maxval_ = vdupq_n_u8(maxval);
j_scalar = roi.width & -8;
for( i = 0; i < roi.height; i++ )
{
const uchar* src = (const uchar*)(_src.data + _src.step*i);
uchar* dst = (uchar*)(_dst.data + _dst.step*i);
switch( type )
{
case THRESH_BINARY:
for( j = 0; j <= roi.width - 32; j += 32 )
{
uint8x16_t v0, v1;
v0 = vld1q_u8 ( src + j );
v1 = vld1q_u8 ( src + j + 16 );
v0 = vcgtq_u8 ( v0, thresh_u );
v1 = vcgtq_u8 ( v1, thresh_u );
v0 = vandq_u8 ( v0, maxval_ );
v1 = vandq_u8 ( v1, maxval_ );
vst1q_u8 ( dst + j, v0 );
vst1q_u8 ( dst + j + 16, v1 );
}
for( ; j <= roi.width - 8; j += 8 )
{
uint8x8_t v2;
v2 = vld1_u8( src + j );
v2 = vcgt_u8 ( v2, vget_low_s8 ( thresh_u ) );
v2 = vand_u8 ( v2, vget_low_s8 ( maxval_ ) );
vst1_u8 ( dst + j, v2 );
}
break;
case THRESH_BINARY_INV:
for( j = 0; j <= roi.width - 32; j += 32 )
{
uint8x16_t v0, v1;
v0 = vld1q_u8 ( src + j );
v1 = vld1q_u8 ( src + j + 16 );
v0 = vcleq_u8 ( v0, thresh_u );
v1 = vcleq_u8 ( v1, thresh_u );
v0 = vandq_u8 ( v0, maxval_ );
v1 = vandq_u8 ( v1, maxval_ );
vst1q_u8 ( dst + j, v0 );
vst1q_u8 ( dst + j + 16, v1 );
}
for( ; j <= roi.width - 8; j += 8 )
{
uint8x8_t v2;
v2 = vld1_u8( src + j );
v2 = vcle_u8 ( v2, vget_low_s8 ( thresh_u ) );
v2 = vand_u8 ( v2, vget_low_s8 ( maxval_ ) );
vst1_u8 ( dst + j, v2 );
}
break;
case THRESH_TRUNC:
for( j = 0; j <= roi.width - 32; j += 32 )
{
uint8x16_t v0, v1;
v0 = vld1q_u8 ( src + j );
v1 = vld1q_u8 ( src + j + 16 );
v0 = vminq_u8 ( v0, thresh_u );
v1 = vminq_u8 ( v1, thresh_u );
vst1q_u8 ( dst + j, v0 );
vst1q_u8 ( dst + j + 16, v1 );
}
for( ; j <= roi.width - 8; j += 8 )
{
uint8x8_t v2;
v2 = vld1_u8( src + j );
v2 = vmin_u8 ( v2, vget_low_s8 ( thresh_u ) );
vst1_u8 ( dst + j, v2 );
}
break;
case THRESH_TOZERO:
for( j = 0; j <= roi.width - 32; j += 32 )
{
uint8x16_t v0, v1;
v0 = vld1q_u8 ( src + j );
v1 = vld1q_u8 ( src + j + 16 );
v0 = vandq_u8 ( vcgtq_u8 ( v0, thresh_u ), vmaxq_u8 ( v0, thresh_u ) );
v1 = vandq_u8 ( vcgtq_u8 ( v1, thresh_u ), vmaxq_u8 ( v1, thresh_u ) );
vst1q_u8 ( dst + j, v0 );
vst1q_u8 ( dst + j + 16, v1 );
}
for( ; j <= roi.width - 8; j += 8 )
{
uint8x8_t v2;
v2 = vld1_u8 ( src + j );
v2 = vand_u8 ( vcgt_u8 ( v2, vget_low_s8(thresh_u) ), vmax_u8 ( v2, vget_low_s8(thresh_u) ) );
vst1_u8 ( dst + j, v2 );
}
break;
case THRESH_TOZERO_INV:
for( j = 0; j <= roi.width - 32; j += 32 )
{
uint8x16_t v0, v1;
v0 = vld1q_u8 ( src + j );
v1 = vld1q_u8 ( src + j + 16 );
v0 = vandq_u8 ( vcleq_u8 ( v0, thresh_u ), vminq_u8 ( v0, thresh_u ) );
v1 = vandq_u8 ( vcleq_u8 ( v1, thresh_u ), vminq_u8 ( v1, thresh_u ) );
vst1q_u8 ( dst + j, v0 );
vst1q_u8 ( dst + j + 16, v1 );
}
for( ; j <= roi.width - 8; j += 8 )
{
uint8x8_t v2;
v2 = vld1_u8 ( src + j );
v2 = vand_u8 ( vcle_u8 ( v2, vget_low_s8(thresh_u) ), vmin_u8 ( v2, vget_low_s8(thresh_u) ) );
vst1_u8 ( dst + j, v2 );
}
break;
}
}
}
#endif