Commit dc4f85e6 SK

优化粘连

1 个父辈 e7982bd2
......@@ -63,6 +63,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AccUtil.cs" />
<Compile Include="ImageUtil.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplitItem.cs" />
......
using AccImage;
using OpenCvSharp;
using OpenCvSharp.Blob;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Threading.Tasks;
namespace Acc.Img
{
public class AccUtil
{
/// <summary>
/// 获取单个元器件的特征
/// </summary>
/// <param name="image"></param>
/// <param name="markX"></param>
/// <param name="markY"></param>
/// <returns></returns>
public static int GetFeature(ref Image image, int markX, int markY)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
Mat dst = new Mat();
Cv2.CvtColor(imageMat, dst, ColorConversionCodes.RGB2GRAY);
//全局二值化
Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
image = BitmapConverter.ToBitmap(dst);
CvBlobs blobs = new CvBlobs();
blobs.Label(dst);
int blobArea = -1;
foreach (CvBlob blob in blobs.Values)
{
if (blob.Rect.Contains(new OpenCvSharp.Point(markX, markY)))
{
if (blob.Area < blobArea || blobArea == -1)
{
blobArea = blob.Area;
}
}
}
return blobArea;
}
/// <summary>
/// 根据元器件特征统计图片中的元器件数量
/// </summary>
/// <param name="image"></param>
/// <param name="itemFeature"></param>
/// <param name="thresh"></param>
/// <param name="inv"></param>
/// <returns></returns>
public static int CountItems(ref Image image, int itemArea)
{
Bitmap bitmap = new Bitmap(image);
Mat imageMat = BitmapConverter.ToMat(bitmap);
Cv2.CvtColor(imageMat, imageMat, ColorConversionCodes.RGBA2BGR);
Mat grayMat = BitmapConverter.ToMat(bitmap);
Cv2.CvtColor(grayMat, grayMat, ColorConversionCodes.RGB2GRAY);
CvBlobs blobs = AutoThreshBlobs(ref grayMat, itemArea);
int totalCount = findCircles(ref imageMat, grayMat, blobs, itemArea);
//imageMat = grayMat;
//int totalCount = CountBlobs(blobs, itemArea, ref imageMat);
image = BitmapConverter.ToBitmap(imageMat);
return totalCount;
}
/// <summary>
/// 查找Blobs包含的元器件数量
/// </summary>
/// <param name="srcMat"></param>
/// <param name="threshMat"></param>
/// <param name="blobs"></param>
/// <param name="avgArea"></param>
/// <returns></returns>
public static int findCircles(ref Mat srcMat, Mat threshMat, CvBlobs blobs, int avgArea)
{
Mat distanceMat = new Mat();
Cv2.DistanceTransform(threshMat, distanceMat, DistanceTypes.L2, DistanceMaskSize.Mask3);
double[,] distanceArr = new double[threshMat.Cols, threshMat.Rows];
Console.WriteLine("Start to distance array");
unsafe
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int y = 0; y < threshMat.Rows; y++)
{
for (int x = 0; x < threshMat.Cols; x++)
{
float* dd = (float*)distanceMat.Ptr(y, x);
distanceArr[x, y] = dd[0];
}
}
sw.Stop();
Console.WriteLine("=" + sw.ElapsedMilliseconds + " ms");
}
Dictionary<int, SplitItem> blobCircles = new Dictionary<int, SplitItem>();
Console.WriteLine("Start find reel center");
RediusPt rediusPt, rediusPtOut;
GetCenter(srcMat, out rediusPt);
GetOutContour(srcMat, out rediusPtOut);
Point2d reelCenter = new Point2d(0, 0);
reelCenter.X = rediusPtOut.pt.X;
reelCenter.Y = rediusPtOut.pt.Y;
srcMat.Line(new OpenCvSharp.Point(rediusPtOut.pt.X - 10, rediusPtOut.pt.Y), new OpenCvSharp.Point(rediusPtOut.pt.X + 10, rediusPtOut.pt.Y), Scalar.Blue);
srcMat.Line(new OpenCvSharp.Point(rediusPtOut.pt.X, rediusPtOut.pt.Y - 10), new OpenCvSharp.Point(rediusPtOut.pt.X, rediusPtOut.pt.Y + 10), Scalar.Blue);
srcMat.Circle(rediusPt.pt, (int)rediusPt.radius -10, Scalar.Red);
srcMat.Circle(rediusPtOut.pt, (int)rediusPtOut.radius - 10, Scalar.Red);
Console.WriteLine("Start find reel Max Radius, max Width");
List<double> widths = new List<double>();
List<double> heights = new List<double>();
List<double> radiusList = new List<double>();
List<Task> oneBlobTasks = new List<Task>();
foreach (CvBlob blob in blobs.Values)
{
Task task = Task.Factory.StartNew(delegate {
int count = BlobHasItem(avgArea, blob);
if (count == 1)
{
var list = blob.Contour.ConvertToPolygon().ToList();
var minRect = Cv2.MinAreaRect(list);
var width = minRect.Size.Width;
var height = minRect.Size.Height;
if (width < height)
{
width = minRect.Size.Height;
height = minRect.Size.Width;
}
widths.Add(width);
heights.Add(height);
SplitItem item = findCircleInBlob(distanceArr, blobs, blob, reelCenter);
Scalar color = Scalar.LightGreen;
foreach (Circle c in item.circles)
{
radiusList.Add(c.radius);
}
}
});
oneBlobTasks.Add(task);
}
Task.WaitAll(oneBlobTasks.ToArray());
var maxWidth = GetStandardMax(widths);
var maxHeight = GetStandardMax(heights);
var maxRadius = GetStandardMax(radiusList);
Console.WriteLine(maxWidth + " = " + maxHeight + " R=" + maxRadius);
//放大宽度,防止误判断
maxWidth = maxWidth * 1.6;
Console.WriteLine("Start count");
List<Circle> resultCircles = new List<Circle>();
List<Task> countTasks = new List<Task>();
foreach (CvBlob blob in blobs.Values)
{
Scalar color = Scalar.RandomColor();
if (blob.Area > 1000)
{
blob.Contour.Render(srcMat, color);
Cv2.PutText(srcMat, blob.Label + "", new OpenCvSharp.Point(blob.Centroid.X - 40, blob.Centroid.Y + 30), HersheyFonts.HersheyComplex, 1.0, color);
}
Task countTask = Task.Factory.StartNew(delegate
{
int count = BlobHasItem(avgArea, blob);
if (count >= 1)
{
bool isReelCenter = blob.Centroid.DistanceTo(new Point2d(rediusPt.pt.X, rediusPt.pt.Y)) < rediusPt.radius - 10;
if (!isReelCenter)
{
if (count == 1)
{
Circle c = new Circle();
c.radius = maxRadius;
c.x = (int)blob.Centroid.X;
c.y = (int)blob.Centroid.Y;
resultCircles.Add(c);
}
else
{
//多个元器件,查找 所有圆
SplitItem item = findCircleInBlob(distanceArr, blobs, blob, reelCenter, maxWidth, maxRadius);
//对所有圆进行分组
List<List<Circle>> groupCircles = item.groupCircles(maxWidth, 2 * maxRadius, reelCenter);
foreach (List<Circle> groupCircle in groupCircles)
{
if (groupCircle.Count == 0)
{
continue;
}
Circle c = groupCircle[0];
c.color = color;
resultCircles.Add(c);
}
}
}
}
});
countTasks.Add(countTask);
}
Task.WaitAll(countTasks.ToArray());
foreach (var c in resultCircles)
{
srcMat.Circle(c.x, c.y, (int)c.radius, c.color);
}
int totalCount = resultCircles.Count;
Cv2.PutText(srcMat, totalCount + "", new OpenCvSharp.Point(reelCenter.X - 40, reelCenter.Y + 30), HersheyFonts.HersheyComplex, 1.0, Scalar.LightGreen);
Console.WriteLine("===========" + totalCount);
return totalCount;
}
private static CvBlobs AutoThreshBlobs(ref Mat imageMat, int blobArea)
{
Mat[] mats = new Mat[] { imageMat };//一张图片,初始化为panda
Mat hist = new Mat();//用来接收直方图
int[] channels = new int[] { 0 };//一个通道,初始化为通道0
int[] histsize = new int[] { 256 };//一个通道,初始化为256箱子
Rangef[] range = new Rangef[1];//一个通道,值范围
range[0].Start = 0.0F;//从0开始(含)
range[0].End = 256.0F;//到256结束(不含)
Mat mask = new Mat();//不做掩码
Cv2.CalcHist(mats, channels, mask, hist, 1, histsize, range);//计算灰度图,dim为1 1维
double total = 0;
for (int i = 0; i < 256; i++)//灰度值总数量
{
total = total + hist.Get<double>(i);
}
double percent = 0;
int startIndex = -1;
int endIndex = -1;
for (int i = 0; i < 256; i++)//直方图
{
double len = hist.Get<double>(i);
if (len > 100)
{//灰度值的像素数小于100的忽略
if (startIndex == -1)
{
startIndex = i;
}
endIndex = i - 1;
percent = percent + len / total;
//近似的认为元器件的灰度值 数量占总数的百分比小于10%
if (percent > 0.1)
{
break;
}
}
}
int avgIndex = (startIndex + endIndex) / 2;
int blobCount = 0;
int threshIndex = avgIndex;
double theArea = blobArea * 0.8;
if (theArea < 1) theArea = 1;
CvBlobs resultBlobs = new CvBlobs();
Mat resultMat = new Mat();
for (int index = startIndex; index < endIndex; index++)
{
Mat threshMat = new Mat();
Cv2.Threshold(imageMat, threshMat, index, 255, ThresholdTypes.BinaryInv);
CvBlobs blobs = new CvBlobs();
blobs.Label(threshMat);
List<CvBlob> blobList = blobs.Values.Where(b => b.Area > theArea).ToList();
if (blobList.Count > blobCount)
{
threshIndex = index;
resultMat = threshMat;
resultBlobs = blobs;
blobCount = blobList.Count;
}
}
imageMat = resultMat;
Console.WriteLine("thresh: " + threshIndex + " Blob: " + resultBlobs.Count + " Area:" + theArea);
return resultBlobs;
}
//获取圆心半径
public struct RediusPt
{
public OpenCvSharp.Point pt;
public int radius;
}
public static bool GetCenter(Mat srcMat, out RediusPt rediusPt)
{
Mat grayMAT = srcMat.Clone();
Cv2.CvtColor(grayMAT, grayMAT, ColorConversionCodes.BGR2GRAY);
Binarizer.Sauvola(grayMAT, grayMAT, 91, 0.1, 61);
Cv2.MedianBlur(grayMAT, grayMAT, 5);
Mat element = Cv2.GetStructuringElement(MorphShapes.Cross, new OpenCvSharp.Size(41, 41));
Cv2.Erode(grayMAT, grayMAT, element);
CvBlobs blobs = new CvBlobs();
blobs.Label(grayMAT);
CvBlob centerMat = null;
Point2d pt;
foreach (CvBlob item in blobs.Values)
{
item.SetMoments();
pt = item.CalcCentroid();
if (pt.X - 3072 * 0.5 < 150 && pt.Y - 3072 * 0.5 < 150 && 3072 * 0.5 - pt.X < 150 && 3072 * 0.5 - pt.Y < 150 && item.Rect.Width < 900)
{
if (centerMat == null)
{
centerMat = item;
}
else if (centerMat.Rect.Width < item.Rect.Width)
{
centerMat = item;
}
}
}
if (centerMat != null)
{
pt = centerMat.CalcCentroid();
rediusPt.pt.X = centerMat.Rect.Width / 2 + centerMat.Rect.X;
rediusPt.pt.Y = centerMat.Rect.Height / 2 + centerMat.Rect.Y;
if (centerMat.Rect.Width < centerMat.Rect.Height)
rediusPt.radius = (int)Math.Round(centerMat.Rect.Width * 0.5);
else
rediusPt.radius = (int)Math.Round(centerMat.Rect.Height * 0.5);
return true;
}
else
{
rediusPt.radius = -1;
rediusPt.pt = new OpenCvSharp.Point(0, 0);
return false;
}
}
//获取最外轮廓
public static bool GetOutContour(Mat srcMat, out RediusPt rediusPt)
{
Mat grayMAT = srcMat.Clone();
Cv2.CvtColor(grayMAT, grayMAT, ColorConversionCodes.BGR2GRAY);
Binarizer.Sauvola(grayMAT, grayMAT, 91, 0.1, 61);
Cv2.MedianBlur(grayMAT, grayMAT, 5);
Mat element = Cv2.GetStructuringElement(MorphShapes.Cross, new OpenCvSharp.Size(41, 41));
Cv2.Erode(grayMAT, grayMAT, element);
grayMAT = ~grayMAT;
CvBlobs blobs = new CvBlobs();
blobs.Label(grayMAT);
CvBlob centerMat = null;
Point2d pt;
foreach (CvBlob item in blobs.Values)
{
item.SetMoments();
pt = item.CalcCentroid();
if (pt.X - 3072 * 0.5 < 150 && pt.Y - 3072 * 0.5 < 150 && 3072 * 0.5 - pt.X < 150 && 3072 * 0.5 - pt.Y < 150)
{
if (centerMat == null)
{
centerMat = item;
}
else if (centerMat.Rect.Width < item.Rect.Width)
{
centerMat = item;
}
}
}
if (centerMat != null)
{
pt = centerMat.CalcCentroid();
rediusPt.pt.X = (int)pt.X;
rediusPt.pt.Y = (int)pt.Y;
if (centerMat.Rect.Width > centerMat.Rect.Height)
rediusPt.radius = (int)Math.Round(centerMat.Rect.Width * 0.5);
else
rediusPt.radius = (int)Math.Round(centerMat.Rect.Height * 0.5);
return true;
}
else
{
rediusPt.radius = -1;
rediusPt.pt = new OpenCvSharp.Point(0, 0);
return false;
}
}
/// <summary>
/// 给定Blob包含几个元器件
/// </summary>
/// <param name="averageArea"></param>
/// <param name="blob"></param>
/// <returns></returns>
public static int BlobHasItem(int averageArea, CvBlob blob)
{
int blobArea = blob.Area;
double minArea = 0.5 * averageArea;
double k = 1.5;
if (averageArea < 50)
{
minArea = 0.2 * averageArea;
}
if (blobArea < minArea * 2 / 3)
{
return 0;
}
int count = 0;
count = (int)((blobArea + k * averageArea) / (k * averageArea));
if (count == 0 || count == 1)
{
count = 1;
}
// if (count <= 5000)
{
return count;
}
// return 0;
}
/// <summary>
/// 查找Blob中包含的所有与给定半径差不多的内接圆
/// </summary>
/// <param name="matDistanceArr"></param>
/// <param name="blobs"></param>
/// <param name="blob"></param>
/// <param name="reelCenter"></param>
/// <param name="oneBlobWidth"></param>
/// <param name="oneBlobRadius"></param>
/// <returns></returns>
public static SplitItem findCircleInBlob(double[,] matDistanceArr, CvBlobs blobs, CvBlob blob, Point2d reelCenter, double oneBlobWidth = -1, double oneBlobRadius = -1)
{
SplitItem item = new SplitItem();
while (true)
{
bool hasFind = false;
bool hasPixToHandle = false;
for (int x = blob.MinX; x < blob.MaxX; x++)
{
for (int y = blob.MinY; y < blob.MaxY; y++)
{
double distance = matDistanceArr[x,y];
if (distance > 0)
{
int label = blobs.GetLabel(x, y);
if (label != blob.Label)
{
//不是当前Blob的像素
continue;
}
hasPixToHandle = true;
if (!item.isEnd)
{
if(distance < SplitItem.DIFF_PERCENT * oneBlobRadius)
{
matDistanceArr[x,y] = 0;
continue;
}
//点到圆心的距离
double distanceToCircle = item.minDistanceToCircles(x, y, reelCenter, oneBlobWidth, oneBlobRadius);
if (distanceToCircle == 0)
{
matDistanceArr[x,y] = 0;
continue;
}
if (distanceToCircle > 0 && distanceToCircle < distance)
{
distance = distanceToCircle;
matDistanceArr[x,y] = distance;
}
if (distance > item.currentMaxRadius)
{
item.currentMaxRadius = distance;
item.centerX = x;
item.centerY = y;
if (oneBlobRadius != -1 && distance >= oneBlobRadius)
{
item.calOneItem(oneBlobRadius);
//填充1.5倍的方框
int left = (int) (x - oneBlobRadius - SplitItem.DIFF_PERCENT * oneBlobRadius);
if(left < 0)
{
left = 0;
}
int right = (int)(x + oneBlobRadius + SplitItem.DIFF_PERCENT * oneBlobRadius);
int top = (int)(y - oneBlobRadius - SplitItem.DIFF_PERCENT * oneBlobRadius);
if(top < 0)
{
top = 0;
}
int bottom = (int)(y + oneBlobRadius + SplitItem.DIFF_PERCENT * oneBlobRadius);
for (int rectX=left; rectX<right; rectX++)
{
if(right >= matDistanceArr.GetLength(1))
{
break;
}
int len = matDistanceArr.GetLength(0);
for (int rectY = top; rectY < bottom; rectY++)
{
if (rectY >=len)
{
break;
}
int pixLabel = blobs.GetLabel(rectX, rectY);
if (pixLabel == blob.Label)
{
matDistanceArr[rectX,rectY] = 0;
}
}
}
hasFind = true;
break;
}
}
}
}
}
if (hasFind)
{
break;
}
}
if (!hasFind)
{
item.calOneItem(oneBlobRadius);
}
if (item.isEnd || !hasPixToHandle)
{
break;
}
//if(item.circles.Count > 3000)
//{
// return item;
//}
//单个Blob只查找 最大的一个
if(oneBlobRadius == -1 && item.circles.Count == 1)
{
return item;
}
}
return item;
}
public static double GetStandardMax(List<double> datas)
{
int count = datas.Count;
if (count > 0)
{
// 计算平均数
double avg = datas.Average();
// 计算各数值与平均数的差值的平方,然后求和
double sum = datas.Sum(d => Math.Pow(d - avg, 2));
// 除以数量,然后开方
double std = Math.Sqrt(sum / count);
List<double> list = datas.Where(d => Math.Abs(d - avg) < std).ToList();
return list.Max();
}
return 0;
}
}
}
......@@ -9,6 +9,7 @@ namespace AccImage
{
public class SplitItem
{
public static double DIFF_PERCENT = 0.5;
public bool isEnd = false;
public double avgRadius = 0;
public int centerX = 0;
......@@ -16,6 +17,8 @@ namespace AccImage
public double currentMaxRadius = 0;
public List<Circle> circles = new List<Circle>();
private List<Circle> nearbyCircles = new List<Circle>();
/// <summary>
/// 一遍结束后调用
/// </summary>
......@@ -29,9 +32,9 @@ namespace AccImage
}
else
{
//与平均半径差值在10%以内,认为OK
//与平均半径差值在50%以内,认为OK
double diff = avgRadius - currentMaxRadius;
if (diff/avgRadius <= 0.5)
if (diff/avgRadius <= DIFF_PERCENT)
{
isValid = true;
}
......@@ -52,26 +55,26 @@ namespace AccImage
circle.x = centerX;
circle.y = centerY;
circles.Add(circle);
if(nearbyCircles.Count % 50 == 0)
{
nearbyCircles = nearbyCircles.Where(c => centerX - c.x < 2 * currentMaxRadius).ToList();
}
nearbyCircles.Add(circle);
currentMaxRadius = 0;
centerX = 0;
centerY = 0;
////平均半径
//double total = 0;
//foreach (Circle c in circles)
//{
// total = total + c.radius;
//}
//avgRadius = total / circles.Count;
if(circle.radius > avgRadius)
{
avgRadius = circle.radius;
}
}
else
{
isEnd = true;
}
}
......@@ -134,31 +137,23 @@ namespace AccImage
{
Point2d point = new Point2d(px, py);
double minDistanceToCircle = -1;
foreach (Circle c in circles)
List<Circle> neighbourCircles = nearbyCircles;
if (oneBlobRadius > 0)
{
neighbourCircles = nearbyCircles.Where(c => Math.Abs(c.x - px) <= 2 * oneBlobRadius && Math.Abs(c.y - py) <= 2 * oneBlobRadius).ToList();
}
foreach (Circle c in neighbourCircles)
{
Point2d circleCenter = new Point2d(c.x, c.y);
double distanceToCircle = point.DistanceTo(circleCenter);
if (distanceToCircle <= c.radius)
double distanceToCircle = point.DistanceTo(circleCenter) - c.radius;
if (distanceToCircle <= DIFF_PERCENT * c.radius)
{
return 0;
}
//当前点到料盘中心与圆心到料盘中心的距离在直径内,忽略
//if(oneBlobRadius != -1 && oneBlobWidth != -1)
//{
// double distanceToReelCenter = point.DistanceTo(reelCenter);
// double circleCenterToReelCenter = circleCenter.DistanceTo(reelCenter);
// if (Math.Abs(distanceToReelCenter - circleCenterToReelCenter) < oneBlobRadius)
// {
// if (distanceToCircle < oneBlobWidth)
// {
// return 0;
// }
// }
//}
if (minDistanceToCircle == -1 || distanceToCircle < minDistanceToCircle)
{
minDistanceToCircle = distanceToCircle - c.radius;
minDistanceToCircle = distanceToCircle;
}
}
return minDistanceToCircle;
......@@ -171,6 +166,7 @@ namespace AccImage
public int x;
public int y;
public double radius;
public Scalar color = Scalar.Green;
public double distanceToCenter = 0;
public double distanceToCircle(Circle c)
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!