public unsafe ImageU8 ToImageU8() { ImageU8 img = new ImageU8(this.Width, this.Height); Grad * start = this.Start; Grad * end = start + this.Length; float max = 0; // 找最大值 Grad *src = start; while (src != end) { max = Math.Max(max, src->Value); src++; } float coeff = max > 255 ? 255f / max : 0; src = start; Byte *dst = img.Start; while (src != end) { *dst = (Byte)(coeff * src->Value); dst++; src++; } return(img); }
public override IImage Clone() { ImageU8 img = new ImageU8(this.Width, this.Height); img.CloneFrom(this); return(img); }
public unsafe ImageU8 ToGrayscaleImage(double rCoeff, double gCoeff, double bCoeff) { ImageU8 img = new ImageU8(this.Width, this.Height); Argb32 *p = Start; Byte * to = img.Start; Argb32 *end = p + Length; if (Length < 1024) { while (p != end) { *to = (Byte)(p->Red * rCoeff + p->Green * gCoeff + p->Blue * bCoeff); p++; to++; } } else { int *bCache = stackalloc int[256]; int *gCache = stackalloc int[256]; int *rCache = stackalloc int[256]; const int shift = 1 << 10; int rShift = (int)(rCoeff * shift); int gShift = (int)(gCoeff * shift); int bShift = shift - rShift - gShift; int r = 0, g = 0, b = 0; for (int i = 0; i < 256; i++) { bCache[i] = b; gCache[i] = g; rCache[i] = r; b += bShift; g += gShift; r += rShift; } while (p != end) { *to = (Byte)((bCache[p->Red] + gCache[p->Green] + rCache[p->Red]) >> 10); p++; to++; } } return(img); }
public unsafe ImageU8 ToImageU8() { ImageU8 imgU8 = new ImageU8(this.Width, this.Height); int length = imgU8.Length; Int32 * start = this.Start; Byte * dst = imgU8.Start; Int32 * end = start + this.Length; while (start != end) { int val = *start; * dst = val < 0 ? (Byte)0 : (val > 255 ? (Byte)255 : (Byte)val); start++; dst++; } return(imgU8); }
public unsafe ImageU8 CopyChannel(int channel) { if (channel < 0 && channel > 2) { throw new ArgumentOutOfRangeException("channel"); } int length = this.Length; Byte * start = (Byte *)this.StartIntPtr; int size = sizeof(Rgb24); Byte * end = start + sizeof(Rgb24) * length; ImageU8 imgU8 = new ImageU8(this.Width, this.Height); Byte * dst = imgU8.Start; while (start != end) { *dst = start[channel]; start += size; dst++; } return(imgU8); }
/// <summary> /// 进行中值滤波。 /// </summary> /// <param name="medianRadius"> /// 中值滤波的核半径,不得小于1. /// </param> public unsafe void ApplyMedianFilter(int medianRadius) { if (medianRadius > 0) { // 进行中值滤波 using (ImageU8 copy = this.Clone() as ImageU8) { int size = medianRadius * 2 + 1; int count = 0; byte[] data = new byte[size * size]; int height = this.Height; int width = this.Width; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { count = 0; for (int h = -medianRadius; h <= medianRadius; h++) { for (int w = -medianRadius; w <= medianRadius; w++) { int hh = y + h; int ww = x + w; if (hh >= 0 && hh < height && ww >= 0 && ww < width) { data[count] = copy[hh, ww]; count++; } } } Array.Sort(data, 0, count); int m = count >> 1; byte median = data[m]; this[y, x] = median; } } } } }
/// <summary> /// 使用 hilditch 算法进行细化 /// </summary> public unsafe void Thinning(Byte foreground = 255) { Byte * start = this.Start; Int32 width = this.Width; Int32 height = this.Height; Int32 *list = stackalloc Int32[8]; Byte background = (Byte)(255 - foreground); Int32 length = this.Length; using (ImageU8 mask = new ImageU8(this.Width, this.Height)) { mask.Fill(0); Boolean loop = true; while (loop == true) { loop = false; for (Int32 r = 1; r < height - 1; r++) { for (Int32 c = 1; c < width - 1; c++) { Byte *p = start + r * width + c; // 条件1:p 必须是前景点 if (*p != foreground) { continue; } // p3 p2 p1 // p4 p p0 // p5 p6 p7 // list 存储的是补集,即前景点为0,背景点为1,以方便联结数的计算 FillNeighbors(p, list, width, foreground); // 条件2:p0,p2,p4,p6 不皆为前景点 if (list[0] == 0 && list[2] == 0 && list[4] == 0 && list[6] == 0) { continue; } // 条件3: p0~p7至少两个是前景点 Int32 count = 0; for (int i = 0; i < 8; i++) { count += list[i]; } if (count > 6) { continue; } // 条件4:联结数等于1 if (DetectConnectivity(list) != 1) { continue; } // 条件5: 假设p2已标记删除,则令p2为背景,不改变p的联结数 if (mask[r - 1, c] == 1) { list[2] = 1; if (DetectConnectivity(list) != 1) { continue; } list[2] = 0; } // 条件6: 假设p4已标记删除,则令p4为背景,不改变p的联结数 if (mask[r, c - 1] == 1) { list[4] = 1; if (DetectConnectivity(list) != 1) { continue; } } mask[r, c] = 1; // 标记删除 loop = true; } } for (int i = 0; i < length; i++) { if (mask[i] == 1) { this[i] = background; } } } } }
public unsafe void ApplyCannyEdgeDetector(double gaussianSiama = 1.4, int gaussianSize = 5, byte lowThreshold = 20, byte highThreshold = 100) { ImageU8 copy = this.Clone() as ImageU8; int startX = 1; int startY = 1; int width = this.Width; int height = this.Height; int stopX = width - 1; int stopY = height - 1; int ww = width - 2; int hh = height - 2; // orientation array byte[] orients = new byte[ww * hh]; // gradients array float[,] gradients = new float[this.Width, this.Height]; float maxGradient = float.NegativeInfinity; double gx, gy; double orientation, toAngle = 180.0 / System.Math.PI; float leftPixel = 0, rightPixel = 0; // 第一步,Gauss 平滑 copy.ApplyGaussianBlur(gaussianSiama, gaussianSize); byte *start = copy.Start + startX; int o = 0; for (int y = startY; y < stopY; y++) { byte *line = start + y * width; byte *p = line; for (int x = startX; x < stopX; x++, p++, o++) { gx = p[-width + 1] + p[width + 1] - p[-width - 1] - p[width - 1] + 2 * (p[1] - p[-1]); gy = p[-width - 1] + p[-width + 1] - p[width - 1] - p[width + 1] + 2 * (p[-width] - p[width]); gradients[x, y] = (float)Math.Sqrt(gx * gx + gy * gy); if (gradients[x, y] > maxGradient) { maxGradient = gradients[x, y]; } // get orientation if (gx == 0) { orientation = (gy == 0) ? 0 : 90; } else { double div = gy / gx; // handle angles of the 2nd and 4th quads if (div < 0) { orientation = 180 - System.Math.Atan(-div) * toAngle; } // handle angles of the 1st and 3rd quads else { orientation = System.Math.Atan(div) * toAngle; } // get closest angle from 0, 45, 90, 135 set if (orientation < 22.5) { orientation = 0; } else if (orientation < 67.5) { orientation = 45; } else if (orientation < 112.5) { orientation = 90; } else if (orientation < 157.5) { orientation = 135; } else { orientation = 0; } } // save orientation orients[o] = (byte)orientation; } } // STEP 3 - suppres non maximums o = 0; start = this.Start + startX; for (int y = startY; y < stopY; y++) { byte *line = start + y * width; byte *p = line; // for each pixel for (int x = startX; x < stopX; x++, p++, o++) { // get two adjacent pixels switch (orients[o]) { case 0: leftPixel = gradients[x - 1, y]; rightPixel = gradients[x + 1, y]; break; case 45: leftPixel = gradients[x - 1, y + 1]; rightPixel = gradients[x + 1, y - 1]; break; case 90: leftPixel = gradients[x, y + 1]; rightPixel = gradients[x, y - 1]; break; case 135: leftPixel = gradients[x + 1, y + 1]; rightPixel = gradients[x - 1, y - 1]; break; } // compare current pixels value with adjacent pixels if ((gradients[x, y] < leftPixel) || (gradients[x, y] < rightPixel)) { *p = 0; } else { *p = (byte)(gradients[x, y] / maxGradient * 255); } } } // STEP 4 - hysteresis start = this.Start + startX; for (int y = startY; y < stopY; y++) { byte *line = start + y * width; byte *p = line; for (int x = startX; x < stopX; x++, p++) { if (*p < highThreshold) { if (*p < lowThreshold) { // non edge *p = 0; } else { // check 8 neighboring pixels if ((p[-1] < highThreshold) && (p[1] < highThreshold) && (p[-width - 1] < highThreshold) && (p[-width] < highThreshold) && (p[-width + 1] < highThreshold) && (p[width - 1] < highThreshold) && (p[width] < highThreshold) && (p[width + 1] < highThreshold)) { *p = 0; } } } } } // STEP 4 将第1行,最后一行,第0列,最后1列 // 第1行 start = this.Start; byte *end = start + width; while (start != end) { *start = 0; start++; } // 最后一行 start = this.Start + width * height - width; end = start + width; while (start != end) { *start = 0; start++; } // 第一列和最后一列 start = this.Start; for (int y = 0; y < height; y++, start += width) { start[0] = 0; start[width - 1] = 0; } }
public unsafe void SkeletonizeByMidPoint(Byte foreground = 255) { using (ImageU8 mask = new ImageU8(this.Width, this.Height)) { mask.Fill(0); Int32 width = this.Width; Int32 height = this.Height; for (Int32 r = 0; r < height; r++) { Int32 lineStart = -1; for (Int32 c = 0; c < width; c++) { if (this[r, c] == foreground) { if (lineStart == -1) { lineStart = c; } } else { if (lineStart != -1) { mask[r, (lineStart + c) / 2] = 1; lineStart = -1; } } } } for (Int32 c = 0; c < width; c++) { Int32 lineStart = -1; for (Int32 r = 0; r < height; r++) { if (this[r, c] == foreground) { if (lineStart == -1) { lineStart = r; } } else { if (lineStart != -1) { mask[(lineStart + r) / 2, c] = 1; lineStart = -1; } } } } Byte bg = (Byte)(255 - foreground); Int32 length = this.Length; for (int i = 0; i < length; i++) { if (mask[i] == 1) { this[i] = foreground; } else { this[i] = bg; } } } }