程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 自動繪圖AI:程序如何畫出動漫美少女,ai美少女

自動繪圖AI:程序如何畫出動漫美少女,ai美少女

編輯:關於.NET

自動繪圖AI:程序如何畫出動漫美少女,ai美少女


  全新的圖形引擎與AI算法,高效流暢地繪出任何一副美麗的圖像。

  IDE:VisualStudio 2015

  Language:VB.NET/C#

  Graphics:EDGameEngine

第一節 背景

  背景是圖畫裡襯托主體事物的景象。

圖1.1 先畫個藍藍的天空

  藍天、白雲和大地,程序最擅長這種色調單一的塗抹了

第二節 輪廓

  輪廓是物體的外周或圖形的外框。

圖2.2 勾勒人物和衣飾輪廓

  現在AI要控制筆觸大小和顏色,讓圖像的主體顯現出來

第三節 光影

  光影是物體在光的照射下呈現出明與暗的關系。

圖3.1 光影提升畫面質感

  AI可不懂什麼是光影,在上一步的基礎上優化細節即可

第四節 潤色

  潤色是增加物體本身及其周圍的色彩。

圖4.1 畫面潤色

  這是關鍵一步,AI需要將丟失的顏色細節補缺回來

第五節 成型

  大功告成!前面所有的步驟都是為這一步鋪墊。

圖5.1 人物已經栩栩如生啦

  事實上AI只進行這一步也可以畫出完整的圖像,但沒有過渡會顯得生硬

第六節 算法

  上文的圖片是程序畫的,文字是筆者瞎編的。

  AI只會計算畫筆軌跡,然後一遍遍重繪,感覺上是人類畫手的效果

  不再是二值化

  • 因為現在要繪制全彩圖像,將圖像劃分為只有黑和白的效果已經沒有什麼意義,二值化不再適用
  • 適用的方法是將RGB顏色空間劃分為若干個顏色子空間,然後逐個處理一幅圖像中屬於某個子空間的區域

  自動循跡

  • 循跡算法沒有大的變動,仍是早前博客裡貼出的代碼
  • 彩色圖像線條較短,可以不再計算點周圍的權值用來中斷軌跡

  重繪

  • 程序先選擇筆觸較大、顏色淡的畫筆繪制一遍,然後在這基礎上逐步減小筆觸並加深色彩
  • 直接按照標准筆觸可以一遍成型,但會顯得突兀和生硬,畢竟這個AI不是真的在思考如何畫一幅圖像
