SSEで画像処理 --- グレースケール化
下記記事にてSSEで簡単な画像処理をしてみました。
SSEで画像処理 --- 導入 - 何でもプログラミング
今回はカラー画像をグレースケール化する処理を実装してみたいと思います。
入力はRGBA32bitを仮定しています。(RGB24bitの場合、48Byte毎に処理する必要があり、さらに複雑になります。)
float化して計算
R、G、Bを各々分解し、係数を掛けて加算を繰り返します。
簡単のため、RGB各々にグレー値を設定して返す内容になっています。(Gray8bitで返す場合は、4 * 16Byteの幅でループを回して結果を詰める必要があります。)
aligned_vector<byte> ToGray(const aligned_vector<byte>& src) { aligned_vector<byte> dst(src.size()); // 係数 __m128 rc = _mm_set1_ps(0.299f); __m128 gc = _mm_set1_ps(0.587f); __m128 bc = _mm_set1_ps(0.114f); // マッピング配列(RGBA -> R32, G32, B32) __m128i mapR = _mm_setr_epi8(0, -1, -1, -1, 4, -1, -1, -1, 8, -1, -1, -1, 12, -1, -1, -1); __m128i mapG = _mm_setr_epi8(1, -1, -1, -1, 5, -1, -1, -1, 9, -1, -1, -1, 13, -1, -1, -1); __m128i mapB = _mm_setr_epi8(2, -1, -1, -1, 6, -1, -1, -1, 10, -1, -1, -1, 14, -1, -1, -1); for (int i = 0; i < (int)src.size(); i += 16) { __m128i s = _mm_load_si128((__m128i*)(src.data() + i)); // RGBA -> R32, G32, B32 __m128i ri = _mm_shuffle_epi8(s, mapR); __m128i gi = _mm_shuffle_epi8(s, mapG); __m128i bi = _mm_shuffle_epi8(s, mapB); // float化 __m128 r = _mm_cvtepi32_ps(ri); __m128 g = _mm_cvtepi32_ps(gi); __m128 b = _mm_cvtepi32_ps(bi); // 0.299 * R + 0.587 * G + 0.114 * B __m128 gray = _mm_mul_ps(rc, r); gray = _mm_add_ps(gray, _mm_mul_ps(gc, g)); gray = _mm_add_ps(gray, _mm_mul_ps(bc, b)); // int化 __m128i grayi = _mm_cvttps_epi32(gray); // RGB各々にgray値設定 (gray, 0, 0, 0) -> (gray, gray, gray, 0) __m128i d = _mm_add_epi32(grayi, _mm_slli_epi32(grayi, 8)); d = _mm_add_epi32(d, _mm_slli_epi32(grayi, 16)); _mm_store_si128((__m128i*)(dst.data() + i), d); } return dst; }
整数演算のみで計算
疑似的に小数演算が出来るよう、大きな定数倍した状態で計算し、最後にその定数で割ります。
定数が大きすぎるとオーバーフローが起きてしまうため、今回は適当に22bitのシフトで計算しています。
aligned_vector<byte> ToGray(const aligned_vector<byte>& src) { aligned_vector<byte> dst(src.size()); // 係数(小数の係数を定数倍) __m128i rc = _mm_set1_epi32((int)((1 << 22) * 0.299)); __m128i gc = _mm_set1_epi32((int)((1 << 22) * 0.587)); __m128i bc = _mm_set1_epi32((int)((1 << 22) * 0.114)); __m128i mapR = _mm_setr_epi8(0, -1, -1, -1, 4, -1, -1, -1, 8, -1, -1, -1, 12, -1, -1, -1); __m128i mapG = _mm_setr_epi8(1, -1, -1, -1, 5, -1, -1, -1, 9, -1, -1, -1, 13, -1, -1, -1); __m128i mapB = _mm_setr_epi8(2, -1, -1, -1, 6, -1, -1, -1, 10, -1, -1, -1, 14, -1, -1, -1); for (int i = 0; i < (int)src.size(); i += 16) { __m128i s = _mm_load_si128((__m128i*)(src.data() + i)); __m128i r = _mm_shuffle_epi8(s, mapR); __m128i g = _mm_shuffle_epi8(s, mapG); __m128i b = _mm_shuffle_epi8(s, mapB); __m128i gray = _mm_mullo_epi32(rc, r); gray = _mm_add_epi32(gray, _mm_mullo_epi32(gc, g)); gray = _mm_add_epi32(gray, _mm_mullo_epi32(bc, b)); // 定数倍されているものを、元に戻す gray = _mm_srli_epi32(gray, 22); __m128i d = _mm_add_epi32(gray, _mm_slli_epi32(gray, 8)); d = _mm_add_epi32(d, _mm_slli_epi32(gray, 16)); _mm_store_si128((__m128i*)(dst.data() + i), d); } return dst; }