using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PaintDotNet.Adjust.BaseImage
{
///
/// 图像处理基本工具
///
public static class BaseTools
{
public static int[] table =
//0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1
{0,0,0,0,0,0,1,3,0,0,3,1,1,0,1,3,0,0,0,0,0,0,0,0,0,0,2,0,3,0,3,3,
0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,3,0,2,2,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,3,0,2,0,
0,0,3,1,0,0,1,3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,3,1,3,0,0,1,3,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,3,0,1,0,0,0,1,0,0,0,0,0,0,0,0,3,3,0,1,0,0,0,0,2,2,0,0,2,0,0,0};
// 2013/12/02: 16,6 2->0
// 2013/12/02: 24,5 0->2
public static int[] table2 =
//0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,2,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
///
/// 创建一个高斯滤波核
///
/// 半径
/// kernel
public static Kernel CreateKernel(float radius)
{
var r = (int)Math.Ceiling(radius);
int rows = r * 2 + 1;
var matrix = new float[rows];
float sigma = radius / 3;
float sigma22 = 2 * sigma * sigma;
var sigmaPi2 = (float)(2 * Math.PI * sigma);
var sqrtSigmaPi2 = (float)Math.Sqrt(sigmaPi2);
float radius2 = radius * radius;
float total = 0;
int index = 0;
for (int row = -r; row <= r; row++)
{
float distance = row * row;
if (distance > radius2)
matrix[index] = 0;
else
matrix[index] = (float)Math.Exp(-(distance) / sigma22) / sqrtSigmaPi2;
total += matrix[index];
index++;
}
for (int i = 0; i < rows; i++)
{
matrix[i] /= total;
}
return new Kernel(rows, 1, matrix);
}
///
/// Blur and transpose a block of ARGB pixels.
///
///
///
///
///
///
///
///
///
///
public static void ConvolveAndTranspose(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, bool alpha, bool premultiply, bool unpremultiply, int edgeAction)
{
float[] matrix = kernel.GetKernel();
int cols = kernel.Width;
int cols2 = cols / 2;
for (int y = 0; y < height; y++)
{
int index = y;
int ioffset = y * width;
for (int x = 0; x < width; x++)
{
float r = 0, g = 0, b = 0, a = 0;
int moffset = cols2;
for (int col = -cols2; col <= cols2; col++)
{
float f = matrix[moffset + col];
if (f != 0)
{
int ix = x + col;
if (ix < 0)
{
if (edgeAction == 1)
ix = 0;
else if (edgeAction == 2)
ix = (x + width) % width;
}
else if (ix >= width)
{
if (edgeAction == 1)
ix = width - 1;
else if (edgeAction == 2)
ix = (x + width) % width;
}
int rgb = inPixels[ioffset + ix];
int pa = (rgb >> 24) & 0xff;
int pr = (rgb >> 16) & 0xff;
int pg = (rgb >> 8) & 0xff;
int pb = rgb & 0xff;
if (premultiply)
{
float a255 = pa * (1.0f / 255.0f);
pr = (int)(pr * a255);
pg = (int)(pg * a255);
pb = (int)(pb * a255);
}
a += f * pa;
r += f * pr;
g += f * pg;
b += f * pb;
}
}
if (unpremultiply && a != 0 && a != 255)
{
float f = 255.0f / a;
r *= f;
g *= f;
b *= f;
}
int ff = (int)(a + 0.5);
if (ff > 255) ff = 255;
if (ff < 0) ff = 0;
int ia = alpha ? ff : 0xff;
int ir = (int)(r + 0.5);
if (ir > 255) ir = 255;
if (ir < 0) ir = 0;
int ig = (int)(g + 0.5);
if (ig > 255) ig = 255;
if (ig < 0) ig = 0;
int ib = (int)(b + 0.5);
if (ib > 255) ib = 255;
if (ib < 0) ib = 0;
outPixels[index] = (ia << 24) | (ir << 16) | (ig << 8) | ib;
index += height;
}
}
}
///
/// 参考https://blog.csdn.net/ls9512/article/details/50001753
/// RgbToHsv
///
///
///
///
///
public static void RgbToHsv(Vec3b prgb, out double H, out double S, out double V)
{
float min, max, tmp;
float R = prgb[2] * 1.0f / 255, G = prgb[1] * 1.0f / 255, B = prgb[0] * 1.0f / 255;
tmp = Math.Min(R, G);
min = Math.Min(tmp, B);
tmp = Math.Max(R, G);
max = Math.Max(tmp, B);
// H
H = 0;
if (max == min)
{
H = 0;
}
else if (max == R && G > B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 0;
}
else if (max == R && G < B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 360;
}
else if (max == G)
{
H = H = 60 * (B - R) * 1.0f / (max - min) + 120;
}
else if (max == B)
{
H = H = 60 * (R - G) * 1.0f / (max - min) + 240;
}
// S
if (max == 0)
{
S = 0;
}
else
{
S = (max - min) * 1.0f / max;
}
// V
V = max;
//return new ColorHSV((int)H, (int)(S * 255), (int)(V * 255));
}
public static void RGBtoHLS(Vec3b prgb, out double H, out double L, out double S)
{
double n_cmax = Math.Max(prgb[0], Math.Max(prgb[1], prgb[2]));
double n_cmin = Math.Min(prgb[0], Math.Min(prgb[1], prgb[2]));
L = (n_cmax + n_cmin) / 2.0 / 255.0;
if (n_cmax == n_cmin)
{
S = H = 0.0;
return;
}
double r = prgb[2] / 255.0;
double g = prgb[1] / 255.0;
double b = prgb[0] / 255.0;
double cmax = n_cmax / 255.0;
double cmin = n_cmin / 255.0;
double delta = cmax - cmin;
if (L < 0.5)
S = delta / (cmax + cmin);
else
S = delta / (2.0 - cmax - cmin);
if (prgb[2] == n_cmax)
H = (g - b) / delta;
else if (prgb[1] == n_cmax)
H = 2.0 + (b - r) / delta;
else
H = 4.0 + (r - g) / delta;
H /= 6.0;
if (H < 0.0)
H += 1.0;
}
public static Vec3b HLStoRGB(double H, double L, double S)
{
if (S < 1.19209290E-07F)//== 0
{
Vec3b r = new Vec3b();
r[0] = (byte)(L * 255);
r[1] = (byte)(L * 255);
r[2] = (byte)(L * 255);
return r;
}
double m1, m2;
if (L < 0.5)
m2 = L * (1.0 + S);
else
m2 = L + S - L * S;
m1 = 2.0 * L - m2;
Vec3b r1 = new Vec3b();
r1[2] = (byte)(__HLS_Value(m1, m2, H * 6.0 + 2.0) * 255);
if (r1[2] < 0) r1[2] = 0; if (r1[2] > 255) r1[2] = 255;
r1[1] = (byte)(__HLS_Value(m1, m2, H * 6.0) * 255);
if (r1[1] < 0) r1[1] = 0; if (r1[1] > 255) r1[1] = 255;
r1[0] = (byte)(__HLS_Value(m1, m2, H * 6.0 - 2.0) * 255);
if (r1[0] < 0) r1[0] = 0; if (r1[0] > 255) r1[0] = 255;
return r1;
}
public static double __HLS_Value(double m1, double m2, double h)
{
if (h > 6.0)
h -= 6.0;
else if (h < 0.0)
h += 6.0;
if (h < 1.0)
return m1 + (m2 - m1) * h;
else if (h < 3.0)
return m2;
else if (h < 4.0)
return m1 + (m2 - m1) * (4.0 - h);
return m1;
}
///
/// Bitmap转int数组
///
///
///
public static int[] ByteArray(Bitmap map)
{
var data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var length = map.Width * map.Height * 4;
byte[] _byteArray = new byte[length];
if (data.Stride == map.Width * 4)
{
Marshal.Copy(data.Scan0, _byteArray, 0, length);
}
else
{
for (int i = 0, l = map.Height; i < l; i++)
{
var p = new IntPtr(data.Scan0.ToInt32() + data.Stride * i);
Marshal.Copy(p, _byteArray, i * map.Width * 4, map.Width * 4);
}
}
map.UnlockBits(data);
var intArray = new int[_byteArray.Length / 4];
for (int i = 0; i < _byteArray.Length; i += 4)
{
intArray[i / 4] = BitConverter.ToInt32(_byteArray, i);
}
return intArray;
}
///
/// 【可以优化速度】判断是否是黑/白二值图像
///
///
///
public static bool DetermineBinaryImage(Mat 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!=255 && v != 0)
{
return false;
}
}
}
return true;
}
///
/// 根据图像的直方图判断是否是二个颜色的图
///
///
///
public static bool DetermineBinaryImageByHist(Mat mat)
{
//根据灰度图的直方图计算是否是二值图
Mat[] mats0 = new Mat[] { mat };
int[] channels0 = new int[] { 0 };
int[] histsize = new int[] { 256 };//一个通道,初始化为256箱子
Rangef[] range = new Rangef[1];
range[0] = new Rangef(0.0F, 256.0F);
Mat hist = new Mat();
Cv2.CalcHist(mats0, channels0, new Mat(), hist, 1, histsize, range);
int value = 0;
for (int i = 0; i < 256; i++)
{
//取每个bin的数目
int temp = (int)(hist.At(i, 0));
if (temp > 0)
value++;
}
return value == 2;
}
///
/// 收缩图像
///
///
///
///
///
public static Mat shrink(Mat ip, Mat ip2, bool hasEdgePixels)
{
if (hasEdgePixels)
{
int width = ip.Width;
int height = ip.Height;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
ip.Set(y, x, ip2.At(y + 1, x + 1));
}
return ip;
}
///
/// 阴影校正功能里面,用来进行行列处理的方法
///
///
///
///
public static Mat ImageSmoothIIR(Mat I_OUT, double threshold)
{
/*int M = I_OUT.Height;
int N = I_OUT.Width;
for (int P = 1; P < M; P++)
{
I_OUT.Row[P] = threshold * 1.0f * (I_OUT.Row[P - 1] - I_OUT.Row[P]) + I_OUT.Row[P];
}
for (int P = M - 2; P > -1; P--)
{
I_OUT.Row[P] = threshold * 1.0f * (I_OUT.Row[P + 1] - I_OUT.Row[P]) + I_OUT.Row[P];
}
for (int L = 1; L < N; L++)
{
I_OUT.Col[L] = threshold * 1.0f * (I_OUT.Col[L - 1] - I_OUT.Col[L]) + I_OUT.Col[L];
}
for (int L = N - 2; L > -1; L--)
{
I_OUT.Col[L] = threshold * 1.0f * (I_OUT.Col[L + 1] - I_OUT.Col[L]) + I_OUT.Col[L];
}
return I_OUT;*/
Mat temp = null;
int M = I_OUT.Height;
int N = I_OUT.Width;
for (int P = 1; P < M; P++)
{
temp = (threshold * 1.0f * (I_OUT.Row(P - 1) - I_OUT.Row(P)) + I_OUT.Row(P));
temp.CopyTo(I_OUT.Row(P));
}
for (int P = M - 2; P > -1; P--)
{
temp = (threshold * 1.0f * (I_OUT.Row(P + 1) - I_OUT.Row(P)) + I_OUT.Row(P));
temp.CopyTo(I_OUT.Row(P));
}
for (int L = 1; L < N - 1; L++)
{
temp = (threshold * 1.0f * (I_OUT.Col(L - 1) - I_OUT.Col(L)) + I_OUT.Col(L));
temp.CopyTo(I_OUT.Col(L));
}
for (int L = N - 2; L > -1; L--)
{
temp = (threshold * 1.0f * (I_OUT.Col(L + 1) - I_OUT.Col(L)) + I_OUT.Col(L));
temp.CopyTo(I_OUT.Col(L));
}
return I_OUT;
}
///
/// 引射线法判断点是否在多边形内部(以该点为起点沿x轴负方向做一条射线)
///
///
///
///
public static bool isPointInPolygon(System.Drawing.Point p, List vPoint)
{
int i, j = vPoint.Count - 1;
bool inside = false;
double x = p.X;
double y = p.Y;
for (i = 0; i < vPoint.Count; i++)
{
if (((vPoint[i].Y < y && vPoint[j].Y >= y) || (vPoint[i].Y >= y && vPoint[j].Y < y)) && (vPoint[i].X <= x || vPoint[j].X <= x))
{
double dTmp = vPoint[i].X + (y - vPoint[i].Y) / (vPoint[j].Y - vPoint[i].Y) * (vPoint[j].X - vPoint[i].X);
bool bTmp;
if (dTmp == x) //点在多边形的边上,也算在多边形内
{
//count = 1;
return true;
}
else
bTmp = (dTmp < x);
inside ^= bTmp;
//if (bTmp)
//count++;
}
j = i;
}
return inside;
}
///
/// 判断点是否在椭圆内
///
///
///
///
public static bool isPointInOval(System.Drawing.Point point, System.Drawing.PointF centerPoint, float xaxis, float yaxis)
{
double v = Math.Pow(centerPoint.X - point.X, 2) / Math.Pow(xaxis, 2) + Math.Pow(centerPoint.Y - point.Y, 2) / Math.Pow(yaxis, 2);
return v < 1;
}
///
/// System.Drawing.Point转OpenCvSharp.Point
///
///
///
///
public static List pointToPoint(List points)
{
List points1 = new List();
foreach(System.Drawing.PointF point in points)
{
points1.Add(new OpenCvSharp.Point(point.X, point.Y));
}
return points1;
}
///
/// 判断mat是否相同
///
///
///
///
public static bool matEqual(Mat a, Mat b) {
Mat temp = new Mat();
Cv2.BitwiseXor(a, b, temp);
Mat[] mats = temp.Split();
int a1 = Cv2.CountNonZero(mats[0]);
int a2 = Cv2.CountNonZero(mats[1]);
int a3 = Cv2.CountNonZero(mats[2]);
int a4 = Cv2.CountNonZero(mats[3]);
return (a1+ a2+ a3+ a4) ==0;
}
///
/// 图像旋转
///
///
///
///
///
public static void ImageRotate(Mat src, Mat dst, float angle, InterpolationFlags flags)
{
Mat dst1 = new Mat();
Point2f center = new Point2f(src.Cols / 2, src.Rows / 2);
Mat rot = Cv2.GetRotationMatrix2D(center, -angle, 1);
Size2f s2f = new Size2f(src.Size().Width, src.Size().Height);
Rect box = new RotatedRect(new Point2f(0, 0), s2f, -angle).BoundingRect();
double xx = rot.At(0, 2) + box.Width / 2 - src.Cols / 2;
double zz = rot.At(1, 2) + box.Height / 2 - src.Rows / 2;
rot.Set(0, 2, xx);
rot.Set(1, 2, zz);
//对图片进行仿射变换
Cv2.WarpAffine(src, dst1, rot, box.Size, flags);
dst1.CopyTo(dst);
}
///
/// 获取不相近的随机颜色
///
public static Color GetRandomColor(Color color)
{
double H, L, S;
Vec3b vec3B = new Vec3b();
vec3B.Item0 = color.B;
vec3B.Item1 = color.G;
vec3B.Item1 = color.R;
RGBtoHLS(vec3B, out H, out L, out S);
double H1=0, L1=0, S1=0;
for (var i = 0; i < 1; i++)
{
double[] ret = RandomHsl();
// 颜色相邻颜色差异须大于 0.25
if (i > 0 && Math.Abs(ret[0] - H) < 0.25)
{
i--;
continue; // 重新获取随机色
}
ret[1] = 0.7 + (ret[1] * 0.2); // [0.7 - 0.9] 排除过灰颜色
ret[2] = 0.4 + (ret[2] * 0.4); // [0.4 - 0.8] 排除过亮过暗色
H1 = ret[0];
S1 = ret[1];
L1 = ret[2];
}
Vec3b v = HLStoRGB(H1, L1, S1);
return Color.FromArgb(v.Item2, v.Item1, v.Item0);
}
public static double[] RandomHsl()
{
Random random = new Random();
double[] hsl = new double[3];
var H = random.NextDouble();
var S = random.NextDouble();
var L = random.NextDouble();
hsl[0] = H;
hsl[1] = S;
hsl[2] = L;
return hsl;
}
///
/// 将bitmap转透明,需要四个通道的bitmap
///
///
///
public static Bitmap BitmapToTransparent(Bitmap bitmap)
{
OpenCvSharp.Mat mat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap);
if(mat.Type() == MatType.CV_8UC4)
{
Mat[] mats = mat.Split();
mats[3].SetTo(new Scalar(0));
Cv2.Merge(mats, mat);
return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);
}
return bitmap;
}
///
/// 形态学孔洞填充
///
/// 二值图0/255
///
public static Mat FillHole(Mat srcBw, Scalar scalar)
{
OpenCvSharp.Size m_Size = srcBw.Size();
//创建扩展边界的图像
Mat temp = Mat.Zeros(m_Size.Height + 2, m_Size.Width + 2, srcBw.Type());
srcBw.CopyTo(new Mat(temp, new Range(1, m_Size.Height + 1), new Range(1, m_Size.Width + 1)));
//new OpenCvSharp.Point(0, 0)
Cv2.FloodFill(temp, new OpenCvSharp.Point(0, 0), scalar);
//裁剪扩展边界的图像
Mat cutImg = new Mat();
new Mat(temp, new Range(1, m_Size.Height + 1), new Range(1, m_Size.Width + 1)).CopyTo(cutImg);
return srcBw | (~cutImg);
}
///
/// 返回合并后的图片
/// 主要适用于四通道合并为三通道
///
///
///
public static Mat MergeMatFromMatArr(Mat[] arr)
{
Mat[] mats = new Mat[3];
mats[0] = arr[0];
mats[1] = arr[1];
mats[2] = arr[2];
Mat dst = new Mat();
Cv2.Merge(mats, dst);
return dst;
}
///
/// 形态学提取连通分量
///
/// 需要0/1的二值图
/// true 8连通 / false 4连通
/// 各个连通区域的种子点的坐标集合
/// 各个连通区域的像素个数集合
public static void MorphologExConnComponents(Mat src, bool iseight, out List points, out List nums)
{
points = new List();
nums = new List();
Mat structelement;
if (iseight)
structelement = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3));
else
structelement = Cv2.GetStructuringElement(MorphShapes.Cross, new OpenCvSharp.Size(3, 3));
//dst = Mat.Ones(src.Size(), src.Type());
Mat tmp = Mat.Ones(src.Size(), src.Type()); // save last reuslt image
//Mat img = Mat.Ones(src.Size(), src.Type()); //image B
int labelnum = 0; //label of connected component
Mat backupsrc = new Mat();
src.CopyTo(backupsrc);
for (int i = 0; i < backupsrc.Rows; i++)
{
for (int j = 0; j < backupsrc.Cols; j++)
{
if (backupsrc.At(i, j) == 255)
{
Mat img = Mat.Ones(src.Size(), src.Type());
img.CopyTo(tmp); img.Set(i, j, 255);
labelnum++;
while (true)
{
Cv2.Dilate(img, img, structelement);
Cv2.BitwiseAnd(img, src, img);
if (Cv2.CountNonZero(img - tmp) == 0)
break;
img.CopyTo(tmp);
}
//Cv2.ImShow("xx" + labelnum, img);
// int xx = Cv2.CountNonZero(img);
//System.Console.WriteLine(labelnum +"、" +i+ "、" + j +":" + xx);
int hh = 0;
//label the connected component
for (int r = 0; r < img.Rows; r++)
{
for (int c = 0; c < img.Cols; c++)
{
if (img.At(r, c) == 255)
{
hh++;
backupsrc.Set(r, c, 0);
//dst.Set(r, c, labelnum);
}
}
}
points.Add(new OpenCvSharp.Point(j, i));
nums.Add(hh);
//System.Console.WriteLine(labelnum + ":" + hh);
}
}
}
//return labelnum;
}
public static Mat MergeMatFromMatArr(Mat src, Vec4b vec4B)
{
Mat[] arr = src.Split();
Mat apa = arr[3].Clone();
apa = apa * 1/255;
arr[0] = apa * vec4B.Item0;
arr[1] = apa * vec4B.Item1;
arr[2] = apa * vec4B.Item2;
Cv2.Merge(arr, src);
return src;
}
}
}