视觉基础算法

tech2025-07-23  4

找到一篇PPT,对于基础知识点的总结比较好,需要自己去深挖知识。链接

一. 图片灰度化

灰度化有多种方式,可以根据自己的需求自己定制,这里列举三种常用的方式。

将彩色图像的R、G、B三分量的亮度之一作为灰度图像灰度值 Gray=B or Gray=G or Gray=R。最大值法:将彩×××像的R、G、B三分量亮度最大值作为灰度图像灰度值max(R,max(G,B)) 注意max函数一次只能比较两个参数;加权平均法:将彩×××像的R、G、B三分量以不同的权重进行加权平均。人眼对绿色敏感最高,对蓝色敏感最低,故采用心理学灰度公式:Gray= 0.114B+ 0.587G+ 0.299R 。即是Opencv的颜色空间转换函数 :CV_BGR2GRAY 加权平均法C#示例代码 //灰度化 public static Bitmap GetGrayImage(Bitmap image) { // 如果直接赋值的话,会改变原来的图片。 Bitmap result = image.Clone() as Bitmap; Color c = new Color(); int ret; for (int i = 0; i < image.Width; i++) { for (int j = 0; j < image.Height; j++) { c = result.GetPixel(i, j); // 计算点i,j的灰度值 ret = (int)(c.R * 0.299 + c.G * 0.587 + c.B * 0.114); result.SetPixel(i, j, Color.FromArgb(ret, ret, ret)); } } return result; }

二. 图片锐化

