2013-03-12 18 views
7

マイクロメータスケールでの位置決めのためにオートフォーカスルーチンを開発していますので、画像間のフォーカス/ブラーの微妙な違いを見つける必要があります。上記の二つのより良い焦点画像を検索ぼかしの微妙な違いを検出するオートフォーカスルーチン

0 µm off50 µm off

  Perfect focus   |   50 µm off 

問題ではありません。幸いなことに、画像パターンは常に同じになります(これらは、256×256の中心オリジナル2枚のMP画像の作物はあります) 、私はほとんどのアルゴリズムが行うと思います。しかし、私は実際に下記のような、焦点ではるかに少ないの違いで画像を比較する必要があります。

5 µm off10 µm off

  5 µm off   |   10 µm off 

最適なフォーカスに近いと近いステッピングの代わりに持っている2枚の画像を見つけることですフォーカス面の反対側に同じ量のボケが発生する。例えば、-50μmから画像を保存し、次にぼけが等しい場合に+50μm付近の画像を見つけようとすることができる。画像が+58μmで見つかったとすると、焦点面は+4μmに位置するはずです。

適切なアルゴリズムのアイデアはありますか?

+1

のために働くのだろうか?信号対ノイズ比が小さすぎますか? –

+0

さて、私はちょうど、基本的なアルゴリズムは、0,5と10μmの画像の間の有効な違いを検出しないと仮定しました。しかし、私はちょうど今いくつかを試して、実際にはかなり有望な結果を得ました。 1μm離れたところにもっと多くの画像を取得し、結果がまだ飽和しているかどうかを確認します。 – Anlo

答えて

10

驚くべきことに、驚くほど多くの非常に単純なオートフォーカスアルゴリズムが、この問題に対して実際にかなりうまく機能しました。私は論文Dynamic evaluation of autofocusing for automated microscopic analysis of blood smear and pap smearに概要が示されている16個のアルゴリズムのうちの11個を、Liu、Wang & Sunによって実装しました。私はしきい値を設定するための推奨事項を見つけるのが難しかったので、しきい値のないいくつかのバリエーションも追加しました。私はまた、ここで見つけたシンプルで巧妙な提案を追加しました.JPEG圧縮画像のファイルサイズを比較します(より大きいサイズ=詳細=より良いフォーカス)。 2μmの焦点距離の間隔、合計範囲±20ミクロンで

  • 保存21枚の画像:

    マイオートフォーカスルーチンは以下のことを行い。

  • 各画像のフォーカス値を計算します。
  • 結果を2次多項式にフィッティングします。
  • 多項式の最大値を与える位置を求めます。

ヒストグラム範囲を除くすべてのアルゴリズムが良好な結果を出しました。いくつかのアルゴリズムはわずかに変更されています。たとえば、Y方向とY方向の両方で輝度差を使用します。また、StdevBasedCorrelation、Entropy、ThresholdedContent、ImagePower、ThresholdedImagePowerアルゴリズムの符号を変更して、フォーカス位置で最小値ではなく最大値を取得する必要がありました。アルゴリズムは、R = G = Bの24ビットのグレースケール画像を期待しています。カラー画像で使用すると、青のチャンネルのみが計算されます(もちろん簡単に修正できます)。

