超高速メディアンフィルタ

#超高速メディアンフィルタ
###ソートを使わない
要素を小さい順(大きい順)に半分カウントしたときのインデックス値が中央値
###注目画素を隣にずらすときに前回のブロックを再利用
注目画素周辺のブロック(n*nの塊)を一々作り直すのではなく，範囲が被っているものを再利用し，ブロック作成時間を短縮
###以下プログラム コピペで動きます 各自各言語に移植して下さい．
 int nはブロックの大きさであり，3,5,7,9などを指定できる 本プログラムでは5以上のときOpenCVに勝てる,
特に7以上のとき顕著
FastestMedian(...)//超高速メデイアンフィルタ
SelectAscendingDescendingOrder(...)//入力画像(の背景)が暗めか明るめは判定する．暗ければ暗い方から中央値を走査する．
SelectBucketMedian//暗い方から走査するか明るい方から走査するか関数を使い分け
GetBucketMedianDescendingOrder(...)//明るい方から走査する
GetBucketMedianAscendingOrder(...)//暗い方から走査する
Hage()が呼び出し元

```C#
        public static unsafe bool FastestMedian(IplImage src_img, IplImage dst_img, int n) {
            Cv.Copy(src_img, dst_img);//元のをコピー
            if ((n & 1) == 0) return false;//偶数はさいなら 
            int MaskSize = n >> 1;//処理できない端部の大きさを定義 左右にあるので2で割る
            SelectBucketMedian BucketMedian = GetBucketMedianAscendingOrder;//背景が暗い場合
            if (SelectAscendingDescendingOrder(src_img) == Is.DESCENDING_ORDER)//背景が明るい(白い)か判定
                BucketMedian = GetBucketMedianDescendingOrder;//背景が明るい場合

            byte* dst = (byte*)dst_img.ImageData;//出力先のポインタ．unsafeが必要
            dst += MaskSize * (src_img.WidthStep) + MaskSize;//端部は処理できないのでポインタを画像の内陸部に進める
            for (int y = MaskSize; y < src_img.Height - MaskSize; ++y, dst += src_img.WidthStep) {
                int[] Bucket = new int[Const.Tone8Bit];//256tone It is cleared each time
                for (int x = 0; x < n; ++x) {//画像左端である．メディアンを求めるブロック(バケツ)を作る
                    byte* src = (byte*)src_img.ImageData;
                    src += (y - MaskSize) * src_img.WidthStep + x;
                    for (int yy = 0; yy < n; ++yy, src += src_img.WidthStep)
                        ++Bucket[*src];
                }/* */
                *dst = BucketMedian(Bucket, ((n * n) >> 1));//中央値(ブロックの((n * n) >> 1)番目の値)を求める．ソートはしない

                for (int x = 0; x < src_img.Width - n; ++x) {
                    byte* src = (byte*)src_img.ImageData;
                    src += (y - MaskSize) * src_img.WidthStep + x;
                    for (int yy = 0; yy < n; ++yy, src += src_img.WidthStep) {//ブロックの更新．一部をリサイクルできるので高速化できる．
                        --Bucket[*src];//ここが味噌 ピクセルをブロックから削除
                        ++Bucket[*(src + n)];//ここが味噌 ピクセルにブロックに追加
                    }
                    *(dst + x + 1) = BucketMedian(Bucket, ((n * n) >> 1));//中央値を求める．ソートをしないので早い
                }
            }
            return true;
        }
        private static unsafe bool SelectAscendingDescendingOrder(IplImage src_img) {
            byte* src = (byte*)src_img.ImageData;
            return src[0] + src[src_img.ImageSize - (src_img.WidthStep - src_img.Width) - 1] + src[src_img.Width - 1] + src[src_img.ImageSize - src_img.Width - 1] > 511 ? Is.DESCENDING_ORDER : Is.ASCENDING_ORDER;
        }

        delegate byte SelectBucketMedian(int[] Bucket, int Median);

        private static byte GetBucketMedianDescendingOrder(int[] Bucket, int Median) {//明るい
            byte YIndex = 0;//byte 0 = 256.中央値を上(白)からを探す
            int ScanHalf = 0;//要素を半分カウントしたときのインデックス値が中央値
            while ((ScanHalf += Bucket[--YIndex]) < Median) ;//Underflow
            return YIndex;
        }
        private static byte GetBucketMedianAscendingOrder(int[] Bucket, int Median) {//暗い
            byte YIndex = 0;//中央値を下(黒)からを探す　　
            int ScanHalf = 0;
            while ((ScanHalf += Bucket[YIndex++]) < Median) ;//要素数を半分カウントしたときのインデックス値が中央値
            return --YIndex;
        }

        public static void Hage() {
            IplImage InputGrayImage = Cv.LoadImage(f, LoadMode.GrayScale);//グレースケールで読み込む
            IplImage MedianImage = Cv.CreateImage(InputGrayImage.Size, BitDepth.U8, 1);//出力先を作る
            FastestMedian(InputGrayImage , MedianImage ,5);//入力，出力，n*nのブロックでフィルタ
            Cv.ReleaseImage(InputGrayImage);
            Cv.ReleaseImage(MedianImage );
        }
```