Commit b290e77b SK

分组算法优化

1 个父辈 bf447aef
......@@ -3,11 +3,13 @@ using OpenCvSharp;
using OpenCvSharp.Blob;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Acc.Img
......@@ -60,7 +62,14 @@ namespace Acc.Img
Cv2.CvtColor(imageMat, imageMat, ColorConversionCodes.RGBA2BGR);
Mat grayMat = BitmapConverter.ToMat(bitmap);
Cv2.CvtColor(grayMat, grayMat, ColorConversionCodes.RGB2GRAY);
Console.WriteLine("Start Thresh");
Stopwatch sw = new Stopwatch();
sw.Start();
CvBlobs blobs = AutoThreshBlobs(ref grayMat, itemArea);
//CvBlobs blobs = AutoThreshBlobsTask(ref grayMat, itemArea);
sw.Stop();
Console.WriteLine("Thresh End: " + sw.ElapsedMilliseconds + " ms");
int totalCount = findCircles(ref imageMat, grayMat, blobs, itemArea);
//imageMat = grayMat;
//int totalCount = CountBlobs(blobs, itemArea, ref imageMat);
......@@ -79,16 +88,16 @@ namespace Acc.Img
/// <returns></returns>
public static int findCircles(ref Mat srcMat, Mat threshMat, CvBlobs blobs, int avgArea)
{
Stopwatch sw = new Stopwatch();
sw.Start();
double[,] distanceArr = new double[threshMat.Cols, threshMat.Rows];
Task distanceTask = Task.Factory.StartNew(delegate {
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++)
......@@ -97,41 +106,29 @@ namespace Acc.Img
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);
List<Circle> resultCircles = new List<Circle>();
int totalCount = 0;
List<CvBlob> bigBlobs = new List<CvBlob>();
double maxWidth = 0;
double maxHeight = 0;
double maxRadius = 0;
Task findOneBlobTask = Task.Factory.StartNew(delegate {
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)
{
//结果数量+1
Interlocked.Increment(ref totalCount);
var list = blob.Contour.ConvertToPolygon().ToList();
var minRect = Cv2.MinAreaRect(list);
var width = minRect.Size.Width;
......@@ -143,90 +140,111 @@ namespace Acc.Img
}
widths.Add(width);
heights.Add(height);
}
else if(count > 1)
{
bigBlobs.Add(blob);
}
}
maxWidth = GetStandardMax(widths);
maxHeight = GetStandardMax(heights);
});
RediusPt rediusPt, rediusPtOut;
Console.WriteLine("Start find reel center");
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);
Task.WaitAll(distanceTask);
SplitItem item = findCircleInBlob(distanceArr, blobs, blob, reelCenter);
Task findMaxRadius = Task.Factory.StartNew(delegate {
List<double> radiusList = new List<double>();
foreach (CvBlob blob in blobs.Values)
{
int count = BlobHasItem(avgArea, blob);
if (count == 1)
{
//这里只会找一个最大的圆
SplitItem item = findCircleInBlob(distanceArr, blobs, blob);
Scalar color = Scalar.LightGreen;
foreach (Circle c in item.circles)
{
radiusList.Add(c.radius);
c.color = color;
resultCircles.Add(c);
}
}
});
oneBlobTasks.Add(task);
}
Task.WaitAll(oneBlobTasks.ToArray());
maxRadius = GetStandardMax(radiusList);
});
Task.WaitAll(findMaxRadius, findOneBlobTask);
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)
//
SplitItem.DIFF_PERCENT = 0.4;
if(maxHeight < maxRadius * 2)
{
maxHeight = maxRadius * 2;
}
maxWidth = maxWidth * 1.5;
Console.WriteLine("Start count");
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);
}
//从大到小排序 ,大的先执行
bigBlobs.OrderByDescending(b => b.Area);
Task countTask = Task.Factory.StartNew(delegate
{
int count = BlobHasItem(avgArea, blob);
if (count >= 1)
List<Task> countTasks = new List<Task>();
foreach (CvBlob blob in bigBlobs)
{
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
{
Task countTask = Task.Factory.StartNew(delegate {
//多个元器件,查找 所有圆
SplitItem item = findCircleInBlob(distanceArr, blobs, blob, reelCenter, maxWidth, maxRadius);
SplitItem item = findCircleInBlob(distanceArr, blobs, blob, maxWidth, maxRadius);
//对所有圆进行分组
List<List<Circle>> groupCircles = item.groupCircles(maxWidth, 2 * maxRadius, reelCenter);
List<List<Circle>> groupCircles = item.groupCircles(maxWidth, maxHeight, reelCenter);
foreach (List<Circle> groupCircle in groupCircles)
{
if (groupCircle.Count == 0)
{
continue;
}
Circle c = groupCircle[0];
c.color = color;
resultCircles.Add(c);
}
}
//resultCircles.AddRange(groupCircle);
Interlocked.Increment(ref totalCount);
resultCircles.Add(groupCircle[0]);
}
}
});
countTasks.Add(countTask);
}
}
Task.WaitAll(countTasks.ToArray());
int index = 0;
foreach (var c in resultCircles)
{
if(c == null)
{
continue;
}
index++;
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);
sw.Stop();
Console.WriteLine("Count: " + totalCount + " Time:" + sw.ElapsedMilliseconds + " ms");
return totalCount;
}
......@@ -280,6 +298,7 @@ namespace Acc.Img
Mat resultMat = new Mat();
for (int index = startIndex; index < endIndex; index++)
{
Console.WriteLine(" Thresh Task " + index);
Mat threshMat = new Mat();
Cv2.Threshold(imageMat, threshMat, index, 255, ThresholdTypes.BinaryInv);
CvBlobs blobs = new CvBlobs();
......@@ -298,6 +317,90 @@ namespace Acc.Img
return resultBlobs;
}
private static CvBlobs AutoThreshBlobsTask(ref Mat grayMat, int blobArea)
{
Mat[] mats = new Mat[] { grayMat };//一张图片,初始化
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 threshIndex = avgIndex;
double theArea = blobArea * 0.8;
if (theArea < 1) theArea = 1;
Console.WriteLine(" Thresh Task Start.. ");
Mat srcMat = grayMat;
List<Task> tasks = new List<Task>();
int index = startIndex;
object lockObj = new object();
int maxBlobCount = 0;
int bestThreshIndex = 0;
CvBlobs resultBlobs = new CvBlobs();
Mat resultMat = new Mat();
for (int i = 0; i < 2; i++)
{
Task task = Task.Factory.StartNew(delegate
{
while (index < endIndex)
{
index = Interlocked.Increment(ref index); ;
Console.WriteLine(" Thresh Task " + index);
Mat threshMat = new Mat();
Cv2.Threshold(srcMat, threshMat, index, 255, ThresholdTypes.BinaryInv);
CvBlobs blobs = new CvBlobs();
blobs.Label(threshMat);
List<CvBlob> blobList = blobs.Values.Where(b => b.Area > theArea).ToList();
lock (lockObj)
{
if (blobList.Count > maxBlobCount)
{
maxBlobCount = blobList.Count;
bestThreshIndex = index;
resultBlobs = blobs;
resultMat = threshMat;
}
}
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("best thresh= " + bestThreshIndex + " Blob: " + resultBlobs.Count + "middle thresh= " + threshIndex + " Area:" + theArea);
return resultBlobs;
}
//获取圆心半径
public struct RediusPt
{
......@@ -448,7 +551,7 @@ namespace Acc.Img
/// <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)
public static SplitItem findCircleInBlob(double[,] matDistanceArr, CvBlobs blobs, CvBlob blob, double oneBlobWidth = -1, double oneBlobRadius = -1)
{
SplitItem item = new SplitItem();
while (true)
......@@ -459,7 +562,7 @@ namespace Acc.Img
{
for (int y = blob.MinY; y < blob.MaxY; y++)
{
double distance = matDistanceArr[x,y];
double distance = matDistanceArr[x, y];
if (distance > 0)
{
int label = blobs.GetLabel(x, y);
......@@ -471,22 +574,22 @@ namespace Acc.Img
hasPixToHandle = true;
if (!item.isEnd)
{
if(distance < SplitItem.DIFF_PERCENT * oneBlobRadius)
if (distance < SplitItem.DIFF_PERCENT * oneBlobRadius)
{
matDistanceArr[x,y] = 0;
matDistanceArr[x, y] = 0;
continue;
}
//点到圆心的距离
double distanceToCircle = item.minDistanceToCircles(x, y, reelCenter, oneBlobWidth, oneBlobRadius);
double distanceToCircle = item.minDistanceToCircles(x, y, oneBlobWidth, oneBlobRadius);
if (distanceToCircle == 0)
{
matDistanceArr[x,y] = 0;
matDistanceArr[x, y] = 0;
continue;
}
if (distanceToCircle > 0 && distanceToCircle < distance)
{
distance = distanceToCircle;
matDistanceArr[x,y] = distance;
matDistanceArr[x, y] = distance;
}
if (distance > item.currentMaxRadius)
{
......@@ -495,41 +598,12 @@ namespace Acc.Img
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)
hasFind = true;
Circle circle = item.calOneItem(oneBlobRadius);
if (circle != null)
{
matDistanceArr[rectX,rectY] = 0;
}
}
removeCircleFromDistanceArr(ref matDistanceArr, circle.x, circle.y, circle.radius, blob.Label, blobs);
}
hasFind = true;
break;
}
}
......@@ -542,28 +616,50 @@ namespace Acc.Img
break;
}
}
if (!hasFind)
{
item.calOneItem(oneBlobRadius);
}
if (item.isEnd || !hasPixToHandle)
Circle circle = item.calOneItem(oneBlobRadius);
if (circle != null)
{
break;
removeCircleFromDistanceArr(ref matDistanceArr, circle.x, circle.y, circle.radius, blob.Label, blobs);
}
}
//if(item.circles.Count > 3000)
//{
// return item;
//}
//单个Blob只查找 最大的一个
if(oneBlobRadius == -1 && item.circles.Count == 1)
if (oneBlobRadius == -1 && item.circles.Count == 1)
{
return item;
}
if (item.isEnd || !hasPixToHandle)
{
break;
}
}
return item;
}
private static void removeCircleFromDistanceArr(ref double[,] distanceArr, int x, int y, double radius, int label, CvBlobs blobs)
{
//填充外接正方形
for (int rectX = (int)(x - radius); rectX < (int)(x + radius); rectX++)
{
// int len = distanceArr.GetLength(0);
for (int rectY = (int)(y - radius); rectY < (int)(y + radius); rectY++)
{
// int pixLabel = blobs.GetLabel(rectX, rectY);
//if (pixLabel == label)
{
distanceArr[rectX, rectY] = 0;
}
}
}
}
public static double GetStandardMax(List<double> datas)
{
int count = datas.Count;
......@@ -576,9 +672,16 @@ namespace Acc.Img
// 除以数量,然后开方
double std = Math.Sqrt(sum / count);
List<double> list = datas.Where(d => Math.Abs(d - avg) < std).ToList();
if (list.Count > 0)
{
return list.Max();
}
else
{
return avg;
}
}
return 0;
}
......
......@@ -9,7 +9,7 @@ namespace AccImage
{
public class SplitItem
{
public static double DIFF_PERCENT = 0.5;
public static double DIFF_PERCENT = 0.4;
public bool isEnd = false;
public double avgRadius = 0;
public int centerX = 0;
......@@ -17,12 +17,10 @@ namespace AccImage
public double currentMaxRadius = 0;
public List<Circle> circles = new List<Circle>();
private List<Circle> nearbyCircles = new List<Circle>();
/// <summary>
/// 一遍结束后调用
/// 一遍结束后调用, 返回当前圆是否有效
/// </summary>
public void calOneItem(double theFixRadius=-1)
public Circle calOneItem(double theFixRadius=-1)
{
//该半径是否有效
bool isValid = false;
......@@ -44,7 +42,7 @@ namespace AccImage
if(currentMaxRadius == 0)
{
isEnd = true;
return;
return null;
}
Circle circle = new Circle();
if(theFixRadius != -1 && currentMaxRadius > theFixRadius)
......@@ -55,11 +53,6 @@ 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;
......@@ -67,13 +60,14 @@ namespace AccImage
{
avgRadius = circle.radius;
}
return circle;
}
else
{
isEnd = true;
}
return null;
}
......@@ -84,45 +78,86 @@ namespace AccImage
{
c.calDistanceToCenter(center);
}
List<List<Circle>> allGroupCircle = new List<List<Circle>>();
List<int> allreadyGroup = new List<int>();
//List<Circle> groupCircle = new List<Circle>();
//groupCircle.Clear();
//排序
circles.OrderBy(c => c.distanceToCenter);
List<List<Circle>> lineCircles = new List<List<Circle>>();
List<int> allreadyLabel = new List<int>();
//与圆心距离在h范围内的分为一组
while (true)
{
List<Circle> groupCircle = new List<Circle>();
Circle labelCircle = null;
for (int i=0;i<circles.Count; i++)
List<Circle> lineCircle = new List<Circle>();
Circle firstLineCircle = null;
for (int i = 0; i < circles.Count; i++)
{
if (!allreadyGroup.Contains(i))
if (!allreadyLabel.Contains(i))
{
Circle c = circles[i];
if (labelCircle == null)
if (firstLineCircle == null)
{
labelCircle = c;
allreadyGroup.Add(i);
groupCircle.Add(c);
firstLineCircle = c;
allreadyLabel.Add(i);
lineCircle.Add(c);
}
else
{
//到圆心的距离差小于H
if (Math.Abs(c.distanceToCenter - labelCircle.distanceToCenter) < h)
if (Math.Abs(c.distanceToCenter - firstLineCircle.distanceToCenter) < h)
{
//两圆心之间的距离+直径小于W认为是同一个元器件
if (c.distanceToCircle(labelCircle) < w+h*2/3)
lineCircle.Add(c);
allreadyLabel.Add(i);
}
}
}
}
lineCircles.Add(lineCircle);
if (allreadyLabel.Count == circles.Count)
{
groupCircle.Add(c);
allreadyGroup.Add(i);
break;
}
}
//再按长度划分
List<List<Circle>> allGroupCircle = new List<List<Circle>>();
foreach (var lineCircle in lineCircles)
{
//排序
lineCircle.OrderBy(c => c.x);
List<int> lineAllreadyLabel = new List<int>();
while (true)
{
List<Circle> groupCircle = new List<Circle>();
Scalar color = Scalar.Yellow;
for (int i = 0; i < lineCircle.Count; i++)
{
if (!lineAllreadyLabel.Contains(i))
{
Circle c = lineCircle[i];
//圆心之间的距离+直径小于W认为是同一个元器件
bool inGroup = true;
foreach (var gCircle in groupCircle)
{
if (c.distanceToCircle(gCircle) > w)
{
inGroup = false;
}
}
if (inGroup)
{
c.color = color;
groupCircle.Add(c);
lineAllreadyLabel.Add(i);
}
}
}
allGroupCircle.Add(groupCircle);
if (allreadyGroup.Count == circles.Count)
if (lineAllreadyLabel.Count == lineCircle.Count)
{
break;
}
}
}
return allGroupCircle;
}
......@@ -133,14 +168,14 @@ namespace AccImage
/// <param name="px"></param>
/// <param name="py"></param>
/// <returns></returns>
public double minDistanceToCircles(int px, int py, Point2d reelCenter, double oneBlobWidth =-1, double oneBlobRadius = -1)
public double minDistanceToCircles(int px, int py, double oneBlobWidth =-1, double oneBlobRadius = -1)
{
Point2d point = new Point2d(px, py);
double minDistanceToCircle = -1;
List<Circle> neighbourCircles = nearbyCircles;
List<Circle> neighbourCircles = circles;
if (oneBlobRadius > 0)
{
neighbourCircles = nearbyCircles.Where(c => Math.Abs(c.x - px) <= 2 * oneBlobRadius && Math.Abs(c.y - py) <= 2 * oneBlobRadius).ToList();
neighbourCircles = neighbourCircles.Where(c => Math.Abs(c.x - px) <= 2 * oneBlobRadius && Math.Abs(c.y - py) <= 2 * oneBlobRadius).ToList();
}
foreach (Circle c in neighbourCircles)
{
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!