using OpenCvSharp;
using PaintDotNet.Adjust.BaseImage;
using PaintDotNet.Base;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace PaintDotNet.Adjust
{
///
/// 二值提取的处理程序
///
public unsafe class BinaryExtractionIntent
{
///
/// 全局中间变量
/// 不带视场的时候就是原图/二值图
/// 带视场的时候就是原图/二值图
///
public static Mat temp, temp_view;
///
/// 用来存储透明图
///
public static Mat alpha;
///
/// 1个颜色区间还是2个
///
public static int colorInterval;
///
/// 1个颜色区间时的起始值
///
public static int colorOneStart;
///
/// 1个颜色区间时的结止值
///
public static int colorOneEnd;
///
/// 2个颜色区间时的第1个区间的起始值
///
public static int colorTwoStart = 0;
///
/// 2个颜色区间时的第1个区间的结止值
///
public static int colorTwoEnd = 0;
///
/// 2个颜色区间时的第2个区间的起始值
///
public static int colorThreeStart = 0;
///
/// 2个颜色区间时的第2个区间的起始值
///
public static int colorThreeEnd = 0;
//碎屑删除面积起
public static int debrisAreaStart = 0;
//碎屑删除面积止
public static int debrisAreaEnd = 0;
///
/// 默认相颜色
///
public static Vec4b vec4B = new Vec4b(255, 0, 0, 255);
///
/// 新增的参数,用于FloodFill方法
///
public static Rect rect;
///
/// 各个连通域的代表点
///
public static List points;
///
/// 用来记录需要删除的连通区域的编号
///
public static Dictionary keyValuePairs = new Dictionary();
public static Mat rgb_ap;
public static Mat temp_ap;
#region 无视场 二值提取
///
/// 二值操作-二值提取-无视场
///
/// 源图像
/// 参数集合
///
public static Mat ImageBinaryExtraction(Mat src, List lists, bool findContours = false)
{
//以下是参数信息---------------------
///1个颜色区间还是2个
colorInterval = 1;
//第一个二值区间
colorOneStart = 0;
colorOneEnd = 0;
//第二个二值区间
colorTwoStart = 0;
colorTwoEnd = 0;
//第三个二值区间
colorThreeStart = 0;
colorThreeEnd = 0;
//处理细节
//删除边界对象
bool deleteBoundaryObject = false;
//孔洞填充
bool holeFilling = false;
//碎屑删除
bool debrisRemoval = false;
//碎屑删除面积起
debrisAreaStart = 0;
//碎屑删除面积止
debrisAreaEnd = 0;
//二值样式
//1实心 2边线
int binaryStyle = 0;
//相颜色
Color phaseColor = Color.Red;
//目标选择
int targetSelection = 1;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "colorInterval":
{
if (args.Value is Boolean)
colorInterval = (Boolean)args.Value ? 2 : 1;
else
colorInterval = (int)args.Value;
}
break;
case "scope1":
colorOneStart = (int)((List)args.Value)[0];
colorOneEnd = (int)((List)args.Value)[1];
break;
case "scope2":
colorTwoStart = (int)((List)args.Value)[0];
colorTwoEnd = (int)((List)args.Value)[1];
break;
case "scope3":
colorThreeStart = (int)((List)args.Value)[0];
colorThreeEnd = (int)((List)args.Value)[1];
break;
case "deleteBoundaryObject":
deleteBoundaryObject = (bool)args.Value;
break;
case "holeFilling":
holeFilling = (bool)args.Value;
break;
case "debrisRemoval":
debrisRemoval = (bool)args.Value;
break;
case "binaryStyle":
binaryStyle = (int)args.Value;
break;
case "phaseColor":
phaseColor = Color.FromArgb((int)args.Value);
break;
case "targetSelection":
targetSelection = (int)args.Value;
break;
case "scope4":
debrisAreaStart = (int)((List)args.Value)[0];
debrisAreaEnd = (int)((List)args.Value)[1];
findContours = true;
break;
default:
break;
}
}
if (phaseColor.A < 1)//#21321
phaseColor = Color.FromArgb(1, phaseColor.R, phaseColor.G, phaseColor.B);
vec4B.Item0 = phaseColor.B;
vec4B.Item1 = phaseColor.G;
vec4B.Item2 = phaseColor.R;
vec4B.Item3 = phaseColor.A;
//中值滤波
//Cv2.MedianBlur(src, src, 3);
//二值提取
src = Binarization(src);
//删除边界对象
if (deleteBoundaryObject)
src = DeleteContours_New(src);
//碎屑删除,参考冈萨雷斯,414,9.5.3,连通分量提取
if (debrisRemoval && debrisAreaEnd > 0)
src = DebrisRemoval_New(src);
//孔洞填充,参考冈萨雷斯,413,9.5.2
if (holeFilling)
src = HoleFilling_New(src);
//实心/边线,参考冈萨雷斯,412,9.5.1,边界提取
if (binaryStyle == 2)
{
Cv2.Erode(src, temp, null);
src = src - temp;
src = BaseTools.MergeMatFromMatArr(src, vec4B);
}
return src;
}
///
/// 二值提取
///
/// 源
/// 1个或2个颜色区间
///
///
///
///
///
///
///
private static Mat Binarization(Mat source)
{
Mat srcGray;
if (source.Type() == MatType.CV_8UC1)
{
srcGray = source;
}
else
{
srcGray = source.CvtColor(ColorConversionCodes.BGR2GRAY);
}
temp = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
srcGray.ForEachAsByte(GrayForEachAsByte);
return temp.Clone();
}
///
/// 并行循环
///
///
///
private static void GrayForEachAsByte(byte* value, int* position)
{
int y = position[0];
int x = position[1];
byte v = *value;
if (colorInterval == 1)
{
if (v >= colorOneStart && v <= colorOneEnd)
{
temp.Set(y, x, vec4B);
}
}
else
{
if ((v >= colorTwoStart && v <= colorTwoEnd) || (v >= colorThreeStart && v <= colorThreeEnd))
{
temp.Set(y, x, vec4B);
}
}
}
///
/// 新版本删除边界
///
///
///
private static Mat DeleteContours_New(Mat src)
{
Mat[] arr = src.Split();
Mat rgb = BaseTools.MergeMatFromMatArr(arr);
Mat temp_1 = arr[3].Clone();
for (int h = 0; h < temp_1.Width; h++)
{
byte vec4B_top = temp_1.At(0, h);
if (vec4B_top > 0)
{
Cv2.FloodFill(rgb, new OpenCvSharp.Point(h, 0), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
Cv2.FloodFill(temp_1, new OpenCvSharp.Point(h, 0), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
}
}
for (int h = 0; h < temp_1.Width; h++)
{
byte vec4B_bottom = temp_1.At(temp_1.Height - 1, h);
if (vec4B_bottom > 0)
{
Cv2.FloodFill(rgb, new OpenCvSharp.Point(h, temp_1.Height - 1), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
Cv2.FloodFill(temp_1, new OpenCvSharp.Point(h, temp_1.Height - 1), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
}
}
for (int y = 0; y < temp_1.Height; y++)
{
Vec4b vec4B_top = temp_1.At(y, 0);
if (vec4B_top.Item3 > 0)
{
Cv2.FloodFill(rgb, new OpenCvSharp.Point(0, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
Cv2.FloodFill(temp_1, new OpenCvSharp.Point(0, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
}
}
for (int y = 0; y < temp_1.Height; y++)
{
byte vec4B_top = temp_1.At(y, temp_1.Width - 1);
if (vec4B_top > 0)
{
Cv2.FloodFill(rgb, new OpenCvSharp.Point(temp_1.Width - 1, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
Cv2.FloodFill(temp_1, new OpenCvSharp.Point(temp_1.Width - 1, y), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
}
}
Mat[] mats = rgb.Split();
arr[0] = mats[0];
arr[1] = mats[1];
arr[2] = mats[2];
arr[3] = temp_1;
Cv2.Merge(arr, src);
return src;
}
///
/// 新孔洞填充,采用形态学填充
///
///
///
private static Mat HoleFilling_New(Mat src)
{
Mat[] arr2 = src.Split();
Mat matc = arr2[3];
//去掉透明层
Mat mat = src.CvtColor(ColorConversionCodes.BGRA2BGR);
//填充孔洞
mat = BaseTools.FillHole(mat, new Scalar(255-vec4B.Item0, 255 - vec4B.Item1, 255 - vec4B.Item2));
matc = BaseTools.FillHole(matc, new Scalar(255));
//循环处理
mat.ForEachAsVec3b(RGBForEachAsByteForHoleFillingNew);
Mat[] arr1 = mat.Split();
arr2[0] = arr1[0];
arr2[1] = arr1[1];
arr2[2] = arr1[2];
arr2[3] = matc;
Cv2.Merge(arr2, src);
return src;
}
private static void RGBForEachAsByteForHoleFillingNew(Vec3b* value, int* position)
{
int y = position[0];
int x = position[1];
if (value->Item0==255 && value->Item1==255 && value->Item2==255)
{
value->Item0 = vec4B.Item0;
value->Item1 = vec4B.Item1;
value->Item2 = vec4B.Item2;
}
}
///
/// 新碎屑删除,使用连通分量找寻碎屑
///
///
///
private static Mat DebrisRemoval_New(Mat src)
{
Mat[] arr = src.Split();
rgb_ap = BaseTools.MergeMatFromMatArr(arr);
temp_ap = arr[3].Clone();
//寻找连通分量
Mat labelMat = new Mat();
Mat stats = new Mat();
Mat centroids = new Mat();
int nonenum = Cv2.ConnectedComponentsWithStats(temp_ap, labelMat, stats, centroids, PixelConnectivity.Connectivity8);
//寻找在范围内需要删除的碎屑
keyValuePairs.Clear();
for (int h = 1; h < centroids.Height; h++)
{
int areaField1 = stats.At(h, 4);
if (debrisAreaStart <= areaField1 && areaField1 <= debrisAreaEnd)
{
keyValuePairs.Add(h, 1);
}
}
labelMat.ForEachAsInt32(CommonForEachForInt32);
//合并rgb和透明度
Mat[] mats = rgb_ap.Split();
arr[0] = mats[0];
arr[1] = mats[1];
arr[2] = mats[2];
arr[3] = temp_ap;
Cv2.Merge(arr, src);
return src;
}
#endregion
#region 有视场 二值提取
///
/// 二值操作-二值提取-有视场
///
/// 视场mat
/// 原图mat
/// 参数列表
///
public static Mat ImageBinaryExtraction(Mat source, Mat mat, List lists, bool findContours = false)
{
//以下是参数信息---------------------
///1个颜色区间还是2个
colorInterval = 1;
//第一个二值区间
colorOneStart = 0;
colorOneEnd = 0;
//第二个二值区间
colorTwoStart = 0;
colorTwoEnd = 0;
//第三个二值区间
colorThreeStart = 0;
colorThreeEnd = 0;
//处理细节
//删除边界对象
bool deleteBoundaryObject = false;
//孔洞填充
bool holeFilling = false;
//碎屑删除
bool debrisRemoval = false;
//碎屑删除面积起
debrisAreaStart = 0;
//碎屑删除面积止
debrisAreaEnd = 0;
//二值样式
//1实心 2边线
int binaryStyle = 0;
//相颜色
Color phaseColor = Color.Red;
//目标选择
int targetSelection = 1;
for (int i = 0; i < lists.Count; i++)
{
Args args = lists[i];
switch (args.Key)
{
case "colorInterval":
{
if (args.Value is Boolean)
colorInterval = (Boolean)args.Value ? 2 : 1;
else
colorInterval = (int)args.Value;
}
break;
case "scope1":
colorOneStart = (int)((List)args.Value)[0];
colorOneEnd = (int)((List)args.Value)[1];
break;
case "scope2":
colorTwoStart = (int)((List)args.Value)[0];
colorTwoEnd = (int)((List)args.Value)[1];
break;
case "scope3":
colorThreeStart = (int)((List)args.Value)[0];
colorThreeEnd = (int)((List)args.Value)[1];
break;
case "deleteBoundaryObject":
deleteBoundaryObject = (bool)args.Value;
break;
case "holeFilling":
holeFilling = (bool)args.Value;
break;
case "debrisRemoval":
debrisRemoval = (bool)args.Value;
break;
case "binaryStyle":
binaryStyle = (int)args.Value;
break;
case "phaseColor":
phaseColor = Color.FromArgb((int)args.Value);
break;
case "targetSelection":
targetSelection = (int)args.Value;
break;
case "scope4":
debrisAreaStart = (int)((List)args.Value)[0];
debrisAreaEnd = (int)((List)args.Value)[1];
break;
default:
break;
}
}
vec4B.Item0 = phaseColor.B;
vec4B.Item1 = phaseColor.G;
vec4B.Item2 = phaseColor.R;
vec4B.Item3 = phaseColor.A;
//二值提取
Mat src = BinarizationWithView(source);
//目标选择
if (targetSelection != 2)
src = CalcContoursByTargetSelection_New(temp_view, targetSelection, source);
//碎屑删除,参考冈萨雷斯,414,9.5.3,连通分量提取
if (debrisRemoval && debrisAreaEnd > 0)
src = DebrisRemoval_NewWithView(src);
//孔洞填充,参考冈萨雷斯,413,9.5.2
if (holeFilling)
src = HoleFilling_NewWithView(src);
//实心/边线,参考冈萨雷斯,412,9.5.1,边界提取
if (binaryStyle == 2)
{
Cv2.Erode(src, temp, null);
src = src - temp;
src = BaseTools.MergeMatFromMatArr(src, vec4B);
}
return src;
}
///
/// 二值提取
///
/// 视场图像
/// 原图像
/// 1个或2个颜色区间
///
///
///
///
///
///
/// 相颜色
///
private static Mat BinarizationWithView(Mat source)
{
Mat[] arr = source.Split();
alpha = arr[3];
Mat srcGray;
if (source.Type() == MatType.CV_8UC1)
{
srcGray = source;
}
else
{
srcGray = source.CvtColor(ColorConversionCodes.BGR2GRAY);
}
temp_view = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
temp = new Mat(source.Rows, source.Cols, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
srcGray.ForEachAsByte(GrayForEachAsByteWithView);
return temp.Clone();
}
///
/// 并行循环
///
///
///
private static void GrayForEachAsByteWithView(byte* value, int* position)
{
int y = position[0];
int x = position[1];
byte v = *value;
if (colorInterval == 1)
{
if (v >= colorOneStart && v <= colorOneEnd)
{
if(alpha.At(y, x) > 0)
{
temp.Set(y, x, vec4B);
temp_view.Set(y, x, vec4B);
}
else
{
temp_view.Set(y, x, new Vec4b(vec4B.Item0, vec4B.Item1, vec4B.Item2,0));
}
}
}
else
{
if (((v >= colorTwoStart && v <= colorTwoEnd) || (v >= colorThreeStart && v <= colorThreeEnd)))
{
if (alpha.At(y, x) > 0)
{
temp.Set(y, x, vec4B);
temp_view.Set(y, x, vec4B);
}
else
{
temp_view.Set(y, x, new Vec4b(vec4B.Item0, vec4B.Item1, vec4B.Item2, 0));
}
}
}
}
///
/// 新碎屑删除,使用连通分量找寻碎屑
///
///
///
private static Mat DebrisRemoval_NewWithView(Mat src)
{
Mat[] arr = src.Split();
rgb_ap = BaseTools.MergeMatFromMatArr(arr);
temp_ap = arr[3].Clone();
//寻找连通分量
Mat labelMat = new Mat();
Mat stats = new Mat();
Mat centroids = new Mat();
int nonenum = Cv2.ConnectedComponentsWithStats(temp_ap, labelMat, stats, centroids, PixelConnectivity.Connectivity8);
//寻找在范围内需要删除的碎屑
keyValuePairs.Clear();
for (int h = 1; h < centroids.Height; h++)
{
int areaField1 = stats.At(h, 4);
if (debrisAreaStart <= areaField1 && areaField1 <= debrisAreaEnd)
{
keyValuePairs.Add(h, 1);
}
}
labelMat.ForEachAsInt32(CommonForEachForInt32);
//合并rgb和透明度
Mat[] mats = rgb_ap.Split();
arr[0] = mats[0];
arr[1] = mats[1];
arr[2] = mats[2];
arr[3] = temp_ap;
Cv2.Merge(arr, src);
return src;
}
///
/// 新孔洞填充,采用形态学填充
///
///
///
private static Mat HoleFilling_NewWithView(Mat src)
{
Mat[] arr2 = src.Split();
Mat tempc = arr2[3];
//去掉透明层
Mat mat = src.CvtColor(ColorConversionCodes.BGRA2BGR);
//填充孔洞
mat = BaseTools.FillHole(mat, new Scalar(255 - vec4B.Item0, 255 - vec4B.Item1, 255 - vec4B.Item2));
tempc = BaseTools.FillHole(tempc, new Scalar(255));
//循环处理
mat.ForEachAsVec3b(RGBForEachAsByteForHoleFillingNewWithView);
Mat[] arr1 = mat.Split();
arr2[0] = arr1[0];
arr2[1] = arr1[1];
arr2[2] = arr1[2];
arr2[3] = tempc;
Cv2.Merge(arr2, src);
return src;
}
private static void RGBForEachAsByteForHoleFillingNewWithView(Vec3b* value, int* position)
{
int y = position[0];
int x = position[1];
if (value->Item0 == 255 && value->Item1 == 255 && value->Item2 == 255)
{
value->Item0 = vec4B.Item0;
value->Item1 = vec4B.Item1;
value->Item2 = vec4B.Item2;
}
}
///
/// 目标选择
///
///
///
///
///
///
public static Mat CalcContoursByTargetSelection_New(Mat src, int targetSelection, Mat source)
{
Mat[] arr = src.Split();
alpha = arr[3].Clone();
Mat rgb = BaseTools.MergeMatFromMatArr(arr);
Mat alphatemp = arr[3].Clone();
Mat mask = Mat.Zeros(rgb.Rows + 2, rgb.Cols + 2, MatType.CV_8UC1);
//考虑循环填充,填充视场的透明层和原图的透明层,然后比较填充区域的大小
//但是循环的时候需要少循环像素,可以处理视场的透明层,减少循环量
//同时记录种子点,然后根据targetSelection判断是填充透明还是不透明
Mat rgbClone = rgb.Clone();
List points = new List();
for(int h=0; h< alphatemp.Height; h++)
{
for (int w = 0; w < alphatemp.Width; w++)
{
int v = alphatemp.At(h, w);
if(v>0)
{
int a = Cv2.FloodFill(alphatemp, new OpenCvSharp.Point(w, h), new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
int b = Cv2.FloodFill(rgbClone, new OpenCvSharp.Point(w, h), new Scalar(0, 0, 0), out rect, null, null, FloodFillFlags.Link8);
if(a !=b)
{
points.Add(new OpenCvSharp.Point(w, h));
}
}
}
}
if(points.Count>0)
{
foreach(OpenCvSharp.Point p in points)
{
if(targetSelection == 1)
{
Cv2.FloodFill(alpha, p, new Scalar(0), out rect, null, null, FloodFillFlags.Link8);
}
else if(targetSelection == 3)
{
Cv2.FloodFill(rgb, mask, p, new Scalar(vec4B.Item0, vec4B.Item1, vec4B.Item2), out rect, null, null, FloodFillFlags.Link8);
}
}
}
if (targetSelection == 3)
{
Mat t = new Mat(mask, new Rect(1, 1, rgb.Width, rgb.Height));
alpha = alpha + t * 255;
}
//合并rgb和透明度
Mat[] mats = rgb.Split();
arr[0] = mats[0];
arr[1] = mats[1];
arr[2] = mats[2];
arr[3] = alpha;
Cv2.Merge(arr, src);
return src;
}
#endregion
private static void CommonForEachForInt32(int* value, int* position)
{
int y = position[0];
int x = position[1];
int v = *value;
if (v > 0 && keyValuePairs.ContainsKey(v))
{
rgb_ap.Set(y, x, new Vec3b(0, 0, 0));
temp_ap.Set(y, x, 0);
}
}
}
}