AutoFocusWorkflow.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. using OpenCvSharp;
  2. using OpenCvSharp.Extensions;
  3. using PaintDotNet.Setting;
  4. using StageController;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Drawing;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Threading;
  11. namespace Metis.AutoAnalysis
  12. {
  13. public class AutoFocusWorkflow
  14. {
  15. private static bool _isWorking;
  16. public static double m_CurrentValue;
  17. public static int m_moveDirection = -1;//记录上次移动的方向
  18. public static void Stop()
  19. {
  20. _isWorking = false;
  21. }
  22. // Add by shayg 20220718 start
  23. private static int orientation = 1;
  24. /// <summary>
  25. /// 自动调焦
  26. /// </summary>
  27. /// <param name="axisController">载物台控制器</param>
  28. /// <param name="currentImage">当前图片</param>
  29. /// <param name="windowLength">趋势窗口长度</param>
  30. /// <param name="viewIndex">视场索引</param>
  31. /// <returns>最清晰图片对象</returns>
  32. public static (double Z, Bitmap Image) AutoFocusFastM3H(AxisController axisController, Func<Bitmap> currentImage, int windowLength, int viewIndex, string dirCurrent)
  33. {
  34. var focusBitmap = (0d, currentImage());
  35. if (_isWorking)
  36. {
  37. return focusBitmap;
  38. }
  39. int index = 0;
  40. int round = 0;
  41. var totalDistance = 0.0;
  42. var testValues = new List<(double Z, double Value, Bitmap Image)>();
  43. var stepLength = FocusingParameter.getRuleFocus().StepLength;
  44. var maxTotalDistance = Math.Min(stepLength * 100, 500);
  45. if (stepLength * windowLength > maxTotalDistance)
  46. {
  47. Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\tStep Length is too large!(Step Length:{stepLength}, MaxTotalDistance:{maxTotalDistance}");
  48. return focusBitmap;
  49. }
  50. _isWorking = true;
  51. Thread.Sleep(100);
  52. axisController.WaitMoveDone();
  53. stepLength = stepLength * orientation;
  54. //var path = Path.Combine(dirCurrent, "FocusImages");
  55. //if (!Directory.Exists(path))
  56. //{
  57. // Directory.CreateDirectory(path);
  58. //}
  59. while (_isWorking)
  60. {
  61. index = index + 1;
  62. totalDistance = totalDistance + stepLength;
  63. if (Math.Abs(totalDistance) >= maxTotalDistance)
  64. {
  65. Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\tOut of focus range!(TotalDistance:{totalDistance}, MaxTotalDistance:{maxTotalDistance})");
  66. break;
  67. }
  68. var bitmap = currentImage();
  69. var meanValue = getMeanValueOfBitmap(bitmap);
  70. testValues.Add((axisController.Z, meanValue, bitmap));
  71. //SaveFocusImage(bitmap, path, viewIndex + 1, index, axisController.Z, meanValue);
  72. //Logs.WriteFocus($"\t{viewIndex + 1}\t{index}\t{axisController.Z}\t{meanValue}");
  73. if (IsChangeDir(testValues, windowLength))
  74. {
  75. round = round + 1;
  76. if (testValues.Count >= windowLength * 2 - 1 || round == 2)
  77. {
  78. orientation = stepLength > 0 ? -1 : 1;
  79. var value = testValues[testValues.Count - windowLength];
  80. focusBitmap = (value.Z, value.Image);
  81. //Logs.WriteFocus($"\t{viewIndex + 1}\t - \t{value.Z}\t{value.Value}");
  82. break;
  83. }
  84. else
  85. {
  86. stepLength = -stepLength;
  87. ToUp(axisController, stepLength * testValues.Count);
  88. testValues = testValues.Take(testValues.Count - windowLength + 1).OrderByDescending(v=>v.Value).ToList();
  89. }
  90. }
  91. else
  92. {
  93. ToUp(axisController, stepLength);
  94. }
  95. }
  96. _isWorking = false;
  97. return focusBitmap;
  98. }
  99. private static void SaveFocusImage(Bitmap img, string path, int viewIndex, int focusIndex, double z, double value)
  100. {
  101. var fileName = path + "\\" + string.Format($"{DateTime.Now.ToString("HHmmss")}_{viewIndex}_{focusIndex}_{z:f4}_{value:f6}.jpg");
  102. try
  103. {
  104. img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
  105. }
  106. catch (Exception ex)
  107. {
  108. LogHelper.log.Error("image save error", ex);
  109. }
  110. }
  111. private static bool IsChangeDir(List<(double Z, double Value, Bitmap Image)> values, int windowLength)
  112. {
  113. if (values != null && values.Count >= windowLength)
  114. {
  115. var window = values.Skip(values.Count - windowLength).ToList();
  116. var maxValues = window.OrderByDescending(v => v.Value).FirstOrDefault();
  117. return window.First().Value == maxValues.Value;
  118. }
  119. return false;
  120. }
  121. private static bool ToUp(AxisController axisController, double distance)
  122. {
  123. var upto = axisController.Z + distance;
  124. axisController.Up(distance);
  125. axisController.WaitMoveDone();
  126. while (Math.Abs(upto - axisController.Z) > 0.0625)
  127. {
  128. axisController.Up(upto - axisController.Z);
  129. axisController.WaitMoveDone();
  130. if (Math.Abs(upto - axisController.Z) > 0.0625)
  131. {
  132. Logs.WriteFocus($"The z axis controller is not working properly(To:{upto}, Now:{axisController.Z})");
  133. return false;
  134. }
  135. }
  136. return true;
  137. }
  138. // Add by shayg 20220718 end
  139. /// <summary>
  140. /// 自动对焦流程
  141. /// </summary>
  142. /// <param name="stage"></param>
  143. /// <param name="CurrentImage"></param>
  144. public static void AutoFocusFast(AxisController stage, Func<Bitmap> CurrentImage)
  145. {
  146. if (_isWorking)
  147. return;
  148. _isWorking = true;
  149. List<double> focusList = new List<double>();
  150. double pulse = FocusingParameter.getRuleFocus().StepLength / 4;//最小步长1.25um
  151. double currentValue = 0.0;
  152. //int dir = -1; //方向
  153. double trip = 0.0;
  154. // _mask = CreateMask(CurrentImage());
  155. try
  156. {
  157. while (_isWorking)
  158. {
  159. //Console.Write("Before focus:");
  160. stage.WaitMoveDone();
  161. //Console.Write("自动聚焦:");
  162. Thread.Sleep(40);
  163. try
  164. {
  165. Bitmap m_bitmap = CurrentImage();
  166. currentValue = getMeanValueOfBitmap(m_bitmap);
  167. focusList.Add(currentValue);
  168. //Console.WriteLine(string.Format("{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z));
  169. }
  170. catch (Exception ex)
  171. {
  172. return;
  173. }
  174. if (focusList.Count == 1) //这里是第一次运动
  175. {
  176. stage.Up(pulse * m_moveDirection);
  177. continue;
  178. }
  179. //前两张确认调焦方向
  180. if (focusList.Count == 2)
  181. {
  182. if (currentValue > focusList[focusList.Count - 2]) //判断是否跑对方向
  183. {
  184. stage.Up(pulse * m_moveDirection);//对的方向
  185. }
  186. else
  187. {
  188. m_moveDirection = -m_moveDirection; //相反方向
  189. stage.Up(pulse * m_moveDirection);//这里不是跑回原来的位置,而是更过去一些,节约时间
  190. Console.WriteLine("change direction");
  191. }
  192. continue;
  193. }
  194. if (currentValue > focusList[focusList.Count - 2])
  195. {
  196. //清晰度大于前一张图,继续同方向移动
  197. trip += pulse * m_moveDirection;
  198. if (Math.Abs(trip) > pulse * 10)
  199. {
  200. Console.WriteLine("Trip of Z out of autofocus range.");
  201. return;
  202. }
  203. stage.Up(pulse * m_moveDirection);
  204. }
  205. else
  206. {
  207. //清晰度变小,返回峰值,对焦结束
  208. int moveDir = -m_moveDirection; //相反方向
  209. stage.Up(pulse * moveDir);//返回上一张图片
  210. stage.WaitMoveDone();
  211. return;
  212. }
  213. }
  214. }
  215. catch
  216. {
  217. }
  218. finally
  219. {
  220. // Console.Write("End focus:");
  221. stage.WaitMoveDone();
  222. //m_CurrentValue = getMeanValueOfBitmap(CurrentImage());
  223. //Console.WriteLine(string.Format("聚焦完成:{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z));
  224. _isWorking = false;
  225. // stage.LockZ();
  226. }
  227. }
  228. //public static void AutoFocusFast(AxisController stage, Func<Bitmap> CurrentImage)
  229. //{
  230. // if (_isWorking)
  231. // return;
  232. // _isWorking = true;
  233. // int round = 0;//翻转次数
  234. // double pulse = FocusingParameter.getRuleFocus().StepLength / 4;//最小步长1.25um
  235. // double lastValue = 0.0;
  236. // int dir = -1; //方向
  237. // double trip = 0.0;
  238. // // _mask = CreateMask(CurrentImage());
  239. // try
  240. // {
  241. // while (_isWorking)
  242. // {
  243. // //Console.Write("Before focus:");
  244. // stage.WaitMoveDone();
  245. // //Console.Write("自动聚焦:");
  246. // Thread.Sleep(40);
  247. // try
  248. // {
  249. // Bitmap m_bitmap = CurrentImage();
  250. // m_CurrentValue = getMeanValueOfBitmap(m_bitmap);
  251. // //Console.WriteLine(string.Format("{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z));
  252. // }
  253. // catch (Exception ex)
  254. // {
  255. // return;
  256. // }
  257. // if (round > 1)
  258. // {
  259. // return;
  260. // }
  261. // else if (lastValue == 0)
  262. // {
  263. // }
  264. // else if (m_CurrentValue > lastValue)
  265. // {
  266. // round = 1;
  267. // }
  268. // else
  269. // {
  270. // //Console.WriteLine("Reverse:" + (dir > 0 ? "+" : "-"));
  271. // round++;
  272. // dir = -dir;
  273. // }
  274. // lastValue = m_CurrentValue;
  275. // trip += pulse * dir;
  276. // if (Math.Abs(trip) > pulse * 10)
  277. // {
  278. // Console.WriteLine("Trip of Z out of autofocus range.");
  279. // return;
  280. // }
  281. // stage.Up(pulse * dir);
  282. // }
  283. // }
  284. // catch
  285. // {
  286. // }
  287. // finally
  288. // {
  289. // // Console.Write("End focus:");
  290. // stage.WaitMoveDone();
  291. // //m_CurrentValue = getMeanValueOfBitmap(CurrentImage());
  292. // //Console.WriteLine(string.Format("聚焦完成:{0:f3} Z:{1:f3}", m_CurrentValue, stage.Z));
  293. // _isWorking = false;
  294. // // stage.LockZ();
  295. // }
  296. //}
  297. static Mat _mask;
  298. private static Mat CreateMask(Bitmap img)
  299. {
  300. Mat converted = PaintDotNet.Camera.Tools.ToMat(img);
  301. Mat imageGrey = new Mat();
  302. try
  303. {
  304. if (converted.Channels() == 3)
  305. OpenCvSharp.Cv2.CvtColor(converted, imageGrey, OpenCvSharp.ColorConversionCodes.RGB2GRAY);
  306. else if (converted.Channels() == 1)
  307. imageGrey = converted;
  308. }
  309. catch (Exception)
  310. {
  311. imageGrey = converted;
  312. }
  313. //stage.SetSpeedZ(40);
  314. Mat mask = new Mat();
  315. Cv2.Threshold(imageGrey, mask, 0, 255, ThresholdTypes.Triangle);
  316. var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20));
  317. Cv2.Dilate(mask, mask, kernel);
  318. //Cv2.Erode(mask, mask, kernel);
  319. return mask;
  320. }
  321. private static Mat CreateMask(Mat imageGrey)
  322. {
  323. Mat mask = new Mat();
  324. Cv2.Threshold(imageGrey, mask, 0, 255, ThresholdTypes.Triangle);
  325. var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20));
  326. Cv2.Dilate(mask, mask, kernel);
  327. return mask;
  328. }
  329. /// <summary>
  330. /// 获取输入图片的清晰度
  331. /// </summary>
  332. /// <param name="bitmap"></param>
  333. /// <returns></returns>
  334. private static double getMeanValueOfBitmap(Bitmap bitmap)
  335. {
  336. // Modify by shayg 20220718 start
  337. //Mat converted = PaintDotNet.Camera.Tools.ToMat(bitmap);
  338. Mat converted = BitmapConverter.ToMat(bitmap);
  339. // Modify by shayg 20220718 end
  340. Mat imageGrey = new Mat();
  341. Mat imageSobel = new Mat();
  342. try
  343. {
  344. if (converted.Channels() == 3)
  345. OpenCvSharp.Cv2.CvtColor(converted, imageGrey, OpenCvSharp.ColorConversionCodes.RGB2GRAY);
  346. else if (converted.Channels() == 1)
  347. imageGrey = converted;
  348. }
  349. catch (Exception)
  350. {
  351. imageGrey = converted;
  352. }
  353. OpenCvSharp.Mat meanValueImage = new OpenCvSharp.Mat();
  354. OpenCvSharp.Mat meanStdValueImage = new OpenCvSharp.Mat();
  355. // Modify by shayg 20220718 start
  356. //_mask = CreateMask(imageGrey);
  357. ////求灰度图像的标准差 值越大越好
  358. //OpenCvSharp.Cv2.MeanStdDev(imageGrey, meanValueImage, meanStdValueImage, _mask);
  359. Cv2.MeanStdDev(imageGrey, meanValueImage, meanStdValueImage);
  360. // Modify by shayg 20220718 end
  361. return meanStdValueImage.At<double>(0, 0);
  362. }
  363. // Add by shayg 20220718 start
  364. /// <summary>
  365. /// 获取输入图片的清晰度(拉普拉斯边缘清晰度判定)
  366. /// </summary>
  367. /// <param name="bitmap">原图片</param>
  368. /// <returns>清晰度(值越大越清晰)</returns>
  369. private static double getMeanValueOfBitmap2(Bitmap bitmap)
  370. {
  371. var converted = BitmapConverter.ToMat(bitmap);
  372. var imageGray = new Mat();
  373. if (converted.Channels() == 1)
  374. {
  375. imageGray = converted;
  376. }
  377. else
  378. {
  379. Cv2.CvtColor(converted, imageGray, ColorConversionCodes.RGB2GRAY);
  380. }
  381. var dst = new Mat();
  382. Cv2.Laplacian(imageGray, dst, MatType.CV_64F);
  383. Cv2.MeanStdDev(dst, out var mean, out var dev);
  384. var meanValue = dev.Val0 * dev.Val0;
  385. return meanValue;
  386. }
  387. // Add by shayg 20220718 end
  388. }
  389. }