2012-01-08
2011-12-27
2011-12-26
■[Frayer] 更新
- OpenCV2.3に対応
- ブラシテクスチャ実装
- ナビゲータウィンドウに画面回転ボタン、画像反転ボタンを追加
- レイヤー更新処理20%程度高速化
- 新規ファイル作成時、規定サイズを選択できるように変更
- レイヤー平行移動後、マスクを使って画像編集すると編集位置がずれるのを修正
- レイヤー平行移動→Undo→画像編集→Undoで元に戻らないのを修正
- ブラシツールを選択して、選択ブラシがかわるときにブラシウィンドウのスクロール位置がおかしくなるのを修正
- psd形式の書き込みで間違ったデータが出力されることがあったのを修正
- fyd形式の書き込みで間違ったデータが出力されることがあったのを修正
- fydファイルレイヤーが画像サイズの外の範囲にあるときに読み込みエラーが出ていたのを修正
- 選択範囲解除後のUndoがおかしかったのを修正
- 選択範囲をカットして新規レイヤー作成後のUndoがおかしかったのを修正
- レイヤーパネルを右クリック→レイヤー削除でヒストリに登録されなかったのを修正
- レイヤー合成のハードライト、ソフトライト、オーバーレイの計算式がおかしかったのを修正
- レイヤーを結合したときに画像がずれるのを修正
- 透明なレイヤー同士を結合すると正しく結合されないのを修正
- ブラシダイアログでパラメータ変更後、ブラシプレビューが更新されないのを修正
- 右クリックでブラシ選択されるように修正
- エフェクトを掛けるときにマスクを点線表示にするように修正
- マウスホイールでレイヤーウィンドウ・ブラシウィンドウ・パレットウィンドウがスクロールするように修正
- Altを押してもメニューにフォーカスしないように修正
- その他細かい修正
1年近くぶりの更新です。
ブラシテクスチャはこんな感じです。
2011-12-03
■[アセンブラ][SSE][VC++]SSE用いたアルファブレンドの高速化4 アライメントがずれている画像同士のアルファブレンド
↓の続き
http://d.hatena.ne.jp/hiroki0/20090914/1252931917
http://d.hatena.ne.jp/hiroki0/20100320/1269074009
http://d.hatena.ne.jp/hiroki0/20111128/1322504210
今回はアライメントをずらした場合での計測をします。
_mm_loadu_si128などアライメントをあわせる必要がないロード・ストア命令を使用する場合は、
前回と同じ様に何も考えずに読み込んでアルファブレンドを行い、書きこむという順で処理を行うことができます。
しかし、アライメントを合わせる必要があるロード・ストア命令を使用する場合は、
処理方法をアライメントのズレによって変える必要があります。
今回のアルファブレンド処理では、
- 入出力メモリともアライメントが揃っている場合
- 入出力メモリともアライメントから同じだけずれている場合
- 入力、出力メモリのアライメントのずれ量が違う場合
の3通りに分けてコードを実装しました。
1の場合は、前回と同様のコードで処理します。
2の場合は、ずれてる分を処理して入出力ともアライメントを合わせた状態にして、1と同じように処理します。
3の場合は、ずれてる分をシフト演算を使用してずれを合わせる処理をいれて、
ロード・ストア命令がアライメントが合った状態で実行されるようにします。
計測方法
32bit,64bit向けに2通りに実行ファイルを作成、それぞれについて
- SSEを使わない処理
- _mm_loadu_si128でメモリ読み込み、_mm_storeu_si128でメモリ書き込み
- _mm_load_si128でメモリ読み込み、_mm_store_si128でメモリ書き込み
- _mm_load_si128でメモリ読み込み、_mm_stream_si128でメモリ書き込み
の4通りの処理の処理速度を計測する。
前回までは間違えて浮動小数点用の_mm_loadu_ps等を使っていたので、今回は_mm_loadu_si128等を使います。
- チャンネルサイズ8bit 5700x5700BGR画像を2枚読み込み、アルファ値を指定してアルファブレンドを行う。
- 画像の保存、読み込みはopenCV 2.3を用いた。
- アルファブレンド部分の処理のみ処理時間を計測した。
- 与えるアルファチャンネルは、上下ともにアルファ値を均等に分布させる
- アルファブレンドでの入力画像は上下でアライメントのずれ量が合った状態にする。
以下の3通りのように意図的にアライメントをずらして計測を行います。
計測1
環境
- CPU: Core i7 2600 3.40GHz
- OS: Windows 7 64bit
- Mem: 8.00GB(7.73GB usable)
- コンパイラ: Visual Studio 2010
- 最適化オプション: 実行速度 (/O2)
32bit向け実行ファイル
- 入出力画像ともにずらさない
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 675.374 msec | 203.678 msec | 166 msec | 166.106 msec |
| 2回目 | 591.512 msec | 181.441 msec | 175.978 msec | 207.662 msec |
| 3回目 | 563.755 msec | 170.575 msec | 179.13 msec | 165.016 msec |
- 入出力画像とも2ピクセルずつずらす
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 584.079 msec | 184.901 msec | 163.356 msec | 197.957 msec |
| 2回目 | 601.959 msec | 229.62 msec | 169.618 msec | 179.718 msec |
| 3回目 | 576.596 msec | 172.858 msec | 172.926 msec | 238.996 msec |
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 581.447 msec | 215.271 msec | 194.737 msec | 183.298 msec |
| 2回目 | 583.466 msec | 168.634 msec | 175.305 msec | 204.374 msec |
| 3回目 | 585.919 msec | 170.421 msec | 179.532 msec | 177.077 msec |
64bit向け実行ファイル
- 入出力画像ともにずらさない
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 321.546 msec | 178.499 msec | 181.309 msec | 192.388 msec |
| 2回目 | 370.826 msec | 170.399 msec | 159.175 msec | 185.213 msec |
| 3回目 | 311.765 msec | 162.117 msec | 166.067 msec | 167.506 msec |
- 入出力画像とも2ピクセルずつずらす
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 316.697 msec | 172.909 msec | 162.31 msec | 167.428 msec |
| 2回目 | 308.39 msec | 169.355 msec | 163.629 msec | 159.495 msec |
| 3回目 | 312.266 msec | 165.965 msec | 158.519 msec | 173.99 msec |
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 303.656 msec | 175.868 msec | 172.008 msec | 163.839 msec |
| 2回目 | 329.698 msec | 157.363 msec | 171.484 msec | 166.765 msec |
| 3回目 | 297.241 msec | 159.257 msec | 164.473 msec | 163.332 msec |
計測2
環境
- CPU: Core2 Duo E8300 3.00GHZ
- OS: Windows 7 32bit
- Mem: 4.00GM(3.50GB usable)
- コンパイラ: Visual Studio 2010
- 最適化オプション: 実行速度 (/O2)
- 入出力画像ともにずらさない
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 1098.98 msec | 217.239 msec | 191.035 msec | 197.02 msec |
| 2回目 | 1103 msec | 218.563 msec | 190.894 msec | 193.745 msec |
| 3回目 | 1104.15 msec | 218.304 msec | 190.167 msec | 196.236 msec |
- 入出力画像とも2ピクセルずつずらす
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 1106.91 msec | 218.914 msec | 194.721 msec | 199.865 msec |
| 2回目 | 1109.58 msec | 223.114 msec | 196.515 msec | 199.412 msec |
| 3回目 | 1127.07 msec | 216.637 msec | 190.159 msec | 196.093 msec |
| 非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 1119.89 msec | 255.483 msec | 245.189 msec | 243.91 msec |
| 2回目 | 1086.53 msec | 221.121 msec | 210.191 msec | 211.133 msec |
| 3回目 | 1086.65 msec | 221.38 msec | 209.693 msec | 221.721 msec |
まとめ
Core i7 2600では、_mm_loadu_si128を使っても_mm_load_si128とバラツキがあるが同程度という結果だった。
32bit Core2 Duo E8400では、アライメントを合わせた効果がCorei7に比べてよく出ていて
入出力画像とも2ピクセルずつずらした場合には約10%、
入力画像を1ピクセル、出力画像を2ピクセルずらした場合には約5%速くなっていることがわかった。
使用したコード
SSEのアルファブレンド関数はinlineを付けてもインライン展開されなかったのでマクロ関数で実装しています。
#include <iostream> #include <opencv/cv.h> #include <opencv/highgui.h> #include "IETimer.h" union UCvPixel32 { uint32_t value; struct { uint8_t b,g,r,a; }; }; typedef UCvPixel32 UCvPixel; inline uint8_t* GetNextLineAddress(const IplImage* image, uint8_t* addr) { return addr + image->widthStep; } inline uint8_t* GetPixelAddress(const IplImage* image, int x, int y) { assert(image); assert(image->depth == IPL_DEPTH_8U); assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1)); return (uint8_t*)(image->imageData + y*image->widthStep + x*image->nChannels); } inline UCvPixel32* GetNextLineAddress32(const IplImage* image, UCvPixel32* addr) { return (UCvPixel32*)((uint8_t*)addr + image->widthStep); } inline UCvPixel32* GetPixelAddress32(const IplImage* image, int x, int y) { assert(image); assert(image->depth == IPL_DEPTH_8U); assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1)); return (UCvPixel32*)(image->imageData + y*image->widthStep + x*image->nChannels); } #define ALPHABLEND_SSE(dst, under, over) \ { \ __m128i __xmzero = _mm_setzero_si128(); \ __m128i __xmff16 = _mm_set1_epi16(0xff); \ \ __m128i __xmo16, __xmu16; \ __m128i __xmlo, __xmhi; \ __m128i __xmover_alpha, __xmover_ralpha, __xmunder_alpha, __xmunder_ralpha, __xmalpha;\ __m128 __xmf_lo, __xmf_hi, __xmf_alpha_lo, __xmf_alpha_hi; \ \ /*load lo double ward */ \ __xmo16 = _mm_unpacklo_epi8(over, __xmzero); \ __xmu16 = _mm_unpacklo_epi8(under, __xmzero); \ \ int over_a1 = _mm_extract_epi16(__xmo16, 3); \ int over_a2 = _mm_extract_epi16(__xmo16, 7); \ if ((over_a1 == 255 && over_a2 == 255)) { \ __xmlo = __xmo16; \ } else { \ int under_a1 = _mm_extract_epi16(__xmu16, 3); \ int under_a2 = _mm_extract_epi16(__xmu16, 7); \ if (under_a1 == 0 && under_a2 == 0) { \ __xmlo = __xmo16; \ } else if (under_a1 == 0 && under_a2 == 0) { \ __xmlo = __xmu16; \ } else if (under_a1 == 255 && under_a2 == 255) { \ __xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));\ __xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha); \ \ __xmu16 = _mm_mullo_epi16(__xmu16, __xmover_ralpha); /* under_pix * over_ralpha */\ __xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */\ __xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* (under_pix * over_ralpha + over_pix * over_alpha) */\ __xmlo = _mm_srli_epi16(__xmo16, 8); /* (under_pix * over_ralpha + over_pix * over_alpha) >> 8 */\ __xmlo = _mm_insert_epi16(__xmlo, 255, 3); /* write alpha */ \ __xmlo = _mm_insert_epi16(__xmlo, 255, 7); /* write alpha */ \ } else { \ __xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));\ __xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha); \ __xmunder_alpha = _mm_shufflelo_epi16(__xmu16, _MM_SHUFFLE(3,3,3,3));\ __xmunder_alpha = _mm_shufflehi_epi16(__xmunder_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmunder_ralpha = _mm_sub_epi16(__xmff16, __xmunder_alpha); \ __xmalpha = _mm_sub_epi16(__xmff16, _mm_srli_epi16( _mm_mullo_epi16(__xmover_ralpha, __xmunder_ralpha), 8)); /* 255 - ((over_ralpha * under_ralpha) >> 8) */\ \ __xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(__xmunder_alpha, __xmover_ralpha), 8), __xmu16); /* under_pix * ((under_alpha * over_ralpha)>>8) */\ __xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */\ __xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* over_pix * over_alpha + (under_pix * under_alpha * over_ralpha)>>8 */\ \ /* calc pixel 1 */ \ __xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmo16, __xmzero) );\ __xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmalpha, __xmzero) );\ __xmf_lo = _mm_mul_ps(__xmf_lo, _mm_rcp_ps(__xmf_alpha_lo)); \ \ /* calc pixel 2 */ \ __xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmo16, __xmzero) );\ __xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmalpha, __xmzero) );\ __xmf_hi = _mm_mul_ps(__xmf_hi, _mm_rcp_ps(__xmf_alpha_hi)); \ \ /* pack to 16bit data */ \ __xmlo = _mm_packs_epi32(_mm_cvtps_epi32(__xmf_lo), _mm_cvtps_epi32(__xmf_hi));\ int a1 = _mm_extract_epi16(__xmalpha, 3); \ __xmlo = _mm_insert_epi16(__xmlo, a1, 3); /* write alpha */ \ int a2 = _mm_extract_epi16(__xmalpha, 7); \ __xmlo = _mm_insert_epi16(__xmlo, a2, 7); /* write alpha */ \ } \ } \ \ /* load hi double ward */ \ __xmo16 = _mm_unpackhi_epi8(over, __xmzero); \ __xmu16 = _mm_unpackhi_epi8(under, __xmzero); \ \ over_a1 = _mm_extract_epi16(__xmo16, 3); \ over_a2 = _mm_extract_epi16(__xmo16, 7); \ if ((over_a1 == 255 && over_a2 == 255)) { \ __xmhi = __xmo16; \ } else { \ int under_a1 = _mm_extract_epi16(__xmu16, 3); \ int under_a2 = _mm_extract_epi16(__xmu16, 7); \ if (under_a1 == 0 && under_a2 == 0) { \ __xmhi = __xmo16; \ } else if (under_a1 == 0 && under_a2 == 0) { \ __xmhi = __xmu16; \ } else if (under_a1 == 255 && under_a2 == 255) { \ __xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));\ __xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha); \ \ __xmu16 = _mm_mullo_epi16(__xmu16, __xmover_ralpha); /* under_pix * over_ralpha */\ __xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */\ __xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* (under_pix * over_ralpha + over_pix * over_alpha) */\ __xmhi = _mm_srli_epi16(__xmo16, 8); /* (under_pix * over_ralpha + over_pix * over_alpha) >> 8 */\ __xmhi = _mm_insert_epi16(__xmhi, 255, 3); /* write alpha */ \ __xmhi = _mm_insert_epi16(__xmhi, 255, 7); /* write alpha */ \ } else { \ __xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));\ __xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha); \ __xmunder_alpha = _mm_shufflelo_epi16(__xmu16, _MM_SHUFFLE(3,3,3,3));\ __xmunder_alpha = _mm_shufflehi_epi16(__xmunder_alpha, _MM_SHUFFLE(3,3,3,3));\ __xmunder_ralpha = _mm_sub_epi16(__xmff16, __xmunder_alpha); \ __xmalpha = _mm_sub_epi16(__xmff16, _mm_srli_epi16( _mm_mullo_epi16(__xmover_ralpha, __xmunder_ralpha), 8)); /* 255 - ((over_ralpha * under_ralpha) >> 8) */\ \ __xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(__xmunder_alpha, __xmover_ralpha), 8), __xmu16); /* under_pix * ((under_alpha * over_ralpha)>>8) */\ __xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */\ __xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* over_pix * over_alpha + (under_pix * ((under_alpha * over_ralpha)>>8) */\ \ /* calc pixel1 */ \ __xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmo16, __xmzero) );\ __xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmalpha, __xmzero) );\ __xmf_lo = _mm_mul_ps(__xmf_lo, _mm_rcp_ps(__xmf_alpha_lo)); \ \ /* calc pixel 2 */ \ __xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmo16, __xmzero) );\ __xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmalpha, __xmzero) );\ __xmf_hi = _mm_mul_ps(__xmf_hi, _mm_rcp_ps(__xmf_alpha_hi)); \ \ /* pack to 16bit data */ \ __xmhi = _mm_packs_epi32(_mm_cvtps_epi32(__xmf_lo), _mm_cvtps_epi32(__xmf_hi));\ int a3 = _mm_extract_epi16(__xmalpha, 3); \ __xmhi = _mm_insert_epi16(__xmhi, a3, 3); /* write alpha */ \ int a4 = _mm_extract_epi16(__xmalpha, 7); \ __xmhi = _mm_insert_epi16(__xmhi, a4, 7); /* write alpha */ \ } \ } \ dst = _mm_packus_epi16(__xmlo, __xmhi); \ } inline void BltLineAlphaBlend( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { int x; for(x = 0; x < blt_width; x++){ //alpha blend if ((over->a == 255) || (under->a == 0)) { dst->value = over->value; } else if (over->a == 0) { dst->value = under->value; } else if (under->a == 255) { uint8_t ra = ~over->a; dst->b = (over->b*over->a + under->b*ra)>>8; dst->g = (over->g*over->a + under->g*ra)>>8; dst->r = (over->r*over->a + under->r*ra)>>8; dst->a = 255; } else { uint8_t ra = ~over->a; uint8_t alpha = 255 - (ra*(255 - under->a)>>8); double inv_alpha = 1.0/alpha; dst->b = (over->b*over->a + ((under->b*under->a*ra)>>8))*inv_alpha; dst->g = (over->g*over->a + ((under->g*under->a*ra)>>8))*inv_alpha; dst->r = (over->r*over->a + ((under->r*under->a*ra)>>8))*inv_alpha; dst->a = alpha; } over++; under++; dst++; } } inline void BltLineAlphaBlend_SSE_unaligned( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { //4ピクセルずつ処理するときのループ回数を求める int nloop = blt_width >> 2; //blt_width / 4 int x; for(x = 0; x < nloop; x++){ __m128i xmunder = _mm_loadu_si128((const __m128i*)under); __m128i xmover = _mm_loadu_si128((const __m128i*)over); __m128i xmdst; ALPHABLEND_SSE(xmdst, xmunder, xmover); _mm_storeu_si128((__m128i*)dst, xmdst); over += 4; under += 4; dst += 4; } //波数を1ピクセルずつ処理 int bn = blt_width & 0x3; //blt_width % 4 BltLineAlphaBlend( dst, under, over, bn); } inline void SubBltLineAlphaBlend_SSE( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); assert (IS_16BYTE_ALIGNMENT(dst) == IS_16BYTE_ALIGNMENT(under)); //4ピクセルずつ処理するときのループ回数を求める int nloop = blt_width >> 2; //blt_width / 4 int x; for(x = 0; x < nloop; x++){ __m128i xmunder = _mm_load_si128((const __m128i*)under); __m128i xmover = _mm_load_si128((const __m128i*)over); __m128i xmdst; ALPHABLEND_SSE(xmdst, xmunder, xmover); _mm_store_si128((__m128i*)dst, xmdst); over += 4; under += 4; dst += 4; } //1ピクセルずつ処理 int bn = blt_width & 0x3; //blt_width % 4 BltLineAlphaBlend( dst, under, over, bn); return; } template <int _SHIFT> void ShiftSubBltLineAlphaBlend_SSE( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { UCvPixel* _dst = dst; const UCvPixel* _over = over; assert(DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); assert(IS_16BYTE_ALIGNMENT(under) && IS_16BYTE_ALIGNMENT(over)); int nshift_pix = _SHIFT >> 2; int nloop = (blt_width >> 2) - 1; //(blt_width / 4) - 1 __m128i xmunder, xmunder0, xmunder1, xmover, xmover0, xmover1; xmunder0 = _mm_load_si128((const __m128i*)under); xmover0 = _mm_load_si128((const __m128i*)over); //_SHIFT byte分処理 //出力メモリを次回からアライメントを合わせて書き込めるようにする。 BltLineAlphaBlend(dst, under, over, nshift_pix); dst = dst + nshift_pix; assert(IS_16BYTE_ALIGNMENT(dst)); // xmunder0 = _mm_srli_si128(xmunder0, _SHIFT); //xmunder0 >> _SHIFT xmover0 = _mm_srli_si128(xmover0, _SHIFT); //xmover0 >> _SHIFT int x; for(x = 0; x < nloop; x++){ // xmunder = _mm_load_si128((const __m128i*)(under + 4)); xmover = _mm_load_si128((const __m128i*)(over + 4)); //読み込んだ分をアライメントを合わせて書き込む時に //合うようにシフト演算でずらしてOR演算で前回の残りと組み合わせる。 xmunder1 = _mm_slli_si128(xmunder, 16 - _SHIFT); //xmunder << (16 - _SHIFT) xmover1 = _mm_slli_si128(xmover, 16 - _SHIFT); //xmmover << (16 - _SHIFT) xmunder1 = _mm_or_si128(xmunder1, xmunder0); xmover1 = _mm_or_si128(xmover1, xmover0); //次回のループで使う分をずらして残しておく xmunder0 = _mm_srli_si128(xmunder, _SHIFT); //xmunder >> _SHIFT xmover0 = _mm_srli_si128(xmover, _SHIFT); //xmover >> _SHIFT __m128i xmdst; ALPHABLEND_SSE(xmdst, xmunder1, xmover1); _mm_store_si128((__m128i*)dst, xmdst); over += 4; under += 4; dst += 4; } //波数を1ピクセルずつ処理 int bn = blt_width - (nloop << 2) - nshift_pix; BltLineAlphaBlend( dst, under + nshift_pix, over + nshift_pix, bn); return; } inline void BltLineAlphaBlend_SSE( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); if (IS_16BYTE_ALIGNMENT(dst) && IS_16BYTE_ALIGNMENT(under)) { //入出力ともアライメントがあってるならSSEで処理 SubBltLineAlphaBlend_SSE(dst, under, over, blt_width); return; } int dif_d = DIFF_16BYTE_ALIGNMENT(dst); //アライメントのずれ量を求める int dif_s = DIFF_16BYTE_ALIGNMENT(under); assert((dif_d & 0x3) == 0); //1pixel、4byteなので4の倍数になるはず assert((dif_s & 0x3) == 0); // if (dif_d == dif_s) { int dif_npix = dif_s >> 2; //アライメントがあっていないピクセル数を得る //アライメントがあっていない分を1ピクセルずつしょり BltLineAlphaBlend( dst, under, over, dif_npix); //残りはSSEで SubBltLineAlphaBlend_SSE( dst + dif_npix, under + dif_npix, over + dif_npix, blt_width - dif_npix); } else { //読み込み元のアライメントを合わせる。 int dif_npix = dif_s >> 2; BltLineAlphaBlend( dst, under, over, dif_npix); //書き込み先のアライメントのズレを再度計算 dst += dif_npix; under += dif_npix; over += dif_npix; int nshift_byte = DIFF_16BYTE_ALIGNMENT(dst); assert((nshift_byte & 0x3) == 0); switch (nshift_byte) { case 4: //1pixelずれ ShiftSubBltLineAlphaBlend_SSE<4>(dst, under, over, blt_width); break; case 8: //2pixelずれ ShiftSubBltLineAlphaBlend_SSE<8>(dst, under, over, blt_width); break; case 12: //3pixelずれ ShiftSubBltLineAlphaBlend_SSE<12>(dst, under, over, blt_width); break; default: assert(0); } } } inline void SubBltLineAlphaBlend_SSE_nocache( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); assert (IS_16BYTE_ALIGNMENT(dst) == IS_16BYTE_ALIGNMENT(under)); int nloop = blt_width >> 2; //blt_width / 4 int x; for(x = 0; x < nloop; x++){ __m128i xmunder = _mm_load_si128((const __m128i*)under); __m128i xmover = _mm_load_si128((const __m128i*)over); __m128i xmdst; ALPHABLEND_SSE(xmdst, xmunder, xmover); _mm_stream_si128((__m128i*)dst, xmdst); over += 4; under += 4; dst += 4; } int bn = blt_width & 0x3; //blt_width % 4 BltLineAlphaBlend( dst, under, over, bn); return; } template <int _SHIFT> void ShiftSubBltLineAlphaBlend_SSE_nocache( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { assert(DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); assert(IS_16BYTE_ALIGNMENT(under) && IS_16BYTE_ALIGNMENT(over)); int nshift_pix = _SHIFT >> 2; int nloop = (blt_width >> 2) - 1; //(blt_width / 4) - 1 __m128i xmunder, xmunder0, xmunder1, xmover, xmover0, xmover1; xmunder0 = _mm_load_si128((const __m128i*)under); xmover0 = _mm_load_si128((const __m128i*)over); //_SHIFT byte分処理 BltLineAlphaBlend(dst, under, over, nshift_pix); dst = dst + nshift_pix; assert(IS_16BYTE_ALIGNMENT(dst)); xmunder0 = _mm_srli_si128(xmunder0, _SHIFT); //xmunder0 >> _SHIFT xmover0 = _mm_srli_si128(xmover0, _SHIFT); //xmover0 >> _SHIFT int x; for(x = 0; x < nloop; x++){ xmunder = _mm_load_si128((const __m128i*)(under + 4)); xmover = _mm_load_si128((const __m128i*)(over + 4)); xmunder1 = _mm_slli_si128(xmunder, 16 - _SHIFT); //xmunder << (16 - _SHIFT) xmover1 = _mm_slli_si128(xmover, 16 - _SHIFT); //xmmover << (16 - _SHIFT) xmunder1 = _mm_or_si128(xmunder1, xmunder0); xmover1 = _mm_or_si128(xmover1, xmover0); xmunder0 = _mm_srli_si128(xmunder, _SHIFT); //xmunder >> _SHIFT xmover0 = _mm_srli_si128(xmover, _SHIFT); //xmover >> _SHIFT __m128i xmdst; ALPHABLEND_SSE(xmdst, xmunder1, xmover1); _mm_store_si128((__m128i*)dst, xmdst); over += 4; under += 4; dst += 4; } int bn = blt_width - (nloop << 2) - nshift_pix; BltLineAlphaBlend( dst, under + nshift_pix, over + nshift_pix, bn); return; } inline void BltLineAlphaBlend_SSE_nocache( UCvPixel* dst, const UCvPixel* under, const UCvPixel* over, int blt_width) { assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over)); if (IS_16BYTE_ALIGNMENT(dst) && IS_16BYTE_ALIGNMENT(under)) { SubBltLineAlphaBlend_SSE(dst, under, over, blt_width); return; } int dif_d = DIFF_16BYTE_ALIGNMENT(dst); int dif_s = DIFF_16BYTE_ALIGNMENT(under); assert((dif_d & 0x3) == 0); //1pixel、4byteなので4の倍数になるはず assert((dif_s & 0x3) == 0); // if (dif_d == dif_s) { int dif_npix = dif_s >> 2; //アライメントがあっていないピクセル数を得る BltLineAlphaBlend( dst, under, over, dif_npix); SubBltLineAlphaBlend_SSE_nocache( dst + dif_npix, under + dif_npix, over + dif_npix, blt_width - dif_npix); } else { //読み込み元のアライメントを合わせる。 int dif_npix = dif_s >> 2; BltLineAlphaBlend( dst, under, over, dif_npix); // dst += dif_npix; under += dif_npix; over += dif_npix; int nshift_byte = DIFF_16BYTE_ALIGNMENT(dst); assert((nshift_byte & 0x3) == 0); switch (nshift_byte) { case 4: //1pixelずれ ShiftSubBltLineAlphaBlend_SSE_nocache<4>(dst, under, over, blt_width); break; case 8: //2pixelずれ ShiftSubBltLineAlphaBlend_SSE_nocache<8>(dst, under, over, blt_width); break; case 12: //3pixelずれ ShiftSubBltLineAlphaBlend_SSE_nocache<12>(dst, under, over, blt_width); break; default: assert(0); } } } void BltAlphaBlend1( IplImage* dst_img, int dst_startX, int dst_startY, int blt_width, int blt_height, const IplImage* under_img, int under_startX, int under_startY, const IplImage* over_img, int over_startX, int over_startY) { int y; UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY); UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY); UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY); for(y = 0; y < blt_height; y++){ BltLineAlphaBlend(dst_line, under_line, over_line, blt_width); over_line = GetNextLineAddress32(over_img, over_line); under_line = GetNextLineAddress32(under_img, under_line); dst_line = GetNextLineAddress32(dst_img, dst_line); } } void BltAlphaBlend2( IplImage* dst_img, int dst_startX, int dst_startY, int blt_width, int blt_height, const IplImage* under_img, int under_startX, int under_startY, const IplImage* over_img, int over_startX, int over_startY) { int y; UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY); UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY); UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY); for(y = 0; y < blt_height; y++){ BltLineAlphaBlend_SSE_unaligned( dst_line, under_line, over_line, blt_width); over_line = GetNextLineAddress32(over_img, over_line); under_line = GetNextLineAddress32(under_img, under_line); dst_line = GetNextLineAddress32(dst_img, dst_line); } } void BltAlphaBlend3( IplImage* dst_img, int dst_startX, int dst_startY, int blt_width, int blt_height, const IplImage* under_img, int under_startX, int under_startY, const IplImage* over_img, int over_startX, int over_startY) { int y; UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY); UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY); UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY); for(y = 0; y < blt_height; y++){ BltLineAlphaBlend_SSE( dst_line, under_line, over_line, blt_width); over_line = GetNextLineAddress32(over_img, over_line); under_line = GetNextLineAddress32(under_img, under_line); dst_line = GetNextLineAddress32(dst_img, dst_line); } } void BltAlphaBlend4( IplImage* dst_img, int dst_startX, int dst_startY, int blt_width, int blt_height, const IplImage* under_img, int under_startX, int under_startY, const IplImage* over_img, int over_startX, int over_startY) { int y; UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY); UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY); UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY); for(y = 0; y < blt_height; y++){ BltLineAlphaBlend_SSE_nocache( dst_line, under_line, over_line, blt_width); over_line = GetNextLineAddress32(over_img, over_line); under_line = GetNextLineAddress32(under_img, under_line); dst_line = GetNextLineAddress32(dst_img, dst_line); } } //sseを使った4ch同士のalphablend int main(void) { //cvCreateImageで帰ってくる画像データのアドレスは先頭が16byteアライメントに合わせてある //これをわざとずらす。 int dst_dif = 1; //この数分、正方向にずらす int src_dif = 2; //この数分、正方向にずらす int x,y; int r,g,b; int r1, g1, b1; int r2, g2, b2; UCvPixel *over_pix; UCvPixel *under_pix; UCvPixel *dst_pix; UCvPixel *over_line; UCvPixel *under_line; UCvPixel *dst_line; IplImage* test1 = cvLoadImage("test1.bmp"); IplImage* test2 = cvLoadImage("test2.bmp"); IplImage* b_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* g_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* r_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* a_ch1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* a_ch2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* img1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img3 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img4 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img5 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img6 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); //上になる画像のアルファ値を均等に分布させる uint8_t* line1 = GetPixelAddress(a_ch1, 0, 0); for (y = 0; y < a_ch1->height; y++) { uint8_t* pix = line1; for (x = 0; x < a_ch1->width; x++) { (*pix++) = ((double)x / a_ch1->width) * 255; } line1 = GetNextLineAddress(a_ch1, line1); } //下になる画像のアルファ値を均等に分布させる uint8_t* line2 = GetPixelAddress(a_ch2, 0, 0); for (y = 0; y < a_ch2->height; y++) { uint8_t* pix = line2; for (x = 0; x < a_ch2->width; x++) { (*pix++) = ((double)y / a_ch2->height) * 255; } line2 = GetNextLineAddress(a_ch2, line2); } cvSplit(test1, b_ch, g_ch, r_ch, NULL); cvMerge(b_ch, g_ch, r_ch, a_ch1, img1); cvSplit(test2, b_ch, g_ch, r_ch, NULL); cvMerge(b_ch, g_ch, r_ch, a_ch2, img2); //ずれがある分、書き込み幅を減らす。 int write_width = img1->width - max(dst_dif, src_dif); //非SSE IETimer timer; BltAlphaBlend1( img3, dst_dif, 0, write_width, img3->height, img2, src_dif, 0, img1, src_dif, 0); std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test3.bmp", img3); //SSE: _mm_loadu_ps + _mm_storeu_ps timer.restart(); BltAlphaBlend2( img4, dst_dif, 0, write_width, img4->height, img2, src_dif, 0, img1, src_dif, 0); std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test4.bmp", img4); //SSE: _mm_load_ps + _mm_store_ps timer.restart(); BltAlphaBlend3( img5, dst_dif, 0, write_width, img5->height, img2, src_dif, 0, img1, src_dif, 0); std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test5.bmp", img5); //SSE: _mm_load_ps + _mm_stream_si128 timer.restart(); BltAlphaBlend4( img6, dst_dif, 0, write_width, img6->height, img2, src_dif, 0, img1, src_dif, 0); std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test6.bmp", img6); cvReleaseImage(&b_ch); cvReleaseImage(&g_ch); cvReleaseImage(&r_ch); cvReleaseImage(&a_ch1); cvReleaseImage(&a_ch2); cvReleaseImage(&img1); cvReleaseImage(&img2); cvReleaseImage(&img3); cvReleaseImage(&img4); cvReleaseImage(&img5); cvReleaseImage(&img6); }
2011-11-28
■[アセンブラ][SSE][VC++]SSE用いたアルファブレンドの高速化3 アルファチャンネルをもつ画像同士のアルファブレンド
↓の続き
http://d.hatena.ne.jp/hiroki0/20090914/1252931917
http://d.hatena.ne.jp/hiroki0/20100320/1269074009
使用するアルファブレンド計算式
チャンネルサイズ8bitとして
ここで、aはアルファ値、dは画素値である。
この計算式をコードにすると以下のようになる。
255による除算は、高速化のために256の除算に変更しシフト演算で処理している。
また、計算量を減らすためにアルファ値により分岐を行なっている。
今回は、この処理をSSEにより実装して計測を行う。
if ((over_pix->a == 255) || (under_pix->a == 0)) { dst_pix->value = over_pix->value; } else if (over_pix->a == 0) { dst_pix->value = under_pix->value; } else if (under_pix->a == 255) { uint8_t ra = ~over_pix->a; dst_pix->b = (over_pix->b*over_pix->a + under_pix->b*ra)>>8; dst_pix->g = (over_pix->g*over_pix->a + under_pix->g*ra)>>8; dst_pix->r = (over_pix->r*over_pix->a + under_pix->r*ra)>>8; dst_pix->a = 255; } else { uint8_t ra = ~over_pix->a; uint8_t alpha = 255 - (ra*(255 - under_pix->a)>>8); double inv_alpha = 1.0/alpha; dst_pix->b = (over_pix->b*over_pix->a + ((under_pix->b*under_pix->a*ra)>>8))*inv_alpha; dst_pix->g = (over_pix->g*over_pix->a + ((under_pix->g*under_pix->a*ra)>>8))*inv_alpha; dst_pix->r = (over_pix->r*over_pix->a + ((under_pix->r*under_pix->a*ra)>>8))*inv_alpha; dst_pix->a = alpha; }
計測方法
32bit,64bit向けに2通りに実行ファイルを作成、それぞれについて
- SSEを使わない処理
- _mm_loadu_psでメモリ読み込み、_mm_storeu_psでメモリ書き込み
- _mm_load_psでメモリ読み込み、_mm_store_psでメモリ書き込み
- _mm_load_psでメモリ読み込み、_mm_stream_si128でメモリ書き込み
の4通りの処理の処理速度を計測する。
- チャンネルサイズ8bit 5700x5700BGR画像を2枚読み込み、アルファ値を指定してアルファブレンドを行う。
- 画像の保存、読み込みはopenCV 2.3を用いた。
- アルファブレンド部分の処理のみ処理時間を計測した。
- アライメントは常に合っている状態で処理を行う。
アルファブレンドの処理は、アルファ値により分岐を入れているので
ただコピーを行う部分、アルファ値1つによるアルファブレンドを行う部分、
アルファ値2つでアルファブレンドを行う部分の3箇所の計測を行いたい。
なので、与えるアルファチャンネルは、
の三通りとした。
計測に使用した環境
計測結果
上下ともにアルファ値255
- 32bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 111.002 msec | 95.006 msec | 90.9281 msec | 90.853 msec |
| 2回目 | 87.0272 msec | 70.3019 msec | 64.3998 msec | 77.862 msec |
| 3回目 | 86.3182 msec | 66.67 msec | 72.2693 msec | 62.0172 msec |
- 64bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 85.2114 msec | 82.4653 msec | 85.074 msec | 83.51 msec |
| 2回目 | 56.3726 msec | 63.0728 msec | 71.2231 msec | 56.5069 msec |
| 3回目 | 55.2358 msec | 62.6271 msec | 62.9312 msec | 58.8877 msec |
このアルファ値では、分岐処理により上の画像の画素を出力メモリにコピーする処理だけになる。
SSEのロード・ストア命令による違いはバラツキがあり、殆ど無いと思われる。
32bitでは、SSEを使ったほうが1.1倍程度速くなっている。
32bitと64bitの結果を比較して、SSEを使わない処理では64bitの方が早くなっている。
下になる画像全てアルファ値255、上になる画像はアルファ値を均等に分布
- 32bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 193.727 msec | 114.325 msec | 94.4707 msec | 101.514 msec |
| 2回目 | 187.955 msec | 93.6102 msec | 93.4529 msec | 91.6304 msec |
| 3回目 | 190.177 msec | 104.997 msec | 94.9949 msec | 91.032 msec |
- 64bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 167.373 msec | 99.5402 msec | 89.3116 msec | 100.947 msec |
| 2回目 | 198.225 msec | 91.6833 msec | 99.7168 msec | 89.8282 msec |
| 3回目 | 186.376 msec | 87.6821 msec | 98.8608 msec | 97.6576 msec |
このアルファ値では、分岐処理により主に一つのアルファ値によるアルファブレンドを行う処理になる。
SSEのロード・ストア命令による違いはバラツキがあり、殆ど無いと思われる。
32bitと64bitによる違いは殆ど見られない。
また、SSEにより1.6倍〜2.0倍程度速くなっていることがわかる。
上下ともにアルファ値を均等に分布
- 32bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 615.202 msec | 182.197 msec | 208.116 msec | 176.766 msec |
| 2回目 | 576.132 msec | 182.21 msec | 196.25 msec | 172.828 msec |
| 3回目 | 586.831 msec | 176.329 msec | 179.473 msec | 171.598 msec |
- 64bit向け実行ファイル
| 非SSE | _mm_loadu_ps + _mm_storeu_ps | _mm_load_ps + _mm_store_ps | _mm_load_ps + _mm_stream_si128 | |
|---|---|---|---|---|
| 1回目 | 327.867 msec | 208.067 msec | 204.649 msec | 170.796 msec |
| 2回目 | 328.621 msec | 173.355 msec | 176.605 msec | 160.269 msec |
| 3回目 | 336.624 msec | 167.226 msec | 168.419 msec | 170.916 msec |
このアルファ値では、分岐処理により主に2つのアルファ値によるアルファブレンドを行う処理になる。
SSEのロード・ストア命令による違いはバラツキがあり、殆ど無いと思われる。
非SSEの処理の32bitの方は64bitに対して1.8倍程度遅くなっており、かなり時間がかかっていることがわかる。
SSEによる速度の向上は32bitで約3倍64bitで約1.8倍である。
まとめ
測定条件のいずれの場合もSSEのロード・ストア命令による違いは見られなかった。
今回は、アライメントは常に合っている状態で計測を行ったのでアライメントをずらいた状態では違いが見られるかもしれない。
コード
SSEでの実装方針は、4ピクセル分のデータをロード、SSEのレジスタに16bitで8つのデータを持ち2ピクセルづつ処理し、
4ピクセル分ストアするようにする。
2つのアルファ値をもつアルファブレンドの処理では途中、除算処理が入る。
SSEの命令には整数除算が無いので、[16bit整数]→[32bit整数]→[32bit浮動小数]と変換して
_mm_rcp_ps()で逆数を求めて除算を行う。
_mm_rcp_ps()を用いることで_mm_div_ps()よりも高速に計算することができる。
_mm_rcp_ps()は11bit程度しか精度がないが、最後は8bit整数にするので問題ないはず。
#include <iostream> #include <opencv/cv.h> #include <opencv/highgui.h> #include "IETimer.h" union UCvPixel32 { uint32_t value; struct { uint8_t b,g,r,a; }; }; typedef UCvPixel32 UCvPixel; inline uint8_t* GetNextLineAddress(const IplImage* image, uint8_t* addr) { return addr + image->widthStep; } inline uint8_t* GetPixelAddress(const IplImage* image, int x, int y) { assert(image); assert(image->depth == IPL_DEPTH_8U); assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1)); return (uint8_t*)(image->imageData + y*image->widthStep + x*image->nChannels); } inline UCvPixel32* GetNextLineAddress32(const IplImage* image, UCvPixel32* addr) { return (UCvPixel32*)((uint8_t*)addr + image->widthStep); } inline UCvPixel32* GetPixelAddress32(const IplImage* image, int x, int y) { assert(image); assert(image->depth == IPL_DEPTH_8U); assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1)); return (UCvPixel32*)(image->imageData + y*image->widthStep + x*image->nChannels); } int main(void) { int x,y; int r,g,b; int r1, g1, b1; int r2, g2, b2; UCvPixel *over_pix; UCvPixel *under_pix; UCvPixel *dst_pix; UCvPixel *over_line; UCvPixel *under_line; UCvPixel *dst_line; IplImage* test1 = cvLoadImage("test1.bmp"); IplImage* test2 = cvLoadImage("test2.bmp"); IplImage* b_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* g_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* r_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* a_ch1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* a_ch2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1); IplImage* img1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img3 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img4 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img5 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); IplImage* img6 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4); //上になる画像のアルファ値をすべて255にする cvSet(a_ch1, cvScalar(255)); //下になる画像のアルファ値をすべて255にする cvSet(a_ch2, cvScalar(255)); //上になる画像のアルファ値を均等に分布させる //uint8_t* line1 = GetPixelAddress(a_ch1, 0, 0); //for (y = 0; y < a_ch1->height; y++) { // uint8_t* pix = line1; // for (x = 0; x < a_ch1->width; x++) { // (*pix++) = ((double)x / a_ch1->width) * 255; // } // line1 = GetNextLineAddress(a_ch1, line1); //} //下になる画像のアルファ値を均等に分布させる //uint8_t* line2 = GetPixelAddress(a_ch2, 0, 0); //for (y = 0; y < a_ch2->height; y++) { // uint8_t* pix = line2; // for (x = 0; x < a_ch2->width; x++) { // (*pix++) = ((double)y / a_ch2->height) * 255; // } // line2 = GetNextLineAddress(a_ch2, line2); //} cvSplit(test1, b_ch, g_ch, r_ch, NULL); cvMerge(b_ch, g_ch, r_ch, a_ch1, img1); cvSplit(test2, b_ch, g_ch, r_ch, NULL); cvMerge(b_ch, g_ch, r_ch, a_ch2, img2); //非SSE IETimer timer; over_line = GetPixelAddress32(img1, 0, 0); under_line = GetPixelAddress32(img2, 0, 0); dst_line = GetPixelAddress32(img3, 0, 0); for(y=0; y<img1->height; y++){ over_pix = over_line; under_pix = under_line; dst_pix = dst_line; for(x=0; x<img1->width; x++){ //alpha blend if ((over_pix->a == 255) || (under_pix->a == 0)) { dst_pix->value = over_pix->value; } else if (over_pix->a == 0) { dst_pix->value = under_pix->value; } else if (under_pix->a == 255) { uint8_t ra = ~over_pix->a; dst_pix->b = (over_pix->b*over_pix->a + under_pix->b*ra)>>8; dst_pix->g = (over_pix->g*over_pix->a + under_pix->g*ra)>>8; dst_pix->r = (over_pix->r*over_pix->a + under_pix->r*ra)>>8; dst_pix->a = 255; } else { uint8_t ra = ~over_pix->a; uint8_t alpha = 255 - (ra*(255 - under_pix->a)>>8); double inv_alpha = 1.0/alpha; dst_pix->b = (over_pix->b*over_pix->a + ((under_pix->b*under_pix->a*ra)>>8))*inv_alpha; dst_pix->g = (over_pix->g*over_pix->a + ((under_pix->g*under_pix->a*ra)>>8))*inv_alpha; dst_pix->r = (over_pix->r*over_pix->a + ((under_pix->r*under_pix->a*ra)>>8))*inv_alpha; dst_pix->a = alpha; } over_pix++; under_pix++; dst_pix++; } over_line = GetNextLineAddress32(img1, over_line); under_line = GetNextLineAddress32(img2, under_line); dst_line = GetNextLineAddress32(img3, dst_line); } std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test3.bmp", img3); //SSE: _mm_loadu_ps + _mm_storeu_ps timer.restart(); { over_line = GetPixelAddress32(img1, 0, 0); under_line = GetPixelAddress32(img2, 0, 0); dst_line = GetPixelAddress32(img4, 0, 0); __m128i xmzero = _mm_setzero_si128(); __m128i xmff16 = _mm_set1_epi16(0xff); for(y=0; y<img1->height; y++){ over_pix = over_line; under_pix = under_line; dst_pix = dst_line; for(x=0; x<img1->width; x+=4){ //4ピクセルロード __m128i under = _mm_castps_si128( _mm_loadu_ps((float*)under_pix) ); __m128i over = _mm_castps_si128( _mm_loadu_ps((float*)over_pix) ); __m128i xmo16, xmu16; __m128i xmlo, xmhi; __m128i xmover_alpha, xmover_ralpha, xmunder_alpha, xmunder_ralpha, xmalpha; __m128 xmf_lo, xmf_hi, xmf_alpha_lo, xmf_alpha_hi; //load lo double ward xmo16 = _mm_unpacklo_epi8(over, xmzero); xmu16 = _mm_unpacklo_epi8(under, xmzero); //アルファ値を各個読み出し比較し分岐へ int over_a1 = _mm_extract_epi16(xmo16, 3); int over_a2 = _mm_extract_epi16(xmo16, 7); int under_a1 = _mm_extract_epi16(xmu16, 3); int under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmlo = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmlo = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { //16bitのアルファ値8個をシャッフルをつかってならべ、 //[a1,a1,a1,a1,a2,a2,a2]というようにする。 xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); //255 - over_alpha //2ピクセル同時にアルファブレンドの計算 xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmlo = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 //アルファ値255を出力用のレジスタに各個書き込み xmlo = _mm_insert_epi16(xmlo, 255, 3); //write alpha xmlo = _mm_insert_epi16(xmlo, 255, 7); //write alpha } else { //16bitのアルファ値8個をシャッフルをつかってならべ //[a1,a1,a1,a1,a2,a2,a2]というようにする。 //上になる画像と下になる画像の2つぶんを行なっておく xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); // 255 - over_alpha xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); // 255 - under_alpha //新しくアルファ値になる値の計算 //ここのアルファ値も[a1,a1,a1,a1,a2,a2,a2]となるようにしている xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) //16bit幅での2ピクセル同時処理 //ここで 16bit乗算 → 8bit右シフト → 16bit乗算という順で計算しているので誤差が出る。 xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * under_alpha * over_ralpha)>>8 //32bit浮動小数に変換してピクセル1の除算の計算 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); ////32bit浮動小数に変換してピクセル2の除算の計算 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //16bit整数に戻してレジスタに書き込み xmlo = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); //新しくアルファ値になる値を取り出して各個書き込み int a1 = _mm_extract_epi16(xmalpha, 3); xmlo = _mm_insert_epi16(xmlo, a1, 3); //write alpha int a2 = _mm_extract_epi16(xmalpha, 7); xmlo = _mm_insert_epi16(xmlo, a2, 7); //write alpha } //load hi double ward xmo16 = _mm_unpackhi_epi8(over, xmzero); xmu16 = _mm_unpackhi_epi8(under, xmzero); over_a1 = _mm_extract_epi16(xmo16, 3); over_a2 = _mm_extract_epi16(xmo16, 7); under_a1 = _mm_extract_epi16(xmu16, 3); under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmhi = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmhi = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmhi = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 xmhi = _mm_insert_epi16(xmhi, 255, 3); //write alpha xmhi = _mm_insert_epi16(xmhi, 255, 7); //write alpha } else { // xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * ((under_alpha * over_ralpha)>>8) //calc pixel1 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); //calc pixel 2 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //pack to 16bit data xmhi = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); int a3 = _mm_extract_epi16(xmalpha, 3); xmhi = _mm_insert_epi16(xmhi, a3, 3); //write alpha int a4 = _mm_extract_epi16(xmalpha, 7); xmhi = _mm_insert_epi16(xmhi, a4, 7); //write alpha } //16bit幅、2ピクセルのデータが入った2つのレジスタを合わせて8bitへ //4ピクセルのデータをメモリへストア _mm_storeu_ps((float*)dst_pix, _mm_castsi128_ps( _mm_packus_epi16(xmlo, xmhi)) ); // over_pix += 4; under_pix += 4; dst_pix += 4; } over_line = GetNextLineAddress32(img1, over_line); under_line = GetNextLineAddress32(img2, under_line); dst_line = GetNextLineAddress32(img4, dst_line); } } std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test4.bmp", img4); //SSE: _mm_load_ps + _mm_store_ps timer.restart(); { over_line = GetPixelAddress32(img1, 0, 0); under_line = GetPixelAddress32(img2, 0, 0); dst_line = GetPixelAddress32(img5, 0, 0); __m128i xmzero = _mm_setzero_si128(); __m128i xmff16 = _mm_set1_epi16(0xff); for(y=0; y<img1->height; y++){ over_pix = over_line; under_pix = under_line; dst_pix = dst_line; for(x=0; x<img1->width; x+=4){ __m128i under = _mm_castps_si128( _mm_load_ps((float*)under_pix) ); __m128i over = _mm_castps_si128( _mm_load_ps((float*)over_pix) ); __m128i xmo16, xmu16; __m128i xmlo, xmhi; __m128i xmover_alpha, xmover_ralpha, xmunder_alpha, xmunder_ralpha, xmalpha; __m128 xmf_lo, xmf_hi, xmf_alpha_lo, xmf_alpha_hi; //load lo double ward xmo16 = _mm_unpacklo_epi8(over, xmzero); xmu16 = _mm_unpacklo_epi8(under, xmzero); int over_a1 = _mm_extract_epi16(xmo16, 3); int over_a2 = _mm_extract_epi16(xmo16, 7); int under_a1 = _mm_extract_epi16(xmu16, 3); int under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmlo = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmlo = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmlo = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 xmlo = _mm_insert_epi16(xmlo, 255, 3); //write alpha xmlo = _mm_insert_epi16(xmlo, 255, 7); //write alpha } else { // xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * under_alpha * over_ralpha)>>8 //calc pixel 1 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); //calc pixel 2 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //pack to 16bit data xmlo = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); int a1 = _mm_extract_epi16(xmalpha, 3); xmlo = _mm_insert_epi16(xmlo, a1, 3); //write alpha int a2 = _mm_extract_epi16(xmalpha, 7); xmlo = _mm_insert_epi16(xmlo, a2, 7); //write alpha } //load hi double ward xmo16 = _mm_unpackhi_epi8(over, xmzero); xmu16 = _mm_unpackhi_epi8(under, xmzero); over_a1 = _mm_extract_epi16(xmo16, 3); over_a2 = _mm_extract_epi16(xmo16, 7); under_a1 = _mm_extract_epi16(xmu16, 3); under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmhi = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmhi = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmhi = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 xmhi = _mm_insert_epi16(xmhi, 255, 3); //write alpha xmhi = _mm_insert_epi16(xmhi, 255, 7); //write alpha } else { // xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * ((under_alpha * over_ralpha)>>8) //calc pixel1 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); //calc pixel 2 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //pack to 16bit data xmhi = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); int a3 = _mm_extract_epi16(xmalpha, 3); xmhi = _mm_insert_epi16(xmhi, a3, 3); //write alpha int a4 = _mm_extract_epi16(xmalpha, 7); xmhi = _mm_insert_epi16(xmhi, a4, 7); //write alpha } //store to memory _mm_store_ps((float*)dst_pix, _mm_castsi128_ps( _mm_packus_epi16(xmlo, xmhi)) ); // over_pix += 4; under_pix += 4; dst_pix += 4; } over_line = GetNextLineAddress32(img1, over_line); under_line = GetNextLineAddress32(img2, under_line); dst_line = GetNextLineAddress32(img5 , dst_line); } } std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test5.bmp", img5); //SSE: _mm_load_ps + _mm_stream_si128 timer.restart(); { over_line = GetPixelAddress32(img1, 0, 0); under_line = GetPixelAddress32(img2, 0, 0); dst_line = GetPixelAddress32(img6, 0, 0); __m128i xmzero = _mm_setzero_si128(); __m128i xmff16 = _mm_set1_epi16(0xff); for(y=0; y<img1->height; y++){ over_pix = over_line; under_pix = under_line; dst_pix = dst_line; for(x=0; x<img1->width; x+=4){ __m128i under = _mm_castps_si128( _mm_load_ps((float*)under_pix) ); __m128i over = _mm_castps_si128( _mm_load_ps((float*)over_pix) ); __m128i xmo16, xmu16; __m128i xmlo, xmhi; __m128i xmover_alpha, xmover_ralpha, xmunder_alpha, xmunder_ralpha, xmalpha; __m128 xmf_lo, xmf_hi, xmf_alpha_lo, xmf_alpha_hi; //load lo double ward xmo16 = _mm_unpacklo_epi8(over, xmzero); xmu16 = _mm_unpacklo_epi8(under, xmzero); int over_a1 = _mm_extract_epi16(xmo16, 3); int over_a2 = _mm_extract_epi16(xmo16, 7); int under_a1 = _mm_extract_epi16(xmu16, 3); int under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmlo = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmlo = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmlo = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 xmlo = _mm_insert_epi16(xmlo, 255, 3); //write alpha xmlo = _mm_insert_epi16(xmlo, 255, 7); //write alpha } else { // xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * under_alpha * over_ralpha)>>8 //calc pixel 1 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); //calc pixel 2 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //pack to 16bit data xmlo = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); //write alpha int a1 = _mm_extract_epi16(xmalpha, 3); xmlo = _mm_insert_epi16(xmlo, a1, 3); //write alpha int a2 = _mm_extract_epi16(xmalpha, 7); xmlo = _mm_insert_epi16(xmlo, a2, 7); //write alpha } //load hi double ward xmo16 = _mm_unpackhi_epi8(over, xmzero); xmu16 = _mm_unpackhi_epi8(under, xmzero); over_a1 = _mm_extract_epi16(xmo16, 3); over_a2 = _mm_extract_epi16(xmo16, 7); under_a1 = _mm_extract_epi16(xmu16, 3); under_a2 = _mm_extract_epi16(xmu16, 7); if (((over_a1 == 255 && over_a2 == 255)) || (under_a1 == 0 && under_a2 == 0)) { xmhi = xmo16; } else if (under_a1 == 0 && under_a2 == 0) { xmhi = xmu16; } else if (under_a1 == 255 && under_a2 == 255) { xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmu16 = _mm_mullo_epi16(xmu16, xmover_ralpha); // under_pix * over_ralpha xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // (under_pix * over_ralpha + over_pix * over_alpha) xmhi = _mm_srli_epi16(xmo16, 8); // (under_pix * over_ralpha + over_pix * over_alpha) >> 8 xmhi = _mm_insert_epi16(xmhi, 255, 3); //write alpha xmhi = _mm_insert_epi16(xmhi, 255, 7); //write alpha } else { // xmover_alpha = _mm_shufflelo_epi16(xmo16, _MM_SHUFFLE(3,3,3,3)); xmover_alpha = _mm_shufflehi_epi16(xmover_alpha, _MM_SHUFFLE(3,3,3,3)); xmover_ralpha = _mm_sub_epi16(xmff16, xmover_alpha); xmunder_alpha = _mm_shufflelo_epi16(xmu16, _MM_SHUFFLE(3,3,3,3)); xmunder_alpha = _mm_shufflehi_epi16(xmunder_alpha, _MM_SHUFFLE(3,3,3,3)); xmunder_ralpha = _mm_sub_epi16(xmff16, xmunder_alpha); xmalpha = _mm_sub_epi16(xmff16, _mm_srli_epi16( _mm_mullo_epi16(xmover_ralpha, xmunder_ralpha), 8)); //255 - ((over_ralpha * under_ralpha) >> 8) xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(xmunder_alpha, xmover_ralpha), 8), xmu16); //under_pix * ((under_alpha * over_ralpha)>>8) xmo16 = _mm_mullo_epi16(xmo16, xmover_alpha); // over_pix * over_alpha xmo16 = _mm_add_epi16(xmu16, xmo16); // over_pix * over_alpha + (under_pix * ((under_alpha * over_ralpha)>>8) //calc pixel1 xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmo16, xmzero) ); xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(xmalpha, xmzero) ); xmf_lo = _mm_mul_ps(xmf_lo, _mm_rcp_ps(xmf_alpha_lo)); //calc pixel 2 xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmo16, xmzero) ); xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(xmalpha, xmzero) ); xmf_hi = _mm_mul_ps(xmf_hi, _mm_rcp_ps(xmf_alpha_hi)); //pack to 16bit data xmhi = _mm_packs_epi32(_mm_cvtps_epi32(xmf_lo), _mm_cvtps_epi32(xmf_hi)); int a3 = _mm_extract_epi16(xmalpha, 3); xmhi = _mm_insert_epi16(xmhi, a3, 3); //write alpha int a4 = _mm_extract_epi16(xmalpha, 7); xmhi = _mm_insert_epi16(xmhi, a4, 7); //write alpha } //store to memory _mm_stream_si128((__m128i*)dst_pix, _mm_packus_epi16(xmlo, xmhi)); // over_pix += 4; under_pix += 4; dst_pix += 4; } over_line = GetNextLineAddress32(img1, over_line); under_line = GetNextLineAddress32(img2, under_line); dst_line = GetNextLineAddress32(img6, dst_line); } } std::cout << timer.elapsed() << " msec" << std::endl; cvSaveImage("test6.bmp", img6); cvReleaseImage(&b_ch); cvReleaseImage(&g_ch); cvReleaseImage(&r_ch); cvReleaseImage(&a_ch1); cvReleaseImage(&a_ch2); cvReleaseImage(&img1); cvReleaseImage(&img2); cvReleaseImage(&img3); cvReleaseImage(&img4); cvReleaseImage(&img5); cvReleaseImage(&img6); }
