Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/2026.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ <h5>Removing</h5>
<li>Function SimdFillBgra.</li>
<li>Function SimdHogDirectionHistograms.</li>
<li>C++ wrapper Simd::HogDirectionHistograms.</li>
<li>Function SimdHogExtractFeatures.</li>
<li>C++ wrapper Simd::HogExtractFeatures.</li>
</ul>

<h4>Python wrapper</h4>
Expand Down Expand Up @@ -93,6 +95,7 @@ <h5>Removing</h5>
<li>Tests for verifying functionality of function FillBgr.</li>
<li>Tests for verifying functionality of function FillBgra.</li>
<li>Tests for verifying functionality of function HogDirectionHistograms.</li>
<li>Tests for verifying functionality of function HogExtractFeatures.</li>
</ul>

<h4>Documentation</h4>
Expand Down
2 changes: 0 additions & 2 deletions src/Simd/SimdAvx2.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,6 @@ namespace Simd
void AbsSecondDerivativeHistogram(const uint8_t *src, size_t width, size_t height, size_t stride,
size_t step, size_t indent, uint32_t * histogram);

void HogExtractFeatures(const uint8_t * src, size_t stride, size_t width, size_t height, float * features);

void HogDeinterleave(const float * src, size_t srcStride, size_t width, size_t height, size_t count, float ** dst, size_t dstStride);

void HogFilterSeparable(const float * src, size_t srcStride, size_t width, size_t height, const float * rowFilter, size_t rowSize, const float * colFilter, size_t colSize, float * dst, size_t dstStride, int add);
Expand Down
295 changes: 0 additions & 295 deletions src/Simd/SimdAvx2Hog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,307 +23,12 @@
*/
#include "Simd/SimdStore.h"
#include "Simd/SimdArray.h"
#include "Simd/SimdUnpack.h"

namespace Simd
{
#ifdef SIMD_AVX2_ENABLE
namespace Avx2
{
class HogFeatureExtractor
{
static const size_t C = 8;
static const size_t Q = 9;
static const size_t Q2 = 18;

typedef Array<int> Array32i;
typedef Array<float> Array32f;

size_t _sx, _sy, _hs;

__m256i _pos[5];
__m256 _cos[5], _sin[5];
__m128 _kx[8], _ky[8];
__m256i _Q, _Q2;

Array32i _index;
Array32f _value;
Array32f _buffer;
Array32f _histogram;
Array32f _norm;

void Init(size_t w, size_t h)
{
_sx = w / C;
_hs = _sx + 2;
_sy = h / C;
for (int i = 0; i < 5; ++i)
{
_cos[i] = _mm256_set1_ps((float)::cos(i*M_PI / Q));
_sin[i] = _mm256_set1_ps((float)::sin(i*M_PI / Q));
_pos[i] = _mm256_set1_epi32(i);
}
for (int i = 0; i < C; ++i)
{
float k0 = float((15 - i * 2) / 16.0f);
float k1 = 1.0f - k0;
_kx[i] = _mm_setr_ps(k0, k1, k0, k1);
_ky[i] = _mm_setr_ps(k0, k0, k1, k1);
}
_Q = _mm256_set1_epi32(Q);
_Q2 = _mm256_set1_epi32(Q2);

_index.Resize(w);
_value.Resize(w);
_buffer.Resize((_sx + 1) * 4 * Q2);
_histogram.Resize((_sx + 2)*(_sy + 2)*Q2);
_norm.Resize((_sx + 2)*(_sy + 2));
}

template <bool align> SIMD_INLINE void GetHistogram(const __m256 & dx, const __m256 & dy, size_t col)
{
__m256 _0 = _mm256_set1_ps(-0.0f);
__m256 adx = _mm256_andnot_ps(_0, dx);
__m256 ady = _mm256_andnot_ps(_0, dy);
__m256 bestDot = _mm256_fmadd_ps(adx, _cos[0], _mm256_mul_ps(ady, _sin[0]));
__m256i bestIndex = _pos[0];
for (int i = 1; i < 5; ++i)
{
__m256 dot = _mm256_fmadd_ps(adx, _cos[i], _mm256_mul_ps(ady, _sin[i]));
__m256 mask = _mm256_cmp_ps(dot, bestDot, _CMP_GT_OS);
bestDot = _mm256_max_ps(dot, bestDot);
bestIndex = _mm256_blendv_epi8(bestIndex, _pos[i], _mm256_castps_si256(mask));
}
__m256i maskDx = _mm256_castps_si256(_mm256_cmp_ps(dx, _mm256_setzero_ps(), _CMP_LT_OS));
bestIndex = _mm256_blendv_epi8(bestIndex, _mm256_sub_epi32(_Q, bestIndex), maskDx);

__m256i maskDy = _mm256_castps_si256(_mm256_cmp_ps(dy, _mm256_setzero_ps(), _CMP_LT_OS));
__m256i corr = _mm256_and_si256(_mm256_castps_si256(_mm256_cmp_ps(adx, _mm256_setzero_ps(), _CMP_EQ_OS)), K32_00000001);
bestIndex = _mm256_blendv_epi8(bestIndex, _mm256_sub_epi32(_Q2, _mm256_add_epi32(bestIndex, corr)), maskDy);

bestIndex = _mm256_andnot_si256(_mm256_cmpeq_epi32(bestIndex, _Q2), bestIndex);

Store<align>((__m256i*)(_index.data + col), bestIndex);
Store<align>(_value.data + col, Sqrt<0>(_mm256_fmadd_ps(adx, adx, _mm256_mul_ps(ady, ady))));
}

template <int part> SIMD_INLINE __m256 ConvertDifference(const __m128i & a, const __m128i & b)
{
return _mm256_cvtepi32_ps(_mm256_cvtepi16_epi32(Sse41::SubUnpackedU8<part>(a, b)));
}

template <bool align> SIMD_INLINE void GetHistogram(const uint8_t * src, size_t stride, size_t col)
{
const uint8_t * s = src + col;
__m128i t = Sse41::Load<false>((__m128i*)(s - stride));
__m128i l = Sse41::Load<false>((__m128i*)(s - 1));
__m128i r = Sse41::Load<false>((__m128i*)(s + 1));
__m128i b = Sse41::Load<false>((__m128i*)(s + stride));
GetHistogram<align>(ConvertDifference<0>(r, l), ConvertDifference<0>(b, t), col + 0);
GetHistogram<align>(ConvertDifference<1>(r, l), ConvertDifference<1>(b, t), col + 8);
}

void AddRowToBuffer(const uint8_t * src, size_t stride, size_t row, size_t width, size_t aligned)
{
const uint8_t * s = src + stride * row;
GetHistogram<false>(s, stride, 1);
for (size_t col = HA; col < aligned; col += HA)
GetHistogram<false>(s, stride, col);
GetHistogram<false>(s, stride, width - 1 - HA);

__m128 ky = _ky[(row + 4) & 7];
__m128 * buffer = (__m128*)_buffer.data;
for (size_t col = 1, n = C, i = 5; col < width - 1; i = 0, n = Simd::Min<size_t>(C, width - col - 1))
{
for (; i < n; ++i, ++col)
{
int index = _index[col];
__m128 value = _mm_set1_ps(_value[col]);
buffer[index] = _mm_fmadd_ps(_mm_mul_ps(ky, _kx[i]), value, buffer[index]);
}
buffer += Q2;
}
}

void AddToHistogram(size_t row, size_t width, size_t height)
{
typedef float f18_t[18];

float * src = _buffer.data;
f18_t * h0 = (f18_t*)_histogram.data + row * _hs;
f18_t * h1 = h0 + _hs;

for (size_t cell = 0; cell <= width; ++cell)
{
for (size_t i = 0; i < 16; i += F)
{
const float * s = src + i * 4;
__m256 a0 = Load<true>(s + 0x00, s + 0x10);
__m256 a1 = Load<true>(s + 0x04, s + 0x14);
__m256 a2 = Load<true>(s + 0x08, s + 0x18);
__m256 a3 = Load<true>(s + 0x0C, s + 0x1C);
__m256 b0 = _mm256_unpacklo_ps(a0, a2);
__m256 b1 = _mm256_unpackhi_ps(a0, a2);
__m256 b2 = _mm256_unpacklo_ps(a1, a3);
__m256 b3 = _mm256_unpackhi_ps(a1, a3);
Store<false>(h0[0] + i, _mm256_add_ps(Load<false>(h0[0] + i), _mm256_unpacklo_ps(b0, b2)));
Store<false>(h0[1] + i, _mm256_add_ps(Load<false>(h0[1] + i), _mm256_unpackhi_ps(b0, b2)));
Store<false>(h1[0] + i, _mm256_add_ps(Load<false>(h1[0] + i), _mm256_unpacklo_ps(b1, b3)));
Store<false>(h1[1] + i, _mm256_add_ps(Load<false>(h1[1] + i), _mm256_unpackhi_ps(b1, b3)));
}
__m128 * ps = (__m128*)src;
__m128 s0 = _mm_add_ps(_mm_unpacklo_ps(ps[16], ps[17]), Sse41::Load(h0[0] + 16, h0[1] + 16));
__m128 s1 = _mm_add_ps(_mm_unpackhi_ps(ps[16], ps[17]), Sse41::Load(h1[0] + 16, h1[1] + 16));
Sse41::StoreHalf<0>(h0[0] + 16, s0);
Sse41::StoreHalf<1>(h0[1] + 16, s0);
Sse41::StoreHalf<0>(h1[0] + 16, s1);
Sse41::StoreHalf<1>(h1[1] + 16, s1);
h0++;
h1++;
src += 72;
}
_buffer.Clear();
}

void EstimateHistogram(const uint8_t * src, size_t stride, size_t width, size_t height)
{
_histogram.Clear();

size_t aligned = AlignHi(width - 1, HA) - HA;

_buffer.Clear();
for (size_t row = 1; row < 4; ++row)
AddRowToBuffer(src, stride, row, width, aligned);
AddToHistogram(0, _sx, _sy);
for (size_t row = 4, cell = 1; row < height - 4; ++row)
{
AddRowToBuffer(src, stride, row, width, aligned);
if ((row & 7) == 3)
AddToHistogram(cell++, _sx, _sy);
}
for (size_t row = height - 4; row < height - 1; ++row)
AddRowToBuffer(src, stride, row, width, aligned);
AddToHistogram(_sy, _sx, _sy);
}

SIMD_INLINE float GetNorm(const float * src)
{
__m256 norm = _mm256_add_ps(_mm256_loadu_ps(src), _mm256_loadu_ps(src + Q));
norm = _mm256_mul_ps(norm, norm);
norm = _mm256_hadd_ps(norm, norm);
norm = _mm256_hadd_ps(norm, norm);
float buf[8];
_mm256_storeu_ps(buf, norm);
return buf[0] + buf[4] + Simd::Square(src[Q - 1] + src[Q2 - 1]);
}

void EstimateNorm()
{
_norm.Clear();
for (size_t y = 0, i = 0; y < _sy; y++)
{
const float * h = _histogram.data + ((y + 1)*_hs + 1)*Q2;
float * n = _norm.data + (y + 1)*_hs + 1;
for (size_t x = 0; x < _sx; x++, i++)
n[x] = GetNorm(h + x * Q2);
}
}

void ExtractFeatures(float * features)
{
__m128 _02 = _mm_set1_ps(0.2f);
__m128 _05 = _mm_set1_ps(0.5f);
__m128 _02357 = _mm_set1_ps(0.2357f);
__m128 eps = _mm_set1_ps(0.0001f);
for (size_t y = 0; y < _sy; y++)
{
float * ph = _histogram.data + ((y + 1)*_hs + 1)*Q2;
for (size_t x = 0; x < _sx; x++)
{
float * dst = features + (y*_sx + x) * 31;

float * p0 = _norm.data + y * _hs + x;
float * p1 = p0 + _hs;
float * p2 = p1 + _hs;

__m128 n = _mm_setr_ps(
p1[1] + p1[2] + p2[1] + p2[2],
p0[1] + p0[2] + p1[1] + p1[2],
p1[0] + p1[1] + p2[0] + p2[1],
p0[0] + p0[1] + p1[0] + p1[1]);

n = _mm_rsqrt_ps(_mm_add_ps(n, eps));

__m128 t = _mm_setzero_ps();

float * src = ph + x * Q2;
for (int o = 0; o < 16; o += 4)
{
__m128 s = _mm_loadu_ps(src);
__m128 h0 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<0>(s), n), _02);
__m128 h1 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<1>(s), n), _02);
__m128 h2 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<2>(s), n), _02);
__m128 h3 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<3>(s), n), _02);
t = _mm_add_ps(t, _mm_add_ps(_mm_add_ps(h0, h1), _mm_add_ps(h2, h3)));
_mm_storeu_ps(dst, _mm_mul_ps(_05, _mm_hadd_ps(_mm_hadd_ps(h0, h1), _mm_hadd_ps(h2, h3))));
dst += 4;
src += 4;
}
{
__m128 h0 = _mm_min_ps(_mm_mul_ps(_mm_set1_ps(*src++), n), _02);
__m128 h1 = _mm_min_ps(_mm_mul_ps(_mm_set1_ps(*src++), n), _02);
t = _mm_add_ps(t, _mm_add_ps(h0, h1));
__m128 h = _mm_hadd_ps(h0, h1);
_mm_storeu_ps(dst, _mm_mul_ps(_05, _mm_hadd_ps(h, h)));
dst += 2;
}

