आश्चर्य की बात है कि, इस समस्या पर वास्तव में बहुत सरल ऑटोफोकस एल्गोरिदम वास्तव में काफी अच्छा प्रदर्शन करते हैं। मैंने लियू, वांग & सूर्य द्वारा पेपर Dynamic evaluation of autofocusing for automated microscopic analysis of blood smear and pap smear में उल्लिखित 16 एल्गोरिदम में से 11 को कार्यान्वित किया। चूंकि मुझे थ्रेसहोल्ड मानों को सेट करने के लिए अनुशंसाएं ढूंढने में परेशानी थी, इसलिए मैंने थ्रेसहोल्ड के बिना कुछ प्रकार भी जोड़े। मैंने SO पर यहां एक सरल लेकिन चालाक सुझाव भी जोड़ा: जेपीईजी संकुचित छवियों के फ़ाइल आकार की तुलना करें (बड़ा आकार = अधिक विस्तार = बेहतर फोकस)। 2 सुक्ष्ममापी फोकस दूरी के अंतराल, कुल रेंज ± 20 सुक्ष्ममापी पर
हिस्टोग्राम रेंज को छोड़कर सभी एल्गोरिदम अच्छे परिणाम दिए। कुछ एल्गोरिदम थोड़ा संशोधित होते हैं, उदाहरण के लिए वे X & वाई दिशाओं में चमक अंतर का उपयोग करते हैं। मुझे फोकस स्थिति पर न्यूनतम की बजाय अधिकतम प्राप्त करने के लिए StdevBasedCorrelation, Entropy, ThresholdedContent, ImagePower और ThresholdedImagePower एल्गोरिदम का संकेत भी बदलना पड़ा। एल्गोरिदम 24 बिट ग्रेस्केल छवि की अपेक्षा करते हैं जहां आर = जी = बी। यदि रंगीन छवि पर उपयोग किया जाता है, तो केवल नीले चैनल की गणना की जाएगी (पाठ्यक्रम की आसानी से सही)।
- स्थिर फोकस स्थिति
- बड़े नकारात्मक x²:
इष्टतम सीमा मूल्यों एल्गोरिदम सीमा के साथ 0 महत्व देता है, 8 से 24 आदि 255 से ऊपर चल रहा है, 16, और के लिए सबसे अच्छा मूल्य का चयन करके मिला था गुणांक बहुपद फिट से
वर्गों का
- कम अवशिष्ट राशि फोकस की स्थिति में एक संकीर्ण चोटी में जिसके परिणामस्वरूप
यह दिलचस्प है ध्यान दें कि ThresholdedSq uaredGredient और ThresholdedBrennerGradient एल्गोरिदम में फोकस स्थिति, x² गुणांक और वर्गों के अवशिष्ट योग की लगभग समतल रेखा होती है। वे दहलीज मूल्य में परिवर्तन के लिए बहुत असंवेदनशील हैं।
एल्गोरिदम के कार्यान्वयन:
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 सुक्ष्ममापी की एक श्रृंखला के लिए सभी एल्गोरिदम के प्लॉट:
:
0 µm | 20 µm
हालात ± 50 सुक्ष्ममापी की एक श्रृंखला के लिए काफी समान दिखाई
0 µm | 50 µm
± 500 की एक सीमा सुक्ष्ममापी बातें और दिलचस्प हो जाता का उपयोग करते समय। चार एल्गोरिदम चौथे डिग्री बहुपद आकार का प्रदर्शन करते हैं, और अन्य गॉसियन कार्यों की तरह दिखने लगते हैं। इसके अलावा, हिस्टोग्राम रेंज एल्गोरिदम छोटी श्रेणियों के मुकाबले बेहतर प्रदर्शन करना शुरू कर देता है।
0 µm | 500 µm
कुल मिलाकर मैं काफी प्रदर्शन और इन सरल एल्गोरिदम की निरंतरता से प्रभावित हूँ। नग्न आंखों के साथ, यह कहना मुश्किल है कि 50 माइक्रोन छवि भी फोकस से बाहर है लेकिन एल्गोरिदम में कुछ माइक्रोन अलग छवियों की तुलना में कोई समस्या नहीं है।
जब आप बुनियादी एल्गोरिदम कोशिश क्या होता है के लिए काम करेंगे? क्या बहुत कम सिग्नल-टू-शोर है? –
ठीक है, मैंने अभी माना है कि एक मूल एल्गोरिदम 0, 5 और 10 माइक्रोन छवियों के बीच किसी भी वैध अंतर का पता नहीं लगाएगा। लेकिन मैंने अभी कुछ कोशिश की और वास्तव में काफी आशाजनक परिणाम मिल गए। मैं सिर्फ 1 माइक्रोन के अलावा और अधिक छवियां प्राप्त करूंगा और देख सकता हूं कि परिणाम अभी भी सतर्क हैं या नहीं। – Anlo