/// <summary> /// 対象物の位置をロボット座標系で取得します。 /// </summary> /// <param name="blob"></param> /// <returns></returns> public static (double x, double y) GetTargetRobotCoordinate(SettingsObj obj, ConnectedComponents.Blob blob) { var srcList = new List <DenseVector>(4); var dstList = new List <DenseVector>(4); // 各座標系の4点をリストに詰める srcList.Add(DenseVector.OfArray(new double[] { obj.TopLeftArPoseX, obj.TopLeftArPoseY })); srcList.Add(DenseVector.OfArray(new double[] { obj.TopRightArPoseX, obj.TopRightArPoseY })); srcList.Add(DenseVector.OfArray(new double[] { obj.BottomRightArPoseX, obj.BottomRightArPoseY })); srcList.Add(DenseVector.OfArray(new double[] { obj.BottomLeftArPoseX, obj.BottomLeftArPoseY })); dstList.Add((HomographyHelper.CreateVector2(obj.TopLeftDobotPoseX, obj.TopLeftDobotPoseY))); dstList.Add(HomographyHelper.CreateVector2(obj.TopRightDobotPoseX, obj.TopRightDobotPoseY)); dstList.Add(HomographyHelper.CreateVector2(obj.BottomRightDobotPoseX, obj.BottomRightDobotPoseY)); dstList.Add(HomographyHelper.CreateVector2(obj.BottomLeftDobotPoseX, obj.BottomLeftDobotPoseY)); // 画像座標系での値 Console.WriteLine($"homography_IMAGE:imgX:{blob.Left + blob.Width / 2}, imgY:{blob.Top + blob.Height / 2}"); // 射影変換行列を求めて var h**o = HomographyHelper.FindHomography(srcList, dstList); // 入力平面から出力平面上の座標に変換 var ret = h**o.Translate(blob.Left + blob.Width / 2, blob.Top + blob.Height / 2); // ロボット座標系での値 Console.WriteLine($"homography_ROBOT:boxX:{ret.dstX}, boxY:{ret.dstY}"); return(ret); }
/// <summary> /// 判断两个blob是否相同 /// </summary> /// <param name="blob1"></param> /// <param name="blob2"></param> /// <returns></returns> private static bool IsSame(ConnectedComponents.Blob blob1, ConnectedComponents.Blob blob2) { //如果两个物体 ,宽度 高度,中心点 area 接近则认为它们是相同的 if (Math.Abs(blob1.Width - blob2.Width) >= 40) { return(false); } if (Math.Abs(blob1.Height - blob2.Height) >= 35) { return(false); } if (Math.Abs(blob1.Centroid.Y - blob2.Centroid.Y) >= 30) { return(false); } if (Math.Abs(blob1.Area - blob2.Area) >= 7000) { return(false); } return(true); }
private static void Blob() { Mat src = new Mat("data/shapes.png", ImreadModes.Color); Mat gray = src.CvtColor(ColorConversionCodes.BGR2GRAY); Mat binary = gray.Threshold(0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary); Mat labelView = new Mat(); Mat rectView = binary.CvtColor(ColorConversionCodes.GRAY2BGR); ConnectedComponents cc = Cv2.ConnectedComponentsEx(binary); if (cc.LabelCount <= 1) { return; } // draw labels /* * Scalar[] colors = cc.Blobs.Select(_ => Scalar.RandomColor()).ToArray(); * int height = cc.Labels.GetLength(0); * int width = cc.Labels.GetLength(1); * var labelViewIndexer = labelView.GetGenericIndexer<Vec3b>(); * for (int y = 0; y < height; y++) * { * for (int x = 0; x < width; x++) * { * int labelValue = cc.Labels[y, x]; * labelViewIndexer[y, x] = colors[labelValue].ToVec3b(); * } * } */ cc.RenderBlobs(labelView); // draw bonding boxes except background foreach (var blob in cc.Blobs.Skip(1)) { rectView.Rectangle(blob.Rect, Scalar.Red); } // filter maximum blob ConnectedComponents.Blob maxBlob = cc.GetLargestBlob(); //cc.Blobs.Skip(1).OrderByDescending(b => b.Area).First(); Mat filtered = new Mat(); cc.FilterByBlob(src, filtered, maxBlob); using (new Window("src", src)) using (new Window("binary", binary)) using (new Window("labels", labelView)) using (new Window("bonding boxes", rectView)) using (new Window("maximum blob", filtered)) { Cv2.WaitKey(); } }
/// <summary> /// 是否需要保存图片 /// </summary> /// <param name="mat">指的是刚刚读进来的灰度图片</param> /// <returns></returns> public static bool IsNeedToSave(Mat mat) { if (mat == null || mat.IsDisposed) { return(false); } #region 预判断 if ((DateTime.Now - lastCallTime).TotalMilliseconds < 600) { //调用间隔至少为600ms NLogHelper.Trace("调用频繁,不需要截图"); return(false); } lastCallTime = DateTime.Now; #endregion #region 加载背景图片 if (backgroundMat == null || backgroundMat.IsDisposed) { if (!File.Exists(backgroundFile)) { NLogHelper.Trace("未找到背景图片:" + backgroundFile); backgroundMat = null; } else { try { backgroundMat = Cv2.ImRead(backgroundFile, ImreadModes.GrayScale); //去噪 backgroundMat = backgroundMat.MedianBlur(3); } catch (Exception ex) { NLogHelper.Trace("加载背景图片失败:" + ex); if (backgroundMat != null && backgroundMat.IsEnabledDispose) { backgroundMat.Dispose(); } backgroundMat = null; } } } #endregion #region 图片处理 ////进行图像灰度化 if (mat.Channels() > 1) { Mat outMat = new Mat(); Cv2.CvtColor(mat, outMat, ColorConversionCodes.BGRA2GRAY); mat = outMat; } //图片去噪 mat = mat.MedianBlur(3); int width = mat.Width; int height = mat.Height; //当前使用的背景图片 Mat usedBackgroundMat = null; if (backgroundMat != null) { if (backgroundMat.Width != width || backgroundMat.Height != height) { NLogHelper.Trace("背景图片与原图大小不一样,进行缩放"); usedBackgroundMat = backgroundMat.Resize(new Size(width, height)); } else { usedBackgroundMat = backgroundMat; } } //是否使用了lightMethod 光照模型去除背景, 拍摄同样一张图片但是不带物体 bool usedLightMethod = false; if (usedBackgroundMat != null && usedBackgroundMat.Width == width && usedBackgroundMat.Height == height) { if (lightMethod == 0) { mat = usedBackgroundMat - mat; usedLightMethod = true; } else if (lightMethod == 1) { mat.ConvertTo(mat, MatType.CV_32F); usedBackgroundMat.ConvertTo(usedBackgroundMat, MatType.CV_32F); mat = (1 - (usedBackgroundMat / mat)) * 255; mat.ConvertTo(mat, MatType.CV_8U); usedLightMethod = true; } } //二值化图像 if (usedLightMethod) { mat = mat.Threshold(30, 255, ThresholdTypes.Binary); } else { mat = mat.Threshold(140, 255, ThresholdTypes.Binary); } #endregion #region 联通组件 ConnectedComponents components = mat.ConnectedComponentsEx(); List <ConnectedComponents.Blob> blobList = new List <ConnectedComponents.Blob>(); for (int i = 0; i < components.Blobs.Count; i++) { if (i == 0) { //背景, continue; } ConnectedComponents.Blob blob = components.Blobs[i]; //实际区域大小 if (blob.Area > MinArea && blob.Width > MinWidth && blob.Height > MinHeight) { if (blob.Width > width * 0.9 && blob.Height > 0.9) { //发现超大物体,此物体有可能是背景或者其它干扰 NLogHelper.Trace("超大物体忽略"); } else { //一瓶矿泉水 width = 227 height=171 area=15907 blobList.Add(blob); } } } NLogHelper.Trace(string.Format("原图共有{0}个物体", blobList.Count)); #endregion #region 判断是否需要截图 //获取上一次的blobs List <ConnectedComponents.Blob> oldLastBlobs = lastBlobList; lastBlobList = blobList; if (blobList.Count == 0) { //没有图片,不需要保存 NLogHelper.Trace("没有图片,不需要保存"); return(false); } //获取最大的宽度的项 ConnectedComponents.Blob maxItem = blobList.OrderByDescending(r => r.Width).First(); if (oldLastBlobs == null || oldLastBlobs.Count == 0) { //之前没有图片 或者 中间范围内没有图片 if (maxItem.Width > width * 0.7 && maxItem.Height > height * 0.4) { //最大的物体很大 NLogHelper.Trace("之前没有图像,最大的物体很大,进行保存"); return(true); } else { //查找位于中间的个体数量 List <ConnectedComponents.Blob> middleBlobs = FindMiddleObjects(width, blobList, 0.81); if (middleBlobs.Count > 0) { //中间有物体 NLogHelper.Trace("之前没有图像,中间有物体,进行保存"); return(true); } else { //中间没有物体或者物体没有完全到中间 NLogHelper.Trace("之前没有图像,中间没有物体或者物体没有完全到中间,进行保存"); return(false); } } } else { //if (maxItem.Width > width*0.7 && maxItem.Height> height*0.4) //{ // //最大的物体很大 // return true; //} //之前图片有物体 List <ConnectedComponents.Blob> newMiddleBlobs = FindMiddleObjects(width, blobList, 0.81); //获取中间旧的 List <ConnectedComponents.Blob> oldMiddleBlobs = FindMiddleObjects(width, oldLastBlobs, 0.81); if (newMiddleBlobs.Count == 0) { //中间没有,认为不需要截图 NLogHelper.Trace("之前有图像,新图中间没有,认为不需要截图"); return(false); } //新的中间有图 if (oldMiddleBlobs.Count == 0) { //之前有图片,但图片不在中间,新的又有了 NLogHelper.Trace("之前有图片,但图片不在中间,新的又有了,需要截图"); return(true); } else { int minDiff = 1;//任务现在和之前相差超过minDiff个物体需要截图 //现在和以前均有图片 if ((newMiddleBlobs.Count - oldMiddleBlobs.Count) > minDiff) { //现在跟置前有两个以上的不同图片 NLogHelper.Trace("现在跟之前有两个以上的不同图片,需要截图"); return(true); } else { ////先按最左点排序,再按中心点排序,再按照面积排序 升序 newMiddleBlobs = newMiddleBlobs.OrderBy(r => r.Left).ThenBy(r => r.Width).ThenBy(r => r.Centroid.Y).ThenBy(r => r.Area).ToList(); oldMiddleBlobs = oldMiddleBlobs.OrderBy(r => r.Left).ThenBy(r => r.Width).ThenBy(r => r.Centroid.Y).ThenBy(r => r.Area).ToList(); var lcsTuple = LCS(newMiddleBlobs, oldMiddleBlobs); List <ConnectedComponents.Blob> commonBlobs = lcsTuple.Item1; List <ConnectedComponents.Blob> onlyNewBlobs = lcsTuple.Item2; List <ConnectedComponents.Blob> onlyOldBlobs = lcsTuple.Item3; if (commonBlobs.Count == 0) { //现在和以前没有公共部分,截图 NLogHelper.Trace("现在和以前没有公共部分,需要截图"); return(true); } else if (onlyNewBlobs.Count == 0 && onlyOldBlobs.Count == 0) { //全部是公共部分 NLogHelper.Trace("现在和以前全部是公共部分,不需要截图"); return(false); } else if (onlyOldBlobs.Count == 0) { //新的部分多了,除此之外都是公共的 NLogHelper.Trace("新的部分多了,除此之外都是公共的,需要截图"); return(true); } else if (onlyNewBlobs.Count == 0) { //旧的部分多了,除此之外全是公共的 NLogHelper.Trace("旧的部分多了,除此之外全是公共的,不需要截图"); return(false); } else { //旧的部分,新的部分,公共的部分都有 NLogHelper.Trace("旧的部分,新的部分,公共的部分都有,需要截图"); return(true); } } } } #endregion }
public static void TestMain() { while (true) { string fileName = @"./Images/"; Console.WriteLine("输入图片名称"); fileName += Console.ReadLine(); Console.WriteLine(DateTime.Now.ToString("HH:mm:ss fff")); bool isNeedToSave = BlobUtils.IsNeedToSave(fileName); Console.WriteLine("IsNeedToSave = " + isNeedToSave); Console.WriteLine(DateTime.Now.ToString("HH:mm:ss fff")); } //图片名称 string imgFile = @"./Images/check1.jpg"; //光线模式文件 string lightPatternFile = @"./Images/blank.jpg"; //移除背景光线的方法 0 different差 1 div 除 (根据测试除的效果略好于差,均大幅好于不用) int lightMethod = 0; Console.WriteLine("lightmethod = " + lightMethod); // 分割的方法 1 connected component 2 connected components with statistic(统计) 3 find contour(轮廓线) int segmentMethod = 1; //转换为单通道灰度图 Mat img = Cv2.ImRead(imgFile, ImreadModes.GrayScale); Mat cloneImg = img.Clone(); //noise removal 噪音去除 img = img.MedianBlur(3); //使用光照模型去除背景, 拍摄同样一张图片但是不带物体 Mat light = Cv2.ImRead(lightPatternFile, ImreadModes.GrayScale); light = light.MedianBlur(3); if (lightMethod == 0) { img = light - img; } else if (lightMethod == 1) { img.ConvertTo(img, MatType.CV_32F); light.ConvertTo(light, MatType.CV_32F); img = (1 - (light / img)) * 255; img.ConvertTo(img, MatType.CV_8U); } //二值化图像 if (lightMethod == 0 || lightMethod == 1) { img = img.Threshold(30, 255, ThresholdTypes.Binary); } else { img = img.Threshold(140, 255, ThresholdTypes.BinaryInv); } if (segmentMethod == 1) { // 1 connected component 2 connected components with statistic(统计) 3 find contour(轮廓线) //int nLabels = Cv2.ConnectedComponents(img, label, PixelConnectivity.Connectivity8, MatType.CV_32S); ConnectedComponents components = img.ConnectedComponentsEx(); int nLabels = components.LabelCount; Point[][] points = img.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxSimple); int findCount = 0; for (int i = 0; i < points.Length; i++) { if (points[i].Length > 100) { findCount++; } } findCount--; Console.WriteLine(points.Length + "=find" + findCount); Console.WriteLine("number of objects = " + components.LabelCount); int count = 0; List <ConnectedComponents.Blob> list = new List <ConnectedComponents.Blob>(); for (int i = 0; i < components.Blobs.Count; i++) { if (i == 0) { continue; } ConnectedComponents.Blob blob = components.Blobs[i]; //实际区域大小 if (blob.Area > 2200 && blob.Width > 50 && blob.Height > 50) { //一瓶矿泉水 width = 227 height=171 area=15907 count++; list.Add(blob); } } list = list.OrderBy(r => r.Centroid.X).ToList(); Console.WriteLine("实际个数是:" + count); Console.WriteLine("width=" + img.Width + ",height=" + img.Height); foreach (var blob in list) { Console.WriteLine("area=" + blob.Area + ", (" + blob.Centroid.X + "," + blob.Centroid.Y + ") width=" + blob.Width + ",height=" + blob.Height + "left=" + blob.Left); } Mat output = Mat.Zeros(img.Rows, img.Cols, MatType.CV_8UC3); for (int m = 1; m < nLabels; m++) { //Mat mask = label.Equals(m); //output.SetTo(Scalar.RandomColor(),mask); Scalar scalar = Scalar.RandomColor(); Vec3b vec3B = scalar.ToVec3b(); for (int i = 0; i < img.Rows; i++) { for (int j = 0; j < img.Cols; j++) { int num = components.Labels[i, j]; if (num == m) { output.Set <Vec3b>(i, j, vec3B); } } } } using (Window window = new Window("check")) { window.ShowImage(output); Cv2.WaitKey(0); } } using (Window window = new Window("check")) { window.ShowImage(cloneImg); Cv2.WaitKey(0); } //Mat src = Cv2.ImRead("./Images/check1.jpg", ImreadModes.GrayScale); //// Histogram view //const int Width = 260, Height = 200; //Mat render = new Mat(new Size(Width, Height), MatType.CV_8UC3, Scalar.All(255)); //// Calculate histogram //Mat hist = new Mat(); //int[] hdims = { 256 }; // Histogram size for each dimension //Rangef[] ranges = { new Rangef(0, 256), }; // min/max //Cv2.CalcHist( // new Mat[] { src }, // new int[] { 0 }, // null, // hist, // 1, // hdims, // ranges); //// Get the max value of histogram //double minVal, maxVal; //Cv2.MinMaxLoc(hist, out minVal, out maxVal); //Scalar color = Scalar.All(100); //// Scales and draws histogram //hist = hist * (maxVal != 0 ? Height / maxVal : 0.0); //for (int j = 0; j < hdims[0]; ++j) //{ // int binW = (int)((double)Width / hdims[0]); // render.Rectangle( // new Point(j * binW, render.Rows - (int)(hist.Get<float>(j))), // new Point((j + 1) * binW, render.Rows), // color); //} //using (new Window("Image", WindowMode.AutoSize | WindowMode.FreeRatio, src)) //using (new Window("Histogram", WindowMode.AutoSize | WindowMode.FreeRatio, render)) //{ // Cv2.WaitKey(); //} //Mat mat = new Mat("./Images/check1.jpg",ImreadModes.GrayScale); //StringBuilder sb = new StringBuilder(); //for (int i = 0; i < mat.Rows; i++) //{ // for (int j = 0; j < mat.Cols; j++) // { // double[] arr = mat.GetArray(i, j); // sb.Append("("); // for (int k = 0; k < arr.Length; k++) // { // sb.Append(arr[k] + ","); // } // sb.Append(")"); // } // sb.AppendLine(); //} //File.WriteAllText("1.txt",sb.ToString(),Encoding.UTF8); //using (Window window = new Window("Lena", WindowMode.Normal, mat)) //{ // window.ShowImage(mat); // Cv2.WaitKey(100000); //} //VideoCapture videoCapture = new VideoCapture(@"D:\BaiduYunDownload\希赛系统架构师视频\3 JG:第03章 系统开发基础.wmv"); //Console.WriteLine(videoCapture.Fps+""+videoCapture.IsOpened()); //int sleepTime = (int)Math.Round(1000/25.0); //using (Window window = new Window("capture")) //{ // using (Mat image = new Mat()) // { // while (true) // { // videoCapture.Read(image); // if (image.Empty()) // { // break; // } // window.ShowImage(image); // Cv2.WaitKey(sleepTime); // } // } //} }