using OpenCvSharp; using OpenCvSharp.Extensions; using PaintDotNet.Setting; using StageController; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Threading; namespace Metis.AutoAnalysis { public class AutoFocusWorkflow { private static bool _isWorking; public static double m_CurrentValue; public static int m_moveDirection = -1;//记录上次移动的方向 public static void Stop() { _isWorking = false; } // Add by shayg 20220718 start private static int orientation = 1; /// /// 自动调焦 /// /// 载物台控制器 /// 当前图片 /// 趋势窗口长度 /// 视场索引 /// 最清晰图片对象 public static (double Z, Bitmap Image) AutoFocusFastM3H(AxisController axisController, Func currentImage, int windowLength, int viewIndex, string dirCurrent) { var focusBitmap = (0d, currentImage()); if (_isWorking) { return focusBitmap; } int index = 0; int round = 0; var totalDistance = 0.0; var testValues = new List<(double Z, double Value, Bitmap Image)>(); var stepLength = FocusingParameter.getRuleFocus().StepLength; var maxTotalDistance = Math.Min(stepLength * 100, 500); if (stepLength * windowLength > maxTotalDistance) { Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\tStep Length is too large!(Step Length:{stepLength}, MaxTotalDistance:{maxTotalDistance}"); return focusBitmap; } _isWorking = true; Thread.Sleep(100); axisController.WaitMoveDone(); stepLength = stepLength * orientation; //var path = Path.Combine(dirCurrent, "FocusImages"); //if (!Directory.Exists(path)) //{ // Directory.CreateDirectory(path); //} while (_isWorking) { index = index + 1; totalDistance = totalDistance + stepLength; if (Math.Abs(totalDistance) >= maxTotalDistance) { Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\tOut of focus range!(TotalDistance:{totalDistance}, MaxTotalDistance:{maxTotalDistance})"); break; } var bitmap = currentImage(); var meanValue = getMeanValueOfBitmap(bitmap); testValues.Add((axisController.Z, meanValue, bitmap)); //SaveFocusImage(bitmap, path, viewIndex + 1, index, axisController.Z, meanValue); //Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\t{meanValue}"); if (IsChangeDir(testValues, windowLength)) { round = round + 1; if (testValues.Count >= windowLength * 2 - 1 || round == 2) { orientation = stepLength > 0 ? -1 : 1; var value = testValues[testValues.Count - windowLength]; focusBitmap = (value.Z, value.Image); //Logs.WriteFocus($"\t{viewIndex + 1}\t - \t{value.Z}\t{value.Value}"); break; } else { stepLength = -stepLength; ToUp(axisController, stepLength * testValues.Count); testValues = testValues.Take(testValues.Count - windowLength + 1).OrderByDescending(v=>v.Value).ToList(); } } else { ToUp(axisController, stepLength); } } _isWorking = false; return focusBitmap; } private static void SaveFocusImage(Bitmap img, string path, int viewIndex, int focusIndex, double z, double value) { var fileName = path + "\\" + string.Format($"{DateTime.Now.ToString("HHmmss")}_{viewIndex}_{focusIndex}_{z:f4}_{value:f6}.jpg"); try { img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg); } catch (Exception ex) { LogHelper.log.Error("image save error", ex); } } private static bool IsChangeDir(List<(double Z, double Value, Bitmap Image)> values, int windowLength) { if (values != null && values.Count >= windowLength) { var window = values.Skip(values.Count - windowLength).ToList(); var maxValues = window.OrderByDescending(v => v.Value).FirstOrDefault(); return window.First().Value == maxValues.Value; } return false; } private static bool ToUp(AxisController axisController, double distance) { var upto = axisController.Z + distance; axisController.Up(distance); axisController.WaitMoveDone(); while (Math.Abs(upto - axisController.Z) > 0.0625) { axisController.Up(upto - axisController.Z); axisController.WaitMoveDone(); if (Math.Abs(upto - axisController.Z) > 0.0625) { Logs.WriteFocus($"The z axis controller is not working properly(To:{upto}, Now:{axisController.Z})"); return false; } } return true; } // Add by shayg 20220718 end /// /// 自动对焦流程 /// /// /// public static void AutoFocusFast(AxisController stage, Func CurrentImage) { if (_isWorking) return; _isWorking = true; List focusList = new List(); double pulse = FocusingParameter.getRuleFocus().StepLength / 4;//最小步长1.25um double currentValue = 0.0; //int dir = -1; //方向 double trip = 0.0; // _mask = CreateMask(CurrentImage()); try { while (_isWorking) { //Console.Write("Before focus:"); stage.WaitMoveDone(); //Console.Write("自动聚焦:"); Thread.Sleep(40); try { Bitmap m_bitmap = CurrentImage(); currentValue = getMeanValueOfBitmap(m_bitmap); focusList.Add(currentValue); //Console.WriteLine(string.Format("{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z)); } catch (Exception ex) { return; } if (focusList.Count == 1) //这里是第一次运动 { stage.Up(pulse * m_moveDirection); continue; } //前两张确认调焦方向 if (focusList.Count == 2) { if (currentValue > focusList[focusList.Count - 2]) //判断是否跑对方向 { stage.Up(pulse * m_moveDirection);//对的方向 } else { m_moveDirection = -m_moveDirection; //相反方向 stage.Up(pulse * m_moveDirection);//这里不是跑回原来的位置,而是更过去一些,节约时间 Console.WriteLine("change direction"); } continue; } if (currentValue > focusList[focusList.Count - 2]) { //清晰度大于前一张图,继续同方向移动 trip += pulse * m_moveDirection; if (Math.Abs(trip) > pulse * 10) { Console.WriteLine("Trip of Z out of autofocus range."); return; } stage.Up(pulse * m_moveDirection); } else { //清晰度变小,返回峰值,对焦结束 int moveDir = -m_moveDirection; //相反方向 stage.Up(pulse * moveDir);//返回上一张图片 stage.WaitMoveDone(); return; } } } catch { } finally { // Console.Write("End focus:"); stage.WaitMoveDone(); //m_CurrentValue = getMeanValueOfBitmap(CurrentImage()); //Console.WriteLine(string.Format("聚焦完成:{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z)); _isWorking = false; // stage.LockZ(); } } //public static void AutoFocusFast(AxisController stage, Func CurrentImage) //{ // if (_isWorking) // return; // _isWorking = true; // int round = 0;//翻转次数 // double pulse = FocusingParameter.getRuleFocus().StepLength / 4;//最小步长1.25um // double lastValue = 0.0; // int dir = -1; //方向 // double trip = 0.0; // // _mask = CreateMask(CurrentImage()); // try // { // while (_isWorking) // { // //Console.Write("Before focus:"); // stage.WaitMoveDone(); // //Console.Write("自动聚焦:"); // Thread.Sleep(40); // try // { // Bitmap m_bitmap = CurrentImage(); // m_CurrentValue = getMeanValueOfBitmap(m_bitmap); // //Console.WriteLine(string.Format("{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z)); // } // catch (Exception ex) // { // return; // } // if (round > 1) // { // return; // } // else if (lastValue == 0) // { // } // else if (m_CurrentValue > lastValue) // { // round = 1; // } // else // { // //Console.WriteLine("Reverse:" + (dir > 0 ? "+" : "-")); // round++; // dir = -dir; // } // lastValue = m_CurrentValue; // trip += pulse * dir; // if (Math.Abs(trip) > pulse * 10) // { // Console.WriteLine("Trip of Z out of autofocus range."); // return; // } // stage.Up(pulse * dir); // } // } // catch // { // } // finally // { // // Console.Write("End focus:"); // stage.WaitMoveDone(); // //m_CurrentValue = getMeanValueOfBitmap(CurrentImage()); // //Console.WriteLine(string.Format("聚焦完成:{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z)); // _isWorking = false; // // stage.LockZ(); // } //} static Mat _mask; private static Mat CreateMask(Bitmap img) { Mat converted = PaintDotNet.Camera.Tools.ToMat(img); Mat imageGrey = new Mat(); try { if (converted.Channels() == 3) OpenCvSharp.Cv2.CvtColor(converted, imageGrey, OpenCvSharp.ColorConversionCodes.RGB2GRAY); else if (converted.Channels() == 1) imageGrey = converted; } catch (Exception) { imageGrey = converted; } //stage.SetSpeedZ(40); Mat mask = new Mat(); Cv2.Threshold(imageGrey, mask, 0, 255, ThresholdTypes.Triangle); var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20)); Cv2.Dilate(mask, mask, kernel); //Cv2.Erode(mask, mask, kernel); return mask; } private static Mat CreateMask(Mat imageGrey) { Mat mask = new Mat(); Cv2.Threshold(imageGrey, mask, 0, 255, ThresholdTypes.Triangle); var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20)); Cv2.Dilate(mask, mask, kernel); return mask; } /// /// 获取输入图片的清晰度 /// /// /// private static double getMeanValueOfBitmap(Bitmap bitmap) { // Modify by shayg 20220718 start //Mat converted = PaintDotNet.Camera.Tools.ToMat(bitmap); Mat converted = BitmapConverter.ToMat(bitmap); // Modify by shayg 20220718 end Mat imageGrey = new Mat(); Mat imageSobel = new Mat(); try { if (converted.Channels() == 3) OpenCvSharp.Cv2.CvtColor(converted, imageGrey, OpenCvSharp.ColorConversionCodes.RGB2GRAY); else if (converted.Channels() == 1) imageGrey = converted; } catch (Exception) { imageGrey = converted; } OpenCvSharp.Mat meanValueImage = new OpenCvSharp.Mat(); OpenCvSharp.Mat meanStdValueImage = new OpenCvSharp.Mat(); // Modify by shayg 20220718 start //_mask = CreateMask(imageGrey); ////求灰度图像的标准差 值越大越好 //OpenCvSharp.Cv2.MeanStdDev(imageGrey, meanValueImage, meanStdValueImage, _mask); Cv2.MeanStdDev(imageGrey, meanValueImage, meanStdValueImage); // Modify by shayg 20220718 end return meanStdValueImage.At(0, 0); } // Add by shayg 20220718 start /// /// 获取输入图片的清晰度(拉普拉斯边缘清晰度判定) /// /// 原图片 /// 清晰度(值越大越清晰) private static double getMeanValueOfBitmap2(Bitmap bitmap) { var converted = BitmapConverter.ToMat(bitmap); var imageGray = new Mat(); if (converted.Channels() == 1) { imageGray = converted; } else { Cv2.CvtColor(converted, imageGray, ColorConversionCodes.RGB2GRAY); } var dst = new Mat(); Cv2.Laplacian(imageGray, dst, MatType.CV_64F); Cv2.MeanStdDev(dst, out var mean, out var dev); var meanValue = dev.Val0 * dev.Val0; return meanValue; } // Add by shayg 20220718 end } }