Commit 8511f266 SK

距离变换找圆法和与料盘中心距离相结合统计数量,

1 个父辈 2671fba1
......@@ -64,6 +64,7 @@
<ItemGroup>
<Compile Include="ImageUtil.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplitItem.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="FodyWeavers.xml" />
......
using OpenCvSharp;
using AccImage;
using OpenCvSharp;
using OpenCvSharp.Blob;
using OpenCvSharp.Extensions;
using System;
......@@ -12,9 +13,6 @@ namespace Acc.Img
{
public class ImageUtil
{
public static bool selectB = false;
public static bool pngB = false;
/// <summary>
/// 读取图片,,支持格式*.raw,*.bmp;*.gif;*.jpg;*.png
/// </summary>
......@@ -27,7 +25,6 @@ namespace Acc.Img
{
if (imagePath.ToLower().EndsWith(".raw"))
{
pngB = false;
byte[] src = System.IO.File.ReadAllBytes(imagePath);
int width = 3072;
int height = 3072;
......@@ -51,44 +48,22 @@ namespace Acc.Img
System.Runtime.InteropServices.Marshal.Copy(buff, 0, bmpData.Scan0, bmpData.Stride * height);
bmp.UnlockBits(bmpData);
//bmp.Save(@"C:\Users\ASA\Desktop\222.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
image= bmp;
image = bmp;
}
else
{
pngB = true;
image = Image.FromFile(imagePath);
image = Image.FromFile(imagePath);
}
}catch(Exception)
}
catch (Exception)
{
}
return image;
}
public static Image FindCircle(Image image, int thresh, bool inv)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
Cv2.PyrDown(imageMat, imageMat);
Mat threshMat = Threshhold(imageMat, thresh, inv);
//Cv2.GaussianBlur(imageMat, imageMat, new OpenCvSharp.Size(7, 7), 5);
//threshMat = Threshhold(imageMat, thresh, inv);
Mat k1 = Mat.Ones(new OpenCvSharp.Size(21, 21), MatType.CV_8UC1);
Cv2.MorphologyEx(threshMat, threshMat, MorphTypes.Open, k1);
CircleSegment[] circles = Cv2.HoughCircles(threshMat, HoughMethods.Gradient, 1, 5);
foreach(CircleSegment circle in circles)
{
Point2f center = circle.Center;
Cv2.Circle(imageMat, new OpenCvSharp.Point(center.X, center.Y), (int)circle.Radius, Scalar.White);
}
return BitmapConverter.ToBitmap(threshMat);
}
/// <summary>
/// 二值化图像
/// </summary>
......@@ -96,10 +71,10 @@ namespace Acc.Img
/// <param name="thresh">阈值</param>
/// <param name="inv">true表示元器件为白色,false表示元器件为黑色</param>
/// <returns>二值化后的图像</returns>
public static Image Threshhold(Image image, int thresh, bool inv=true)
public static Image Threshhold(Image image, int thresh)
{
Mat imageMat =BitmapConverter.ToMat(new Bitmap(image));
Mat threshMat = Threshhold(imageMat, thresh, inv);
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
Mat threshMat = Threshhold(imageMat, thresh);
return BitmapConverter.ToBitmap(threshMat);
}
......@@ -112,10 +87,10 @@ namespace Acc.Img
/// <param name="thresh">二值化阈值</param>
/// <param name="inv">true表示元器件为白色,false表示元器件为黑色</param>
/// <returns>鼠标指向的元器件特征值</returns>
public static int GetItemFeature(Image image, int markX = -1, int markY = -1, int thresh = -1, bool inv = true)
public static int GetItemFeature(Image image, int markX = -1, int markY = -1, int thresh = -1)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
List<CvBlob> blobList = GetBlobs(imageMat, thresh, inv);
List<CvBlob> blobList = GetBlobs(imageMat, thresh);
int blobCount = blobList.Count;
int selectIndex = blobCount / 2;
if (markX != -1 && markY != -1)
......@@ -136,7 +111,7 @@ namespace Acc.Img
if (markIndex != -1)
{
int area = blobList[markIndex].Area;
area = area * 5/3 - 23;
area = area * 5 / 3 - 23;
return area;
}
}
......@@ -145,12 +120,12 @@ namespace Acc.Img
public static int GetItemFeatureAuto(Image image, int markX = -1, int markY = -1, int thresh = -1, bool inv = true)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
List<CvBlob> blobList = GetBlobs(imageMat, thresh, inv);
List<CvBlob> blobList = GetBlobs(imageMat, thresh);
List<int> sampleList = new List<int>();
CvBlob srcBlob = new CvBlob();
srcBlob.Area = -1;
int blobCount = blobList.Count;
int selectIndex = blobCount / 2+blobCount/16;
int selectIndex = blobCount / 2 + blobCount / 16;
for (int i = 0; i < blobList.Count; i++)
{
if (srcBlob.Area == -1)
......@@ -179,7 +154,7 @@ namespace Acc.Img
sampleList.Add(blobList[i].Area);
}
}
if (sampleList.Count == blobList.Count-1 || i == blobList.Count-1)
if (sampleList.Count == blobList.Count - 1 || i == blobList.Count - 1)
{
int nums = 0;
for (int j = 0; j < sampleList.Count; j++)
......@@ -192,7 +167,7 @@ namespace Acc.Img
//int sss = (int)Math.Round(areaI);
return areaI;
}
}
//if (markX != -1 && markY != -1)
......@@ -228,30 +203,21 @@ namespace Acc.Img
/// <param name="thresh"></param>
/// <param name="inv"></param>
/// <returns></returns>
public static int CountItems(ref Image image, int itemFeature, int thresh = -1, bool inv = true)
public static int CountItems(ref Image image, int itemArea)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
Cv2.CvtColor(imageMat, imageMat, ColorConversionCodes.RGBA2BGR);
Mat grayMat = BitmapConverter.ToMat(new Bitmap(image));
Cv2.CvtColor(grayMat, grayMat, ColorConversionCodes.RGBA2RGB);
if (pngB)
{
Cv2.Threshold(imageMat, imageMat, 70, 255, ThresholdTypes.Binary);
}
else
{
Cv2.Threshold(imageMat, imageMat, 0, 255, ThresholdTypes.Binary);
}
List<CvBlob> blobList = GetBlobs(imageMat, thresh, inv);
int itemArea = (itemFeature + 23) * 3 /5;
if(itemArea <= 0)
{
itemArea = 3;
}
int totalCount = CountBlobs(blobList, itemArea, ref imageMat);
Cv2.CvtColor(grayMat, grayMat, ColorConversionCodes.RGB2GRAY);
CvBlobs blobs = AutoThreshBlobs(ref grayMat, itemArea);
int totalCount = findCircles(ref imageMat, grayMat, blobs, itemArea);
//int totalCount = CountBlobs(blobs, itemArea, ref imageMat);
image = BitmapConverter.ToBitmap(imageMat);
return totalCount;
}
private static void FindCours(Mat srcMat, Mat threshMat)
{
Mat[] contours = null;
......@@ -322,7 +288,7 @@ namespace Acc.Img
{
labels[i] = "1";
labelCount = labelCount + 1;
if (anotherBlob.MinX < minX || minX == 0)
if (anotherBlob.MinX < minX || minX == 0)
{
minX = anotherBlob.MinX;
}
......@@ -334,7 +300,7 @@ namespace Acc.Img
{
maxX = anotherBlob.MaxX;
}
if (anotherBlob.MaxY> maxY)
if (anotherBlob.MaxY > maxY)
{
maxY = anotherBlob.MaxY;
}
......@@ -372,26 +338,27 @@ namespace Acc.Img
} while (upY > minY);
}
}
private static double GetLabelStep(List<CvBlob> blobList, int avgArea, out CvBlob markBlob)
{
int leastNeighbourBlobCount = 15;
int selectIndex = blobList.Count / 2;
markBlob = blobList[selectIndex];
for(int i = selectIndex; i< blobList.Count; i ++)
for (int i = selectIndex; i < blobList.Count; i++)
{
CvBlob blob = blobList[i];
int blobArea = markBlob.Area;
if(BlobHasItem(avgArea,blob) == 1)
// if(blobArea > 0.9 * avgArea && blobArea < 1.1 * avgArea)
if (BlobHasItem(avgArea, blob) == 1)
// if(blobArea > 0.9 * avgArea && blobArea < 1.1 * avgArea)
{
//面积与给定的面积差不多,以其为中心,周围至少要有15个Blob
int neighbourCount = 0;
int blobRectSize = (blob.MaxX-blob.MinX) < (blob.MaxY - blob.MinY) ? (blob.MaxX - blob.MinX) : (blob.MaxY - blob.MinY);
int blobRectSize = (blob.MaxX - blob.MinX) < (blob.MaxY - blob.MinY) ? (blob.MaxX - blob.MinX) : (blob.MaxY - blob.MinY);
double radius = blobRectSize;
while (neighbourCount != blobList.Count)
{
neighbourCount = blobList.Count(b => {
neighbourCount = blobList.Count(b =>
{
if (BlobHasItem(avgArea, b) >= 1)
{
double distance = blob.Centroid.DistanceTo(b.Centroid);
......@@ -414,100 +381,100 @@ namespace Acc.Img
}
return Math.Sqrt(avgArea);
}
/// <summary>
/// 获取Blob个数
/// </summary>
/// <param name="blobList"></param>
/// <param name="avgArea"></param>
/// <param name="srcMat"></param>
/// <returns></returns>
private static int CountBlobs(List<CvBlob> blobList, int avgArea, ref Mat srcMat)
private static CvBlobs AutoThreshBlobs(ref Mat imageMat, int blobArea)
{
//List<CvBlob> filterBlobList = blobList.Where(b => b.Area > 0.3 * avgArea).ToList();
List<CvBlob> filterBlobList = blobList.Where(b => b.Area > 0).ToList();
if (blobList.Count == 0)
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++)//灰度值总数量
{
return 0;
total = total + hist.Get<double>(i);
}
//CvBlob markBlob = null;
//double labelStep = GetLabelStep(blobList, avgArea, out markBlob);
//string[] labels = new string[filterBlobList.Count];
//double markBlobX = markBlob.Centroid.X;
//double markBlobY = markBlob.Centroid.Y;
//LabelBlobsInCircle(ref labels, filterBlobList, markBlobX, markBlobY, labelStep);
//int totalCount = 0;
//for (int i = 0; i < labels.Length; i++)
//{
// if (labels[i] != null)
// {
// CvBlob blob = filterBlobList[i];
// Scalar color = Scalar.Red;
// int count = BlobHasItem(avgArea, blob);
// if (count > 0)
// {
// if (count == 1)
// {
// color = Scalar.Green;
// }
// else if (count == 2)
// {
// color = Scalar.Blue;
// //Cv2.PutText(srcMat, count + "", blob.Centroid, HersheyFonts.HersheySimplex, 0.5, color);
// }
// else if (count >= 3)
// {
// color = Scalar.Red;
// Point2d center = blob.Centroid;
// Cv2.PutText(srcMat, count + "", new OpenCvSharp.Point(center.X, center.Y), HersheyFonts.HersheySimplex, 0.5, color);
// }
// totalCount = totalCount + count;
// blob.Contour.Render(srcMat, color);
// }
// }
//}
int totalCount = 0;
foreach (CvBlob blob in filterBlobList)
double percent = 0;
int startIndex = -1;
int endIndex = -1;
for (int i = 0; i < 256; i++)//直方图
{
Scalar color = Scalar.Red;
if (blob.MaxX - blob.MinX > 450 && blob.MaxY - blob.MinY > 450) continue;
int count = BlobHasItem(avgArea, blob);
if (count > 0)
{
if (count == 1)
{
color = Scalar.Green;
}
else if (count == 2)
double len = hist.Get<double>(i);
if(len > 100)
{//灰度值的像素数小于100的忽略
percent = percent + len / total;
if (startIndex == -1)
{
color = Scalar.Blue;
//Cv2.PutText(srcMat, count + "", blob.Centroid, HersheyFonts.HersheySimplex, 0.5, color);
startIndex = i;
}
else if (count >= 3)
//近似的认为元器件的灰度值 数量占总数的百分比小于10%
if (percent > 0.1)
{
color = Scalar.Red;
Point2d center = blob.Centroid;
Cv2.PutText(srcMat, count + "", new OpenCvSharp.Point(center.X, center.Y), HersheyFonts.HersheySimplex, 0.5, color);
endIndex = i - 1;
break;
}
totalCount = totalCount + count;
blob.Contour.Render(srcMat, color);
}
}
string countText = "Count: " + totalCount;
int baseLine = 0;
OpenCvSharp.Size textSize = Cv2.GetTextSize(countText, HersheyFonts.HersheySimplex, 1, 1, out baseLine);
Cv2.PutText(srcMat, countText, new OpenCvSharp.Point(srcMat.Width / 2 - textSize.Width / 2, srcMat.Height / 2 - textSize.Height / 2), HersheyFonts.HersheySimplex, 1, Scalar.Blue);
//Cv2.Circle(srcMat, markBlob.Centroid, (int)labelStep, Scalar.Red, 2);
return totalCount;
int avgIndex = (startIndex + endIndex) / 2;
Mat threshMat = new Mat();
Cv2.Threshold(imageMat, threshMat, avgIndex, 255, ThresholdTypes.BinaryInv);
CvBlobs resultBlobs = new CvBlobs();
resultBlobs.Label(threshMat);
List<CvBlob> autoBlobList = resultBlobs.Values.Where(b => b.Area > blobArea).ToList();
int blobCount = resultBlobs.Count();
int threshIndex = avgIndex;
double theArea = blobArea * 0.8;
if (theArea < 1) theArea = 1;
while (true)
{
//阈值向下走,找满足条件Blob数量最多的
threshIndex = threshIndex - 1;
Cv2.Threshold(imageMat, threshMat, threshIndex, 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)
{
resultBlobs = blobs;
blobCount = blobList.Count;
}
else
{
break;
}
}
threshIndex = avgIndex;
while (true)
{
//阈值向上走,找满足条件Blob数量最多的
threshIndex = threshIndex + 1;
Cv2.Threshold(imageMat, threshMat, threshIndex, 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)
{
resultBlobs = blobs;
blobCount = blobList.Count;
}
else
{
break;
}
}
imageMat = threshMat;
Console.WriteLine(threshIndex + "==== Blob: " + blobCount + " Area:" + theArea);
return resultBlobs;
}
/// <summary>
/// 二值化图像
/// </summary>
......@@ -515,83 +482,24 @@ namespace Acc.Img
/// <param name="thresh"></param>
/// <param name="inv"></param>
/// <returns></returns>
private static Mat Threshhold(Mat imageMat, int thresh = -1, bool inv = false)
private static Mat Threshhold(Mat imageMat, int thresh = -1)
{
Mat dst = new Mat();
Cv2.CvtColor(imageMat, dst, ColorConversionCodes.RGB2GRAY);
//if (selectB)
//{
// //全局二值化
// Cv2.Threshold(dst, dst, 30, 255, ThresholdTypes.Binary);
//}
//else
//{
// //全局二值化
// Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Binary);
//}
//if (selectB)
//{
// if (thresh == -1)
// {
// //自动局部二值化
// Binarizer.Sauvola(dst, dst, 21, 0.2, 32);
// }
// else
// {
// //全局二值化
// if (pngB)
// {
// Cv2.Threshold(dst, dst, 70, 255, ThresholdTypes.Binary);
// }
// else
// {
// Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Binary);
// }
// }
// if (inv)
// {
// if (pngB)
// {
// Cv2.Threshold(dst, dst, 70, 150, ThresholdTypes.BinaryInv);
// }
// else
// {
// Cv2.Threshold(dst, dst, 0, 150, ThresholdTypes.BinaryInv);
// }
// }
//}
//else
//{
// if (pngB)
// {
// Cv2.Threshold(dst, dst, 70, 255, ThresholdTypes.Binary);
// Cv2.Threshold(dst, dst, 0, 150, ThresholdTypes.BinaryInv);
// }
// else
// {
// Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Binary);
// Cv2.Threshold(dst, dst, 0, 150, ThresholdTypes.BinaryInv);
// }
//}
if (thresh == -1)
{
//全局自动二值 化
Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);
//自动局部二值化
Binarizer.Sauvola(dst, dst, 21, 0.2, 32);
//Binarizer.Sauvola(dst, dst, 221, 0.02, 232);
//Cv2.AdaptiveThreshold(dst, dst, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 5, 0);
}
else
{
//全局二值化
Cv2.Threshold(dst, dst, 0, 255, ThresholdTypes.Otsu);
}
if (inv)
{
Cv2.Threshold(dst, dst, 0, thresh, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);
//二值化
Cv2.Threshold(dst, dst, thresh, 255, ThresholdTypes.BinaryInv);
}
return dst;
}
/// <summary>
......@@ -601,19 +509,16 @@ namespace Acc.Img
/// <param name="thresh"></param>
/// <param name="inv"></param>
/// <returns></returns>
private static List<CvBlob> GetBlobs(Mat imageMat, int thresh = -1, bool inv = false)
private static List<CvBlob> GetBlobs(Mat imageMat, int thresh = -1)
{
Cv2.CvtColor(imageMat, imageMat, ColorConversionCodes.RGBA2BGR);
//Cv2.CvtColor(imageMat, imageMat, ColorConversionCodes.RGB2GRAY);
Mat dst = Threshhold(imageMat, thresh, inv);
Mat k1 = Mat.Ones(new OpenCvSharp.Size(1, 1), MatType.CV_8UC1);
Cv2.MorphologyEx(dst, dst, MorphTypes.Open, k1);
Mat dst = Threshhold(imageMat, thresh);
CvBlobs blobs = new CvBlobs();
blobs.Label(dst);
List<CvBlob> blobList = blobs.Values.Where(b => b.Area > 0).ToList();
return blobList;
}
/// <summary>
/// 给定Blob包含几个元器件
......@@ -631,7 +536,7 @@ namespace Acc.Img
}
if (blobArea < minArea)
{
return 1;
return 0;
}
//if (blobArea >= 0.5 * averageArea && blobArea <= 1.5 * averageArea)
//{
......@@ -645,8 +550,8 @@ namespace Acc.Img
//{
// return 3;
//}
int count = (int)((blobArea+1.5 * averageArea) / (1.5 * averageArea));
if(count == 0)
int count = (int)((blobArea + 1.5 * averageArea) / (1.5 * averageArea));
if (count == 0)
{
count = 1;
}
......@@ -665,7 +570,7 @@ namespace Acc.Img
private static Bitmap[] ReadRaw(string imagePath)
{
byte[] buff = System.IO.File.ReadAllBytes(imagePath);
if (buff == null || buff.Length == 0)
{
......@@ -695,7 +600,7 @@ namespace Acc.Img
byte filterByte = buff[i * 2 + 1];
if (needRevertLayer)
{
currentByte = (byte)(currentByte * 3);
currentByte = (byte)(currentByte * 3);
filterByte = (byte)(filterByte * 3);
}
buffer_src[i] = currentByte;
......@@ -713,11 +618,11 @@ namespace Acc.Img
//翻转图层
if (needRevertLayer)
{
return new Bitmap[] { filter_bitmap, src_bitmap};
return new Bitmap[] { filter_bitmap, src_bitmap };
}
else
{
return new Bitmap[] { src_bitmap, filter_bitmap};
return new Bitmap[] { src_bitmap, filter_bitmap };
}
}
/// <summary>
......@@ -751,11 +656,9 @@ namespace Acc.Img
return bmp;
}
private static List<OpenCvSharp.Point> findContourPoints(CvBlob blob)
private static List<OpenCvSharp.Point> toContourPoints(CvContourChainCode contour)
{
CvContourChainCode contour = blob.Contour;
List<OpenCvSharp.Point> contourPoints = new List<OpenCvSharp.Point>();
contourPoints.Add(contour.StartingPoint);
int x = contour.StartingPoint.X;
int y = contour.StartingPoint.Y;
......@@ -768,7 +671,7 @@ namespace Acc.Img
return contourPoints;
}
private static CvBlob findMarkBlob(List<CvBlob> blobList, int markX = -1, int markY = -1, int thresh = -1, bool inv = true)
private static CvBlob findMarkBlob(List<CvBlob> blobList, int markX = -1, int markY = -1, int thresh = -1, bool inv = true)
{
int blobCount = blobList.Count;
int selectIndex = blobCount / 2;
......@@ -795,144 +698,354 @@ namespace Acc.Img
}
return null;
}
public static Image Mark(Image image, int markX = -1, int markY = -1, int thresh = -1, bool inv = true)
/// <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(ref double[,] matDistanceArr, CvBlobs blobs, CvBlob blob, Point2d reelCenter, double oneBlobWidth= -1, double oneBlobRadius = -1)
{
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
List<CvBlob> blobList = GetBlobs(imageMat, thresh, inv);
//TODO:查找标记Blob,这里可遍历blobList,对BlobHasItem结果为1的blob进行标记,查找最小的内接半径
CvBlob markBlob = findMarkBlob(blobList, markX, markY, thresh, inv);
if(markBlob != null)
{
OpenCvSharp.Point center = new OpenCvSharp.Point() ;
double r = 0;
findCircle(markBlob, out center, out r);
Cv2.Circle(imageMat, center, (int)r, Scalar.Red);
Console.WriteLine("" + r);
int markArea = markBlob.Area;
int totalCount = 0;
foreach(CvBlob blob in blobList)
{
int count = BlobHasItem(markArea, blob);
totalCount = totalCount + count;
if (count == 1)
{//单个Blob
blob.Contour.Render(imageMat, Scalar.Green);
}
else if (count >0 && count < 100)
SplitItem item = new SplitItem();
while (true)
{
bool hasFind = false;
for (int y = blob.MinY; y < blob.MaxY; y++)
{
for (int x = blob.MinX; x < blob.MaxX; x++)
{
//查找对大一点的Blob的内接圆数量,进行标记
Stopwatch sw = new Stopwatch();
sw.Start();
List<OpenCvSharp.Point> centers = markBlobInImage(blob, r);
foreach(OpenCvSharp.Point c in centers)
double distance = matDistanceArr[x, y];
if (distance > 0)
{
Cv2.Circle(imageMat, c, (int)r, Scalar.Red);
int label = blobs.GetLabel(x, y);
if (label != blob.Label)
{
//不是当前Blob的像素
matDistanceArr[x, y] = 0;
continue;
}
if (!item.isEnd)
{
double distanceToCircle = item.minDistanceToCircles(x, y, reelCenter);
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);
hasFind = true;
break;
}
}
}
}
sw.Stop();
Console.WriteLine("耗时:" + sw.ElapsedMilliseconds + " ms 数量:"+ centers.Count);
}
if (hasFind)
{
break;
}
}
if (!hasFind)
{
item.calOneItem(oneBlobRadius);
}
if (item.isEnd)
{
break;
}
}
return BitmapConverter.ToBitmap(imageMat);
return item;
}
/// <summary>
/// 查找Blob包含多少个指定半径的圆
/// 查找Blobs包含的元器件数量
/// </summary>
/// <param name="blob"></param>
/// <param name="radius">指定半径</param>
/// <returns>找到的圆的中心点列表,一个中心点为一个圆</returns>
private static List<OpenCvSharp.Point> markBlobInImage(CvBlob blob, double radius)
/// <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)
{
List<OpenCvSharp.Point> centers = new List<OpenCvSharp.Point>();
List<OpenCvSharp.Point> contourPoints = findContourPoints(blob);
CvContourPolygon polygon = blob.Contour.ConvertToPolygon();
//从左向右,从上到下遍历
int x = blob.MinX;
while(x <= blob.MaxX)
{
int y = blob.MinY;
while (y <= blob.MaxY)
{
//当前的遍历的点
OpenCvSharp.Point currentPoint = new OpenCvSharp.Point(x, y);
double distance = Cv2.PointPolygonTest(contourPoints, currentPoint, true);
//TODO: 内轮廓也需要判断
if(distance >= radius)
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");
for (int y = 0; y < threshMat.Rows; y++)
{
for (int x = 0; x < threshMat.Cols; x++)
{
distanceArr[x,y] = distanceMat.At<float>(y, x);
}
}
Dictionary<int, SplitItem> blobCircles = new Dictionary<int, SplitItem>();
Console.WriteLine("Start find reel center");
Point2d reelCenter = new Point2d(0,0) ;
//查找中心
foreach (CvBlob blob in blobs.Values)
{
int count = BlobHasItem(avgArea, blob);
if (count > 10 && reelCenter.X == 0)
{
Point2d center = blob.Centroid;
//中间的圆,查找圆心
if (center.DistanceTo(new Point2d(srcMat.Cols / 2, srcMat.Rows / 2)) < 200)
{
//当前点到轮廓的最小距离大于半径时,判断是否与其他圆相交,如果相交,忽略这个点
bool valid = true;
foreach (OpenCvSharp.Point c in centers)
{
double dis = currentPoint.DistanceTo(c);
if (dis < 2 * radius)
{
valid = false;
break;
}
}
if (valid)
reelCenter = center;
srcMat.Line(new OpenCvSharp.Point(center.X-10, center.Y), new OpenCvSharp.Point(center.X+10, center.Y), Scalar.Blue);
srcMat.Line(new OpenCvSharp.Point(center.X, center.Y-10), new OpenCvSharp.Point(center.X, center.Y+10), Scalar.Blue);
break;
}
}
}
Console.WriteLine("Start find reel Max Radius, max Width");
//最大
double maxRadius = 0;
double maxWidth = 0;
foreach (CvBlob blob in blobs.Values)
{
int count = BlobHasItem(avgArea, blob);
if (count == 1)
{
if (blob.Rect.Width > maxWidth)
{
maxWidth = blob.Rect.Width;
}
if (blob.Rect.Height > maxWidth)
{
maxWidth = blob.Rect.Height;
}
SplitItem item = findCircleInBlob(ref distanceArr, blobs, blob, reelCenter);
foreach (Circle c in item.circles)
{
if (c.radius > maxRadius || maxRadius == 0)
{
//找到一个,从当前点向下偏移一个半径的距离
y = (int)(y + radius);
Console.WriteLine("" + x + ", " + y + " dis=" + distance + " ");
centers.Add(currentPoint);
maxRadius = c.radius;
}
else
//srcMat.Circle(c.x, c.y, (int)c.radius, Scalar.Green);
}
}
}
Console.WriteLine("Start count");
int totalCount = 0;
foreach (CvBlob blob in blobs.Values)
{
int count = BlobHasItem(avgArea, blob);
if(count == 1)
{
//单个元器件
totalCount = totalCount +1;
srcMat.Circle((int)blob.Centroid.X, (int)blob.Centroid.Y, (int)maxRadius/2, Scalar.LightGreen);
}
else if (count > 1)
{
if (count > 20)
{
//中间的圆,去除
if(blob.Centroid.DistanceTo(new Point2d(srcMat.Cols / 2, srcMat.Rows / 2)) < 200)
{
//与其他圆相交,忽略,继续下一个点
y++;
continue;
}
}
else
//多个元器件,查找 所有圆
SplitItem item = findCircleInBlob(ref distanceArr, blobs, blob, reelCenter, maxWidth, maxRadius);
//对所有圆进行分组
List<List<Circle>> groupCircles = item.groupCircles(maxWidth, maxRadius, reelCenter);
Scalar color = Scalar.RandomColor();
blob.Contour.Render(srcMat, color);
foreach (List<Circle> groupCircle in groupCircles)
{
//当前点到轮廓的最小距离小于半径,继续下一个点
y++;
Circle c = groupCircle[0];
srcMat.Circle(c.x, c.y, (int)c.radius/2, Scalar.Yellow);
totalCount = totalCount + 1;
//Scalar color = Scalar.RandomColor();
//foreach (Circle c in groupCircle)
//{
// srcMat.Circle(c.x, c.y, (int)c.radius, color);
//}
}
}
x++;
}
return centers;
Cv2.PutText(srcMat, totalCount + "", new OpenCvSharp.Point(reelCenter.X-40, reelCenter.Y+30), HersheyFonts.HersheySimplex, 1.0, Scalar.LightGreen);
Console.WriteLine("===========" + totalCount);
return totalCount;
}
/// <summary>
/// 查找Blob最大的内接圆
/// </summary>
/// <param name="blob"></param>
/// <param name="centerPoint"></param>
/// <param name="r"></param>
private static void findCircle(CvBlob blob, out OpenCvSharp.Point centerPoint, out double r)
//TODO: 测试距离变换,用后删除
public static Image DistanceTransform(Image image)
{
//TODO: 需要进行修改,找多个,这里只找了一个
List<OpenCvSharp.Point> contourPoints = findContourPoints(blob);
OpenCvSharp.Point center = new OpenCvSharp.Point(-1,-1);
double ridus = 0;
for (int x = blob.MinX; x < blob.MaxX; x++)
Mat imageMat = BitmapConverter.ToMat(new Bitmap(image));
Mat gray = new Mat();
Cv2.CvtColor(imageMat, gray, ColorConversionCodes.RGB2GRAY);
////开运算
Mat k1 = Mat.Ones(new OpenCvSharp.Size(1, 1), MatType.CV_8UC1);
Cv2.MorphologyEx(gray, gray, MorphTypes.Open, k1, new OpenCvSharp.Point(0, 0), 3);
Mat distanceMat = new Mat();
Mat labels = new Mat() ;
Cv2.DistanceTransform(gray, distanceMat, DistanceTypes.L2, DistanceMaskSize.Mask3) ;
Cv2.Normalize(distanceMat, gray, 0, 255, NormTypes.MinMax);
gray.ConvertTo(gray, MatType.CV_8UC1);
Cv2.Threshold(gray, gray, 0, 255, ThresholdTypes.Otsu);
Mat colorImg = Mat.Zeros(gray.Size(), MatType.CV_8UC3);
Cv2.ConnectedComponents(gray, labels, PixelConnectivity.Connectivity8);
Dictionary<int, SplitItem> blobCircles = new Dictionary<int, SplitItem>();
for(int y=0;y<labels.Rows; y++)
{
for (int x = 0; x < labels.Cols; x++)
{
int label = labels.At<int>(y, x);
float distance = distanceMat.At<float>(y, x);
SplitItem item = new SplitItem();
if (blobCircles.ContainsKey(label))
{
item = blobCircles[label];
}
if (distance > item.currentMaxRadius)
{
item.currentMaxRadius = distance;
item.centerX = x;
item.centerY = y;
}
blobCircles[label] = item;
byte b = (byte)(label % 255);
byte g = 0;
if (b < 128 && b>0)
{
g = (byte)(255 - b);
}
Vec3b color = new Vec3b(b,g,0);
colorImg.Set<Vec3b>(y, x, color);
}
}
foreach (SplitItem circle in blobCircles.Values)
{
for(int y= blob.MinY; y<blob.MaxY; y++)
circle.calOneItem(-1);
}
while (true)
{
for (int y = 0; y < labels.Rows; y++)
{
OpenCvSharp.Point currentPoint = new OpenCvSharp.Point(x, y);
double minDistance = -1;
foreach (OpenCvSharp.Point p in contourPoints)
for (int x = 0; x < labels.Cols; x++)
{
double distance = currentPoint.DistanceTo(p);
if (distance < minDistance || minDistance == -1)
int label = labels.At<int>(y, x);
if (label == 0) continue;
SplitItem item = new SplitItem();
if (blobCircles.ContainsKey(label))
{
minDistance = distance;
item = blobCircles[label];
}
if (!item.isEnd)
{
double distance = distanceMat.At<float>(y, x);
//此Blob未结束
// bool validPoint = item.isValidPoint(x, y);
bool validPoint = false;
if (validPoint)
{
if (distance > item.currentMaxRadius)
{
item.currentMaxRadius = distance;
item.centerX = x;
item.centerY = y;
blobCircles[label] = item;
}
}
}
}
if(minDistance > ridus)
}
bool needContinue = false;
foreach (SplitItem circle in blobCircles.Values)
{
circle.calOneItem();
if (!circle.isEnd)
{
ridus = minDistance;
center = currentPoint;
needContinue = true;
}
}
if (!needContinue)
{
break;
}
}
int totalCount = 0;
foreach (SplitItem item in blobCircles.Values)
{
foreach(Circle circle in item.circles)
{
Cv2.Circle(colorImg, circle.x, circle.y, (int)circle.radius,Scalar.White);
totalCount++;
}
}
Console.WriteLine("Total: " + totalCount);
return BitmapConverter.ToBitmap(colorImg);
//dist.SaveImage("d:\\image\\dsitdist1.jpg");
//Cv2.Threshold(dist, dist, 79, 255, ThresholdTypes.Binary);
//Cv2.CvtColor(dist,dist,ColorConversionCodes.BGRA2BGR);
//dist.ConvertTo(dist, MatType.CV_8UC1);
//image = BitmapConverter.ToBitmap(dist);
//return image;
}
/// <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);
foreach(CvBlob blob in blobs.Values) {
if (blob.Rect.Contains(new OpenCvSharp.Point(markX, markY)))
{
return blob.Area;
}
}
centerPoint = center;
r = ridus;
return -1;
}
}
}
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AccImage
{
public class SplitItem
{
public bool isEnd = false;
public double avgRadius = 0;
public int centerX = 0;
public int centerY = 0;
public double currentMaxRadius = 0;
public List<Circle> circles = new List<Circle>();
/// <summary>
/// 一遍结束后调用
/// </summary>
public void calOneItem(double theFixRadius=-1)
{
//该半径是否有效
bool isValid = false;
if (avgRadius == 0)
{
isValid = true;
}
else
{
//与平均半径差值在10%以内,认为OK
double diff = avgRadius - currentMaxRadius;
if (diff/avgRadius <= 0.2)
{
isValid = true;
}
}
if (isValid)
{
Circle circle = new Circle();
if(theFixRadius != -1 && currentMaxRadius > theFixRadius)
{
currentMaxRadius = theFixRadius;
}
circle.radius = currentMaxRadius;
circle.x = centerX;
circle.y = centerY;
circles.Add(circle);
currentMaxRadius = 0;
centerX = 0;
centerY = 0;
//平均半径
double total = 0;
foreach (Circle c in circles)
{
total = total + c.radius;
}
avgRadius = total / circles.Count;
}
else
{
isEnd = true;
}
}
public List<List<Circle>> groupCircles(double w, double h, Point2d center)
{
foreach(Circle c in circles)
{
c.calDistanceToCenter(center);
}
List<List<Circle>> allGroupCircle = new List<List<Circle>>();
List<int> allreadyGroup = new List<int>();
while (true)
{
List<Circle> groupCircle = new List<Circle>();
Circle labelCircle = null;
for (int i=0;i<circles.Count; i++)
{
if (!allreadyGroup.Contains(i))
{
Circle c = circles[i];
if (labelCircle == null)
{
labelCircle = c;
allreadyGroup.Add(i);
groupCircle.Add(c);
}
else
{
//到圆心的距离差小于H
if (Math.Abs(c.distanceToCenter - labelCircle.distanceToCenter) < h)
{
//两圆心之间的距离+直径小于W认为是同一个元器件
if (c.distanceToCircle(labelCircle) < w)
{
groupCircle.Add(c);
allreadyGroup.Add(i);
}
}
}
}
}
allGroupCircle.Add(groupCircle);
if (allreadyGroup.Count == circles.Count)
{
break;
}
}
return allGroupCircle;
}
/// <summary>
/// 该点是否与其他圆内,或与圆的中心距离在半径内
/// </summary>
/// <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)
{
Point2d point = new Point2d(px, py);
double minDistanceToCircle = -1;
foreach (Circle c in circles)
{
Point2d circleCenter = new Point2d(c.x, c.y);
double distanceToCircle = point.DistanceTo(circleCenter);
if (distanceToCircle <= 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;
}
}
return minDistanceToCircle;
}
};
public class Circle
{
public int x;
public int y;
public double radius;
public double distanceToCenter = 0;
public double distanceToCircle(Circle c)
{
double distance = new Point2d(x, y).DistanceTo(new Point2d(c.x, c.y));
return distance + radius + c.radius;
}
public void calDistanceToCenter(Point2d center)
{
if(center.X > 0 && distanceToCenter <= 0)
{
distanceToCenter = center.DistanceTo(new Point2d(x, y));
}
}
}
}
......@@ -69,7 +69,16 @@ namespace Acc.Demo
{
theshValue = int.Parse(textBoxThesh.Text);
}
Image result = ImageUtil.Threshhold(orginalImage, theshValue, false);
Image result;
result = ImageUtil.Threshhold(orginalImage, theshValue);
//if (theshValue == -1)
//{
// result = ImageUtil.Threshhold(imageBox.Image, theshValue);
//}
//else
//{
// result = ImageUtil.DistanceTransform(imageBox.Image);
//}
imageBox.Image = result;
}
......@@ -105,26 +114,12 @@ namespace Acc.Demo
private void Count()
{
ClearStatusBar();
int theshValue = -1;
if (!checkBoxAutoThresh.Checked)
{
theshValue = int.Parse(textBoxThesh.Text);
}
Task.Factory.StartNew(delegate {
//Image image = ImageUtil.FindCircle(orginalImage, theshValue, inv);
//imageBox.Image = image;
Stopwatch sw = new Stopwatch();
sw.Start();
int itemFeature = int.Parse(textBoxFeature.Text);
Image image = orginalImage;
if (!ImageUtil.selectB)
{
itemFeature = ImageUtil.GetItemFeatureAuto(image, markX, markY, theshValue);
}
ImageUtil.selectB = false;
int count = ImageUtil.CountItems(ref image, itemFeature, theshValue);
int count = ImageUtil.CountItems(ref image, itemFeature);
sw.Stop();
labelTime.Text = "耗时:" + sw.ElapsedMilliseconds + " ms";
labelCount.Text = "数量:" + count;
......@@ -152,14 +147,23 @@ namespace Acc.Demo
private void 元件特征ToolStripMenuItem_Click(object sender, EventArgs e)
{
ClearStatusBar();
int theshValue = -1;
if (!checkBoxAutoThresh.Checked)
{
theshValue = int.Parse(textBoxThesh.Text);
}
ImageUtil.selectB = true;
Image image = ImageUtil.Mark(orginalImage, markX, markY, theshValue);
Image image = orginalImage;
int feature = ImageUtil.GetFeature(ref image, markX, markY);
textBoxFeature.Text = feature.ToString();
imageBox.Image = image;
//int theshValue = -1;
//if (!checkBoxAutoThresh.Checked)
//{
// theshValue = int.Parse(textBoxThesh.Text);
//}
//Stopwatch sw = new Stopwatch();
//sw.Start();
//Image image = ImageUtil.Mark(orginalImage, markX, markY, theshValue);
//imageBox.Image = image;
//sw.Stop();
//Console.WriteLine("总耗时:" + sw.ElapsedMilliseconds + " ms");
}
private void 保存当前图片ToolStripMenuItem_Click(object sender, EventArgs e)
......
......@@ -83,7 +83,7 @@
//
this.元件特征ToolStripMenuItem.Name = "元件特征ToolStripMenuItem";
this.元件特征ToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.元件特征ToolStripMenuItem.Text = "标记记数";
this.元件特征ToolStripMenuItem.Text = "元件特征";
this.元件特征ToolStripMenuItem.Click += new System.EventHandler(this.元件特征ToolStripMenuItem_Click);
//
// 计数ToolStripMenuItem
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!