GaussianBlur関数でほぼ空っぽの画像が生成されるバグ

# TL;DR
オーバーフロー

# はじめに
- 本記事は[OpenCVアドベントカレンダー2021](https://qiita.com/advent-calendar/2021/opencv)の21日目の記事です
    - 昨日は@eqsさんによる[対戦ゲームの動画記録から特定のイベントを自動的に探してクリップを生成する画像処理作った](https://blog.eqseqs.work/2021/12/20/000000/)でした
    - その他の記事は[目次を参照ください](https://qiita.com/advent-calendar/2021/opencv)
- 本日は`cudafilters`モジュールの回帰テストがFAILするのを直したお話
- テストが以下のようにFAILします

<details><summary>テストFAIL時のログ</summary>

```
[  FAILED  ] 73 tests, listed below:
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/388, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(9x9), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/393, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(11x11), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/396, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(11x11), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/397, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(11x11), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/413, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(15x15), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/417, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(17x17), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/434, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(21x21), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/446, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(23x23), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/453, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(25x25), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/456, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(27x27), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/458, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(27x27), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/476, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(31x31), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/505, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(9x9), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/511, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(9x9), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/537, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(17x17), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/538, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(17x17), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/566, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(23x23), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/569, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(25x25), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/575, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(25x25), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/591, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(29x29), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/593, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(31x31), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/594, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(3), KSize(31x31), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/639, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(11x11), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/648, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(15x15), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/649, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(15x15), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/652, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(15x15), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/658, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(17x17), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/664, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(19x19), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/667, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(19x19), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/703, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(4), KSize(27x27), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1817, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(7x7), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1819, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(7x7), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1826, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(9x9), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1827, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(9x9), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1837, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(11x11), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1843, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(13x13), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1864, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(19x19), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1872, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(21x21), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1874, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(21x21), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1878, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(21x21), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1884, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(23x23), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1892, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(25x25), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1898, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(27x27), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1902, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(27x27), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1905, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(29x29), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1916, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(1), KSize(31x31), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1945, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(9x9), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1952, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(11x11), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1957, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(11x11), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1960, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(13x13), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1965, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(13x13), BORDER_CONSTANT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1969, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(15x15), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1977, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(17x17), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1978, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(17x17), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/1982, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(17x17), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2008, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(25x25), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2023, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(27x27), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2024, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(29x29), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2032, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(31x31), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2038, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(3), KSize(31x31), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2073, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(11x11), BORDER_REFLECT101, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2086, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(13x13), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2088, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(15x15), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2096, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(17x17), BORDER_REFLECT101, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2108, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(19x19), BORDER_CONSTANT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2111, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(19x19), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2114, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(21x21), BORDER_REPLICATE, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2118, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(21x21), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2123, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(23x23), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2131, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(25x25), BORDER_REPLICATE, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2135, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(25x25), BORDER_REFLECT, sub matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2142, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(27x27), BORDER_REFLECT, whole matrix)
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/2153, where GetParam() = (NVIDIA Tegra X1, 113x113, CV_16U, Channels(4), KSize(31x31), BORDER_REFLECT101, sub matrix)
73 FAILED TESTS
```
</details>

- テストは`opencv_test_cudafilters`でしたが、後述の通り、`imgproc`モジュールが引き金です

# 再現性
コードに飛び込む前に状況を整理してみましょう

## CV_16Uに限る
- テストのメッセージを見るに、`CV_16U`というパラメータが見えます
- `GaussianBlur`関数に渡す画像の要素が`CV_8U`か`CV_32F`の場合はテストはPASSするのですが、`CV_16U`のときだけFAILします

```
[  FAILED  ] CUDA_Filters/GaussianBlur.Accuracy/388, where GetParam() = (NVIDIA Tegra X1, 128x128, CV_16U, Channels(1), KSize(9x9), BORDER_CONSTANT, whole matrix)
テスト名の後ろ、GetParamがついている場合は同じ関数を複数のパラメータでチェックする。ここにCV_16U__________^^^^^^
他のパラメータは変化するが、このCV_16Uだけ変化しない
```

## deterministicな再現性
- 同じテストを何回実行しても決まって同じテストがコケます

```
$ for i in `seq 1 3 ` ; do ./bin/opencv_test_cudafilters --gtest_filter=*Gaussian* --gtest_param_filter=*CV_16U* | grep "FAILED TESTS" ; done
67 FAILED TESTS
67 FAILED TESTS
67 FAILED TESTS
```

- 一方で、`--gtest_repeat=3`オプションを利用すると、テストがFAILする数が変わる

```
$ ./bin/opencv_test_cudafilters --gtest_filter=*Gaussian* --gtest_param_filter=*CV_16U* --gtest_repeat=3 | grep "FAILED TESTS"
67 FAILED TESTS
64 FAILED TESTS
77 FAILED TESTS
```

- このコケ方が変化することの意味は
    - `bash`の`for`文で複数回実行した場合は固定のシードで実行されるので、deterministicな結果が得られる
    - 一方で、`gtest_repeat`オプションをつけた場合、シードの初期化が1度だけ行われたあと同じ乱数系列が引き続き使われる
    - そのため、生成される乱数は微妙に違ってくる
- つまり、微妙なパラメータの違いでテストがコケることを意味している

# 画像
## 入力画像
![src_emphasized[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/08c743f7-a3b9-6cfb-44f6-787739af8ba3.png)
## 出力画像GPU版
![dst_gpu_emphasized[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/c6a6b884-6a42-3cbe-5856-bf4805932246.png)
## 出力画像CPU版
![dst_cpu_emphasized[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/e5c71be8-3a77-312f-2274-d02bca3ab7fa.png)

- というわけでテストは`cudafilters`モジュールのテストがコケてるのですが、当該テストは
    - CUDA版のガウシアンフィルタの結果と
    - CPU版のガウシアンフィルタの結果を
    - それぞれ比較し、結果が一致しなければFAIL
    - という仕組みです
- 今回のFAILは、どうやらCPU版が予期せぬ結果を返したためにFAILとなっている模様です。
- CPU版の実装は`imgproc`モジュール内にあります

# bit-exactなGaussianBlur
- 奇しくも[2日目の記事](https://qiita.com/OECkoec/items/796bb06302c53391f172)でもGaussianBlur関数に触れてますが、ここではもう少しGaussianBlur関数を掘ってみましょう

## 関数の仕組み
```.cpp
void GaussianBlur(InputArray _src, OutputArray _dst, Size ksize, double sigma1, double sigma2, int borderType)
```
- このように、GaussianBlur関数には
    - 入力画像
    - 出力用バッファ
    - 平滑化カーネルの大きさ
    - 平滑化具合を示すシグマ (x2)
    - 境界線の処理を指定するフラグ
    - を指定します
- `sigma1`は横方向への平滑化度合い、`sigma2`は縦方向への平滑化度合いを指定します
    - `sigma`は値が大きい方が平滑化の度合いが強くなり、小さい方が平滑化の効果は小さくなる[^notify]
    - 現状テストコードからは`sigma2`は無罪で、`sigma1`だけが引き金のようです。
- 内部では、この`sigma`の値をもとに、カーネル(畳み込み積分される係数)が作られます。テストケースでは7x7のフィルタのようです。
    - 縦横独立して計算するため、7つのパラメータが指定されます。
    - また、OpenCVは3.3ぐらいから[^urooboe] bit-exactなGaussianBlurが実装されました
    - なので、7つのパラメータは`float`ではなく、内部では32bit符号なし整数もしくは16bit符号なし整数として表現されます
- カーネルを作るところを見てみましょう。

[^urooboe]: （うろ覚え）
[^notify]: 初稿では`sigma`の取る範囲は`[0:1]`と書いたが、誤りだった。1以上の値も取れる。

```.cpp
    Mat kx, ky;
    createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2);
```

- ここでは`kx`が横方向への平滑化を行うカーネルです
- 出てきた`kx`の中身を見てみましょう

```
0.00000000
3.38778060e-21
7.62910940e-06
0.999984741
7.62910940e-06
3.38778060e-21
0.00000000
```

- 指数表示なのでわかりにくいですが、小数点を揃えると、こんな感じです。

```
0.00000000000000000000000000000
0.00000000000000000000338778060
0.00000762910940
0.999984741
0.00000762910940
0.00000000000000000000338778060
0.00000000000000000000000000000
```

- 試しに棒グラフでプロットしてみるとこんな感じです。
![coeff.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/e3bcd713-a41a-234c-dcf1-ec72bcb3585b.png)

- 何ということでしょう。人生で一番不毛なグラフを描いてしまった気もします。
- なお、対数スケールで表示すればちゃんと正規分布の形をしてることがかろうじてわかります
![coeff_log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/34a5deee-32df-b4d7-c63d-d8b4ac6e3284.png)

- とは言え、1番目と2番目の係数が100,000倍(10万倍)違います。
    - そして内部的には`CV_16U`で、表せる最小の桁数は`1/65536`です。
    - 実はこのあたりがバグのキモではないかと薄々このあたりで筆者は思い始めました。
- この値は、`CV_16U`のケースなので、内部的には固定小数点として以下の形式で保持されます

```
0x00000000
0x00000000
0x00000000
0x00010000
0x00000000
0x00000000
0x00000000
```

- 状況から考えるに、`CV_16U`のbit幅で`0x10000`が発生したときにだけエラーになってるのでは？とプンプンにおってきます。

##実際にバグのトリガとなるコード
- そこから更に進むと、SIMD最適化されたコードに到着します

```.cpp
v_mul_expand(vx_load(src + pre_shift * cn), vx_setall_u16((uint16_t) *((uint32_t*)(m + pre_shift))), v_res0, v_res1);
```

- ここの内部で使われている`vx_setall_u16`は前述のパラメータをロードして、ベクトルレジスタの全要素に設定する`intrinsic`です
- ここで、この行をよく読み解くと

```.cpp
vx_setall_u16((uint16_t) *((uint32_t*)(m + pre_shift)))
//             ^            ^          ^____メモリアドレス
//             |            |_______________32bit符号なし整数としてロード
//             |____________________________16bit符号なし整数に変換(!)
```

- という具合に
    1. メモリアドレスを計算
    1. 32bit符号なし整数としてロード
    1. **16bit符号なし整数に変換（←イマココ**）
    1. ベクトルレジスタの全要素に同じ値を設定
- ということをしています。
- ところで**32bit符号なし整数、`0x10000`を16bit符号なし整数に変換すると結果はいくつでしょう？**

## 原因中の原因
- というわけで、バグとしては「オーバーフローが発生していた」のが原因です

## `sigma2`が無罪なわけ

```.cpp
v_uint32 v_mul = vx_setall_u32(*((uint32_t*)(m + pre_shift)));
```

- 同様に縦方向にガウシアンフィルタをかける実装ではご覧の通り、符号なし32bit整数として処理されております


# まとめ
- GaussianBlur関数の画像が`CV_16U`型の場合で`sigma1`に0.2以下の値[^around2]を渡すと、空っぽな画像が生成されます
- 原因はオーバーフロー

[^around2]: 厳密には0.2を超えても再現する。0.201ちょっと。計算の結果、`float`なら突出したインパルス関数のような形になるのですが、符号なし16bit整数で表されてるため、結果がオーバーフローします

## 再現バージョン
- 3.4系列の一部 (3.4.13-3.4.16まで。次期リリース予定の3.4.17でFIX)
- 4.5系列の一部 (4.5.1-4.5.4まで。次期リリース予定の4.5.5でFIX)