src = ph + x * Q2;
for (int o = 0; o < 8; o += 4)
{
__m128 s = _mm_add_ps(_mm_loadu_ps(src), _mm_loadu_ps(src + Q));
__m128 h0 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<0>(s), n), _02);
__m128 h1 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<1>(s), n), _02);
__m128 h2 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<2>(s), n), _02);
__m128 h3 = _mm_min_ps(_mm_mul_ps(Sse41::Broadcast<3>(s), n), _02);
_mm_storeu_ps(dst, _mm_mul_ps(_05, _mm_hadd_ps(_mm_hadd_ps(h0, h1), _mm_hadd_ps(h2, h3))));
dst += 4;
src += 4;
}
{
__m128 s = _mm_set1_ps(src[0] + src[Q]);
__m128 h = _mm_min_ps(_mm_mul_ps(s, n), _02);
h = _mm_dp_ps(_05, h, 0xF1);
_mm_store_ss(dst++, h);
}
_mm_storeu_ps(dst, _mm_mul_ps(t, _02357));
}
}
}

public:

void Run(const uint8_t * src, size_t stride, size_t width, size_t height, float * features)
{
Init(width, height);

EstimateHistogram(src, stride, width, height);

EstimateNorm();

ExtractFeatures(features);
}
};

void HogExtractFeatures(const uint8_t * src, size_t stride, size_t width, size_t height, float * features)
{
assert(width % 8 == 0 && height % 8 == 0 && width >= 16 && height >= 16);
assert(width >= HA + 2);

HogFeatureExtractor extractor;
extractor.Run(src, stride, width, height, features);
}

SIMD_INLINE void HogDeinterleave(const float * src, size_t count, float ** dst, size_t offset, size_t i)
{
src += i;
Expand Down
2 changes: 0 additions & 2 deletions src/Simd/SimdAvx512bw.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,6 @@ namespace Simd

void Gemm32fNT(size_t M, size_t N, size_t K, const float* alpha, const float* A, size_t lda, const float* B, size_t ldb, const float* beta, float* C, size_t ldc);

void HogExtractFeatures(const uint8_t * src, size_t stride, size_t width, size_t height, float * features);

void HogDeinterleave(const float * src, size_t srcStride, size_t width, size_t height, size_t count, float ** dst, size_t dstStride);

void HogFilterSeparable(const float * src, size_t srcStride, size_t width, size_t height, const float * rowFilter, size_t rowSize, const float * colFilter, size_t colSize, float * dst, size_t dstStride, int add);
Expand Down
Loading