zoukankan      html  css  js  c++  java
  • 玩转你画我猜(一):程序实现自动绘图

    说明

      本文发布较早,了解最新动态,请查看 GitHub 项目。(2021 年 5 月 注)

    准备

      IDE:Visual Studio

      Language:VB.NET / C#

      GitHub:AutoPaint.NET

      通过编程实现自动画画,以后玩你画我猜再也不用担心被吐槽啦ヾ(゚∀゚ゞ)

    第一节 导入图片

      程序画画,首先得导入一张图片作为模板。

      图像格式

      我们选择使用位图,它是由像素数据定义的图像格式

      WinForm 下使用封装了 GDI+ 位图的 System.Drawing.Bitmap 对象

      UWP 下可以使用 CanvasBitmap 对象(需要安装 Win2D 的 Nuget 包)

      导入本地文件

      直接导入本地图像资源

      详情见 System.Drawing.Bitmap 构造函数

      使用屏幕截图

      先用搜索引擎搜图,然后直接截屏

      System.Drawing.Graphics 对象提供从屏幕到 Graphics 的位块传输

      动态合成图像

      比如合成文本或者图案到指定的图像上

      System.Drawing.Graphics 对象提供一系列的 GDI+ 绘图命令

        ''' <summary>
        ''' 返回指定矩形区域的屏幕图像
        ''' </summary>
        ''' <param name="rect">指定的矩形区域</param>
        ''' <returns></returns>
        Public Function GetScreenImage(ByVal rect As Rectangle) As Bitmap
            Dim resultBmp As New Bitmap(rect.Width, rect.Height)
            Using pg As Graphics = Graphics.FromImage(resultBmp)
                pg.CopyFromScreen(rect.X, rect.Y, 0, 0, New Size(rect.Width, rect.Height))
            End Using
            Return resultBmp
        End Function
    VB.NET-GetScreenImage
        ''' <summary>
        ''' 返回指定文字生成的位图
        ''' </summary>
        ''' <param name="text">文本</param>
        ''' <param name="font">字体</param>
        ''' <param name="width">位图宽度</param>
        ''' <param name="height">位图高度</param>
        ''' <returns></returns>
        Public Function GetTextImage(ByVal text As String, ByVal font As Font, ByVal width As Integer, ByVal height As Integer) As Bitmap
            Dim resultBmp As New Bitmap(width, height)
            Using pg = Graphics.FromImage(resultBmp)
                pg.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias '抗锯齿
                pg.DrawString(text, font, Brushes.Black, 0, 0)
            End Using
            Return resultBmp
        End Function
    VB.NET-GetTextImage
    /// <summary>
    /// 返回指定矩形区域的屏幕图像
    /// </summary>
    /// <param name="rect">指定的矩形区域</param>
    /// <returns></returns>
    public Bitmap GetScreenImage(Rectangle rect)
    {
        Bitmap resultBmp = new Bitmap(rect.Width, rect.Height);
        using (Graphics pg = Graphics.FromImage(resultBmp)) {
            pg.CopyFromScreen(rect.X, rect.Y, 0, 0, new Size(rect.Width, rect.Height));
        }
        return resultBmp;
    }
    C#-GetScreenImage
    /// <summary>
    /// 返回指定文字生成的位图
    /// </summary>
    /// <param name="text">文本</param>
    /// <param name="font">字体</param>
    /// <param name="width">位图宽度</param>
    /// <param name="height">位图高度</param>
    /// <returns></returns>
    public Bitmap GetTextImage(string text, Font font, int width, int height)
    {
        Bitmap resultBmp = new Bitmap(width, height);
        using (pg == Graphics.FromImage(resultBmp)) {
            pg.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
            //抗锯齿
            pg.DrawString(text, font, Brushes.Black, 0, 0);
        }
        return resultBmp;
    }
    C#-GetTextImage

    第二节 图像处理

      接下来对模板图像进行处理,以便于下一步的轨迹寻找。

          二值化

      基于一个阈值 T,大于 T 的像素群设定为白色,小于 T 的像素群设定为黑色

      也就是将整个图像呈现出明显的只有黑和白的视觉效果

    图2-1 全局二值化

          细化

      将粗线条细化为细线条(线条宽度通常为一像素)

      细化算法是为了找出图像的轮廓,对不同图像的效果不同

    图2-2 轮廓化

     

    ''' <summary>
    ''' 提供对位图图像和颜色的一系列操作的对象
    ''' </summary>
    Public Class ImageProcess
        ''' <summary> 
        ''' 基于RGB根据指定阈值判断两个颜色是否相近
        ''' </summary> 
        Public Function CompareRGB(ByVal Color1 As Color, ByVal Color2 As Color, ByVal Distance As Single) As Boolean
            Dim r As Integer = Int(Color1.R) - Int(Color2.R)
            Dim g As Integer = Int(Color1.G) - Int(Color2.G)
            Dim b As Integer = Int(Color1.B) - Int(Color2.B)
            Dim absDis As Integer = Math.Sqrt(r * r + g * g + b * b)
            If absDis < Distance Then
                Return True
            Else
                Return False
            End If
        End Function
        ''' <summary> 
        ''' 基于HSB根据指定阈值判断两个颜色是否相近
        ''' </summary> 
        Public Function CompareHSB(ByVal Color1 As Color, ByVal Color2 As Color, ByVal Distance As Single) As Boolean
            '向量距离
            'Dim h As Single = (Color1.GetHue - Color2.GetHue) / 360
            'Dim s As Single = Color1.GetSaturation - Color2.GetSaturation
            'Dim b As Single = Color1.GetBrightness - Color2.GetBrightness
            'Dim absDis As Single = Math.Sqrt(h * h + s * s + b * b)
            'If absDis < Distance Then
            '    Return True
            'Else
            '    Return False
            'End If
            '向量夹角
            Dim h1 As Single = Color1.GetHue / 360
            Dim s1 As Single = Color1.GetSaturation
            Dim b1 As Single = Color1.GetBrightness
            Dim h2 As Single = Color2.GetHue / 360
            Dim s2 As Single = Color2.GetSaturation
            Dim b2 As Single = Color2.GetBrightness
            Dim absDis As Single = (h1 * h2 + s1 * s2 + b1 * b2) / (Math.Sqrt(h1 * h1 + s1 * s1 + b1 * b1) * Math.Sqrt(h2 * h2 + s2 * s2 + b2 * b2))
            If absDis > Distance / 5 + 0.8 Then
                Return True
            Else
                Return False
            End If
        End Function
        ''' <summary> 
        ''' 返回指定颜色的中值
        ''' </summary> 
        Public Function gethHD(ByVal color1 As Color)
            Dim HD, r, g, b As Integer
            r = color1.R
            g = color1.G
            b = color1.B
            HD = (r + g + b) / 3
            Return HD
        End Function
        ''' <summary>
        ''' 返回指定位图的颜色数组
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <returns></returns>
        Public Function GetColorArr(ByRef gBitmap As Bitmap) As Color(,)
            Dim TempArr(gBitmap.Width - 1, gBitmap.Height - 1) As Color
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    TempArr(i, j) = gBitmap.GetPixel(i, j)
                Next
            Next
            Return TempArr
        End Function
        ''' <summary>
        ''' 返回指定矩形区域的屏幕图像
        ''' </summary>
        ''' <param name="rect">指定的矩形区域</param>
        ''' <returns></returns>
        Public Function GetScreenImage(ByVal rect As Rectangle) As Bitmap
            Dim resultBmp As New Bitmap(rect.Width, rect.Height)
            Using pg As Graphics = Graphics.FromImage(resultBmp)
                pg.CopyFromScreen(rect.X, rect.Y, 0, 0, New Size(rect.Width, rect.Height))
            End Using
            Return resultBmp
        End Function
        ''' <summary>
        ''' 返回指定文字生成的位图
        ''' </summary>
        ''' <param name="text">文本</param>
        ''' <param name="font">字体</param>
        ''' <param name="width">位图宽度</param>
        ''' <param name="height">位图高度</param>
        ''' <returns></returns>
        Public Function GetTextImage(ByVal text As String, ByVal font As Font, ByVal width As Integer, ByVal height As Integer) As Bitmap
            Dim resultBmp As New Bitmap(width, height)
            Using pg = Graphics.FromImage(resultBmp)
                pg.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias '抗锯齿
                pg.DrawString(text, font, Brushes.Black, 0, 0)
            End Using
            Return resultBmp
        End Function
        ''' <summary>
        ''' 返回指定图位图的二值化图像
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <param name="gSplitNum"></param>
        ''' <returns></returns>
        Public Function GetThresholdImage(ByVal gBitmap As Bitmap, ByVal gSplitNum As Single, Optional IsHSB As Boolean = False) As Bitmap
            Dim ResultBitmap As New Bitmap(gBitmap.Width, gBitmap.Height)
            Dim ColorArr(,) = GetColorArr(gBitmap)
            Dim TempHD As Integer
            Dim IsOverThreshold = Function(ByVal C1 As Color, ByVal gNum As Single)
                                      TempHD = gethHD(C1)
                                      Return (If(IsHSB, (C1.GetHue / 360 + C1.GetSaturation + C1.GetBrightness) / 3 < gNum,
                                      TempHD < gNum))
                                  End Function
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    ResultBitmap.SetPixel(i, j, If(IsOverThreshold(ColorArr(i, j), gSplitNum), Color.Black, Color.White))
                Next
            Next
    
            Return ResultBitmap
        End Function
        ''' <summary>
        ''' 返回指定位图的轮廓图像
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <param name="gDistance"></param>
        ''' <returns></returns>
        Public Function GetOutLineImage(ByVal gBitmap As Bitmap, ByVal gDistance As Single, Optional IsHSB As Boolean = False) As Bitmap
            Dim xArray2() As Short = {0, 1, 0, -1}
            Dim yArray2() As Short = {-1, 0, 1, 0}
            'Dim ResultBitmap As New Bitmap(gBitmap) '在原图的基础上绘图
            Dim ResultBitmap As New Bitmap(gBitmap.Width, gBitmap.Height)
            Dim Color1, Color2 As Color
            Dim CompareColor = Function(ByVal C1 As Color, ByVal C2 As Color, ByVal Distance As Single)
                                   Return If(IsHSB,
                                   CompareHSB(Color1, Color2, Distance),
                                   CompareRGB(Color1, Color2, Distance))
                               End Function
            Dim CompareColorExtra = Function(ByVal C1 As Color, ByVal C2 As Color)
                                        Return If(IsHSB,
                                        Color1.GetBrightness - Color2.GetBrightness > 0,
                                        gethHD(Color1) - gethHD(Color2) > 0)
                                    End Function
            Dim ColorArr(,) = GetColorArr(gBitmap)
            For i = 1 To gBitmap.Width - 2
                For j = 1 To gBitmap.Height - 2
                    ResultBitmap.SetPixel(i, j, Color.White)
                    Color1 = ColorArr(i, j)
                    For p = 0 To 3
                        Color2 = ColorArr(i + xArray2(p), j + yArray2(p))
                        If Not CompareColor(Color1, Color2, gDistance) And CompareColorExtra(Color1, Color2) Then
                            ResultBitmap.SetPixel(i, j, Color.Black)
                            ' ResultBitmap.SetPixel(i, j, ColorArr(i, j))
                        End If
                    Next
                Next
            Next
            Return ResultBitmap
        End Function
        ''' <summary>
        ''' 返回指定位图的空心图像
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <returns></returns>
        Public Function GetAroundImage(gBitmap As Bitmap)
            Dim ResultBitmap As New Bitmap(gBitmap.Width, gBitmap.Height)
            Dim ImageBolArr(,) As Integer = GetImageBol(gBitmap)
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    If ImageBolArr(i, j) = 1 AndAlso CheckPointAround(ImageBolArr, i, j) = False Then
                        ResultBitmap.SetPixel(i, j, Color.Black)
                    Else
                        ResultBitmap.SetPixel(i, j, Color.White)
                    End If
                Next
            Next
            Return ResultBitmap
        End Function
        ''' <summary>
        ''' 返回指定位图的反相图像
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <returns></returns>
        Public Function GetInvertImage(gBitmap As Bitmap)
            Dim ResultBitmap As New Bitmap(gBitmap.Width, gBitmap.Height)
            Dim ImageBolArr(,) As Integer = GetImageBol(gBitmap)
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    If ImageBolArr(i, j) = 1 Then
                        ResultBitmap.SetPixel(i, j, Color.White)
                    Else
                        ResultBitmap.SetPixel(i, j, Color.Black)
                    End If
                Next
            Next
            Return ResultBitmap
        End Function
        ''' <summary>
        ''' 返回指定位图的色块图像
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <returns></returns>
        Public Function GetLumpImage(gBitmap As Bitmap, Optional Range As Integer = 10)
            Dim ResultBitmap As New Bitmap(gBitmap.Width, gBitmap.Height)
            Dim ColorArr(,) = GetColorArr(gBitmap)
            Dim R, G, B As Integer
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    R = Int(ColorArr(i, j).R / Range) * Range
                    G = Int(ColorArr(i, j).G / Range) * Range
                    B = Int(ColorArr(i, j).B / Range) * Range
                    ResultBitmap.SetPixel(i, j, Color.FromArgb(R, G, B))
                Next
            Next
            Return ResultBitmap
        End Function
        ''' <summary>
        ''' 返回指定位图的二值化数据
        ''' </summary>
        ''' <param name="gBitmap"></param>
        ''' <returns></returns>
        Private Function GetImageBol(ByVal gBitmap As Bitmap) As Integer(,)
            Dim ResultArr(gBitmap.Width - 1, gBitmap.Height - 1) As Integer
            For i = 0 To gBitmap.Width - 1
                For j = 0 To gBitmap.Height - 1
                    If Not gBitmap.GetPixel(i, j).Equals(Color.FromArgb(255, 255, 255)) Then
                        ResultArr(i, j) = 1
                    Else
                        ResultArr(i, j) = 0
                    End If
                Next
            Next
            Return ResultArr
        End Function
        ''' <summary>
        ''' 检查一个点是否被包围
        ''' </summary>
        ''' <param name="BolArr"></param>
        ''' <param name="x"></param>
        ''' <param name="y"></param>
        ''' <returns></returns>
        Private Function CheckPointAround(BolArr As Integer(,), ByVal x As Integer, ByVal y As Integer) As Boolean
            If Not (x > 0 And y > 0 And x < BolArr.GetUpperBound(0) And y < BolArr.GetUpperBound(1)) Then Return True
            If BolArr(x - 1, y) = 1 And BolArr(x + 1, y) = 1 And BolArr(x, y - 1) = 1 And BolArr(x, y + 1) = 1 Then
                Return True '当前点为实体内部
            Else
                Return False '当前点为实体边缘
            End If
        End Function
    End Class
    VB.NET-ImageProcess
    using System;
    using System.Drawing;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    /// <summary>
    /// 提供对位图图像和颜色的一系列操作的对象
    /// </summary>
    public class ImageProcess
    {
        /// <summary> 
        /// 基于RGB根据指定阈值判断两个颜色是否相近
        /// </summary> 
        public bool CompareRGB(Color Color1, Color Color2, float Distance)
        {
            int r = Conversion.Int(Color1.R) - Conversion.Int(Color2.R);
            int g = Conversion.Int(Color1.G) - Conversion.Int(Color2.G);
            int b = Conversion.Int(Color1.B) - Conversion.Int(Color2.B);
            int absDis = Math.Sqrt(r * r + g * g + b * b);
            if (absDis < Distance) {
                return true;
            } else {
                return false;
            }
        }
        /// <summary> 
        /// 基于HSB根据指定阈值判断两个颜色是否相近
        /// </summary> 
        public bool CompareHSB(Color Color1, Color Color2, float Distance)
        {
            //向量距离
            //Dim h As Single = (Color1.GetHue - Color2.GetHue) / 360
            //Dim s As Single = Color1.GetSaturation - Color2.GetSaturation
            //Dim b As Single = Color1.GetBrightness - Color2.GetBrightness
            //Dim absDis As Single = Math.Sqrt(h * h + s * s + b * b)
            //If absDis < Distance Then
            //    Return True
            //Else
            //    Return False
            //End If
            //向量夹角
            float h1 = Color1.GetHue / 360;
            float s1 = Color1.GetSaturation;
            float b1 = Color1.GetBrightness;
            float h2 = Color2.GetHue / 360;
            float s2 = Color2.GetSaturation;
            float b2 = Color2.GetBrightness;
            float absDis = (h1 * h2 + s1 * s2 + b1 * b2) / (Math.Sqrt(h1 * h1 + s1 * s1 + b1 * b1) * Math.Sqrt(h2 * h2 + s2 * s2 + b2 * b2));
            if (absDis > Distance / 5 + 0.8) {
                return true;
            } else {
                return false;
            }
        }
        /// <summary> 
        /// 返回指定颜色的中值
        /// </summary> 
        public object gethHD(Color color1)
        {
            int HD = 0;
            int r = 0;
            int g = 0;
            int b = 0;
            r = color1.R;
            g = color1.G;
            b = color1.B;
            HD = (r + g + b) / 3;
            return HD;
        }
        /// <summary>
        /// 返回指定位图的颜色数组
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <returns></returns>
        public Color[,] GetColorArr(ref Bitmap gBitmap)
        {
            Color[,] TempArr = new Color[gBitmap.Width, gBitmap.Height];
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    TempArr(i, j) = gBitmap.GetPixel(i, j);
                }
            }
            return TempArr;
        }
        /// <summary>
        /// 返回指定矩形区域的屏幕图像
        /// </summary>
        /// <param name="rect">指定的矩形区域</param>
        /// <returns></returns>
        public Bitmap GetScreenImage(Rectangle rect)
        {
            Bitmap resultBmp = new Bitmap(rect.Width, rect.Height);
            using (Graphics pg = Graphics.FromImage(resultBmp)) {
                pg.CopyFromScreen(rect.X, rect.Y, 0, 0, new Size(rect.Width, rect.Height));
            }
            return resultBmp;
        }
        /// <summary>
        /// 返回指定文字生成的位图
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="font">字体</param>
        /// <param name="width">位图宽度</param>
        /// <param name="height">位图高度</param>
        /// <returns></returns>
        public Bitmap GetTextImage(string text, Font font, int width, int height)
        {
            Bitmap resultBmp = new Bitmap(width, height);
            using (pg == Graphics.FromImage(resultBmp)) {
                pg.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
                //抗锯齿
                pg.DrawString(text, font, Brushes.Black, 0, 0);
            }
            return resultBmp;
        }
        /// <summary>
        /// 返回指定图位图的二值化图像
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <param name="gSplitNum"></param>
        /// <returns></returns>
        public Bitmap GetThresholdImage(Bitmap gBitmap, float gSplitNum, bool IsHSB = false)
        {
            Bitmap ResultBitmap = new Bitmap(gBitmap.Width, gBitmap.Height);
            [,] ColorArr = GetColorArr(ref gBitmap);
            int TempHD = 0;
            dynamic IsOverThreshold = (Color C1, float gNum) =>
            {
                TempHD = gethHD(C1);
                return (IsHSB ? (C1.GetHue / 360 + C1.GetSaturation + C1.GetBrightness) / 3 < gNum : TempHD < gNum);
            };
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    ResultBitmap.SetPixel(i, j, IsOverThreshold(ColorArr(i, j), gSplitNum) ? Color.Black : Color.White);
                }
            }
    
            return ResultBitmap;
        }
        /// <summary>
        /// 返回指定位图的轮廓图像
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <param name="gDistance"></param>
        /// <returns></returns>
        public Bitmap GetOutLineImage(Bitmap gBitmap, float gDistance, bool IsHSB = false)
        {
            short[] xArray2 = {
                0,
                1,
                0,
                -1
            };
            short[] yArray2 = {
                -1,
                0,
                1,
                0
            };
            //Dim ResultBitmap As New Bitmap(gBitmap) '在原图的基础上绘图
            Bitmap ResultBitmap = new Bitmap(gBitmap.Width, gBitmap.Height);
            Color Color1 = default(Color);
            Color Color2 = default(Color);
            dynamic CompareColor = (Color C1, Color C2, float Distance) => { return IsHSB ? CompareHSB(Color1, Color2, Distance) : CompareRGB(Color1, Color2, Distance); };
            dynamic CompareColorExtra = (Color C1, Color C2) => { return IsHSB ? Color1.GetBrightness - Color2.GetBrightness > 0 : gethHD(Color1) - gethHD(Color2) > 0; };
            [,] ColorArr = GetColorArr(ref gBitmap);
            for (i = 1; i <= gBitmap.Width - 2; i++) {
                for (j = 1; j <= gBitmap.Height - 2; j++) {
                    ResultBitmap.SetPixel(i, j, Color.White);
                    Color1 = ColorArr(i, j);
                    for (p = 0; p <= 3; p++) {
                        Color2 = ColorArr(i + xArray2[p], j + yArray2[p]);
                        if (!CompareColor(Color1, Color2, gDistance) & CompareColorExtra(Color1, Color2)) {
                            ResultBitmap.SetPixel(i, j, Color.Black);
                            // ResultBitmap.SetPixel(i, j, ColorArr(i, j))
                        }
                    }
                }
            }
            return ResultBitmap;
        }
        /// <summary>
        /// 返回指定位图的空心图像
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <returns></returns>
        public object GetAroundImage(Bitmap gBitmap)
        {
            Bitmap ResultBitmap = new Bitmap(gBitmap.Width, gBitmap.Height);
            int[,] ImageBolArr = GetImageBol(gBitmap);
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    if (ImageBolArr[i, j] == 1 && CheckPointAround(ImageBolArr, i, j) == false) {
                        ResultBitmap.SetPixel(i, j, Color.Black);
                    } else {
                        ResultBitmap.SetPixel(i, j, Color.White);
                    }
                }
            }
            return ResultBitmap;
        }
        /// <summary>
        /// 返回指定位图的反相图像
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <returns></returns>
        public object GetInvertImage(Bitmap gBitmap)
        {
            Bitmap ResultBitmap = new Bitmap(gBitmap.Width, gBitmap.Height);
            int[,] ImageBolArr = GetImageBol(gBitmap);
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    if (ImageBolArr[i, j] == 1) {
                        ResultBitmap.SetPixel(i, j, Color.White);
                    } else {
                        ResultBitmap.SetPixel(i, j, Color.Black);
                    }
                }
            }
            return ResultBitmap;
        }
        /// <summary>
        /// 返回指定位图的色块图像
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <returns></returns>
        public object GetLumpImage(Bitmap gBitmap, int Range = 10)
        {
            Bitmap ResultBitmap = new Bitmap(gBitmap.Width, gBitmap.Height);
            [,] ColorArr = GetColorArr(ref gBitmap);
            int R = 0;
            int G = 0;
            int B = 0;
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    R = Conversion.Int(ColorArr(i, j).R / Range) * Range;
                    G = Conversion.Int(ColorArr(i, j).G / Range) * Range;
                    B = Conversion.Int(ColorArr(i, j).B / Range) * Range;
                    ResultBitmap.SetPixel(i, j, Color.FromArgb(R, G, B));
                }
            }
            return ResultBitmap;
        }
        /// <summary>
        /// 返回指定位图的二值化数据
        /// </summary>
        /// <param name="gBitmap"></param>
        /// <returns></returns>
        private int[,] GetImageBol(Bitmap gBitmap)
        {
            int[,] ResultArr = new int[gBitmap.Width, gBitmap.Height];
            for (i = 0; i <= gBitmap.Width - 1; i++) {
                for (j = 0; j <= gBitmap.Height - 1; j++) {
                    if (!gBitmap.GetPixel(i, j).Equals(Color.FromArgb(255, 255, 255))) {
                        ResultArr[i, j] = 1;
                    } else {
                        ResultArr[i, j] = 0;
                    }
                }
            }
            return ResultArr;
        }
        /// <summary>
        /// 检查一个点是否被包围
        /// </summary>
        /// <param name="BolArr"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private bool CheckPointAround(int[,] BolArr, int x, int y)
        {
            if (!(x > 0 & y > 0 & x < BolArr.GetUpperBound(0) & y < BolArr.GetUpperBound(1)))
                return true;
            if (BolArr[x - 1, y] == 1 & BolArr[x + 1, y] == 1 & BolArr[x, y - 1] == 1 & BolArr[x, y + 1] == 1) {
                return true;
                //当前点为实体内部
            } else {
                return false;
                //当前点为实体边缘
            }
        }
    }
    C#-ImageProcess

    第三节 画笔轨迹

      从非黑即白的像素数据中计算出轨迹。

          递归循迹

      首先将图像的二值化数据保存在一个二维数组里

      程序绘图时仅绘制值为 1 的元素所对应的位置

      然后寻找画笔起始位置,依次检查每个位置,当对应值为1时该点即为起点

      最后递归检查每一个邻居点,同步模拟鼠标操作

          空心轨迹

      只要某像素位置的上下左右位置均为 1 即认为该点在实体内部

      绘制时跳过该像素就可以实现空心(主要用于空心字体的绘制)

    ''' <summary>
    ''' 提供由图像循迹生成绘图序列的对象
    ''' </summary>
    Public Class SequenceManager
        ''' <summary>
        ''' 绘制序列的集合
        ''' </summary>
        Public SequenceList As List(Of PointSequence)
    
        Public Sub New(BolArr(,) As Integer)
            SequenceList = New List(Of PointSequence)
            CalculateSequence(BolArr)
        End Sub
        ''' <summary>
        ''' 创建新的序列
        ''' </summary>
        Private Sub CreateNewSequence()
            SequenceList.Add(New PointSequence)
        End Sub
        ''' <summary>
        ''' 添加新的位置
        ''' </summary>
        Private Sub AddPoint(point As PointF)
            SequenceList.Last.PointList.Add(point)
        End Sub
        Dim xArray() As Integer = {-1, 0, 1, 1, 1, 0, -1, -1}
        Dim yArray() As Integer = {-1, -1, -1, 0, 1, 1, 1, 0}
        Dim NewStart As Boolean
        ''' <summary>
        ''' 递归循迹
        ''' </summary>
        Private Sub CheckMove(ByRef BolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer, ByVal StepNum As Integer)
            Application.DoEvents() '处理主线程消息
            If StepNum > 10000 Then Return
            Dim xBound As Integer = BolArr.GetUpperBound(0)
            Dim yBound As Integer = BolArr.GetUpperBound(1)
            Dim dx, dy As Integer
            Dim AroundValue As Integer = GetAroundValue(BolArr, x, y)
            If AroundValue > 2 AndAlso AroundValue < 8 Then
                Return
            End If
            For i = 0 To 7
                dx = x + xArray(i)
                dy = y + yArray(i)
                If Not (dx > 0 And dy > 0 And dx < xBound And dy < yBound) Then
                    Return
                ElseIf BolArr(dx, dy) = 1 Then
                    BolArr(dx, dy) = 0
                    If NewStart = True Then
                        Me.CreateNewSequence()
                        Me.AddPoint(New PointF(dx, dy))
                        NewStart = False
                    Else
                        Me.AddPoint(New PointF(dx, dy))
                    End If
                    CheckMove(BolArr, dx, dy, StepNum + 1)
                    NewStart = True
                End If
            Next
        End Sub
        ''' <summary>
        ''' 计算序列
        ''' </summary>
        Private Sub CalculateSequence(BolArr(,) As Integer)
            Dim xCount As Integer = BolArr.GetUpperBound(0)
            Dim yCount As Integer = BolArr.GetUpperBound(1)
            Dim CP As New Point(xCount / 2, yCount / 2)
            Dim R As Integer = 0
            For R = 0 To If(xCount > yCount, xCount, yCount)
                For Theat = 0 To Math.PI * 2 Step 1 / R
                    Dim dx As Integer = CP.X + R * Math.Cos(Theat)
                    Dim dy As Integer = CP.Y + R * Math.Sin(Theat)
                    If Not (dx > 0 And dy > 0 And dx < xCount And dy < yCount) Then Continue For
                    If BolArr(dx, dy) = 1 Then
                        BolArr(dx, dy) = 0
                        Me.CreateNewSequence()
                        Me.AddPoint(New PointF(dx, dy))
                        CheckMove(BolArr, dx, dy, 0)
                        NewStart = True
                    End If
                Next
            Next
        End Sub
        ''' <summary>
        ''' 返回指定像素位置的权值
        ''' </summary>
        Private Function GetAroundValue(ByRef BolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer) As Integer
            Dim dx, dy, ResultValue As Integer
            Dim xBound As Integer = BolArr.GetUpperBound(0)
            Dim yBound As Integer = BolArr.GetUpperBound(1)
            For i = 0 To 7
                dx = x + xArray(i)
                dy = y + yArray(i)
                If dx > 0 And dy > 0 And dx < xBound And dy < yBound Then
                    If BolArr(dx, dy) = 1 Then
                        ResultValue += 1
                    End If
                End If
            Next
            Return ResultValue
        End Function
    End Class
    VB.NET-SequenceManager
    ''' <summary>
    ''' 表示一条画图曲线的绘制序列
    ''' </summary>
    Public Class PointSequence
        Public PointList As New List(Of PointF)
    End Class
    VB.NET-PointSequence
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    /// <summary>
    /// 提供由图像循迹生成绘图序列的对象
    /// </summary>
    public class SequenceManager
    {
        /// <summary>
        /// 绘制序列的集合
        /// </summary>
    
        public List<PointSequence> SequenceList;
        public SequenceManager(int[,] BolArr)
        {
            SequenceList = new List<PointSequence>();
            CalculateSequence(BolArr);
        }
        /// <summary>
        /// 创建新的序列
        /// </summary>
        private void CreateNewSequence()
        {
            SequenceList.Add(new PointSequence());
        }
        /// <summary>
        /// 添加新的位置
        /// </summary>
        private void AddPoint(PointF point)
        {
            SequenceList.Last.PointList.Add(point);
        }
        int[] xArray = {
            -1,
            0,
            1,
            1,
            1,
            0,
            -1,
            -1
        };
        int[] yArray = {
            -1,
            -1,
            -1,
            0,
            1,
            1,
            1,
            0
        };
        bool NewStart;
        /// <summary>
        /// 递归循迹
        /// </summary>
        private void CheckMove(ref int[,] BolArr, int x, int y, int StepNum)
        {
            Application.DoEvents();
            //处理主线程消息
            if (StepNum > 10000)
                return;
            int xBound = BolArr.GetUpperBound(0);
            int yBound = BolArr.GetUpperBound(1);
            int dx = 0;
            int dy = 0;
            int AroundValue = GetAroundValue(ref BolArr, x, y);
            if (AroundValue > 2 && AroundValue < 8) {
                return;
            }
            for (i = 0; i <= 7; i++) {
                dx = x + xArray[i];
                dy = y + yArray[i];
                if (!(dx > 0 & dy > 0 & dx < xBound & dy < yBound)) {
                    return;
                } else if (BolArr[dx, dy] == 1) {
                    BolArr[dx, dy] = 0;
                    if (NewStart == true) {
                        this.CreateNewSequence();
                        this.AddPoint(new PointF(dx, dy));
                        NewStart = false;
                    } else {
                        this.AddPoint(new PointF(dx, dy));
                    }
                    CheckMove(ref BolArr, dx, dy, StepNum + 1);
                    NewStart = true;
                }
            }
        }
        /// <summary>
        /// 计算序列
        /// </summary>
        private void CalculateSequence(int[,] BolArr)
        {
            int xCount = BolArr.GetUpperBound(0);
            int yCount = BolArr.GetUpperBound(1);
            Point CP = new Point(xCount / 2, yCount / 2);
            int R = 0;
            for (R = 0; R <= xCount > yCount ? xCount : yCount; R++) {
                for (Theat = 0; Theat <= Math.PI * 2; Theat += 1 / R) {
                    int dx = CP.X + R * Math.Cos(Theat);
                    int dy = CP.Y + R * Math.Sin(Theat);
                    if (!(dx > 0 & dy > 0 & dx < xCount & dy < yCount))
                        continue;
                    if (BolArr[dx, dy] == 1) {
                        BolArr[dx, dy] = 0;
                        this.CreateNewSequence();
                        this.AddPoint(new PointF(dx, dy));
                        CheckMove(ref BolArr, dx, dy, 0);
                        NewStart = true;
                    }
                }
            }
        }
        /// <summary>
        /// 返回指定像素位置的权值
        /// </summary>
        private int GetAroundValue(ref int[,] BolArr, int x, int y)
        {
            int dx = 0;
            int dy = 0;
            int ResultValue = 0;
            int xBound = BolArr.GetUpperBound(0);
            int yBound = BolArr.GetUpperBound(1);
            for (i = 0; i <= 7; i++) {
                dx = x + xArray[i];
                dy = y + yArray[i];
                if (dx > 0 & dy > 0 & dx < xBound & dy < yBound) {
                    if (BolArr[dx, dy] == 1) {
                        ResultValue += 1;
                    }
                }
            }
            return ResultValue;
        }
    }
    C#-SequenceManager
    using System;
    using System.Collections.Generic;
    /// <summary>
    /// 表示一条画图曲线的绘制序列
    /// </summary>
    public class PointSequence
    {
        public List<PointF> PointList = new List<PointF>();
    }
    C#-PointSequence

    第四节 绘图

      最后,控制鼠标在画板上依次画出线条序列即可。

      光标按键控制

      调用 user32.dll 库下的 mouse_event() ,它提供综合的光标击键和光标动作模拟

        光标指针移动

      System.Windows.Forms.Cursor 对象提供对系统光标的访问

      直接对 Cursor.Position 赋值就能控制指针坐标

      也可以调用 user32.dll 库下的 SetCursorPos() 实现模拟光标移动

      Windows画板的局限性

      至此,我们的程序已经可以通过控制光标在 Windows 画板上实现自动绘图

      但由于画板短时间内不能响应过多的 Windows 消息,导致画图速度不能过快

      同时程序也很难控制画笔的笔触大小与色彩

         Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Int32, ByVal dx As Int32, ByVal dy As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32)
        Private Declare Function SetCursorPos Lib "user32" (ByVal x As Integer, ByVal y As Integer) As Integer
        ''' <summary>
        ''' 模拟鼠标左键按下或弹起
        ''' </summary>
        ''' <param name="dx"></param>
        ''' <param name="dy"></param>
        ''' <param name="type"></param>
        Private Sub MouseDownUp(ByVal dx As Integer, ByVal dy As Integer, ByVal type As Boolean)
            If type Then '按下
                mouse_event(&H2, 0, 0, 0, IntPtr.Zero)
            Else '弹起
                mouse_event(&H4, 0, 0, 0, IntPtr.Zero)
            End If
        End Sub
        ''' <summary>
        ''' 模拟鼠标移动
        ''' </summary>
        ''' <param name="dx"></param>
        ''' <param name="dy"></param>
        Private Sub MouseMove(ByVal dx As Integer, ByVal dy As Integer)
            Cursor.Position = New Point(dx, dy)
        End Sub
    VB.NET-MouseControl
    using System.Runtime.InteropServices;
            [DllImport("user32")]
            public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
    private void MouseDownUp(int dx, int dy, bool type)
    {
        //按下
        if (type) {
            mouse_event(0x2, 0, 0, 0, IntPtr.Zero);
        //弹起
        } else {
            mouse_event(0x4, 0, 0, 0, IntPtr.Zero);
        }
    }
    /// <summary>
    /// 模拟鼠标移动
    /// </summary>
    /// <param name="dx"></param>
    /// <param name="dy"></param>
    private void MouseMove(int dx, int dy)
    {
        Cursor.Position = new Point(dx, dy);
    }
    C#-MouseControl

    视频

    附录

      后续文章:更优秀的自动绘图程序

      后续文章:儿童涂鸦遇上程序绘图

      后续文章:程序如何画动漫美少

  • 相关阅读:
    ADSL PPPoE出错详解及宽带连接中的一些错误代码含义
    2007年世界顶级防火墙排名(附下载地址)
    Asp.net Mvc问题索引
    .NET 操作GPRS Model的类库 ATSMS
    .NET 3.5多个工程编译的DOS命令
    Google Chrome浏览器JS执行效率惊人 实测比IE快十几倍
    FTP文件同步工具(FTP_File_Synchronizer) 源代码
    [转载] ORACLE中SQL查询优化研究
    ext的grid导出为excel 方法
    数据库分页SQL语句
  • 原文地址:https://www.cnblogs.com/experdot/p/4494351.html
Copyright © 2011-2022 走看看