最適閾値を閾値は0、8、16、255から24までなど、最大、最高の値を選択するための値でアルゴリズムを実行することによって発見された:

    • 安定したフォーカス位置に大きな負のX 2多項式フィット

    から正方形の焦点位置

  • 低残留合計で狭いピークが得られた係数は、それはThresholdedSqことに注意することは興味深いですuaredGradientアルゴリズムとThresholdedBrennerGradientアルゴリズムは、ほぼ平坦な線の位置、x2係数、残差平方和を持ちます。これらは閾値の変化に対して非常に鈍感です。アルゴリズムの

    実装:

    public unsafe List<Result> CalculateFocusValues(string filename) 
    { 
        using(Bitmap bmp = new Bitmap(filename)) 
        { 
        int width = bmp.Width; 
        int height = bmp.Height; 
        int bpp = Bitmap.GetPixelFormatSize(bmp.PixelFormat)/8; 
        BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat); 
    
        long sum = 0, squaredSum = 0; 
        int[] histogram = new int[256]; 
    
        const int absoluteGradientThreshold = 148; 
        long absoluteGradientSum = 0; 
        long thresholdedAbsoluteGradientSum = 0; 
    
        const int squaredGradientThreshold = 64; 
        long squaredGradientSum = 0; 
        long thresholdedSquaredGradientSum = 0; 
    
        const int brennerGradientThreshold = 184; 
        long brennerGradientSum = 0; 
        long thresholdedBrennerGradientSum = 0; 
    
        long autocorrelationSum1 = 0; 
        long autocorrelationSum2 = 0; 
    
        const int contentThreshold = 35; 
        long thresholdedContentSum = 0; 
    
        const int pixelCountThreshold = 76; 
        long thresholdedPixelCountSum = 0; 
    
        const int imagePowerThreshold = 40; 
        long imagePowerSum = 0; 
        long thresholdedImagePowerSum = 0; 
    
        for(int row = 0; row < height - 1; row++) 
        { 
         for(int col = 0; col < width - 1; col++) 
         { 
         int current = *((byte *) (data.Scan0 + (row + 0) * data.Stride + (col + 0) * bpp)); 
         int col1 = *((byte *) (data.Scan0 + (row + 0) * data.Stride + (col + 1) * bpp)); 
         int row1 = *((byte *) (data.Scan0 + (row + 1) * data.Stride + (col + 0) * bpp)); 
    
         int squared = current * current; 
         sum += current; 
         squaredSum += squared; 
         histogram[current]++; 
    
         int colDiff1 = col1 - current; 
         int rowDiff1 = row1 - current; 
    
         int absoluteGradient = Math.Abs(colDiff1) + Math.Abs(rowDiff1); 
         absoluteGradientSum += absoluteGradient; 
         if(absoluteGradient >= absoluteGradientThreshold) 
          thresholdedAbsoluteGradientSum += absoluteGradient; 
    
         int squaredGradient = colDiff1 * colDiff1 + rowDiff1 * rowDiff1; 
         squaredGradientSum += squaredGradient; 
         if(squaredGradient >= squaredGradientThreshold) 
          thresholdedSquaredGradientSum += squaredGradient; 
    
         if(row < bmp.Height - 2 && col < bmp.Width - 2) 
         { 
          int col2 = *((byte *) (data.Scan0 + (row + 0) * data.Stride + (col + 2) * bpp)); 
          int row2 = *((byte *) (data.Scan0 + (row + 2) * data.Stride + (col + 0) * bpp)); 
    
          int colDiff2 = col2 - current; 
          int rowDiff2 = row2 - current; 
          int brennerGradient = colDiff2 * colDiff2 + rowDiff2 * rowDiff2; 
          brennerGradientSum += brennerGradient; 
          if(brennerGradient >= brennerGradientThreshold) 
          thresholdedBrennerGradientSum += brennerGradient; 
    
          autocorrelationSum1 += current * col1 + current * row1; 
          autocorrelationSum2 += current * col2 + current * row2; 
         } 
    
         if(current >= contentThreshold) 
          thresholdedContentSum += current; 
         if(current <= pixelCountThreshold) 
          thresholdedPixelCountSum++; 
    
         imagePowerSum += squared; 
         if(current >= imagePowerThreshold) 
          thresholdedImagePowerSum += current * current; 
         } 
        } 
        bmp.UnlockBits(data); 
    
        int pixels = width * height; 
        double mean = (double) sum/pixels; 
        double meanDeviationSquared = (double) squaredSum/pixels; 
    
        int rangeMin = 0; 
        while(histogram[rangeMin] == 0) 
         rangeMin++; 
        int rangeMax = histogram.Length - 1; 
        while(histogram[rangeMax] == 0) 
         rangeMax--; 
    
        double entropy = 0.0; 
        double log2 = Math.Log(2); 
        for(int i = rangeMin; i <= rangeMax; i++) 
        { 
         if(histogram[i] > 0) 
         { 
         double p = (double) histogram[i]/pixels; 
         entropy -= p * Math.Log(p)/log2; 
         } 
        } 
    
        return new List<Result>() 
        { 
         new Result("AbsoluteGradient",   absoluteGradientSum), 
         new Result("ThresholdedAbsoluteGradient", thresholdedAbsoluteGradientSum), 
         new Result("SquaredGradient",    squaredGradientSum), 
         new Result("ThresholdedSquaredGradient", thresholdedSquaredGradientSum), 
         new Result("BrennerGradient",    brennerGradientSum), 
         new Result("ThresholdedBrennerGradient", thresholdedBrennerGradientSum), 
         new Result("Variance",     meanDeviationSquared - mean * mean), 
         new Result("Autocorrelation",    autocorrelationSum1 - autocorrelationSum2), 
         new Result("StdevBasedCorrelation",  -(autocorrelationSum1 - pixels * mean * mean)), 
         new Result("Range",      rangeMax - rangeMin), 
         new Result("Entropy",      -entropy), 
         new Result("ThresholdedContent",   -thresholdedContentSum), 
         new Result("ThresholdedPixelCount",  thresholdedPixelCountSum), 
         new Result("ImagePower",     -imagePowerSum), 
         new Result("ThresholdedImagePower",  -thresholdedImagePowerSum), 
         new Result("JpegSize",     new FileInfo(filename).Length), 
        }; 
        } 
    } 
    
    public class Result 
    { 
        public string Algorithm { get; private set; } 
        public double Value  { get; private set; } 
    
        public Result(string algorithm, double value) 
        { 
        Algorithm = algorithm; 
        Value  = value; 
        } 
    } 
    

    それらは0と1(scaled = (value - min)/(max - min))の間の値にスケーリングされた異なるアルゴリズムの焦点値をプロットし、比較することができるようにします。 ±20μmの範囲のためのすべてのアルゴリズムの

    プロット:

    ±20 µm

    0 µm20 µm

       0 µm    |    20 µm 
    

    物事は±50μmの範囲のために非常に似ています±50 µm

    物事をより面白くし500μm±の範囲を使用して

    0 µm50 µm

       0 µm    |    50 µm 
    

    。 4つのアルゴリズムは、4次多項式形状の多くを示し、他のアルゴリズムは、ガウス関数のように見えるようになります。また、ヒストグラム範囲アルゴリズムは、より小さな範囲よりも優れた性能を発揮します。全体的に

    ±500 µm

    0 µm500 µm

       0 µm    |    500 µm 
    

    私は非常にこれらの簡単なアルゴリズムの性能と一貫性に感銘を受けています。肉眼では、50μmの画像でさえも焦点が合っていないが、アルゴリズムはわずか数ミクロン離れた画像を比較しても問題がないことは明らかです。

  • +0

    こんにちは、これは古い質問ですが、私は非常に似たような問題に取り組んでいます。だから、この点について「2次多項式にフィットさせることができます」と期待していました。 – NindzAI

    +0

    こんにちはNindzAl、私の余分な答えを参照してください(http://stackoverflow.com/a/17758994/306074) – Anlo

    +0

    @Anlo私はそれは非常に古い質問ですが、多分あなたはまた、 NASAの好奇心マーズローバー、私の答えを参照してくださいhttp://stackoverflow.com/a/32951113/15485 –

    3

    元の答えにNindzAlさんのコメントに余分な答え:

    私は、第二次多項式にシャープネス値に合うようにExtreme Optimizationライブラリを使用しています。最大シャープネスの距離は、多項式の1次導関数を使用して抽出されます。

    エクストリーム最適化ライブラリは、単一の開発ライセンスで999ドルかかりますが、フィッティングも同様に実行できるオープンソースの数学ライブラリがあると確信しています。無料のライブラリのよう

    // Distances (in µm) where the images were saved 
    double[] distance = new double[] 
    { 
        -50, 
        -40, 
        -30, 
        -20, 
        -10, 
        0, 
        +10, 
        +20, 
        +30, 
        +40, 
        +50, 
    }; 
    
    // Sharpness value of each image, as returned by CalculateFocusValues() 
    double[] sharpness = new double[] 
    { 
        3960.9, 
        4065.5, 
        4173.0, 
        4256.1, 
        4317.6, 
        4345.4, 
        4339.9, 
        4301.4, 
        4230.0, 
        4131.1, 
        4035.0, 
    }; 
    
    // Fit data to y = ax² + bx + c (second degree polynomial) using the Extreme Optimization library 
    const int SecondDegreePolynomial = 2; 
    Extreme.Mathematics.Curves.LinearCurveFitter fitter = new Extreme.Mathematics.Curves.LinearCurveFitter(); 
    fitter.Curve = new Extreme.Mathematics.Curves.Polynomial(SecondDegreePolynomial); 
    fitter.XValues = new Extreme.Mathematics.LinearAlgebra.GeneralVector(distance, true); 
    fitter.YValues = new Extreme.Mathematics.LinearAlgebra.GeneralVector(sharpness, true); 
    fitter.Fit(); 
    
    // Find distance of maximum sharpness using the first derivative of the polynomial 
    // Using the sample data above, the focus point is located at distance +2.979 µm 
    double focusPoint = fitter.Curve.GetDerivative().FindRoots().First(); 
    
    0

    、Math.Netは、基本的なアルゴリズムをしようとするとどうなりますか、その目的

    関連する問題