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; } } }