方法壹:将原图像与拉普拉斯模板相乘,再加上原图像。转载自 private void button13_Click(object sender, EventArgs e) //点击“锐化”控件产生锐化效果 { Bitmap myBitmap = new Bitmap(pictureBox1.Image); //建立拉普拉斯模板 int[] Laplacian = { -1, -1, -1, -1, 9, -1, -1, -1, -1 }; Color pixel; //这里注意边界的像素暂不处理,否则超出数组范围 for (int i = 1; i < myBitmap.Width - 1; i++) { for (int j = 1; j < myBitmap.Height - 1; j++) { int red = 0, green = 0, blue = 0; int index = 0; for (int col = -1; col <= 1; col++) //3*3处理 { for (int row = -1; row <= 1; row++) { pixel = myBitmap.GetPixel(i + row, j + col); red += pixel.R * Laplacian[index]; green += pixel.G * Laplacian[index]; blue += pixel.B * Laplacian[index]; index++; } } if (red > 255) red = 255; if (red < 0) red = 0; if (green > 255) green = 255; if (green < 0) green = 0; if (blue > 255) blue = 255; if (blue < 0) blue = 0; myBitmap.SetPixel(i-1, j-1, Color.FromArgb((int)red, (int)green, (int)blue)); //这里注意是i-1,j-1,否则效果很糟糕 } } pictureBox1.Image = myBitmap; } 方法贰:用当前像素减去周围八个像素的平均值得到差,得到的差值乘以锐化程度,再加原来的像素,得到新的当前像素值。在示例中,由于直接操作内存是不安全的,所以要加unsafe。(来自网络思路) /// <summary> /// 锐化处理2 /// </summary> /// <param name="b">图片</param> /// <param name="val">锐化程度,值越大程度越大</param> /// <returns></returns> public static Bitmap KiSharpen(Bitmap b, float val) { if (b == null) { return null; } int w = b.Width; int h = b.Height; try { Bitmap bmpRtn = new Bitmap(w, h, PixelFormat.Format24bppRgb); BitmapData srcData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); BitmapData dstData = bmpRtn.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); unsafe { byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pOut = (byte*)dstData.Scan0.ToPointer(); int stride = srcData.Stride; byte* p; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { //取周围9点的值。位于边缘上的点不做改变。 if (x == 0 || x == w - 1 || y == 0 || y == h - 1) { //不做 pOut[0] = pIn[0]; pOut[1] = pIn[1]; pOut[2] = pIn[2]; } else { int r1, r2, r3, r4, r5, r6, r7, r8, r0; int g1, g2, g3, g4, g5, g6, g7, g8, g0; int b1, b2, b3, b4, b5, b6, b7, b8, b0; float vR, vG, vB; //左上 p = pIn - stride - 3; r1 = p[2]; g1 = p[1]; b1 = p[0]; //正上 p = pIn - stride; r2 = p[2]; g2 = p[1]; b2 = p[0]; //右上 p = pIn - stride + 3; r3 = p[2]; g3 = p[1]; b3 = p[0]; //左侧 p = pIn - 3; r4 = p[2]; g4 = p[1]; b4 = p[0]; //右侧 p = pIn + 3; r5 = p[2]; g5 = p[1]; b5 = p[0]; //右下 p = pIn + stride - 3; r6 = p[2]; g6 = p[1]; b6 = p[0]; //正下 p = pIn + stride; r7 = p[2]; g7 = p[1]; b7 = p[0]; //右下 p = pIn + stride + 3; r8 = p[2]; g8 = p[1]; b8 = p[0]; //自己 p = pIn; r0 = p[2]; g0 = p[1]; b0 = p[0]; vR = (float)r0 - (float)(r1 + r2 + r3 + r4 + r5 + r6 + r7 + r8) / 8;//当前像素-8个的平均 vG = (float)g0 - (float)(g1 + g2 + g3 + g4 + g5 + g6 + g7 + g8) / 8; vB = (float)b0 - (float)(b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8) / 8; vR = r0 + vR * val; vG = g0 + vG * val; vB = b0 + vB * val; if (vR > 0) { vR = Math.Min(255, vR); } else { vR = Math.Max(0, vR); } if (vG > 0) { vG = Math.Min(255, vG); } else { vG = Math.Max(0, vG); } if (vB > 0) { vB = Math.Min(255, vB); } else { vB = Math.Max(0, vB); } pOut[0] = (byte)vB; pOut[1] = (byte)vG; pOut[2] = (byte)vR; } pIn += 3; pOut += 3; }// end of x pIn += srcData.Stride - w * 3; pOut += srcData.Stride - w * 3; } // end of y } b.UnlockBits(srcData); // 释放锁 bmpRtn.UnlockBits(dstData); return bmpRtn; } catch { return null; } } 方法叁:将像素从内存中拷贝出来操作完后再重新赋值,以达到对图像的快速读取和处理。(程序源自网络) public static Bitmap SharpenImage(Bitmap bmp) { int height = bmp.Height; int width = bmp.Width; Bitmap newbmp = new Bitmap(width, height); LockBitmap lbmp = new LockBitmap(bmp); LockBitmap newlbmp = new LockBitmap(newbmp); lbmp.LockBits(); newlbmp.LockBits(); Color pixel; //拉普拉斯模板 int[] Laplacian = { -1, -1, -1, -1, 9, -1, -1, -1, -1 }; for (int x = 1; x < width - 1; x++) { for (int y = 1; y < height - 1; y++) { int r = 0, g = 0, b = 0; int Index = 0; for (int col = -1; col <= 1; col++) { for (int row = -1; row <= 1; row++) { pixel = lbmp.GetPixel(x + row, y + col); r += pixel.R * Laplacian[Index]; g += pixel.G * Laplacian[Index]; b += pixel.B * Laplacian[Index]; Index++; } } //处理颜色值溢出 r = r > 255 ? 255 : r; r = r < 0 ? 0 : r; g = g > 255 ? 255 : g; g = g < 0 ? 0 : g; b = b > 255 ? 255 : b; b = b < 0 ? 0 : b; newlbmp.SetPixel(x - 1, y - 1, Color.FromArgb(r, g, b)); } } lbmp.UnlockBits(); newlbmp.UnlockBits(); return newbmp; } /// <summary> /// 快速读取像素和处理 /// </summary> public class LockBitmap { Bitmap source = null; IntPtr Iptr = IntPtr.Zero; BitmapData bitmapData = null; public byte[] Pixels { get; set; } public int Depth { get; private set; } public int Width { get; private set; } public int Height { get; private set; } public LockBitmap(Bitmap source) // 初始化,传递bitmap图片 { this.source = source; } /// <summary> /// Lock bitmap data /// </summary> public void LockBits() { try { // Get width and height of bitmap Width = source.Width; Height = source.Height; // get total locked pixels count 获取所有的像素点 int PixelCount = Width * Height; // Create rectangle to lock 画矩形,传两个坐标位置 Rectangle rect = new Rectangle(0, 0, Width, Height); // get source bitmap pixel format size 获取指定格式像素的颜色深度 // PixelFormat: (指定图像中每个像素的颜色数据的格式) Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat); // Check if bpp (Bits Per Pixel) is 8, 24, or 32 if (Depth != 8 && Depth != 24 && Depth != 32) { throw new ArgumentException("Only 8, 24 and 32 bpp images are supported."); } // Lock(锁) bitmap and return bitmap data bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, source.PixelFormat); // create byte array to copy pixel values int step = Depth / 8; Pixels = new byte[PixelCount * step]; Iptr = bitmapData.Scan0; // Copy data from pointer to array Marshal.Copy(Iptr, Pixels, 0, Pixels.Length); } catch (Exception ex) { throw ex; } } /// <summary> /// Unlock bitmap data /// </summary> public void UnlockBits() { try { // Copy data from byte array to pointer Marshal.Copy(Pixels, 0, Iptr, Pixels.Length); // Unlock bitmap data source.UnlockBits(bitmapData); } catch (Exception ex) { throw ex; } } /// <summary> /// Get the color of the specified pixel /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public Color GetPixel(int x, int y) { Color clr = Color.Empty; // Get color components count int cCount = Depth / 8; // Get start index of the specified pixel int i = ((y * Width) + x) * cCount; if (i > Pixels.Length - cCount) throw new IndexOutOfRangeException(); if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha { byte b = Pixels[i]; byte g = Pixels[i + 1]; byte r = Pixels[i + 2]; byte a = Pixels[i + 3]; // a clr = Color.FromArgb(a, r, g, b); } if (Depth == 24) // For 24 bpp get Red, Green and Blue { byte b = Pixels[i]; byte g = Pixels[i + 1]; byte r = Pixels[i + 2]; clr = Color.FromArgb(r, g, b); } if (Depth == 8) // For 8 bpp get color value (Red, Green and Blue values are the same) { byte c = Pixels[i]; clr = Color.FromArgb(c, c, c); } return clr; } /// <summary> /// Set the color of the specified pixel /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="color"></param> public void SetPixel(int x, int y, Color color) { // Get color components count int cCount = Depth / 8; // Get start index of the specified pixel int i = ((y * Width) + x) * cCount; if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha { Pixels[i] = color.B; Pixels[i + 1] = color.G; Pixels[i + 2] = color.R; Pixels[i + 3] = color.A; } if (Depth == 24) // For 24 bpp set Red, Green and Blue { Pixels[i] = color.B; Pixels[i + 1] = color.G; Pixels[i + 2] = color.R; } if (Depth == 8) // For 8 bpp set color value (Red, Green and Blue values are the same) { Pixels[i] = color.B; } } }

