using OpenCvSharp;
using OpenCvSharp.XImgProc;
using PaintDotNet.Adjust.BaseImage;
using PaintDotNet.Base;
using PaintDotNet.Base.Enum;
using PaintDotNet.Base.Functionodel;
using PaintDotNet.Base.CommTool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace PaintDotNet.Adjust
{
///
/// 形态学处理
///
public class MorphologyIntent
{
#region 腐蚀
///
/// 腐蚀
///
///
///
///
public static Mat ImageErosion(Mat mat, List lists)
{
int count = 1;
Structure structure = Structure.horizon;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
count = int.Parse(args.Value.ToString());
break;
case "Structures":
structure = (Structure)args.Value;
break;
default:
break;
}
}
Mat element = null;
switch (structure)
{
case Structure.horizon:
InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } });
element = kernel1.GetMat();
break;
case Structure.angle45:
InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } });
element = kernel2.GetMat();
break;
case Structure.vertical:
InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } });
element = kernel3.GetMat();
break;
case Structure.angle135:
InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } });
element = kernel4.GetMat();
break;
case Structure.cross:
element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3));
break;
case Structure.square:
element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
break;
case Structure.octagon:
InputArray kernel7 = InputArray.Create(new int[7, 7] {
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 1, 1, 1, 0, 0 }
});
element = kernel7.GetMat();
break;
}
if (structure == Structure.conventional)
{
Cv2.Erode(mat, mat, null, null, count);
}
else
{
element.ConvertTo(element, MatType.CV_8UC1);
Cv2.Erode(mat, mat, element, null, count, BorderTypes.Constant);
if (element != null)
{
element.Dispose();
}
}
return mat;
}
#endregion
#region 膨胀
///
/// 膨胀
///
///
///
///
public static Mat ImageDilation(Mat mat, List lists)
{
int count = 1;
Structure structure = Structure.horizon;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
count = int.Parse(args.Value.ToString());
break;
case "Structures":
structure = (Structure)args.Value;
break;
default:
break;
}
}
Mat element = null;
switch (structure)
{
case Structure.horizon:
InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } });
element = kernel1.GetMat();
break;
case Structure.angle45:
InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } });
element = kernel2.GetMat();
break;
case Structure.vertical:
InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } });
element = kernel3.GetMat();
break;
case Structure.angle135:
InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } });
element = kernel4.GetMat();
break;
case Structure.cross:
element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3));
break;
case Structure.square:
element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
break;
case Structure.octagon:
InputArray kernel7 = InputArray.Create(new int[7, 7] {
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 1, 1, 1, 0, 0 }
});
element = kernel7.GetMat();
break;
}
if (structure == Structure.conventional)
{
Cv2.Dilate(mat, mat, null, null, count);
}
else
{
element.ConvertTo(element, MatType.CV_8UC1);
Cv2.Dilate(mat, mat, element, null, count, BorderTypes.Constant);
}
return mat;
}
#endregion
#region 开运算
///
/// 开运算
///
///
///
///
public static Mat ImageOpen(Mat mat, List lists)
{
int count = 1;
Structure structure = Structure.horizon;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
count = int.Parse(args.Value.ToString());
break;
case "Structures":
structure = (Structure)args.Value;
break;
default:
break;
}
}
Mat element = null;
switch (structure)
{
case Structure.horizon:
InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } });
element = kernel1.GetMat();
break;
case Structure.angle45:
InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } });
element = kernel2.GetMat();
break;
case Structure.vertical:
InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } });
element = kernel3.GetMat();
break;
case Structure.angle135:
InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } });
element = kernel4.GetMat();
break;
case Structure.cross:
element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3));
break;
case Structure.square:
element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
break;
case Structure.octagon:
InputArray kernel7 = InputArray.Create(new int[7, 7] {
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 1, 1, 1, 0, 0 }
});
element = kernel7.GetMat();
break;
}
if (structure == Structure.conventional)
{
Cv2.MorphologyEx(mat, mat, MorphTypes.Open, null, null, count, BorderTypes.Constant);
}
else
{
element.ConvertTo(element, MatType.CV_8UC1);
Cv2.MorphologyEx(mat, mat, MorphTypes.Open, element, null, count, BorderTypes.Constant);
if (element != null)
{
element.Dispose();
}
}
return mat;
}
#endregion
#region 闭运算
///
/// 闭运算
///
///
///
///
public static Mat ImageClose(Mat mat, List lists)
{
int count = 1;
Structure structure = Structure.horizon;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
count = int.Parse(args.Value.ToString());
break;
case "Structures":
structure = (Structure)args.Value;
break;
default:
break;
}
}
Mat element = null;
switch (structure)
{
case Structure.horizon:
InputArray kernel1 = InputArray.Create(new int[1, 3] { { 1, 1, 1 } });
element = kernel1.GetMat();
break;
case Structure.angle45:
InputArray kernel2 = InputArray.Create(new int[3, 3] { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 } });
element = kernel2.GetMat();
break;
case Structure.vertical:
InputArray kernel3 = InputArray.Create(new int[3, 1] { { 1 }, { 1 }, { 1 } });
element = kernel3.GetMat();
break;
case Structure.angle135:
InputArray kernel4 = InputArray.Create(new int[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } });
element = kernel4.GetMat();
break;
case Structure.cross:
element = Cv2.GetStructuringElement(MorphShapes.Cross, new Size(3, 3));
break;
case Structure.square:
element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
break;
case Structure.octagon:
InputArray kernel7 = InputArray.Create(new int[7, 7] {
{ 0, 0, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 1, 1, 1, 0, 0 }
});
element = kernel7.GetMat();
break;
}
if (structure == Structure.conventional)
{
Cv2.MorphologyEx(mat, mat, MorphTypes.Close, null, null, count, BorderTypes.Constant);
}
else
{
element.ConvertTo(element, MatType.CV_8UC1);
Cv2.MorphologyEx(mat, mat, MorphTypes.Close, element, null, count, BorderTypes.Constant);
if (element != null)
{
element.Dispose();
}
}
return mat;
}
#endregion
#region 粗化/细化
private static int GetArgCount(List lists)
{
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
if (args.Key == "Count")
{
return int.Parse(args.Value.ToString());
}
}
return 0;
}
///
/// 粗化,需要二值化的图像
///
///
///
///
public static Mat Thickening(Mat mat, List lists, int color)
{
System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color);
Mat dstC1 = null;
Mat dstC4 = null;
Mat scrC1 = null;
try
{
scrC1 = new Mat(mat.Size(), MatType.CV_8UC1);
for (int o = 0; o < scrC1.Height; o++)
for (int p = 0; p < scrC1.Width; p++)
{
var v = mat.At(o, p).Item3;
if (v == 0)
{
scrC1.Set(o, p, 255);
}
else
{
scrC1.Set(o, p, 0);
}
}
//扩充图像边界,方便处理,最后在统一截掉
var times = GetArgCount(lists);//细化执行次数
Thinning(scrC1, out dstC1, times);
dstC4 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4);
for (int o = 0; o < dstC1.Height; o++)
{
for (int p = 0; p < dstC1.Width; p++)
{
byte v = dstC1.At(o, p);
if (v > 0)
{
dstC4.Set(o, p, new Vec4b(0, 0, 0, 0));
}
else
{
dstC4.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255));
}
}
}
dstC4.CopyTo(mat);
return mat;
}
catch (Exception)
{
return mat;
}
finally
{
if (dstC1 != null && !dstC1.IsDisposed) dstC1.Dispose();
if (scrC1 != null && !scrC1.IsDisposed) scrC1.Dispose();
if (dstC4 != null && !dstC4.IsDisposed) dstC4.Dispose();
GC.Collect();
}
}
///
/// 细化,需要黑白二值图
///
///
///
///
public static Mat Thinning(Mat mat, List lists, int color)
{
System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color);
Mat dstC4 = null;
Mat dstC1 = null;
Mat scrC1 = null;
try
{
scrC1 = new Mat(mat.Size(), MatType.CV_8UC1);
for (int o = 0; o < scrC1.Height; o++)
for (int p = 0; p < scrC1.Width; p++)
{
var v = mat.At(o, p).Item3;
if (v > 0)
{
scrC1.Set(o, p, 255);
}
else
{
scrC1.Set(o, p, 0);
}
}
var times = GetArgCount(lists);//细化执行次数
Thinning(scrC1, out dstC1, times);
dstC4 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4);
for (int o = 0; o < mat.Height; o++)
{
for (int p = 0; p < mat.Width; p++)
{
byte v = dstC1.At(o, p);
if (v > 0)
{
dstC4.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255));
}
else
{
dstC4.Set(o, p, new Vec4b(0, 0, 0, 0));
}
}
}
dstC4.CopyTo(mat);
return mat;
}
catch (Exception)
{
return mat;
}
finally
{
if (dstC4 != null && !dstC4.IsDisposed) dstC4.Dispose();
if (dstC1 != null && !dstC1.IsDisposed) dstC1.Dispose();
if (scrC1 != null && !scrC1.IsDisposed) scrC1.Dispose();
}
}
///
///
///
/// Source 8-bit single-channel image, containing binary blobs, with blobs having
/// Destination image of the same size and the same type as src. The function can
private static unsafe int Thinning(Mat src, out Mat dst, int times = 0)
{
int i, j, n;
int width, height;
int count = 0;
int edge = 2;
//之所以减1,是方便处理8邻域,防止越界
int p2, p3, p4, p5, p6, p7, p8, p9;
byte* img;
bool ifEnd;
var temp = new Mat(new OpenCvSharp.Size(src.Width + 2 * edge, src.Height + 2 * edge), MatType.CV_8U);
Cv2.CopyMakeBorder(src, temp, edge, edge, edge, edge, BorderTypes.Constant, Scalar.All(255));
dst = temp;
int step = (int)dst.Step();
int[] dir = new int[4] { -step, step, 1, -1 };
width = dst.Cols - 2;
height = dst.Rows - 2;
using (Mat tmpimg = new Mat())
{
do
{
count++;
//分四个子迭代过程,分别对应北,南,东,西四个边界点的情况
ifEnd = false;
for (n = 0; n < 4; n++)
{
dst.CopyTo(tmpimg);
img = tmpimg.DataPointer;
for (i = 1; i < height; i++)
{
img += step;
for (j = 1; j < width; j++)
{
byte* p = img + j;
//如果p点是背景点或者且为方向边界点,依次为北南东西,继续循环
if (p[0] == 0 || p[dir[n]] > 0) continue;
p2 = p[-step] > 0 ? 1 : 0;
p3 = p[-step + 1] > 0 ? 1 : 0;
p4 = p[1] > 0 ? 1 : 0;
p5 = p[step + 1] > 0 ? 1 : 0;
p6 = p[step] > 0 ? 1 : 0;
p7 = p[step - 1] > 0 ? 1 : 0;
p8 = p[-1] > 0 ? 1 : 0;
p9 = p[-step - 1] > 0 ? 1 : 0;
int adjsum;
adjsum = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
//判断是否是邻接点或孤立点,0,1分别对于那个孤立点和端点
if (adjsum > 1)
{
//8 simple判定
int is8simple = 1;
if ((p2 == 0 && p6 == 0) && ((p9 == 1 || p8 == 1 || p7 == 1) && (p3 == 1 || p4 == 1 || p5 == 1)))
is8simple = 0;
else if ((p4 == 0 && p8 == 0) && ((p9 == 1 || p2 == 1 || p3 == 1) && (p5 == 1 || p6 == 1 || p7 == 1)))
is8simple = 0;
else if ((p8 == 0 && p2 == 0) && (p9 == 1 && (p3 == 1 || p4 == 1 || p5 == 1 || p6 == 1 || p7 == 1)))
is8simple = 0;
else if ((p4 == 0 && p2 == 0) && (p3 == 1 && (p5 == 1 || p6 == 1 || p7 == 1 || p8 == 1 || p9 == 1)))
is8simple = 0;
else if ((p8 == 0 && p6 == 0) && (p7 == 1 && (p3 == 9 || p2 == 1 || p3 == 1 || p4 == 1 || p5 == 1)))
is8simple = 0;
else if ((p4 == 0 && p6 == 0) && (p5 == 1 && (p7 == 1 || p8 == 1 || p9 == 1 || p2 == 1 || p3 == 1)))
is8simple = 0;
if (is8simple == 1)
{
dst.Set(i, j, 0); //满足删除条件,设置当前像素为0
ifEnd = true;
}
}
}
}
}
} while (ifEnd && (times == 0 || count < times));//已经没有可以细化的像素了,则退出迭代
}
dst = new Mat(dst, new Rect(edge, edge, src.Width, src.Height));
return count;
}
///
/// OpenCV's Thinning
///
private static Mat ThinningCv(Mat src)
{
Mat dst = new Mat(src.Size(), src.Type());
Mat[] arr = src.Split();
CvXImgProc.Thinning(arr[0], dst, ThinningTypes.ZHANGSUEN);
return dst;
}
#endregion
#region 划痕处理 & 污迹处理
///
/// 污迹处理
///
///
///
/// 除了需要修复的部分之外其他部分的像素值全部为0
///
public static Mat SmudgeTreatment(Mat mat, List lists, Mat mask)
{
//获取通道数量
int channels = mat.Channels();
//如果是四通道转成三通道
if (channels == 4)
OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGRA2BGR);
//处理因子
double inpaintRadius = 5.0;
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "InpaintRadius":
inpaintRadius = double.Parse(args.Value.ToString());
break;
}
}
Mat matCopy = mat.Clone();
//图像修复
Cv2.Inpaint(mat, mask, matCopy, inpaintRadius, InpaintMethod.Telea);
//如果原图是四通道,将处理完之后mat转为四通道
if (channels == 4)
OpenCvSharp.Cv2.CvtColor(matCopy, matCopy, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
return matCopy;
}
///
/// 划痕处理
///
///
///
/// 除了需要修复的部分之外其他部分的像素值全部为0
///
public static Mat ScratchTreatment(Mat mat, List lists, Mat mask)
{
//获取通道数量
int channels = mat.Channels();
//如果是四通道转成三通道
if (channels == 4)
OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGRA2BGR);
Mat matCopy = mat.Clone();
////痕宽
//double inpaintRadius = 5.0;
////读取参数信息
//for (int i = 0; i < lists.Count; i++)
//{
// Args args = lists[i];
// switch (args.Key)
// {
// case "InpaintRadius":
// inpaintRadius = double.Parse(args.Value.ToString());
// break;
// }
//}
//Cv2.ImShow("mat", mat);
//图像修复
Cv2.Inpaint(mat, mask, matCopy/*, inpaintRadius*/, 5.0, InpaintMethod.Telea);
//Cv2.ImShow("matCopy", matCopy);
//如果原图是四通道,将处理完之后mat转为四通道
if (channels == 4)
OpenCvSharp.Cv2.CvtColor(matCopy, matCopy, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
return matCopy;
}
#endregion
#region 分水岭分割
/** the following constants are used to set bits corresponding to pixel types */
static byte MAXIMUM = (byte)1; // marks local maxima (irrespective of noise tolerance)
static byte LISTED = (byte)2; // marks points currently in the list
static byte PROCESSED = (byte)4; // marks points processed previously
static byte MAX_AREA = (byte)8; // marks areas near a maximum, within the tolerance
//static byte EQUAL = (byte)16; // marks contigous maximum points of equal level
//static byte MAX_POINT = (byte)32; // marks a single point standing for a maximum
static byte ELIMINATED = (byte)64; // marks maxima that have been eliminated before watershed
///
/// 分水岭分割
///
///
///
///
public static Mat WatershedSegment(Mat mat, PhaseModel phase, List lists)
{
//中间变量
//Mat temp = new Mat();
int paramCount = 0;
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
paramCount = int.Parse(args.Value.ToString());
break;
}
}
//灰度图
Mat Inmat = phase.mat.CvtColor(ColorConversionCodes.BGR2GRAY);
int width = Inmat.Width;
int height = Inmat.Height;
Mat distance_temp = new Mat();
//Cv2.ImShow("Inmat", Inmat);
Cv2.DistanceTransform(Inmat, distance_temp, DistanceTypes.L2, DistanceMaskSize.Precise);
//Mat dst_temp = new Mat();
//Cv2.Normalize(src_temp, dst_temp, 0, 255, NormTypes.MinMax);
//Mat dst = new Mat();
//dst_temp.ConvertTo(dst, MatType.CV_8UC1);
//Cv2.ImShow("DistanceTransform", dst);
//getSortedMaxPoints............
BitMap2d ip = new BitMap2d(distance_temp);
MaximunFinder maximunFinder = new MaximunFinder(ip);
List maxPoints = maximunFinder.FindMaxima();
//AnalyzeAndMarkMaxima.............
BitMap2d types = new BitMap2d(width, height, 0);
float globalMin = float.MaxValue;
float globalMax = float.MinValue;
foreach (Int16DoubleWithValue item in maxPoints)
{
if (item.V < globalMin) globalMin = item.V;
if (item.V > globalMax) globalMax = item.V;
types.SetPixel(item.O, MAXIMUM);
}
int nMax = maxPoints.Count();
int[] pList = new int[width * height]; //here we enter points starting from a maximum
int[] dirOffset = new int[] { -width, -width + 1, +1, +width + 1, +width, +width - 1, -1, -width - 1 };
float maxSortingError = (float)(1.1 * (Math.Sqrt(2) / 2));//sorted sequence may be inaccurate by this value
float tolerance = (float)0.5;// 05;
for (int iMax = nMax - 1; iMax >= 0; iMax--)
{ //process all maxima now, starting from the highest
int offset0 = maxPoints[iMax].O;
if (((byte)types.GetPixel(offset0) & PROCESSED) != 0) //this maximum has been reached from another one, skip it
continue;
float v0 = ip.GetPixel(offset0);
Boolean sortingError = false;
do
{
pList[0] = offset0;
types.SetPixel(offset0, (byte)((byte)types.GetPixel(offset0) | LISTED)); //mark first point as equal height (to itself) and listed
int listLen = 1; //number of elements in the list
int listI = 0; //index of current element in the list
sortingError = false;
Boolean maxPossible = true; //it may be a true maximum
do
{ //while neigbor list is not fully processed (to listLen)
int offset = pList[listI];
int x = offset % width;
int y = offset / width;
Boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); //not necessary, but faster than isWithin
for (int d = 0; d < 8; d++)
{ //analyze all neighbors (in 8 directions) at the same level
int offset2 = offset + dirOffset[d];
if ((isInner || isWithin(x, y, d, width, height)) && ((byte)types.GetPixel(offset2) & LISTED) == 0)
{
float v2 = ip.GetPixel(offset2);
if (ip.GetPixel(offset2) <= 0)
{
continue; //ignore the background (non-particles)
}
if (((byte)types.GetPixel(offset2) & PROCESSED) != 0)
{
maxPossible = false; //we have reached a point processed previously, thus it is no maximum now
break;
}
if (v2 > v0 + maxSortingError)
{
maxPossible = false; //we have reached a higher point, thus it is no maximum
break;
}
else if (v2 >= v0 - tolerance)
{
if (v2 > v0)
{
sortingError = true;
offset0 = offset2;
v0 = v2;
}
pList[listLen] = offset2;
listLen++; //we have found a new point within the tolerance
types.SetPixel(offset2, (byte)((byte)types.GetPixel(offset2) | LISTED));
}
} // if isWithin & not LISTED
} // for directions d
listI++;
} while (listI < listLen);
if (sortingError)
{ //if x0,y0 was not the true maximum but we have reached a higher one
for (listI = 0; listI < listLen; listI++)
types.SetPixel(pList[listI], (byte)0); //reset all points encountered, then retry
}
else
{
//...............................................................................//
int resetMask = ~LISTED;
for (listI = 0; listI < listLen; listI++)
{
int offset = pList[listI];
types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) & resetMask)); //reset attributes no longer needed
types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | PROCESSED)); //mark as processed
if (maxPossible)
{
types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | MAX_AREA)); //reset attributes no longer needed
}
} // for listI
}
}
while (sortingError);
}// for all maxima iMax
//if (nMax == 0) //no initial maxima at all? then consider all as 'within tolerance'
// Arrays.fill(types, (byte)(PROCESSED | MAX_AREA));
//makeUEPs:::
double threshold = 0.5;
//make8bit............
double offset01 = globalMin - (globalMax - globalMin) * (1.0 / 253 / 2 - 1e-6); //everything above minValue should become >(byte)0
double factor = 253 / (globalMax - globalMin);
BitMap2d pixels = new BitMap2d(width, height, 0);
int dataLen = height * width;
for (int offset = 0; offset < dataLen; offset++)
{
float rawValue = ip.GetPixel(offset);
if (rawValue < threshold) { }
else if (((byte)types.GetPixel(offset) & MAX_AREA) != 0)
pixels.SetPixel(offset, (byte)255); //prepare watershed by setting "true" maxima+surroundings to 255
else
{
long v = (long)(1 + Math.Round((rawValue - offset01) * factor));
if (v < 1) pixels.SetPixel(offset, (byte)1);
else if (v <= 254) pixels.SetPixel(offset, (byte)(v & 255));
else pixels.SetPixel(offset, (byte)254);
}
}
//cleanupMaxima............
for (int iMax = nMax - 1; iMax >= 0; iMax--)
{
int offset0 = maxPoints[iMax].O; //type cast gets lower 32 bits where pixel offset is encoded
if (((int)types.GetPixel(offset0) & (MAX_AREA | ELIMINATED)) != 0) continue;
int level = (byte)pixels.GetPixel(offset0) & 255;
int loLevel = level + 1;
pList[0] = offset0; //we start the list at the current maximum
types.SetPixel(offset0, (byte)((byte)types.GetPixel(offset0) | LISTED)); //mark first point as listed
int listLen = 1; //number of elements in the list
int lastLen = 1;
int listI = 0; //index of current element in the list
Boolean saddleFound = false;
while (!saddleFound && loLevel > 0)
{
loLevel--;
lastLen = listLen; //remember end of list for previous level
listI = 0; //in each level, start analyzing the neighbors of all pixels
do
{ //for all pixels listed so far
int offset = pList[listI];
int x = offset % width;
int y = offset / width;
Boolean isInner = (y != 0 && y != height - 1) && (x != 0 && x != width - 1); //not necessary, but faster than isWithin
for (int d = 0; d < 8; d++)
{ //analyze all neighbors (in 8 directions) at the same level
int offset2 = offset + dirOffset[d];
if ((isInner || isWithin(x, y, d, width, height)) && ((byte)types.GetPixel(offset2) & LISTED) == 0)
{
if (((byte)types.GetPixel(offset2) & MAX_AREA) != 0 || ((((byte)types.GetPixel(offset2) & ELIMINATED) != 0) && ((byte)pixels.GetPixel(offset2) & 255) >= loLevel))
{
saddleFound = true; //we have reached a point touching a "true" maximum...
break; //...or a level not lower, but touching a "true" maximum
}
else if (((byte)pixels.GetPixel(offset2) & 255) >= loLevel && ((byte)types.GetPixel(offset2) & ELIMINATED) == 0)
{
pList[listLen] = offset2;
listLen++; //we have found a new point to be processed
types.SetPixel(offset2, (byte)((byte)types.GetPixel(offset2) | LISTED));
}
} // if isWithin & not LISTED
} // for directions d
if (saddleFound) break; //no reason to search any further
listI++;
} while (listI < listLen);
} // while !levelFound && loLevel>=0
for (listI = 0; listI < listLen; listI++) //reset attribute since we may come to this place again
types.SetPixel(pList[listI], (byte)((byte)types.GetPixel(pList[listI]) & ~LISTED));
for (listI = 0; listI < lastLen; listI++)
{ //for all points higher than the level of the saddle point
int offset = pList[listI];
pixels.SetPixel(offset, (byte)loLevel); //set pixel value to the level of the saddle point
types.SetPixel(offset, (byte)((byte)types.GetPixel(offset) | ELIMINATED)); //mark as processed: there can't be a local maximum in this area
}
} // for all maxima iMax
//watershedSegment
int[] histogram = new int[256];
for (int v1 = 0; v1 < 255; v1++)
{
histogram[v1] = 0;
}
for (int offset = 0; offset < dataLen; offset++)
{
int v1 = (byte)pixels.GetPixel(offset) & 255;
if (v1 > 0 && v1 < 255)
{
histogram[v1]++;
}
}
int arraySize = width * height - histogram[0] - histogram[255];
int[] coordinates = new int[arraySize]; //from pixel coordinates, low bits x, high bits y
int highestValue = 0;
int maxBinSize = 0;
int offset02 = 0;
int[] levelStart = new int[256];
for (int v1 = 1; v1 < 255; v1++)
{
levelStart[v1] = offset02;
offset02 += histogram[v1];
if (histogram[v1] > 0) highestValue = v1;
if (histogram[v1] > maxBinSize) maxBinSize = histogram[v1];
}
int[] levelOffset = new int[highestValue + 1];
for (int y = 0, i = 0; y < height; y++)
{
for (int x = 0; x < width; x++, i++)
{
int v1 = (byte)pixels.GetPixel(i) & 255;
if (v1 > 0 && v1 < 255)
{
offset02 = levelStart[v1] + levelOffset[v1];
coordinates[offset02] = x + y * width;
levelOffset[v1]++;
}
} //for x
} //for y
// Create an array of the points (pixel offsets) that we set to 255 in one pass.
// If we remember this list we need not create a snapshot of the ImageProcessor.
int[] setPointList = new int[Math.Min(maxBinSize, (width * height + 2) / 3)];
// now do the segmentation, starting at the highest level and working down.
// At each level, dilate the particle (set pixels to 255), constrained to pixels
// whose values are at that level and also constrained (by the fateTable)
// to prevent features from merging.
int[] table = makeFateTable();
//IJ.showStatus("Segmenting (Esc to cancel)");
/*final */
int[] directionSequence = new int[] { 7, 3, 1, 5, 0, 4, 2, 6 }; // diagonal directions first
for (int level = highestValue; level >= 1; level--)
{
int remaining = histogram[level]; //number of points in the level that have not been processed
int idle = 0;
while (remaining > 0 && idle < 8)
{
int sumN = 0;
int dIndex = 0;
do
{ // expand each level in 8 directions
int n = processLevel(directionSequence[dIndex % 8], pixels, table,
levelStart[level], remaining, coordinates, setPointList, width, height);
//IJ.log("level="+level+" direction="+directionSequence[dIndex%8]+" remain="+remaining+"-"+n);
remaining -= n; // number of points processed
sumN += n;
if (n > 0) idle = 0; // nothing processed in this direction?
dIndex++;
} while (remaining > 0 && idle++ < 8);
}
if (remaining > 0 && level > 1)
{ // any pixels that we have not reached?
int nextLevel = level; // find the next level to process
do
nextLevel--;
while (nextLevel > 1 && histogram[nextLevel] == 0);
// in principle we should add all unprocessed pixels of this level to the
// tasklist of the next level. This would make it very slow for some images,
// however. Thus we only add the pixels if they are at the border (of the
// image or a thresholded area) and correct unprocessed pixels at the very
// end by CleanupExtraLines
if (nextLevel > 0)
{
int newNextLevelEnd = levelStart[nextLevel] + histogram[nextLevel];
for (int i = 0, p = levelStart[level]; i < remaining; i++, p++)
{
int pOffset = coordinates[p];
int x = pOffset % width;
int y = pOffset / width;
//if ((pixels[pOffset] & 255) == 255) IJ.log("ERROR");
Boolean addToNext = false;
if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
addToNext = true; //image border
else for (int d = 0; d < 8; d++)
if (isWithin(x, y, d, width, height) && pixels.GetPixel(pOffset + dirOffset[d]) == 0)
{
addToNext = true; //border of area below threshold
break;
}
if (addToNext)
coordinates[newNextLevelEnd++] = pOffset;
}
//tasklist for the next level to process becomes longer by this:
histogram[nextLevel] = newNextLevelEnd - levelStart[nextLevel];
}
}
}
System.Drawing.Color color = System.Drawing.Color.FromArgb(phase.color);
Mat matPixels = OpenCvSharp.Extensions.BitmapConverter.ToMat(pixels.MakeBmp());
for (int h = 0; h < matPixels.Height; h++)
{
for (int w = 0; w < matPixels.Width; w++)
{
if (matPixels.At(h, w) == 0)
matPixels.Set(h, w, new Vec4b(color.B, color.G, color.R, 255));
else
matPixels.Set(h, w, new Vec4b(0, 0, 0, 0));
}
}
return matPixels;
}
/** returns whether the neighbor in a given direction is within the image
* NOTE: it is assumed that the pixel x,y itself is within the image!
* Uses class variables width, height: dimensions of the image
* @param x x-coordinate of the pixel that has a neighbor in the given direction
* @param y y-coordinate of the pixel that has a neighbor in the given direction
* @param direction the direction from the pixel towards the neighbor (see makeDirectionOffsets)
* @return true if the neighbor is within the image (provided that x, y is within)
*/
public static Boolean isWithin(int x, int y, int direction, int width, int height)
{
int xmax = width - 1;
int ymax = height - 1;
switch (direction)
{
case 0:
return (y > 0);
case 1:
return (x < xmax && y > 0);
case 2:
return (x < xmax);
case 3:
return (x < xmax && y < ymax);
case 4:
return (y < ymax);
case 5:
return (x > 0 && y < ymax);
case 6:
return (x > 0);
case 7:
return (x > 0 && y > 0);
}
return false; //to make the compiler happy :-)
} // isWithin
/** dilate the UEP on one level by one pixel in the direction specified by step, i.e., set pixels to 255
* @param pass gives direction of dilation, see makeFateTable
* @param ip the EDM with the segmeted blobs successively getting set to 255
* @param table The fateTable
* @param levelStart offsets of the level in pixelPointers[]
* @param levelNPoints number of points in the current level
* @param pixelPointers[] list of pixel coordinates (x+y*width) sorted by level (in sequence of y, x within each level)
* @param xCoordinates list of x Coorinates for the current level only (no offset levelStart)
* @return number of pixels that have been changed
*/
public static int processLevel(int pass, BitMap2d pixels, int[] fateTable,
int levelStart, int levelNPoints, int[] coordinates, int[] setPointList, int width, int height)
{
int xmax = width - 1;
int ymax = height - 1;
//byte[] pixels = (byte[])ip.getPixels();
////byte[] pixels2 = (byte[])ip2.getPixels();
int nChanged = 0;
int nUnchanged = 0;
for (int i = 0, p = levelStart; i < levelNPoints; i++, p++)
{
int offset = coordinates[p];
int x = offset % width;
int y = offset / width;
int index = 0; //neighborhood pixel ocupation: index in fateTable
if (y > 0 && ((byte)pixels.GetPixel(offset - width) & 255) == 255)
index ^= 1;
if (x < xmax && y > 0 && ((byte)pixels.GetPixel(offset - width + 1) & 255) == 255)
index ^= 2;
if (x < xmax && ((byte)pixels.GetPixel(offset + 1) & 255) == 255)
index ^= 4;
if (x < xmax && y < ymax && ((byte)pixels.GetPixel(offset + width + 1) & 255) == 255)
index ^= 8;
if (y < ymax && ((byte)pixels.GetPixel(offset + width) & 255) == 255)
index ^= 16;
if (x > 0 && y < ymax && ((byte)pixels.GetPixel(offset + width - 1) & 255) == 255)
index ^= 32;
if (x > 0 && ((byte)pixels.GetPixel(offset - 1) & 255) == 255)
index ^= 64;
if (x > 0 && y > 0 && ((byte)pixels.GetPixel(offset - width - 1) & 255) == 255)
index ^= 128;
int mask = 1 << pass;
if ((fateTable[index] & mask) == mask)
setPointList[nChanged++] = offset; //remember to set pixel to 255
else
coordinates[levelStart + (nUnchanged++)] = offset; //keep this pixel for future passes
} // for pixel i
//IJ.log("pass="+pass+", changed="+nChanged+" unchanged="+nUnchanged);
for (int i = 0; i < nChanged; i++)
pixels.SetPixel(setPointList[i], (byte)255);
return nChanged;
} //processLevel
static public int[] makeFateTable()
{
int[] table = new int[256];
Boolean[] isSet = new Boolean[8];
for (int item = 0; item < 256; item++)
{ //dissect into pixels
for (int i = 0, mask = 1; i < 8; i++)
{
isSet[i] = (item & mask) == mask;
mask *= 2;
}
for (int i = 0, mask = 1; i < 8; i++)
{ //we dilate in the direction opposite to the direction of the existing neighbors
if (isSet[(i + 4) % 8]) table[item] |= mask;
mask *= 2;
}
for (int i = 0; i < 8; i += 2) //if side pixels are set, for counting transitions it is as good as if the adjacent edges were also set
if (isSet[i])
{
isSet[(i + 1) % 8] = true;
isSet[(i + 7) % 8] = true;
}
int transitions = 0;
for (int i = 0; i < 8; i++)
{
if (isSet[i] != isSet[(i + 1) % 8])
transitions++;
}
if (transitions >= 4)
{ //if neighbors contain more than one region, dilation ito this pixel is forbidden
table[item] = 0;
}
else
{
}
}
return table;
} // int[] makeFateTable
///
/// 分水岭分割
///
///
///
///
public static Mat WatershedSegment(Mat mat, List lists)
{
//中间变量
//Mat temp = new Mat();
int paramCount = 0;
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
paramCount = int.Parse(args.Value.ToString());
break;
}
}
//灰度图
Mat Inmat = mat.CvtColor(ColorConversionCodes.BGR2GRAY);
//BGR
Mat Src_3 = mat.CvtColor(ColorConversionCodes.BGRA2BGR);// .Clone();
//二值图
Mat mat_bw = GetBW(Inmat);
//Cv2.ImShow("mat_bw00", mat_bw);
Mat result = Oper_Deal(mat_bw, Src_3);
//mat.CopyTo(temp);
return result;// temp;
}
private static Mat Oper_Deal(Mat ImIn, Mat src_3)
{
#region 距离变换
Mat Im1 = new Mat(ImIn.Size(), ImIn.Type());
//归一化
Cv2.Normalize(ImIn, Im1, 0, 1, NormTypes.MinMax);
Mat Im_dis = new Mat(ImIn.Size(), MatType.CV_32FC1);
Cv2.DistanceTransform(ImIn, Im_dis, DistanceTypes.L2, DistanceMaskSize.Precise);
Cv2.Normalize(Im_dis, Im_dis, 0, 1, NormTypes.MinMax);
Mat conv = new Mat(Im_dis.Size(), MatType.CV_8UC1);
Im_dis.ConvertTo(conv, MatType.CV_8UC1);
Cv2.Threshold(conv, conv, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
#endregion
#region 轮廓标记
Mat dis_8U = conv;
//提取标记
Point[][] contours; HierarchyIndex[] hierarchy;
Cv2.FindContours(dis_8U, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new Point());
#endregion
#region 将标记存入markers,作分水岭注水点用
var markers = new Mat(ImIn.Size(), MatType.CV_32SC1, Scalar.All(0));
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(markers, contours, i, Scalar.All(i + 1), 10, LineTypes.Link8, hierarchy);
}
#endregion
Cv2.Watershed(src_3, markers);
#region 分水岭结果图处理
//连通域为1,线条为0
var markers_ind = markers.GetGenericIndexer();
for (int i = 0; i < markers.Rows; i++)
{
for (int j = 0; j < markers.Cols; j++)
{
if (markers_ind[i, j] != -1)
{
markers_ind[i, j] = int.MaxValue;
}
else if (markers_ind[i, j] == -1)
{
markers_ind[i, j] = 0;
}
}
}
//结合初始二值图背景为0
var ImIn_ind = ImIn.GetGenericIndexer();
for (int i = 0; i < markers.Rows; i++)
{
for (int j = 0; j < markers.Cols; j++)
{
if (ImIn_ind[i, j] == 0)
{
markers_ind[i, j] = 0;
}
}
}
#endregion
#region 连通域信息
Mat water = new Mat(markers.Size(), MatType.CV_8UC1);
markers.ConvertTo(water, MatType.CV_8UC1);
var water_ind = water.GetGenericIndexer();
Mat water_bw = new Mat();
Cv2.Threshold(water, water_bw, 0, 255, ThresholdTypes.Otsu);
//连通图的信息获取
Mat labels = new Mat(); Mat stats = new Mat(); Mat centroids = new Mat();
int num = Cv2.ConnectedComponentsWithStats(water_bw, labels, stats, centroids);
#endregion
#region
Vec3b[] color = new Vec3b[num + 1];
color[0] = new Vec3b(0, 0, 0);
var stats_ind = stats.GetGenericIndexer();
Random rand = new Random();
byte ranB = (byte)rand.Next(100, 255); byte ranG = (byte)rand.Next(100, 255); byte ranR = (byte)rand.Next(100, 255);
for (int i = 1; i < num; i++)
{
color[i] = new Vec3b(ranB, ranG, ranR);
int S = stats_ind[i, 4];
if (S < 200)
{
color[i] = new Vec3b(0, 0, 0);
}
}
#endregion
#region mat填色
Mat final = new Mat(water.Size(), MatType.CV_8UC3);
for (int i = 0; i < final.Rows; i++)
{
for (int j = 0; j < final.Cols; j++)
{
int label = labels.Get(i, j);
final.Set(i, j, color[label]);
}
}
#endregion
return final;
}
private static Mat GetBW(Mat Src)
{
//(最大类间方差)二值化
//Mat result = Src.Threshold(109.242, 255, ThresholdTypes.Otsu);
Mat result = Src.AdaptiveThreshold(255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 35, 5);
//Cv2.ImShow("test", result); Cv2.WaitKey();
return result;
}
#endregion
#region 去碎屑
///
/// 去碎屑(功能实际是颗粒筛选的功能)
/// 需要传进来二值的图
///
///
///
///
public static Mat Debris(Mat mat, List lists, int color, double rule)
{
//相颜色
System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color);
//原始轮廓信息
OpenCvSharp.Point[][] contours;
//轮廓的拓扑信息
HierarchyIndex[] hierachy;
//筛选参数
FilterParameters filterParameters = FilterParameters.Area;
//筛选范围
double min = 0, max = 0;
//筛选单位
MeasurementUnit measurementUnit = MeasurementUnit.Pixel;
//筛选单方式
FunctionParameters functionParameters = FunctionParameters.Choise;
//边界保留
bool boundaryPreservation = false;
//中间变量
Mat temp = new Mat();
//轮廓总面积
//double areas = 0;
//删除的边界
List deleteIndexs = new List();
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "FilterParameters":
filterParameters = (FilterParameters)args.Value;
break;
case "Scope":
min = ((List)args.Value)[0];
max = ((List)args.Value)[1];
break;
case "UnitParameters":
measurementUnit = (MeasurementUnit)args.Value;
break;
case "FunctionParameters":
functionParameters = (FunctionParameters)args.Value;
break;
case "BoundaryPreservation":
boundaryPreservation = (bool)args.Value;
break;
}
}
mat.CopyTo(temp);
Cv2.FindContours(mat.CvtColor(ColorConversionCodes.BGR2GRAY), out contours, out hierachy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone);
//没有选中边界保留
if (!boundaryPreservation)
{
if (contours.Length > 0)
{
for (int i = 0; i < contours.Length; i++)
{
for (int y = 0; y < contours[i].Length; y++)
{
if (contours[i][y].X == 0 || contours[i][y].X == temp.Width - 1 || contours[i][y].Y == 0 || contours[i][y].Y == temp.Height - 1)
{
deleteIndexs.Add(i);
List pointsTemp = new List();
RecursiveFindChildContours(contours.ToList(), hierachy, i, deleteIndexs);
break;
}
}
}
}
//用于绘制的轮廓
List drawContours = contours.ToList();
//循环处理轮廓,过滤到被删除的轮廓及其子轮廓
if (deleteIndexs.Count > 0)
{
drawContours.Clear();
for (int i = 0; i < contours.Length; i++)
{
if (!deleteIndexs.Exists(a => a == i))// && !deleteIndexs.Exists(a => a == hierachy[i].Parent)
{
drawContours.Add(contours[i]);
}
}
}
temp = new Mat(mat.Size(), mat.Type());
Cv2.FillPoly(temp, drawContours, new Scalar(color1.B, color1.G, color1.R, 255), LineTypes.Link8);
}
/*//计算总面积
foreach (Point[] points in contours)
{
areas += Math.Abs(Cv2.ContourArea(points));
}
if (measurementUnit == MeasurementUnit.Micron) areas = areas * rule;*/
List> ps = new List>();
for (int i = 0; i < hierachy.Length; i++)
{
if (deleteIndexs.Exists(a => a == i))
continue;
//计算面积
if (filterParameters == FilterParameters.Area)
{
double area = Math.Abs(Cv2.ContourArea(contours[i]));
if (measurementUnit == MeasurementUnit.Micron) area = area * rule * rule;
if (functionParameters == FunctionParameters.Remove)
{
if (area >= min && area <= max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
else if (functionParameters == FunctionParameters.Choise)
{
if (area <= min || area >= max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
}
//计算面积比
else if (filterParameters == FilterParameters.AreaRatio)
{
double area = Math.Abs(Cv2.ContourArea(contours[i]));
Point2f center;
float radius;
Cv2.MinEnclosingCircle(contours[i], out center, out radius);
double areas = Math.PI * radius * radius;
if (measurementUnit == MeasurementUnit.Micron)
{
area = area * rule * rule;
areas = areas * rule * rule;
}
if (functionParameters == FunctionParameters.Remove)
{
if (area / areas > min && area / areas < max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
else if (functionParameters == FunctionParameters.Choise)
{
if (area / areas < min || area / areas > max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
}
//计算宽高比
else if (filterParameters == FilterParameters.AspectRatio)
{
double area = BasicCalculationHelper.CalcAspectRatio(contours[i]);
if (functionParameters == FunctionParameters.Remove)
{
if (area > min && area < max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
else if (functionParameters == FunctionParameters.Choise)
{
if (area < min || area > max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
}
//计算最大卡规直径(长径)
else if (filterParameters == FilterParameters.LongTrail)
{
double area = BasicCalculationHelper.CalcLongTrail(contours[i]) * 2;
if (measurementUnit == MeasurementUnit.Micron) area = area * rule;
if (functionParameters == FunctionParameters.Remove)
{
if (area > min && area < max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
else if (functionParameters == FunctionParameters.Choise)
{
if (area < min || area > max && hierachy[i].Parent == -1)
{
ps.Add(contours[i].ToList());
}
}
}
}
if (functionParameters == FunctionParameters.Choise)
Cv2.FillPoly(temp, ps, Scalar.Red);
else if (functionParameters == FunctionParameters.Remove)
Cv2.FillPoly(temp, ps, new Scalar(0, 0, 0, 0));
return temp;
}
private static void RecursiveFindChildContours(
List drawContours,
HierarchyIndex[] hierachy,
int position,
List points
)
{
int m = 0;
foreach (HierarchyIndex index in hierachy)
{
if (index.Parent == position)
{
points.Add(m);
RecursiveFindChildContours(drawContours, hierachy, m, points);
}
m++;
}
}
#endregion
#region 孔洞删除
///
/// 孔洞删除,用其它颜色进行填充
///
///
///
///
public static Mat HoleRemoval(Mat mat, List lists, int pColor, double rule, out double outmin, out double outmax)
{
//原始轮廓信息
OpenCvSharp.Point[][] contours;
//轮廓的拓扑信息
HierarchyIndex[] hierachy;
//筛选参数
FilterParameters filterParameters = FilterParameters.Area;
//筛选范围
double min = 0, max = 0;
//输出的值
outmin = -1;
outmax = -1;
//筛选单位
MeasurementUnit measurementUnit = MeasurementUnit.Pixel;
//孔洞颜色
int color = 0;
//面积合计大小
//double areas = 0;
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "FilterParameters":
filterParameters = (FilterParameters)args.Value;
break;
case "Scope":
min = ((List)args.Value)[0];
max = ((List)args.Value)[1];
break;
case "UnitParameters":
measurementUnit = (MeasurementUnit)args.Value;
break;
case "HoleColor":
color = (int)args.Value;
break;
}
}
System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color);
System.Drawing.Color color2 = System.Drawing.Color.FromArgb(pColor);
Mat dst = new Mat();
mat.CopyTo(dst);
Cv2.FindContours(dst.CvtColor(ColorConversionCodes.BGR2GRAY), out contours, out hierachy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone);
/*//计算总面积
foreach (Point[] points in contours)
{
areas += Math.Abs(Cv2.ContourArea(points));
}
if (measurementUnit == MeasurementUnit.Micron) areas = areas * rule * rule;*/
List ints = new List();
List> ps = new List>();
List> ps1 = new List>();
for (int i = 0; i < hierachy.Length; i++)
{
if (hierachy[i].Parent > -1)
{
ps1.Add(contours[i].ToList());
//计算面积
if (filterParameters == FilterParameters.Area)
{
double area = Math.Abs(Cv2.ContourArea(contours[i]));
//double area = BasicCalculationHelper.DecimalCeiling(Math.Abs(Cv2.ContourArea(contours[i])));
if (measurementUnit == MeasurementUnit.Micron) area = area * rule * rule;
if (outmin == -1)
{
outmin = area;
outmax = area;
}
else
{
if (outmin > area) outmin = BasicCalculationHelper.DecimalFloor(area);
if (outmax < area) outmax = BasicCalculationHelper.DecimalCeiling(area);
}
if (area >= min && area <= max)
{
ints.Add(i);
//ps.Add(contours[i].ToList());
}
}
//计算面积比
else if (filterParameters == FilterParameters.AreaRatio)
{
double area = Math.Abs(Cv2.ContourArea(contours[i]));
Point2f point;
float radius;
Cv2.MinEnclosingCircle(contours[i], out point, out radius);
double areas = Math.PI * radius * radius;
if (outmin == -1)
{
outmin = area / areas;
outmax = Math.Round(area / areas, 2);
}
else
{
if (outmin > area / areas) outmin = area / areas;
if (outmax < area / areas) outmax = Math.Round(area / areas, 2);
}
if (area / areas >= min && Math.Round(area / areas, 2) <= max)
{
ints.Add(i);
//ps.Add(contours[i].ToList());
}
}
//计算宽高比
else if (filterParameters == FilterParameters.AspectRatio)
{
double area = Math.Round(BasicCalculationHelper.CalcAspectRatio(contours[i]), 2);
if (outmin == -1)
{
outmin = area;
outmax = area;
}
else
{
if (outmin > area) outmin = area;
if (outmax < area) outmax = area;
}
if (area >= min && area <= max)
{
ints.Add(i);
//ps.Add(contours[i].ToList());
}
}
//计算最大卡规直径(长径)
else if (filterParameters == FilterParameters.LongTrail)
{
double area = BasicCalculationHelper.CalcLongTrail(contours[i]) * 2;
if (measurementUnit == MeasurementUnit.Micron) area = area * rule;
if (outmin == -1)
{
outmin = area;
outmax = area;
}
else
{
if (outmin > area) outmin = BasicCalculationHelper.DecimalFloor(area);
if (outmax < area) outmax = BasicCalculationHelper.DecimalCeiling(area);
}
if (area >= min && area <= max)
{
ints.Add(i);
//his.Add(hierachy[i]);
//ps.Add(contours[i].ToList());
}
}
}
}
Cv2.FillPoly(mat, ps1, new Scalar(color1.B, color1.G, color1.R, color1.A));
for (int k = 0; k < ints.Count(); k++)
{
Cv2.DrawContours(mat, contours, ints[k], new Scalar(color2.B, color2.G, color2.R, 255), -1, LineTypes.Link8, hierachy, 0);
}
//foreach (List points in ps)
//{
//Cv2.FillPoly(mat, InputArray.Create(ps[0]), new Scalar(color2.B, color2.G, color2.R, 255));
//}
//Cv2.DrawContours(mat, ps, -1, new Scalar(color2.B, color2.G, color2.R, 255), -1, LineTypes.Link8, his, 1);
//Cv2.FillPoly(mat, ps, new Scalar(color2.B, color2.G, color2.R, 255));
return mat;
}
#endregion
#region 抽骨架
unsafe byte* pixels;
int foreground;
int width, height;
int xMin, xMax, yMin, yMax;
Mat dst;
///
/// 抽骨架
/// 使用demo里面的form26里面的ImageJ查表法
///
///
///
///
public unsafe Mat ImageSkeleton(Mat mat, List lists, int color)
{
//如果不是单通道,转单通道
if (mat.Type() != MatType.CV_8UC1)
{
Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
}
//判断是否二值化图像
if (!BaseTools.DetermineBinaryImageByHist(mat))
{
MessageBox.Show("Please enter abinarization image");
return mat;
}
//判断是否黑白的二值图
if (!BaseTools.DetermineBinaryImage(mat))
{
//处理为黑/白二值化的图像
for (int o = 0; o < mat.Height; o++)
{
for (int p = 0; p < mat.Width; p++)
{
byte v = mat.At(o, p);
if (v > 0)
{
mat.Set(o, p, 255);
}
}
}
}
//背景颜色
int fg = 0;
//前景颜色
foreground = 255 - fg;
int pass = 0;
int pixelsRemoved;
dst = new Mat();
bool edgePixels = hasEdgePixels(mat);
if (edgePixels)
{
dst = new Mat(new OpenCvSharp.Size(mat.Width + 2, mat.Height + 2), MatType.CV_8UC1, Scalar.All(0));
Cv2.CopyMakeBorder(mat, dst, 1, 1, 1, 1, BorderTypes.Constant, Scalar.All(0));
}
else
{
mat.CopyTo(dst);
}
width = dst.Width;
height = dst.Height;
xMin = 1;
xMax = width - 2;
yMin = 1;
yMax = height - 2;
pixels = (byte*)dst.Data;
do
{
pixelsRemoved = thin(pass++, BaseTools.table);
pixelsRemoved += thin(pass++, BaseTools.table);
} while (pixelsRemoved > 0);
do
{
pixelsRemoved = thin(pass++, BaseTools.table2);
pixelsRemoved += thin(pass++, BaseTools.table2);
} while (pixelsRemoved > 0);
BaseTools.shrink(mat, dst, edgePixels);
System.Drawing.Color color1 = System.Drawing.Color.FromArgb(color);
Mat temp1 = new Mat(mat.Size(), OpenCvSharp.MatType.CV_8UC4);
for (int o = 0; o < mat.Height; o++)
{
for (int p = 0; p < mat.Width; p++)
{
byte v = dst.At(o, p);
if (v > 0)
{
temp1.Set(o, p, new Vec4b(color1.B, color1.G, color1.R, 255));
}
else
{
temp1.Set(o, p, new Vec4b(0, 0, 0, 0));
}
}
}
temp1.CopyTo(mat);
return mat;
}
bool hasEdgePixels(Mat ip)
{
int width = ip.Width;
int height = ip.Height;
bool edgePixels = false;
for (int x = 0; x < width; x++)
{ // top edge
if (ip.At(0, x) == foreground)
edgePixels = true;
}
for (int x = 0; x < width; x++)
{ // bottom edge
if (ip.At(height - 1, x) == foreground)
edgePixels = true;
}
for (int y = 0; y < height; y++)
{ // left edge
if (ip.At(y, 0) == foreground)
edgePixels = true;
}
for (int y = 0; y < height; y++)
{ // right edge
if (ip.At(y, width - 1) == foreground)
edgePixels = true;
}
return edgePixels;
}
unsafe int thin(int pass, int[] table)
{
int p1, p2, p3, p4, p5, p6, p7, p8, p9;
int bgColor = 0; //255
//if (parent.isInvertedLut())
// bgColor = 0;
Mat temp = new Mat();
dst.CopyTo(temp);
byte* pixels2 = (byte*)temp.Data;
int v, index, code;
int offset, rowOffset = temp.Width;
int pixelsRemoved = 0;
for (int y = yMin; y <= yMax; y++)
{
offset = xMin + y * temp.Width;
for (int x = xMin; x <= xMax; x++)
{
p5 = pixels2[offset];
v = p5;
if (v != bgColor)
{
p1 = pixels2[offset - rowOffset - 1];
p2 = pixels2[offset - rowOffset];
p3 = pixels2[offset - rowOffset + 1];
p4 = pixels2[offset - 1];
p6 = pixels2[offset + 1];
p7 = pixels2[offset + rowOffset - 1];
p8 = pixels2[offset + rowOffset];
p9 = pixels2[offset + rowOffset + 1];
index = 0;
if (p1 != bgColor) index |= 1;
if (p2 != bgColor) index |= 2;
if (p3 != bgColor) index |= 4;
if (p6 != bgColor) index |= 8;
if (p9 != bgColor) index |= 16;
if (p8 != bgColor) index |= 32;
if (p7 != bgColor) index |= 64;
if (p4 != bgColor) index |= 128;
code = table[index];
if ((pass & 1) == 1)
{ //odd pass
if (code == 2 || code == 3)
{
v = bgColor;
pixelsRemoved++;
}
}
else
{ //even pass
if (code == 1 || code == 3)
{
v = bgColor;
pixelsRemoved++;
}
}
}
pixels[offset++] = (byte)v;
}
}
return pixelsRemoved;
}
#endregion
#region 空洞填充
///
/// 孔洞填充
///
///
///
///
public static Mat HolesFill(Mat mat, int c)
{
Mat temp = null;
try
{
//mat的颜色
System.Drawing.Color color = System.Drawing.Color.FromArgb(c);
//原始轮廓信息
OpenCvSharp.Point[][] contours;
//轮廓的拓扑信息
HierarchyIndex[] hierachy;
//中间变量
temp = new Mat();
mat.CopyTo(temp);
Cv2.FindContours(temp.CvtColor(ColorConversionCodes.BGRA2GRAY), out contours, out hierachy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone);
if (hierachy != null)
{
for (int i = 0; i < hierachy.Length; i++)
{
//如果需要还可以判断面积范围
if (hierachy[i].Parent > -1)
{
Vec3b vec3B = mat.At(contours[i][0].Y, contours[i][0].X);
if ((vec3B.Item0 == (byte)color.B) && (vec3B.Item1 == (byte)color.G) && (vec3B.Item2 == (byte)(color.R)))
{
List> ps = new List>();
ps.Add(contours[i].ToList());
Cv2.FillPoly(mat, ps, new Scalar(color.B, color.G, color.R, 255));
}
}
}
}
}
catch (Exception)
{
System.Windows.Forms.MessageBox.Show("Abnormal program");
}
finally
{
if (temp != null && !temp.IsDisposed) temp.Dispose();
}
return mat;
}
#endregion
#region 形态学分割
public static Mat MorphologySegment(Mat mat, PhaseModel model, List lists)
{
//获取参数
int paramCount = 0;
//读取参数信息
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "Count":
paramCount = int.Parse(args.Value.ToString());
break;
}
}
//处理相
for (int h = 0; h < model.mat.Height; h++)
{
for (int w = 0; w < model.mat.Width; w++)
{
Vec4b vec4B = model.mat.At(h, w);
if (vec4B.Item3 == 0)
{
model.mat.Set(h, w, new Vec4b(255, 255, 255, 255));
}
else
{
model.mat.Set(h, w, new Vec4b(0, 0, 0, 255));
}
}
}
//构造八边形的卷积核
InputArray kernel = InputArray.Create(new int[5, 5] {
{ 0, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 0, 1, 1, 1, 0 } });
Mat element = kernel.GetMat();
element.ConvertTo(element, MatType.CV_8UC1);
Mat lab1Img = new Mat();
model.mat.CopyTo(lab1Img);
//原图灰度化
Mat img = lab1Img.CvtColor(ColorConversionCodes.BGR2GRAY);
//用来判断极限腐蚀是否完成的中间变量
Mat erode = new Mat();
img.CopyTo(erode);
//用来观察腐蚀次数
int h1 = 0;
Mat temp = new Mat();
//开始
while (Cv2.CountNonZero(erode) > 0)
{
//寻找并标记区域个数
Mat labelMat = new Mat();
Mat stats = new Mat();
Mat centroids = new Mat();
int num = Cv2.ConnectedComponentsWithStats(erode, labelMat, stats, centroids, PixelConnectivity.Connectivity8);
//进行腐蚀
temp = erode.Erode(element, null, 1);
//跳出循环的条件
int count = Cv2.CountNonZero(temp);
//循环质心,和腐蚀后的图片进行比较,如果腐蚀后的图片质心为黑色了,那说明被腐蚀掉了,则在下面需要被还原回来
for (int h = 1; h < centroids.Height; h++)
{
double a = centroids.At(h, 0);
double b = centroids.At(h, 1);
OpenCvSharp.Point point = new OpenCvSharp.Point(a, b);
//计算每个stats范围内的像素和是否为0
int x = stats.At(h, 0);
int y = stats.At(h, 1);
int width = stats.At(h, 2);
int height = stats.At(h, 3);
System.Console.WriteLine("x:" + x + " y:" + y);
Rect roi1 = new Rect(x, y, width, height);
Mat ImageROI1 = new Mat(temp, roi1);
int cc1 = Cv2.CountNonZero(ImageROI1);
//代表区域已经消失了,需要还原回来
if (cc1 == 0)
{
Rect roi = new Rect(x, y, width, height);
Mat ImageROI = new Mat(erode, roi);
Rect rect = new Rect(x, y, width, height);
Mat pos = new Mat(temp, rect);
ImageROI.CopyTo(pos);
}
}
if (h1 == paramCount && paramCount > 0) break;
temp.CopyTo(erode);
h1++;
if (count == 0)
{
break;
}
}
Mat fffff = new Mat();
Mat labels = new Mat();
Cv2.DistanceTransformWithLabels(~temp, fffff, labels, DistanceTypes.L1, DistanceMaskSize.Precise);
//labels.ConvertTo(labels, MatType.CV_8UC3);
//找到轮廓
OpenCvSharp.Point[][] contours;
HierarchyIndex[] hierachy;
Cv2.FindContours(labels, out contours, out hierachy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone);
Mat lk = new Mat(labels.Size(), MatType.CV_8UC1, Scalar.All(255));
Cv2.DrawContours(lk, contours, -1, Scalar.All(0), 1, LineTypes.Link8, hierachy);
//Cv2.MorphologyEx(img, img, MorphTypes.Open, element, null, 1);
//Cv2.Dilate(img, img, element, null, 1);
System.Drawing.Color color = System.Drawing.Color.FromArgb(model.color);
for (int h = 0; h < lab1Img.Height; h++)
{
for (int w = 0; w < lab1Img.Width; w++)
{
if (lab1Img.At(h, w) == 0)
lab1Img.Set(h, w, new Vec3b(color.B, color.G, color.R));
/*if (lab1Img.At(h, w) == 0 || lk.At(h, w) == 0)
lab1Img.Set(h, w, new Vec4b(color.B, color.G, color.R, 255));
else
lab1Img.Set(h, w, new Vec4b(0, 0, 0, 0));*/
}
}
for (int h = 0; h < lk.Height; h++)
{
for (int w = 0; w < lk.Width; w++)
{
if (lk.At(h, w) == 0)
lab1Img.Set(h, w, new Vec3b(color.B, color.G, color.R));
}
}
Cv2.Erode(lab1Img, lab1Img, null, null, 1);
Cv2.Dilate(lab1Img, lab1Img, null, null, 1);
//Cv2.ImShow("dddd", lk);
//Cv2.ImShow("粗化操作1", lab1Img);
return lab1Img;
}
#endregion
}
public struct Int16DoubleWithValue : IComparable
{
public int O;
public float V;
public Int16DoubleWithValue(int offset, float value)
{
O = offset;
V = value;
}
public int CompareTo(Int16DoubleWithValue other)
{
return this.V.CompareTo(other.V);
}
}
public class BitMap2d
{
public float[] data;
public int width;
public int height;
///
/// 初始化操作对象
///
///
///
/// 初始值
public BitMap2d(int width, int height, float v)
{
this.width = width;
this.height = height;
data = new float[width * height];
for (int i = 0; i < width * height; i++)
data[i] = v;
}
///
/// 根据mat初始化
///
/// 初始EDM对象
public unsafe BitMap2d(Mat mat)
{
this.width = mat.Width;
this.height = mat.Height;
data = new float[width * height];
mat.ForEachAsFloat(FunctionForEachAsFloat);
}
private unsafe void FunctionForEachAsFloat(float* value, int* position)
{
data[position[0] * width + position[1]] = *value;
}
public void SetPixel(int x, int y, byte v)
{
data[x + y * width] = v;
}
public float GetPixel(int x, int y)
{
return data[x + y * width];
}
public void SetPixel(int offset, byte v)
{
data[offset] = v;
}
public float GetPixel(int offset)
{
return data[offset];
}
public System.Drawing.Bitmap MakeBmp()
{
float min = float.MaxValue;
float max = float.MinValue;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
float r = this.GetPixel(i, j);
if (r > max)
max = r;
if (r < min)
min = r;
}
}
float delta = max - min;
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(this.width, this.height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
float r = this.GetPixel(i, j);
int b = 255 - (int)(255 * (r - min) / delta);
System.Drawing.Color c = System.Drawing.Color.FromArgb((byte)b, (byte)b, (byte)b);
bmp.SetPixel(i, j, c);
}
}
return bmp;
}
}
public class MaximunFinder
{
BitMap2d bmp;
int width;
int height;
public MaximunFinder(BitMap2d bmp)
{
this.bmp = bmp;
this.width = bmp.width;
this.height = bmp.height;
}
public List FindMaxima()
{
List list = new List();
int offset = 0;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++, offset++)
{
if (IsMaxima(i, j, offset))
{
list.Add(new Int16DoubleWithValue(offset, bmp.GetPixel(offset)));
}
}
}
list.Sort();
return list;
}
private bool IsMaxima(int i, int j, int offset)
{
float v = bmp.GetPixel(offset);
if (v == 0) return false;
bool b1 = v >= bmp.GetPixel(Math.Max(0, i - 1), Math.Max(0, j - 1));
bool b2 = v >= bmp.GetPixel(i, Math.Max(0, j - 1));
bool b3 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), Math.Max(0, j - 1));
bool b4 = v >= bmp.GetPixel(Math.Max(0, i - 1), j);
bool b5 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), j);
bool b6 = v >= bmp.GetPixel(Math.Max(0, i - 1), Math.Min(height - 1, j + 1));
bool b7 = v >= bmp.GetPixel(i, Math.Min(height - 1, j + 1));
bool b8 = v >= bmp.GetPixel(Math.Min(width - 1, i + 1), Math.Min(height - 1, j + 1));
return b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8 && (v > 0);
}
}
}