C#图像:2.图像处理

类库项目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;
                    else
                        datas[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.mfbz.cn/a/592751.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

麦克纳姆轮 Mecanum 小车运动学模型和动力学分析

目录 一、简介 二、运动学模型分析 1. 逆运动学方程 2. 正运动学方程 三、动力学模型 四、广泛运动学模型 一、简介 参考文献https://www.geometrie.tugraz.at/gfrerrer/publications/MecanumWheel.pdf 移动机器人的运动学模型是为了解决小车的正向运动学和逆向运动学问…

springmvc下

第二类初始化操作 multipartResolver应用 localeResolver应用 themeResolver应用 handlerMapping应用 handlerAdapter应用 handlerExceptionReslver requestToViewNameTranslator应用 viewResolver应用 flashMapManager应用 dispatcherServlet逻辑处理 processRequest处理web请…

【Flask 系统教程 5】视图进阶

类视图 在 Flask 中&#xff0c;除了使用函数视图外&#xff0c;你还可以使用类视图来处理请求。类视图提供了一种更为结构化和面向对象的方式来编写视图函数&#xff0c;使得代码组织更清晰&#xff0c;并且提供了更多的灵活性和可扩展性。 创建类视图 要创建一个类视图&am…

Docker高频使用命令

一、Docker常用命令总结 1.镜像命令管理 指令描述ls列出镜像build构建镜像来自Dockerfilehoistory查看历史镜像inspect显示一个或多个镜像的详细信息pull从镜像仓库拉取镜像push推送一个镜像仓库rm移除一个或多个镜像prune一处未使用的镜像&#xff0c;没有被标记或被任何容器…

初始化Linux或者Mac下Docker运行环境

文章目录 1 Mac下安装Docker2 Linux下安装Docker2.1 确定Linux版本2.2 安装Docker2.3 配置加速镜像 3 Docker安装校验4 安装docker-compose4.1 直接下载二进制文件4.2 移动二进制文件到系统路径4.3 设置可执行权限4.4 验证安装 1 Mac下安装Docker mac 安装 docker 还是比较方便…

哥白尼高程Copernicus DEM下载(CSDN_20240505)

哥白尼数字高程模型(Copernicus DEM, COP-DEM)由欧洲航天局(European Space Agency, 简称ESA或欧空局)发布&#xff0c;全球范围免费提供30米和90米分辨率DEM。COP-DEM是数字表面模型(DSM)&#xff0c;它表示地球表面(包括建筑物、基础设施和植被)的高程。COP-DEM是经过编辑的D…

c++set和map

目录 一、set的使用 1、set对象的创建 2、multiset 二、map的使用 1、map对象的创建 2、map的operator[] 序列式容器&#xff1a;vector、list、deque....单纯的存储数据&#xff0c;数据和数据之间没有关联 关联式容器&#xff1a;map、set.....不仅仅是存储数据&#x…

2000-2020年县域创业活跃度数据

2000-2020年县域创业活跃度数据 1、时间&#xff1a;2000-2020年 2、指标&#xff1a;地区名称、年份、行政区划代码、经度、纬度、所属城市、所属省份、年末总人口万人、户籍人口数万人、当年企业注册数目、县域创业活跃度1、县域创业活跃度2、县域创业活跃3 3、来源&#…

python数据可视化:显示两个变量间的关系散点图scatterplot()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 python数据可视化&#xff1a; 显示两个变量间的关系 散点图 scatterplot() [太阳]选择题 请问关于以下代码表述错误的选项是&#xff1f; import seaborn as sns import matplotlib.pyplot …

VISO流程图之子流程的使用

子流程的作用 整个流程图的框图多而且大&#xff0c;进行分块&#xff1b;让流程图简洁对于重复使用的流程&#xff0c;可以归结为一个子流程图&#xff0c;方便使用&#xff0c;避免大量的重复性工作&#xff1b; 新建子流程 方法1&#xff1a; 随便布局 框选3 和4 &#…

SQL:NOT IN与NOT EXISTS不等价

在对SQL语句进行性能优化时&#xff0c;经常用到一个技巧是将IN改写成EXISTS&#xff0c;这是等价改写&#xff0c;并没有什么问题。问题在于&#xff0c;将NOT IN改写成NOT EXISTS时&#xff0c;结果未必一样。 目录 一、举例验证二、三值逻辑简述三、附录&#xff1a;用到的S…

3.3Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3框架-企业级应用-Vue组合式API

为什么要使用Composition API 一个Options API实例 在前面的课程中&#xff0c;我们都是采用 Options API&#xff08;基于选项的 API &#xff09; 来写一个组件的。下面是一个实例&#xff1a; <template> Count is: {{ count }}, doubleCount is: {{ doubleCount…

深入理解网络原理3----TCP核心特性介绍(上)【面试高频考点】

文章目录 前言TCP协议段格式一、确认应答【保证可靠性传输的机制】二、超时重传【保证可靠性传输的机制】三、连接管理机制【保证可靠性传输的机制】3.1建立连接&#xff08;TCP三次握手&#xff09;---经典面试题3.2断开连接&#xff08;四次挥手&#xff09;3.3TCP状态转换 四…

【skill】onedrive的烦人问题

Onedrive的迷惑行为 安装Onedrive&#xff0c;如果勾选了同步&#xff0c;会默认把当前用户的数个文件夹&#xff08;桌面、文档、图片、下载 等等&#xff09;移动到安装时提示的那个文件夹 查看其中的一个文件的路径&#xff1a; 这样一整&#xff0c;原来的文件收到严重影…

政安晨:【Keras机器学习示例演绎】(三十五)—— 使用 LayerScale 的类注意图像变换器

目录 简介 导入 层刻度层 随机深度层 类注意力 会说话的头注意力 前馈网络 其他模块 拼凑碎片&#xff1a;CaiT 模型 定义模型配置 模型实例化 加载预训练模型 推理工具 加载图像 获取预测 关注层可视化 结论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#…

Topaz Video AI 5.0.3激活版 AI视频无损缩放增强

Topaz Video AI专注于很好地完成一些视频增强任务&#xff1a;去隔行&#xff0c;放大和运动插值。我们花了五年时间制作足够强大的人工智能模型&#xff0c;以便在真实世界的镜头上获得自然的结果。 Topaz Video AI 还将充分利用您的现代工作站&#xff0c;因为我们直接与硬件…

【数学建模】矩阵微分方程

一、说明 我相信你们中的许多人都熟悉微分方程&#xff0c;或者至少知道它们。微分方程是数学中最重要的概念之一&#xff0c;也许最著名的微分方程是布莱克-斯科尔斯方程&#xff0c;它控制着任何股票价格。 ​​ 股票价格的布莱克-斯科尔斯模型 微分方程可以由数学中的许多…

MidJourney提示词大全

大家好&#xff0c;我是无界生长。 这篇文章分享一下MidJourney提示词&#xff0c;篇幅内容有限&#xff0c;关注公众号&#xff1a;无界生长&#xff0c;后台回复&#xff1a;“MJ”&#xff0c;获取全部内容。 我是无界生长&#xff0c;如果你觉得我分享的内容对你有帮助&…

ArcGIS软件:地图投影的认识、投影定制

这一篇博客介绍的主要是如何在ArcGIS软件中查看投影数据&#xff0c;如何定制投影。 1.查看地图坐标系、投影数据 首先我们打开COUNTIES.shp数据&#xff08;美国行政区划图&#xff09;&#xff0c;并点击鼠标右键&#xff0c;再点击数据框属性就可以得到以下的界面。 我们从…

【Mac】graphpad prism for Mac(专业医学绘图工具) v10.2.3安装教程

软件介绍 GraphPad Prism for Mac是一款专业的科学数据分析和绘图软件&#xff0c;广泛用于生物医学和科学研究领域。它具有强大的统计分析功能&#xff0c;可以进行各种数据分析&#xff0c;包括描述性统计、生存分析、回归分析、方差分析等。同时&#xff0c;它还提供了丰富…
最新文章