三. 二值化

二值化:将图片各像素按照阈值设定为255(纯白)、0(纯黑)两种状态,对于自适应阈值的设定可以按照自己的需求进行修改。(示例来源网络,自己添加注释,如有错误请留言) public static Bitmap DoThresholdings1(Bitmap img1, int threshold) { int w = img1.Width; int h = img1.Height; Bitmap dstBitmap = new Bitmap(w, h, PixelFormat.Format24bppRgb); int[] histogram = new int[256]; int minGrayValue = 255, maxGrayValue = 0; //求取直方图 for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { Color pixelColor = img1.GetPixel(i, j); // 获取像素点 histogram[pixelColor.R]++; // R通道每个值的个数, if (pixelColor.R > maxGrayValue) maxGrayValue = pixelColor.R; // 计算最大值 if (pixelColor.R < minGrayValue) minGrayValue = pixelColor.R; // 计算最小值 } } //迭代计算阀值 if (threshold == -1) { int newThreshold = (minGrayValue + maxGrayValue) / 2; // 求平均 for (int iterationTimes = 0; threshold != newThreshold && iterationTimes < 100; iterationTimes++) { threshold = newThreshold; int lP1 = 0; int lP2 = 0; int lS1 = 0; int lS2 = 0; //求两个区域的灰度的平均值 for (int i = minGrayValue; i < threshold; i++) { lP1 += histogram[i] * i; // histogram[i]:像素i的个数,i:像素值; lP1:所有像素乘个数的和 lS1 += histogram[i]; // 共有的像素个数 } int mean1GrayValue = (lP1 / lS1); // 平均像素:x = ( x1*w1 + x2*w2+...+xn*wn ) / (w1 + w2 +...+ wn) for (int i = threshold + 1; i < maxGrayValue; i++) { lP2 += histogram[i] * i; lS2 += histogram[i]; } int mean2GrayValue = (lP2 / lS2); newThreshold = (mean1GrayValue + mean2GrayValue) / 2; } } //MessageBox.Show(threshold.ToString()); //计算二值化 for (int i =0; i < w; i++) { for (int j = 0; j < h; j++) { Color pixelColor = img1.GetPixel(i, j); if (pixelColor.R > threshold) { dstBitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255)); // 二值化赋值 //Console.WriteLine("白色区域位置:{0},{1}", i, j); } else { dstBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0)); //Console.WriteLine("黑色区域位置:{0},{1}", i, j); } } } return dstBitmap; } 全局平均值二值化:计算全图平均值,按照平均值进行二值化赋值。 具体例程如下,相对简单: /// <summary> /// 平均值法获取二值化的阈值 /// </summary> /// <param name="image">灰度图像</param> /// <returns></returns> public static Bitmap GetBinaryzationImage1(Bitmap image) { Bitmap result = image.Clone() as Bitmap; // 计算灰度平均值 List<int> tempList = new List<int>(); for (int i = 0; i < result.Width; i++) { for (int j = 0; j < result.Height; j++) { tempList.Add(result.GetPixel(i, j).R); // 将R通道的像素加入到列表中 } } double average = tempList.Average(); // 计算列表的所有像素的平均值 Color color = new Color(); for (int i = 0; i < result.Width; i++) { for (int j = 0; j < result.Height; j++) { color = result.GetPixel(i, j); if ((color.R + color.G + color.B) / 3 > average)// 三通道平均值和全图像素平均值做比较,再进行二值化赋值 { result.SetPixel(i, j, Color.White); } else { result.SetPixel(i, j, Color.Black); } } } return result; }

四. 图像边缘检测

Canny算子(程序如下,原理后续补齐)。网上找到一篇不错的博文。链接 //Canny算子 public class Canny { public Bitmap _bitmap; public Canny(Bitmap _bitmap) { this._bitmap = _bitmap; } public static Image CannyProcess(Bitmap src, int highThreshould, int lowThreshould) { if (src != null) { int w = src.Width; int h = src.Height; byte[] temp = Bitmap2Bytes(src); byte[] tempMask = (byte[])temp.Clone(); int[,] srcBytes = new int[w, h]; for (int j = 0; j < h; j++)//转灰度 { for (int i = 0; i < w; i++) { srcBytes[i, j] = (int)(tempMask[i * 4 + j * w * 4] * 0.114 + tempMask[i * 4 + 1 + j * w * 4] * 0.587 + tempMask[i * 4 + 2 + j * w * 4] * 0.299); } } float gradientMax = 100; float[,] gradient = new float[w, h];//梯度数组 byte[,] degree = new byte[w, h];//梯度角度数组 GaussFilter(ref srcBytes, w, h);//高斯平滑 GetGradientDegree(srcBytes, ref gradient, ref degree, ref gradientMax, w, h);//计算梯度 NonMaxMini(gradient, ref srcBytes, gradientMax, w, h, degree);//非极大值抑制 TwoThreshouldJudge(highThreshould, lowThreshould, ref srcBytes, w, h); byte bytex; Bitmap dstBitmap = new Bitmap(w, h, PixelFormat.Format24bppRgb); for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { bytex = (byte)srcBytes[i, j]; dstBitmap.SetPixel(i, h - j - 1, Color.FromArgb(bytex, bytex, bytex)); } } return dstBitmap; } else { return null; } } //高斯滤波 private static void GaussFilter(ref int[,] src, int x, int y) { for (int j = 1; j < y - 1; j++) { for (int i = 1; i < x - 1; i++) { src[i, j] = (4 * src[i, j] + src[i - 1, j - 1] + src[i + 1, j - 1] + src[i - 1, j + 1] + src[i + 1, j + 1] + 2 * src[i, j - 1] + 2 * src[i - 1, j] + 2 * src[i, j + 1] + 2 * src[i + 1, j]) / 16; } } } //梯度相位角获取 private static void GetGradientDegree(int[,] srcBytes, ref float[,] gradient, ref byte[,] degree, ref float GradientMax, int x, int y) { gradient = new float[x, y]; degree = new byte[x, y]; int gx, gy; int temp; double div; for (int j = 1; j < y - 1; j++) { for (int i = 1; i < x - 1; i++) { gx = srcBytes[i + 1, j - 1] + 2 * srcBytes[i + 1, j] + srcBytes[i + 1, j + 1] - srcBytes[i - 1, j - 1] - 2 * srcBytes[i - 1, j] - srcBytes[i - 1, j + 1]; gy = srcBytes[i - 1, j - 1] + 2 * srcBytes[i, j - 1] + srcBytes[i + 1, j - 1] - srcBytes[i - 1, j + 1] - 2 * srcBytes[i, j + 1] - srcBytes[i + 1, j + 1]; gradient[i, j] = (float)Math.Sqrt((double)(gx * gx + gy * gy)); if (GradientMax < gradient[i, j]) { GradientMax = gradient[i, j]; } if (gx == 0) { temp = (gy == 0) ? 0 : 90; } else { div = (double)gy / (double)gx; if (div < 0) { temp = (int)(180 - Math.Atan(-div) * 180 / Math.PI); } else { temp = (int)(Math.Atan(div) * 180 / Math.PI); } if (temp < 22.5) { temp = 0; } else if (temp < 67.5) { temp = 45; } else if (temp < 112.5) { temp = 90; } else if (temp < 157.5) { temp = 135; } else temp = 0; } degree[i, j] = (byte)temp; } } } //非极大值抑制 private static void NonMaxMini(float[,] gradient, ref int[,] srcBytes, float GradientMax, int x, int y, byte[,] degree) { float leftPixel = 0, rightPixel = 0; for (int j = 1; j < y - 1; j++) { for (int i = 1; i < x - 1; i++) { switch (degree[i, j]) { case 0: leftPixel = gradient[i - 1, j]; rightPixel = gradient[i + 1, j]; break; case 45: leftPixel = gradient[i - 1, j + 1]; rightPixel = gradient[i + 1, j - 1]; break; case 90: leftPixel = gradient[i, j + 1]; rightPixel = gradient[i, j - 1]; break; case 135: leftPixel = gradient[i + 1, j + 1]; rightPixel = gradient[i - 1, j - 1]; break; default: break; } if ((gradient[i, j] < leftPixel) || (gradient[i, j] < rightPixel)) { srcBytes[i, j] = 0; } else { int xx = (int)(255 * gradient[i, j] / GradientMax); srcBytes[i, j] = xx; } } } } //双阈值边缘判断 private static void TwoThreshouldJudge(int highThreshold, int lowThreshould, ref int[,] srcBytes, int x, int y) { for (int j = 1; j < y - 1; j++) { for (int i = 1; i < x - 1; i++) { if (srcBytes[i, j] > highThreshold) { srcBytes[i, j] = 255; } else if (srcBytes[i, j] < lowThreshould) { srcBytes[i, j] = 0; } else { if (srcBytes[i - 1, j - 1] < highThreshold && srcBytes[i, j - 1] < highThreshold && srcBytes[i + 1, j - 1] < highThreshold && srcBytes[i - 1, j] < highThreshold && srcBytes[i + 1, j] < highThreshold && srcBytes[i - 1, j + 1] < highThreshold && srcBytes[i, j + 1] < highThreshold && srcBytes[i + 1, j + 1] < highThreshold) { srcBytes[i, j] = 0; } else srcBytes[i, j] = 255; } } } } public static byte[] Bitmap2Bytes(Bitmap b) { MemoryStream ms = new MemoryStream(); b.Save(ms, ImageFormat.Bmp); byte[] bytes = ms.GetBuffer(); ms.Close(); return bytes; } public static Bitmap Bytes2Bitmap(byte[] bytes, int w, int h) { MemoryStream ms1 = new MemoryStream(bytes, 0, w * 4 * h); Bitmap bm = (Bitmap)Image.FromStream(ms1); ms1.Close(); return bm; } }
最新回复(0)