革博士V2程序
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

875 rindas
36 KiB

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