Пример #1
0
        /// <summary>
        /// GaussianBlurを施す。横方向と縦方向を分離するため、高速。
        /// </summary>
        /// <param name="pixels">画像データの一次元配列</param>
        /// <param name="width">画像の幅</param>
        /// <param name="radius">ピクセル単位でのフィルムにじみ半値半幅 </param>
        /// <returns></returns>
        unsafe static public double[] GaussianBlurFast(double[] pixels, int width, double hwhm)
        {
            int limit  = (int)(hwhm * 3) * 2 + 1;
            int height = pixels.Length / width;

            int center = limit / 2;

            double[] pixelsFilmBlur1 = new double[width * height];
            double[] pixelsFilmBlur2 = new double[width * height];

            if (limit == 1)
            {
                for (int i = 0; i < pixels.Length; i++)
                {
                    pixelsFilmBlur2[i] = pixels[i];
                }
                return(pixelsFilmBlur2);
            }

            double[] blur = new double[limit];
            for (int h = 0; h < limit; h++)
            {
                blur[h] = Math.Exp(-(h - center) * (h - center) / hwhm / hwhm * Math.Log(2));
            }
            blur = Statistics.Normarize(blur);

            Parallel.For(0, width, w =>
            {
                for (int h = 0; h < height; h++)
                {
                    //if (pixels[h * width + w] != 0)
                    //for (int n = Math.Max(0, center - h); n < Math.Min(blur.Length, height - h + center); n++)
                    //    pixelsFilmBlur1[(h - center + n) * width + w] += blur[n] * pixels[h * width + w];

                    for (int n = Math.Max(0, center - h); n < Math.Min(blur.Length, height - h + center); n++)
                    {
                        pixelsFilmBlur1[h * width + w] += blur[n] * pixels[(h - center + n) * width + w];
                    }
                }
            });

            Parallel.For(0, height, h =>
            {
                for (int w = 0; w < width; w++)
                {
                    //if (pixelsFilmBlur1[h * width + w] != 0)
                    //for (int n = Math.Max(0, center - w); n < Math.Min(blur.Length, width - w + center); n++)
                    //    pixelsFilmBlur2[h * width + w - center + n] += blur[n] * pixelsFilmBlur1[h * width + w];
                    for (int n = Math.Max(0, center - w); n < Math.Min(blur.Length, width - w + center); n++)
                    {
                        pixelsFilmBlur2[h * width + w] += blur[n] * pixelsFilmBlur1[h * width + w - center + n];
                    }
                }
            });
            pixelsFilmBlur1 = null;

            return(pixelsFilmBlur2);
        }
