革博士V2程序
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

738 wiersze
30 KiB

  1. #define online
  2. using DocumentFormat.OpenXml.Spreadsheet;
  3. using OpenCvSharp;
  4. using OpenCvSharp.XImgProc;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Security.Cryptography;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. namespace GeBoShi.SysCtrl
  12. {
  13. public static class OpencvUtils
  14. {
  15. public static int image_width = 2048;
  16. public static int image_height = 2048;
  17. #region 图像预处理
  18. public static Mat Resize(Mat mat, int width, int height, out int xw)
  19. {
  20. OpenCvSharp.Size dsize = new OpenCvSharp.Size(width, height);
  21. //Mat mat2 = new Mat();
  22. //ResizeUniform(mat, dsize, out mat2, out xw);
  23. xw = (width - mat.Cols) / 2;
  24. Mat mat2 = new Mat(height, width, MatType.CV_8UC3, new Scalar(114, 114, 114));
  25. Rect roi = new Rect((width - mat.Cols) / 2, (height - mat.Rows) / 2, mat.Cols, mat.Rows);
  26. mat.CopyTo(new Mat(mat2, roi));
  27. return mat2;
  28. }
  29. /// <summary>
  30. /// 等比例缩放
  31. /// </summary>
  32. /// <param name="src"></param>
  33. /// <param name="dst_size"></param>
  34. /// <param name="dst"></param>
  35. /// <returns></returns>
  36. public static int ResizeUniform(Mat src, Size dst_size, out Mat dst, out int xw)
  37. {
  38. xw = 0;
  39. int w = src.Cols;
  40. int h = src.Rows;
  41. int dst_w = dst_size.Width;
  42. int dst_h = dst_size.Height;
  43. //std::cout << "src: (" << h << ", " << w << ")" << std::endl;
  44. dst = new Mat(dst_h, dst_w, MatType.CV_8UC3, new Scalar(114, 114, 114));
  45. float[] ratio = new float[2];
  46. float ratio_src = w * 1.0f / h;
  47. float ratio_dst = dst_w * 1.0f / dst_h;
  48. int tmp_w = 0;
  49. int tmp_h = 0;
  50. if (ratio_src > ratio_dst)
  51. {
  52. tmp_w = dst_w;
  53. tmp_h = (int)(dst_w * 1.0f / w) * h;
  54. ratio[0] = (float)w / (float)tmp_w;
  55. ratio[1] = (float)h / (float)tmp_h;
  56. }
  57. else if (ratio_src < ratio_dst)
  58. {
  59. tmp_h = dst_h;
  60. tmp_w = (int)((dst_h * 1.0f / h) * w);
  61. ratio[0] = (float)w / (float)tmp_w;
  62. ratio[1] = (float)h / (float)tmp_h;
  63. }
  64. else
  65. {
  66. Cv2.Resize(src, dst, dst_size);
  67. ratio[0] = (float)w / (float)tmp_w;
  68. ratio[1] = (float)h / (float)tmp_h;
  69. return 0;
  70. }
  71. //std::cout << "tmp: (" << tmp_h << ", " << tmp_w << ")" << std::endl;
  72. Mat tmp = new Mat();
  73. Cv2.Resize(src, tmp, new Size(tmp_w, tmp_h));
  74. unsafe
  75. {
  76. if (tmp_w != dst_w)
  77. { //高对齐,宽没对齐
  78. int index_w = (int)((dst_w - tmp_w) / 2.0);
  79. xw = index_w;
  80. //std::cout << "index_w: " << index_w << std::endl;
  81. for (int i = 0; i < dst_h; i++)
  82. {
  83. Buffer.MemoryCopy(IntPtr.Add(tmp.Data, i * tmp_w * 3).ToPointer(), IntPtr.Add(dst.Data, i * dst_w * 3 + index_w * 3).ToPointer(), tmp_w * 3, tmp_w * 3);
  84. }
  85. }
  86. else if (tmp_h != dst_h)
  87. { //宽对齐, 高没有对齐
  88. int index_h = (int)((dst_h - tmp_h) / 2.0);
  89. //std::cout << "index_h: " << index_h << std::endl;
  90. Buffer.MemoryCopy(tmp.Data.ToPointer(), IntPtr.Add(dst.Data, index_h * dst_w * 3).ToPointer(), tmp_w * tmp_h * 3, tmp_w * tmp_h * 3);
  91. }
  92. else
  93. {
  94. }
  95. }
  96. return 0;
  97. }
  98. public static Mat ResizeMat(Mat mat, int width, int height)
  99. {
  100. OpenCvSharp.Size dsize = new OpenCvSharp.Size(width, height);
  101. Mat mat2 = new Mat();
  102. mat2 = mat.Resize(dsize);
  103. //Cv2.Resize(mat, mat2, dsize);
  104. return mat2;
  105. }
  106. /// <summary>
  107. /// 计算合理宽幅
  108. /// </summary>
  109. /// <param name="sumWidth">多个相机图像总宽(外部去除重合部分)</param>
  110. /// <returns></returns>
  111. public static int GetWidthForResize(int sumWidth)
  112. {
  113. //保证计算8x2 16个小图
  114. int count = (int)Math.Round(sumWidth * 1.0f / image_width, 0);
  115. count = 8;
  116. return count * image_width;
  117. //int count = sumWidth / image_width;
  118. ////int remainder = sumWidth % image_width;
  119. //if (count % 2 == 0)
  120. // return count * image_width;
  121. //else
  122. // return count * image_width+ image_width;
  123. }
  124. /// <summary>
  125. /// 裁切指定区域
  126. /// </summary>
  127. /// <param name="mat"></param>
  128. /// <param name="x"></param>
  129. /// <param name="y"></param>
  130. /// <param name="width"></param>
  131. /// <param name="height"></param>
  132. /// <returns></returns>
  133. public static Mat CutImage(Mat mat, int x, int y, int width, int height)
  134. {
  135. Rect roi = new Rect(x, y, width, height);
  136. return new Mat(mat, roi).Clone();
  137. }
  138. #endregion
  139. #region 裁边
  140. /// <summary>
  141. /// 裁边
  142. /// </summary>
  143. /// <param name="mat_rgb"></param>
  144. /// <param name="isLeft"></param>
  145. /// <param name="marginHoleWidth"></param>
  146. /// <param name="marginWidth"></param>
  147. /// <returns></returns>
  148. public static Mat getMaxInsetRect2(Mat mat_rgb, bool isLeft, int marginHoleWidth, out int marginWidth)
  149. {
  150. int bian = 3500;
  151. Rect Roi;
  152. if (!isLeft)
  153. Roi = new Rect(mat_rgb.Width - bian, 0, bian, mat_rgb.Height);
  154. else
  155. Roi = new Rect(0, 0, bian, mat_rgb.Height);
  156. int type = isLeft ? 1 : 0;
  157. int len = 0;
  158. if(!ConfMgr.Instance.SysConfigParams.OpenAIEdge)
  159. len = EdgeClipping2(mat_rgb, type, Roi, isLeft);
  160. else
  161. len = EdgeClipping3(mat_rgb, type, Roi, isLeft);
  162. #if false
  163. //Mat mat_rgb = new Mat("E:\\CPL\\测试代码\\边缘检测\\test\\test\\test\\img\\19.bmp");
  164. Mat image_gray = new Mat();
  165. Cv2.CvtColor(mat_rgb, image_gray, ColorConversionCodes.BGR2GRAY);
  166. //cvtColor(image_RGB, image, COLOR_RGB2GRAY);
  167. int height = image_gray.Rows;
  168. int width = image_gray.Cols;
  169. // 算法定义:取均分5段图片的五条横线,经过一系列处理之后,二值化,找到沿边位置,然后取均值作为直边,在缩进一段有针眼的位置
  170. // 定义每段的行数
  171. int num_rows = 5;
  172. int segment_height = height / num_rows - 1;
  173. // 定义空数组保存结果
  174. int[] total = new int[num_rows];
  175. // 平均截取5行数据并处理图像
  176. for (int i = 0; i < num_rows; i++)
  177. {
  178. // 截取当前行的图像
  179. int start_row = i * segment_height;
  180. Rect roi = new Rect(0, start_row, width, 1);
  181. Mat current_segment = image_gray.Clone(roi);
  182. // 对当前行的图像进行平滑处理
  183. Mat smoothed_image = new Mat();
  184. Cv2.GaussianBlur(current_segment, smoothed_image, new Size(5, 1), 0);
  185. // 计算当前行的灰度直方图
  186. Mat absolute_histo = new Mat();
  187. Cv2.CalcHist(new Mat[] { smoothed_image }, new int[] { 0 }, new Mat(), absolute_histo, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) });
  188. Cv2.GaussianBlur(current_segment, smoothed_image, new Size(19, 1), 0);
  189. // 对图片进行分割i+1
  190. //double otsu_threshold;
  191. //threshold(smoothed_image, smoothed_image, 0, 255, THRESH_BINARY + THRESH_OTSU, &otsu_threshold);
  192. Cv2.Threshold(smoothed_image, smoothed_image, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
  193. // 使用形态学操作进行孔洞填充
  194. Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(25, 1));
  195. Mat filled_image = new Mat();
  196. Cv2.MorphologyEx(smoothed_image, filled_image, MorphTypes.Close, kernel);
  197. // 取较长的一个值作为皮革的宽度
  198. int num_255 = Cv2.CountNonZero(filled_image);
  199. int length_t = (num_255 > width / 2) ? num_255 : width - num_255;
  200. total[i] = (length_t);
  201. API.OutputDebugString($"getMaxInsetRect2: 【{i + 1}】{length_t}={num_255}|{width}");
  202. }
  203. // 取平均值作为宽度
  204. int length = (int)total.Average();
  205. marginWidth = width-length;
  206. #endif
  207. if (len > 0)
  208. {
  209. int length = len; //(len > mat_rgb.Width / 2) ? len : mat_rgb.Width - len;
  210. if (isLeft)
  211. marginWidth = length;
  212. else
  213. marginWidth = mat_rgb.Width - length;
  214. // 判断数据是否异常,判断当前线段的宽度是否大于设定像素的偏差
  215. //int abnormal_pxl = 200;
  216. //for (int i = 0; i < num_rows; i++)
  217. //{
  218. // if (Math.Abs(total[i] - length) > abnormal_pxl)
  219. // throw new Exception("数据异常,当段图片的宽度有问题!");
  220. //}
  221. //右侧相机,拍摄产品,边缘位于右侧判断,缩进100像素,去点针眼
  222. //Cv2.Line(mat_rgb, new Point(length - 100, 0), new Point(length - 100, height), new Scalar(255, 0, 0), 20);
  223. ////左侧相机,拍摄产品,边缘位于左侧判断,缩进100像素,去点针眼
  224. //Cv2.Line(mat_rgb, new Point(width - length + 100, 0), new Point(width - length + 100, height), new Scalar(0, 255, 0), 20);
  225. //int decWidth = width - length + marginHoleWidth;
  226. //if (isLeft)
  227. // return cutImage(mat_rgb, decWidth, 0, width- decWidth, height);
  228. //else
  229. // return cutImage(mat_rgb, 0, 0, width - decWidth, height);
  230. //API.OutputDebugString($"getMaxInsetRect2:margin={marginWidth},length={length}({marginHoleWidth}),isLeft={isLeft},mat_rgb={mat_rgb.Width}*{mat_rgb.Height},w={length - marginHoleWidth},h={mat_rgb.Height}");
  231. #if online
  232. if (isLeft)
  233. return CutImage(mat_rgb, length + marginHoleWidth, 0, mat_rgb.Width - length - marginHoleWidth, mat_rgb.Height);
  234. else
  235. return CutImage(mat_rgb, 0, 0, length - marginHoleWidth, mat_rgb.Height);
  236. #else
  237. if (isLeft)
  238. {
  239. Cv2.Line(mat_rgb, new Point(length + marginHoleWidth, 0), new Point(length + marginHoleWidth, mat_rgb.Height), new Scalar(255, 0, 0), 20);
  240. return mat_rgb;
  241. }
  242. else
  243. {
  244. Cv2.Line(mat_rgb, new Point(length - marginHoleWidth, 0), new Point(length - marginHoleWidth, mat_rgb.Height), new Scalar(0, 255, 0), 20);
  245. return mat_rgb;
  246. }
  247. #endif
  248. }
  249. else
  250. {
  251. marginWidth = 0;
  252. if (isLeft)
  253. return CutImage(mat_rgb, bian + marginHoleWidth, 0, mat_rgb.Width - bian - marginHoleWidth, mat_rgb.Height);
  254. else
  255. return CutImage(mat_rgb, 0, 0, mat_rgb.Width - bian - marginHoleWidth, mat_rgb.Height);
  256. }
  257. }
  258. /// <summary>
  259. /// 寻边算法
  260. /// </summary>
  261. /// <param name="image"></param>
  262. /// <param name="FindType"></param>
  263. /// <param name="Roi"></param>
  264. /// <param name="IsLeft"></param>
  265. /// <returns></returns>
  266. private static int EdgeClipping2(Mat image, int FindType, Rect Roi, bool IsLeft)
  267. {
  268. Mat mat_rgb = image.Clone(Roi);
  269. int height = mat_rgb.Rows;
  270. int width = mat_rgb.Cols;
  271. int sf = 10; //缩放比例
  272. int pix = 5; //获取均值区域长宽像素
  273. int pointNum = 15; //获取找遍点数
  274. int offsetGray = 5; //二值化偏差
  275. //按比例缩放
  276. int sf_height = height / sf;
  277. int sf_width = width / sf;
  278. Cv2.Resize(mat_rgb, mat_rgb, new Size(sf_width, sf_height), 0, 0, InterpolationFlags.Linear);
  279. //mat_rgb = mat_rgb.Resize(new Size(sf_width, sf_height));
  280. int[] maxlev = new int[3] { 3, 1, 0 };
  281. int[] srlev = new int[3] { 29, 25, 11 };
  282. int length_t = 0;
  283. Mat[] lineImg = new Mat[3];
  284. List<int> lines = new List<int>();
  285. List<int> total_t = new List<int>();
  286. for (int lv = 0; lv < 3; lv++)
  287. {
  288. lines.Clear();
  289. total_t.Clear();
  290. Mat himg = mat_rgb.Clone();
  291. //mat_rgb = mat_rgb.PyrMeanShiftFiltering(10, 27, 3);
  292. Cv2.PyrMeanShiftFiltering(himg, himg, 10, srlev[lv], maxlev[lv]);//10,17;
  293. //分通道处理
  294. Mat[] mv = Cv2.Split(himg);
  295. for (int cht = 0; cht < 3; cht++)
  296. {
  297. //转灰度图
  298. //mat_rgb = mat_rgb.CvtColor(ColorConversionCodes.BGR2GRAY);
  299. Mat image_gray = new Mat();
  300. image_gray = mv[cht];
  301. //image_gray.ImWrite($"image_gray{cht}.jpg");
  302. Mat image_Canny = new Mat();
  303. Cv2.Canny(image_gray, image_Canny, 32, 64, 3);
  304. //image_Canny.ImWrite($"image_Canny{cht}.jpg");
  305. var lins = Cv2.HoughLinesP(image_Canny, 1, Math.PI / 360, 0, 20, 10);
  306. lineImg[cht] = new Mat(new Size(himg.Cols, himg.Rows), MatType.CV_8UC1, new Scalar());
  307. lines.Add(lins.Length);
  308. foreach (var item in lins)
  309. {
  310. var fdang = Math.Atan2((item.P2.Y - item.P1.Y), (item.P2.X - item.P1.X));
  311. var ang = Math.Abs(fdang * (180 / Math.PI));
  312. if (ang > 60 && ang < 120)
  313. Cv2.Line(lineImg[cht], item.P1.X, item.P1.Y, item.P2.X, item.P2.Y, new Scalar(255, 255, 255), 2);
  314. }
  315. //lineImg[cht].ImWrite($"image_Canny2_{cht}.jpg");
  316. //mat_rgb = mat_rgb.Canny(32, 64);
  317. if (lins.Length >= 1)
  318. break;
  319. }
  320. if (lines.Count > 0 && lines.Max() >= 1)
  321. break;
  322. }
  323. //二值化
  324. Mat image_Otsu = new Mat();
  325. int hDis = sf_height / (pointNum + 2); //去除边缘两点
  326. #if false //二值算法
  327. List<double> LeftAvg = new List<double>();
  328. List<double> RightAvg = new List<double>();
  329. //double thb = Cv2.Threshold(image_gray, image_Otsu, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
  330. #region 多点获取二值化均值
  331. for (int i = 0; i < pointNum; i++)
  332. {
  333. Rect roiLeft = new Rect(0, hDis + hDis * i, pix, pix);
  334. Mat current_segmentL = image_gray.Clone(roiLeft);
  335. //Scalar ttr = current_segmentL.Mean();
  336. LeftAvg.Add(current_segmentL.Mean().Val0);
  337. Rect roiRight = new Rect(sf_width - pix, hDis + hDis * i, pix, pix);
  338. Mat current_segmentR = image_gray.Clone(roiRight);
  339. RightAvg.Add(current_segmentR.Mean().Val0);
  340. }
  341. double thres = 0;
  342. if (IsLeft)
  343. {
  344. if (LeftAvg.Average() > RightAvg.Average())
  345. thres = RightAvg.Max() + offsetGray;
  346. else
  347. thres = RightAvg.Min() - offsetGray;
  348. }
  349. else
  350. {
  351. if (LeftAvg.Average() > RightAvg.Average())
  352. thres = LeftAvg.Min() - offsetGray;
  353. else
  354. thres = LeftAvg.Max() + offsetGray;
  355. }
  356. //double thres = (RightAvg.Average() + )/2;
  357. #endregion
  358. #endif
  359. #if false
  360. double min, max;
  361. image_gray.MinMaxLoc(out min, out max);
  362. double thres = (min + max) / 2;
  363. #endif
  364. #if false //二值化图片
  365. //Cv2.Threshold(image_gray, image_Otsu, 0, 255, ThresholdTypes.Otsu);
  366. double thb = Cv2.Threshold(image_gray, image_Otsu, thres, 255, ThresholdTypes.Binary);
  367. image_Otsu.ImWrite("Otsu1.jpg");
  368. Cv2.MedianBlur(image_Otsu, image_Otsu, 21);
  369. image_Otsu.ImWrite("Otsu2.jpg");
  370. endTime = DateTimeOffset.Now;
  371. Console.WriteLine("灰度图二值化(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
  372. startTime = DateTimeOffset.Now;
  373. #else
  374. /*
  375. image_Otsu = image_Canny;
  376. */
  377. #endif
  378. int findex = lines.FindIndex(x => x == lines.Max());
  379. image_Otsu = lineImg[findex];
  380. // 定义空数组保存结果
  381. int[] total = new int[pointNum];
  382. total_t = new List<int>();
  383. //bool isLeft = FindType == 0 ? true : false;
  384. // 平均截取pointNum行数据并处理图像
  385. for (int i = 0; i < pointNum; i++)
  386. {
  387. // 截取当前行的图像
  388. Rect roi = new Rect(0, hDis + hDis * i, sf_width, 1);
  389. //Mat current_segment = image_Otsu.Clone(roi);
  390. Mat current_segment = image_Otsu.Clone(roi);
  391. #if false
  392. #region 预处理
  393. // 对当前行的图像进行平滑处理
  394. Mat smoothed_image2 = new Mat();
  395. Cv2.GaussianBlur(current_segment, smoothed_image2, new Size(5, 1), 0);
  396. // 计算当前行的灰度直方图
  397. Mat absolute_histo2 = new Mat();
  398. Cv2.CalcHist(new Mat[] { smoothed_image2 }, new int[] { 0 }, new Mat(), absolute_histo2, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) });
  399. Cv2.GaussianBlur(current_segment, smoothed_image2, new Size(9, 1), 0);
  400. // 对图片进行分割
  401. //double otsu_threshold;
  402. //threshold(smoothed_image, smoothed_image, 0, 255, THRESH_BINARY + THRESH_OTSU, &otsu_threshold);
  403. double otsu_threshold2 = Cv2.Threshold(smoothed_image2, smoothed_image2, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
  404. // 使用形态学操作进行孔洞填充
  405. Mat kernel3 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 1));
  406. Mat filled_image3 = new Mat();
  407. Cv2.MorphologyEx(smoothed_image2, filled_image3, MorphTypes.Close, kernel3);
  408. #endregion
  409. #else
  410. //Mat filled_image3 = current_segment.Clone();
  411. Mat filled_image3 = current_segment;
  412. #endif
  413. #if true
  414. //从左到右判断边和从右到左判断边
  415. int numX = 0;
  416. byte tempVal = 0;
  417. if (!IsLeft)
  418. {
  419. tempVal = filled_image3.At<byte>(0, 0);
  420. //filled_image3.
  421. for (int j = 0; j < filled_image3.Cols; j++)
  422. {
  423. if (filled_image3.At<byte>(0, j) != tempVal)
  424. {
  425. numX = j;
  426. break;
  427. }
  428. }
  429. }
  430. else
  431. {
  432. tempVal = filled_image3.At<byte>(0, filled_image3.Cols - 1);
  433. for (int j = filled_image3.Cols - 1; j >= 0; j--)
  434. {
  435. if (filled_image3.At<byte>(0, j) != tempVal)
  436. {
  437. numX = j;
  438. break;
  439. }
  440. }
  441. }
  442. //int numX = 0;
  443. //byte tempVal = 0;
  444. //unsafe
  445. //{
  446. // byte* ptr = (byte*)filled_image3.Data;
  447. // if (isLeft)
  448. // {
  449. // tempVal = ptr[0];
  450. // for (int j = 0; j < filled_image3.Cols; j++)
  451. // {
  452. // if (ptr[j] != tempVal)
  453. // {
  454. // numX = j;
  455. // break;
  456. // }
  457. // }
  458. // }
  459. // else
  460. // {
  461. // tempVal = ptr[filled_image3.Cols - 1];
  462. // //tempVal = filled_image3.At<byte>(0, filled_image3.Cols - 1);
  463. // for (int j = filled_image3.Cols - 1; j >= 0; j--)
  464. // {
  465. // if (ptr[j] != tempVal)
  466. // {
  467. // numX = j;
  468. // break;
  469. // }
  470. // }
  471. // }
  472. //}
  473. #else
  474. int numX = Cv2.CountNonZero(filled_image3);
  475. #endif
  476. //int length_t = (numX > (sf_width / 2)) ? numX :sf_width - numX;
  477. length_t = numX;
  478. total[i] = (length_t);
  479. if (length_t > 0)
  480. total_t.Add(length_t);
  481. current_segment.Dispose();
  482. }
  483. // 取平均值作为宽度
  484. int length = 0;
  485. if (total_t.Count > 0)
  486. {
  487. length = (int)total_t.Average();
  488. //乘上换算系数还原
  489. length = length * sf + Roi.X;
  490. }
  491. //endTime = DateTimeOffset.Now;
  492. //Console.WriteLine("计算边(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
  493. // 判断数据是否异常,判断当前线段的宽度是否大于设定像素的偏差
  494. //int abnormal_pxl = 100 / 4;
  495. //for (int i = 0; i < pointNum; i++)
  496. //{
  497. // if (Math.Abs(total[i] - length) > abnormal_pxl)
  498. // Console.WriteLine("数据异常!");
  499. // //出现数据异常,当段图片的宽度有问题
  500. //}
  501. //if ((length > 6520 && length < 6530) || (length > 1570 && length < 1590))
  502. // ;
  503. //else
  504. // ;
  505. mat_rgb.Dispose();
  506. //himg.Dispose();
  507. //image_gray.Dispose();
  508. //image_Canny.Dispose();
  509. //image_Otsu.Dispose();
  510. return length;
  511. }
  512. private static StructuredEdgeDetection _edgeDetect;
  513. public static void LoadEdgeMode()
  514. {
  515. if(_edgeDetect == null)
  516. _edgeDetect = OpenCvSharp.XImgProc.CvXImgProc.CreateStructuredEdgeDetection("model.yml");
  517. }
  518. /// <summary>
  519. /// 模型寻边
  520. /// </summary>
  521. /// <param name="image"></param>
  522. /// <param name="FindType"></param>
  523. /// <param name="Roi"></param>
  524. /// <param name="IsLeft"></param>
  525. /// <returns></returns>
  526. private static int EdgeClipping3(Mat image, int FindType, Rect Roi, bool IsLeft)
  527. {
  528. Mat mat_rgb = image.Clone(Roi);
  529. int height = mat_rgb.Rows;
  530. int width = mat_rgb.Cols;
  531. int sf = 10; //缩放比例
  532. int pix = 5; //获取均值区域长宽像素
  533. int pointNum = 15; //获取找遍点数
  534. int offsetGray = 5; //二值化偏差
  535. int length_t = 0;
  536. List<int> lines = new List<int>();
  537. List<int> total_t = new List<int>();
  538. //按比例缩放
  539. double sf_height = height / sf;
  540. double sf_width = width / sf;
  541. Cv2.Resize(mat_rgb, mat_rgb, new Size(sf_width, sf_height), 0, 0, InterpolationFlags.Linear);
  542. Mat himg = new Mat();
  543. Mat edgeimg = new Mat();
  544. Cv2.CvtColor(mat_rgb, edgeimg, ColorConversionCodes.BGR2RGB);
  545. Mat edges = new Mat();
  546. edgeimg.ConvertTo(edgeimg, MatType.CV_32F, 1 / 255.0);
  547. if(_edgeDetect == null)
  548. LoadEdgeMode();
  549. //Cv2.Normalize(edgeimg, edgeimg, 1.0, 0, NormTypes.L2, -1);
  550. _edgeDetect.DetectEdges(edgeimg, edges);
  551. Mat image_Otsu = new Mat();
  552. int hDis = (int)sf_height / (pointNum + 2); //去除边缘两点
  553. edges.ConvertTo(image_Otsu, MatType.CV_8U, 255.0);
  554. Cv2.Threshold(image_Otsu, image_Otsu, 0, 255, ThresholdTypes.Otsu);
  555. // 定义空数组保存结果
  556. int[] total = new int[pointNum];
  557. // 平均截取pointNum行数据并处理图像
  558. for (int i = 0; i < pointNum; i++)
  559. {
  560. // 截取当前行的图像
  561. Rect roi = new Rect(0, hDis + hDis * i, (int)sf_width, 1);
  562. Mat current_segment = image_Otsu.Clone(roi);
  563. //Mat filled_image3 = current_segment.Clone();
  564. Mat filled_image3 = current_segment;
  565. #if true
  566. //从左到右判断边和从右到左判断边
  567. int numX = 0;
  568. int tm = 0;
  569. byte tempVal = 0;
  570. bool findOne = false;
  571. if (!IsLeft)
  572. {
  573. tempVal = filled_image3.At<byte>(0, 0);
  574. //filled_image3.
  575. for (int j = 0; j < filled_image3.Cols; j++)
  576. {
  577. if (filled_image3.At<byte>(0, j) != tempVal)
  578. {
  579. if (!findOne)
  580. {
  581. tm = j;
  582. findOne = true;
  583. tempVal = filled_image3.At<byte>(0, j);
  584. }
  585. else
  586. {
  587. //numX = j;
  588. numX = (tm + j) / 2;
  589. break;
  590. }
  591. }
  592. }
  593. }
  594. else
  595. {
  596. tempVal = filled_image3.At<byte>(0, filled_image3.Cols - 1);
  597. for (int j = filled_image3.Cols - 1; j >= 0; j--)
  598. {
  599. if (filled_image3.At<byte>(0, j) != tempVal)
  600. {
  601. if (!findOne)
  602. {
  603. tm = j;
  604. findOne = true;
  605. tempVal = filled_image3.At<byte>(0, j);
  606. }
  607. else
  608. {
  609. //numX = j;
  610. numX = (tm + j) / 2;
  611. break;
  612. }
  613. }
  614. }
  615. }
  616. #else
  617. int numX = Cv2.CountNonZero(filled_image3);
  618. #endif
  619. //length_t = (numX > (sf_width / 2)) ? numX :(int)(sf_width - numX);
  620. length_t = numX;
  621. total[i] = (length_t);
  622. if (length_t > 0)
  623. total_t.Add(length_t);
  624. }
  625. // 取平均值作为宽度
  626. int length = 0;
  627. if (total_t.Count > 0)
  628. {
  629. length = (int)total_t.Average();
  630. if (IsLeft)
  631. length = length - ConfMgr.Instance.SysConfigParams.Crop_offset;
  632. else
  633. length = length + ConfMgr.Instance.SysConfigParams.Crop_offset;
  634. //乘上换算系数还原
  635. length = length * sf + Roi.X;
  636. }
  637. return length;
  638. }
  639. #endregion
  640. #region 合并
  641. /// <summary>
  642. /// 合并MAT(宽高必需一致)
  643. /// </summary>
  644. /// <param name="mats"></param>
  645. /// <param name="isHorizontal"></param>
  646. /// <returns></returns>
  647. public static Mat MergeImage_sameSize(Mat[] mats, bool isHorizontal = true)
  648. {
  649. Mat matOut = new Mat();
  650. if (isHorizontal)
  651. Cv2.HConcat(mats, matOut);//横向拼接
  652. else
  653. Cv2.VConcat(mats, matOut);//纵向拼接
  654. return matOut;
  655. }
  656. #endregion
  657. }
  658. }