C#图像:2.图像处理

news/2024/5/18 17:32:46
类库项目ImageProcessingLibrary代码

里面是几种图像处理函数,可以生成DLL文件被其他(下面)项目添加引用

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;namespace ImageProcessingLibrary
{public static class ImageProcessing{/// 获取直方图数组,并绘制直方图/// <param name="image">需要处理的图像</param>/// <param name="indexColor">处理的颜色索引值,Blue:0,Green:1,Red:2</param>/// <param name="histogram">直方图统计数组</param>/// <returns>绘制好的直方图</returns>public static Bitmap GetHistogram(Bitmap image, int indexColor, out int[] histogram){histogram = new int[256];                               //直方图统计数组BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);  //将图像锁定到内存中byte[] datas = new byte[data.Stride * image.Height];   //图像数组Marshal.Copy(data.Scan0, datas, 0, datas.Length);     //将图像在内存中的数据复制到图像数组中for (int y = 0; y < image.Height * data.Stride; y += data.Stride) //data.Stride代表图像一行数据的字节总数/步长为data.Stride{//外层循环是遍历行for (int x = 0; x < image.Width * 3; x += 3)//遍历当前行中的每个像素/每个像素由三个字节(RGB)组成//每个颜色分量(红色、绿色或蓝色)可以有的不同强度级别就是2^8,即256个级别{int index = y + x;                     //颜色在内存中的索引/每个索引偏移量3字节(对应R,G,B)histogram[datas[index + indexColor]]++;//增加直方图中对应颜色分量出现的次数}}image.UnlockBits(data);byte maxValue = 0;                             //直方图中的最大值for (int value = 1; value < 256; value++){if (histogram[value] > histogram[maxValue]) maxValue = (byte)value;}Bitmap imageHistogram = new Bitmap(256, 256);Graphics GHistogram = Graphics.FromImage(imageHistogram);GHistogram.Clear(Color.Blue);for (int value = 1; value< 256; value++){int length = byte.MaxValue * histogram[value] / histogram[maxValue];GHistogram.DrawLine(new Pen(Color.FromArgb(value, value, value), 1f), value,256, value, 256 - length);                            //绘制直方图}Font font = new Font("宋体", 9f);//绘制统计标识for (int value = 32; value < 256; value += 32){int count = histogram[maxValue] / 8 * value / 32;Pen pen = new Pen(Color.Lime);pen.DashStyle = DashStyle.DashDot;SizeF sizeCount = GHistogram.MeasureString(count.ToString(), font);GHistogram.DrawLine(pen, 0, 255 - value, 255, 255 - value);//绘制数量等级线GHistogram.DrawString(count.ToString(), font, Brushes.Red, 5, 255 - value - sizeCount.Height / 2);SizeF sizeValue = GHistogram.MeasureString(value.ToString(), font);GHistogram.DrawLine(Pens.Red, value, 250, value, 255);//绘制颜色值等级线GHistogram.DrawString(value.ToString(), font, Brushes.Red, value - sizeValue.Width / 2, 240);}font.Dispose();return imageHistogram;}/// 将图像进行二值化处理/// <param name="image">需要处理的图像</param>/// <param name="indexColor">处理的颜色索引值,Blue:0,Green:1,Red:2</param>/// <param name="thresholdMin">阈值下限</param>/// <param name="thresholdMax">阈值上限</param>public static void BinaryImage(Bitmap image, int indexColor, int thresholdMin, int thresholdMax){//将图像锁定到内存中BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);byte[] datas = new byte[data.Stride * image.Height];           //图像数组Marshal.Copy(data.Scan0, datas, 0, datas.Length);              //将图像在内存中的数据复制到图像数组中for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int index = y + x;//根据阈值将图像分成黑色和白色,其中阈值内的为黑色,阈值外的为白色if (datas[index + indexColor] >= thresholdMin && datas[index + indexColor] <= thresholdMax)datas[index] = datas[index + 1] = datas[index + 2] = 0;elsedatas[index] = datas[index + 1] = datas[index + 2] = 255;}}Marshal.Copy(datas, 0, data.Scan0, datas.Length);      //将图像数组复制到内存中image.UnlockBits(data);                                //将图像从内存中解锁}/// 将彩色图像转换成灰度图像/// <param name="image">需要处理的图像</param>/// <returns></returns>public static void GreyImage(Bitmap image){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);     //将图像锁定到内存中byte[] datas = new byte[data.Stride * image.Height];           //图像数组Marshal.Copy(data.Scan0, datas, 0, datas.Length);              //将图像在内存中的数据复制到图像数组中//计算灰度矩阵for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int index = y + x;                             //颜色在内存中的索引byte Blue = datas[index];                      //蓝色值byte Green = datas[index + 1];                 //绿色值byte Red = datas[index + 2];                   //红色值datas[index] = datas[index + 1] = datas[index + 2] =(byte)((Red * 19595 + Green * 38469 + Blue * 7472) >> 16);//灰度值}}Marshal.Copy(datas, 0, data.Scan0, datas.Length);      //将图像数组复制到内存中image.UnlockBits(data);}/// 对图像进行边缘提取/// <param name="image">灰度图像</param>/// <param name="threshold">边缘阈值</param>public static void ExtractEdge(Bitmap image){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);      //将图像锁定到内存中byte[] datas = new byte[data.Stride * image.Height];           //图像数组byte[] edges = new byte[data.Stride * image.Height];           //图像边界Marshal.Copy(data.Scan0, datas, 0, datas.Length);              //将图像在内存中的数据复制到图像数组中for (int y = data.Stride; y < image.Height * data.Stride - data.Stride; y += data.Stride){for (int x = 3; x < image.Width * 3 - 3; x += 3){int index = y + x;byte grey = datas[index];                      //像素灰度值int value = 0;//将像素点的灰度值与周围8个点的灰度值分别求差的绝对值,再求和for (int yy = -data.Stride; yy <= data.Stride; yy += data.Stride){for (int xx = -3; xx <= 3; xx += 3){if (yy == 0 && xx == 0) continue;index = x + y + xx + yy;value += Math.Abs(grey - datas[index]);}}//上一步求和的结果除以8,然后赋值给边界数组edges[index] = edges[index + 1] = edges[index + 2] = (byte)(value >> 3);}}Marshal.Copy(edges, 0, data.Scan0, datas.Length);      //将边界数组复制到内存中image.UnlockBits(data);                                //将图像从内存中解锁}/// 将图像进制中值滤波处理/// <param name="image">需要处理的图像</param>public static void MedianFilter(Bitmap image){BitmapData dataImage = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//将图像锁定到内存中unsafe{byte* pImage = (byte*)dataImage.Scan0.ToPointer(); //获取图像头指针for (int y = 1; y < image.Height - 1; y++){for (int x = 1; x < image.Width - 1; x++){List<byte>[] datas = new List<byte>[3];        //创建存放八邻域像素颜色值的列表for (int k = 0; k < 3; k++) datas[k] = new List<byte>();for (int yy = -1; yy < 2; yy++){for (int xx = -1; xx < 2; xx++){int index = (y + yy) * dataImage.Stride + (x + xx) * 3;  //八邻域像素索引//将八邻域像素颜色值添加到列表中for (int k = 0; k < 3; k++) datas[k].Add(pImage[index + k]);}}for (int k = 0; k < 3; k++) datas[k].Sort();            //对八邻域颜色值排序int indexMedian = y * dataImage.Stride + x * 3;for (int k = 0; k < 3; k++){pImage[indexMedian + k] = datas[k][4]; //取排序后的中间值作为像素颜色值datas[k].Clear();}datas = null;}}}image.UnlockBits(dataImage);}/// <summary>/// 利用自动对比度增强方法,对图像进行对比度增强处理/// </summary>/// <param name="image">需要处理的灰度图像</param>/// <param name="sourceMin">定义原始图像中像素值的最小范围。</param>/// <param name="sourceMax">定义原始图像中像素值的最大范围。</param>/// <param name="destMin">定义增强后图像中像素值想要映射到的最小范围</param>/// <param name="destMax">定义增强后图像中像素值想要映射到的最大范围</param>public static void EnhanceImage(Bitmap image, int sourceMin, int sourceMax, int destMin, int destMax){//将图像锁定到内存中BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);byte[] datas = new byte[data.Stride * image.Height];   //图像数组Marshal.Copy(data.Scan0, datas, 0, datas.Length);      //将图像在内存中的数据复制到图像数组中for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int index = y + x;                             //颜色在内存中的索引float value = (float)(destMax - destMin) / (float)(sourceMax - sourceMin)* (float)(datas[index] - sourceMin) + destMin;     //灰度值映射关系datas[index] = datas[index + 1] = datas[index + 2]= value < byte.MinValue ? byte.MinValue : (value > byte.MaxValue ? byte.MaxValue : (byte)value);}}Marshal.Copy(datas, 0, data.Scan0, datas.Length);      //将图像数组复制到内存中image.UnlockBits(data);}/// 连通区域标记/// <param name="image">二值图像</param>public static void ConnectRegion(Bitmap image){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);  //将图像锁定到内存中byte[] datas = new byte[data.Stride * image.Height];       //图像数组Marshal.Copy(data.Scan0, datas, 0, datas.Length);          //将图像在内存中的数据复制到图像数组中int sign = 40;for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int index = y + x;                                  //颜色在内存中的索引if (datas[index] == 0){SignRegionIterative(datas, x, y, sign, data.Stride, image.Width, image.Height);sign += 40;}}}Marshal.Copy(datas, 0, data.Scan0, datas.Length);      //将图像数组复制到内存中image.UnlockBits(data);}/// <summary>/// 区域标记(可被迭代版本SignRegionIterative替换)/// </summary>/// <param name="datas"></param>/// <param name="x">初始点(x, y)</param>/// <param name="y"></param>/// <param name="sign"></param>/// <param name="stride"></param>/// <param name="width"></param>/// <param name="height"></param>static void SignRegion(byte[] datas, int x, int y, int sign, int stride, int width, int height){datas[y + x] = datas[y + x + 1] = datas[y + x + 2] = (byte)sign;       //标记区域if (x > 0 && datas[y + x - 3] == 0){SignRegion(datas, x - 3, y, sign, stride, width, height);  //搜索左边像素}if (x < width * 3 - 3 && datas[y + x + 3] == 0){SignRegion(datas, x + 3, y, sign, stride, width, height);  //搜索右边像素}if (y > 0 && datas[y - stride + x] == 0){SignRegion(datas, x, y - stride, sign, stride, width, height); //搜索上边像素}if (y < stride * height && datas[y + stride + x] == 0){SignRegion(datas, x, y + stride, sign, stride, width, height); //搜索下边像素}}static void SignRegionIterative(byte[] datas, int x, int y, int sign, int stride, int width, int height){Stack<Tuple<int, int>> stack = new Stack<Tuple<int, int>>();//使用了一个栈来保存待处理的像素坐标stack.Push(Tuple.Create(x, y));//将初始坐标(x, y)压入栈中while (stack.Count > 0)//循环会一直执行,直到栈为空{var current = stack.Pop();//每次循环中,我们从栈顶弹出一个坐标,并将其标记int currentX = current.Item1;int currentY = current.Item2;// 标记当前点  if (currentX >= 0 && currentX < width * 3 && currentY >= 0 && currentY < height * stride){int index = currentY + currentX;datas[index] = datas[index + 1] = datas[index + 2] = (byte)sign;// 搜索相邻的可标记像素并压入栈  if (currentX > 0 && datas[currentY + currentX - 3] == 0){stack.Push(Tuple.Create(currentX - 3, currentY)); // 左边像素  }if (currentX < width * 3 - 3 && datas[currentY + currentX + 3] == 0){stack.Push(Tuple.Create(currentX + 3, currentY)); // 右边像素  }if (currentY > 0 && datas[currentY - stride + currentX] == 0){stack.Push(Tuple.Create(currentX, currentY - stride)); // 上边像素  }if (currentY < stride * (height - 1) && datas[currentY + stride + currentX] == 0){stack.Push(Tuple.Create(currentX, currentY + stride)); // 下边像素  }}}}/*在这个迭代版本中,我们使用了一个栈来保存待处理的像素坐标。开始时,我们将初始坐标(x, y)压入栈中。然后,* 我们进入一个循环,该循环会一直执行,直到栈为空。在每次循环中,我们从栈顶弹出一个坐标,并将其标记。然后,我们检查该坐标的左、右、上、下四个相邻像素,如果它们的颜色值为0(即未被标记),我们就将它们压入栈中以便后续处理。请注意,我们在压入相邻像素之前进行了边界检查,以确保我们不会尝试访问数组之外的元素。这个迭代版本应该能够避免递归版本可能导致的栈溢出问题。*//// <summary>/// 缩放函数/// </summary>/// <param name="image"></param>/// <param name="horizon"></param>/// <param name="verticale"></param>public static void Dilation(Bitmap image, float horizon, float verticale){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//将图像锁定到内存中byte[] datasOld = new byte[data.Stride * image.Height];        //图像数组byte[] datasNew = new byte[data.Stride * image.Height];        //图像数组//将图像在内存中的数据复制到图像数组中Marshal.Copy(data.Scan0, datasOld, 0, datasOld.Length);for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int xNew = x / 3, yNew = y / data.Stride;int xOld = (int)(xNew / horizon);int yOld = (int)(yNew / verticale);if (xOld < 0 || xOld >= image.Width || yOld < 0 || yOld >= image.Height) continue;int indexOld = yOld * data.Stride + xOld * 3;int indexNew = y + x;datasNew[indexNew] = datasNew[indexNew + 1] = datasNew[indexNew + 2] = datasOld[indexOld];}}Marshal.Copy(datasNew, 0, data.Scan0, datasOld.Length);  //将图像数组复制到内存中image.UnlockBits(data);                                  //将图像从内存中解锁}/// <summary>/// 平移函数/// </summary>/// <param name="image"></param>/// <param name="horizon"></param>/// <param name="verticale"></param>public static void Translation(Bitmap image, float horizon, float verticale){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//将图像锁定到内存中byte[] datasOld = new byte[data.Stride * image.Height];        //图像数组byte[] datasNew = new byte[data.Stride * image.Height];        //图像数组//将图像在内存中的数据复制到图像数组中Marshal.Copy(data.Scan0, datasOld, 0, datasOld.Length);for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int xNew = x / 3, yNew = y / data.Stride;int xOld = (int)(xNew - horizon);int yOld = (int)(yNew - verticale);if (xOld < 0 || xOld >= image.Width || yOld < 0 || yOld >= image.Height) continue;int indexOld = yOld * data.Stride + xOld * 3;int indexNew = y + x;datasNew[indexNew] = datasNew[indexNew + 1] = datasNew[indexNew + 2] = datasOld[indexOld];}}Marshal.Copy(datasNew, 0, data.Scan0, datasOld.Length);  //将图像数组复制到内存中image.UnlockBits(data);                                  //将图像从内存中解锁}/// <summary>/// 旋转函数/// </summary>/// <param name="image"></param>/// <param name="angle"></param>/// <param name="center"></param>public static void Rotation(Bitmap image, float angle, Point center){BitmapData data = image.LockBits(new Rectangle(new Point(), image.Size),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //将图像锁定到内存中byte[] datasOld = new byte[data.Stride * image.Height];        //图像数组byte[] datasNew = new byte[data.Stride * image.Height];        //图像数组//将图像在内存中的数据复制到图像数组中Marshal.Copy(data.Scan0, datasOld, 0, datasOld.Length);for (int y = 0; y < image.Height * data.Stride; y += data.Stride){for (int x = 0; x < image.Width * 3; x += 3){int xNew = x / 3, yNew = y / data.Stride;int xOld = (int)((xNew - center.X) * Math.Cos(angle * Math.PI / 180)- (yNew - center.Y) * Math.Sin(angle * Math.PI / 180) + center.X);int yOld = (int)((xNew - center.X) * Math.Sin(angle * Math.PI / 180)+ (yNew - center.Y) * Math.Cos(angle * Math.PI / 180) + center.Y);if (xOld < 0 || xOld >= image.Width|| yOld < 0 || yOld >= image.Height) continue;int indexOld = yOld * data.Stride + xOld * 3;int indexNew = y + x;datasNew[indexNew] = datasNew[indexNew + 1] = datasNew[indexNew + 2] = datasOld[indexOld];}}Marshal.Copy(datasNew, 0, data.Scan0, datasOld.Length); //将图像数组复制到内存中image.UnlockBits(data);                                 //将图像从内存中解锁}}}
/*假设颜色分量是8位的,那么每个颜色分量(红色、绿色或蓝色)可以有的不同强度级别就是2^8,即256个级别。* 这是因为8位可以表示从0到255的整数,总共256个不同的数值。在数字图像处理中,8位颜色深度是常见的,* 因为它提供了足够的动态范围来表示大多数自然和人工颜色的细微差别,同时保持数据量相对较小。当你说“直方图大小为256”时,你指的是直方图的横坐标(即颜色强度的可能值)有256个不同的条目,
每个条目对应一个特定的颜色强度值(从0到255)。直方图的纵坐标通常表示该颜色强度值在图像中出现的频率或像素数量。因此,如果我们想为8位颜色分量的图像构建直方图,我们将创建一个大小为256的数组,数组的每个元素初始化为0。
然后,我们遍历图像的每个像素,对于每个像素的特定颜色分量(如红色、绿色或蓝色),我们增加直方图中对应颜色强度值的计数。
这个过程最终会给我们一个表示图像中每个颜色强度出现频率的直方图。*//*datas[index] = datas[index + 1] = datas[index + 2] = some_value;
这里实际上是在进行连续赋值。some_value首先被赋给datas[index + 2](蓝色通道),然后这个值又被赋给datas[index + 1]
(绿色通道),最后再次被赋给datas[index](红色通道)。由于赋值操作符=是从右到左结合的,
所以这行代码的效果是三个颜色通道都被赋予了相同的值。在图像处理的上下文中,这种操作通常用于灰度化(将彩色图像转换为灰度图像,即每个通道具有相同的亮度值),
或者在进行某些类型的颜色校正或调整时,需要对所有颜色通道进行统一的变换。*//*datas[index] = datas[index + 1] = datas[index + 2]  = value < byte.MinValue ? byte.MinValue : (value > byte.MaxValue ? byte.MaxValue : (byte)value);
value < byte.MinValue ? byte.MinValue : ... :这部分是一个条件运算符(也称作三元运算符)。
它检查value是否小于byte.MinValue(即0)。如果是,则直接返回byte.MinValue(0),因为负值在字节表示中是无效的。... : (value > byte.MaxValue ? byte.MaxValue : ...) :如果value不小于byte.MinValue,
代码会进入这个分支,并检查value是否大于byte.MaxValue(即255)。如果是,则返回byte.MaxValue(255),
因为超出255的值也无法用一个字节表示。... : (byte)value :如果value既不小于byte.MinValue也不大于byte.MaxValue,则将其强制转换为byte类型,
这会自动截去小数部分,只保留整数部分。*//*float value = (float)(destMax - destMin) / (float)(sourceMax - sourceMin)  * (float)(datas[index] - sourceMin) + destMin;
(float)(destMax - destMin):计算目标值域的范围,即最大值与最小值之间的差。(float)(sourceMax - sourceMin):计算原始值域的范围,即原图像中像素值的最大值与最小值之间的差。(float)(destMax - destMin) / (float)(sourceMax - sourceMin):计算目标值域范围与原始值域范围的比例。这个比例将用于调整原始像素值,使其适应新的值域。(float)(datas[index] - sourceMin):计算当前像素值相对于原始值域最小值的偏移量。* (float)(datas[index] - sourceMin):将上述比例与原始像素值的偏移量相乘,得到在新值域中的相对偏移量。+ destMin:将新值域中的相对偏移量加上新值域的最小值,从而得到映射后的绝对像素值。float value = ...:将最终计算出的映射后的像素值存储在value变量中*//*。像素灰度的变换公式如下:
GreyNew=(destMax - destMin) / (sourceMax - sourceMin) * (GreyOld - sourceMin) + destMin;
其中,GreyNew为增强后的灰度值,GreyOld为增强前的灰度值,sourceMax和sourceMin表示增强前的灰度范围,
destMax和destMin表示增强后的灰度范围。从实例效果图的直方图可以看出,图像在增强前的灰度分布在0到100之间,
所以图像整体颜色比较灰暗,增强后的灰度范围是整个灰度范围,所以增强后的图像更有层次感,也更清晰。*/

 下面是各个独立窗体应用程序,都需要添加引用上面的类库(DLL),复制测试这些代码需要自己在......项目名\bin\Debug路径下添加命名自己的图片(可参考C#&图像第一章

1.基于差分的边缘检测与提取
using ImageProcessingLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace EdgeExtraction
{public partial class FormEdgeExtraction : Form{public FormEdgeExtraction(){InitializeComponent();}protected override void OnPaint(PaintEventArgs e)//重写OnPaint事件函数{Graphics G = e.Graphics;Bitmap image = new Bitmap("apple.jpg");                    //加载图像ImageProcessing.GreyImage(image);                          //生成灰度图像Rectangle rectImage = new Rectangle(new Point(), image.Size);G.DrawImage(image, rectImage);rectImage.Offset(rectImage.Width, 0);ImageProcessing.ExtractEdge(image);                        //提取边缘G.DrawImage(image, rectImage);rectImage.Offset(-rectImage.Width, rectImage.Height);Bitmap image2 = image.Clone() as Bitmap;ImageProcessing.BinaryImage(image2, 0, 20, 255);           //在灰度20到255的范围提取边界G.DrawImage(image2, rectImage);image2.Dispose();rectImage.Offset(rectImage.Width, 0);ImageProcessing.BinaryImage(image, 0, 40, 255);            //在灰度40到255的范围提取边界G.DrawImage(image, rectImage);image.Dispose();}}
}
2.利用中值滤波使图像变得平滑
using ImageProcessingLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace MedianFilte
{public partial class FormMedianFilter : Form{public FormMedianFilter(){InitializeComponent();}protected override void OnPaint(PaintEventArgs e)//重写OnPaint事件函数{Graphics G = e.Graphics;Bitmap image = new Bitmap("apple.jpg");                    //加载图像Random random = new Random();Rectangle rectImage = new Rectangle(new Point(), image.Size);for (int i = 0; i < 500; i++)//随机产生噪点{ image.SetPixel(random.Next(image.Width), random.Next(image.Height), Color.FromArgb(random.Next(int.MaxValue)));}G.DrawImage(image, rectImage);                             //绘制原始图像ImageProcessing.MedianFilter(image);//中值滤波处理rectImage.Offset(0, rectImage.Height);G.DrawImage(image, rectImage);                             //绘制中值滤波后的图像image.Dispose();}}
}
3.增强图像对比度
using ImageProcessingLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace ImageEnhancement
{public partial class FormImageEnhancement : Form{public FormImageEnhancement(){InitializeComponent();}protected override void OnPaint(PaintEventArgs e)//重写OnPaint事件函数{Graphics G = e.Graphics;Bitmap image = new Bitmap("jdq.gif");                     //加载图像Size newSize = new Size(image.Width / 2, image.Height / 2);//图像尺寸缩半Rectangle rectImage = new Rectangle(new Point(), newSize);Rectangle rectHistogram = new Rectangle(rectImage.Width, rectImage.Top, 256, 256);int[] histogram;Bitmap imageHistogram = ImageProcessing.GetHistogram(image, 0, out histogram);G.DrawImage(image, rectImage);                             //绘制原始图像G.DrawImage(imageHistogram, rectHistogram);                //绘制原始直方图ImageProcessing.EnhanceImage(image, 0, 255, 0, 200);imageHistogram = ImageProcessing.GetHistogram(image, 0, out histogram);rectImage.Offset(0, Math.Max(rectImage.Height, 256));//位置最少偏移图片高度rectHistogram.Offset(0, Math.Max(rectImage.Height, 256));G.DrawImage(image, rectImage);                             //绘制增强图像G.DrawImage(imageHistogram, rectHistogram);                //绘制增强直方图image.Dispose();imageHistogram.Dispose();}}
}
4.目标提取与区域标记 
using ImageProcessingLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace ConnectRgeion
{public partial class FormConnectRgeion : Form{public FormConnectRgeion(){InitializeComponent();}protected override void OnPaint(PaintEventArgs e)//重写OnPaint事件函数{Graphics G = e.Graphics;Bitmap image = new Bitmap("657.png");                    //加载图像Rectangle rectImage = new Rectangle(new Point(), image.Size);G.DrawImage(image, rectImage); //原始图ImageProcessing.BinaryImage(image, 0, 0, 210);                     //通过二值化将目标分割出来rectImage.Offset(  rectImage.Width,0);G.DrawImage(image, rectImage);ImageProcessing.MedianFilter(image);//中值滤波处理;rectImage.Offset(-rectImage.Width, image.Height);G.DrawImage(image, rectImage);ImageProcessing.ConnectRegion(image);                     //连通区域标记rectImage.Offset(rectImage.Width, 0);G.DrawImage(image, rectImage);image.Dispose();}}
}
5.图像的变形 
using ImageProcessingLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace Transformation
{public partial class FormTransformation : Form{public FormTransformation(){InitializeComponent();}protected override void OnPaint(PaintEventArgs e)//重写OnPaint事件函数{Graphics G = e.Graphics;Bitmap image = new Bitmap("lina.jpg");                 //加载图像Size newSize = new Size(image.Width / 3, image.Height / 3);//图像尺寸缩半Rectangle rectImage = new Rectangle(new Point(), newSize);G.DrawImage(image, rectImage);                                     //绘制原始图像rectImage.Offset(rectImage.Width, 0);ImageProcessing.Dilation(image, 0.8f, 0.8f);                       //对图像进行缩小处理G.DrawImage(image, rectImage);rectImage.Offset(-rectImage.Width, rectImage.Height);ImageProcessing.Translation(image, image.Width / 10, image.Height / 10); //将图像平移居中-居中还和和图像缩小有关(0.5f-4/0.8f-10)G.DrawImage(image, rectImage);rectImage.Offset(rectImage.Width, 0);//将图像围绕中心点逆时针旋转45°ImageProcessing.Rotation(image, 45, new Point(image.Width / 2, image.Height / 2));G.DrawImage(image, rectImage);image.Dispose();}/// <summary>/// 根据加载的图片大小动态调整Windows Forms窗口的大小/// </summary>/// <param name="imagePath"></param>private void LoadImageAndResizeForm(){try{Bitmap image = new Bitmap("lina.jpg");// 调整窗口大小以适应图片,并考虑额外的空间  this.Size = new Size(image.Width*1+ SystemInformation.Border3DSize.Width * 10,image.Height*1 + SystemInformation.CaptionHeight + SystemInformation.Border3DSize.Height * 10);}catch (Exception ex){MessageBox.Show("加载图片时出错: " + ex.Message);}}private void MainForm_Load(object sender, EventArgs e){// 使用绝对路径//string imagePath = @"D:\Users\图像处理\WindowsFormsCeShi\bin\Debug\lina.jpg";// LoadImageAndResizeForm(imagePath);LoadImageAndResizeForm();//  MessageBox.Show("窗体正在初始化");}}
}


http://www.mrgr.cn/p/60478624

相关文章

扫雷实现详解【递归展开+首次必展开+标记雷+取消标记雷】

扫雷 一.扫雷设计思路二.扫雷代码逐步实现1.创建游戏菜单2.初始化棋盘3.打印棋盘4.随机布置雷5.统计周围雷的个数6.递归展开棋盘7.标记雷8.删除雷的标记9.保证第一次排雷的安全性棋盘必定展开10.排查雷11.判断输赢 三.扫雷总代码四.截图 一.扫雷设计思路 1.创建游戏菜单。  2.…

深度学习500问——Chapter08:目标检测(7)

文章目录 8.3.8 RFBNet 8.3.9 M2Det 8.3.8 RFBNet RFBNet有哪些创新点 1. 提出RF block&#xff08;RFB&#xff09;模块 RFBNet主要想利用一些技巧使得轻量级模型在速度和精度上达到很好的trade-off的检测器。灵感来自人类视觉的感受野结构Receptive Fields&#xff08;RFs…

分布式websocket IM即时通讯聊天开源项目如何启动

前言 自己之前分享了分布式websocket的视频有同学去fork项目了&#xff0c;自己启动一下更方便理解项目嘛。然后把项目启动需要的东西全部梳理出来。支持群聊单聊,表情包以及发送图片。 支持消息可靠&#xff0c;消息防重&#xff0c;消息有序。同时基础架构有分布式权限&…

axios.get请求 重复键问题??

封装的接口方法&#xff1a; 数据&#xff1a; 多选框多选后 能得到对应的数组 但是请求的载荷却是这样的,导致会请求不到数据 departmentChecks 的格式看起来是一个数组&#xff0c;但是通常 HTTP 请求的查询参数不支持使用相同的键&#xff08;key&#xff09;名多次。如…

分享一个网站实现永久免费HTTPS访问的方法

免费SSL证书作为一种基础的网络安全工具&#xff0c;以其零成本的优势吸引了不少网站管理员的青睐。要实现免费HTTPS访问&#xff0c;您可以按照以下步骤操作&#xff1a; 一、 选择免费SSL证书提供商 选择一个提供免费SSL证书的服务商。如JoySSL&#xff0c;他们是国内为数不…

JVM知识总汇(JVM面试题篇5.1)

个人理解&#xff0c;所学有限&#xff0c;若有不当&#xff0c;还请指出 1.JVM是由哪些部分组成&#xff0c;运行流程是什么&#xff1f; JVM为java虚拟机&#xff0c;是java程序的运行环境&#xff08;其实是java字节码文件的运行环境&#xff09;&#xff0c;能够实现一次编…

电路板/硬件---器件

电阻 电阻作用 电阻在电路中扮演着重要的角色&#xff0c;其作用包括&#xff1a; 限制电流&#xff1a;电阻通过阻碍电子流动的自由而限制电流。这是电阻最基本的功能之一。根据欧姆定律&#xff0c;电流与电阻成正比&#xff0c;电阻越大&#xff0c;通过电阻的电流就越小。…

【副本向】Lua副本逻辑

副本生命周期 OnCopySceneTick() 子线程每次心跳调用 --副本心跳 function x3323_OnCopySceneTick(elapse)if x3323_g_IsPlayerEnter 0 thenreturn; -- 如果没人进入&#xff0c;则函数直接返回endif x3323_g_GameOver 1 thenif x3323_g_EndTick > 0 thenx3323_CountDown…

如何低成本创建个人网站?

目录 前言 网站源代码 虚拟主机或服务器 域名注册或免费二级域名 域名解析 上传源代码压缩包 添加刚刚的域名 成功搭建 失败的解决方案 结语 前言 很多小白都非常想拥有自己的网站&#xff0c;但很多人虽然有了自己的源代码但苦于不知道怎么将其变成所有人都能够访…

Jenkins流水线部署springboot项目

文章目录 Jenkins流水线任务介绍Jenkins流水线任务构建Jenkins流水线任务Groovy脚本Jenkinsfile实现 Jenkins流水线任务实现参数化构建拉取Git代码构建代码制作自定义镜像并发布 Jenkins流水线任务介绍 之前采用Jenkins的自由风格构建的项目&#xff0c;每个步骤流程都要通过不…

GPU虚拟化和算力隔离探讨

1. 术语介绍 术语 全称 说明 GPU Graphics Processing Unit 显卡 CUDA Compute Unified Device Architecture 英伟达2006年推出的计算API VT/VT-x/VT-d Intel Virtualization Technology -x表示x86 CPU&#xff0c;-d表示Device SVM AMD Secure Virtual Machine …

17 内核开发-内核内部内联汇编学习

​ 17 内核开发-内核内部内联汇编学习 课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中…

鸿蒙准备1

鸿蒙心路 感慨索性&#xff0c; 看看鸿蒙吧。打开官网相关介绍 新建工程目录结构 感慨 最近面试Android应用开发&#xff0c;动不动就问framework的知识&#xff0c;什么touch事件的触发源是啥&#xff08;eventHub&#xff09;&#xff0c;gc流程是啥&#xff0c;图形框架是什…

(centos)yum安装mysql8.4

1.MySQL官方已经提供了适用于不同Linux发行版的预构建软件包&#xff0c;包括适用于CentOS的Yum Repository MySQL :: MySQL Community Downloads 2.在/usr/local文件夹下创建mysql文件夹&#xff0c;将下载的rpm文件放到目录下 3.执行安装命令 yum install mysql-community-…

vue快速入门(五十一)历史模式

注释很详细&#xff0c;直接上代码 上一篇 新增内容 历史模式配置方法 默认哈希模式&#xff0c;历史模式与哈希模式在表层的区别是是否有/#/ 其他差异暂不深究 源码 //导入所需模块 import Vue from "vue"; import VueRouter from "vue-router"; import m…

Android BINDER是干嘛的?

1.系统架构 2.binder 源码位置&#xff1a; 与LINUX传统IPC对比

探索小猪APP分发平台:构建高效的应用推广之路

在当今快速发展的移动互联网时代探索小猪APP分发平台&#xff1a;构建高效的应用推广之路&#xff0c;应用分发成为连接开发者与用户的关键桥梁。一个高效的分发平台可以显著提升应用的可达性和用户增长速度。 小猪app分发zixun.ppzhu.net 引言&#xff1a;小猪APP分发平台简介…

DIM层数据处理

一、了解DIM层 这个就是数仓开发的分层架构 我们现在是在DIM层&#xff0c;从ods表中数据进行加工处理&#xff0c;导入到dwd层&#xff0c;但是记住我们依然是在DIM层&#xff0c;而非是上面的ODS和DWD层。 二、处理维度表数据 ①先确认hive的配置 -- 开启动态分区方案 -- …

开源博客项目Blog .NET Core源码学习(19:App.Hosting项目结构分析-7)

本文学习并分析App.Hosting项目中后台管理页面的主页面。如下图所示&#xff0c;开源博客项目的后台主页面采用layui预设类layui-icon-shrink-right设置样式&#xff0c;点击主页面中的菜单&#xff0c;其它页面采用弹框或者子页面形式显示在主页面的内容区域。   后台主页面…

【电路笔记】-Twin-T振荡器

Twin-T振荡器 文章目录 Twin-T振荡器1、概述2、Twin-T振荡器3、Twin-T放大4、Twin-T 振荡器示例5、总结Twin-T 振荡器是另一种 RC 振荡器电路,它使用两个并联的 RC 网络来产生单一频率的正弦输出波形。 1、概述 Twin-T 振荡器是另一种类型的 RC 振荡器,它产生正弦波输出,用…