Пример #2
0
        /// <summary>
        /// 複数ピークをフィッティングする. 戻り値は、R値
        /// </summary>
        /// <param name="pt">フィッティング対象プロファイルのデータ配列</param>
        /// <param name="BackgroundFitting">バックグラウンドをフィッティングするかどうか</param>
        /// <param name="RemoveBadSN">SN比の悪いピークについてはNaNを返すかどうか</param>
        /// <param name="p">ピーク関数 格納されている中心値、半値幅、フィッティングRangeを設定しておく</param>
        public static double FitMultiPeaksThread(PointD[] pt, bool BackgroundFitting, double RemoveBadSN, ref PeakFunction[] p)
        {
            //マルカールの方法でPseudoVoigtをとく。高速になるはず

            if (p.Length == 0)
            {
                return(double.PositiveInfinity);
            }
            if (pt == null || pt.Length < 3)
            {
                return(double.PositiveInfinity);
            }

            //まずここからプロファイルをとる領域や強度ななどの情報を集める
            List <double> PtX     = new List <double>();
            List <double> PtY     = new List <double>();
            double        sum     = 0;
            double        temp    = double.NegativeInfinity;
            double        tempMin = double.PositiveInfinity;

            //指定された範囲内のプロファイルをPtX,PtYに格納
            for (int i = 0; i < pt.Length; i++)
            {
                if (pt[i].X >= Math.Min(p[0].X - p[0].range, p[p.Length - 1].X - p[p.Length - 1].range) && pt[i].X <= Math.Max(p[0].X + p[0].range, p[p.Length - 1].X + p[p.Length - 1].range))
                {
                    PtX.Add(pt[i].X);
                    PtY.Add(pt[i].Y / 1000);
                    sum += pt[i].Y / 1000;
                    if (temp < pt[i].Y / 1000)
                    {
                        temp = pt[i].Y / 1000;
                    }
                }
            }

            //p[i].SerchPeakTop
            for (int i = 0; i < p.Length; i++)
            {
                if (p[i].SerchPeakTop)
                {
                    double max   = double.NegativeInfinity;
                    double tempX = 0;
                    for (int j = 0; j < PtX.Count; j++)
                    {
                        if (PtX[j] >= p[i].X - p[i].range && PtX[j] <= p[0].X + p[0].range)
                        {
                            if (max < PtY[j])
                            {
                                max   = PtY[j];
                                tempX = PtX[j];
                            }
                        }
                    }
                    p[i].X = tempX;
                }
            }

            //最も小さい強度を探す
            for (int i = 0; i < pt.Length; i++)
            {
                if (pt[i].X >= Math.Min(p[0].X - p[0].range, p[p.Length - 1].X - p[p.Length - 1].range) && pt[i].X <= Math.Max(p[0].X + p[0].range, p[p.Length - 1].X + p[p.Length - 1].range))
                {
                    if (tempMin > pt[i].Y / 1000)
                    {
                        tempMin = pt[i].Y / 1000;
                    }
                }
            }

            if (PtY.Count < 3 || /*temp <= 0 ||*/ (PtY[PtY.Count - 1] == 0 && PtY[PtY.Count - 2] == 0 && PtY[PtY.Count - 3] == 0))
            {
                for (int i = 0; i < p.Length; i++)
                {
                    p[i].X   = double.NaN;
                    p[i].Int = double.NaN;
                }
                return(double.PositiveInfinity);
            }

            double[] x      = PtX.ToArray();
            double[] y      = PtY.ToArray();
            int      length = x.Length;
            //ここまででプロファイルの領域などを決定

            //ここから決めなければいけないパラメータの数を設定する
            int ParamNum = 0;

            for (int i = 0; i < p.Length; i++)
            {
                ParamNum += p[i].GetParamNumber();
            }
            if (ParamNum < 0)
            {
                for (int i = 0; i < p.Length; i++)
                {
                    p[i].X   = double.NaN;
                    p[i].Int = double.NaN;
                }
                return(double.PositiveInfinity);
            }

            double[,] diff = new double[ParamNum + 2, length];
            var Alpha = new DenseMatrix(ParamNum + 2, ParamNum + 2);
            var Beta  = new DenseMatrix(ParamNum + 2, 1);

            PeakFunction[] pCurrent = new PeakFunction[p.Length];
            PeakFunction[] pNew     = new PeakFunction[p.Length];
            for (int i = 0; i < p.Length; i++)
            {
                pCurrent[i] = new PeakFunction();
                pNew[i]     = new PeakFunction();
                pCurrent[i] = p[i].Copy();
            }
            //ここまで

            //Intの値を大雑把に決める
            double[] peakIntensity = new double[p.Length];
            for (int i = 0; i < p.Length; i++)
            {
                double d = double.PositiveInfinity;
                for (int j = 0; j < PtX.Count; j++)
                {
                    if (d > Math.Abs(x[j] - p[i].X))
                    {
                        d = Math.Abs(x[j] - p[i].X);
                        peakIntensity[i] = y[j];
                    }
                }
                peakIntensity[i] -= tempMin;
            }
            //相対値として再配分する
            peakIntensity = Statistics.Normarize(peakIntensity);

            double[] ResidualCurrent = new double[length];
            double[] ResidualNew     = new double[length];
            double   ResidualSquareCurrent;
            double   ResidualSquareNew = 0;
            double   residual;
            double   centerX = (x[0] + x[x.Length - 1]) / 2;
            double   B1, B2, B1_New, B2_New;

            B1 = B2 = 0;

            double bestResidual = double.PositiveInfinity;
            int    bestInitial  = 0;
            int    startInitial = 0;
            int    endInitial   = 1;// 2;
            int    counter      = 0;

            for (int Initial = startInitial; Initial < endInitial; Initial++)
            {
                double[] c = new double[3];
                switch (Initial)
                {
                case 00: c = new double[] { 1, 1, 1 }; break;

                case 01: c = new double[] { 1, 1, 2 }; break;

                case 02: c = new double[] { 1, 1, 0.5 }; break;

                case 03: c = new double[] { 0.5, 1, 1 }; break;

                case 04: c = new double[] { 0.5, 1, 2 }; break;

                case 05: c = new double[] { 0.5, 1, 0.5 }; break;

                case 06: c = new double[] { 2, 1, 1 }; break;

                case 07: c = new double[] { 2, 1, 0.5 }; break;

                case 08: c = new double[] { 2, 1, 2 }; break;

                case 09: c = new double[] { 1, 2, 1 }; break;

                case 10: c = new double[] { 1, 0.5, 1 }; break;

                case 11: c = new double[] { 0.5, 2, 2 }; break;
                }
                //ここから初期値をきめる
                //Xはすでに代入済み
                //Int以外のの初期値を大雑把に決める
                for (int i = 0; i < p.Length; i++)
                {
                    pCurrent[i].X = p[i].X;

                    if (p[i].Hk > 0)
                    {
                        pCurrent[i].Hk = p[i].Hk * c[0];
                    }
                    else
                    {
                        pCurrent[i].Hk = p[i].range * 0.5 * c[0];
                    }

                    pCurrent[i].eta  = 0.5 * c[1];
                    pCurrent[i].etaH = 0.5 * c[1];
                    pCurrent[i].etaL = 0.5 * c[1];
                    pCurrent[i].m    = 2 * c[1];
                    pCurrent[i].Rl   = 2 * c[1];
                    pCurrent[i].Rh   = 2 * c[1];
                    pCurrent[i].A    = 0;
                }
                B1 = tempMin;
                B2 = 0;
                //Int
                for (int i = 0; i < p.Length; i++)
                {
                    pCurrent[i].Int = 1;
                    pCurrent[i].Int = (sum - tempMin * x.Length) * (x[1] - x[0]) * peakIntensity[i] / pCurrent[i].GetIntegral() * c[2];
                }
                //ここまで初期値決め

                //現在の残差を計算
                ResidualSquareCurrent = 0;
                double[] IntCurrent = new double[x.Length];
                double[] IntNew     = new double[x.Length];
                for (int i = 0; i < length; i++)
                {
                    IntCurrent[i] = B1 + B2 * (x[i] - centerX);
                }
                for (int j = 0; j < p.Length; j++)
                {
                    pCurrent[j].RenewParameter();
                    for (int i = 0; i < length; i++)
                    {
                        IntCurrent[i] += pCurrent[j].GetValue(x[i], false);
                    }
                }
                for (int i = 0; i < length; i++)
                {
                    ResidualCurrent[i]     = y[i] - IntCurrent[i];
                    ResidualSquareCurrent += ResidualCurrent[i] * ResidualCurrent[i];
                }

                double ramda = 100;
                counter = 0;
                do
                {
                    counter++;
                    //偏微分を作る
                    int n = 0;
                    int m = 0;
                    for (int j = 0; j < p.Length; j++)
                    {
                        n = m;
                        pCurrent[j].RenewParameter();
                        for (int i = 0; i < length; i++)
                        {
                            m = n;
                            double[] d = pCurrent[j].GetDifferentialValue(x[i], false);
                            for (int k = 0; k < d.Length; k++)
                            {
                                diff[m, i] = d[k];
                                m++;
                            }
                        }
                    }
                    for (int i = 0; i < length; i++)
                    {//バックグラウンド変数の偏微分
                        diff[ParamNum, i]     = 1;
                        diff[ParamNum + 1, i] = x[i] - centerX;
                    }
                    //偏微分を作る ここまで

                    //行列Alpha, Betaを作る
                    for (int i = 0; i < ParamNum + 2; i++)
                    {
                        for (int j = i; j < ParamNum + 2; j++)
                        {
                            Alpha[i, j] = 0;
                            for (int k = 0; k < length; k++)
                            {
                                Alpha[i, j] += diff[i, k] * diff[j, k];
                            }
                            Alpha[j, i] = Alpha[i, j];
                            if (i == j)
                            {
                                Alpha[i, j] *= (1 + ramda);
                            }
                        }
                        Beta[i, 0] = 0;
                        for (int k = 0; k < length; k++)
                        {
                            Beta[i, 0] += ResidualCurrent[k] * diff[i, k];
                        }
                    }
                    //行列Alpha、Beta、ここまで

                    var alphaInv = Alpha.TryInverse();

                    if (alphaInv == null)
                    {
                        for (int i = 0; i < p.Length; i++)
                        {
                            p[i].X   = double.NaN;
                            p[i].Int = double.NaN;
                        }
                        return(double.PositiveInfinity);
                    }

                    var delta = alphaInv * Beta;

                    //新しいパラメータをセットして適宜修正
                    n = 0;
                    bool flag = true;
                    for (int j = 0; j < p.Length; j++)
                    {
                        pNew[j] = pCurrent[j].Copy();
                        if (p[j].Option == PeakFunctionForm.PseudoVoigt)//PseudoVoigt:      Int, Eta, Hk, X ;
                        {
                            pNew[j].Int = pCurrent[j].Int + delta[n++, 0];
                            pNew[j].eta = pCurrent[j].eta + delta[n++, 0];
                            pNew[j].Hk  = pCurrent[j].Hk + delta[n++, 0];
                            pNew[j].X   = pCurrent[j].X + delta[n++, 0];

                            //if (pNew[j].eta < 0 || pNew[j].eta > 1)
                            //    flag = false;
                            if (pNew[j].eta < 0)
                            {
                                pNew[j].eta = 0;
                            }
                            if (pNew[j].eta > 1)
                            {
                                pNew[j].eta = 1;
                            }
                        }
                        else if (p[j].Option == PeakFunctionForm.Peason)//PearsonVII:       Int, Hk,  m,  X
                        {
                            pNew[j].Int = pCurrent[j].Int + delta[n++, 0];
                            pNew[j].Hk  = pCurrent[j].Hk + delta[n++, 0];
                            pNew[j].m   = pCurrent[j].m + delta[n++, 0];
                            pNew[j].X   = pCurrent[j].X + delta[n++, 0];

                            if (pNew[j].m <= 0.5 || pNew[j].m >= 10)
                            {
                                flag = false;
                            }
                        }
                        else if (p[j].Option == PeakFunctionForm.SplitPseudoVoigt)//SplitPseudoVoigt:  Int, Hk,  A,  etaL , etaH, X
                        {
                            pNew[j].Int  = pCurrent[j].Int + delta[n++, 0];
                            pNew[j].Hk   = pCurrent[j].Hk + delta[n++, 0];
                            pNew[j].A    = pCurrent[j].A + delta[n++, 0];
                            pNew[j].etaL = pCurrent[j].etaL + delta[n++, 0];
                            pNew[j].etaH = pCurrent[j].etaH + delta[n++, 0];
                            pNew[j].X    = pCurrent[j].X + delta[n++, 0];
                            if (pNew[j].etaL < 0 || pNew[j].etaL > 1)
                            {
                                flag = false;
                            }
                            if (pNew[j].etaH < 0 || pNew[j].etaH > 1)
                            {
                                flag = false;
                            }
                            if (pNew[j].A < -2 || pNew[j].A > 2)
                            {
                                flag = false;
                            }
                        }
                        else if (p[j].Option == PeakFunctionForm.SplitPearson)//SplitPearsonVII:  Int, Hk,  A,  Rl , Rh, X
                        {
                            pNew[j].Int = pCurrent[j].Int + delta[n++, 0];
                            pNew[j].Hk  = pCurrent[j].Hk + delta[n++, 0];
                            pNew[j].A   = pCurrent[j].A + delta[n++, 0];
                            pNew[j].Rl  = pCurrent[j].Rl + delta[n++, 0];
                            pNew[j].Rh  = pCurrent[j].Rh + delta[n++, 0];
                            pNew[j].X   = pCurrent[j].X + delta[n++, 0];
                            if (pNew[j].Rl <= 0.5 || pNew[j].Rl >= 10)
                            {
                                flag = false;
                            }
                            if (pNew[j].Rh <= 0.5 || pNew[j].Rh >= 10)
                            {
                                flag = false;
                            }
                            if (pNew[j].A < -2 || pNew[j].A > 2)
                            {
                                flag = false;
                            }
                        }

                        if (pNew[j].Hk < 0)
                        {
                            flag = false; //pNew[j].Hk = pCurrent[j].Hk;
                        }
                        //2本以上のフィッティングの場合、XがRangeの1/8以上動いたらもどす
                        if (p.Length > 1)
                        {
                            if (pNew[j].X - p[j].X > p[j].range / 8 || pNew[j].X - p[j].X < -p[j].range / 8)
                            {
                                pNew[j].X = pCurrent[j].X;
                            }
                        }
                        //    flag = false; //pNew[j].X = p[j].X + p[j].range / 4;
                    }
                    B1_New = B1 + delta[ParamNum, 0];
                    B2_New = B2 + delta[ParamNum + 1, 0];
                    //if (B1_New + B2_New * (x[0] - centerX) < 0 || B1_New + B2_New * (x[x.Length - 1] - centerX) < 0)
                    //    flag = false; //B1_New = B1;//B2_New = B2;

                    //あたらしいパラメータでの残差を計算
                    if (flag)
                    {
                        ResidualSquareNew = 0;
                        for (int i = 0; i < length; i++)
                        {
                            IntNew[i] = B1_New + B2_New * (x[i] - centerX);
                        }
                        for (int j = 0; j < p.Length; j++)
                        {
                            pNew[j].RenewParameter();
                            for (int i = 0; i < length; i++)
                            {
                                IntNew[i] += pNew[j].GetValue(x[i], false);
                            }
                        }
                        for (int i = 0; i < length; i++)
                        {
                            ResidualNew[i]     = y[i] - IntNew[i];
                            ResidualSquareNew += ResidualNew[i] * ResidualNew[i];
                        }
                    }
                    //残差計算ここまで

                    //新旧の値を比較
                    if (flag && ResidualSquareCurrent >= ResidualSquareNew)
                    {
                        ramda *= 0.8;
                        for (int i = 0; i < length; i++)
                        {
                            ResidualCurrent[i] = ResidualNew[i];
                        }
                        for (int j = 0; j < p.Length; j++)
                        {
                            pCurrent[j] = pNew[j].Copy();
                        }
                        B1 = B1_New;
                        B2 = B2_New;
                        if (counter > 50 && (ResidualSquareCurrent - ResidualSquareNew) / ResidualSquareCurrent < 0.0000000001)
                        {
                            break;
                        }
                        ResidualSquareCurrent = ResidualSquareNew;
                    }
                    else
                    {
                        ramda *= 2;
                    }
                } while (ramda < 100000000000 && counter < 1000);

                for (int i = 0; i < length; i++)
                {
                    IntNew[i] = B1_New + B2_New * (x[i] - centerX);
                }
                for (int j = 0; j < p.Length; j++)
                {
                    pNew[j].RenewParameter();
                    for (int i = 0; i < length; i++)
                    {
                        IntNew[i] += pNew[j].GetValue(x[i], false);
                    }
                }
                residual = 0;
                for (int i = 0; i < length; i++)
                {
                    residual += (IntNew[i] - y[i]) / IntNew[i] * (IntNew[i] - y[i]) / IntNew[i];
                }
                residual /= length;
                if (bestResidual > residual)//最も結果のよかったカウントを格納
                {
                    bestResidual = residual;
                    bestInitial  = Initial;
                }
                if (Math.Sqrt(residual) < 0.25)//平均2乗残差が5%以下だったら終了
                {
                    break;
                }
                if (Initial == endInitial - 1 && endInitial - startInitial > 2)//5%以下のものが見つからなかったとき
                {
                    //50%を超えるくらいひどかったら
                    if (Math.Sqrt(bestResidual) > 0.90)
                    {
                        for (int i = 0; i < p.Length; i++)
                        {
                            p[i].X   = double.NaN;
                            p[i].Int = double.NaN;
                        }
                        return(double.PositiveInfinity);
                    }
                    //一番ましな初期値でやり直す
                    startInitial = bestInitial - 1;
                    Initial      = bestInitial - 1;
                    endInitial   = bestInitial + 1;
                }
            }

            //行列アルファを決める
            //行列Alpha, Betaを作る
            for (int i = 0; i < ParamNum + 2; i++)
            {
                for (int j = i; j < ParamNum + 2; j++)
                {
                    Alpha[i, j] = 0;
                    for (int k = 0; k < length; k++)
                    {
                        Alpha[i, j] += diff[i, k] * diff[j, k];
                    }
                    Alpha[j, i] = Alpha[i, j];
                }
            }
            counter = 0;
            //最後にそれぞれもとまった数値を入れる。
            for (int i = 0; i < p.Length; i++)
            {
                //p[i] = pCurrent[i].Copy();
                p[i].Option    = pCurrent[i].Option;
                p[i].intensity = pCurrent[i].intensity;
                p[i].eta       = pCurrent[i].eta;
                p[i].etaH      = pCurrent[i].etaH;
                p[i].etaL      = pCurrent[i].etaL;
                p[i].Rl        = pCurrent[i].Rl;
                p[i].Rh        = pCurrent[i].Rh;
                p[i].X         = pCurrent[i].X;
                p[i].Hk        = pCurrent[i].Hk;
                p[i].Int       = pCurrent[i].Int;
                p[i].A         = pCurrent[i].A;
                p[i].B1        = pCurrent[i].B1;
                p[i].B2        = pCurrent[i].B2;
                p[i].B3        = pCurrent[i].B3;
                counter       += p[i].GetParamNumber();
                p[i].Xerr      = 1 / Math.Sqrt(Alpha[counter - 1, counter - 1]);
                p[i].m         = pCurrent[i].m;
                p[i].range     = pCurrent[i].range;
                p[i].Residual  = bestResidual;
                p[i].Int      *= 1000;
                p[i].B1        = B1 + B2 * (pCurrent[i].X - centerX);
                p[i].B2        = B2;
                p[i].B1       *= 1000;
                p[i].B2       *= 1000;
            }

            //これより下は主にIPAからだけ呼ばれる部分
            if (RemoveBadSN > 0)
            {
                //S/N比が悪いデータは除去する
                double BackGround = 0;
                double Signal     = 0;
                p[0].RenewParameter();
                for (int i = 0; i < length; i++)
                {
                    Signal     += p[0].GetValue(x[i], false);
                    BackGround += p[0].B1 + p[0].B2 * (p[0].X - x[i]);
                }
                if (double.IsNaN(Signal / (BackGround + Signal)) || double.IsInfinity(Signal / (BackGround + Signal)) || (Signal / (BackGround + Signal)) < RemoveBadSN)
                {
                    p[0].X = double.NaN;
                }

                //中心位置が外れて過ぎても失格
                if (p[0].X < x[0] || x[x.Length - 1] < p[0].X)
                {
                    p[0].X = double.NaN;
                }
            }

            /*if (counter < 1000)
             *  return true;
             * else
             *  return false;*/
            return(bestResidual);
        }