LoginSignup
2
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-09-14

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

ソートを使わない

要素を小さい順(大きい順)に半分カウントしたときのインデックス値が中央値

注目画素を隣にずらすときに前回のブロックを再利用

注目画素周辺のブロック(n*nの塊)を一々作り直すのではなく,範囲が被っているものを再利用し,ブロック作成時間を短縮

以下プログラム コピペで動きます 各自各言語に移植して下さい.

int nはブロックの大きさであり,3,5,7,9などを指定できる 本プログラムでは5以上のときOpenCVに勝てる,
特に7以上のとき顕著
FastestMedian(...)//超高速メデイアンフィルタ
SelectAscendingDescendingOrder(...)//入力画像(の背景)が暗めか明るめは判定する.暗ければ暗い方から中央値を走査する.
SelectBucketMedian//暗い方から走査するか明るい方から走査するか関数を使い分け
GetBucketMedianDescendingOrder(...)//明るい方から走査する
GetBucketMedianAscendingOrder(...)//暗い方から走査する
Hage()が呼び出し元

        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 );
        }
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3