Imports System.Numerics ''' <summary> ''' 表示自動循跡並生成繪制序列的AI ''' </summary> Public Class SequenceAI ''' <summary> ''' 線條序列List ''' </summary> ''' <returns></returns> Public Property Sequences As List(Of PointSequence) ''' <summary> ''' 掃描方式 ''' </summary> Public Property ScanMode As ScanMode = ScanMode.Rect 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> ''' 創建並初始化一個可自動生成繪制序列AI的實例 ''' </summary> Public Sub New(BolArr(,) As Integer) Sequences = New List(Of PointSequence) CalculateSequence(BolArr) For Each SubItem In Sequences SubItem.CalcSize() Next End Sub ''' <summary> ''' 新增一個序列 ''' </summary> Private Sub CreateNewSequence() Sequences.Add(New PointSequence) End Sub ''' <summary> ''' 在序列List末尾項新增一個點 ''' </summary> Private Sub AddPoint(point As Vector2) Sequences.Last.Points.Add(point) End Sub ''' <summary> ''' 計算序列 ''' </summary> Private Sub CalculateSequence(BolArr(,) As Integer) If ScanMode = ScanMode.Rect Then ScanRect(BolArr) Else ScanCircle(BolArr) End If End Sub ''' <summary> ''' 圓形掃描 ''' </summary> ''' <param name="BolArr"></param> Private Sub ScanCircle(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 = CInt(CP.X + R * Math.Cos(Theat)) Dim dy As Integer = CInt(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 Vector2(dx, dy)) CheckMove(BolArr, dx, dy, 0) NewStart = True End If Next Next End Sub ''' <summary> ''' 矩形掃描 ''' </summary> ''' <param name="BolArr"></param> Private Sub ScanRect(BolArr(,) As Integer) Dim xCount As Integer = BolArr.GetUpperBound(0) Dim yCount As Integer = BolArr.GetUpperBound(1) For i = 0 To xCount - 1 For j = 0 To yCount - 1 Dim dx As Integer = i Dim dy As Integer = j 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 Vector2(dx, dy)) CheckMove(BolArr, dx, dy, 0) NewStart = True End If Next Next End Sub ''' <summary> ''' 遞歸循跡算法 ''' </summary> Private Sub CheckMove(ByRef bolArr(,) As Integer, ByVal x As Integer, ByVal y As Integer, ByVal StepNum As Integer) If StepNum > 1000 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 Vector2(dx, dy)) NewStart = False Else Me.AddPoint(New Vector2(dx, dy)) End If CheckMove(bolArr, dx, dy, StepNum + 1) NewStart = True End If 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 ''' <summary> ''' 線條掃描方式 ''' </summary> Public Enum ScanMode ''' <summary> ''' 矩形掃描 ''' </summary> Rect ''' <summary> ''' 圓形掃描 ''' </summary> Circle End Enum VB.NET-SequenceAI Imports System.Numerics ''' <summary> ''' 表示由一系列點向量組成的線條 ''' </summary> Public Class PointSequence Public Property Points As New List(Of Vector2) Public Property Sizes As Single() ''' <summary> ''' 計算畫筆大小 ''' </summary> Public Sub CalcSize() If Points.Count < 1 Then Exit Sub Static Mid, PenSize As Single ReDim Sizes(Points.Count - 1) For i = 0 To Points.Count - 1 Mid = CSng(Math.Abs(i - Points.Count / 2)) PenSize = 1 - Mid / Points.Count * 2 Sizes(i) = PenSize Next End Sub End Class VB.NET-PointSequence using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Numerics; /// <summary> /// 表示自動循跡並生成繪制序列的AI /// </summary> public class SequenceAI { /// <summary> /// 線條序列List /// </summary> /// <returns></returns> public List<PointSequence> Sequences { get; set; } /// <summary> /// 掃描方式 /// </summary> public ScanMode ScanMode { get; set; } int[] xArray = { -1, 0, 1, 1, 1, 0, -1, -1 }; int[] yArray = { -1, -1, -1, 0, 1, 1, 1, 0 }; bool NewStart; /// <summary> /// 創建並初始化一個可自動生成繪制序列AI的實例 /// </summary> public SequenceAI(int[,] BolArr) { Sequences = new List<PointSequence>(); CalculateSequence(BolArr); foreach (object SubItem_loopVariable in Sequences) { SubItem = SubItem_loopVariable; SubItem.CalcSize(); } } /// <summary> /// 新增一個序列 /// </summary> private void CreateNewSequence() { Sequences.Add(new PointSequence()); } /// <summary> /// 在序列List末尾項新增一個點 /// </summary> private void AddPoint(Vector2 point) { Sequences.Last.Points.Add(point); } /// <summary> /// 計算序列 /// </summary> private void CalculateSequence(int[,] BolArr) { if (ScanMode == ScanMode.Rect) { ScanRect(BolArr); } else { ScanCircle(BolArr); } } /// <summary> /// 圓形掃描 /// </summary> /// <param name="BolArr"></param> private void ScanCircle(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 = Convert.ToInt32(CP.X + R * Math.Cos(Theat)); int dy = Convert.ToInt32(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 Vector2(dx, dy)); CheckMove(ref BolArr, dx, dy, 0); NewStart = true; } } } } /// <summary> /// 矩形掃描 /// </summary> /// <param name="BolArr"></param> private void ScanRect(int[,] BolArr) { int xCount = BolArr.GetUpperBound(0); int yCount = BolArr.GetUpperBound(1); for (i = 0; i <= xCount - 1; i++) { for (j = 0; j <= yCount - 1; j++) { int dx = i; int dy = j; if (!(dx > 0 & dy > 0 & dx < xCount & dy < yCount)) continue; if (BolArr[dx, dy] == 1) { BolArr[dx, dy] = 0; this.CreateNewSequence(); this.AddPoint(new Vector2(dx, dy)); CheckMove(ref BolArr, dx, dy, 0); NewStart = true; } } } } /// <summary> /// 遞歸循跡算法 /// </summary> private void CheckMove(ref int[,] bolArr, int x, int y, int StepNum) { if (StepNum > 1000) 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 AndAlso AroundValue < 8 Then //Return //End If 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 Vector2(dx, dy)); NewStart = false; } else { this.AddPoint(new Vector2(dx, dy)); } CheckMove(ref bolArr, dx, dy, StepNum + 1); 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; } } /// <summary> /// 線條掃描方式 /// </summary> public enum ScanMode { /// <summary> /// 矩形掃描 /// </summary> Rect, /// <summary> /// 圓形掃描 /// </summary> Circle } C#-SequenceAI using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Numerics; /// <summary> /// 表示由一系列點向量組成的線條 /// </summary> public class PointSequence { public List<Vector2> Points { get; set; } public float[] Sizes { get; set; } float static_CalcSize_Mid; /// <summary> /// 計算畫筆大小 /// </summary> float static_CalcSize_PenSize; public void CalcSize() { if (Points.Count < 1) return; Sizes = new float[Points.Count]; for (i = 0; i <= Points.Count - 1; i++) { static_CalcSize_Mid = Convert.ToSingle(Math.Abs(i - Points.Count / 2)); static_CalcSize_PenSize = 1 - static_CalcSize_Mid / Points.Count * 2; Sizes[i] = static_CalcSize_PenSize; } } } C#-PointSequence

附錄

  相關鏈接

  Demo視頻:AutomaticDrawing_人民 (上文的視頻暫不上傳\(^o^)/)

  Demo開源:EDGameEngine.Visuals.AutoDraw

  相關博客

  玩轉你畫我猜(一):程序實現自動繪圖 

  玩轉你畫我猜(二):更優秀的自動繪圖程序 

  AR創意分享:兒童塗鴉遇上程序繪圖 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved